一、本文目的

以前的文档中、详细的介绍了FreeType开源字体引擎库的基础知识、基本用法、但并未详细的阐明在TurboCG中、是如何解析出一个文字的轮廓的,本文集中阐述、怎么样使用FreeType开源字体引擎库、读取一个文字的轮廓、获取轮廓关键点(控制点)之后,解析这些关键点;并使用Qt作为辅助GUI接口、绘制出字体的轮廓。

本文虽然集中讲解文字轮廓处理、但为了完整性,也会介绍怎么初始化字体库等等,通过本文的学习、读者能够快速的了解到使用FreeType的步骤流程,并能够使用FreeType进行文字处理,本文包含了使用FreeType的所有基本API调用的全部内容,是一篇短小实用的指南。

二、FreeType库简介

(一)、FreeType架构结构简介

FT可以看作是一组组件,每个组件负责一部分任务,它们包括 :
1、 客户应用程序一般会调用FT高层API,它的功能都在一个组件中,叫做基础层。

2、 根据上下文和环境,基础层会调用一个或多个模块进行工作,大多数情况下,客户应用程序不知道使用那个模块。
3、基础层还包含一组例程来进行一些共通处理,例如内存分配,列表处理、io流解析、固定点计算等等,这些函数可以被模块随意调用,它们形成了一个底层基础API。

(二)、FT中的面向对象

虽然FT是使用ANSI C编写,但是采用面向对象的思想,所以这个库非常容易扩展,因此,下面有一些代码规约。

1、每个对象类型/类都有一个对应的结构类型和一个对应的结构指针类型,后者称为类型或类的句柄类型
设想我们需要管理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、FT_Library类
这个类型对应一个库的单一实例句柄,没有定义相应的FT_LibraryRec,使客户应用无法访问它的内部属性。库对象是所有FT其他对象的父亲,你需要在做任何事情前创建一个新的库实例,销毁它时会自动销毁他所有的孩子,如face和module等。 通常客户程序应该调用FT_Init_FreeType()来创建新的库对象,准备作其他操作时使用。另一个方式是通过调用函数FT_New_Library()来创建一个新的库对象,它在<freetype/ftmodule.h>中定义,这个函数返回一个空的库,没有任何模块注册,你可以通过调用FT_Add_Module()来安装模块。调用FT_Init_FreeType()更方便一些,因为他会缺省地注册一些模块。这个方式中,模块列表在构建时动态计算,并依赖ftinit部件的内 容。(见ftinit.c[l73]行,include FT_CONFIG_MODULES_H,其实就是包含ftmodule.h,在ftmodule.h中定义缺省的模块,所以模块数组ft_default_modules的大小是在编译时动态确定的。)

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直接访问。

三、字体轮廓描述

(一)、轮廓曲线分解

一个轮廓是2D平面上一系列封闭的轮廓线。每个轮廓线由一系列线段和Bezier弧组成,根据文件格式不同,曲线可以是二次和三次多项式,前者叫quadratic或conic弧,它们在TrueType格式中用到,后者叫cubic弧,多数用于Type1格式。
      每条弧由一系列起点、终点和控制点描述,轮廓的每个点有一个特定的标记,表示它用来描述一个线段还是一条弧。这个标记可以有以下值:

FT_Curve_Tag_On当点在曲线上,这对应线段和弧的起点和终点。其他标记叫做“Off”点,即它不在轮廓线上,但是作为Bezier弧的控制点。

FT_Curve_Tag_Conic一个Off点,控制一个conic Bezier弧
FT_Curve_Tag_Cubic 一个Off点,控制一个cubicBezier弧
下面的规则应用于将轮廓点分解成线段和弧
A 、 两个相邻的“on”点表示一条线段;
B、 一个conic Off点在两个on点之间表示一个conic Bezier弧,off点是控制点,on点是起点和终点;
C、 两个相邻的cubic off点在两个on点之间表示一个cubic Bezier弧,它必须有两个cubic控制点和两个on点。
D、最后,两个相邻的conic off点强制、在它们正中间创建一个虚拟的on点。这大大方便定义连续的conic弧。

(二)、轮廓描述符
FT轮廓通过一个简单的结构描述

typedef struct  FT_Outline_

