在嵌入式Linux图形显示系统中,字符显示是人机交互的重要组成部分。传统的点阵字库由于字体大小固定,在缩放或旋转时容易产生模糊、锯齿等问题,难以满足高分辨率LCD和灵活显示需求。为了解决上述问题,系统通常采用矢量字体(Vector Font)结合字体引擎库进行字符渲染,其中FreeType是最常用、最成熟的开源解决方案之一。

1 矢量字体的基本原理

        与点阵字体直接存储像素不同,矢量字体并不保存具体的像素信息,而是通过数学方式描述字符轮廓。一个字符的生成过程通常包括以下三个步骤:

1.1 确定关键点(Key Points)

        关键点是字符轮廓中具有代表性的拐点或控制点,用于描述字符的整体结构。例如,对于字母 “A”,其笔画的端点、转折点均属于关键点。

字符A的关键点

1.2 使用数学曲线连接关键点  

        通过贝塞尔曲线(Bezier Curve)等数学曲线,将关键点平滑连接,形成若干条封闭或半封闭的轮廓曲线。

封闭关键点

1.3 填充封闭区域内部空间,形成字符形状

        对闭合曲线包围的区域进行颜色填充,即可得到完整的字符图形。

填充封闭曲线

        由于字符仅由关键点和曲线参数描述,当字体进行放大、缩小或旋转时,只需对关键点坐标进行线性或仿射变换,字符形状仍能保持平滑、不失真。这也是矢量字体能够实现无级缩放与高质量显示的根本原因。

关键特点总结:

  • 字体不依赖固定分辨率
  • 支持任意缩放、旋转、变形
  • 显示质量由数学曲线精度决定

2 FreeType字体引擎与字体文件结构

        FreeType是一个跨平台的开源字体引擎库,支持TrueType(TTF)、TrueType Collection(TTC)等多种字体格式。其核心作用是:解析字体文件→提取字符轮廓→渲染为位图供显示设备使用

2.1 字体文件与glyph的概念

        在矢量字体文件中,每一个字符对应一个glyph(字形)。glyph内部保存了该字符的关键点、曲线信息以及轮廓结构。

        以 Windows 系统字体为例:

  • 字体文件通常位于C:\Windows\Fonts
  • .ttf.ttc 文件均为矢量字体
  • 本实验使用的新宋体simsun.ttc属于TTC字体集合文件

2.2 charmap(字符映射表)

        字体文件并不直接通过字符“外观”查找glyph,而是通过编码值映射实现:

  • 字体内部包含一个或多个Charmaps
  • 每个charmap对应一种字符编码(如UNICODE、GB2312、BIG5等)
  • 只要字体支持某种编码,就可以用该编码值查找到对应glyph

        一般情况下:

  • 现代字体几乎都支持UNICODE
  • FreeType默认推荐使用UNICODE编码

2.3 字符到位图的整体流程

一个字符从“编码”到“显示”的完整流程可以概括为:

  • 确定字符的编码值(ASCII/UNICODE/GB2312)
  • 设置字体大小(像素或点阵尺寸)
  • 通过charmap查找字符对应的glyph
  • 根据字体大小调整glyph关键点
  • 将glyph渲染为灰度位图
  • 将位图绘制到LCD帧缓冲中

2.4 freetype的官方文档与如何使用freetype库

        从https://www.freetype.org/可以下载到 “ freetype-doc 2.10.2.tar.xz”,下图中的文件就是官方文档:

        参考上图中的step1、step2、step3里的内容,可以学习如何使用freetype库,总结出以下步骤:

  1. 初始化:FT_InitFreetype
  2. 加载(打开)字体Face:FT_New_Face
  3. 设置字体大小:FT_Set_Char_Sizes或FT_Set_Pixel_Sizes
  4. 选择charmap:FT_Select_Charmap
  5. 根据编码值charcode找到glyph_index:glyph_index=FT_Get_Char_Index(face,charcode)
  6. 根据glyph_index取出glyph:FT_Load_Glyph(face,glyph_index)
  7. 转为位图:FT_Render_Glyph
  8. 移动或旋转:FT_Set_Transform
  9. 最后显示出来

        上面的5,6,7可以直接使用一个函数替代:FT_Load_Char(face, charcode, FT_LOAD_RENDER),它就可以得到位图。

