图片格式基础: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. 嵌入式场景的选型决策树

你想想看,拿到一张图片,怎么选格式?我一般按这个逻辑走:

  1. 需要透明效果吗? → 是 → PNG(8bit 索引色 + Alpha 优先)
  2. 是照片或渐变背景吗? → 是 → JPEG(质量 85% 左右,平衡体积和画质)
  3. 需要动画吗? → 是 → GIF(256 色以内)或 PNG 序列帧(颜色丰富时)
  4. 是纯色图标或文字吗? → 是 → BMP 或 PNG 索引色
  5. RAM 极小(<64KB)? → 避免 JPEG,优先 BMP 或 PNG 逐行解码

一句话总结:BMP 做底图、JPEG 做背景、PNG 做 UI、GIF 做动画。但具体怎么用,还得看你 MCU 的 Flash 大小、RAM 余量和解码器实现。

7. 知识体系结构图

下面这张图帮你理清四种格式的核心差异和选型路径:

嵌入式 GUI 图片格式选型结构图 图片格式选型 BMP 无压缩 JPEG 有损压缩 PNG 无损压缩 GIF 256色动画 解码快 体积大 适合小图标/启动图 无透明通道 体积小 解码慢 适合照片背景 无透明通道 体积适中 有透明 适合 UI 元素 支持 Alpha 通道 颜色少 支持动画 适合简单动效 256 色限制 选型建议:UI 用 PNG 8bit + Alpha,背景用 JPEG 85% 质量

8. 实战中的几个小建议

  • 能用索引色就别用真彩色:PNG 8bit 索引色 + Alpha 通道,在大多数 UI 场景下画质完全够用,体积只有 24bit 的 1/3。
  • JPEG 质量别设太高:85% 和 95% 在嵌入式小屏幕上几乎看不出区别,但体积差一倍。我一般固定 85%。
  • GIF 动画慎用:解码需要逐帧合成,内存占用大。如果只是简单旋转或闪烁,用代码画比用 GIF 省资源得多。
  • BMP 只做临时缓冲:别把 BMP 存到 Flash 里,除非你的图小于 32×32。否则 Flash 很快就满了。

最后说一句:格式选型只是第一步。下一层是解码器优化、缓存策略、DMA 加速……这些咱们后面章节慢慢聊。今天先把基础打牢,后面调优时才不会跑偏。