1、资源压缩概述:为什么嵌入式GUI需要资源压缩?

做嵌入式GUI开发的朋友,十有八九都遇到过同一个问题——Flash不够用了。我最早做的一款产品,MCU只有2MB Flash,光一套中文字库就占了1.2MB,剩下的空间连一张全屏背景图都塞不进去。那时候我才真正意识到:资源压缩不是锦上添花,而是刚需。

说白了,嵌入式系统的资源是「挤」出来的。你想想看,一个典型的智能家居面板,可能同时要放字体、图标、背景图、动画帧、音频提示音,甚至还有多语言字库。这些东西如果不压缩,再大的Flash也扛不住。

为什么嵌入式GUI必须做资源压缩?

原因其实就三个字:成本。我换个角度给你拆开讲。

  • Flash容量有限:大部分MCU的Flash在512KB到8MB之间。一张800×480的24位BMP图,不压缩是1.1MB,直接吃掉四分之一。你想想看,一个产品要放几十张图,不压缩根本不可能。
  • RAM更金贵:很多GUI框架会把解码后的资源缓存到RAM里。一张图解压后可能占几百KB,而MCU的RAM通常只有几十到几百KB。我见过一个项目,因为没做压缩,UI切换时直接内存溢出复位。
  • 带宽瓶颈:尤其是SPI Flash或者外部存储,读取速度有限。资源体积越大,界面加载越慢。用户按一下按钮等两秒才出画面,这产品基本就废了。

核心观点:资源压缩的本质,是用「计算时间」换「存储空间」。嵌入式GUI的压缩策略,就是在Flash容量、RAM占用、解码速度和UI响应之间找到平衡点。

常见资源类型及压缩目标

嵌入式GUI里需要压缩的资源,我按类型分成了四大类。每一类的压缩目标和手段都不一样,咱们一个一个看。

资源类型 典型体积 压缩目标 常用方法
图片 最大,占总量60%~80% 减少存储体积,控制解码RAM JPEG压缩、PNG量化、索引色、RLE
字体 较大,尤其是中文 减少字库体积,按需加载 子集化、点阵压缩、矢量转描边
音频 中等 降低码率,减少Flash占用 ADPCM、μ-law、MP3低码率
布局文件 较小,但数量多 减少解析开销,合并存储 JSON压缩、二进制序列化、模板复用

图片资源——压缩大头

图片是嵌入式GUI里最占空间的资源,没有之一。我做过一个统计:一个中等复杂度的HMI项目,图片资源占了总Flash的70%以上。

常见的图片格式有这些:

  • BMP:完全不压缩,直接存像素数据。优点是解码快,缺点是体积巨大。我一般只在调试阶段用,正式产品从来不用。
  • JPEG:有损压缩,适合照片和渐变背景。压缩比可以到10:1甚至更高。但解码需要RAM,而且MCU软解JPEG比较慢。
  • PNG:无损压缩,适合图标和界面元素。支持透明通道,但解码复杂度比JPEG还高。
  • 索引色图:把真彩色降到256色甚至16色,体积直接缩到原来的三分之一。我特别喜欢用这种方式做图标,效果几乎看不出区别。

个人经验:对于图标和按钮这类小图,我习惯用「索引色+RLE」组合压缩。先把颜色降到16色,再用游程编码压缩。这样解码只需要几行代码,RAM占用也极小。我在一个STM32F4的项目上,用这个方法把图标资源从800KB压到了120KB。

字体资源——中文的痛

字体压缩是嵌入式GUI里最头疼的问题。尤其是中文字库,一个完整的GB2312字库(6763个字),用16×16点阵存,大概要2MB左右。如果用矢量字体,体积更大。

我常用的字体压缩策略:

  • 子集化:只包含界面用到的字符。比如一个菜单界面,可能只需要几十个汉字。我做过一个项目,全字库1.2MB,子集化之后只有48KB。
  • 点阵压缩:对点阵字模做RLE或LZSS压缩。16×16的点阵数据是32字节,压缩后可能只有10~15字节。
  • 矢量转描边:把矢量字体转成描边轮廓,只存关键点。适合大字号显示,体积可以压缩到原来的20%。

避坑指南:我曾经在一个产品里直接用全字库,结果Flash塞不下,临时换大容量MCU,交期延误了两周,成本还涨了30%。从那以后,我每个项目的第一件事就是统计界面用到的字符数,然后做子集化。这个习惯一直保持到现在。

音频资源——被忽视的压缩点

很多人做GUI的时候只关注图片和字体,忽略了音频。其实提示音、按键音、报警音加起来,体积也不小。

音频压缩的几个方向:

  • ADPCM:自适应差分脉冲编码调制,压缩比4:1,解码极简单,MCU软解毫无压力。
  • μ-law/A-law:对数压缩,压缩比2:1,适合语音提示。
  • 低码率MP3:压缩比可以到10:1以上,但解码需要专门的芯片或软件库。

我个人最推荐ADPCM。原因很简单:解码代码只有几十行,不占RAM,而且音质对于提示音来说完全够用。

布局文件——小而多的问题

布局文件虽然单个体积不大,但数量一多,管理起来就很麻烦。一个复杂的界面可能有几十个JSON或XML文件,加起来也有几百KB。

压缩手段:

  • JSON压缩:去掉空格和换行,用短键名。比如把"background_color"改成"bg"。
  • 二进制序列化:把JSON转成二进制结构体,体积能再缩30%~50%。
  • 模板复用:相同的布局结构只存一份,不同界面只存差异数据。

知识体系总览

下面这张图,是我梳理的嵌入式GUI资源压缩的完整知识框架。你可以把它当作整个课程的地图。

嵌入式GUI资源压缩知识体系 嵌入式GUI资源压缩知识体系 资源压缩核心目标 图片资源 字体资源 音频资源 布局文件 JPEG / PNG / 索引色 RLE / LZSS / 调色板 子集化 / 点阵压缩 矢量转描边 / 差量存储 ADPCM / μ-law 低码率MP3 / 采样率降频 JSON压缩 / 二进制序列化 模板复用 / 差量更新 核心原则:用计算时间换存储空间,在Flash、RAM、解码速度之间找平衡 不同资源类型采用不同压缩策略,没有万能方案 Flash占用 ↓ RAM占用 ↓ 加载速度 ↑ 压缩率不是越高越好,要综合考虑解码速度和运行时开销

压缩目标的三维平衡

做资源压缩,不能只盯着压缩率看。我见过有人把图片压到极致,结果解码时间长了10倍,界面卡得没法用。所以压缩目标其实有三个维度:

  1. 存储体积:这是最直观的目标,越小越好。
  2. 解码速度:压缩后的资源需要解压才能用,解码不能太慢。我一般要求单张图解码不超过50ms。
  3. 运行时RAM:有些压缩算法需要额外的RAM做解码缓冲区。对于只有几十KB RAM的MCU,这个开销必须严格控制。

这三个目标往往是互相矛盾的。压缩率越高,解码越慢,RAM开销也可能越大。所以实际项目中,要根据硬件资源做取舍。

我的做法:拿到一个新项目,我第一件事就是看MCU的Flash和RAM规格。Flash小于1MB的,优先考虑有损压缩;RAM小于64KB的,尽量用不需要额外缓冲区的压缩算法。比如索引色+RLE,解码只需要一个像素的缓冲区,几乎不占RAM。