1. 字体基础概念:点阵字体与矢量字体的区别、字库结构解析、字符编码在嵌入式中的应用

做嵌入式GUI,绕不开字体。我刚开始搞这块时,也以为字体就是“把字画出来”那么简单。结果第一个项目就栽了跟头——屏幕显示中文全是乱码,查了三天才发现是编码没对齐。今天咱们就把字体这块地基打牢。

1.1 点阵字体 vs 矢量字体

先说点阵字体。说白了,就是用一堆小方格拼出字形。每个像素要么亮要么灭,简单粗暴。

点阵字体的核心特点:

  • 每个字符对应一个固定大小的位图矩阵
  • 显示速度快,直接内存映射即可
  • 缩放会锯齿严重,放大就糊
  • 存储空间固定,16×16点阵一个汉字占32字节

矢量字体就不一样了。它存的是轮廓的数学描述——贝塞尔曲线、直线段这些。我记得第一次用FreeType渲染一个24pt的宋体,看到那个平滑的曲线,确实被惊艳到了。

对比项 点阵字体 矢量字体
存储方式 像素位图 数学曲线描述
缩放效果 锯齿明显 平滑无损
渲染速度 极快(直接拷贝) 较慢(需计算轮廓)
内存占用 固定,随字号增大 相对固定
典型应用 低端MCU、OLED屏 高端MPU、Linux设备
常用工具 PCtoLCD、FontCreator FreeType、stb_truetype

我的经验:在STM32F103这类资源紧张的MCU上,我一般用12×12或16×16点阵。跑Linux的板子,比如全志V3s,直接上FreeType渲染矢量字体,效果完全不一样。

1.2 字库结构解析

字库不是简单地把所有字堆在一起。它有一套组织方式。我拆过一个点阵字库,结构大概是这样的:

// 点阵字库文件结构示例
typedef struct {
    uint16_t font_height;      // 字高
    uint16_t font_width;       // 字宽
    uint16_t char_count;       // 字符总数
    uint16_t first_char;       // 起始编码
    uint8_t  data[0];          // 位图数据
} FontHeader;

// 每个字符的位图数据排列
// 16×16点阵:每行2字节,共16行 = 32字节
// 字节内高位对应左侧像素

矢量字库的结构更复杂。TrueType字体文件里,有cmap表(字符映射)、glyf表(字形轮廓)、head表(全局信息)等等。我建议初学者先别钻太深,会用FreeType的API就够了。

注意:点阵字库的字节序问题。我踩过坑——在ARM大端和小端之间移植字库,显示全乱了。后来统一用小端存储,并在加载时做字节序判断。

1.3 字符编码在嵌入式中的应用

编码这块,是嵌入式GUI最容易出问题的地方。咱们一个一个说。

ASCII

7位编码,0x00-0x7F,128个字符。英文字母、数字、标点。在嵌入式里,ASCII基本不占空间,一个字节一个字符。但只能处理英文。

GB2312

国标简体中文编码。一个汉字占两个字节,范围从0xA1A1开始。我最早做中文菜单时用的就是GB2312。但有个问题——它和ASCII混在一起时,程序得判断当前字节是ASCII还是汉字的一部分。

// GB2312字符判断示例
int is_gb2312_char(uint8_t byte1, uint8_t byte2) {
    // 第一个字节在0xA1-0xF7之间
    // 第二个字节在0xA1-0xFE之间
    return (byte1 >= 0xA1 && byte1 <= 0xF7) &&
           (byte2 >= 0xA1 && byte2 <= 0xFE);
}

Unicode

统一编码,一个字符一个码点。嵌入式里常用UTF-8编码——变长存储,ASCII字符还是1字节,中文变成3字节。我建议新项目直接用UTF-8,省去编码转换的麻烦。

编码方式 英文字符 中文字符 嵌入式适用性
ASCII 1字节 不支持 仅英文场景
GB2312 1字节 2字节 中文菜单、低端设备
UTF-8 1字节 3字节 通用、推荐新项目
UTF-16 2字节 2字节 Windows CE、部分RTOS

核心建议:如果你的MCU有256KB以上Flash,直接上UTF-8+矢量字体。如果资源紧张,用GB2312点阵字库,但要做好编码转换层。

1.4 知识体系总览

下面这张图,把本章的核心知识点串起来了。我画图时习惯把“编码”放在中间——因为它是连接“字库”和“显示”的桥梁。

嵌入式字体显示知识体系 字符编码 点阵字体 固定像素矩阵 速度快,内存固定 缩放锯齿 矢量字体 贝塞尔曲线描述 缩放平滑 渲染计算量大 字库结构 点阵字库:位图数组 矢量字库:cmap/glyf表 编码映射表 嵌入式字体显示知识体系 ASCII GB2312 Unicode

这张图里,编码是核心枢纽。左边是点阵体系,右边是矢量体系,下面是字库结构。你想想看,不管用哪种字体,最终都要通过编码找到对应的字形数据。这个逻辑搞清楚了,后面做字体显示校准就顺了。

一个小技巧:做字库时,我习惯把ASCII和中文分开存储。ASCII用8×16点阵,中文用16×16点阵。这样英文显示紧凑,中文清晰,内存也省。