FreeType是一个用C语言实现的字体光栅化引擎制作的一个函式库.它可以用来将字符栅格化并映射成位图以及提供其它字体相关业务的支持.FreeType也是一个跨平台的字体库,下面实例以 Windows环境加DX9.0实现.
接下来讲一下简易的字形装载,包括以下内容
* 初始化库
* 通过创建一个新的 face 对象来打开一个字体文件
* 以点或者象素的形式选择一个字符大小
* 装载一个字形(glyph)图像,并把它转换为位图
* 渲染一个简单的字符串
1) 初始化库
简单地创建一个FT_Library类型的变量,例如library,然后象下面那样调用函数FT_Init_FreeType:
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Library m_FT2Lib;
FT_Error error = FT_Init_FreeType( &m_FT2Lib);
if (error)
{
// 当初始化库时发生了一个错误
}
这个函数,它创建一个FreeType 2库的新实例,并且设置句柄library为它.它装载库中FreeType所知道的每一个模块.
返回值为0的错误代码始终意味着操作成功了,否则,返回值指示错误,library设为NULL。
2) 装载一个字体face
a.从一个字体文件装载
应用程序通过调用FT_New_Face创建一个新的face对象.一个face对象描述了一个特定的字样和风格.例如,"黑体"(simhei.ttf文件).
FT_Library m_FT2Lib; //库的句柄
FT_Face m_FT_Face; // face对象的句柄
...
FT_Error error = FT_New_Face( m_FT2Lib, "/font/simhei.ttf",0,&m_FT_Face);
if (error)
{
... 装载出错
}
FT_New_Face函数
FT_New_Face( FT_Library library, // 一个FreeType库实例的句柄,face对象从中建立
const char* filepathname, // 字体文件路径名(一个标准的C字符串),这个索引指示你想装载的face,Index 0总是正确的.
FT_Long face_index, // 某些字体格式允许把几个字体face嵌入到同一个文件中
FT_Face *aface );Library // 一个指向新建的face对象的指针,当失败时其值被置为NULL
要知道一个字体文件包含多少个face,只要简单地装载它的第一个face(把face_index设置为0),face->num_faces的值就指示出了有多少个face嵌入在该字体文件中.
b.从内存装载
如果你已经把字体文件装载到内存,你可以简单地使用FT_New_Memory_Face为它新建一个face对象,如下所示:
FT_Library m_FT2Lib; //库的句柄
FT_Face m_FT_Face; // face对象的句柄
....
FT_Error error = FT_New_Memory_Face( library, // 一个FreeType库实例的句柄,face对象从中建立
buffer, // 缓存的第一个字节
size, // 缓存的大小(以字节表示)
0, // face索引
&face );
if ( error ) { ...装载失败 }
FT_New_Memory_Face简单地用字体文件缓存的指针和它的大小(以字节计算)代替文件路径.除此之外,它与FT_New_Face的语义一致.
3) 设置像素大小
error = FT_Set_Pixel_Sizes(
face, //face对象句柄
0, // 象素宽度
14 ); // 象素高度
这个例子把字符象素设置为14x14象素.尺寸中的任一个为0意味着"与另一个尺寸值相等"
4) 设置字符表
error = FT_Select_CharMap(
face, // 目标face对象
FT_ENCODING_UNICODE ); // 编码
这种方式只限于你所需的编码已经有对应的枚举定义在FT_FREETYPE_H中.至于没有的,我们可以遍历num_charmaps查找出face中所支持的字符表.这个不怎么常用,在这也不多扯了.
5) 装载一个字形图像
a) 装载一个字符的glyph
error = FT_Load_Char(
m_FT_Face,
ch, // 字符索引
FT_LOAD_RENDER | FT_LOAD_TARGET_LIGHT ); // 装载标志
load_flags的值是位标志集合,用位运算来表示,是用来指示某些特殊操作的.其默认值是FT_LOAD_DEFAULT即0
获取位图描述符
FT_GlyphSlot slot = m_FT_Face->glyph;
FT_Bitmap bitmap = slot->bitmap;
b) 把位图数据拷贝自己定义的数据区里
因为FreeType中生成的每个字符位图的大小是不一样的,所以,我们还得记录一下,图的大小及一些偏移值等.这些值用来我们渲染字符串时对齐排版用.这些记录后,我们还需要将数据转成我们自己需要的数据格式.
face中生成的图片有二种模式FT_RENDER_MODE_NORMAL一个高质量的抗锯齿(256级灰度)位图.还有一个是生成黑白位图FT_RENDER_MODE_MONO标志.下面以DX9.0来说.
FT_RENDER_MODE_NORMAL这个模式下,我们将这个256级灰度转成纹理的alpha值,这样就可以行到一个纹理.
FT_RENDER_MODE_MONO模式下,我们将0作为alpha的0,1作为alpha的255,这样也可以得到一个这样的纹理.
LPDIRECT3DTEXTURE9 d3d9_texture = NULL;
if (m_pDevice->CreateTexture(width, height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &d3d9_texture, NULL) == D3D_OK)
{
D3DLOCKED_RECT locked_rect;
d3d9_texture->LockRect(0, &locked_rect, NULL, 0);
switch (m_FT_Face->glyph->bitmap.pixel_mode)
{
case FT_PIXEL_MODE_GRAY:
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
unsigned char _vl = (x>=bitmap.width || y>=bitmap.rows) ? 0 : bitmap.buffer[x + bitmap.width*y];
byte* destination_pixel = ((byte*) locked_rect.pBits) + locked_rect.Pitch * y + x * 4;
destination_pixel[0] = 0xff; // b
destination_pixel[1] = 0xff; // g
destination_pixel[2] = 0xff; // r
destination_pixel[3] = _vl; // a
}
}
}
break;
case FT_PIXEL_MODE_MONO:
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
unsigned char _vl = 0;
if(bitmap.buffer[y*bitmap.pitch + x/8] & (0x80 >> (x & 7)))
_vl = 0xFF;
else
_vl = 0x00;
byte* destination_pixel = ((byte*) locked_rect.pBits) + locked_rect.Pitch * y + x * 4;
destination_pixel[0] = 0xFF;
destination_pixel[1] = 0xFF;
destination_pixel[2] = 0xFF;
destination_pixel[3] = _vl;
}
}
}
break;
}
d3d9_texture->UnlockRect(0);
}
这样,一个字符的纹理图片就装载好了.只要渲染出来就可以了.
装下来看完整的代码实例吧.
以上程序代码实例下载: VDISK网盘下载
本文参考 <The FreeType 2 Tutorial> 和 Unigine引擎SDK中FreeType实现
原文链接:http://zengwu3915.blog.163.com/blog/static/278348972011911103541256?suggestedreading

