(八)LVGL 8.3.11 移植与应用实战:图片解码 —— 格式、缓存与性能

(八)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 选格式的思考顺序:

  1. 图里有透明区域吗? 没有 → CF_TRUE_COLOR_565(最省);有 → 看下一步;
  2. 颜色种类少吗(图标 ≤256 色)? 是 → CF_INDEXED_8BIT(体积约 1/4);否 → CF_TRUE_COLOR(保留 Alpha);
  3. 放哪里? 启动必需、不想依赖 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_imgset_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 解码)全部打通。


相关推荐
alive9031 年前
STM32移植LVGL8.3 (保姆级图文教程)
stm32·单片机·嵌入式硬件·stm32f407·lvgl8.3·lvgl移植