像素级渲染生成色彩图:Colorful ColorMapView颜色轮实现原理深度解析
像素级渲染生成色彩图:Colorful ColorMapView颜色轮实现原理深度解析
【免费下载链接】Color-Picker-for-iOSColorful: iOS color picker built with Swift.项目地址: https://gitcode.com/gh_mirrors/co/Color-Picker-for-iOS
Colorful 是一款用 Swift 编写的 iOS 取色器(Color Picker)库,其中的颜色轮由ColorMapView通过像素级渲染逐点绘制生成,支持 sRGB 与扩展 sRGB(Display P3)宽色域。本文将带你深入解析这个圆形 HSB 颜色轮从数学模型到像素渲染的完整实现原理,即使你是 iOS 新手也能轻松看懂。
一、Colorful 是什么?一个高颜值的 iOS 取色器
Colorful 的取色器由四部分组合而成:
- 🎨颜色轮(ColorMapView):圆形区域,横轴为色相(Hue)、半径为饱和度(Saturation)
- 📏亮度滑杆(BrightnessSlider):右侧竖条,上下滑动调节明度(Brightness)
- 🎯颜色轮光标(ColorMapCursor):跟随手指移动的圆点指示器
- ⚙️容器(ColorPicker):继承自
UCtrl(UIControl),负责手势分发与事件回调
整个 UI 的组装逻辑在 ColorPicker.swift 中,各子视图分别位于Colorful/Classes/目录下。
二、核心原理 1:圆形颜色轮的数学模型
颜色轮的本质是一次坐标系变换:把手指在圆盘上的位置(直角坐标)转换为 HSB 颜色(色相 + 饱和度)。这部分由ColorMapView.swift底部的HRColorMapModel类完成。
位置 → 颜色的映射规则非常直观:
- 饱和度 = 距圆心距离 × 2(距离超过半径 0.5 时封顶为 1,因此圆心是纯色中心)
- 色相 = 指针与圆心连线的角度(顺时针 0~360°,对应 HSB 的 0~1 归一化色相)
用三角函数实现起来只有几行:hypot()求半径、atan2()求角度,再归一化到 0~2π 区间即可。
颜色 → 位置则是它的逆运算(用于程序化设置取色器初始颜色时定位光标):
- 半径 = 饱和度 ÷ 2
- 角度 = 色相 × (−2π)
- 用
cos/sin还原出圆盘上的坐标,最后加上圆心偏移 (0.5, 0.5)
这套正逆映射保证了「手指点哪里,就取到什么颜色;设置什么颜色,光标就停在哪里」,二者严格一致。
三、核心原理 2:像素级渲染,逐点"画"出颜色图
颜色轮图片不是截图、不是渐变图层,而是实时逐像素计算生成的。关键方法是createColorMapImage(size:model:)(位于 ColorMapView.swift 第 31~67 行):
- 分配原始内存:为
宽 × 高 × 3字节的 RGB 数据申请一块CFMutableData缓冲区 - 双重循环遍历每个像素:对每个 (x, y) 坐标,先归一化为 0~1 的圆盘坐标,再调用上一步的
HRColorMapModel.color(at:)算出该点对应的 HS 颜色 - 换算为 RGB:以亮度 1(最亮)为基准把 HS 颜色转换为 8bit 的 R/G/B 字节,按行优先顺序写入缓冲区
- 封装成 CGImage:用
CGDataProvider包装原始数据,指定色彩空间后创建CGImage,并设置shouldInterpolate: false禁止插值——这是保证颜色轮边缘色相过渡精准的关键细节
最终这张 CGImage 被赋值给CALayer的contents属性显示在屏幕上,并用CAShapeLayer的圆形路径作为mask,把正方形渲染结果"裁"成圆盘形状。
💡 为什么选择逐像素渲染?因为 HSB 颜色轮是非线性渐变,任何简单的渐变图层或 Core Graphics 路径着色都无法精确还原"角度=色相、半径=饱和度"的数学关系,只有逐点计算才能保证所见即所得。
四、宽色域支持:sRGB 与扩展 sRGB
Colorful 的一大亮点是同时支持两种色彩空间,由HRColorSpace枚举定义(见 ColorfulEntity.swift):
| 色彩空间 | 底层实现 | 适用场景 |
|---|---|---|
.sRGB | CGColorSpace.sRGB | 标准色彩,兼容性最好 |
.extendedSRGB | CGColorSpace.displayP3 | 覆盖更广的 P3 色域,颜色更鲜艳 |
渲染时会为CGImage选择对应的色彩空间;颜色与 UIKit 的互转则集中在HSVColor的扩展中完成——uiColor属性会根据当前色彩空间自动选择UIColor(hue:...)还是UIColor(displayP3Red:...)构造器,确保 P3 颜色不会被错误压缩到 sRGB。
五、亮度如何影响颜色轮?一个巧妙的"透明度"技巧
很多人会疑惑:颜色轮上明明只渲染了亮度=1 的最亮颜色,那亮度是怎么生效的?
答案藏在ColorMapView的set(brightness:)方法里:
- 颜色轮图片始终保持最亮状态
- 调节亮度时,直接把整个
colorMap图层的opacity设为亮度值 - 图层下方垫了一个黑色背景层,因此透明度越高(亮度越低),颜色就越黑
这个"黑色背景 + 透明度"的技巧避免了亮度变化时重新逐像素渲染整张图,性能开销几乎为零。而右侧亮度滑杆(BrightnessSlider.swift)则用 20 个小色块实时展示当前 HS 颜色在不同亮度下的梯度,方便用户预览。
六、触摸交互:从手指坐标到颜色的完整链路
当你在颜色轮上滑动时,ColorPicker的handleColorMapPan(pan:)会启动如下链路:
- 手指坐标通过
normalize()换算为 0~1 的归一化圆盘坐标 HRColorMapModel.color(at:)算出新的色相与饱和度- 合并当前亮度得到完整
HSVColor mapColorToView()同步刷新:光标位置、亮度滑杆色块、以及颜色轮透明度- 触发
valueChanged事件,并伴随触觉反馈(UISelectionFeedbackGenerator)
其中光标的位置又用到「颜色 → 位置」的逆映射,形成完整的闭环。
七、总结
Colorful 的ColorMapView颜色轮实现堪称小型教学范本,核心可以归纳为三点:
- 📐数学模型先行:用极坐标(角度=色相,半径=饱和度)定义颜色轮,正逆映射保证双向精确
- 🖌️像素级渲染:逐点计算 RGB 写入内存缓冲区,生成无插值的
CGImage,圆形 mask 裁出圆盘 - ⚡性能小聪明:亮度变化只改图层透明度,宽色域切换交给色彩空间枚举统一管理
如果你想在自己的 iOS 项目中加入一个美观、支持宽色域、带触觉反馈的取色器,Colorful 是一个非常值得参考的轻量级选择——它的核心源码不足 500 行,阅读Colorful/Classes/下的六个文件即可掌握全部实现细节。
【免费下载链接】Color-Picker-for-iOSColorful: iOS color picker built with Swift.项目地址: https://gitcode.com/gh_mirrors/co/Color-Picker-for-iOS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
