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里统一管理图片资源。步骤如下:

  1. 准备图片:把所有24位BMP放到assets/images目录下,文件名不要有中文和空格
  2. 打开Designer:在Images面板里,你会看到所有图片自动加载
  3. 设置格式:选中一张或多张图片,在右侧属性面板找到Format,选择RGB565
  4. 配置抖动:勾选Dithering(建议开启,能减少颜色断层)
  5. 批量应用:按住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的完整流程和关键决策点:

24位BMP转RGB565流程与决策图 24位BMP → RGB565 转换流程 24位BMP源文件 选择转换方式 TouchGFX Designer ImageConverter.exe 配置参数:格式/抖动 Clean Build 编译 RGB565 C++数组(Flash) 关键:抖动算法推荐FloydSteinberg,批量转换用命令行脚本,改配置后必须Clean Build

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传输都会轻松很多。