FreeType 2 Tutorial:FreeType 2 教程

1. Header files(包含头文件:3步)

Locate the FreeType 2 include directory.  (将FreeType include目录加载进编译包含include目录中。)
Include the file named ft2build.h.
Include the main FreeType 2 API header file.
#include <ft2build.h>  
#include FT_FREETYPE_H

2. Initialize the library(初始化库)

创建 FT_Library 变量类型,并调用FT_Init_FreeType函数

#include <ft2build.h>
#include FT_FREETYPE_H 
FT_Library library; 
... 
error = FT_Init_FreeType( &library ); 
if ( error ) {  
... an error occurred during library initialization ... 
}

函数功能:

  • 创建FreeType 2 库新实例, 并设置句柄为 library.

  • 装载FreeType知道的所有模块.  library 可以处理 TrueType, Type 1, CID-keyed & OpenType/CFF fonts .

返回0成功,失败 library 设置为 NULL.

3. Load a font face(装载字体FACE)

a. From a font file(由字体文件装载)

通过调用 FT_New_Face创建新的face对象.一个 face描述了特定字样与风格.

FT_Library library; /* handle to library */ 
FT_Face face; /* handle to face object */ 
error = FT_Init_FreeType( &library ); 
if ( error ) { 
...
}  
error = FT_New_Face( library, "/usr/share/fonts/truetype/arial.ttf", 0, &face );  
if ( error == FT_Err_Unknown_File_Format )
{
... 可以打开、读这个字体文件,但不支持该字体格式
} else if ( error ) {
... 其他代码意味着不可读与打开,文件可能损坏了
}

如你所想, FT_New_Face 打开字体文件,提取字体. 参数如下

library

A handle to the FreeType library instance where the face object is created.

filepathname

The font file pathname (a standard C string).字体文件路径(标准C字符串)

face_index

某些字体格式允许多种字体被嵌入在一个单一的文件.

这个索引将告诉我们那个字体将被装载. 若值太大将返回一个错误.

索引0总是正确的。

face

pointer to the handle that will be set to describe the new face object.

It is set to NULL in case of error.

若想知道一个字体文件包含多少种字体,  face_index 置为0, 检查 face->num_faces的值,将会指出字体文件中嵌入了多少种字体.

b. From memory(由内存装载)

在已经将字体文件装入内存的情况下,可以使用 FT_New_Memory_Face 创建FACE对象

FT_Library library; /* handle to library */
FT_Face face; /* handle to face object */
error = FT_Init_FreeType( &library );
if ( error )
{ ... }
error = FT_New_Memory_Face( library, buffer, /* first byte in memory */size, /* size in bytes */0, /* face_index*/&face );
if ( error ) { ... }

FT_New_Memory_Face简单的用指向字体文件的缓存和他的大小代替文件路径,其他参数与 FT_New_Face相同.

注意:在调用FT_Done_Face之前,你不能释放这个内存.

c. From other sources (compressed files, network, etc.)(由其他源文件装载)

在有些情况下,只是用以上两种是不够的. 在FreeType 2内,可以通过自己的  i/o 例程来实现。

这种情况是通过 FT_Open_Face 函数来实现的,FT_Open_Face可以实现使用一个自定义的输入流,选择一个特定的驱动器来打开,乃至当创建该对象时传递外部参数给字体驱动器。我们建议你查阅“FreeType 2参考手册”,学习如何使用它。 


4. Accessing face content(访问FACE内容)

一个face对象包含该face的全部全局描述信息。通常的,这些数据可以通过分别查询句柄来直接访问,例如face->num_glyphs。 

FT_FaceRec结构描述包含了可用字段的完整列表。我们在这里详细描述其中的某些:

num_glyphs

这个值给了字体face中可用的 glyphs 数量. 一个glyph 是一个字体图像. 它不一定对应一个字体代码。

face_flags

一个包含标志位的32-bit 整数, 用来描述许多 face 特性. 例如,标志位 FT_FACE_FLAG_SCALABLE 用来指定 face's font 结构是可伸缩性的 ,并且该字形图像(glyph images)可以渲染到任何字符象素尺寸. 更多信息请查阅FreeType 2 API Reference.

units_per_EM

这个字段只对可伸缩性的格式有效 (it is set to 0 otherwise). 它可以指出被EM转换的字体单元的数量.

num_fixed_sizes

这个字段给了当前face嵌入位图strikes的数量, 一个strike就是某一特定字符象素尺寸下的一系列字形图像。例如,一个字体face可以包含象素尺寸为10、12和14的strike。要注意的是即使是可伸缩的字体格式野可以包含嵌入的位图!

