FreeType矢量字符库的介绍
FreeType 是一个开源的跨平台字体引擎库,广泛用于 Linux 嵌入式系统中实现字符显示的功能。它提供了高效的 TrueType、OpenType 和其他字体格式的解析和渲染功能,在嵌入式开发中尤其适合用来绘制矢量字体和位图字体。
FreeType 的特点
-
多字体格式支持:
- 支持 TrueType (TTF)、OpenType (OTF)、PostScript 字体等主流字体格式。
- 可解析嵌入式字体文件或通过文件系统加载字体。
-
高可移植性:
- FreeType 是纯 C 实现,可以轻松移植到各种嵌入式平台。
-
轻量级:
- 设计简洁,适用于资源受限的嵌入式设备。
-
渲染能力强:
- 支持矢量字体的抗锯齿渲染。
- 支持位图字体的缩放和变形。
- 提供字形的详细轮廓信息,可用于高级排版需求。
使用 FreeType 的流程
FreeType 的基本用法分为以下几个步骤:
-
初始化 FreeType 库:
使用FT_Init_FreeType()函数创建并初始化库对象。 -
加载字体:
使用FT_New_Face()加载字体文件,创建一个字体 face 对象。 -
设置字体大小:
调用FT_Set_Pixel_Sizes()或FT_Set_Char_Size()设置字符的像素大小。 -
加载字符字形:
使用FT_Load_Char()加载单个字符,并获取它的字形信息。 -
渲染字形:
调用FT_Render_Glyph()将字形转换为位图数据,随后可以将位图数据绘制到显示设备。 -
释放资源:
释放 face 和库对象。
示例代码
以下是一个简单的使用 FreeType 渲染字符的示例代码:
#include <ft2build.h>
#include FT_FREETYPE_H
void render_text(const char *font_path, const char *text, int font_size) {
FT_Library library;
FT_Face face;
// 初始化 FreeType 库
if (FT_Init_FreeType(&library)) {
printf("Could not initialize FreeType library\n");
return;
}
// 加载字体
if (FT_New_Face(library, font_path, 0, &face)) {
printf("Could not load font\n");
FT_Done_FreeType(library);
return;
}
// 设置字体大小
FT_Set_Pixel_Sizes(face, 0, font_size);
// 遍历渲染每个字符
for (const char *p = text; *p; p++) {
if (FT_Load_Char(face, *p, FT_LOAD_RENDER)) {
printf("Could not load character '%c'\n", *p);
continue;
}
// 获取字形位图
FT_GlyphSlot slot = face->glyph;
FT_Bitmap bitmap = slot->bitmap;
// 处理 bitmap 数据(例如绘制到屏幕)
printf("Rendered character '%c' with width %d and height %d\n",
*p, bitmap.width, bitmap.rows);
}
// 释放资源
FT_Done_Face(face);
FT_Done_FreeType(library);
}
嵌入式开发中的注意事项
-
字体资源的管理:
- 嵌入式设备通常存储空间有限,应选择合适的字体文件(例如简化版的 TTF 文件)。
- 可以将字体文件存储在只读文件系统或嵌入程序中。
-
字符渲染的性能优化:
- 避免频繁加载字体文件。
- 可预渲染常用字符到缓存中以减少实时渲染的开销。
-
显示设备适配:
- FreeType 输出的是字形的位图数据,需要结合显示设备的驱动将其绘制到屏幕上。
-
国际化支持:
- FreeType 提供 Unicode 支持,可以方便地显示多语言文本。
- 开发时需注意字符编码的转换。
FreeType使用 1/64 像素作为其基本度量单位
FreeType 中单位采用 1/64 像素 的原因主要与字体渲染的精度和字体设计的本质相关。以下是详细介绍:
1. 为什么 FreeType 使用 1/64 像素?
(1) 子像素精度
FreeType 的内部单位是 1/64 像素,这是一种固定点表示法,允许在渲染过程中达到亚像素精度。具体原因如下:
-
字体轮廓是矢量图形
字体在设计时是矢量形式(例如,TrueType 或 OpenType 字体中的点阵描述)。这些轮廓需要高精度的计算,才能正确地生成字符的形状和位置。 -
避免精度损失
如果直接用像素单位(整数)表示位置或尺寸,可能会因为舍入而导致偏移或变形。例如:- 在移动字符时,小数部分会被丢弃。
- 字符的位置可能会因为累积的误差而错位。
使用 1/64 像素能够表示更精细的位置和尺寸。
-
适配各种 DPI
显示设备的分辨率(DPI)各不相同。例如,普通显示器通常为 96 DPI,而高分辨率设备(如 Retina 显示屏)可能超过 300 DPI。通过使用 1/64 像素,FreeType 能够更灵活地适配不同的分辨率。
(2) 适配 FreeType 的固定点计算模型
FreeType 中广泛使用 固定点数值(Fixed Point)而不是浮点数。
- 固定点表示
- 整数部分:表示像素。
- 小数部分:1 像素被分为 64 个子单位。
- 例如,
1.5 像素表示为96(1 * 64 + 32)。
- 这种表示方法比浮点运算更高效且易于移植,特别是在嵌入式环境中,避免了对硬件浮点运算单元的依赖。
2. 具体如何用 1/64 像素表示?
在 FreeType 中,坐标值和尺寸通常以 FT_Pos 类型表示,单位为 1/64 像素:
-
表示方法
1 像素等于 64 单位,0.5 像素等于 32 单位,依此类推。
例如:pen.x = 10 像素→pen.x = 10 * 64 = 640pen.y = 15.25 像素→pen.y = 15.25 * 64 = 976
-
在代码中的体现
pen.x = ptFontBitMap->iCurOriginX * 64; pen.y = ptFontBitMap->iCurOriginY * 64;将整数像素坐标转换为 1/64 像素的单位。
3. 1/64 像素对渲染的好处
(1) 亚像素定位
字符的起始位置不必严格对齐像素网格(整数像素),可以在像素之间调整位置,达到更平滑的显示效果。例如:
- 在 10.25 像素处绘制字符,比单纯绘制在 10 像素处更精准。
(2) 减少视觉伪影
在低分辨率设备上,舍入误差可能导致字符不均匀或出现锯齿。通过 1/64 像素的精度,可以对字符的位置和形状进行微调,减少伪影。
(3) 增强旋转和缩放的效果
矢量字体支持任意缩放和旋转。1/64 像素的单位可以更精确地计算旋转后的坐标或缩放后的尺寸,避免字体失真。
4. 与嵌入式系统的关系
在嵌入式系统中,资源有限(如 CPU 性能较低,缺乏浮点支持)。1/64 像素单位具有以下优势:
-
节省资源
使用固定点表示法,比浮点数计算更快,同时占用更少的存储空间。 -
高精度渲染
即使设备分辨率较低,1/64 像素的精度仍能提供平滑的字符显示。 -
良好的跨平台性
不依赖浮点运算单元,代码可以在多种硬件架构(如 ARM 架构)上高效运行。
5. 总结
FreeType 使用 1/64 像素作为单位,主要是为了:
- 提高字符定位和尺寸计算的精度。
- 支持高质量的字体渲染,特别是在旋转、缩放或低分辨率显示器上。
- 通过固定点计算优化性能,适配嵌入式环境。
这种设计平衡了精度、性能和跨平台性,非常适合嵌入式系统和需要高质量字体渲染的应用场景。
FreeType的交叉编译及安装配置
01-确认交叉编译器gcc中有zlib库和libpng库
freetype 依赖于 libpng,libpng 又依赖于 zlib,所以我们应该:先编译
安装 zlib,再编译安装 libpng,最后编译安装 freetype。
但是,有些交叉编译器工具链里已经有 zlib库和freetype,所以我们需要确认下我们的gcc编译器工具链中有没有zlib库和libpng库,怎么看呢?
看交叉编译器工具链里有没有zlib库和libpng库的方法见下面这篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/144752237
确认有zlib库和libpng库后方可进行下面的操作。
如果没有zlib库和libpng库,则两个库的源码如下:
zlib库的源码下载链接:
https://pan.baidu.com/s/1Mn7AZZbXGJm-ajK04-zK3w?pwd=cmkv
libpng库的源码下载链接:
https://pan.baidu.com/s/10MG29Bw1-H88NkKlc-Aydw?pwd=kgbu
02-源码下载并解压
freetype-2.10.2源码下载地址:
https://pan.baidu.com/s/1rcAnUxwmugiMrNuR4UDsCg?pwd=hr5u
源码下载好后放到目录/home/book/usedlib下

