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有多张风格相近的图片(比如同一套图标),可以让它们共用同一个调色板。这样,调色板只需要存一次,每张图片只存像素索引。

具体做法:

  1. 把所有图片合并成一张大图,然后做颜色量化,生成一个全局调色板。
  2. 每张图片单独保存像素索引,引用同一个调色板。

这样做的好处:

  • 调色板存储从 N×768字节 降到 768字节。
  • 切换图片时,不需要重新加载调色板,刷屏更快。

避坑指南:共享调色板时,要确保所有图片的颜色范围相近。如果一张图是蓝色系,另一张是红色系,共用调色板会导致颜色失真。我曾经吃过这个亏,后来改成按主题分组,每个组一个调色板。

3.3.4 动态调色板:运行时切换

有些场景下,你需要根据环境光或用户设置改变UI色调。比如白天用亮色主题,晚上用暗色主题。这时候,动态调色板就很有用。

做法:

  • 图片数据只存像素索引。
  • 运行时,根据当前主题,重新计算调色板里的颜色值。
  • 只需要更新调色板RAM,图片数据不用动。

我做过一个产品,用动态调色板实现了“一键换肤”。用户点一下,整个UI从蓝色变成红色,刷屏时间不到10毫秒。如果不用调色板,你得重新解码整张图片,至少几百毫秒。

3.4 调色板技术知识体系

下面这张图,帮你理清调色板技术的核心脉络:

调色板技术知识体系 调色板技术知识体系 调色板技术 索引色原理 256色 vs 16位色 调色板优化 像素=索引 查表映射 存储量对比 场景匹配 颜色量化 排序/共享 核心思路 1. 用少量索引代替大量颜色数据,降低存储和带宽。 2. 根据场景选择256色或16位色,平衡质量与资源。 3. 通过量化、排序、共享、动态切换,进一步优化效果。

调色板技术,说难不难,说简单也不简单。关键是要理解它的本质:用空间换时间,用索引换带宽。我在实际项目中,靠调色板技术省下的存储和带宽,少说也有几十MB。你想想看,如果你的产品能省下这些资源,是不是可以多放点功能,或者用更便宜的芯片?