0.实现
/* color : 0x00RRGGBB */ void lcd_put_pixel(int x, int y, unsigned int color) { // 获取当前像素的地址 unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; unsigned short *pen_16; unsigned int *pen_32; unsigned int red, green, blue; pen_16 = (unsigned short *)pen_8; pen_32 = (unsigned int *)pen_8; switch (var.bits_per_pixel) { case 8: { // 8位色 *pen_8 = color; break; } case 16: { // 16位色 red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = (color >> 0) & 0xff; color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); *pen_16 = color; break; } case 32: { // 32位色 *pen_32 = color; break; } default: { printf("can't surport %dbpp\n", var.bits_per_pixel); break; } } }
lcd_put_pixel 函数接受三个参数:x,y 和 color。它用于在指定的 x 和 y 坐标上设置屏幕上像素的颜色。color 参数是一个无符号整数,表示像素的颜色,格式为 0x00RRGGBB,其中 RR 表示红色分量,GG 表示绿色分量,BB 表示蓝色分量
/* Replace this function with something useful. */ void draw_bitmap( FT_Bitmap* bitmap, FT_Int x, FT_Int y) { FT_Int i, j, p, q; FT_Int x_max = x + bitmap->width; FT_Int y_max = y + bitmap->rows; //printf("x = %d, y = %d\n", x, y); for ( i = x, p = 0; i < x_max; i++, p++ ) { for ( j = y, q = 0; j < y_max; j++, q++ ) { if ( i < 0 || j < 0 || i >= var.xres || j >= var.yres ) continue; //image[j][i] |= bitmap->buffer[q * bitmap->width + p]; lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]); } } }
draw_bitmap函数用于在屏幕上绘制一个位图。它接受三个参数:bitmap是要绘制的位图,x和y是位图的左上角坐标。该函数使用lcd_put_pixel函数将位图的像素写入屏幕。
int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t * wstr, TGlyph glyphs[]) { int n; PGlyph glyph = glyphs; int pen_x = 0; int pen_y = 0; int error; FT_GlyphSlot slot = face->glyph;; for (n = 0; n < wcslen(wstr); n++) { glyph->index = FT_Get_Char_Index( face, wstr[n]); /* store current pen position */ glyph->pos.x = pen_x; glyph->pos.y = pen_y; /* load时是把glyph放入插槽face->glyph */ error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT); if ( error ) continue; error = FT_Get_Glyph(face->glyph, &glyph->image ); if ( error ) continue; /* translate the glyph image now */ /* 这使得glyph->image里含有位置信息 */ FT_Glyph_Transform(glyph->image, 0, &glyph->pos ); pen_x += slot->advance.x; /* 1/64 point */ /* increment number of glyphs */ glyph++; } /* count number of glyphs loaded */ return (glyph - glyphs); }
compute_string_bbox函数接受一个TGlyph数组glyphs,数组中的字形数量num_glyphs和一个FT_BBox指针abbox。它通过循环遍历glyphs数组中的每个字形并使用FT_Glyph_Get_CBox检索其边界框来计算字符串的边界框。然后,该函数通过将字形边界框与当前整体边界框进行比较来更新整体边界框。
1.变量定义
FT_Library library; // FreeType 库 FT_Face face; // 字体 int error; // 错误码 FT_Vector pen; // 绘制笔 FT_GlyphSlot slot; // 字形插槽 int i; // 循环计数器 FT_BBox bbox; // 字符串边界框 int line_box_ymin = 10000; // 行边界框最小 y 坐标 int line_box_ymax = 0; // 行边界框最大 y 坐标 int line_box_width; // 行边界框宽度 int line_box_height; // 行边界框高度 TGlyph glyphs[MAX_GLYPHS]; /* 字形表 */ FT_UInt num_glyphs; // 字形数量
2.lcd操作获取屏幕信息
fd_fb = open("/dev/fb0", O_RDWR); if (fd_fb < 0) { printf("can't open /dev/fb0\n"); return -1; } // 获取屏幕信息 if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) { printf("can't get var\n"); return -1; } // 获取固定屏幕信息 if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) { printf("can't get fix\n"); return -1; } // 获取屏幕宽度 line_width = var.xres * var.bits_per_pixel / 8; // 获取像素宽度 pixel_width = var.bits_per_pixel / 8; // 获取屏幕大小 screen_size = var.xres * var.yres * var.bits_per_pixel / 8; // 映射屏幕内存 fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); if (fbmem == (unsigned char *)-1) { printf("can't mmap\n"); return -1; }
3.freetype初始化
/* 全部设为黑色 */
memset(fbmem, 0, screen_size);
/* 初始化FreeType库 /
error = FT_Init_FreeType( &library ); / initialize library /
/ 错误处理省略 */
/* 创建字体对象 /
error = FT_New_Face( library, argv[1], 0, &face ); / create face object /
/ 错误处理省略 */
slot = face->glyph;
/* 设置字体大小 */
FT_Set_Pixel_Sizes(face, 24, 0);
4.绘画
/* wstr1 */ // 将字符串转换为字形表 num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr1, glyphs); // 计算字符串的边界框 compute_string_bbox(glyphs, num_glyphs, &bbox); line_box_width = bbox.xMax - bbox.xMin; line_box_height = bbox.yMax - bbox.yMin; // 计算字符串在屏幕上的位置 pen.x = (var.xres - line_box_width)/2 * 64; pen.y = (var.yres - line_box_height)/2 * 64; // 绘制字符串 Draw_Glyphs(glyphs, num_glyphs, pen); /* wstr2 */ num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr2, glyphs); compute_string_bbox(glyphs, num_glyphs, &bbox); line_box_width = bbox.xMax - bbox.xMin; line_box_height = bbox.yMax - bbox.yMin; pen.x = (var.xres - line_box_width)/2 * 64; pen.y = pen.y - 24 * 64; Draw_Glyphs(glyphs, num_glyphs, pen);
1.字形度量
从字形度量这个名字可以想到,字形度量是关联每一个字形的确定距离,以此描述如何使用该距离来排版文本。
通常一个字形有两个度量集:用来排版水平文本排列的字形(拉丁文、西里尔文、阿拉伯文、希伯来文等等)和用来排版垂直文本排列的字形(中文、日文、韩文等等)。
要注意的是只有很少的字体格式提供了垂直度量。你可以使用宏 FT_HAS_VERTICAL 测试某个给出的face 对象是否包含垂直度量,当结果为真时表示包含。
个别的字形度量可以先装载字形到 face 的字形槽,然后通过 face->glyph->metrics 结构访问,其类型为FT_Glyph_Metrics。我们将在下面详细讨论它,现在,我们只关注该结构包含如下的字段:
Width
这是字形图像的边框的宽度。它与排列方向无关。
Height
这是字形图像的边框的高度。它与排列方向无关。
horiBearingX
用于水平文本排列,这是从当前光标位置到字形图像最左边的边界的水平距离。
horiBearingY
用于水平文本排列,这是从当前光标位置(位于基线)到字形图像最上边的边界的水平距离。
horiAdvance
用于水平文本排列,当字形作为字符串的一部分被绘制时,这用来增加笔位置的水平距离。
vertBearingX
用于垂直文本排列,这是从当前光标位置到字形图像最左边的边框的垂直距离。
vertBearingY
用于垂直文本排列,这是从当前光标位置(位于基线)到字形图像最上边的边框的垂直距离。
vertAdvance
用于垂直文本排列,当字形作为字符串的一部分被绘制时,这用来增加笔位置的垂直距离。
注意:因为不是所有的字体都包含垂直度量,当 FT_HAS_VERTICAL 为假时,vertBearingX,vertBearingY和 vertAdvance 的值是不可靠的。
下面的图形更清楚地图解了度量。第一个图解了水平度量,其基线为水平轴:
对于垂直文本排列,基线是垂直的,与垂直轴一致:
对于垂直文本排列,基线是垂直的,与垂直轴一致:
Face->glyph->metrics 中的度量通常以 26.6 象素格式(例如 1/64 象素)表示,除非你在调用 FT_Load_Glyph
或 FT_Load_Char 时使用了 FT_LOAD_NO_SCALE 标志,这样的话度量会用原始字体单位表示。
字形槽(glyph slot)对象也有一些其他有趣的字段可以减轻开发者的工作。你可以通过 face->glyph->xxx 访问它们,其中 xxx 是下面字段之一:
Advance
这个字段是一个 FT_Vector,保存字形的变换推进。当你通过 FT_Set_Transform 使用变换时,这是很有用的,这在第一部分的循环文本例子中已经展示过了。与那不同,这个值是默认的(metrics.horiAdvance,0),除非你在装载字形图像时指定 FT_LOAD_VERTICAL,那么它将会为(0,metrics.vertAdvance)。
linearHoriAdvance
这个字段包含字形水平推进宽度的线性刻度值。实际上,字形槽返回的 metrics.horiAdvance 值通常四舍五入为整数象素坐标(例如,它是 64 的倍数),字体驱动器用它装载字形图像。linearHoriAdvance 是一个 16.16固定浮点数,提供了以 1/65536 象素为单位的原始字形推进宽度的值。它可以用来完成伪设备无关文字排版。
linearVertAdvance
这与 linearHoriAdvance 类似,但它用于字形的垂直推进高度。只有当字体 face 包含垂直度量时这个值才是可靠的。
2.类
1.FT 中的面向对象
虽然 FT 是使用 ANSI C 编写,但是采用面向对象的思想,是这个库非常容易扩展,因此,下面有一些代码规约。
- 每个对象类型/类都有一个对应的结构类型和一个对应的结构指针类型,后者称为类型/类的句柄类型
设想我们需要管理 FT 中一个 foo 类的对象,可以定义如下
typedef struct FT_FooRec_* FT_Foo; typedef struct FT_FooRec_ { // fields for the foo class … }FT_FooRec;
依照规约,句柄类型使用简单而有含义的标识符,并以 FT_开始,如 FT_Foo,而结构体使用相同的名称但是加上 Rec 后缀。 Rec 是记录的缩写。每个类类型都有对应的句柄类型;
2. 类继承通过将基类包装到一个新类中实现,例如,我们定义一个 foobar 类,从 foo 类继承,可以实现为
typedef struct FT_FooBarRec_ * FT_FooBar; typedef struct FT_FooBarRec_ { FT_FooRec root; //基类 }FT_FooBarRec;
可以看到,将一个 FT_FooRec 放在 FT_FooBarRec 定义的开始,并约定名为 root,可以确保一个 foobar 对象也是一个 foo 对象。
在实际使用中,可以进行类型转换。
2.FT_Library 类
这个类型对应一个库的单一实例句柄,没有定义相应的 FT_LibraryRec,使客户应用无法访问它的内部属性。 库对象是所有 FT 其他对象的父亲,你需要在做任何事情前创建一个新的库实例,销毁它时会自动销毁他所有的孩子,如 face 和 module 等。 通常客户程序应该调用 FT_Init_FreeType()来创建新的库对象,准备作其他操作时使用。 另一个方式是通过调用函数 FT_New_Library()来创建一个新的库对象,它在<freetype/ftmodule.h> 中 定 义 , 这 个 函 数 返 回 一 个 空 的 库 , 没 有 任 何 模 块 注 册 , 你 可 以 通 过 调 用FT_Add_Module()来安装模块。
调用 FT_Init_FreeType()更方便一些,因为他会缺省地注册一些模块。这个方式中,模块列表在构建时动态计算,并依赖 ftinit 部件的内容。
3.FT_Face 类
一个外观对象对应单个字体外观,即一个特定风格的特定外观类型,例如 Arial 和 Arial Italic 是两个不同的外观。
一个外观对象通常使用 FT_New_Face()来创建,这个函数接受如下参数:一个 FT_Library 句柄,一个表示字体文件的 C 文件路径名,一个决定从文件中装载外观的索引(一个文件中可能有不同的外观),和 FT_Face句柄的地址,它返回一个错误码。
FT_Error FT_New_Face( FT_Library library, const char* filepathname, FT_Long face_index, FT_Face* face);
函数调用成功,返回 0, face 参数将被设置成一个非 NULL 值。外观对象包含一些用来描述全局字体数据的属性,可以被客户程序直接访问。例如外观中字形的数量、外观家族的名称、风格名称、 EM 大小等,详见 FT_FaceRec 定义。
4 FT_Size 类
每个 FT_Face 对象都有一个或多个 FT_Size 对象,一个尺寸对象用来存放指定字符宽度和高度的特定数据,每个新创建的外观对象有一个尺寸,可以通过 face->size 直接访问。
尺寸对象的内容可以通过调用 FT_Set_Pixel_Sizes()或 FT_Set_Char_Size()来改变。
一个新的尺寸对象可以通过 FT_New_Size()创建,通过 FT_Done_Size()销毁,一般客户程序无需做这一步,它们通常可以使用每个 FT_Face 缺省提供的尺寸对象。
FT_Size 公共属性定义在 FT_SizeRec 中,但是需要注意的是有些字体驱动定义它们自己的 FT_Size 的子类,以存储重要的内部数据,在每次字符大小改变时计算。大多数情况下,它们是尺寸特定的字体 hint。例如,TrueType 驱动存储 CVT 表,通过 cvt 程序执行将结果放入TT_Size 结构体中,而 Type1 驱动将scaled global
metrics 放在 T1_Size 对象中。
5 FT_GlyphSlot 类
字形槽的目的是提供一个地方,可以很容易地一个个地装入字形映象,而不管它的格式(位图、向量轮廓或其他)。理想的,一旦一个字形槽创建了,任何字形映象可以装入,无需其他的内存分配。在实际中,只对于特定格式才如此,像 TrueType,它显式地提供数据来计算一个槽地最大尺寸。
另一个字形槽地原因是他用来为指定字形保存格式特定的 hint,以及其他为正确装入字形的必要数据。基本的 FT_GlyphSlotRec 结构体只向客户程序展现了字形 metics 和映象,而真正的实现回包含更多的数据。例如,TrueType 特定的 TT_GlyphSlotRec 结构包含附加的属性,存放字形特定的字节码、在 hint 过程中暂时的轮廓和其他一些东西。
最后,每个外观对象有一个单一字形槽,可以用 face->glyph 直接访问。
3.函数
转载到字形槽得字形图像可以转换到一幅位图中,这可以在装载时使用 FT_LOAD_RENDER 标志或者调用 FT_Render_Glyph 函数实现。每一次你装载一个新的字形图像到字形槽,前面装载的将会从字形槽中抹去。但是,你可能需要从字形槽中提取这个图像,以用来在你的应用程序中缓存它,或者进行附加的变换,或者在转换到位图前测量它。 FreeType 2 API 有一个特殊的扩展能够以一种灵活和普通的方式处理字形图像。要使用它,你首先需要包含 FT_GLYPH_H 头文件,如下:
#include FT_GLYPH_H
现在我们将解释如何使用这个文件定义的这个函数。
1.把一个字符码转换为一个字形索引FT_Get_Char_Index函数
常,一个应用程序想通过字符码来装载它的字形图像。字符码是一个特定编码中代表该字符的数值。例如,字符码 64 代表了 ASCII 编码中的’A’。
一个 face 对象包含一个或多个字符表(charmap),字符表是用来转换字符码到字形索引的。例如,很多TrueType 字体包含两个字符表,一个用来转换 Unicode 字符码到字形索引,另一个用来转换 Apple Roman 编码到字形索引。这样的字体既可以用在 Windows(使用 Unicode)和 Macintosh(使用 Apple Roman)。同时要注意,一个特定的字符表可能没有覆盖完字体里面的全部字形。
当新建一个 face 对象时,它默认选择 Unicode 字符表。如果字体没包含 Unicode 字符表,FreeType 会尝试在字形名的基础上模拟一个。注意,如果字形名是不标准的那么模拟的字符表有可能遗漏某些字形。对于
某些字体,包括符号字体和旧的亚洲手写字体,Unicode 模拟是不可能的。我们将在稍后叙述如何寻找 face 中特定的字符表。现在我们假设 face 包含至少一个 Unicode 字符表,并且在调用FT_New_Face 时已经被选中。我们使用 FT_Get_Char_Index 把一个 Unicode 字符码转换为字形索引,
如下所示:
glyph_index = FT_Get_Char_Index( face, charcode );
这个函数会在 face 里被选中的字符表中查找与给出的字符码对应的字形索引。如果没有字符表被选中,这个函数简单的返回字符码。
注意,这个函数是 FreeType 中罕有的不返回错误码的函数中的一个。然而,当一个特定的字符码在 face
中没有字形图像,函数返回 0。按照约定,它对应一个特殊的字形图像――缺失字形,通常会显示一个框或一个空格。
2.从 face 中装载一个字形FT_Load_Glyph与FT_Render_Glyph函数
一旦你获得了字形索引,你便可以装载对应的字形图像。在不同的字体中字形图像存储为不同的格式。对于固定尺寸字体格式,如 FNT 或者 PCF,每一个图像都是一个位图。对于可伸缩字体格式,如 TrueType或者 Type1,使用名为轮廓(outlines)的矢量形状来描述每一个字形。一些字体格式可能有更特殊的途径来表示字形(如 MetaFont――但这个格式不被支持)。幸运的, FreeType2 有足够的灵活性,可以通过一个简单的API 支持任何类型的字形格式。
字形图像存储在一个特别的对象――字形槽(glyph slot)中。就如其名所暗示的,一个字形槽只是一个简单的容器,它一次只能容纳一个字形图像,可以是位图,可以是轮廓,或者其他。每一个 face 对象都有一个字形槽对象,可以通过 face->glyph 来访问。它的字段在 FT_GlyphSlotRec 结构的文档中解释了。
通过调用 FT_Load_Glyph 来装载一个字形图像到字形槽中,如下:
error = FT_Load_Glyph( face, /* face 对象的句柄 */ glyph_index, /* 字形索引 */ load_flags ); /* 装载标志,参考下面 */
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, /* 字形槽 /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.提取字形图像FT_Get_Glyph函数
提取字形图像
你可以很简单地提取一个字形图像。这里有一向代码向你展示如何去做:
FT_Glyph glyph; /* 字形图像的句柄 */ ... error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NORMAL ); if ( error ) { ... } error = FT_Get_Glyph( face->glyph, &glyph ); if ( error ) { ... }
• 创建一个类型为 FT_Glyph,名为glyph 的变量。这是一个字形图像的句柄(即指针)。
• 装载字形图像(通常情况下)到 face 的字形槽中。我们不使用 FT_LOAD_RENDER 因为我们想抓取一个
可缩放的字形图像,以便后面对其进行变换。
• 通过调用 FT_Get_Glyph,把字形图像从字形槽复制到新的 FT_Glyph 对象 glyph 中。这个函数返回一个错误码并且设置 glyph。
要非常留意,被取出的字形跟字形槽中的原始字形的格式是一样的。例如,如果我们从 TrueType 字体文件中装载一个字形,字形图像将是可伸缩的矢量轮廓。 如果你想知道字形是如何模型和存储的,你可以访问 flyph->format 字段。一个新的字形对象可以通过调用FT_Done_Glyph 来销毁。 字形对象正好包含一个字形图像和一个 2D 矢量,2D 矢量以 16.16 固定浮点坐标的形式表示字形的推进。后者可以直接通过glyph->advance 访问。 注意,不同于其他 TrueType 对象,库不保存全部分配了的字形对象的列表。这意味着你必须自己销毁它们,而不是依靠 FT_Done_FreeType 完成全部的清除。
4.变换和复制字形图像FT_Glyph_Transform函数
如果字形图像是可伸缩的(例如,如果 glyph->format 不等于 FT_GLYPH_FORMAT_BITMAP),那么就可以随时通过调用 FT_Glyph_Transform 来变换该图像。
你也可以通过 FT_Glyph_Copy 复制一个字形图像。这里是一些例子代码:
FT_Glyph glyph, glyph2; FT_Matrix matrix; FT_Vector delta; ... 装载字形图像到 `glyph' ... /* 复制 glyph 到 glyph2 */ error = FT_Glyph_Copy( glyph, &glyph2 ); if ( error ) { ... 无法复制(内存不足) ... } /* 平移 `glyph' */ delta.x = -100 * 64; /* 坐标是 26.6 象素格式的 */ delta.y = 50 * 64; FT_Glyph_Transform( glyph, 0, &delta ); /* 变换 glyph2 (水平剪切) */ matrix.xx = 0x10000L; matrix.xy = 0.12 * 0x10000L; matrix.yx = 0; matrix.yy = 0x10000L; FT_Glyph_Transform( glyph2, &matrix, 0 );
注意, 2x2 矩阵变换总是适用于字形的 16.16 推进矢量,所以你不需要重修计算它。
5.测量字形图像FT_Glyph_Get_CBox函数
你也可以通过 FT_Glyph_Get_CBox 函数检索任意字形图像(无论是可伸缩或者不可伸缩的)的控制(约束)框,如下:
FT_BBox bbox; ... FT_Glyph_Get_CBox( glyph, bbox_mode, &bbox );
坐标是跟字形的原点(0, 0)相关的,使用 y 向上的约定。这个函数取一个特殊的参数: bbox_mode 来指出如何表示框坐标。 如果字形装载时使用了 FT_LOAD_NO_SCALE 标志, bbox_mode 必须设置为FT_GLYPH_BBOX_UNSCALED , 以 此 来 获 得 以 26.6 象 素 格 式 为 单 位 表 示 的 不 可 缩 放 字 体 。 值FT_GLYPH_BBOX_SUBPIXELS 是这个常量的另一个名字。 要注意,框(box)的最大坐标是唯一的,这意味着你总是可以以整数或 26.6 象素的形式计算字形图像的宽度和高度,公式如下:
width = bbox.xMax - bbox.xMin; height = bbox.yMax - bbox.yMin;
同时要注意,对于 26.6 坐标,如果 FT_GLYPH_BBOX_GRIDFIT 被用作为 bbox_mode,坐标也将网格对齐,
符合如下公式:
bbox.xMin = FLOOR( bbox.xMin ) bbox.yMin = FLOOR( bbox.yMin ) bbox.xMax = CEILING( bbox.xMax ) bbox.yMax = CEILING( bbox.yMax )
要把 bbox 以整数象素坐标的形式表示,把 bbox_mode 设置为 FT_GLYPH_BBOX_TRUNCATE。最后,要把约束框以网格对齐象素坐标的形式表示,把 bbox_mode 设置为 FT_GLYPH_BBOX_PIXELS。
6.转换字形图像为位图FT_Glyph_To_Bitmap函数
当你已经把字形对象缓存或者变换后,你可能需要转换它到一个位图。这可以通过 FT_Glyph_To_Bitmap函数简单得实现。它负责转换任何字形对象到位图,如下:
FT_Vector origin; origin.x = 32; /* 26.6 格式的 1/2 象素 */ origin.y = 0; error = FT_Glyph_To_Bitmap( &glyph, render_mode, &origin, 1 ); /* 销毁原始图像 == true */
• 第一个参数是源字形句柄的地址。当这个函数被调用时,它读取该参数来访问源字形对象。调用结束后,这个句柄将指向一个新的包含渲染后的位图的字形对象。
• 第二个参数时一个标准渲染模式,用来指定我们想要哪种位图。它取 FT_RENDER_MODE_DEFAULT 时表示 8 位颜色深度的抗锯齿位图;它取 FT_RENDER_MODE_MONO 时表示 1 位颜色深度的黑白位图。
• 第三个参数是二维矢量的指针。该二维矢量是在转换前用来平移源字形图像的。要注意,函数调用后源图像将被平移回它的原始位置(这样便不会有变化)。如果你在渲染前不需要平移源字形,设置这个指针
为 0。
• 最后一个参数是一个布尔值,用来指示该函数是否要销毁源字形对象。如果为 false,源字形对象不会被销毁,即使它的句柄丢失了(客户应用程序需要自己保留句柄)。
如果没返回错误,新的字形对象总是包含一个位图。并且你必须把它的句柄进行强制类型转换,转换为FT_BitmapGlyph 类型,以此访问它的内容。这个类型是 FT_Glyph 的一种“子类”,它包含下面的附加字段(看 FT_BitmapGlyphRec):
Left
类似于字形槽的 bitmap_left 字段。这是字形原点(0,0)到字形位图最左边象素的水平距离。它以整数象素的形式表示。
Top
类似于字形槽的 bitmap_top 字段。它是字形原点(0,0)到字形位图最高象素之间的垂直距离(更精确来说,到位图上面的象素)。这个距离以整数象素的形式表示,并且 y 轴向上为正。
Bitmap
这是一个字形对象的位图描述符,就像字形槽的 bitmap 字段。
实例代码
#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <linux/fb.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <math.h> #include <wchar.h> #include <ft2build.h> #include FT_FREETYPE_H #include FT_GLYPH_H typedef struct TGlyph_ { FT_UInt index; /* glyph index */ FT_Vector pos; /* glyph origin on the baseline */ FT_Glyph image; /* glyph image */ } TGlyph, *PGlyph; #define MAX_GLYPHS 100 int fd_fb; struct fb_var_screeninfo var; /* Current var */ struct fb_fix_screeninfo fix; /* Current fix */ int screen_size; unsigned char *fbmem; unsigned int line_width; unsigned int pixel_width; /* color : 0x00RRGGBB */ void lcd_put_pixel(int x, int y, unsigned int color) { unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width; unsigned short *pen_16; unsigned int *pen_32; unsigned int red, green, blue; pen_16 = (unsigned short *)pen_8; pen_32 = (unsigned int *)pen_8; switch (var.bits_per_pixel) { case 8: { *pen_8 = color; break; } case 16: { /* 565 */ red = (color >> 16) & 0xff; green = (color >> 8) & 0xff; blue = (color >> 0) & 0xff; color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); *pen_16 = color; break; } case 32: { *pen_32 = color; break; } default: { printf("can't surport %dbpp\n", var.bits_per_pixel); break; } } } /* Replace this function with something useful. */ void draw_bitmap( FT_Bitmap* bitmap, FT_Int x, FT_Int y) { FT_Int i, j, p, q; FT_Int x_max = x + bitmap->width; FT_Int y_max = y + bitmap->rows; //printf("x = %d, y = %d\n", x, y); for ( i = x, p = 0; i < x_max; i++, p++ ) { for ( j = y, q = 0; j < y_max; j++, q++ ) { if ( i < 0 || j < 0 || i >= var.xres || j >= var.yres ) continue; //image[j][i] |= bitmap->buffer[q * bitmap->width + p]; lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]); } } } int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t * wstr, TGlyph glyphs[]) { int n; PGlyph glyph = glyphs; int pen_x = 0; int pen_y = 0; int error; FT_GlyphSlot slot = face->glyph;; for (n = 0; n < wcslen(wstr); n++) { glyph->index = FT_Get_Char_Index( face, wstr[n]); /* store current pen position */ glyph->pos.x = pen_x; glyph->pos.y = pen_y; /* load时是把glyph放入插槽face->glyph */ error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT); if ( error ) continue; error = FT_Get_Glyph(face->glyph, &glyph->image ); if ( error ) continue; /* translate the glyph image now */ /* 这使得glyph->image里含有位置信息 */ FT_Glyph_Transform(glyph->image, 0, &glyph->pos ); pen_x += slot->advance.x; /* 1/64 point */ /* increment number of glyphs */ glyph++; } /* count number of glyphs loaded */ return (glyph - glyphs); } void compute_string_bbox(TGlyph glyphs[], FT_UInt num_glyphs, FT_BBox *abbox ) { FT_BBox bbox; int n; bbox.xMin = bbox.yMin = 32000; bbox.xMax = bbox.yMax = -32000; for ( n = 0; n < num_glyphs; n++ ) { FT_BBox glyph_bbox; FT_Glyph_Get_CBox(glyphs[n].image, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox ); if (glyph_bbox.xMin < bbox.xMin) bbox.xMin = glyph_bbox.xMin; if (glyph_bbox.yMin < bbox.yMin) bbox.yMin = glyph_bbox.yMin; if (glyph_bbox.xMax > bbox.xMax) bbox.xMax = glyph_bbox.xMax; if (glyph_bbox.yMax > bbox.yMax) bbox.yMax = glyph_bbox.yMax; } *abbox = bbox; } void Draw_Glyphs(TGlyph glyphs[], FT_UInt num_glyphs, FT_Vector pen) { int n; int error; for (n = 0; n < num_glyphs; n++) { FT_Glyph_Transform(glyphs[n].image, 0, &pen); /* convert glyph image to bitmap (destroy the glyph copy!) */ error = FT_Glyph_To_Bitmap(&glyphs[n].image, FT_RENDER_MODE_NORMAL, 0, /* no additional translation */ 1 ); /* destroy copy in "image" */ if ( !error ) { FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[n].image; draw_bitmap(&bit->bitmap, bit->left, var.yres - bit->top); FT_Done_Glyph(glyphs[n].image ); } } } int main(int argc, char **argv) { wchar_t *wstr1 = L"个人博客P-Paranoid"; wchar_t *wstr2 = L"https://blog.csdn.net/weixin_52849254"; FT_Library library; FT_Face face; int error; FT_Vector pen; FT_GlyphSlot slot; int i; FT_BBox bbox; int line_box_ymin = 10000; int line_box_ymax = 0; int line_box_width; int line_box_height; TGlyph glyphs[MAX_GLYPHS]; /* glyphs table */ FT_UInt num_glyphs; if (argc != 2) { printf("Usage : %s <font_file>\n", argv[0]); return -1; } fd_fb = open("/dev/fb0", O_RDWR); if (fd_fb < 0) { printf("can't open /dev/fb0\n"); return -1; } if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) { printf("can't get var\n"); return -1; } if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) { printf("can't get fix\n"); return -1; } line_width = var.xres * var.bits_per_pixel / 8; pixel_width = var.bits_per_pixel / 8; screen_size = var.xres * var.yres * var.bits_per_pixel / 8; fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); if (fbmem == (unsigned char *)-1) { printf("can't mmap\n"); return -1; } /* 清屏: 全部设为黑色 */ memset(fbmem, 0, screen_size); /* 显示矢量字体 */ error = FT_Init_FreeType( &library ); /* initialize library */ /* error handling omitted */ error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */ /* error handling omitted */ slot = face->glyph; FT_Set_Pixel_Sizes(face, 24, 0); /* wstr1 */ num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr1, glyphs); compute_string_bbox(glyphs, num_glyphs, &bbox); line_box_width = bbox.xMax - bbox.xMin; line_box_height = bbox.yMax - bbox.yMin; pen.x = (var.xres - line_box_width)/2 * 64; pen.y = (var.yres - line_box_height)/2 * 64; Draw_Glyphs(glyphs, num_glyphs, pen); /* wstr2 */ num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr2, glyphs); compute_string_bbox(glyphs, num_glyphs, &bbox); line_box_width = bbox.xMax - bbox.xMin; line_box_height = bbox.yMax - bbox.yMin; pen.x = (var.xres - line_box_width)/2 * 64; pen.y = pen.y - 24 * 64; Draw_Glyphs(glyphs, num_glyphs, pen); return 0; }