嵌入式 装载一个字形图像

 3.7.1 把一个字符码转换为一个字形索引

通常,一个应用程序想通过字符码来装载它的字形图像。字符码是一个特定编码中代表该字符的数值。
例如,字符码 64 代表了 ASCII 编码 中的’ A ’ 。

一个 face 对象包含一个 或多个字符 表 (charm ap) ,字符表是 用来转换字 符码到字形 索引的。例 如,很多
T r u e T ype 字 体包含两个字符表, 一个用来转换 Un icod e 字符 码到字形索引, 另一个用来转换 Apple Rom an 编
码到字形索引。这样的字体既可以用在 W i nd ow s (使用 U nic ode )和 Mac i ntos h (使用 Apple R om a n ) 。同时
要注意,一个特定的字符表可能没有覆盖完字体里面的全部字形。

当新建一个 face 对象时, 它默认选择 Un icod e 字符 表。如果字体没包含 U nicode 字符表 ,FreeT ype 会尝
试在字形名的基础上模拟一个。 注意, 如果字形名是不标准的那么模拟的字符表有可能遗漏某些字形。 对于
某些字体,包括符号字体和旧的亚洲手写字体, Un icod e 模 拟是不可能的。
我们将在稍后叙述如何寻找 face 中 特定的字符表。 现在我 们假设 face 包含至少一个 Un ico d e 字符表,并
且在调用 FT_New_Face 时已经被选中。 我们使用 FT_G et_C har _ Ind ex 把一 个 Un icod e字 符码转换为字形索引,
如下所示:

g l yp h_ in dex = FT_ Get _Ch a r_ In dex( fac e , charc o de);
这个函数会在 face 里被 选中的字符表中查找与给出的字符码对应的字形索引。如果没有字符表被选中,
这个函数简单的返回字符码。

注意, 这个函数是 FreeT ype 中罕有的 不返回错误码的函数中的一个。 然而, 当一个特定的字符码在face
中没有字形图像,函数返回 0 。按照约定,它对应一个特殊的字形图像――缺失字形,通常会显示一个框或
一个空格。

3.7.2 从 face 中装载一个字形

一旦你获得了字形索引,你便可以装载对应的字形图像。在不同的字体中字形图像存储为不同的格式。
对于固定尺寸字体格式,如 FNT 或者 PCF ,每一个图像都是一个位图。对于可伸缩字体格式,如 T r u e Type
或者 Ty p e 1 , 使 用 名为轮 廓 (outlines) 的矢量形 状 来描述每 一 个字形。 一 些字体格 式 可能有更特 殊的途径 来表
示字形 (如 MetaF o n t ― ―但这个格式不被支持) 。 幸运的, FreeT ype 2 有足 够的灵活性,可以通过一个简单的
API 支持任 何类型的字形格式。

字形图像 存 储在一个 特 别的对象 ― ―字形槽 (gly ph sl ot) 中。就如其名 所 暗示的, 一 个字形槽 只是一个简
单的容器, 它一次只能容纳一个字形图像, 可以是位图, 可以是轮廓, 或者其他。 每一个 face 对象都有一 个
字形槽对象,可以通过 face-> g lyph 来 访问。它的字段在 FT_Gly phSlo tRe c结构的文档中解释了。

通过调用 FT_Loa d_G ly p h 来装载一个字形图像到字形槽中,如下:
error = FT_Load_Glyph(
face,
g l yp h_ in dex ,
loa d_fl a gs ); / * 装载标志 ,参考下面 */
loa d_fl ags 的 值是位标志集合,是用来指示某些特殊操作的。其默认值是 FT_ L OAD_ DEF AUL T 即 0。
这个函数会设法从 face 中装载对应的字形图像:
如果找到一个对应该字形和象素尺寸的位图,那么它将会被装载到字形槽中。嵌入的位图总是比原生的
图像格式优先装载。 因为 我们假定对一个字形, 它 有更高质量的版本。 这可 以用 FT_ L OAD_ NO_ BITMAP
标志来改变。
否则, 将装载一个该字形的原生图像, 把它伸缩到当前的象素尺寸, 并且对应如 T r ueT y pe 和 Ty p e 1这些
格式,也会完成 hi nte d 操作。
字段 face-> g lyph->f orm a t 描 述了字 形槽 中存 储的 字形 图像 的格 式。 如果 它的值不 是