{

short       n_contours;    轮廓中轮廓线数

short       n_points;      轮廓中的点数

FT_Vector* points;       点坐标数组

char*       tags;

short*      contours;    轮廓线端点索引数组

int         flags;         点标记数组

} FT_Outline;

这里,points是一个FT_Vector记录数组的指针,用来存储每个轮廓点的向量坐标。它表示为一个象素1/64,也叫做26.6固定浮点格式。
    contours是一组点索引,用来划定轮廓的轮廓线。例如,第一个轮廓线总是从0点开始,以contours[0]点结束。第二个轮廓线从contours[0]+1点开始,以contours[1]结束,等等。 注意,每条轮廓线都是封闭的,n_points应该和contours[n_controus-1]+1相同。最后,tags是一组字节,用来存放每个轮廓的点标记。

四、 使用FreeType库进行文字轮廓解析实例代码

(一)、FreeType字体库初始。

A、初始化库 FT_Library

FT_Library  library

FT_Error error =FT_Init_FreeType( &library );

B、初始化 FT_Face face

FT_Error  error = FT_New_Face( library, "C:\\Windows\\Fonts\\msuighur.ttf", 0,&face );

创建一个宋体的FT_FACE。

C、设置所要绘制的文字的大小。

设置字体大小,有两种方式,一种是设置尺寸,是用长度、作为度量单位的,另一种方式,是设置像素个数,字体的宽高、以像素来作为度量单位。

直接用用像素作为度量单位来设置字体大小:

FT_Set_Pixel_Sizes(face,560,560);

(二)、Qt绘制字体轮廓线。

通过上面的的设置、就可以使用FreeType来或者文字的轮廓了。获取英文字母“J”的轮廓字槽方法如下:

int charX= 'J';

intiGlyphIndex = FT_Get_Char_Index(face,charX);

FT_Int32 loadflags =FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP;

FT_Error error = FT_Load_Glyph(face,iGlyphIndex,loadflags);

FT_GlyphSlot pGlyphSlot = face->glyph;

FT_Outline* outline = &pGlyphSlot->outline;

至此、字的轮廓信息完全存储在outline中,下面的代码就是拿来解析,并渲染轮廓的。解析的原理,在上一章节中,已经有了具体的阐述、这里只是代码实现,因此,就不再介绍解析的原理了。

QPainter painter(this);

painter.translate(400, 400);

FT_Vector* point;

FT_Vector* limit;

char*       tags;

FT_Vector  v_last;

FT_Vector  v_control;

FT_Vector  v_start;

int first= 0;

for(int n = 0; n < outline->n_contours; n++)

