[福利]国内首篇利用freetype的跨平台truetype字体真正轮廓(非位图)获取(带完整qt工程代码)-秒杀GetGlyphOutline

前言

最近在做一款激光打标控制的产品,我的思路是将所有的图元矢量化,但是当做到文字矢量化的时候,真是让我想破了脑袋,后来搜索得知了GetGlyphOutline,就是一个WINAPI,众所周知,WINAPI大都需要一个HDC,但是我使用的Qt,这样就出现了几个问题:

  • Qt中的控件有些是直接渲染的,很难直接获取控件句柄-
  • 不跨平台,以后这一块还是要重写
  • VC的各种变量类型写在Qt里面,调试起来真是一团糟

做了些实验以后,发现这种方法实在是缘木求鱼,然后轻易地搜索到了freeType这一款优秀的字体引擎,下面我们就开始对freetype的探索。

所需基本知识点

trueType字体的一些基本概念

  • 字体(font):不同字符图像的集合

  • 字体外观(font face):一个外观对应一个(.ttf)文件,但是一个字体家族可能占用多个字体文件,因为它包括多种外观,比如字体族Arial,它包括两种外观,于是就有 arial.ttf对应Arial Regular外观,ariali.ttf对应Arial Italic外观,我们习惯把Arial Regular也称为一种字体,实际上它只是一种字体外观

  • 字体文件的基本结构:一般里面有一个或多个字符图(charmap),不同的字符图一般标识不同的平台,所以在一种平台上一般只有一种字符图,这个字符图可以宏观简单的理解为一个key-value,key-字符索引一般就是字符对应编码的编码值,value(字符构成)在truetype一般是对其矢量图形的描述,我们只要将字符编码对应的矢量图形描述得到,就可以进行随心所欲的处理了

trueType字体的基本构成

首现说一下字体轮廓,拿我自己解析出来的一个例子来说吧(这是俺的大名:)):
[福利]国内首篇利用freetype的跨平台truetype字体真正轮廓(非位图)获取(带完整qt工程代码)-秒杀GetGlyphOutline_第1张图片
渲染出来的矢量还是不错的!
轮廓线就是字体轮廓中一条条的封闭曲线:
[福利]国内首篇利用freetype的跨平台truetype字体真正轮廓(非位图)获取(带完整qt工程代码)-秒杀GetGlyphOutline_第2张图片
红色箭头标注的就是字体轮廓线,共有两条;每条轮廓线又由其他直线或曲线组成:

  • 直线
  • 二次Bezier曲线
  • freetype官方说明字体曲线可能包含有(三次Bezier曲线),但经过我的实验以及其他官方资料,truetype字体并不含有三次Bezier曲线

besier曲线定义

由于此处只用到一次贝塞尔(有界直线)和二次贝塞尔曲线
给定点P0、P1,线性bezier曲线只是一条两点之间的直线。这条线由下式给出,且其等同于线性插值。
一次bezier曲线表达式
二次方bezier曲线的路径由给定点P0、P1、P2的函数B(t)追踪:
二次bezier曲线表达式
按照上述插值规则,如果t分割的足够密,就可以得到平滑的曲线

freetype对trueType的解析

解析基本步骤

Created with Raphaël 2.1.2 开始 freetype字体初始化 设置字符编码方式 获取字符对应的编码值 查charmap表获取编码值的索引 根据索引获取轮廓描述 对获取到的轮廓数据进行补偿 相关渲染和处理 结束

freetype字体库初始化(省略了变量声明)

int  error = FT_Init_FreeType( &library );
    if(error)
    {
        printf("load freetype errror!");
    }
    error = FT_New_Face(library,fontFilePath.toStdString().c_str() ,0,&face);
    if ( error == FT_Err_Unknown_File_Format )
        {
        printf("FT_Err_Unknown_File_Format");
    }
    else if(error)
    {
        printf(" another error code means that the font file could not  or simply that it is broken...");
    }
    error = FT_Set_Pixel_Sizes(face, /* handle to face object */
                                      0, /* pixel_width */
                                   8 );/* pixel_height */
    if(error)
    {
        printf("char size set error");
    }

设置字体编码方式

