3. 调色板技术:索引色原理、256色与16位色选择策略、实际项目中的调色板优化
调色板技术,说白了就是用一个小字典来映射颜色。你想想看,一张图片可能有几万种颜色,但我们的显存和带宽都有限。这时候,调色板就派上用场了。我最早接触这个,是在做一款低端家电的触摸屏界面,那时候Flash存储才几百KB,不用调色板根本塞不下图片。
3.1 索引色原理:从“真彩色”到“查表”
真彩色(比如24位色)每个像素用3个字节直接表示RGB值。而索引色,每个像素只存一个“编号”,这个编号对应调色板里的一个颜色条目。显示的时候,硬件根据编号去调色板里查,再把真正的颜色画出来。
举个例子:
- 24位真彩色:像素值 = (R, G, B),每个分量8位,共16,777,216种颜色。
- 8位索引色(256色):像素值 = 0~255的索引号,调色板里有256个颜色条目,每个条目24位。
这样做的好处很明显:
- 存储量暴减:一张320×240的图片,真彩色需要320×240×3 = 230,400字节;256色只需要320×240×1 + 256×3 = 77,568字节,省了约66%。
- 带宽降低:刷屏时,DMA传输的数据量只有原来的1/3。
核心公式:索引色存储量 = 像素数 × log₂(颜色数)/8 + 调色板大小(颜色数 × 3字节)。
我在一个项目里遇到过,有人直接用24位色存图标,结果Flash塞不下。后来改成256色调色板,同样的图标,存储量从120KB降到了45KB。嗯,这就是索引色的威力。
3.2 256色与16位色:怎么选?
很多工程师会纠结:到底用256色还是16位色(RGB565)?我个人的习惯是,先看你的屏是啥接口,再看你的图片内容。
先看一张对比表:
| 特性 | 256色(8位索引) | 16位色(RGB565) |
|---|---|---|
| 每个像素位数 | 8位 | 16位 |
| 可显示颜色数 | 256种(可自定义) | 65,536种(固定) |
| 存储量(320×240) | 约77KB | 约150KB |
| 颜色渐变效果 | 较差,有带状条纹 | 较好,过渡平滑 |
| 硬件支持 | 需要调色板RAM | 直接输出,无需查表 |
| 适用场景 | 图标、文字、简单图形 | 照片、渐变背景、复杂UI |
我的选择策略:
- 图标和按钮:256色足够了。图标通常颜色少,256色完全能覆盖,而且省一半存储。
- 背景渐变或照片:用16位色。256色做渐变会有明显的色带,看着很廉价。我曾经在一个产品上试过用256色做渐变背景,结果客户说“这屏是不是坏了?”——后来改成16位色,问题解决。
- 存储极度紧张:优先用256色,配合抖动算法(Floyd-Steinberg)来模拟更多颜色。抖动后的256色,视觉上接近16位色,但存储量不变。
小技巧:如果你的MCU支持硬件调色板(比如某些STM32的LTDC),256色的刷屏速度会比16位色快很多,因为DMA传输的数据量小。我建议在硬件支持的情况下,优先用256色。
3.3 实际项目中的调色板优化
调色板不是随便选256种颜色就完事了。优化得好,图片质量能提升一个档次。我总结了几条实战经验:
3.3.1 颜色量化:从真彩色到256色
把一张真彩色图片转成256色,需要做颜色量化。常用的算法有:
- 中位切分法(Median Cut):把颜色空间切分成256个盒子,每个盒子取平均色。效果不错,速度也快。
- 八叉树量化(Octree):用八叉树结构合并相近颜色,精度更高,但计算量大。
- K-Means聚类:把颜色聚成256类,效果最好,但速度最慢。
我个人习惯用中位切分法,因为它在嵌入式工具链里支持最广。比如用Python的PIL库:
from PIL import Image
img = Image.open('input.png')
# 转换成256色调色板模式
img = img.quantize(colors=256, method=Image.MEDIANCUT)
img.save('output.png')
嗯,这里要注意:量化后的图片,如果原图有大量渐变色,可能会出现色带。这时候可以加一点抖动(dithering):
img = img.quantize(colors=256, method=Image.MEDIANCUT, dither=Image.FLOYDSTEINBERG)
3.3.2 调色板排序:提升压缩率
调色板里的颜色顺序,会影响后续的压缩效率。比如你用RLE或LZ4压缩图片数据,如果相邻像素的索引号相近,压缩率会更高。
怎么做?把调色板按亮度排序,或者按色相排序。这样,颜色相近的像素,索引号也相近,压缩算法更容易找到重复模式。
我曾经在一个项目里,把调色板按亮度排序后,LZ4压缩率从原来的60%提升到了75%。没花一分钱硬件成本,就多省了15%的存储。
3.3.3 共享调色板:多张图片共用
如果你的UI有多张风格相近的图片(比如同一套图标),可以让它们共用同一个调色板。这样,调色板只需要存一次,每张图片只存像素索引。
具体做法:
- 把所有图片合并成一张大图,然后做颜色量化,生成一个全局调色板。
- 每张图片单独保存像素索引,引用同一个调色板。
这样做的好处:
- 调色板存储从 N×768字节 降到 768字节。
- 切换图片时,不需要重新加载调色板,刷屏更快。
避坑指南:共享调色板时,要确保所有图片的颜色范围相近。如果一张图是蓝色系,另一张是红色系,共用调色板会导致颜色失真。我曾经吃过这个亏,后来改成按主题分组,每个组一个调色板。
3.3.4 动态调色板:运行时切换
有些场景下,你需要根据环境光或用户设置改变UI色调。比如白天用亮色主题,晚上用暗色主题。这时候,动态调色板就很有用。
做法:
- 图片数据只存像素索引。
- 运行时,根据当前主题,重新计算调色板里的颜色值。
- 只需要更新调色板RAM,图片数据不用动。
我做过一个产品,用动态调色板实现了“一键换肤”。用户点一下,整个UI从蓝色变成红色,刷屏时间不到10毫秒。如果不用调色板,你得重新解码整张图片,至少几百毫秒。
3.4 调色板技术知识体系
下面这张图,帮你理清调色板技术的核心脉络:
调色板技术,说难不难,说简单也不简单。关键是要理解它的本质:用空间换时间,用索引换带宽。我在实际项目中,靠调色板技术省下的存储和带宽,少说也有几十MB。你想想看,如果你的产品能省下这些资源,是不是可以多放点功能,或者用更便宜的芯片?