一、本文目的

以前的文档中、详细的介绍了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. 基于vc的freetype字体轮廓解析_字体术语集

    @font-face 能够在服务器上自定义的屏幕字体 accent 读音符号 alphabet 字母列表,书写特定语言所需的字母和符号集合 alternates 备选字符 / 备用体 ampersan ...

  2. 基于vc的freetype字体轮廓解析_MulayCap:基于多层表达的单目彩色相机的人体动作捕捉新方案...

    MulayCap: Multi-layer Human Performance Capture Using A Monocular Video Camera 最近几年,深度学习的发展为基于单目RGB相 ...

  3. 基于vc的freetype字体轮廓解析_2020款丰田坦途功能详解析 价格下调折扣多_搜狐汽车...

    本文车型速览 × 除了文章作者的主观观点外,我们正尝试基于全网可查的客观数据,为您提供中立.客观的参考依据: 本文部分车型速览: 畅销车型质量排行top10 展开 × 微信扫码,直接一次看完附近所有城 ...

  4. QT怎么设置字体轮廓、字体位置、字体样式、字体间距、窗口背景色大小、隐藏鼠标图标

    QT怎么设置字体轮廓.字体位置.字体样式.字体间距.窗口背景色大小.隐藏鼠标图标 原创 2017年12月14日 19:05:21 标签: qt / ui 277 在qt中经常会对字体设置一些属性字体轮 ...

  5. linux提取ttf字体轮廓,[TTF字体]提取TTF字体的轮廓(二)

    一. TTF字体轮廓解析与绘制 1.1 数据提取 void CHYTTFCharacter::InitTTPOLY() { if(0 == m_pTTPOLYData)// GetGlyphOutli ...

  6. 基于QT的【第一个项目】设计+所有组件配合使用+网络编程局域网通信+文件IO操作+登录界面和头像+多界面跳转+JSON数据解析+表情包制作

    基于QT的第一个项目+所有组件配合使用+网络编程局域网通信+文件IO操作+登录界面和头像+多界面跳转+JSON数据解析+表情包制作 第一阶段 网络编程局域网TCP/IP聊天QT实现 main.c ma ...

  7. qt linux字体,Qt字体轮廓的绘制

    绘制的过程如下: 1     QFont font; 2     font.setPointSize(this->height() * 2 * mZoomRatio / 3); 3     fo ...

  8. 基于QT Creator 5.14的仿QQ聊天系统【UDP通讯】

    一.使用工具 本次整个项目的开发基于Qt Creator 5.14.2进行的开发与调试,主要通信功能采用UDPSocket完成,是我学习Qt过程中联系的小案例,过程中遇到的bug和完整代码会尽量展现出 ...

  9. ac和av的结构linux系统,基于nxp MCIMX6S6AVM08AC的汽车电子主板解析之软件系统等设置!...

    续接上篇文章:基于nxp MCIMX6S6AVM08AC的汽车电子主板解析之硬件系统.调试串口(两篇文章衔接的有点久,还望谅解) 3.UBoot Hack 他的这个UBoot并不是SDK里面的Uboo ...

最新文章

  1. 自己封装线程(Demo)
  2. 深入剖析 iOS 编译 Clang LLVM(编译流程)
  3. C++和Java中成员数据名和成员函数名的冲突问题
  4. gitlab 开源项目 星_Docker实战之Gitlab搭建
  5. go 声明二维数组_一篇文章了解Go语言中数组Arrays的使用内幕
  6. Jn建站系统2.0源码 附视频安装教程
  7. DataView对象的用法 1207
  8. JavaScript变异与非变异数组方法
  9. 中的挂起是什么意思_仪表板亮奇怪指示灯,乌龟晒太阳是什么意思?老司机:不懂别上路...
  10. http协议中content-length 以及chunked编码分析
  11. UVALive5910 UVA1641 POJ4022 ASCII Area【水题+输入输出】
  12. python截图识别文字_python截图并转换文字
  13. 毕业设计:深度学习卷积神经网络垃圾分类系统 - 深度学习 神经网络 图像识别 垃圾分类 算法 小程序
  14. 项目管理如何建立有效的团队沟通机制
  15. 扫地机器人噪音响_扫地机器人噪音大的解决方法
  16. 如何使用内网穿透,将自己的内网接口暴露到外网
  17. opensuse12.2 KDE 使用环境配置
  18. s11 day103 luffy项目结算部分+认证+django-redis
  19. 用梯度下降法求根号2的值
  20. ArcGIS API For Javascript 4.15 绘制地图:在地图上测距离、测面积和在不同图层上搜索

热门文章

  1. pr无法启动此程序因为计算机中丢失api,pr2017安装丢失的api
  2. 判别分析(Discriminate Analysis)
  3. 助力创业者成就未来,亚马逊云科技优势何在?
  4. 简述物联网感知技术_物联网综合测试试题及答案
  5. 如何撰写计算机SCI论文的引言部分 - 易智编译EaseEditing
  6. 正则表达式(regular expression [regExp])
  7. Java-对象数组以及内存图解
  8. 今天说一件细心的事情
  9. 马平福为华侨创作中国书法艺术品受好评
  10. 怎么在电脑上玩电击文库零境交错 电击文库零境交错电脑版教程