只需一句代码

FT_Select_Charmap(face,FT_ENCODING_UNICODE);

获取字符编码值

wchar_t charX= L'元';

对字体轮廓进行解析

这里我们需要先对FT_Outline这个freetype内置结构体做一个说明:
FT_Outline

  • n_points:轮廓中的点数
  • n_contours 轮廓中轮廓线数
  • points 点坐标数组
  • contours 轮廓线端点索引数组
  • tags 点标记数组

注意:下方全程高能
这里,points是一个FT_Vector记录数组的指针,用来存储每个轮廓点的向量坐标。它表示为一个象素1/64,也叫做26.6固定浮点格式。
contours是一组点索引,用来划定轮廓的轮廓线。例如,第一个轮廓线总是从0点开始,以contours[0]点结束。第二个轮廓线从contours[0]+1点开始,以contours[1]结束,等等。
注意,每条轮廓线都是封闭的,n_points应该和contours[n_controus-1]+1相同。最后,tags是一组字节,用来存放每个轮廓的点标记。
从这儿,大家可能就会感觉以下的步骤很麻烦了!是的,至少会需要两层循环,这还不算完,最难处理的是下面这一堆规定:

轮廓内部点规则描述

  • 每条弧由一系列起点、终点和控制点描述,轮廓的每个点有一个特定的标记,表示它用来描述一个线段还是一条弧。这个标记可以有以下值:
  • FT_Curve_Tag_On 当点在曲线上,这对应线段和弧的起点和终点。其他标记叫做“Off”点,即它不在轮廓线上,但是作为Bezier弧的控制点。
  • FT_Curve_Tag_Conic 一个Off点,控制一个conic Bezier弧
  • FT_Curve_Tag_Cubic 一个Off点,控制一个cubic Bezier弧
  • 下面的规则应用于将轮廓点分解成线段和弧 z 两个相邻的“on”点表示一条线段;
  • 弧z 一个conic Off(二次bezier曲线控制点)在两个on点之间表示一个conic Bezier(二次bezier)弧,off点是控制点,on点是起点和终点;
  • 两个相邻的cubic off(三次bezier曲线控制点)点在两个on点之间表示一个cubic Bezier(三次bezier)弧,它必须有两个cubic控制点和两个on
    点。
  • 两个相邻的conic off(二次bezier曲线控制点)强制在它们正中间创建一个虚拟的on点(坐标为两者中点)。这大大方便定义连续的conic弧。TrueType规范就是这么定义的。

轮廓端点规则描述

  • 如果轮廓的首尾点均为FT_Curve_Tag_On,则不需做任何处理
  • 如果轮廓首点为FT_Curve_Tag_Conic,尾点为FT_Curve_Tag_On,就将尾点作为第一条bezier曲线的首点
  • 如果轮廓首点为FT_Curve_Tag_On,尾点为FT_Curve_Tag_Conic,就将首点作为最后一条bezier曲线的尾点
  • 如果首尾点均为FT_Curve_Tag_Conic,则取两者平均值分别作为第一条bezier曲线的首点和最后一条曲线的尾点。

规则总结

这一串规则看起来非常复杂晦涩,其实总结一下就是,每个轮廓点就是是一个’圆环’的点集,当出现相邻的bezier控制点后,就补偿一个中值。最后,我们’拆环’就可以了

解析算法描述

Created with Raphaël 2.1.2 获取outline中的各个值 单个轮廓线首尾索引值获取 首端点补偿 中点连续的控制点的中值补偿 尾端点补偿 拆分成一段段独立bezier曲线 结束

单个轮廓线首尾索引值获取

Created with Raphaël 2.1.2 单个轮廓线首尾索引值获取 第几条轮廓线 是否是第0条 ? 索引值 0~outline->contours[0] 处理完毕 索引值 outline->contours[contourIndex -1] +1~outline->contours[contourIndex] yes no

首尾端点补偿

按照上述规则即可,具体看附件工程

中值补偿

也是按照上述规则

很多东西看代码比较直接,附上大家最想要的完整工程,记得改一下pro文件里面的lib目录路径:
戳我下载源码

你可能感兴趣的:(Qt)