(八)LVGL 8.3.11 移植与应用实战:图片解码 ------ 格式、缓存与性能
目标:搞懂 LVGL 图片从存储介质到屏幕的完整链路,会为每种资源选对格式与路径,并控制得住解码的内存与时间开销。
1. 原理篇:图片解码框架
1.1 解码器抽象
lv_img 控件从不直接读图片数据,一切经过 lv_img_decoder 统一接口:
info_cb:读取图片头,返回宽、高、色彩格式;open_cb:打开数据源并准备解码;read_line_cb:按行产出像素------LVGL 渲染到第几行就解码第几行,无需整图驻留 RAM;close_cb:收尾释放。
8.3 内置解码器处理 LVGL 自有格式(转换器产物);PNG/JPG/BMP/GIF 由可选解码库补充(LV_USE_PNG / LV_USE_SJPG / LV_USE_BMP / LV_USE_GIF 等)。
1.2 三条资源路径

| 路径 | 存储 | RAM 解码缓冲 | 加载延迟 | 适用 |
|---|---|---|---|---|
| A:C 数组 | 内部 Flash | 无(直接映射) | 零 | 启动画面、关键图标 |
| B:转换器 bin | SD 卡 | 仅行缓冲 | 毫秒级(SDIO) | 背景图、皮肤资源 |
| C:原始 PNG/JPG + 解码库 | SD 卡 | 宽×高×4B 整图 | 几十~几百 ms | 用户导入图片等不可预知场景 |
1.3 色彩格式(转换器的核心选项)
| 格式 | 每像素 | Alpha | 说明 |
|---|---|---|---|
CF_TRUE_COLOR |
32bit (ARGB8888) | 有 | 质量最好,DMA2D 可直接做混合加速 |
CF_TRUE_COLOR_565 |
16bit | 无 | 不透明图首选,体积减半 |
CF_TRUE_COLOR_CHROMA_KEYED |
16bit | 色键透明 | 纯色背景抠透明,省体积 |
CF_INDEXED_1/2/4/8BIT |
1~8bit + 调色板 | 可选 | 图标/单色图利器,体积可缩到 1/4 |
CF_RAW |
原始 | --- | 数据原样交给自定义解码器,特殊场景用 |
CF_ALPHA_1/2/4/8BIT |
仅 Alpha | 仅透明通道 | 纯蒙版/阴影贴图 |
2. 应用篇:转换与使用
2.1 在线转换器决策流程
https://lvgl.io/tools/imageconverter 选格式的思考顺序:
- 图里有透明区域吗? 没有 →
CF_TRUE_COLOR_565(最省);有 → 看下一步; - 颜色种类少吗(图标 ≤256 色)? 是 →
CF_INDEXED_8BIT(体积约 1/4);否 →CF_TRUE_COLOR(保留 Alpha); - 放哪里? 启动必需、不想依赖 SD 卡 → 输出 C 数组 ;量大、可换皮 → 输出 Binary 放 SD 卡。
2.2 使用代码
c
/* 路径 A:C 数组(转换器输出 .c 文件加入工程) */
LV_IMG_DECLARE(img_logo);
lv_obj_t *img1 = lv_img_create(lv_scr_act());
lv_img_set_src(img1, &img_logo);
/* 路径 B/C:SD 卡文件(第 04 篇的 FS 驱动提供 'S' 盘符) */
lv_obj_t *img2 = lv_img_create(lv_scr_act());
lv_img_set_src(img2, "S:/pic/bg.bin"); /* 转换器 bin */
lv_img_set_src(img2, "S:/pic/photo.png"); /* 需 LV_USE_PNG 1 */
2.3 启用 PNG/JPG 解码库
c
#define LV_USE_PNG 1 /* 内置 lodepng,无需外部库 */
#define LV_USE_SJPG 1 /* Split JPG:低内存 JPG 解码(tjpgd) */
#define LV_USE_GIF 1 /* GIF89a,lv_gif 控件 */
#define LV_USE_BMP 1
lodepng 是 LVGL 内置的 PNG 解码器(源码在 lvgl/src/extra/libs/png),打开宏即编译,无需额外移植;JPG 有两个选择:LV_USE_SJPG(split-jpg,分块解码省内存,推荐嵌入式)与完整 libjpeg(PC 模拟器用得多)。
3. 实践篇:缓存、性能与排障
3.1 解码缓存:LV_IMG_CACHE_DEF_SIZE
解码结果(解码后的整图像素)会按 src 为 key 缓存,LV_IMG_CACHE_DEF_SIZE 是缓存条数(默认 1!)。
c
#define LV_IMG_CACHE_DEF_SIZE 16 /* 调试期建议 8~16 */
- 缓存命中:同一张图再次显示零解码零读卡;
- 缓存的像素数据从 LVGL 堆 分配------16 张大图缓存意味着几百 KB~几 MB 堆占用,必须与第 06 篇的
LV_MEM_SIZE联动规划; - 图片文件更新后记得
lv_img_cache_invalidate_src("S:/pic/bg.bin")失效旧缓存,否则显示的仍是旧图。
3.2 性能优化清单
| 手段 | 收益 | 说明 |
|---|---|---|
| 资源离线转 bin/C 数组 | ★★★ | 消除运行时解码(路径 C 的百 ms 级开销归零) |
| 图片尺寸 = 显示尺寸 | ★★★ | lv_img_set_zoom() 是软件逐像素插值,800×480 缩放一次就是几十 ms;转换阶段就定好分辨率 |
| SD 卡用 SDIO 4bit | ★★★ | 读 750KB 背景图:SDIO 约几十 ms,SPI 模式慢一个数量级 |
| 开启解码缓存 | ★★☆ | 重复显示零开销 |
| 页面切换前预加载 | ★★☆ | 隐藏 lv_img 先 set_src 触发解码,切换时直接显示 |
避免 lv_img_set_angle 旋转 |
★★☆ | 同样是软件逐像素运算 |
| DMA2D 混合 | ★★☆ | CF_TRUE_COLOR 图片的 Alpha 混合走 lv_gpu_stm32_dma2d(第 02 篇开启后自动生效) |
3.3 关键细节解析
① 路径 C 的内存为什么这么吓人?
PNG 解码必须整图展开:解码缓冲 = 宽 × 高 × 4 字节(lodepng 输出 RGBA8888),一张 800×480 的 PNG 就是 1.5MB------内部 SRAM 根本放不下 。三个对策按优先级:能用路径 A/B 就不用 C;必须解码 JPG 时用 SJPG(按 MCU 块分块解码,峰值内存降到几十 KB);必须 PNG 时确认 LVGL 堆在 SDRAM 且 ≥ 2MB 裕量。
② 为什么路径 B 的 bin 不需要整图 RAM?
转换器 bin(TrueColor/Indexed)的数据布局与屏幕像素一一对应,read_line_cb 可以直接从文件把当前行读进小行缓冲交给渲染器------文件访问是流式的,RAM 里永远只有一行。这也是为什么"从 SD 卡加载 750KB 背景图"是可行的:耗时主要花在 SD 读取上(SDIO 下可接受),而不是内存占用。
③ 解码失败时 LVGL 的静默行为
lv_img_set_src 失败(路径错、格式不支持、缓存分配失败)不会报错 ,只是显示空白或占位图。务必在调试期开 LV_USE_LOG:解码器链上每一环的失败原因都会打出来(file not found / unknown format / out of memory),这是图片问题排查的第一现场。
④ GIF 动图为什么"贴上去不动"?
lv_gif 的帧推进依赖 LVGL 定时器------也就是 lv_timer_handler() 的周期调用。GIF 卡住先查:lv_timer_handler 是否正常周期运行;再就是 GIF 文件本身超过尺寸/帧数限制时解码内存不足(GIF 每帧同样要整帧缓冲)。
3.4 排障速查表
| 现象 | 排查方向 |
|---|---|
| 显示空白/占位图 | 开 LV_USE_LOG 看解码器日志;路径盘符(S:/);格式与解码库宏是否匹配 |
| 图片花屏/偏色 | 转换时色彩格式选错(如 565 的图当 TRUE_COLOR 显示);C 数组方式与 bin 方式混用 |
| 大图显示卡死/复位 | 解码缓冲撑爆堆:LV_MEM_SIZE 太小或堆没搬到 SDRAM;看 Mem Monitor |
| 换皮后显示旧图 | 未调 lv_img_cache_invalidate_src |
| 切页瞬间卡顿明显 | 页面图未预载;或用了路径 C 的 PNG 大图解码头 |
| GIF 不动 | lv_timer_handler 未周期运行;帧缓冲不足 |
3.5 验证用例
c
/* 三路径一次验证 */
lv_obj_t *a = lv_img_create(lv_scr_act()); lv_img_set_src(a, &img_logo); /* A */
lv_obj_t *b = lv_img_create(lv_scr_act()); lv_img_set_src(b, "S:/pic/bg.bin"); /* B */
lv_obj_t *c = lv_img_create(lv_scr_act()); lv_img_set_src(c, "S:/pic/test.png"); /* C */
lv_obj_align(b, LV_ALIGN_LEFT_MID, 10, 0);
lv_obj_align(c, LV_ALIGN_RIGHT_MID, -10, 0);
三张图同屏正确显示,即图片链路(Flash 直读 / SD bin / PNG 解码)全部打通。