int main(int argc, char **argv)
{
	wchar_t *chinese_str = L"程";

	FT_Library	  library;
	FT_Face 	  face;
	int error;
    FT_Vector     pen;
	FT_GlyphSlot  slot;
	int font_size = 24;

	if (argc < 2)
	{
		printf("Usage : %s <font_file> [font_size]\n", argv[0]);
		return -1;
	}

	if (argc == 3)
		font_size = strtoul(argv[2], NULL, 0);
		
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}

	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var\n");
		return -1;
	}

	line_width  = var.xres * var.bits_per_pixel / 8;
	pixel_width = var.bits_per_pixel / 8;
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
	fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fbmem == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}

	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);

	/* 显示矢量字体 */
	error = FT_Init_FreeType( &library );			   /* 1.初始化库 */
	/* error handling omitted */
	
	error = FT_New_Face( library, argv[1], 0, &face ); /* 2.加载或打开字体 */
	/* error handling omitted */	
	slot = face->glyph;

	FT_Set_Pixel_Sizes(face, font_size, 0);            /* 3.设置字体大小 */

	/* 确定座标:
	 */
	//pen.x = 0;
	//pen.y = 0;

    /* set transformation */
    //FT_Set_Transform( face, 0, &pen);

    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );  /* 得到位图 */
	if (error)
	{
		printf("FT_Load_Char error\n");
		return -1;
	}
	
    draw_bitmap( &slot->bitmap,
                 var.xres/2,
                 var.yres/2);

	return 0;	
}

3 使用FreeType显示单个汉字

3.1 使用wchar_t获取UNICODE编码

        在Linux C程序中,普通char*字符串保存的通常是UTF-8或GB2312编码,不能直接作为UNICODE编码传给FreeType
        为此,需要使用宽字符类型 wchar_t

wchar_t *chinese_str = L"中gif";

在ARM-Linux环境中:

  • sizeof(wchar_t) = 4
  • 每个wchar_t直接保存UNICODE码值(如 “中”→0x4E2D)

这一步是显示中文矢量字体的前提条件

3.2 FreeType 获取字符位图的核心步骤

        FreeType的使用流程高度模块化,主要包括以下步骤:

        ①初始化与字体加载

  • FT_Init_FreeType:初始化 FreeType 库
  • FT_New_Face:加载字体文件,生成 face 对象

        ②设置字体大小

  • FT_Set_Pixel_SizesFT_Set_Char_Sizes

        ③加载并渲染字符

  • FT_Load_Char(face, charcode, FT_LOAD_RENDER)
    • 查找glyph
    • 加载glyph
    • 渲染为位图(bitmap)

        渲染完成后,字符位图存放在:

face->glyph->bitmap

3.3 位图数据与LCD显示

        FreeType生成的bitmap是灰度位图

  • 每个像素占1字节
  • 取值范围:0–255
  • 表示像素强度(透明度)

        在LCD帧缓冲中显示时:

  • 可将灰度值映射到RGB颜色通道
  • 示例中直接映射到蓝色通道,因此显示为蓝色字符

4 矢量字体的旋转显示原理

        由于矢量字体基于数学模型描述,FreeType天然支持字符的旋转、缩放和平移

4.1 旋转的实现机制

        FreeType使用二维仿射变换矩阵对glyph进行变形:

  • 定义旋转角度(弧度制)
  • 构造旋转矩阵FT_Matrix
  • 通过FT_Set_Transform设置变换关系
  • 再加载并渲染glyph

        旋转操作发生在:

  • glyph→bitmap之前
  • 不影响字体质量

4.2 关键代码

        ①定义了两个变量,分别为矩阵与角度,如下图所示:

        ②设置了角度值:

angle  = ( 1.0* strtoul(argv[2], NULL, 0) / 360 ) * 3.14159 * 2;	

        代码解析:将命令行传入的第二个参数(角度值,字符串格式)转为无符号整数,再把角度制数值换算为三角函数通用的弧度制数值,最终赋值给angle

        关键运算逻辑:先用strtoulargv[2]的字符串角度值转为无符号长整型(base=0自动识别10/8/16进制);加1.0*强制浮点运算,避免整数除法精度丢失,再通过角度/360×2×π(π≈3.14159)完成角度到弧度的转换。

        ③设置矩阵、变形、加载位图:

4.3 编译与运行效果

        在程序中指定:

  • 字体文件
  • 旋转角度(如 90°)
  • 字体大小

        即可在 LCD 上显示一个旋转后的汉字矢量字体,显示效果清晰、无锯齿。

5 本节总结

  • 矢量字体通过关键点 + 曲线 + 填充描述字符形状
  • FreeType是嵌入式Linux中最常用的矢量字体引擎
  • 字符显示核心流程:编码→charmap→glyph→bitmap→LCD
  • 中文显示推荐使用 UNICODE+wchar_t
  • FreeType支持字符的缩放、旋转和平移,且不降低显示质量
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