4. 纹理压缩与格式:RGB565 vs ARGB8888选择、L8调色板纹理、DXT/ETC压缩格式应用
纹理,说白了就是UI的“血肉”。你界面再漂亮,纹理一崩,帧率直接跳水。我见过太多项目,CPU算力明明够,GPU却因为纹理带宽被拖死。今天咱们就聊聊怎么给纹理“瘦身”。
核心观点:纹理优化的本质,是用最小的显存带宽,换取人眼可接受的最大画质。没有银弹,只有权衡。
4.1 RGB565 vs ARGB8888:别让Alpha通道白吃带宽
这两个格式,是TouchGFX里最基础的纹理格式选择。ARGB8888每个像素占4字节,RGB565只占2字节。你想想看,同样一张1024x1024的图,前者要4MB,后者只要2MB。显存带宽直接砍半。
什么时候用ARGB8888?只有一种情况:你真的需要半透明渐变、阴影、或者抗锯齿边缘。比如按钮的圆角遮罩、弹窗的阴影层。这些地方Alpha通道是刚需。
什么时候用RGB565?绝大多数情况。图标、背景、纯色按钮、文字底图。我个人的习惯是:只要纹理里没有半透明像素,一律RGB565。曾经有个项目,UI设计师给了全套ARGB8888的图标,我批量转成RGB565后,帧率从42fps直接飙到58fps。设计师肉眼根本看不出区别。
| 特性 | ARGB8888 | RGB565 |
|---|---|---|
| 每像素大小 | 4字节 | 2字节 |
| Alpha通道 | 8位(256级) | 无 |
| 颜色精度 | 1600万色 | 6.5万色 |
| 适用场景 | 半透明、渐变、阴影 | 不透明图标、背景、文字 |
| 显存带宽消耗 | 高 | 低 |
小技巧:在TouchGFX Designer里,你可以为每个图片资源单独设置格式。别偷懒用全局设置。我习惯在资源导入后,逐个检查Alpha通道:如果Alpha通道全是255,直接降级为RGB565。
4.2 L8调色板纹理:小内存设备的救命稻草
L8格式,说白了就是“索引色”。它用一个256色的调色板,每个像素只存一个字节的索引值。这样一张1024x1024的图,只需要1MB + 1KB(调色板)。
但L8有个硬伤:它只支持256种颜色。如果你的纹理是照片级渐变,或者有大量平滑过渡,L8会出现明显的色阶断层。我踩过这个坑——有一次做智能家居面板,背景用了L8的星空渐变图,结果星星周围全是色块,像马赛克一样。
L8的最佳实践:
- 图标和简单图形:颜色数少于256的图标,L8是首选。比如设置图标、返回箭头、电量符号。
- 文字纹理:字体纹理天生就是单色或少量颜色,L8非常合适。
- 低端MCU:如果你的MCU只有512KB或1MB的SRAM,L8几乎是唯一选择。
避坑指南:我曾经把一个ARGB8888的按钮背景直接转成L8,结果按钮上的文字边缘出现了彩色噪点。原因是按钮背景有抗锯齿边缘,颜色数超过了256。后来我先把图片量化到256色,再转L8,问题解决。记住:L8不是万能药,它只适合颜色数少的纹理。
4.3 DXT/ETC压缩格式:硬件解码的降维打击
RGB565和L8都是“无压缩”格式。而DXT(S3TC)和ETC是“有损压缩”格式。它们利用硬件解码器,在GPU读取纹理时实时解压。压缩比通常能达到4:1到6:1,而且画质损失很小。
DXT系列:DXT1(无Alpha)、DXT3(显式Alpha)、DXT5(插值Alpha)。DXT1压缩比最高,适合不透明纹理。DXT5适合有半透明的纹理。但DXT是NVIDIA的专利,虽然大部分GPU都支持,但在某些低端MCU上可能没有硬件解码器。
ETC系列:ETC1(无Alpha)、ETC2(有Alpha)。ETC是OpenGL ES的标准压缩格式,几乎所有现代GPU都原生支持。ETC2是ETC1的升级版,支持Alpha通道,画质更好。
| 格式 | 压缩比 | Alpha支持 | 硬件支持 | 画质 |
|---|---|---|---|---|
| DXT1 | 6:1 | 无 | 广泛 | 良好 |
| DXT5 | 4:1 | 插值Alpha | 广泛 | 良好 |
| ETC1 | 6:1 | 无 | 几乎所有 | 良好 |
| ETC2 | 4:1 | 显式Alpha | 几乎所有 | 优秀 |
我的选择策略:如果目标芯片支持ETC2,我无脑选ETC2。如果不支持,退而求其次选DXT5。如果都不支持,那就只能用RGB565或L8了。记住:压缩格式不是万能的,它只适合大纹理(比如背景图、全屏图)。小图标用压缩格式反而浪费。
4.4 纹理格式选择决策树
说了这么多,到底怎么选?我画了一张决策图,你照着走就行。
4.5 实战建议:别让纹理成为瓶颈
最后,我总结几条实战经验:
- 先分析,后优化:用TouchGFX的Profiler工具,看看哪些纹理占用了最多的带宽。别凭感觉优化。
- 批量转换:写个脚本,自动检测纹理的Alpha通道和颜色数,然后批量转换格式。我习惯用Python + Pillow库做这件事。
- 测试覆盖:每种格式转换后,一定要在目标硬件上跑一遍。有些MCU的GPU对压缩格式支持不完整,可能会出现花屏。
- 不要过度压缩:纹理压缩不是越狠越好。DXT1虽然压缩比高,但画质损失明显。我一般只在背景图或大纹理上用压缩格式,小图标保持RGB565或L8。
我的习惯:每次项目启动前,我会先和UI设计师定一个“纹理格式规范”。比如:图标一律RGB565,背景图一律ETC2,弹窗阴影用ARGB8888。这样后期优化时,就不用一张张去改了。
纹理格式的选择,说白了就是一场“画质 vs 性能”的博弈。没有绝对的对错,只有适合不适合。你只要记住:带宽是有限的,人眼是宽容的。在保证视觉体验的前提下,尽量用更少的字节去表达更多的像素。