4、图像资源优化:将24位BMP转为16位RGB565
图像资源,说白了就是嵌入式UI的“面子”。你界面再流畅,图标一糊、颜色一断层,用户第一印象就崩了。TouchGFX里最常用的颜色格式就是RGB565——16位,红5绿6蓝5,每个像素只占2字节。相比24位BMP的3字节,直接省掉三分之一内存。这章我们就来搞定这件事。
核心结论:24位BMP转RGB565,不是简单砍掉8位,而是用TouchGFX ImageConverter做批量转换,同时保证视觉损失最小。
4.1 为什么非转不可?
我刚开始做TouchGFX项目时,也想过“直接用24位图不就行了?反正MCU内存够大”。结果呢?一张480x272的全屏背景图,24位是480×272×3 ≈ 391KB,而RGB565只有480×272×2 ≈ 261KB。差130KB。你想想看,一个STM32F429的SRAM才256KB,一张图就吃掉全部内存,还跑不跑界面了?
RGB565的优势很明显:
- 内存减半:每像素从3字节降到2字节
- 传输更快:DMA搬运数据量减少33%
- 帧缓冲友好:双缓冲模式下,两个缓冲区的总大小直接降低
但代价呢?颜色精度从1677万色降到65536色。不过说实话,在4.3寸、5寸这种小屏幕上,肉眼几乎看不出区别。除非你的UI里有渐变天空、人像照片这种连续色调,否则RGB565完全够用。
我的经验:如果UI里必须保留照片级图片,可以考虑局部用L8(8位索引色)或ALPHA压缩,但大部分图标、按钮、背景图,RGB565是性价比最高的选择。
4.2 TouchGFX ImageConverter 是什么?
ImageConverter是TouchGFX Designer内置的图片处理工具链。它不是一个独立软件,而是集成在项目构建流程中的。你只需要把原始图片放到项目的assets/images目录下,配置好转换规则,编译时它会自动完成:
- 格式转换(24位→16位、8位、4位等)
- 抖动处理(Dithering)
- 压缩(RLE、LZ4等)
- 生成C++数组(直接嵌入到Flash)
说白了,你不需要手动写任何转换代码。但前提是——你得知道怎么配置它。
4.3 批量转换的配置方法
我个人习惯在TouchGFX Designer里统一管理图片资源。步骤如下:
- 准备图片:把所有24位BMP放到
assets/images目录下,文件名不要有中文和空格 - 打开Designer:在Images面板里,你会看到所有图片自动加载
- 设置格式:选中一张或多张图片,在右侧属性面板找到Format,选择RGB565
- 配置抖动:勾选Dithering(建议开启,能减少颜色断层)
- 批量应用:按住Ctrl或Shift多选,一次性设置所有图片
嗯,这里要注意:Designer里设置的是“转换规则”,真正执行转换是在编译时。所以改完配置后,一定要重新编译项目。
我曾经踩过的坑:有一次我改了图片格式,但只点了保存,没重新编译。结果运行起来图片还是24位,内存直接爆了。记住:ImageConverter只在编译时执行,改完配置必须Clean Build。
4.4 命令行批量转换(进阶)
如果你有几十上百张图,在Designer里一张张点太慢了。我建议用命令行方式。TouchGFX提供了ImageConverter.exe(在TouchGFX安装目录的framework/tools/imageconverter下)。
写一个批处理脚本:
@echo off
set INPUT_DIR=.\assets\images
set OUTPUT_DIR=.\generated\images
set CONVERTER="C:\TouchGFX\4.21.0\framework\tools\imageconverter\ImageConverter.exe"
for %%f in (%INPUT_DIR%\*.bmp) do (
%CONVERTER% --input "%%f" ^
--output "%OUTPUT_DIR%\%%~nf.png" ^
--format RGB565 ^
--dithering FloydSteinberg
)
echo 转换完成!
pause
这个脚本会遍历所有BMP文件,转成RGB565格式的PNG(TouchGFX内部用PNG存储压缩后的图像数据)。FloydSteinberg抖动算法能有效减少颜色量化时的条纹感。
我建议:如果UI以图标为主(颜色少、边缘硬),可以不用抖动,能省一点CPU时间。如果是照片或渐变背景,一定要开抖动,否则会出现明显的色带。
4.5 转换前后的效果对比
为了让你直观感受差异,我整理了一个对比表:
| 指标 | 24位BMP | 16位RGB565 | 差异 |
|---|---|---|---|
| 每像素位数 | 24 | 16 | 减少33% |
| 颜色数 | 16,777,216 | 65,536 | 减少99.6% |
| 480x272图片大小 | ~391KB | ~261KB | 节省130KB |
| Flash占用(无压缩) | 391KB | 261KB | 节省33% |
| 视觉质量(小屏幕) | 优秀 | 良好 | 肉眼几乎无差别 |
你看,130KB的节省,在嵌入式世界里就是“能不能跑起来”的区别。
4.6 转换后的内部结构
转换完成后,TouchGFX会生成一个C++数组,存在Flash里。比如一张名为bg_main的图片,会生成:
// generated/images/src/BitmapDatabase.cpp
const uint16_t bg_main_data[] = {
0x0000, 0x0001, 0x0002, ... // RGB565像素数据
};
const Bitmap bitmap_bg_main = {
480, 272, // 宽高
16, // 每像素位数
(const uint8_t*)bg_main_data,
Bitmap::RGB565
};
注意这里的数据类型是uint16_t,每个元素就是一个RGB565像素。如果你直接看十六进制值,高位是R(5位),中间是G(6位),低位是B(5位)。
调试技巧:如果图片显示颜色不对,可以用这个公式手动验证一个像素:RGB565 = ((R >> 3) << 11) | ((G >> 2) << 5) | (B >> 3)。R、G、B是0-255的值。
4.7 本章知识体系
下面这张图总结了24位BMP转RGB565的完整流程和关键决策点:
4.8 避坑指南
最后,分享几个我实际项目中遇到的坑:
- 颜色发紫:RGB565的R和B通道只有5位,如果原图有纯红(255,0,0),转换后R=31(5位最大值),但G和B为0,显示正常。但如果原图是深紫色(128,0,128),转换后R=16,B=16,G=0,颜色会偏暗。解决办法:在PS里先调高饱和度。
- 图片尺寸不对:TouchGFX要求图片宽高都是偶数?其实不是硬性要求,但DMA传输时偶数字节对齐效率更高。我习惯把图片宽高都设为4的倍数。
- 文件名冲突:两个不同目录下的同名图片,转换时会覆盖。我建议所有图片文件名全局唯一,或者用子目录名做前缀。
重要提醒:不要在运行时用软件把24位图转成RGB565!那会消耗大量CPU时间,而且占用临时内存。所有转换必须在编译时完成,运行时只读取Flash里的数组。
好了,这章的内容就这些。记住:图像资源优化是嵌入式UI性能优化的第一道关卡,RGB565转换做得好,后面双缓冲、DMA传输都会轻松很多。