使用 Direct2D 和 DirectWrite 的文本呈现

与其他 API(如 GDI、GDI+ 或 WPF)不同,Direct2D 与另一个 API(DirectWrite)互操作,以操作和呈现文本。 本主题介绍这些独立组件的优点和互操作。

本主题包含以下各节:

Direct2D 启用增量采用

出于各种原因,将应用程序从一个图形 API 移动到另一个图形 API 可能很困难,也可能不是所需的。 这可能是因为必须支持仍采用旧接口的插件,因为应用程序本身太大,无法移植到一个版本中的新 API,或者因为较新的 API 的某些部分是可取的,但较旧的 API 适用于应用程序的其他部分。

由于 Direct2DDirectWrite 是作为单独的组件实现的,因此可以升级整个 2D 图形系统或仅升级其文本部分。 例如,可以将应用程序更新为对文本使用 DirectWrite,但仍使用 GDI 或 GDI+ 进行呈现。

文本服务与文本呈现

随着应用程序的发展,其文本处理要求变得越来越复杂。 起初,文本通常仅限于静态布局 UI,文本呈现在定义完善的框(如按钮)中。 随着应用程序开始以越来越多的语言提供,这种方法变得更加难以维持,因为翻译文本的宽度和高度在语言之间可能会有显著差异。 为了适应,应用程序开始动态布局其 UI,以依赖于文本的实际呈现大小,而不是相反。

为了帮助应用程序完成此任务,DirectWrite提供了 IDWriteTextLayout 接口。 此 API 使应用程序能够指定具有复杂特征的文本,例如不同的字体和字号、下划线、删除线、双向文本、效果、省略号,甚至嵌入的非字形字符 (,例如位图表情符号或图标) 。 然后,应用程序可以更改文本的各种特征,因为它以迭代方式确定其 UI 布局。 下图和教程:入门DirectWrite主题中的DirectWrite Hello World示例演示了其中许多效果。

“hello world”示例的屏幕截图。

