前提
这篇文章代码是韦东山老师Linux应用开发里的一个代码,具体是在01_all_series_quickstart\04_嵌入式Linux应用开发基础知识\source\10_freetype\04_show_line,以下是我的一些解析,主要包括自己遇到的一些疑问和相应的解决。建议多看几遍原视频。
初始化结构体变量等
代码:
wchar_t *wstr = L"百问网www.100ask.net";
- wchar_t是一个unsigned short,两个字节
- L:表示“”使用宽字符编码”,就是把你后面字符串的每个字符都用两个字节的unicode来存储
从这里直到后面调用FT_Init_FreeType,在视频里都讲过很多遍,我没什么问题,就不说了。现在直接跳转到调用FT_Init_FreeType函数这一块
初始化library变量
error = FT_Init_FreeType( &library );
这一块是freetype一定要加的,此处error的作用如下:
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Library library;
error = FT_Init_FreeType( &library );
if ( error )
{
//失败的逻辑处理
}
- what: 初始化library结构体变量
- error的取值:
- 0(FT_Err_Ok): 成功
- 其他值: 失败
所以此处才用一个if判断是否为非0值,判断是否失败,失败会将library置为NULL
从字体文件中获取face
error = FT_New_Face( library, argv[1], 0, &face );
按官方文档,一个字体文件有多个face,通过调用FT_New_Face将face搞出来。face我粗浅地认为就是一个字体文件里的某些信息吧。
咱看一下声明中的参数:
FT_New_Face( FT_Library library,
const char* pathname,
FT_Long face_index,
FT_Face* aface )
- 你干嘛: 通过调用FT_New_Face来获取字体文件中的face
- 参数说明:
- library: 之前所说的Library结构体
- pathname:字体的路径
- face_index:字体中的第几个face,0就是第一个,简单来说就是我不知道里面有几个face(官方文档说可以查),但是第一个总是存在的。那我就取第一个就得了。
- aface :提取face后保存在哪里?保存在这个参数(指针)所指向的结构体那
总的来说就是初始化face,以后就用face去做些其他的东西。
设置字符的大小
FT_Set_Pixel_Sizes(face, font_size, 0);
看一下函数声明:
FT_Set_Pixel_Sizes( FT_Face face,
FT_UInt pixel_width,
FT_UInt pixel_height );
简单来说就是,设置face后面产生的字形的宽度和高度(单位是像素),此处如果宽度和高度有一个写为0,表示这个参数的值和另一个参数的值一样。
if ( pixel_width == 0 )
pixel_width = pixel_height;
else if ( pixel_height == 0 )
pixel_height = pixel_width;
所以此处就是设置宽度和高度都为font_size,然后这个font_size默认是24(main里的初始化),可以通过执行程序时的参数来修改。
if (argc == 5)
font_size = strtoul(argv[4], NULL, 0);
stroul函数把char类型的数字变成int类型的数字。函数声明如下:
strtoul(
_In_z_ char const* _String,
_Out_opt_ _Deref_post_z_ char** _EndPtr,
_In_ int _Radix
);
第一个参数是要转化的字符串的指针,第二个参数直接NULL就行,第三个参数是进制,0表示默认,默认是10进制。stroul的具体可看这篇文章。
display_string函数
接下来就是关于display_string函数的相关内容,主要做的就是循环地处理每个字形,然后获取每个字形的LCD坐标,调用draw_bitmap函数写到LCD映射的内存去,从而显示出来,主要是要掌握对坐标的理解还有对freetype函数的理解。
原点的坐标乘64
pen.x = (x - bbox.xMin) * 64; /* 单位: 1/64像素 */
pen.y = (y - bbox.yMax) * 64; /* 单位: 1/64像素 */
我一度很纠结这个东西,现在反正你别管,人家freetype就是要求你的像素,你的坐标要乘以64,你就乘吧。具体地话,因为freetype里边不止用像素这个单位,还有用pt(point)英镑等单位,可以表示更精细的东西,反正这就是freetype的设计,别纠结了,咱们只是用这个库而已。还有关于原点,这个视频里有讲。
此处这两行分别表示在LCD上的某个点,这个点的坐标使用的是笛卡尔坐标系,是一个绝对的坐标。主要是给后面FT_Set_Transform函数使用的。这个后面讲。
循环处理每个字形
for (i = 0; i < wcslen(wstr); i++)
{
/* 转换:transformation */
FT_Set_Transform(face, 0, &pen);
/* 加载位图: load glyph image into the slot (erase previous one) */
error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
if (error)
{
printf("FT_Load_Char error\n");
return -1;
}
/* 在LCD上绘制: 使用LCD坐标 */
draw_bitmap( &slot->bitmap,
slot->bitmap_left, //边框的左上部分
var.yres - slot->bitmap_top);
/* 计算下一个字符的原点: increment pen position */
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}
一个一个函数来看:
FT_Set_Transform函数
FT_Set_Transform(face, 0, &pen);
老规矩,看函数声明
FT_Set_Transform( FT_Face face,
FT_Matrix* matrix,
FT_Vector* delta )
-
matrix: 一个矩阵,主要是做一些旋转啊什么的变化,我不懂不重要
-
delta: 原点怎么变化?原点定在哪里?
此处的delta是指在LCD屏幕上,使用笛卡尔坐标系,原点的位置。
传入这个原点在LCD上的坐标之后,freetyep才能够计算出某个字形的左上角边框在LCD坐标系里的坐标,不然freetype得到的只是某个字形基于原点的相对位置,而这个相对位置和LCD没关系,不能作为参数给draw_bitmap函数为什么要知道LCD左上角坐标? 因为我们的draw_bitmap函数(自己写的)的实现逻辑就是从位图的左上角开始写。
FT_Load_char函数
error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
先看声明吧:
FT_Load_Char( FT_Face face,
FT_ULong char_code,
FT_Int32 load_flags )
这个函数,做了一下几件事情
- 通过char_code(unicode)查找他所对应的字形
- 把字形用FT_LOAD_RENDER的模式绘制,这个FT_LOAD_RENDER表示加载位图的意思
- 把加载好的位图+相关信息放到face->glyph.bitmap(slot->bitmap)里去
draw_bitmap函数
draw_bitmap( &slot->bitmap,
slot->bitmap_left, //边框的左上部分
var.yres - slot->bitmap_top);
- 为什么突然出现了一个slot?我在前面没有处理过他啊?他的值又是怎么变化的?
此处的slot是一个指针,类型是FT_GlyphSlot
FT_GlyphSlot slot = face->glyph;
这个类型是一个指针,指向FT_GlyphSlotRec_结构体:typedef struct FT_GlyphSlotRec_* FT_GlyphSlot;
反正你只需要知道他是个指针,此处不是建立了一个新的结构体,而是建立一个指针指向原来face里的某个东西(glyphslot),这个东西里面有我们想要的bitmap。
此处的slot->bitmap_left和slot->bitmap_top,我存有疑惑,有谁知道还请解答下,根据官方文档所说的,
Note that bitmap_left is the horizontal distance from the current pen position to the leftmost border of the glyph bitmap, while bitmap_top is the vertical distance from the pen position (on the baseline) to the topmost border of the glyph bitmap. It is positive to indicate an upwards distance.
意思大概是说left是相对于原点的距离,top是相对于原点所在的基线的垂直距离,总而言之就是说他是相对于原点的距离,但是我在调试的过程中发现他是一个绝对距离,指向的是一个字符边框的左上角。这里我就不太理解了,望大神解答。
同时需要注意这个坐标(slot->bitmap_left,slot->bitmap_top)是笛卡尔坐标系,要给lcd显示函数当参数要转为lcd的坐标系,所以使用var.yres(lcd上纵坐标上的像素点)减掉slot->bitmap_top得到实际的适用于lcd坐标系的坐标。
参考
参考文档:freetype官方教程
【硬核】韦东山:使用freetype显示一行文字
px pt sp dp dip dpi ppi ,不再傻傻分不清
pt和px有什么区别?pt和px如何转换?