{

int  last =outline->contours[n];

limit= outline->points + last;

v_start= outline->points[first];

v_last  = outline->points[last];

v_control= v_start;

point= outline->points + first;

tags  = outline->tags  + first;

char tag   =FT_CURVE_TAG(tags[0]);

float fpriX = int26p6_to_float(v_control.x);

float fpriY = -int26p6_to_float(v_control.y);

float startX = fpriX;

float startY = fpriY;

while(point < limit)

{

point++;

tags++;

tag = FT_CURVE_TAG(tags[0]);

switch(tag)

{

caseFT_CURVE_TAG_ON:

{

float fEndX = int26p6_to_float(point->x);

float fEndY = -int26p6_to_float(point->y);

QPen pen(RandColor());

painter.setPen(pen);

painter.drawLine(startX,startY,fEndX,fEndY);

startX= fEndX;

startY= fEndY;

}

break;

case FT_CURVE_TAG_CONIC:  //二次Bezier曲线

{

v_control.x= point->x;

v_control.y= point->y;

Do_Conic:

if(point < limit)

{

FT_Vectorvec;

FT_Vectorv_middle;

point++;

tags++;

tag= FT_CURVE_TAG(tags[0]);

vec.x= point->x;

vec.y= point->y;

if(tag == FT_CURVE_TAG_ON)

{

float x1 = int26p6_to_float(v_control.x);

float y1 = -int26p6_to_float(v_control.y);

float x2 = int26p6_to_float(vec.x);

float y2 = -int26p6_to_float(vec.y);

QPen pen(RandColor());

painter.setPen(pen);

QPainterPathpath;

path.moveTo(startX,startY);

path.quadTo(x1,y1,x2,y2);

painter.drawPath(path);

startX= x2;

startY= y2;

continue;

}

if(tag != FT_CURVE_TAG_CONIC)

{

return;

}

v_middle.x= (v_control.x + vec.x) / 2;

v_middle.y= (v_control.y + vec.y) / 2;

float x1 = int26p6_to_float(v_control.x);

float y1 = -int26p6_to_float(v_control.y);

float x2 = int26p6_to_float(v_middle.x);

float y2 = -int26p6_to_float(v_middle.y);

QPenpen(RandColor());

painter.setPen(pen);

QPainterPathpath;

path.moveTo(startX,startY);

path.quadTo(x1,y1,x2,y2);

painter.drawPath(path);

startX= x2;

startY= y2;

v_control= vec;

goto Do_Conic;

}

float x1 = int26p6_to_float(v_control.x);

float y1 = -int26p6_to_float(v_control.y);

float x2 = int26p6_to_float(v_start.x);

float y2 = -int26p6_to_float(v_start.y);

QPenpen(RandColor());

painter.setPen(pen);

QPainterPathpath;

path.moveTo(startX,startY);

path.quadTo(x1,y1,x2,y2);

painter.drawPath(path);

startX= x2;

startY= y2;

goto Close;

}

break;

default:  // FT_CURVE_TAG_CUBIC 三次Bezier曲线

{

FT_Vectorvec1, vec2;

if(point + 1 > limit ||FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)

{

return;

}

vec1.x= point[0].x;

vec1.y= point[0].y;

vec2.x= point[1].x;

vec2.y= point[1].y;

point+= 2;

tags += 2;

if(point <= limit)

{

FT_Vectorvec;

vec.x= point->x;

vec.y= point->y;

float x1 = int26p6_to_float(vec1.x);

float y1 = -int26p6_to_float(vec1.y);

float x2 = int26p6_to_float(vec2.x);

float y2 = -int26p6_to_float(vec2.y);

float x3 = int26p6_to_float(vec.x);

float y3 = -int26p6_to_float(vec.y);

QPenpen(RandColor());

painter.setPen(pen);

QPainterPathpath;

path.moveTo(startX,startY);

path.cubicTo(x1,y1,x2,y2,x3,y3);

painter.drawPath(path);

startX= x3;

startY= y3;

continue;

}

float x1 = int26p6_to_float(vec1.x);

float y1 = -int26p6_to_float(vec1.y);

float x2 = int26p6_to_float(vec2.x);

float y2 = -int26p6_to_float(vec2.y);

float x3 = int26p6_to_float(v_start.x);

float y3 = -int26p6_to_float(v_start.y);

QPenpen(QColor(255,0,0));

painter.setPen(pen);

QPainterPathpath;

path.moveTo(startX,startY);

path.cubicTo(x1,y1,x2,y2,x3,y3);

painter.drawPath(path);

startX= x3;

startY= y3;

goto Close;

}

}

}

Close:

QPenpen(QColor(255,0,0));

painter.setPen(pen);

painter.drawLine(startX,startY,fpriX,fpriY);

first= last + 1;

}

上面的解析代码中。并没有自己去计算二、三次Bezier曲线、而是使用了Qt库中,绘制Bezier曲线的方法。具体代码是:      QPainterPath path;

path.moveTo(startX,startY);

path.cubicTo(x1,y1,x2,y2,x3,y3);

painter.drawPath(path);为了显示轮廓线的具体细节、也就是每个轮廓线的,一条条的曲线、在绘制的时候、每段小曲线段、使用了不同的颜色,这样就可以很清楚的看出一条条的Bezier曲线,或者直线段,同时也绘制了一张单一颜色的图像、两张图像如下。