error = FT_Render_Glyph( face->glyph,
render_mode );
render_mode 参数是一个位标志集合, 用来指示如何渲染字形图像。 把它设为FT_RENDER_MODE_NORMAL
渲染出一个高质量的抗锯齿 (256 级灰度 ) 位图 。这是默认情况,如果你想生成黑白位图, 可以使用
FT_RENDER_MODE_MONO 标志。
一旦你生成了一个字形图像的位图,你可以通过 glyph->bitmap( 一个简单的位图描述符 ) 直接访问,同时
用 glyph->bitmap_left 和 glyph->bitmap_top 来指定起始位置。

要注意, bitmap_left 是从字形位图当前笔位置到最左边界的水平距离,而 bitmap_top是从笔位置(位于
基线)到最高边界得垂直距离。他么是正数,指示一个向上的距离。
下一部分将给出字形槽内容的更多细节,以及如何访问特定的字形信息(包括度量)。

3.7.3 使用其他字符表

如前面所说的, 当一个新 face 对象创建时, 它会寻找一个 Unicode 字符表并且选择它。 当前被选中的字
符表可以通过 face->charmap 访问。当没有字符表被选中时,该字段为 NULL 。这种情况在你从一个不含
Unicode 字符表的字体文件(这种文件现在非常罕见)创建一个新的 FT_Face 对象时发生。
有两种途径可以在 FreeType 2 中选择不同的字符表。 最轻松的途径是你所需的编码已经有对应的枚举定
义在 FT_FREETYPE_H 中,例如 FT_ENCODING_BIG5 。在这种 情况下, 你可以简 单地调用
FT_Select_CharMap ,如下:
error = FT_Select_CharMap(
face,
FT_ENCODING_BIG5 );
另一种途径是手动为 face 解析字符表。这通过 face 对象的字段 num_charmaps 和 charmaps( 注意这是复数)
来访问。 如你想到的, 前者是 face 中的字符表的数目, 后者是一个嵌入在 face 中的指向字符表的指针表 (atable
of pointers to the charmaps) 。

每一个字符表有一些可见的字段, 用来更精确地描述它, 主要用到的字段是 charmap->platform_id和
charmap->encoding_id 。这两者定义了一个值组合,以更普
通的形式用来描述该字符表。
每一个值组合对应一个特定的编码。例如组合 (3,1) 对应 Unicode 。组合列表定义在 TrueType 规范中,但
你也可以使用文件 FT_TRUETYPE_IDS_H 来处理它们,该文件定义了几个有用的常数。
要选择一个具体的编码, 你需要在规范中找到一个对应的值组合, 然后在字符表列表中寻找它。 别忘记,

一旦某个字符表被选中,无论通过 FT_Select_CharMap 还是通过 FT_Set_CharMap ,它都会在后面的
FT_Get_Char_Index 调用使用。

3.7.4 字形变换

当字形图像被装载时,可以对该字形图像进行仿射变换。当然,这只适用于可伸缩(矢量)字体格式。
简单地调用 FT_Set_Transform 来完成这个工作,如下:

error = FT_Set_Transform(
face,
&matrix,
&delta );

这个函数将对指定的 face 对象设置变换。它的第二个参数是一个指向 FT_Matrix 结 构的指针。该结构
描述了一个 2x2 仿射矩阵。第三个参数是一个指向 FT_Vector 结构的指针。该结构描述了一个简单的二维矢
量。该矢量用来在 2x2 变换后对字形图像平移。
注意,矩阵指针可以设置为 NULL ,在这种情况下将进行恒等变换。矩阵的系数是 16.16 形式的固定浮
点单位。
矢量指针也可以设置为 NULL ,在这种情况下将使用 (0, 0) 的 delta 。矢量坐标以一个象素的 1/64为单位
表示(即通常所说的 26.6 固定浮点格式)。
注意:变换将适用于使用 FT_Load_Glyph 装载的全部字形,并且完全独立于任何 hinting 处理。这意味
着你对一个 12 象素的字形进行 2 倍放大变换不会得到与 24 象素字形相同的结果(除非你禁止 hints )。


如果你需要使用非正交变换和最佳 hints ,你首先必须把你的变换分解为一个伸缩部分和一个旋转 / 剪切部分。
使用伸缩部分来计算一个新的字符象素大小,然后使用旋转 / 剪切部分来调用 FT_Set_Transform 。

同时要注意,对一个字形位图进行非同一性变换将产生错误。

3.7.5 简单的文字渲染

现在我们将给出一个非常简单的例子程序, 该例子程序渲染一个 8 位 Latin-1 文本字符串, 并且假定 face
包含一个 Unicode 字符表。该程序的思想是建立一个循环,在该循环的每一次迭代中装载一个字形图像,把

