图片格式基础:BMP、JPEG、PNG、GIF 原理与选型
做嵌入式 GUI 这几年,我踩过最多的坑就是图片格式选型。选对了,内存占用砍半、刷屏速度翻倍;选错了,动不动就 OOM 或者界面卡成幻灯片。今天咱们就把 BMP、JPEG、PNG、GIF 这四种最常用的格式掰开揉碎聊清楚。
核心观点:嵌入式 GUI 里没有「最好的格式」,只有「最适合当前场景的格式」。理解每种格式的压缩原理,你才能做出正确的取舍。
1. BMP —— 简单粗暴的「裸数据」
BMP 是 Windows 时代的遗产格式。说白了,它就是像素数据的直接转储。每个像素占多少 bit,文件里就存多少 bit,几乎不做任何压缩。
原理:文件头 + 信息头 + 调色板(可选)+ 像素阵列。像素按行从下往上或从上往下排列,每个像素的 RGB 值直接写入。
| 特性 | 说明 |
|---|---|
| 压缩率 | 无压缩(或 RLE 简单压缩,极少用) |
| 解码速度 | 极快,几乎零开销 |
| 内存占用 | 与分辨率成正比,24bit 图每像素 3 字节 |
| 适用场景 | 小尺寸图标、启动 Logo、无需压缩的临时帧缓冲 |
我的经验:在 STM32F429 上做 480×272 的 BMP 全屏图,直接解码到显存只需要一次 memcpy,延迟不到 1ms。但同样尺寸的 JPEG 解码要 30ms 以上。所以启动画面我从来不用 JPEG。
注意:BMP 文件体积巨大。一张 1024×768 的 24bit 图,体积是 1024×768×3 ≈ 2.25MB。在 Flash 只有 8MB 的 MCU 上,你存不了几张。
2. JPEG —— 有损压缩的「照片专家」
JPEG 专为自然图像设计。它利用人眼对亮度敏感、对色度不敏感的特性,扔掉大量人眼不易察觉的高频信息。
原理:颜色空间转换(RGB→YCbCr)→ 下采样(4:2:0 或 4:2:2)→ 分块(8×8)→ DCT 变换 → 量化 → 熵编码(Huffman)。
量化这一步是关键。量化表里的系数越大,丢掉的高频信息越多,压缩率越高,画质越差。
| 特性 | 说明 |
|---|---|
| 压缩率 | 极高,通常 10:1 ~ 20:1 |
| 解码速度 | 慢,需要 IDCT + 反量化 + 颜色转换 |
| 内存占用 | 解码时需要至少 3 个 8×8 块缓冲区 + 整图缓冲区 |
| 适用场景 | 照片背景、大尺寸自然图像、资源受限时的存储优化 |
我曾经踩过的坑:在 ESP32 上用 JPEG 做全屏壁纸,解码时 malloc 了 300KB 的缓冲区,结果堆碎片导致后续任务分配失败。后来改用 JPEG 逐行解码 + 分块渲染才解决。记住:JPEG 解码器需要大量临时内存,小 RAM 设备慎用。
3. PNG —— 无损压缩的「全能选手」
PNG 是 GIF 的替代者,支持真彩色、Alpha 通道、无损压缩。它用 DEFLATE 算法(LZ77 + Huffman)压缩像素数据。
原理:PNG 先对像素行做「过滤」(Filter),让相邻像素的差值变小,再用 DEFLATE 压缩。过滤有 5 种模式:None、Sub、Up、Average、Paeth。解码器需要逐行解压并反向过滤。
| 特性 | 说明 |
|---|---|
| 压缩率 | 中等,比 BMP 小但比 JPEG 大 |
| 解码速度 | 中等,需要 zlib 解压 + 反向过滤 |
| Alpha 通道 | 支持 8bit 透明度,完美抗锯齿 |
| 适用场景 | 图标、按钮、UI 元素、需要透明效果的图片 |
我个人的习惯:所有 UI 图标都用 PNG 8bit 索引色 + Alpha 通道。颜色数少,压缩率高,解码快。一张 32×32 的图标用 PNG 8bit 只有 1KB 左右,比 BMP 24bit 的 3KB 小得多。
4. GIF —— 动画的「老前辈」
GIF 只有 256 色,用 LZW 压缩。它最大的价值是支持多帧动画。但颜色数限制让它在现代 UI 中越来越边缘化。
原理:全局调色板(最多 256 色)+ 逐帧图像 + 帧间控制(延迟时间、透明色、帧替换方式)。LZW 压缩对重复像素序列效果好,但对照片类图像效率很低。
| 特性 | 说明 |
|---|---|
| 颜色数 | 最多 256 色 |
| 动画支持 | 原生支持,帧间可设置延迟 |
| 解码速度 | 中等,LZW 解压 + 帧合成 |
| 适用场景 | 简单动画、加载指示器、低色深小动画 |
注意:GIF 的 256 色限制在渐变背景上会出现明显的色带。我在一个温控器项目里用过 GIF 做动画温度计,结果色带让界面显得很廉价。后来改用 PNG 序列帧 + 软件混合才解决。
5. 四种格式的横向对比
| 维度 | BMP | JPEG | PNG | GIF |
|---|---|---|---|---|
| 压缩类型 | 无压缩 | 有损 | 无损 | 无损(LZW) |
| 颜色深度 | 1~32bit | 24bit | 1~48bit | 8bit(256色) |
| Alpha 通道 | 不支持 | 不支持 | 支持 | 1bit 透明 |
| 解码复杂度 | 极低 | 高 | 中 | 中 |
| 存储效率 | 差 | 优 | 良 | 中 |
| 嵌入式适用性 | 小尺寸、无压缩场景 | 照片、大图存储 | UI 元素、透明图 | 简单动画 |
6. 嵌入式场景的选型决策树
你想想看,拿到一张图片,怎么选格式?我一般按这个逻辑走:
- 需要透明效果吗? → 是 → PNG(8bit 索引色 + Alpha 优先)
- 是照片或渐变背景吗? → 是 → JPEG(质量 85% 左右,平衡体积和画质)
- 需要动画吗? → 是 → GIF(256 色以内)或 PNG 序列帧(颜色丰富时)
- 是纯色图标或文字吗? → 是 → BMP 或 PNG 索引色
- RAM 极小(<64KB)? → 避免 JPEG,优先 BMP 或 PNG 逐行解码
一句话总结:BMP 做底图、JPEG 做背景、PNG 做 UI、GIF 做动画。但具体怎么用,还得看你 MCU 的 Flash 大小、RAM 余量和解码器实现。
7. 知识体系结构图
下面这张图帮你理清四种格式的核心差异和选型路径:
8. 实战中的几个小建议
- 能用索引色就别用真彩色:PNG 8bit 索引色 + Alpha 通道,在大多数 UI 场景下画质完全够用,体积只有 24bit 的 1/3。
- JPEG 质量别设太高:85% 和 95% 在嵌入式小屏幕上几乎看不出区别,但体积差一倍。我一般固定 85%。
- GIF 动画慎用:解码需要逐帧合成,内存占用大。如果只是简单旋转或闪烁,用代码画比用 GIF 省资源得多。
- BMP 只做临时缓冲:别把 BMP 存到 Flash 里,除非你的图小于 32×32。否则 Flash 很快就满了。
最后说一句:格式选型只是第一步。下一层是解码器优化、缓存策略、DMA 加速……这些咱们后面章节慢慢聊。今天先把基础打牢,后面调优时才不会跑偏。