下载
像前面FreeType 字体库使用 -- 简易的字形装载中讲的那样,只是简单的显示一下没问题,但如果在实际应用中,那样使用,一直在不停的切换纹理是一些非常费的,一种极度不推荐的方法.要提高效率,可以从减少纹理的切换这一点不入手考虑.下面讲一下大致的思路吧.
1) 动态从FreeType库中加载所需字体.
对于汉字来说,一个字体库中包含几千个字,初始化时加载所有的文字是很纠结的,而且很多字体在整个程序运行过程中都用不到.所以,我们对于从FreeType字体库加载字体是用到时加载,不用不加载的原则.
2) 创建大纹理贴图.
我们可以在程序初始化的时候,建立几张大的空纹理,然后动态加载的字符数据填写到这些大纹理上,渲染时用UV纹理坐理来裁剪.创建的纹理过大,加载会过慢,太小纹理的切换次数也会过多.这个要根据具体的应用去创建.
像偶现在开发的游戏中,创建四张256*256的纹理,相对来说是比较适中的.游戏中使用12~24号字体,一张纹理上也能填充上200左右的字,游戏中用到的字也不算多.大多情况下,在一张纹理上就能满足游戏中所有的字了.如果是文字编辑等文字较多的程序来说,可以再把创建的纹理调大一点.
3) 创建字符渲染缓存.
没有必要在每次要渲染字符串的时候,都把数据送给设备去渲染.这样要渲染字符串调用的地方太多,切换纹理的频率也不会低.我们可以把一帧中所有要渲染的字符,按字符对应纹理按UV和渲染位置缓存在对应的BF列表中.然后在统一渲染.这样切换一次就把一张大纹理上所要渲染的所有的字符都渲染出来了.大大减少了纹理的切换次数.
大致的思路是这样的,附上简单的用应代码. VDISK网盘下载
原文链接:http://zengwu3915.blog.163.com/blog/static/278348972011108115939975/

下载