布局可以像 WPF 一样根据字形的宽度 (理想地定位标志符号) ,也可以像 GDI) 一样,将字形贴靠到最近的像素位置 (。

除了获取文本度量值外,应用程序还可以命中测试文本的各个部分。 例如,它可能想要知道文本中的超链接被单击。 (有关命中测试的详细信息,请参阅 如何对文本布局执行命中测试 主题。)

文本布局接口与应用程序使用的呈现 API 分离,如下图所示:

文本布局和图形 API 关系图。

这种分离是可能的,因为 DirectWrite 提供了一个呈现接口, (IDWriteTextRenderer) 应用程序可以实现该接口,以便通过使用所需的任何图形 API 来呈现文本。 应用程序实现 IDWriteTextRenderer::D rawGlyphRun 回调方法由DirectWrite在呈现文本布局时调用。 此方法负责执行绘制操作或传递这些操作。

对于绘制字形,Direct2D 提供用于绘制到 Direct2D 图面的 ID2D1RenderTarget::D rawGlyphRunDirectWrite提供 IDWriteBitmapRenderTarget::D rawGlyphRun 用于绘制到 GDI 图面,然后使用 GDI 将其传输到窗口。 Direct2D 和 DirectWrite 中的 DrawGlyphRun 与应用程序在 IDWriteTextRenderer 上实现的 DrawGlyphRun 方法完全兼容。

在类似的分离之后,文本特定功能 ((如字体枚举和管理、字形分析等)) 由 DirectWrite 而不是 Direct2D 处理。 Direct2D 直接接受DirectWrite对象。 为了帮助现有 GDI 应用程序利用DirectWrite,它为 IDWriteGdiInterop 方法接口提供了执行以下操作的方法:

字形与文本

文本是一组 Unicode 码位, (字符) ,具有各种样式修饰符, (字体、粗细、下划线、删除线等) 排列在矩形中。 相比之下,字形是特定字体文件中的特定索引。 字形定义了一组可以呈现的曲线,但它没有任何文本含义。 字形和字符之间可能存在多对多映射。 来自同一个字面并在基线上按顺序布局的字形序列称为 GlyphRunDirectWriteDirect2D 都调用其最精确的字形呈现 API DrawGlyphRun,它们具有非常相似的签名。 以下是来自 Direct2D 中的 ID2D1RenderTarget

STDMETHOD_(void, DrawGlyphRun)(
        D2D1_POINT_2F baselineOrigin,
        __in CONST DWRITE_GLYPH_RUN *glyphRun,
        __in ID2D1Brush *foregroundBrush,
        DWRITE_MEASURING_MODE measuringMode = DWRITE_MEASURING_MODE_NATURAL 
        ) PURE;

此方法来自 DirectWrite 中的 IDWriteBitmapRenderTarget

STDMETHOD(DrawGlyphRun)(
        FLOAT baselineOriginX,
        FLOAT baselineOriginY,
        DWRITE_MEASURING_MODE measuringMode,
        __in DWRITE_GLYPH_RUN const* glyphRun,
        IDWriteRenderingParams* renderingParams,
        COLORREF textColor,
        __out_opt RECT* blackBoxRect = NULL
        ) PURE;

DirectWrite版本保留基线原点、测量模式和字形运行参数,并包含其他参数。

DirectWrite还支持通过实现 IDWriteTextRenderer 接口来对字形使用自定义呈现器。 此接口还具有 DrawGlyphRun 方法,如以下代码示例所示。

STDMETHOD(DrawGlyphRun)(
        __maybenull void* clientDrawingContext,
        FLOAT baselineOriginX,
        FLOAT baselineOriginY,
        DWRITE_MEASURING_MODE measuringMode,
        __in DWRITE_GLYPH_RUN const* glyphRun,
        __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
        __maybenull IUnknown* clientDrawingEffect
        ) PURE;

此版本包含实现 自定义文本呈现器时有用的更多参数。 最终参数用于应用程序实现的自定义绘图效果。 (有关客户端绘图效果的详细信息,请参阅 如何向文本布局添加客户端绘图效果

每个字形运行从原点开始,并置于从此原点开始的行上。 标志符号由当前世界转换和关联呈现目标上的所选文本呈现设置更改。 通常,只有执行自己的布局的应用程序 (例如,Word处理器) 或已实现 IDWriteTextRenderer 接口的应用程序直接调用此 API。

DirectWrite和 Direct2D

Direct2D 通过 DrawGlyphRun 提供字形级渲染服务。 但是,这要求应用程序实现呈现的详细信息,这基本上可以自行从 GDI 重现 DrawText API 的功能。

因此, Direct2D 提供的 API 接受文本而不是字形: ID2D1RenderTarget::D rawTextLayoutID2D1RenderTarget::D rawText。 这两种方法都呈现到 Direct2D 图面。 为了呈现到 GDI 图面,提供了 IDWriteBitmapRenderTarget::D rawGlyphRun 。 但此方法要求应用程序实现自定义文本呈现器。 (有关详细信息,请参阅 呈现到 GDI Surface 主题。)

应用程序对文本的使用通常很简单:例如,将 “确定”“取消 ”放在固定布局按钮上。 但是,随着时间的推移,随着国际化和其他功能的添加,它变得更加复杂。 最终,许多应用程序必须使用DirectWrite的文本布局对象并实现文本呈现器。

因此, Direct2D 提供了分层 API,使应用程序能够简单启动并变得更加复杂,而无需回溯或放弃其工作代码。 下图中显示了简化视图:

directwrite 和 direct2d 应用程序关系图。

DrawText

DrawText 是使用的最简单 API。 它采用 Unicode 字符串、前景画笔、单个格式对象和目标矩形。 它将布局和呈现布局矩形中的整个字符串,并选择性地剪裁它。 在固定布局 UI 中放入一段简单的文本时,这非常有用。

DrawTextLayout

通过创建 IDWriteTextLayout 对象,应用程序可以开始测量和排列文本和其他 UI 元素,并支持多种字体、样式、下划线和删除线。 Direct2D 提供 DrawTextLayout API,该 API 直接接受此对象并在给定点呈现文本。 (宽度和高度由布局对象) 提供。 除了实现所有预期的文本布局功能外,Direct2D 还将将任何效果对象解释为画笔,并将该画笔应用于所选的字形范围。 它还将调用任何内联对象。 然后,应用程序可以根据需要在文本中插入) (图标的非字形字符。 使用文本布局对象的另一个优点是,字形位置将缓存在其中。 因此,通过将同一布局对象重新用于多个绘制调用并避免为每个调用重新计算字形位置,可以大幅提高性能。 GDI 的 DrawText 不存在此功能。

DrawGlyphRun

最后,应用程序可以实现 IDWriteTextRenderer 接口本身,并调用 DrawGlyphRunFillRectangle 本身,或任何其他呈现 API。 与文本布局对象的所有现有交互将保持不变。

有关如何实现自定义文本呈现器的示例,请参阅 使用自定义文本呈现器进行呈现 主题。

字形呈现

向现有 GDI 应用程序添加DirectWrite使应用程序能够使用 IDWriteBitmapRenderTarget API 来呈现字形。 DirectWrite提供的 IDWriteBitmapRenderTarget::D rawGlyphRun 方法将以纯色呈现到内存 DC,而无需任何其他 API(如 Direct2D)。

这使应用程序能够获取高级文本呈现功能,如下所示:

  • 子像素 ClearType 使应用程序能够在子像素位置上放置字形,以便同时呈现字形和字形布局。
  • Y 方向抗锯齿可以在较大的字形上更平滑地呈现曲线。

迁移到 Direct2D 的应用程序还将获得以下功能:

  • 硬件加速。
  • 能够使用任意 Direct2D 画笔填充文本,例如径向渐变、线性渐变和位图。
  • 支持通过 PushAxisAlignedClipPushLayerCreateCompatibleRenderTarget API 进行分层和剪裁。
  • 支持灰度文本呈现的功能。 这会根据文本画笔不透明度和文本的抗锯齿正确填充目标 alpha 通道。

为了有效地支持硬件加速, Direct2D 使用与伽玛校正的近似值略有不同,称为 alpha 更正。 这不需要 Direct2D 在呈现文本时检查呈现目标颜色像素。

结论

本主题介绍 Direct2DDirectWrite 之间的差异和相似之处,以及将它们作为单独的协作 API 提供这些 API 的体系结构动机。