http://my.unix-center.net/~Simon_fu/?p=372
http://my.unix-center.net/~Simon_fu/?p=385
第一章 简易的字形装载
介绍
* 初始化库
* 通过创建一个新的 face 对象来打开一个字体文件
* 以点或者象素的形式选择一个字符大小
* 装载一个字形(glyph)图像,并把它转换为位图
* 渲染一个简单的字符串
* 容易地渲染一个旋转的字符串
1.头文件
1.FreeType2 include 目录
你必须把FreeType2头文件的目录添加到编译包含(include)目录中。
注意,现在在Unix系统,你可以运行freetype-config脚本加上–cflags选项来获得正确的编译标记。这个脚本也可以用来检查安装在你系统中的库的版本,以及需要的库和连接标记。
2. 包含名为ft2build.h的文件
Ft2build.h包含了接下来要#include的公共FreeType2头文件的宏声明。
3. 包含主要的FreeType2 API头文件
你要使用FT_FREETYPE_H宏来完成这个工作,就像下面这样:
#include <ft2build.h>
#include FT_FREETYPE_H
FT_FREETYPE_H是在ftheader.h中定义的一个特别的宏。Ftheader.h包含了一些安装所特定的宏,这些宏指名了FreeType2 API的其他公共头文件。
你可以阅读“FreeType 2 API参考”的这个部分来获得头文件的完整列表。
#include语句中宏的用法是服从ANSI的。这有几个原因:
* 这可以避免一些令人痛苦的与FreeType 1.x公共头文件的冲突。
* 宏名字不受限于DOS的8.3文件命名限制。象FT_MULTIPLE_MASTERS_H或FT_SFNT_NAMES_H这样的名字比真实的文件名ftmm.h和fsnames.h更具可读性并且更容易理解。
* 它允许特别的安装技巧,我们不在这里讨论它。
注意:从FreeType 2.1.6开始,旧式的头文件包含模式将不会再被支持。这意味着现在如果你做了象下面那样的事情,你将得到一个错误:
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
. . .
2. 初始化库
#include <ft2build.h> #include FT_FREETYPE_H FT_LIBRARY library; ... Error = FT_Init_FreeType ( &library ); If ( error ) { ... 当初始化库时发生了一个错误 . . . }
* 它创建一个FreeType 2库的新实例,并且设置句柄library指向他。
* 它装载库中FreeType所知道的每一个模块。在这些模块支持下,你新建的library对象可以优雅地处理TrueType, Type 1, CID-keyed 和OpenType/CFF字体。
3.装载一个字体face
a.从一个字体文件装载
FT_Library library; FT_Face face; 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 ) { ... 其它的错误码意味着这个字体文件不能打开和读, ... 或者简单的说它损坏了... }
Library
一个FreeType库实例的句柄,face对象从中建立。
Filepathname
字体文件路径名(一个标准的C字符串) Face_index
某些字体格式允许把几个字体face嵌入到同一个文件中。 这个索引指示你想装载的face。
如果这个值太大,函数将会返回一个错误。Index 0总是正确的。
Face
一个指向新建的face对象的指针。
当失败时其值被置为NULL。
FT_Library library; FT_Face face; error = FT_Init_FreeType( &library ); if ( error ) { ... } error = FT_New_Memory_Face( library, buffer, size, 0, &face ); if ( error ) { ... }
4.访问face内容
Num_glyphs
这个值给出了该字体face中可用的字形(glyphs)数目。简单来说,一个字形就是一个字符图像。但一个字形不一定和字符代码一一对应。
Flags
一个32位整数,包含一些用来描述face特性的位标记。例如,标记FT_FACE_FLAG_SCALABLE用来指示该face的字体格式是可伸缩并且该字形图像可以渲染到任何字符象素尺寸。要了解face标记的更多信息,请阅读“FreeType 2 API 参考”。 Units_per_EM
这个字段只对可伸缩字体格式有效,在其他格式它将会置为0。它指示了EM所覆盖的字体单位的个数。 Num_fixed_size
这个字段给出了当前face中嵌入的位图的个数。简单来说,一个strike就是某一特定字符象素尺寸下的一系列字形图像。例如,一个字体face可以包含象素尺寸为10、12和14的strike。要注意的是即使是可伸缩的字体格式野可以包含嵌入的位图! Fixed_sizes
一个指向FT_Bitmap_Size成员组成的数组的指针。每一个FT_Bitmap_Size指示face中的每一个strike的水平和垂直字符象素尺寸。
5.设置当前象素尺寸
error = FT_Set_Char_Size( face, 0, 16*64, 300, 300 );
* 字符宽度和高度以1/64点为单位表示。一个点是一个1/72英寸的物理距离。通常,这不等于一个象素。
* 设备的水平和垂直分辨率以每英寸点数(dpi)为单位表示。显示设备(如显示器)的常规值为72dpi或96dpi。这个分辨率是用来从字符点数计算字符象素大小的。
* 字符宽度为0意味着“与字符高度相同”,字符高度为0意味着“与字符宽度相同”。对于其他情况则意味着指定不一样的字符宽度和高度。
* 水平或垂直分辨率为0时表示使用默认值72dpi。
* 第一个参数是face对象的句柄,不是size对象的。
error = FT_Set_Pixel_Sizes( face, 0, 16 );
注意这两个函数都返回错误码。通常,错误会发生在尝试对定长字体格式(如FNT或PCF)设置不在face->fixed_size数组中的象素尺寸值。
6.装载一个字形图像
a.把一个字符码转换为一个字形索引
glyph_index = FT_Get_Char_Index( face, charcode );
这个函数会在face里被选中的字符表中查找与给出的字符码对应的字形索引。如果没有字符表被选中,这个函数简单的返回字符码。
通过调用FT_Load_Glyph来装载一个字形图像到字形槽中,如下:
error = FT_Load_Glyph( face, glyph_index, load_flags );
这个函数会设法从face中装载对应的字形图像:
* 如果找到一个对应该字形和象素尺寸的位图,那么它将会被装载到字形槽中。嵌入的位图总是比原生的图像格式优先装载。因为我们假定嵌入的位图对一个字形是比较高质量的版本。这可以用FT_LOAD_NO_BITMAP标志来改变。
* 否则,将装载一个该字形的原生图像,把它伸缩到当前的象素尺寸,并且对应如TrueType和Type1这些格式,也会完成hinted操作。
error = FT_Render_Glyph( face->glyph, render_mode );
要注意,bitmap_left是从字形位图当前笔位置到最左边界的水平距离,而bitmap_top是从笔位置(位于基线)到最高边界得垂直距离。他么是正数,指示一个向上的距离。
error = FT_Select_CharMap( face, FT_ENCODING_BIG5 );
通的形式用来描述该字符表。
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 ) { ... } error = FT_Set_CharMap( face, found ); if ( error ) { ... }
简单地调用FT_Set_Transform来完成这个工作,如下:
error = FT_Set_Transform( face, &matrix, &delta );
7. 简单的文字渲染
FT_GlyphSlot slot = face->glyph; 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; 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减它,而不是加它。
FT_GlyphSlot slot = face->glyph; 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++ ) { 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_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 ); pen.x = 300 * 64; pen.y = ( my_target_height - 200 ) * 64; 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。
* 步进宽度总会在变换后返回,这就是它可以直接加到当前笔位置的原因。注意,这次它不会四舍五入。
结论