嵌入式 装载一个字形图像

原创 2013年12月02日 14:52:18

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

相关文章推荐

基于ARM的嵌入式USB图像采集与显示

基于ARM的嵌入式USB图像采集与显示 引言   目前市场上大部分的图像采集与处理系统是基于DSP 芯片的,这种图像采集与处理系统成本高、功耗高、体积约束等特点并不适用于一些简单的应用。随着USB...

OpenCV嵌入式图像处理(四)第一个OpenCV程序

在装完了OpenCV和CUDA之后很自然的事情就是先跑个程序库函数是否安装完毕。对于在Windows下习惯用Visual Studio的同志们来说,可能很多人还不知道可以不用IDE编程(至少我当年转L...

转:图像算法在DSP嵌入式移植中常用的优化方法

以下内容来源于大嘴说图像微博:”大嘴说图像微博入口地址“ 今天大嘴主要介绍一下这些年来本人在做图像算法的嵌入式移植时常采用的优化方法,由于篇幅和时间有限,这里主要列出一个大框,具...
  • lcydhr
  • lcydhr
  • 2014年12月15日 16:51
  • 497

图像算法在DSP嵌入式移植中常用的优化方法

以上内容来源于大嘴说图像微博:”大嘴说图像微博入口地址“ 今天大嘴主要介绍一下这些年来本人在做图像算法的嵌入式移植时常采用的优化方法,由于篇幅和时间有限,这里主要列出一个大框,具体的如果大家有兴趣...

基于ARM+FPGA+多DSP的嵌入式实时图像处理系统

实时图像处理、高速数据运算处理要求其系统具有对数据处理速度快、数据吞吐率高以及多任务处理功能。目前大多数方案都是采用HPI数据传输方式,将ARM和DSP进行组合完成一些图像处理,DSP处理器只是完成图...
  • yuyin86
  • yuyin86
  • 2012年04月18日 13:59
  • 2175

OpenVX嵌入式图像处理(一)硬件平台及软件介绍

Opencv嵌入式图像处理

Opencv嵌入式图像处理(二)Jeston Tk1 安装OpenCV

Tegra 平台为OpenCV提供了GPU加速的功能。除了OpenCV中的GPU模块外,例如核心的Core模块等一系列OpenCV中的常用模块都可以使用GPU加速。但是GPU加速的前提是你已经安装好C...

一个嵌入式监控系统的总体设计

转眼间在这个非IT公司研发部有两年之久了,这个项目算是流程化一个项目的开篇吧,其实前面做了个项目但构架非常糟糕。   刚开始毕业会写写C#代码就以为自己,写写类似五子棋,打地鼠之类的小游戏就觉得自己...
  • skybeer
  • skybeer
  • 2012年06月12日 00:09
  • 393

如何实现一个自己的嵌入式语言y

可嵌入c/c++的规则语言有 lua ,可嵌入java 的有drools,由于某些业务特性,或者说有些特殊需求需要自己定义规则语言的场景,下面是我实现一个规则语言,采用 c++ ,lex ,yacc ...

请不要做一个浮躁的嵌入式工程师!!!

1. 不要看到别人的回复,第一句话就说:给个代码吧!你应该想想为什么。当你自己想出来再参考别人的提示,你就知道自己和别人思路的差异。 2. 初...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:嵌入式 装载一个字形图像
举报原因:
原因补充:

(最多只允许输入30个字)