LVGL 图片对象基础:从创建到显示
图片在嵌入式 UI 里是最常用的元素之一。LVGL 对图片的处理,说白了就是三个核心动作:创建对象、设置数据源、刷新显示。这一节我带你把这几个动作拆开揉碎,看看底层到底发生了什么。
图片对象的生命周期
LVGL 里所有可见元素都是对象(lv_obj_t),图片也不例外。创建一个图片对象,只需要两行代码:
lv_obj_t * img = lv_img_create(lv_scr_act()); // 在当前屏幕创建
lv_obj_set_size(img, 100, 100); // 设置显示尺寸
嗯,这里要注意:lv_img_create 只是分配了一个空壳子,它还没有任何图像数据。你想想看,就像你买了个相框,但里面还没放照片。
lv_obj_align(img, LV_ALIGN_CENTER, 0, 0),这样后面调整位置时心里有数。
核心函数:lv_img_set_src
这个函数是图片加载的入口。它的原型很简单:
void lv_img_set_src(lv_obj_t * img, const void * src);
但 src 参数的类型却藏着不少门道。LVGL 支持三种数据源:
| 数据源类型 | 标识方式 | 典型场景 |
|---|---|---|
| 内部变量 | 直接传指针 | 小图标、Logo,编译时嵌入 |
| 文件路径 | 以 file:// 开头 |
SD 卡、Flash 中的大图 |
| 符号字体 | 以 LV_SYMBOL_ 开头 |
按钮图标、状态指示 |
我曾经在项目里踩过一个坑:用文件路径时忘了加 file:// 前缀,结果图片死活显示不出来。调试了半天才发现是字符串格式问题。所以我的建议是——写代码时先把路径打印出来看一眼。
lv_img_set_src 的第二个参数必须是指向文件路径的字符串,且路径前缀要匹配你注册的文件系统驱动。
图片描述符结构:lv_img_dsc_t
LVGL 内部用 lv_img_dsc_t 这个结构体来管理图片的元数据。它长这样:
typedef struct {
lv_img_header_t header; // 图片头信息
uint32_t data_size; // 数据总字节数
const uint8_t * data; // 指向像素数据的指针
} lv_img_dsc_t;
而 lv_img_header_t 又包含了:
typedef struct {
uint32_t cf : 5; // 颜色格式(色彩深度、压缩方式)
uint32_t always_zero : 3;
uint32_t reserved : 2;
uint32_t w : 12; // 图片宽度(像素)
uint32_t h : 12; // 图片高度(像素)
} lv_img_header_t;
你可能会问:为什么宽度和高度只用了 12 位?因为 LVGL 设计时考虑的是嵌入式场景,单张图片最大 4095 像素,够用了。我见过有人试图加载 4K 图片,结果内存直接爆掉——嗯,这就是没看文档的后果。
颜色格式(Color Format)
cf 字段决定了每个像素占多少字节、如何解析。LVGL 支持的颜色格式很多,但常用的就这几种:
| 宏定义 | 描述 | 每像素字节数 |
|---|---|---|
LV_IMG_CF_TRUE_COLOR |
真彩色(按全局颜色深度) | 2 或 4 |
LV_IMG_CF_INDEXED_1BIT |
索引色,1 位色深 | 0.125 |
LV_IMG_CF_INDEXED_2BIT |
索引色,2 位色深 | 0.25 |
LV_IMG_CF_INDEXED_4BIT |
索引色,4 位色深 | 0.5 |
LV_IMG_CF_INDEXED_8BIT |
索引色,8 位色深 | 1 |
LV_IMG_CF_ALPHA_8BIT |
仅 Alpha 通道 | 1 |
图片加载的完整流程
从调用 lv_img_set_src 到图片真正显示在屏幕上,中间经历了这些步骤:
- 解析数据源类型:判断 src 是变量、文件还是符号。
- 读取图片头:获取宽、高、颜色格式。
- 分配解码缓冲区:如果图片是压缩格式,需要临时解压。
- 解码像素数据:将原始数据转换为显示驱动需要的格式。
- 刷新显示区域:通知 LVGL 重绘图片所在区域。
这里面最耗时的就是第 4 步。如果是外部存储加载,还要加上文件 I/O 的时间。我建议你在性能敏感的项目里,用 lv_img_cache_set_size 开启图片缓存,避免重复解码。
知识结构图
下面这张图把图片对象、描述符、数据源之间的关系画清楚了:
实际项目中的注意事项
结合我自己的经验,给你几个实用建议:
- 图片尺寸要对齐:LVGL 内部处理时,宽度最好是 2 的倍数,否则某些颜色格式下会有性能损失。
- 颜色格式匹配:如果显示驱动是 RGB565,图片也尽量用 RGB565,避免运行时转换。
- 大图分块加载:超过 100KB 的图片,建议用
lv_img_set_zoom配合缓存,不要一次性解码全部数据。 - 调试时打印描述符:在
lv_img_set_src之后,可以打印img->dsc->header.w和img->dsc->header.h,确认图片是否被正确解析。
lv_img_dsc_t 的 C 语言定义,省去手动计算头信息的麻烦。
好了,这一节的内容就到这里。图片对象的基础你已经掌握了,接下来就是如何让 LVGL 从外部存储(比如 SD 卡、SPI Flash)里加载这些图片——那才是真正考验驱动设计能力的地方。