FreeType的使用

FreeType是一款字体服务库,它支持多种字体,并且提供高效,高质量的文字。

freetype相关介绍见:http://blog.csdn.net/ganxingming/archive/2006/06/05/774796.aspx


1, 编译FreeType
解压后进入./include/freetype,复制config到当前文件夹,随便去个什么名,这里我用custom_config

然后进入 ./builds/win32/visualc
这里是vs工程,因为FreeType的支持相当广泛,有很多我们不需要东西,因此需要重新编译FreeType。
而刚才我们复制的文件夹中,就是FreeType的可定制文件

打开工程后,将你刚才复制的文件夹加入到工程中,即custom_config
然后打开ft2build.h
可以看到这里包含了一个文件,
#include <freetype/config/ftheader.h>
这个文件就是用于定制FreeType的主要文件,我们要做的就是创建自己的定制文件,将其替代这个文件,将其改成
#include <freetype/custom_config/ftheader.h>
custom_config是你自己的文件夹哈

然后进入custom_config/ftheader.h
修改这几行
#define FT_CONFIG_CONFIG_H   <freetype/config/ftconfig.h>
#define FT_CONFIG_STANDARD_LIBRARY_H   <freetype/config/ftstdlib.h>
#define FT_CONFIG_OPTIONS_H   <freetype/config/ftoption.h>
#define FT_CONFIG_MODULES_H   <freetype/config/ftmodule.h>
改成
#define FT_CONFIG_CONFIG_H   <freetype/custom_config/ftconfig.h>
#define FT_CONFIG_STANDARD_LIBRARY_H   <freetype/custom_config/ftstdlib.h>
#define FT_CONFIG_OPTIONS_H   <freetype/custom_config/ftoption.h>
#define FT_CONFIG_MODULES_H   <freetype/custom_config/ftmodule.h>


接着进入custom_config/ftmodule.h
这个文件是FreeType的模块注册,我把它改成了
/*FT_USE_MODULE(autofit_module_class)*/
FT_USE_MODULE(tt_driver_class)
/*FT_USE_MODULE(t1_driver_class)*/
/*FT_USE_MODULE(cff_driver_class)*/
/*FT_USE_MODULE(t1cid_driver_class)*/
/*FT_USE_MODULE(pfr_driver_class)*/
/*FT_USE_MODULE(t42_driver_class)*/
/*FT_USE_MODULE(winfnt_driver_class)*/
/*FT_USE_MODULE(pcf_driver_class)*/
/*FT_USE_MODULE(psaux_module_class)*/
/*FT_USE_MODULE(psnames_module_class)*/
/*FT_USE_MODULE(pshinter_module_class)*/
/*FT_USE_MODULE(ft_raster1_renderer_class)*/
FT_USE_MODULE(sfnt_module_class)
FT_USE_MODULE(ft_smooth_renderer_class)
/*FT_USE_MODULE(ft_smooth_lcd_renderer_class)*/
/*FT_USE_MODULE(ft_smooth_lcdv_renderer_class)*/
/*FT_USE_MODULE(bdf_driver_class)*/
只留下了3个模块

好,现在FreeType定制完毕了,但是还有个问题,FreeType是编译的静态链接库,我们一般是使用动态链接库的,
因此需要将其修改成动态链接库。

进入custom_config/ftconfig.h,将
#ifndef FT_EXPORT

#ifdef __cplusplus
#define FT_EXPORT( x )   extern "C"   x
#else
#define FT_EXPORT( x )   extern   x
#endif

改成

#ifdef   DLL_EXPORT
#undef   DLL_EXPORT
#define DLL_EXPORT   __declspec(dllexport)
#else
#define DLL_EXPORT   __declspec(dllimport)
#endif /* !DLL_EXPORT */

#ifndef FT_EXPORT

#ifdef __cplusplus
#define FT_EXPORT( x ) extern "C" DLL_EXPORT x
#else
#define FT_EXPORT( x ) extern DLL_EXPORT x
#endif

接下来修改工程,进入工程属性的Release Multithreaded配置,我们编译多线程版本哈
进入常规,修改配置类型为动态库
进入C/C++ -> 预处理器,在预处理器定义中加入DLL_EXPORT

好了,现在我们可以开始编译了。编译成功后的目录在objs/release_mt,现在可以拷贝
freetype.dll,freetype.lib和整个include文件夹,到我们的工程中了