available_sizes

一个指向FT_Bitmap_Size成员组成的数组的指针. 每个 FT_Bitmap_Size为strikes指出垂直级水平字符像素大小,这个呈现在face中.

Note that, generally speaking, these are not the cell size of the bitmap strikes.


5. Setting the current pixel size(设置当前像素大小)

对于特定face中与字符大小相关的信息,FreeType 2使用size对象来构造. 例如, 当字符大小为12点时,一个size对象以1/64th像素为单位保存一定指标值(ascender or text height).

当 FT_New_Face  (or one of its cousins)被调用, 它为返回的face自动创建一个新的size对象. size对象通过 face−>size直接调用.

NOTE: 一个单一的face 对象可以同时处理一个或多个 size 对象; 然而, 只有少数程序员用到这个功能。 我们从而决定简化这个API(i.e., one size per face) 同时通过附加功能实现.

当一个新的face对象被创建, 所有元素被初始化为0.  通过调用 FT_Set_Char_Size为这个结构体填充合理值. 这里有一个例子,它在一个300x300dpi设备上把字符大小设置为16pt:

error = FT_Set_Char_Size( face, /* handle to face object */ 0, /* char_width in 1/64th of points */ 16*64, /* char_height in 1/64th of points */ 300, /* horizontal device resolution */ 300 ); /* vertical device resolution */

需要注意:

  • 字符串的长度与宽度以 1/64th点为单位。 一个点的物理距离为1/72th英寸. 通常来说,这并不等于一个像素.

  • 字符串宽度为0 意味着与长度值相等,字符串长度为0 意味着与宽度值相等. 对于其他情况,指定不一样的长度与宽度。

  • 设备的垂直与水平分辨率以dots-per-inch(dpi)为单位 . 设备显示器的常规值为72 or 96 dpi 。 这个分辨率是用来从字符点数计算字符象素大小的。 

  • 水平分辨率为0意味着与垂直分辨率相等, 垂直分辨率为0意味着与水平分辨率相等. 都为零, 72 dpi 作为默认值被使用.

  • 第一个参数为face 对象的句柄,不是 size 对象.

这个函数计算对应字符宽度、长度及设备分辨率的字符像素大小。可以通过调用函数 FT_Set_Pixel_Sizes自己指定像素大小, as in

error = FT_Set_Pixel_Sizes( face, /* handle to face object */ 0, /* pixel_width */ 16 ); /* pixel_height */

这个例子指定 字符串像素大小为16×16 像素. 如前所说的,尺寸中的任一个为0意味着“与另一个尺寸值相等”.

注意这两个函数都返回错误码。通常,错误会发生在尝试对定长字体格式(如FNT或PCF)设置不在face->fixed_size数组中的象素尺寸值.


6. Loading a glyph image(装载字形 图形)