然后解压:

说明:以下编译、配置、安装过程参考我的另一篇博文:
tslib(触摸屏输入设备的轻量级库)的学习、编译及测试记录
03-编译配置(生成Makefile文件)
cd /home/book/usedlib/freetype-2.10.2
./configure --host=arm-buildroot-linux-gnueabihf --prefix=/

04-make及make install
首先确认交叉编译器gcc中有zlib库和libpng库
freetype 依赖于 libpng,libpng 又依赖于 zlib,所以我们应该:先编译
安装 zlib,再编译安装 libpng,最后编译安装 freetype。
但是,有些交叉编译器工具链里已经有 zlib库和freetype,所以我们需要确认下我们的gcc编译器工具链中有没有zlib库和libpng库,怎么看呢?
看交叉编译器工具链里有没有zlib库和libpng库的方法见下面这篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/144752237
确认有zlib库和libpng库后方可进行make的操作。
如果没有zlib库和libpng库,则两个库的源码如下:
zlib库的源码下载链接:
https://pan.baidu.com/s/1Mn7AZZbXGJm-ajK04-zK3w?pwd=cmkv
libpng库的源码下载链接:
https://pan.baidu.com/s/10MG29Bw1-H88NkKlc-Aydw?pwd=kgbu
确认有zlib库和libpng库后方可进行make的操作。
make