2, 使用FreeType
这里就请大家参考帖子
http://www.unixresources.net/lin ... 0/59/21/592188.html 
里面有详细的介绍。

下面,我说一下自己使用FreeType的心得
1, 得到字符的正确绘制位置

首先在创建好FTFace,Freetype推荐使用基线作为绘制基准,但是通常都是设置字符左上角的位置开始绘制。需要获

得基线到字符轮廓最高点的距离,这个信息放在

Ascender = FTFace->size->metrics.ascender   >> 6; // 基线到字符轮廓最高点的距离, 由于是26.6的定点数,

因此获取整数部分需要除以64

然后每个字符的高度是不同的,Freetype生成的bitmap一般刚刚好包围到字符,比如a和l的bitmap图高度是不同的。

因此还需要获得每个字符的偏移宽度和高度,这两个信息放在

bitmap_left = FTFace->glyph->bitmap_left;   // 字符距离左边界的距离
bitmap_top = FTFace->glyph->bitmap_top; // 字符最高点距离基线的距离

好了,现在假设要在(posx, posy)处绘制字符,(charposx, charposy)表示字符的正确绘制位置

charposx = posx + bitmap_left;
charposy = posy + Ascender - bitmap_top;

2,行距
字符轮廓最大高度放在
FTFace->size->metrics.height       // 字符轮廓最大高度, 26.6定点数
但是我实测发现,这个高度太高了点,所以我一般是这样用的

Descender = FTFace->size->metrics.descender >> 6;   // // 基线到字符轮廓最低点的距离
FontHeight   = ((FTFace->size->metrics.height >> 6) + Ascender + Descender) / 2;

然后还可以在FontHeight上加上一个固定高度,比如1或2


3,字符颜色
Freetype生成的图是8bit灰度图(也有别的,不过8bit好看些),文字部分为白色,背景为黑色。
这就有个麻烦,一般字体都是黑的,那好如果把字体颜色取反,就黑的变白的,白的变黑的了。
可是呢,要加上颜色怎么办?这就是Freetype的文字是白色的原因,因为是8bit灰度图,因此不是黑色或白色的地方

,颜色就成了一个比例因子,只需将其与想要设置的颜色相乘再除以256即可,比如灰度图中某点的颜色是156,想要

设置的颜色是RGB(127, 42, 186),那么实际的颜色是

(127 * 156 >> 8, 42 * 156 >> 8, 186 * 156 >> 8)

回到之前说的,如果将黑的变白的,白的变黑的,那么比例因子就要再取反一次,麻烦了。
而且,如果字体被设置过颜色后,灰度图的比例因子效果就丧失了,不能再被设置成其他颜色了(重新获取灰度图就要

花多余的时间了)。
解决方法是,把8bit灰度图保存在alpha通道中。如果想设置颜色就从alpha通道中获取灰度值即可。

4,把字符变的好看些
上面把灰度图保存在alpha通道中了,如果你的驱动支持alpha混合,那么恭喜你了,字符能和背景混合。
不错,灰度图还兼有alpha功能,如果想好看些就做alpha混合吧。
我的驱动不支持alpha混合,只好自己混合了。

源自 http://hi.baidu.com/yq_sun2008/blog/item/cbc18bfb5a52c71e6c22eb04.html




文字的显示依赖于字体字库,大致的字体字库分为点阵字库、笔画字库和轮廓字库。

点阵字库:缺点比较明显,缩放存在锯齿,渲染旋转等操作相对复杂,且效果不理想,先大多用在嵌入式行业(基本抛弃),常见格式有bdf,pcf,fnt,hbf,hzf等。

笔画字体:不讨论。

轮廓字体:即矢量字体,利用字体轮廓及填充实现字体显示,优势明显,渲染缩放较容易,但效率相对低些(相对于嵌入式)

 

简单来说,freetype为字体字库提供了一套解决方案,支持文字字体渲染等操作,主要还是其为C语言编写,跨平台,为很多不支持矢量字体格式的嵌入式系统提供使用嵌入式字体的可能,且效率不低。

基本流程为:
加载字体字库文件-> 查找待显示的文字索引-> 渲染操作(若反走样处理)->处理为位图数据->显示

freetype官网http://freetype.sourceforge.net/index2.html

