4、字体压缩技术:点阵字体 vs 矢量字体,字库裁剪与子集化
字体在嵌入式GUI里,一直是个让人又爱又恨的东西。爱的是它决定了界面好不好看,恨的是它太占地方了。我见过不少项目,前期UI设计得花里胡哨,一到移植阶段,发现Flash塞不下,只能回头砍字体——那叫一个心疼。
今天咱们就聊聊字体压缩这件事。说白了,就是怎么让字体在保持可读性的前提下,把体积降下来。核心就两条路:选对字体格式,以及做字库裁剪。
4.1 点阵字体 vs 矢量字体:选哪个?
先搞清楚这两个东西的本质区别。点阵字体,就是每个字都存一张像素图。矢量字体,存的是数学曲线,渲染时再算出来。
| 对比维度 | 点阵字体 | 矢量字体 |
|---|---|---|
| 存储方式 | 每个字模的像素矩阵 | 贝塞尔曲线 + 控制点 |
| 缩放效果 | 放大后锯齿明显 | 任意缩放不失真 |
| 渲染速度 | 直接拷贝像素,极快 | 需要光栅化,较慢 |
| 内存占用 | 固定大小,与字号成正比 | 字库小,但渲染需要临时缓冲区 |
| 适用场景 | 低分辨率屏、MCU、固定字号 | 高分辨率屏、MPU、动态字号 |
我个人习惯是:如果屏幕分辨率在320x240以下,或者主控是Cortex-M系列不带硬件浮点,我首选点阵字体。为什么?因为省事。点阵字体不需要解码,直接memcpy到显存就完事了。我曾经在一个STM32F103的项目上,用12x12的点阵显示中文,整个字库才几十KB,跑得飞快。
但如果你用的是Cortex-A系列,或者屏幕分辨率上了480x800,那就老老实实用矢量字体吧。否则点阵字体会膨胀到你怀疑人生——每个字号都要存一套,16px、18px、20px……存几套下来,Flash就没了。
核心结论:资源受限选点阵,追求效果选矢量。没有绝对的好坏,只有合不合适的场景。
4.2 字库裁剪:别把整个字库都塞进去
很多新手犯的错误,就是把Windows上的字体文件直接扔到嵌入式设备里。一个完整的宋体字库,动辄十几MB,MCU根本吃不消。其实你仔细想想,你的设备上真的需要显示所有汉字吗?
字库裁剪,说白了就是只保留你真正用到的字符。我管它叫「按需取字」。具体怎么做?
- 静态裁剪:编译前确定好所有会用到的字符,生成一个最小字库。
- 动态裁剪:运行时按需加载,但需要文件系统支持,适合资源稍丰富的平台。
- 子集化:从完整字库中提取指定字符,重新打包成新的字体文件。
我记得有一次做智能家居面板,界面就显示几个数字和单位符号。客户非要支持全字库,说「万一以后要显示中文呢?」我跟他算了一笔账:全字库2MB,芯片Flash总共512KB。最后他妥协了,我们只裁剪了100多个常用字,字库缩到8KB。嗯,有时候技术问题其实是沟通问题。
小技巧:用Python脚本扫描源码中所有字符串,去重后生成字符列表,再用FontTools库做子集化。整个过程自动化,每次编译前跑一遍就行。
4.3 字库子集化实战:用工具说话
这里我以常用的FreeType + 自定义工具链为例,演示一下子集化的流程。假设我们只需要显示「温度湿度设置」这六个字。
# 使用 fonttools 做子集化
from fontTools.ttLib import TTFont
from fontTools.subset import Subsetter
font = TTFont('SourceHanSansSC-Regular.otf')
subsetter = Subsetter()
subsetter.populate(text='温度湿度设置')
subsetter.subset(font)
font.save('subset.otf')
这段代码跑完,原本5MB的思源黑体,直接缩到不到10KB。你想想看,这差距有多大。
但要注意一点:子集化后的字体文件,只能显示你指定的那几个字。如果运行时遇到没包含的字符,渲染结果就是空白或者方框。所以一定要确保字符列表覆盖全面。
避坑指南:我曾经在一个项目里,子集化时漏掉了空格和冒号。结果界面上所有时间显示都挤在一起,排查了半天才发现是字体里没有空格字符。所以记得把标点符号、数字、空格都加进去。
4.4 点阵字体的压缩技巧
如果你决定用点阵字体,压缩空间其实更大。点阵字模本质上是二值图像,每个像素只占1bit。但很多新手直接存成字节数组,白白浪费了空间。
常用的压缩方法:
- RLE(游程编码):连续相同的像素合并记录,适合大面积空白的中文字符。
- 差分编码:只记录相邻行的差异,适合笔画较细的字体。
- 字典压缩:把重复出现的字模片段做成字典,适合有大量相同偏旁部首的汉字。
我试过用RLE压缩16x16的点阵字库,压缩率大概在40%~60%之间。解压速度也很快,就是逐行扫描一下,对MCU来说几乎不占时间。
4.5 本章知识体系
下面这张图,把字体压缩的核心决策路径画出来了。你可以对照自己的项目,走一遍这个流程。
4.6 总结一下
字体压缩这件事,说白了就是做减法。选对格式省一半,裁剪字符再省一半。我见过太多项目在字体上栽跟头,其实只要提前规划好,完全可以把字库控制在几十KB以内。
最后提醒一句:不管你用哪种方案,一定要在项目初期就确定字体策略。等UI都画好了再改字体,那代价可就大了。