2、图片格式详解(上):BMP格式结构、JPEG格式特点、PNG格式优势与局限

做嵌入式GUI开发,说白了就是跟图片打交道。LVGL要显示一张图,你得先搞清楚这张图到底是什么来头。我见过不少新手,直接把电脑上的图片扔进工程,结果要么显示不出来,要么内存爆了。

今天咱们就聊聊三种最常见的图片格式:BMP、JPEG、PNG。它们各有各的脾气,选对了事半功倍,选错了……嗯,我踩过的坑可以写本书了。

核心观点:没有最好的格式,只有最合适的场景。嵌入式环境资源有限,选格式就是做权衡。

图片格式知识体系图 图片格式核心对比 嵌入式图片格式 BMP 位图 JPEG 压缩图 PNG 无损图 特性 • 无压缩,体积大 • 结构简单,解析快 • 适合小尺寸图标 特性 • 有损压缩,体积小 • 不支持透明通道 • 适合照片背景 特性 • 无损压缩,质量高 • 支持Alpha透明 • 解码较慢,占RAM

2.1 BMP格式:简单粗暴的原始数据

BMP是Windows的亲儿子,也是所有图片格式里最老实的一个。它不压缩,不耍花招,像素数据怎么来就怎么存。我刚开始做嵌入式时,最喜欢用BMP做测试——因为它好解析,一眼就能看出问题在哪。

BMP的文件结构

一个BMP文件由四部分组成,咱们从上往下看:

区块 大小 内容说明
BMP文件头 14字节 文件类型标识(0x4D42 = "BM")、文件大小、数据偏移
位图信息头 40字节(常见) 宽度、高度、颜色深度、压缩方式
调色板 可选(仅索引色模式) 颜色查找表,每个颜色4字节(BGR+保留)
像素数据 剩余部分 从下到上、从左到右存储的BGR像素

小技巧:BMP的像素数据是从左下角开始存的,跟咱们平时从上往下读的习惯相反。解析时记得把行顺序倒过来,否则图片会上下颠倒。我第一回就栽在这上面了。

颜色深度与存储方式

BMP支持多种颜色深度,但嵌入式里常用的就两种:

  • 24位真彩色:每个像素3字节(BGR),没有透明通道。文件大小 = 宽 × 高 × 3 + 头开销。
  • 32位带Alpha:每个像素4字节(BGRA),多了透明通道。但注意——很多嵌入式解码库不支持BMP的Alpha,存了也白存。

举个例子,一张320×240的24位BMP图片:

文件大小 ≈ 320 × 240 × 3 + 54(头)
         = 230,400 + 54
         ≈ 225 KB

你看,一张小图就200多KB。要是换成800×480的屏幕,一张全屏BMP直接飙到1.1MB。这在单片机上基本是灾难。

注意:BMP在LVGL里通常只适合做小图标或调试用。正式产品里,我建议你把BMP转成LVGL内置的C数组格式(LV_IMG_CF_TRUE_COLOR),省去解码开销。

2.2 JPEG格式:以小博大的有损压缩

JPEG这玩意儿,说白了就是牺牲画质换体积。它能把一张几MB的照片压到几百KB,人眼还看不太出来。但代价是什么?解码复杂,而且不支持透明。

JPEG的核心特点

  • 有损压缩:丢弃人眼不敏感的高频细节,压缩比可达10:1甚至更高。
  • 不支持透明通道:JPEG天生没有Alpha,做UI时背景只能是矩形。
  • 解码开销大:需要做哈夫曼解码、反量化、逆DCT变换,MCU资源吃紧。

我记得有一次做智能家居面板,客户非要放一张高清风景图做壁纸。我用了JPEG,压缩到80KB,效果还行。但解码花了将近200ms,换页时明显卡顿。后来改成预解码成RGB565存到外部Flash,才解决了问题。

JPEG在LVGL中的使用建议

场景 建议 原因
全屏背景图 可用JPEG,但建议预解码 节省Flash空间,但解码慢
小图标/按钮 不要用JPEG 压缩比不高,且不支持透明
照片/渐变图 JPEG首选 人眼对高频不敏感,压缩效果好

个人经验:LVGL自带JPEG解码库(TJpgDec),但只支持基线JPEG,不支持渐进式。做图时记得存成"标准"格式,别选"优化"或"渐进",否则解码会崩。

2.3 PNG格式:无损但吃内存的优等生

PNG是我在UI开发里用得最多的格式。为什么?因为它支持透明通道,而且无损。但说实话,PNG在嵌入式里并不好伺候。

PNG的优势

  • 无损压缩:用Deflate算法压缩,解压后跟原图一模一样。
  • Alpha透明通道:支持0-255级透明度,做圆角按钮、阴影效果必备。
  • 色彩深度灵活:支持索引色、灰度、真彩色,甚至48位深色。

PNG的局限

  • 解码需要大量RAM:PNG解码器需要一次性读入整张图片的数据,然后逐行解压。一张800×480的32位PNG,光像素数据就占1.5MB RAM。
  • 解码速度慢:Deflate解压加上滤镜处理,在低主频MCU上可能耗时数百毫秒。
  • 文件体积不一定小:对于照片类图片,PNG可能比JPEG大3-5倍。

避坑指南:我曾经在一个项目里用了大量PNG图标,结果内存不够,系统频繁重启。后来我把所有PNG都转成了LVGL的C数组格式(LV_IMG_CF_TRUE_COLOR_ALPHA),解码开销直接归零。记住——在嵌入式里,预解码永远比实时解码靠谱。

2.4 三种格式的横向对比

咱们用一张表把它们的优缺点说清楚:

对比项 BMP JPEG PNG
压缩方式 无压缩 有损压缩 无损压缩
透明通道 部分支持(32位) 不支持 完美支持
解码复杂度 极低 高 中高
解码RAM需求 低 中 高
文件体积 大 小 中
LVGL推荐场景 调试/小图标 背景图(预解码) UI元素(转C数组)

你想想看,如果你的MCU只有512KB RAM,你敢用PNG做实时解码吗?反正我不敢。我一般的原则是:

  • 小图标(< 64×64):直接用BMP转C数组,简单粗暴。
  • 全屏背景:用JPEG预解码成RGB565,存外部Flash。
  • 带透明的UI元素:用PNG转C数组(带Alpha),解码开销为零。