下面为在XP下显示中文字体的实例,在官方下载源码,在../freetype-2.4.2/builds/win32/vc2008/下打开工程,编译为链接库,新建VS2008的MFC程序,加载freetype242.lib库。

 

简单考虑,直接在MFC中的draw函数中画出一个中文汉字。便于显示,使用GDI+画出汉字,因此首先对GDI+进行初始化等操作(GDI+的相关知识不讨论,不清楚可以留言或索取GDI+文档,网上也可以搜搜)

在view.h中添加头文件声明

 #include <ft2build.h>

#include FT_FREETYPE_H


在view.h中添加成员变量

 public :

FT_Library library;

FT_Face face;

 

在view.cpp的构造函数中添加

 bool bError = FT_Init_FreeType(&library); // 初始化库

if (!bError)// 是否初始化成功

{

}

bError = FT_New_Face(library, "C://WINDOWS//Fonts//simhei.ttf" ,0, &face); // 加载一个字库文件,这里为黑体中文字库


FT_Select_Charmap(face,FT_ENCODING_UNICODE);// 设定为UNICODE,默认也是


bError = FT_Set_Char_Size(face, 0, 16*64, 300, 300); // 设定字体字符宽高和分辨率


在::OnDraw(CDC* pDC)中添加代码

 bool bError;

wchar_t wChar= _T( 'A' );

FT_UInt glyph_index = FT_Get_Char_Index(face, wChar);

bError = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); // 装载字型图像到字形槽

if (face->glyph->format != FT_GLYPH_FORMAT_BITMAP)

{

bError = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO); // 第二个参数为渲染模式,这里渲染为1位位图(黑白位图),若为FT_RENDER_MODE_NORMAL则渲染为256级灰度图

}


这里便可以通过face->glyph->bitmap获得字符“A”的位图数据了,bitmap中存放了如位图的宽高、色深,调色板等信息,便可以通过GDI+绘制该图像了 

 

这部分代码不多解释,只是显示位图数据,这里face->glyph->bitmap是没有调色板的1位位图,源于使用FT_RENDER_MODE_MONO渲染模式



加载一位图,可以使用LoadImage:

HANDLE LoadImage(HINSTANCE hinst,LPCTSTR lpszName,UINT uType,int cxDesired,int CyDesired,UINT fuLoad);

LoadImage可以用来加载位图,图标和光标

加载时可以规定加载图的映射到内存的大小:

    cxDesired:指定图标或光标的宽度,以像素为单位。如果此参数为零并且参数fuLoad值中LR_DEFAULTSIZE没有被使用,那么函数使用目前的资源宽度。

 cyDesired:指定图标或光标的高度,以像素为单位。如果此参数为零并且参数fuLoad值中LR_DEFAULTSIZE没有被使用,那么函数使用目前的资源高度。

LoadImage的返回值是相关资源的句柄。因为加载的是位图所以返回的句柄是HBITMAP型的(需要强制转换)。

 

延伸理解 HBITMAP/CBitmap/BITMAP:

HBITMAP是bitmap的指针,

msdn中如是:Handle to a bitmap.typedef HANDLE HBITMAP;

CBitmap是mfc中封装bitmap的类;

msdn中:

Encapsulates(囊括) a Windows graphics device interface (GDI) bitmap and provides member functions to manipulate(操作) the bitmap.

BITMAP是一个结构体,封装着bitmap的一些信息。定义了逻辑位图的高,宽,颜色格式和位值。

MSDN中如是:This structure defines the type, width, height, color format, and bit values of a bitmap.

 

三者之间的关系转换:

HBITMAP hBitmap;

CBitmap bitmap;

BITMAP bm;

//下面是三者之间的联系:

bitmap.Attach(hBitmap);//由HBITMAP 得到关联的CBitmap

bitmap.GetBitmap(&bm); // 由CBitmap 得到关联的BITMAP 
hBitmap=(HBITMAP)bitmap.GetSafeHandle();//由CBitmap得到相关的HBITMAP

 

BITMAP结构具有如下形式:
typedef struct tagBITMAP

     int      bmType;
     int      bmWidth;//宽
     int      bmHeight;//高
     int      bmWidthBytes;
     BYTE     bmPlanes;
     BYTE     bmBitsPixel;
     LPVOID bmBits;
}  BITMAP;

 

