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

news2024/11/24 19:10:16

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/169557.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【vue2】vue生命周期的理解

🥳博 主:初映CY的前说(前端领域) 🌞个人信条:想要变成得到,中间还有做到! 🤘本文核心:vue生命周期的介绍、vue生命周期钩子函数详解,vue生命周期的执行顺序 目录 …

使用管控平台管理redis集群

1 添加redis集群 在数据库资源中添加redis集群,配置参数并将URL中cluster调整为true。 2 验证配置资源是否正常 3 操作redis数据库中的数据 可以通过使用图形化界面或者命令窗口进行Redis数据库的CRUD 3.1 图形化界面操作 操作Redis字符串列表 3.1.1 新增 右…

Apache Iceberg 背后的设计

原文地址: 阿帕奇冰山:幕后的建筑外观 |德雷米奥 (dremio.com)绝对的精品文章!!!机器翻译和自我调整组成了这篇文章,供大家学习。介绍数据湖的构建希望是实现数据民主化,以允许越来越多的人员、工具和应用程序使用越来越多的数据。实现这一目…

十五天学会Autodesk Inventor,看完这一系列就够了(八),图框自定义

所周知,Autocad是一款用于二维绘图、详细绘制、设计文档和基本三维设计,现已经成为国际上广为流行的绘图工具。Autodesk Inventor软件也是美国AutoDesk公司推出的三维可视化实体模拟软件。因为很多人都熟悉Autocad,所以再学习Inventor&#x…

【数据库数据恢复】华为云mysql数据库数据被delete的数据恢复案例

数据库数据恢复环境: 华为云ECS,linux操作系统; mysql数据库,实例内数据表默认存储引擎为innodb。 数据库故障: 在执行数据库版本更新测试时,用户误将本应在测试库测试的sql脚本执行在生产库中&#xff0c…

拉伯证券|芯片半导体迎来“行业底部出清”,大资金进场迹象明显

近期关于小芯片的利好不断,英特尔近期就发布了根据小芯片技能的处理器,而近期长电科技也在小芯片范畴获得突破。据长电科技在互动平台表明,公司现已完成4nm工艺制程手机芯片的封装,最大封装体面积约为1500平方毫米的体系级封装。长…

人工智能所需高等数学知识大全(收藏版)

来源:投稿 作者:愤怒的可乐 编辑:学姐 不懂数学是学不好人工智能的,本系列文章就汇总了人工智能所需的数学知识。本文是高等数学篇。 另有线代篇和概率论篇 函数与极限 函数 yf(x) ,x是函数f的自变量,y是因变量 函…

数据结构(4)线段树、延迟标记、扫描线

活动 - AcWing 参考-《算法竞赛进阶指南》 一、延迟标记(懒标记) 在线段树的区间查询命令中,每当遇到被查询区间[l,r]完全覆盖节点时,可以直接把节点上的答案作为备选答案返回。我们已经证明,这样操作的复杂度为O(4…

01-React(脚手架+MVC/MVVM+JSX)

使用 create-react-app 构建React工程化项目 安装 create-react-app $ npm i create-react-app -g 「mac需要加sudo」 基于脚手架创建项目「项目名称需要符合npm包规范」 $ create-react-app xxx 目录结构: |- node_modules 包含安装的模块 |- public 页面模板…

79.循环神经网络的从零开始实现

从头开始基于循环神经网络实现字符级语言模型。 这样的模型将在H.G.Wells的时光机器数据集上训练。 和之前一样, 我们先读取数据集。 %matplotlib inline import math import torch from torch import nn from torch.nn import functional as F from d2l import to…

Rockchip开发系列 - 9.watchdog看门狗

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 dts中的watchdog节点watchdog驱动文件TRM watchdog:WDT框图功能描述计数器中断系统复位复位脉冲长度操作流程图寄存器描述寄存器设置…

Nessus Host Discovery

系列文章 Nessus介绍与安装 Nessus Host Discovery 1.启动nessus cd nessus sh qd_nessus.sh2.进入nessus网站 https://192.168.3.47:8834/3.点击【New Scan】 4.选择【Host Discovery】 5.输入name【主机发现】,Description【主机发现】,Targets【…

Android 蓝牙开发——服务启动流程(二)

首先我们要知道,主要系统服务都是在 SystemServer 启动的,蓝牙也是如此: 1、SystemServer 源码路径:/frameworks/base/services/java/com/android/server/SystemServer.java private void startOtherServices(NonNull TimingsT…

labelme(2)json文件转类别灰度图

首先感谢大佬:https://blog.csdn.net/tzwsg/article/details/114653071一、上代码,json2gray.py:#!/usr/bin/python # -*- coding: UTF-8 -*- # !H:\Anaconda3\envs\new_labelme\python.exe import argparse import json import os import os…

go语言中变量和常量的注意点

1、类型转换:大类型可以转换成小类型但是精度丢失;小类型不能转换成大类型。例如: package mainimport "fmt"//golang中使用" var "关键字来定义变量 //定义变量的语法:1、var var_name1[,var_name2,...] va…

day16|654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

654.最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums 构…

深度学习入门基础CNN系列——填充(padding)与步幅(stride)

填充(padding) 在上图中,输入图片尺寸为333\times333,输出图片尺寸为222\times222,经过一次卷积之后,图片尺寸为222\times222,经过一次卷积之后,图片尺寸变小。卷积输出特征图的尺寸…

el-table表头添加勾选框

el-table表头添加勾选框嘚吧嘚实现嘚吧嘚 table的行勾选是比较常规的操作,但是有的时候就有各种奇葩的需求蹦出来。😭 比如最近有一个需求,不仅需要勾选行,还需要勾选列,其实我心中有了一万头可爱的小羊驼&#xff0c…

NISP三级证书含金量如何

国家信息安全水平测试(NationalInformationSecurityTestProgram,通称NISP),是通过中国信息安全测评中心执行塑造我国网络空间安全优秀人才的一个项目。 为培养大量出色的实践型网络安全人才,中国信息安全测评中心上线…

SpringCloud Alibaba微服务 -- Seata的原理和使用

文章目录一、认识Seata1.1 Seata 是什么?1.2 了解AT、TCC、SAGA事务模式?AT 模式前提整体机制如何实现写隔离如何实现读隔离TCC 模式Saga 模式Saga 模式适用场景Saga 模式优势Saga 模式缺点二、Seata安装2.1 下载2.2 创建所需数据表2.2.1 创建 分支表、全局表、锁表2.2.2 创建…