a. Converting a character code into a glyph index(把一个字符码转换为一个字形索引 

通常说,一想要装载字形个应用程序图像需要依靠它的字符码,字符码是一给定的值用来定义字符编码.例如在ASCII码中65代表A。

一个face对象包含一个或多个表项(charmaps),这个用来将字符码转换成字符索引.例如,许多TrueType字符包含两charmaps. 个用来转换Unicode字符码到字形索引,另一个用来转换AppleRoman编码到字形索引.这样的字体既可以用在Windows(使用Unicode)和Macintosh(使用Apple Roman). 同时要注意,一个特定的字符表可能没有覆盖完字体里面的全部字形。 

当一个新的face对象被创建,默认选择Unicode表项.如果字体没包含Unicode字符表,FreeType会尝试在字形名的基础上模拟一个。注意到如果字形名字不标准的话,可能会丢失模拟字形。对许多字体而言, 包括符号字体和旧的亚洲手写字体,Unicode模拟是不可能的。 

我们将在稍后叙述如何寻找face中特定的字符表. 现在我们假设face至少包含Unicode charmap,并且在调用 FT_New_Face时被选择. 为了将Unicode 字符码转换成字体字形索引, 我们需要函数 FT_Get_Char_Index, as in

glyph_index = FT_Get_Char_Index( face, charcode );

通过给定的charcode在当前选中的FACE charmap中寻找自行索引. 您应该使用Unicode的UTF-32的表现形式,例如,如果你想载入字符U+1F028,使用值0x1F028作为字符码的值。

如果charmap没有被选中, 函数返回charcode.

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

b. Loading a glyph from the face(由face中装载一个字形)

一旦你有了字形索引,你可以加载相应的字形图像.字体图像在字体文件中可以被存储为不同格式.对于像FNT与PCF这样固定大小的格式,每一个图像都是一个位图. 像TrueType or Type 1这样的可伸缩格式使用名为outlines的矢量形状,来描述每一个字形. 许多格式可能拥有更多特殊的方式来描述字形(e.g., MetaFont — 但是这个格式是不支持的). 幸运的是, FreeType 2 足够灵活通过调用简单的API来支持多种字形格式.

字形图像被存储在一个叫做字形槽(glyph slot)的特殊对象中. 就像名字所暗示的一样, 字形槽只是一个简单的容器同一时刻只能保持一个字形图像, 可以是位图,轮廓或其他。每一个face对象有一个单一的字形槽对象,可以通过face->glyph来访问. 它的字段在FT_GlyphSlotRec结构的文档中解释了.

通过调用FT_Load_Glyph 来装载字形图像进字形槽中

error = FT_Load_Glyph( face, /* handle to face object */ glyph_index, /* glyph index */ load_flags ); /* load flags, see below */

load_flags的值是位标志集合,是用来指示某些特殊操作的。其默认值是FT_LOAD_DEFAULT即0。

这个函数会设法从face中装载对应的字形图像:

  • 如果找到一个对应该字形和象素尺寸的位图,那么它将会被装载到字形槽中。嵌入的位图总是比原生的图像格式优先装载。因为我们假定对一个字形,它有更高质量的版本。这可以用FT_LOAD_NO_BITMAP标志来改变

  • 否则,将装载一个该字形的原生图像,把它伸缩到当前的象素尺寸,并且对应如TrueType和Type1这些格式,也会完成hinted操作。 

字段face->glyph->format描述了字形槽中存储的字形图像的格式。如果它的值不是FT_GLYPH_FORMAT_BITMAP,你可以通过FT_Render_Glyph把它直接转换为一个位图。如下: 

error = FT_Render_Glyph( face->glyph, /* glyph slot */render_mode ); /* render mode */

参数render_mode 设置位标志,用来指定怎样去渲染字形图像。设置为FT_RENDER_MODE_NORMAL去渲染高质量锯齿位图(256灰阶)作为默认值.如果你想生成黑白位图,可以使用FT_RENDER_MODE_MONO标志。 

一旦你有了字形图像位图, 可以通过glyph->bitmap(a simple bitmap descriptor)直接访问, 通过glyph->bitmap_leftglyph->bitmap_top来指定起始位置.

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

c. Using other charmaps(使用其他字符表)

正如前面所说,当一个新的face对象被创建,它会寻找一个Unicode字符表并且选择它。当前选择的字符表通过face->charmap来访问. 当无字符表被选择时,这个值为NULL,当由字体文件创建一个新的FT_Face对象但是其并未包含Unicode字符表时face->charmap为NULL(这种情况非常罕见).

有两种途径可以在FreeType2中选择不同的字符表。最轻松的途径是你所需的编码已经有对应的枚举定义在FT_FREETYPE_H中,例如FT_ENCODING_BIG5。在这种情况下,你可以简单地调用FT_Select_CharMap,如下: 

error = FT_Select_CharMap( face, /* target face object */ FT_ENCODING_BIG5 ); /* encoding */

另一种途径是手动为face解析字符表。这通过face对象的字段num_charmaps和charmaps(注意这是复数)来访问。如你想到的,前者是face中的字符表的数目,后者是一个嵌入在face中的指向字符表的指针表(a table of pointers to the charmaps)。

每一个字符表有一些可见的字段,用来更精确地描述它,主要用到的字段是charmap->platform_id和charmap->encoding_id。这两者定义了一个值组合,以更普通的形式用来描述该字符表。 

每一个值组合对应一个特定的编码。例如组合(3,1)对应Unicode。组合列表定义在TrueType规范中,但你也可以使用文件FT_TRUETYPE_IDS_H来处理它们,该文件定义了几个有用的常数。 
要选择一个具体的编码,你需要在规范中找到一个对应的值组合,然后在字符表列表中寻找它。别忘记,由于历史的原因,某些编码会对应几个值组合。这里是一些代码:

FT_CharMap found = 0;
FT_CharMap charmap;
int n;
for ( n = 0; n < face->num_charmaps; n++ )
{ charmap = face->charmaps[n];
if ( charmap->platform_id == my_platform_id && charmap->encoding_id == my_encoding_id )
{
found = charmap;
break;
}
}
if ( !found )
{ ... }
/* now, select the charmap for the face object */
error = FT_Set_CharMap( face, found );
if ( error )
{ ... }

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

d. Glyph transformations(字形变换)

当字形图像被装载时,可以对该字形图像进行仿射变换。当然,这只适用于可伸缩(矢量)字体格式。 

要做到这个可以调用函数 FT_Set_Transform, as in:

error = FT_Set_Transform(
face,
/* target face object */
&matrix,
/* pointer to 2x2 matrix */
&delta );
/* pointer to 2d vector */

这个函数将对给定的face对象设置变换.第二个变量指向FT_Matrix结构体,该结构表述了一个简单二维数组.第三个参数指向 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。这在本教程的后面部分有详细解释。 
同时要注意,对一个字形位图进行非同一性变换将产生错误。


7. Simple text rendering(简单文字渲染)

现在我们将给出一个非常简单的例子程序,该例子程序渲染一个8位Latin-1文本字符串,并且假定face包含一个Unicode字符表。

该程序的思想是建立一个循环,在该循环的每一次迭代中装载一个字形图像,把它转换为一个抗锯齿位图,把它绘制到目标表面(surface)上,然后增加当前笔的位置。

a. Basic code(基本代码)

下面的代码完成我们上面提到的简单文本渲染和其他功能。 

FT_GlyphSlot slot = face->glyph;
/* a small shortcut */
int pen_x, pen_y, n;
... initialize library ...
... create face object ...
... set character size ...
pen_x = 300;
pen_y = 200;
for ( n = 0; n < num_chars; n++ )
{
FT_UInt glyph_index;
/* retrieve glyph index from character code */
glyph_index = FT_Get_Char_Index( face, text[n] );
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
if ( error )
continue;
/* ignore errors */
/* convert to an anti-aliased bitmap */
error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );
if ( error )
continue;
/* now, draw to our target surface */
my_draw_bitmap( &slot->bitmap,
pen_x + slot->bitmap_left,
pen_y - slot->bitmap_top );
/* increment pen position */
pen_x += slot->advance.x >> 6;
pen_y += slot->advance.y >> 6;
/* not useful for now */
这个代码需要一些解释: 
* 我们定义了一个名为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. Refined code(精简代码)

The following code is a refined version of the example above. It uses features and functions of FreeType 2 that have not yet been introduced, and which are explained below:

FT_GlyphSlot slot = face->glyph;
/* a small shortcut */
FT_UInt glyph_index;
int pen_x, pen_y, n;
... initialize library ...
... create face object ...
... set character size .. .
pen_x = 300;
pen_y = 200;
for ( n = 0; n < num_chars; n++ )
{
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
if ( error )
continue;
/* ignore errors */
/* now, draw to our target surface */
my_draw_bitmap( &slot->bitmap,
pen_x + slot->bitmap_left,
pen_y - slot->bitmap_top );
/* increment pen position */
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. More advanced rendering(更高级渲染)

现在,让我们来尝试渲染变换文字(例如通过一个环)。我们可以用FT_Set_Transform来完成。这里是示例代码: 

FT_GlyphSlot slot;
FT_Matrix matrix;
/* transformation matrix */
FT_UInt glyph_index;
FT_Vector pen;
/* untransformed origin */
int n;
... initialize library ...
... create face object ...
... set character size ...
slot = face->glyph;
/* a small shortcut */
/* set up matrix */ 
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 ); 
/* the pen position in 26.6 cartesian space coordinates */ 
/* start at (300,200) */
pen.x = 300 * 64;
pen.y = ( my_target_height - 200 ) * 64
; for ( n = 0; n < num_chars; n++ )
{
/* set transformation */
FT_Set_Transform( face, &matrix, &pen );
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
if ( error )
continue;
/* ignore errors */
/* now, draw to our target surface (convert position) */
my_draw_bitmap( &slot->bitmap,
slot->bitmap_left,
my_target_height - slot->bitmap_top );
/* increment pen position */
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。 

* 步进宽度总会在变换后返回,这就是它可以直接加到当前笔位置的原因。注意,这次它不会四舍五入。 

一个例子完整的源代码可以在这里找到。 

要很注意,虽然这个例子比前面的更复杂,但是变换效果是完全一致的。因此它可以作为一个替换(但更强大)。 

然而该例子有少许缺点,我们将在本教程的下一部分中解释和解决。 

结论 

在这个部分,你已经学习了FreeType2的基础以及渲染旋转文字的充分知识。 

下一部分将深入了解FreeType 2 API更详细的资料,它可以让你直接访问字形度量标准和字形图像,还能让你学习到如何处理缩放、hinting、自居调整,等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值