然后 make install
make install DESTDIR=$PWD/tmp


05-检查生成的动态库文件是否是运行于目标架构之上的
完成之后首先用readelf命令检查下生成的so动态库文件是否是运行于ARM架构的,有时候我们在编译配置阶段提供的gcc编译前缀是错的,但此种情况下系统make时不会报错,而会去用系统默认的编译器。
cd /home/book/usedlib/freetype-2.10.2/tmp/lib

readelf -h /home/book/usedlib/freetype-2.10.2/tmp/lib/libfreetype.so
运行结果如下:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x6c60
Start of program headers: 52 (bytes into file)
Start of section headers: 2719560 (bytes into file)
Flags: 0x5000400, Version5 EABI, hard-float ABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 5
Size of section headers: 40 (bytes)
Number of section headers: 35
Section header string table index: 34

从结果截图可以清晰看出这个ELF的动态库文件是32位还是64位,并且还是运行于ARM架构上的动态库文件。
06-关于头文件和库文件的部署安装
头文件放到指定目录
关于下面放置头文件的详细操作,可参考:
https://blog.youkuaiyun.com/wenhao_ir/article/details/144844740
对于freetype库而言,我的操作是把其make install获取到的头文件目录freetype和头文件ft2build.h放于下面这个目录中:
/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include/


不需要复制库文件到Ubuntu的相关目录中
在这里,我们不需要把它的库文件复制到gcc交叉编译器的伪文件系统的目录/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib中,虽然目录/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib是gcc在进行交叉编译时搜索的目录,但是为gcc在交叉编译时不会去搜索目录/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib的子目录,所以这里你把FreeType进行make install后得到的目录复制到目录/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib中也没有用。
为了交叉编译时能够使用这个库,我们只需要在交叉编译时指定库目录就行了,比如下面的命令:
arm-buildroot-linux-gnueabihf-gcc -o my_program my_program.c -L/home/book/usedlib/freetype-2.10.2/tmp/lib -lfreetype
需要复制库文件到开发板的相关目录中
当然,为了我们编译出的程序要能在开发板上运行,那么开发板的根文件系统的库目录中也该有FreeType的动态库文件,详细的复制过程这里就略过了。
我只说下我们需要把库目录/home/book/usedlib/freetype-2.10.2/tmp/lib复制到开发板的目录/lib下,可以参考博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/144621008 【搜索 “按下面的操作复制tslib的库文件到开发板”】
3096

被折叠的 条评论
为什么被折叠?



