化繁为简、性能提升 -- 在WPF程序中,使用Freetype库心得

12 篇文章 1 订阅
12 篇文章 12 订阅

本人使用WPF开发了一款OFD阅读器,显示字体是阅读器中最重要的功能。处理字体显示有多种方案,几易其稿,最终选用Freetype方案。本文对WPF中如何使用Freetype做简单描述。

OFD中有两种字体:嵌入字体和非嵌入字体。1) 非嵌入字体就是只提供字体名称,不提供字体对应的文件。2)嵌入字体:提供字体文件,字体名称是啥并不影响显示。由于阅读器中需要显示大量文本,必须采用最优的方式显示,否则性能难以满足要求。

WPF字体显示有多种方案。 通常可以将文本呈现分为三层:

  1. 直接使用 Glyphs 和 GlyphRun 对象。
  2. 使用 FormattedText 对象。
  3. 使用高级控件,如 TextBlock 和 FlowDocument 对象。

第三种方案,显然不合适,可行的只有第一和第二种方案。对于嵌入字体,必须采用第一种方案;非嵌入字体可以采用第一和第二种方案。总之,可以采用第三种方案解决一切字体问题。

但是,本人在使用GlyphRun 过程中,遇到很多奇怪的问题,有两方面原因导致的:

      1)越底层的功能使用的人越少,缺少相应资料。

        2)微软在开放底层功能上犹抱琵琶半遮面,导致使用过程中不知所以然,遇到问题难以解决。

本人开发的OFD阅读器最初采用了WPF自带字体方案,遇到很多坑,还有一些坑无论如何也解决不了。痛定思痛,决定采用Freetype库显示字体;经过一番折腾,终于成功了。

历经曲折,获得一番感悟:微软为了使开发者开发更方便,屏蔽了太多底层的东西;这导致开发人员遇到问题时无所适从。不要紧盯着微软不放;另辟蹊径,反而柳暗花明又一村。

使用Freetype显示字体前,需要弄明白字体的本质;字体的本质就是一系列曲线和对应的unicode编码;显示字体就是画曲线,和画一条直线、圆等没有区别。 unicode编码是为了交互用的,从阅读器上复制一行字,其实就是复制字体的unicode编码。字体除了这些属性以外,还有很多概念:字体高度、行间距、baseline、原点等。

使用Freetype,就需要获取每个字体对应的曲线和字体相关的一些列属性。但是,Freetype是用c语言开发的,导出的函数接口难以与c#交互,需要在Freetype的基础上再次封装,以方便c#调用。以获取字体对应的曲线为例,阐述如何进一步封装Freetype。

我们见到的曲线多种多样;但是从底层来看,所有的曲线可分为两类:直线和贝塞尔曲线。这些恰好对应OFD的图形对象(PathObject),看下图:

OFD文件中一段图形对象:

阅读器处理PathObject时,就需要解析这些字符串,生成对应的曲线。Freetype也需要从字库中解析出曲线,其对应的函数是非常复杂的,摘录一段函数:

很显然,c#无法直接使用;考虑PathObject的描述方式,本人眼前一亮,何不将字体曲线描述为PathObject方式,对外输出字符串。函数接口定义如下:

extern "C" FREETYPELIB_EXPORT INT32 Freetype_GetTextPath(UINT64 handle,
    UINT32 nGlyphIndex, INT32 fontSize, char* textPath, INT32 textPathLen);

textPath包含曲线信息,其格式与OFD中的PathObject规范一致,我们就可以用处理PathObject的方式处理字体。
WPF显示曲线使用PathGeometry,需要将textPath转换为PathGeometry。再而,我们就可以使用WPF类DrawingContext画出曲线

class DrawingContext
{
public abstract void DrawGeometry(Brush brush, Pen pen, Geometry geometry);
 ......
}

至此,字体就可以显示出来了。通过这一番操作,我们也对字体的本质有更加深刻的了解。前文只是简单对显示字体做了讲述,要正确处理字体还有大量的工作要做。

Freetype性能如何?

功能正常了,下一步关心的问题就是性能。为了提高性能,从FreeType获取字体的PathGeometry时做缓冲;同一个字体,下次显示时,直接使用上次的字体生成的PathGeometry,不再从Freetype获取字体。
本人做了简单的对比测试,Freetype性能要高于使用FormattedText方案,性能大概提升三倍;与GlyphRun方案没做对比。

总结: 使用FreeType库,可以使我们更好的理解底层处理逻辑,更好的理解字体的本质。抓住了本质,就能直面问题,解决问题就顺畅很多。不同的库、不同函数都有它的应用场景;当微软提供的库不再适合当前应用时,大胆的使用新库,反而可能使我们快速走出困局!

这份文档提供了FreeType 2函数设计与实现的细节。本文档的目标是让开发人员更好的理解FreeType 2是如何组织的,并让他们扩充、定制和调试它。 首先,我们先了解这个的目的,也就是说,为什么会写这个: * 它让客户应用程序方便的访问字体文件,无论字体文件存储在哪里,并且与字体格式无关。 * 方便的提取全局字体数据,这些数据在平常的字体格式普遍存在。(例如:全局度量标准,字符编码/字符映射表,等等) * 方便的提取某个字符的字形数据(度量标准,图像,名字,其他任何东西) * 访问字体格式特定的功能(例如,SFNT表,多重控制,OpenType轮廓表) Freetype 2的设计也受如下要求很大的影响: * 高可移植性。这个必须可以运行在任何环境。这个要求引入了一些非常激烈的选择,这些是FreeType2的低级系统界面的一部分。 * 可扩展性。新特性应该可以在极少改动基础代码的前提下添加。这个要求引入了非常简单的设计:几乎所有操作都是以模块的形式提供的。 * 可定制。它应该能够很容易建立一个只包含某个特定项目所需的特性的版本。当你需要集成它到一个嵌入式图形的字体服务器时,这是非常重要的。 * 简洁高效。这个的主要目标是只有很少cpu和内存资源的嵌入式系统。 这份文档的其他部分分为几个部分。首先,一些章节介绍了的基本设计以及Freetype 2内部对象/数据的管理。 接下来的章节专注于的定制和与这个话题相关的系统特定的界面,如何写你自己的模块和如何按需裁减初始化和编译。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值