知识大全 使用 DirectWrite 和最新 C++ 管理字体
Posted 知
篇首语:努力学习,勤奋工作,让青春更加光彩。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 使用 DirectWrite 和最新 C++ 管理字体相关的知识,希望对你有一定的参考价值。
使用 DirectWrite 和最新 C++ 管理字体 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
DirectWrite 是一种相当强大的文本布局 API 它支持从 XAML 和 Office 的 Windows 运行时 (WinRT) 实现到 Internet Explorer 和更高版本的几乎所有领先 Windows 应用程序和技术 它本身并不是呈现引擎 但与 Direct D 有很近的关系 是 Direct D 在 DirectX 系列中的同级产品 当然 Direct D 是首要的硬件加速即时模式图形 API
您可以将 DirectWrite 与 Direct D 结合使用 以提供硬件加速的文本呈现 说明一下 之前我在 DirectWrite 方面的著述并不多 我不希望您认为 Direct D 只是 DirectWrite 呈现引擎 Direct D 远不只如此 DirectWrite 还有其他很多功能 在本月的专栏中 我将演示一些使用 DirectWrite 可以完成的任务 看看最新 C++ 是如何帮助简化编程模型的 DirectWrite API
我将使用 DirectWrite 探究系统字体集 首先 我需要获取 DirectWrite 工厂对象 这是编写任何要使用 DirectWrite 的出色排版功能的应用程序的第一步 与大多数 Windows API 相同 DirectWrite 也依赖于 基础内容 我需要调用 DWriteCreateFactory 函数来创建 DirectWrite 工厂对象 此函数返回一个指向该工厂对象的 接口
ComPtr<IDWriteFactory > factory;
IDWriteFactory 接口是今年早些时候随 Windows 和 DirectX 推出的最新版本 DirectWrite 工厂接口 IDWriteFactory 继承自 IDWriteFactory 而 IDWriteFactory 继承自 IDWriteFactory 后者是原始的 DirectWrite 工厂接口 它公开了大部分工厂功能
我将基于前面的 ComPtr 类模板调用 DWriteCreateFactory 函数
HR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED __uuidof(factory) reinterpret_cast<IUnknown **>(factory GetAddressOf())));
DirectWrite 包含一项名为 Windows 字体缓存服务 (FontCache) 的 Windows 服务 第一个参数指示获得的工厂是否参与此跨过程缓存的字体使用 有 DWRITE_FACTORY_TYPE_SHARED 和 DWRITE_FACTORY_TYPE_ISOLATED 两个选项 SHARED 和 ISOLATED 工厂都可以利用已缓存的字体数据 只有 SHARED 工厂向缓存返回字体数据 第二个参数具体指示我希望在第三个和最后一个参数中返回哪个版本的 DirectWrite 工厂接口
在 DirectWrite 工厂对象给定的情况下 我可以直接要求其提供系统字体集
ComPtr<IDWriteFontCollection> fonts; HR(factory >GetSystemFontCollection(fonts GetAddressOf()));
GetSystemFontCollection 方法的第二个参数是可选的 指示其是否检查已安装字体集的更新或更改 幸运的是 这个参数默认为 false 因此 除非要确保结果集反映最近的更改 否则不必考虑它 在字体集给定的情况下 我可以获取集合中的字体系列数 如下所示
unsigned const count = fonts >GetFontFamilyCount();
然后我使用 GetFontFamily 方法通过从零开始的索引检索单个字体系列对象 一个字体系列对象表示这样一组字体 它们共享一个名称 当然也是一种设计 但粗细 样式和拉伸并不相同
ComPtr<IDWriteFontFamily> family; HR(fonts >GetFontFamily(index family GetAddressOf()));
IDWriteFontFamily 接口继承自 IDWriteFontList 接口 因此 我可以枚举该字体系列中的各种字体 能够检索字体系列名称合乎情理并且非常有用 不过 系列名称已经过本地化 所以它并不像您期待的那样简单直接 首先 我需要字体系列提供一个本地化字符串对象 该对象针对每种支持的区域设置均包含一个系列名称
ComPtr<IDWriteLocalizedStrings> names; HR(family >GetFamilyNames(names GetAddressOf()));
我也可以枚举系列名称 但一般只查找用户默认区域设置对应的名称 事实上 IDWriteLocalizedStrings 接口提供了 FindLocaleName 方法来检索本地化系列名称的索引 我从调用 GetUserDefaultLocaleName 函数以获取用户默认区域设置开始
wchar_t locale[LOCALE_NAME_MAX_LENGTH]; VERIFY(GetUserDefaultLocaleName(locale countof(locale)));
然后 我将默认区域设置传递给 IDWriteLocalizedStrings FindLocaleName 方法 确定该字体系列是否有针对当前用户本地化的名称
unsigned index; BOOL exists; HR(names >FindLocaleName(locale &index &exists));
如果请求的区域设置在集中不存在 我可能会回到一些默认设置 如 en us 假设存在 就可以使用 IDWriteLocalizedStrings GetString 方法获取副本
if (exists) wchar_t name[ ]; HR(names >GetString(index name _countof(name)));
如果担心长度 可以先调用 GetStringLength 方法 只要确保缓冲区足够大就可以 图 提供了一个完整列表 显示所有内容是如何共同枚举已安装字体的
图 使用 DirectWrite API 枚举字体
ComPtr<IDWriteFactory > factory; HR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED __uuidof(factory) reinterpret_cast<IUnknown **>(factory GetAddressOf()))); ComPtr<IDWriteFontCollection> fonts; HR(factory >GetSystemFontCollection(fonts GetAddressOf())); wchar_t locale[LOCALE_NAME_MAX_LENGTH]; VERIFY(GetUserDefaultLocaleName(locale _countof(locale))); unsigned const count = fonts >GetFontFamilyCount(); for (unsigned familyIndex = ; familyIndex != count; ++familyIndex) ComPtr<IDWriteFontFamily> family; HR(fonts >GetFontFamily(familyIndex family GetAddressOf())); ComPtr<IDWriteLocalizedStrings> names; HR(family >GetFamilyNames(names GetAddressOf())); unsigned nameIndex; BOOL exists; HR(names >FindLocaleName(locale &nameIndex &exists)); if (exists) wchar_t name[ ]; HR(names >GetString(nameIndex name countof(name))); wprintf(L"%sn" name);
最新 C++ 浅谈
如果您经常阅读本杂志 就会知道我已经介绍过 DirectX 特别是 Direct D 的最新 C++ 处理 dx h 头文件 (d) 也包含 DirectWrite 可以用它大大简化我在上面提供的代码 不必调用 DWriteCreateFactory 我可以从 DirectWrite 命名空间直接调用 CreateFactory 函数
auto factory = CreateFactory();
获取系统字体集同样简单
auto fonts = factory GetSystemFontCollection();
枚举此字体集是 dx h 的真正亮点 我不必编写传统的 for 循环 也不必调用 GetFontFamilyCount 和 GetFontFamily 方法 我只需要编写一个最新的基于范围的 for 循环
for (auto family : fonts)
这些代码与以往相同 编译器(在 dx h 的帮助下)进行生成 我使用了一个更为自然的编程模型 编写正确有效的代码更为简单 前面的 GetSystemFontCollection 方法返回一个 FontCollection 类 其中包含一个迭代器 它将延迟提取字体系列对象 这使得编译器能够有效地实现基于范围的循环 图 提供了完整列表 与图 中的代码比较 它更为清晰 效率更高
图 使用 dx h 枚举字体
auto factory = CreateFactory(); auto fonts = factory GetSystemFontCollection(); wchar_t locale[LOCALE_NAME_MAX_LENGTH]; VERIFY(GetUserDefaultLocaleName(locale _countof(locale))); for (auto family : fonts) auto names = family GetFamilyNames(); unsigned index; if (names FindLocaleName(locale index)) wchar_t name[ ]; names GetString(index name); wprintf(L"%sn" name);
字体浏览器与 Windows 运行时
DirectWrite 的作用远不只是枚举字体 我将前面这些内容与 Direct D 结合起来 创建一个简单的字体浏览器应用程序 在我 年 月的专栏 () 中 介绍了如何用标准 C++ 编写基本的 WinRT 应用程序模型 在我 年 月的专栏 (msd/magazine/dn ) 中 介绍了如何通过 DirectX(具体而言是 Direct D)在此基于 CoreWindow 的应用程序中进行呈现 现在介绍如何在 DirectWrite 的帮助下扩展代码 从而使用 Direct D 呈现文本
这些专栏发表后 Windows 发布了 它对 DPI 缩放在最新应用程序和桌面应用程序中的处理方式有一些改动 以后我会详细介绍 DPI 这里暂不讨论这些更改 我们只重点扩展我在八月提出 在十月扩展过的 SampleWindow 类 以便支持文本呈现并简化字体浏览
首先 将 DirectWrite Factory 类添加为成员变量
DirectWrite::Factory m_writeFactory;
在 SampleWindow CreateDeviceIndependentResources 方法内 可以创建 DirectWrite 工厂
m_writeFactory = DirectWrite::CreateFactory();
在这里我还可以获取系统字体集和用户的默认区域设置 为枚举字体系列做准备
auto fonts = m_writeFactory GetSystemFontCollection(); wchar_t locale[LOCALE_NAME_MAX_LENGTH]; VERIFY(GetUserDefaultLocaleName(locale _countof(locale)));
我将使应用程序在用户按下向上和向下箭头键时循环切换字体 我不通过 接口不断枚举字体集 而只是将字体系列名称复制到一个标准 set 容器
set<wstring> m_fonts;
现在 我可以在 CreateDeviceIndependentResources 中使用图 中基于范围的 for 循环将名称添加到 set
m_fonts insert(name);
set 填充后 使用一个指向 set 开头的迭代器开始应用程序 将迭代器存储为成员变量
set<wstring>::iterator m_font;
SampleWindow CreateDeviceIndependentResources 方法初始化迭代器并调用 CreateTextFormat 方法 定义如下
m_font = begin(m_fonts); CreateTextFormat();
在 Direct D 绘制文本前 需要创建一个文本格式对象 这需要字体系列名称和所需字号 我允许用户使用向左和向右箭头键更改字号 因此添加一个成员变量来跟踪字号
float m_size;
Visual C++ 编译器很快会允许我初始化像这样的类内非静态数据成员 现在 需要在 SampleWindow 的构造函数中将它设置为合理的默认值 接下来 需要定义 CreateTextFormat 方法 这只是 DirectWrite 工厂方法的同名包装 但它更新了一个成员变量 Direct D 可以使用该变量定义要绘制的文本的格式
TextFormat m_textFormat;
然后 CreateTextFormat 方法从 set 迭代器检索字体系列名称 将其与当前字号组合 创建一个新的文本格式对象
void CreateTextFormat() m_textFormat = m_writeFactory CreateTextFormat(m_font >c_str() m_size);
我已经把它包装起来 因此 除了最初在 CreateDeviceIndependentResources 末尾调用它外 我还可以在每次用户按下一个箭头键更改字体系列或字号时调用它 这引出了在 WinRT 应用程序模型中如何处理按键的问题 在桌面应用程序中 这与 WM_KEYDOWN 消息处理有关 好在 CoreWindow 提供了 KeyDown 事件 它是这一消息的最新等效项 我从定义 IKeyEventHandler 接口开始 SampleWindow 需要实现该接口
typedef ITypedEventHandler<CoreWindow * KeyEventArgs *> IKeyEventHandler;
然后 可以将此接口添加到继承接口的 SampleWindow 列表中 并相应地更新 QueryInterface 实现 接下来只需提供它的 Invoke 实现
auto __stdcall Invoke( ICoreWindow * IKeyEventArgs * args) > HRESULT override return S_OK;
IKeyEventArgs 接口提供的信息与 LPARAM 和 WPARAM 向 WM_KEYDOWN 消息提供的信息基本相同 它的 get_VirtualKey 方法对应于后者的 WPARAM 指示按下了哪个非系统键
VirtualKey key; HR(args >get_VirtualKey(&key));
与此类似 它的 get_KeyStatus 方法对应于 WM_KEYDOWN 的 LPARAM 这样可以提供有关按键事件状态的丰富信息
CorePhysicalKeyStatus status; HR(args >get_KeyStatus(&status));
为方便用户 我支持在用户按住箭头键时加速 以便更快地调整所呈现字体的字号 为此 需要另一个成员变量
unsigned m_accelerate;
然后 可以使用该事件的键状态来确定是将字号更改一个增量还是增加一定的大小
if (!status WasKeyDown) m_accelerate = ; else m_accelerate += ; m_accelerate = std::min( U m_accelerate);
我设置了上限 因此加速不会太过 现在可以分别处理不同的按键 首先是向左箭头键 用于缩小字号
if (VirtualKey_Left == key) m_size = std::max( f m_size m_accelerate);
我很小心 不会使字号无效 然后是向右箭头键 用于增加字号
else if (VirtualKey_Right == key) m_size += m_accelerate;
接下来 移到上一字体系列 处理向上箭头键
if (begin(m_fonts) == m_font) m_font = end(m_fonts); m_font;
然后 我仔细循环到最后一个字体 看看迭代器是否会回到序列开头 接下来 移到下一字体系列 处理向下箭头键
else if (VirtualKey_Down == key) ++m_font; if (end(m_fonts) == m_font) m_font = begin(m_fonts);
在这里 再一次仔细循环到开头 看看迭代器是否会回到序列结尾 最后 我可以在事件处理程序末尾调用 CreateTextFormat 方法来重新创建文本格式对象
剩下的事情是 更新 SampleWindow Draw 方法 用当前文本格式绘制一些文本 方法如下
wchar_t const text [] = L"The quick brown fox jumps over the lazy dog"; m_target DrawText(text _countof(text) m_textFormat RectF( f f size Width f size Height f) m_brush);
Direct D 呈现目标的 DrawText 方法直接支持 DirectWrite 现在 DirectWrite 就可以处理文本布局了 而且呈现速度非常快 这就是该脚本所执行的所有操作 图 是执行效果 我可以按向上和向下箭头键遍历字体系列 按向左和向右箭头键调整字号 Direct D 根据当前选择自动重新呈现
图 字体浏览器
彩色字体
Windows 引入了一个称为彩色字体的新功能 去掉了一些实现彩色字体的次优解决方案 当然 这都是 DirectWrite 和 Direct D 促成的 令人高兴的是 调用 Direct D DrawText 方法就像使用 D D _DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT 常量一样简单 我可以将 SampleWindow 的 Draw 方法更新为使用相应范围的枚举值
m_target DrawText(text _countof(text) m_textFormat RectF( f f size Width f size Height f) m_brush); DrawTextOptions::EnableColorFont);
图 也是字体浏览器 这次是一些 Unicode 表情
图 彩色字体
彩色字体的亮点在于可以自动缩放 而不影响质量 我可以在字体浏览器应用程序中按向右箭头键 更近地查看细节 结果如图 所示
图 放大的彩色字体
cha138/Article/program/Web/201405/30797相关参考
用python和C++解决这个问题 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!lishixinz
c++有两种继承:单继承和?单继承和多继承。多继承比较复杂,一般情况下不会使用。java单继承和多继承分别如何实现如果是单继承的话,使用extends关键字来完成。java不支持多继承,但使用接口可以
注意C++和C#引用的区别 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! C#用多了最近又回到C
C#和C++结构体Socket通信 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 最近在用C#做
Java版本和C++版本简单Stack程序 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 现在对
横向技术分析C#、C++和Java优劣 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! C#诞生之
Oracle发布最新CRM管理软件11i.10版 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
知识大全 我的微信群群主管理转让入口怎么不显示,我的微信版本是最新的了
我的微信群群主管理转让入口怎么不显示,我的微信版本是最新的了!打开自己是群主的群窗口右上角群管理群管理权转让就出现本群所有成员希望能帮你我的微信怎么改不了头像?微信版本是最新的·我的也改不了,QQ上都
知识大全 谁能告诉我全国计算机等级考试网络三级和C++二级具体的考试时间啊
谁能告诉我全国计算机等级考试网络三级和C++二级具体的考试时间啊?谢谢了! 以下文字资料是由(历史新知网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一
超越C++下一代C++—C++/CLI简介 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!一绪论