延伸理解下Attach/Detach:

  attach是把一个C++对象与一个WINDOWS对象关联,直到用detach则把关联去掉。  
  如果attach了以后没有detach,则C++对象销毁的时候WINDOWS对象跟着一起完蛋。  
  attach了以后,C++对象的指针和WINDOWS对象的HWND会有一个映射关系,其作用相当于你直接用一个C++对象去Create一个WINDOWS对象,例如   CEdit   edit;   edit.create(...)  
  并且此映射是永久的,知道此对象完蛋为止。  
  如果用类似GetDlgItem函数也可以返回一个指针,并可以强制转换。GetDlgItem会到映射表里找。  
  有2种映射表,一中是永久的,一种是临时的。  
  直接用C++对象创建的WINDOWS对象或者是通过attach的对象的映射关系都被放到永久表中,否则就在临时表中创建映射。  
  所以GetDlgItem不推荐你保存返回的指针,因为你很难保证你的WINDOWS对象跟C++对象的关联是否放在永久表中。  
  如果映射是放在临时表中,那么在空闲时间会被自动删除。  
  用attcah完全是为了方便用MFC类的成员函数去操纵WINDOWS对象。 



位图文件头
其结构定义如下:
typedef struct tagBITMAPFILEHEADER{

 WORDbfType; // 位图文件的类型,必须为BM
DWORD bfSize; // 位图文件的大小,以字节为单位
WORDbfReserved1; // 位图文件保留字,必须为0
WORDbfReserved2; // 位图文件保留字,必须为0
DWORD bfOffBits; // 位图数据的起始位置,以相对于位图
// 文件头的偏移量表示,以字节为单位
} BITMAPFILEHEADER;

位图文件头分4部分,共14字节:

名称 占用空间 内容 实际数据
bfType 2字节 标识,就是“BM”二字 BM
bfSize 4字节 整个BMP文件的大小 0x50(80)
bfReserved1/2 4字节 保留字,没用 0
bfOffBits 4字节 偏移数,即 位图文件头+位图信息头+调色板 的大小 0x36(54)

注意,Windows的数据是倒着念的,这是PC电脑的特色。如果一段数据为50 1A 25 3C,倒着念就是3C 25 1A 50,即0x3C251A50。因此,如果bfSize的数据为50 00 00 00,实际上就成了0x00000050,也就是0x50。

 

位图信息头
BMP位图信息头数据用于说明位图的尺寸等信息。


typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本结构所占用字节数
LONGbiWidth; // 位图的宽度,以像素为单位
LONGbiHeight; // 位图的高度,以像素为单位
WORD biPlanes; // 目标设备的级别,必须为1
WORD biBitCount// 每个像素所需的位数,必须是1(双色),
// 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),
// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
DWORD biSizeImage; // 位图的大小,以字节为单位
LONGbiXPelsPerMeter; // 位图水平分辨率,每米像素数
LONGbiYPelsPerMeter; // 位图垂直分辨率,每米像素数
DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数
DWORD biClrImportant;// 位图显示过程中重要的颜色数
} BITMAPINFOHEADER;

位图信息头共40字节:

名称 占用空间 内容 实际数据
biSize 4字节 位图信息头的大小,为40 0x28(40)
biWidth 4字节 位图的宽度,单位是像素 2
biHeight 4字节 位图的高度,单位是像素 3
biPlanes 2字节 固定值1 1
biBitCount 2字节 每个像素的位数
1-黑白图,4-16色,8-256色,24-真彩色 0x18(24)
biCompression 4字节 压缩方式,BI_RGB(0)为不压缩 0
biSizeImage 4字节 位图全部像素占用的字节数,BI_RGB时可设为0 0x1A
biXPelsPerMeter 4字节 水平分辨率(像素/米) 0xB12(2834)
biYPelsPerMeter 4字节 垂直分辨率(像素/米) 0xB12(2834)
biClrUsed 4字节 位图使用的颜色数
如果为0,则颜色数为2的biBitCount次方 0
biClrImportant 4字节 重要的颜色数,0代表所有颜色都重要 0

作为真彩色位图,我们主要关心的是biWidth和biHeight这两个数值,两个数值告诉我们图像的尺寸。biSize,biPlanes,biBitCount这几个数值是固定的。想偷懒的话,其它的数值可以一律用0来填充。


展开阅读全文

没有更多推荐了,返回首页