基于Qt的FreeType字体轮廓解析的更多相关文章

  1. 【Qt编程】基于Qt的词典开发系列<二>--本地词典的设计

    我设计的词典不仅可以实现在线查单词,而且一个重大特色就是具有丰富的本地词典库:我默认加入了八个类型的词典,如下所示: 由于是本人是通信专业,因此加入了华为通信词典.电子工程词典,又由于我喜爱编程,也加 ...

  2. 【Qt编程】基于Qt的词典开发系列<一>--词典框架设计及成品展示

    去年暑假的时候,作为学习Qt的实战,我写了一个名为<我爱查词典>的词典软件.后来由于导师项目及上课等原因,时间不足,所以该软件的部分功能欠缺,性能有待改善.这学期重新拿出来看时,又有很多东 ...

  3. 【Qt编程】基于Qt的词典开发系列<十>--国际音标的显示

    在年前的一篇文章中,我提到要学习Qt.于是在这学期看了一个月的qt.现在大致对qt有了一些了解.但是现在导师又把我调到了android应用开发平台,所以说qt的学习要搁置了.本打算这学期做一个单词查询 ...

  4. 基于QT的webkit与ExtJs开发CB/S结构的企业应用管理系统

      一:源起       1.何为CB/S的应用程序       C/S结构的应用程序,是客户端/服务端形式的应用程序,这种应用程序要在客户电脑上安装一个程序,客户使用这个程序与服务端通信,完成一定的 ...

  5. Qt之JSON生成与解析

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.它基于JavaScript(Standard ECMA-262 3rd Edition - December ...

  6. 基于QT的一个简易的安防

    工程描述 opencv2.4.8 QT5 背景建模后,当有异物入侵时,把入侵的帧写到视频文件 使用BackgroundSubtractorMOG2背景建模 程序基于QT对话框 .pro #------ ...

  7. 基于Qt的第三方库和控件

    ====================== 基于Qt的第三方库和控件 ======================     libQxt --------   http://dev.libqxt.o ...

  8. 【Qt编程】基于Qt的词典开发系列--后序

    从去年八月份到现在,总算完成了词典的编写以及相关技术文档的编辑工作.从整个过程来说,文档的编写比程序的实现耗费的时间更多.基于Qt的词典开发系列文章,大致包含了在编写词典软件过程中遇到的技术重点与难点 ...

  9. 【Qt编程】基于Qt的词典开发系列<六>--界面美化设计

    本文讲一讲界面设计,作品要面向用户,界面设计的好坏直接影响到用户的体验.现在的窗口设计基本都是扁平化的,你可以从window XP与window 8的窗口可以明显感觉出来.当然除了窗口本身的效果,窗口 ...

随机推荐

  1. 使用Groovy进行依赖注入

    为什么选择Groovy? 传统的依赖注入是XML,对我而言,可读性太差,太不美观,强烈地想换一个方式进行依赖注入,Groovy作为XML的替代方案,在Spring4之后被引入,是基于JVM的一门方言, ...

  2. 基于hash的文档判重——simhash

    本文环境: python3.5 ubuntu 16.04 第三方库: jieba 文件寄于github: https://github.com/w392807287/angelo_tools.git ...

  3. java覆写equals方法

    何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念). object规范规定,如果要重写equals(),也要重写hashcode() 如何覆写equals() ...

  4. Oracle 创建用户并且授权

    以sysdba登陆 创建用户:CREATE USER username IDENTIFIED BY password; 授予(角色)权限:GRANT CONNECT,RESOURCE TO usern ...

  5. C#变量命名规范

    1.1命名 1.  所有命名必须有意义 2.  成员变量声明在类的顶端,并且每个变量一行 3.  局部变量声明在引用之前 1.1.1  常量命名 1.  常量名用全大写:MAX_PARAMETER_C ...

  6. CSDN书籍下载

    http://download.csdn.net/user/vanridin/uploads/38 (2016英文书下载) http://download.csdn.net/user/ramissue ...

  7. Windows Phone 8初学者开发—第9部分:Windows Phone 8模拟器概述

    原文 Windows Phone 8初学者开发—第9部分:Windows Phone 8模拟器概述 第9部分:Windows Phone 8模拟器概述 原文地址: http://channel9.ms ...

  8. hadoop的WordCount样例

    package cn.lmj.mapreduce; import java.io.IOException; import java.util.Iterator; import org.apache.h ...

  9. java排序方法中的选择排序方法

    每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完. package array; //选择排序方法 public class arra ...

  10. php四舍五入函数(floor、ceil、round与intval)

    原文链接:php四舍五入函数(floor.ceil.round与intval) PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法 ...