{
FT_UInt glyph_index;



glyph_index = FT_Get_Char_Index( face, text[n] );


error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
if ( error )
continue;


error = FT_Render_Glyph( face->glyph, ft_render_mode_normal);
if ( error )
continue;


my_draw_bitmap( &slot->bitmap,
pen_x + slot->bitmap_left,
pen_y - slot->bitmap_top );


pen_x += slot->advance.x >> 6;
pen_y += slot->advance.y >> 6;
}

这个代码需要一些解释:

我们定义了一个名为 slot 的句柄,它指向 face 对象的字形槽。( FT_GlyphSlot类型是一个指针)。这是为
了便于避免每次都使用 face->glyph->XXX 。
我们以 slot->advance 增加笔位置, slot->advance符合字形的步进宽度(也就是通常所说的走格

(escapement) )。步进矢量以象素的 1/64 为单位表示,并且在每一次迭代中删减为整数象素。
函数 my_draw_bitmap 不是 FreeType 的一部分,但必须由应用程序提供以用来绘制位图到目标表面。在
这个例子中,该函数以一个 FT_Bitmap 描述符的指针和它的左上角位置为参数。
Slot->bitmap_top 的值是正数,指字形图像顶点与 pen_y 的垂直距离。我们假定 my_draw_bitmap采用的
坐标使用一样的约定(增加 Y 值对应向下的扫描线)。我们用 pen_y 减它,而不是加它。

b. 精练的代码

下面的代码是上面例子程序的精练版本。 它使用了 FreeType 2 中我们还没有介绍的特性和函数, 我们将在下

pen_x = 300;
pen_y = 200;

for ( n = 0; n < num_chars; n++ )
{

error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
if ( error )
continue;


my_draw_bitmap( &slot->bitmap,
pen_x + slot->bitmap_left,
pen_y - slot->bitmap_top );


pen_x += slot->advance.x >> 6;
}
我们简化了代码的长度,但它完成相同的工作:
我们使用函数 FT_Loac_Char 代替 FT_Load_Glyph 。如你大概想到的, 它相当于先调用GT_Get_Char_Index

然后调用 FT_Get_Load_Glyph 。
我们不使用 FT_LOAD_DEFAULT 作为装载模式,使用 FT_LOAD_RENDER 。它指示了字形图像必须立
即转换为一个抗锯齿位图。这是一个捷径,可以取消明显的调用 FT_Render_Glyph ,但功能是相同的。
注意,你也可以指定通过附加 FT_LOAD_MONOCHROME 装载标志来获得一个单色位图。

c. 更高级的渲染
现在,让我们来尝试渲染变换文字(例如通过一个环)。我们可以用 FT_Set_Transform 来完成。 这里是示例
代码:
FT_GlyphSlot slot;
FT_Matrix matrix;
FT_UInt glyph_index;
FT_Vector pen;
int n;
... initialize library ...
... create face object ...
... set character size ...

slot = face->glyph;


matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );

for ( n = 0; n < num_chars; n++ )
{

FT_Set_Transform( face, &matrix, &pen );


error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
if ( error )
continue;


my_draw_bitmap( &slot->bitmap,
slot->bitmap_left,
my_target_height - slot->bitmap_top );


pen.x += slot->advance.x;
pen.y += slot->advance.y;
}

一些说明:

现在我们使用一个 FT_Vector 类型的矢量来存储笔位置,其坐标以象素的 1/64 为单位表示,并且倍增。
该位置表示在笛卡儿空间。
不同于系统典型的对位图使用的坐标系(其最高的扫描线是坐标 0 ), FreeType 中, 字形图像的装载、变
换和描述总是采用笛卡儿坐标系(这意味着增加 Y 对应向上的扫描线)。 因此当我们定义笔位置和计算位
图左上角时必须在两个系统之间转换。
我们对每一个字形设置变换来指示旋转矩阵以及使用一个 delta 来移动转换后的图像到当前笔位置(在笛

卡儿空间, 不是位图空间)。 结果, bitmap_left 和 bitmap_top 的值对应目标空间象素中的位图原点。因此,
我们在调用 my_draw_bitmap 时不在它们的值上加 pen.x 或 pen.y 。
步进宽度总会在变换后返回,这就是它可以直接加到当前笔位置的原因。注意,这次它不会四舍五入。

你可能感兴趣的:(嵌入式 装载一个字形图像)