教程:使用 DirectWrite 入门
本文档介绍如何使用 DirectWrite 和 Direct2D 创建包含单个格式的简单文本,然后创建包含多种格式的文本。
本教程包含以下部分:
源代码
本概述中显示的源代码取自DirectWrite Hello World示例。 每个部件在 SimpleText 和 MultiformattedText) 的单独类 (实现,并显示在单独的子窗口中。 每个类都表示一个 Microsoft Win32 窗口。 除了 WndProc 方法外,每个类还包含以下方法:
功能 | 说明 |
---|---|
CreateDeviceIndependentResources | 创建与设备无关的资源,以便可在任何位置重复使用。 |
DiscardDeviceIndependentResources | 在不再需要与设备无关的资源后释放这些资源。 |
CreateDeviceResources | 创建绑定到特定设备的资源,如画笔和呈现目标。 |
DiscardDeviceResources | 在不再需要设备相关的资源后释放这些资源。 |
DrawD2DContent | 使用 Direct2D 呈现到屏幕。 |
DrawText | 使用 Direct2D 绘制文本字符串。 |
OnResize | 在窗口大小更改时调整 Direct2D 呈现目标的大小。 |
可以使用提供的示例,或按照以下说明将 DirectWrite 和 Direct2D 添加到自己的 Win32 应用程序。 有关示例和关联的项目文件的详细信息,请参阅 helloWorld DirectWrite。
绘制简单文本
本部分介绍如何使用 DirectWrite 和 Direct2D 呈现采用单一格式的简单文本,如以下屏幕截图所示。
在屏幕上绘制简单文本需要四个组件:
- 要呈现的字符串。
- IDWriteTextFormat 的实例。
- 要包含文本的区域的尺寸。
- 一个可以呈现文本的对象。 在本教程中, 使用 Direct2D 呈现器目标。
IDWriteTextFormat 接口描述用于设置文本格式的字体系列名称、大小、粗细、样式和拉伸,并描述区域设置信息。 IDWriteTextFormat 还定义用于设置和获取以下属性的方法:
- 行距。
- 相对于布局框的左边缘和右边缘的文本对齐方式。
- 相对于布局框顶部和底部的段落对齐方式。
- 阅读方向。
- 溢出布局框的文本的文本剪裁粒度。
- 增量制表位。
- 段落流方向。
IDWriteTextFormat 接口是使用本文档 中所述的两个过程绘制文本所必需的。
在创建 IDWriteTextFormat 对象或任何其他DirectWrite对象之前,需要一个 IDWriteFactory 实例。 使用 IDWriteFactory 创建 IDWriteTextFormat 实例和其他DirectWrite对象。 若要获取工厂实例,请使用 DWriteCreateFactory 函数。
第 1 部分:声明DirectWrite和 Direct2D 资源。
在此部分中,声明稍后将用于创建文本并将其显示为类的私有数据成员的对象。 DirectWrite的所有接口、函数和数据类型都在 dwrite.h 头文件中声明,Direct2D 的所有接口、函数和数据类型都在 d2d1.h 中声明;如果尚未执行此操作,请在项目中包括这些标头。
在类头文件 (SimpleText.h) 中,将指向 IDWriteFactory 和 IDWriteTextFormat 接口的指针声明为私有成员。
IDWriteFactory* pDWriteFactory_; IDWriteTextFormat* pTextFormat_;
声明成员以保存要呈现的文本字符串和字符串的长度。
const wchar_t* wszText_; UINT32 cTextLength_;
声明指向 ID2D1Factory、 ID2D1HwndRenderTarget 和 ID2D1SolidColorBrush 接口的指针,以使用 Direct2D 呈现文本。
ID2D1Factory* pD2DFactory_; ID2D1HwndRenderTarget* pRT_; ID2D1SolidColorBrush* pBlackBrush_;
第 2 部分:创建与设备无关的资源。
Direct2D 提供两种类型的资源:依赖于设备的资源和与设备无关的资源。 与设备相关的资源与呈现设备相关联,如果删除该设备,则不再正常运行。 另一方面,独立于设备的资源可以在应用程序范围内持续使用。
DirectWrite资源与设备无关。
在本部分中,将创建应用程序使用的与设备无关的资源。 必须通过调用 接口的 Release 方法释放这些资源。
使用的某些资源只需创建一次,并且不绑定到设备。 这些资源的初始化放置在 SimpleText::CreateDeviceIndependentResources 方法中,该方法在初始化类时调用。
在类实现文件 (SimpleText.cpp) 的 SimpleText ::CreateDeviceIndependentResources 方法内,调用 D2D1CreateFactory 函数以创建 ID2D1Factory 接口,该接口是所有 Direct2D 对象的根工厂接口。 使用相同的工厂来实例化其他 Direct2D 资源。
hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory_ );
调用 DWriteCreateFactory 函数以创建 IDWriteFactory 接口,该接口是所有DirectWrite对象的根工厂接口。 使用相同的工厂来实例化其他DirectWrite资源。
if (SUCCEEDED(hr)) { hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWriteFactory_) ); }
初始化文本字符串并存储其长度。
wszText_ = L"Hello World using DirectWrite!"; cTextLength_ = (UINT32) wcslen(wszText_);
使用 IDWriteFactory::CreateTextFormat 方法创建 IDWriteTextFormat 接口对象。 IDWriteTextFormat 指定将用于呈现文本字符串的字体、粗细、拉伸、样式和区域设置。
if (SUCCEEDED(hr)) { hr = pDWriteFactory_->CreateTextFormat( L"Gabriola", // Font family name. NULL, // Font collection (NULL sets it to use the system font collection). DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us", &pTextFormat_ ); }
通过调用 IDWriteTextFormat::SetTextAlignment 和 IDWriteTextFormat::SetParagraphAlignment 方法,将文本水平和垂直居中。
// Center align (horizontally) the text. if (SUCCEEDED(hr)) { hr = pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); } if (SUCCEEDED(hr)) { hr = pTextFormat_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); }
在本部分中,你初始化了应用程序使用的与设备无关的资源。 在下一部分中,初始化与设备相关的资源。
第 3 部分:创建 Device-Dependent 资源。
在此部分中,将创建 ID2D1HwndRenderTarget 和 ID2D1SolidColorBrush 用于呈现文本。
呈现器目标是一个 Direct2D 对象,用于创建绘图资源并将绘图命令呈现到呈现设备。 ID2D1HwndRenderTarget 是呈现给 HWND 的呈现器目标。
呈现器目标可以创建的绘图资源之一是用于绘制轮廓、填充和文本的画笔。 ID2D1SolidColorBrush 以纯色绘制。
ID2D1HwndRenderTarget 和 ID2D1SolidColorBrush 接口在创建时都绑定到呈现设备,如果设备无效,必须释放并重新创建。
在 SimpleText::CreateDeviceResources 方法中,检查呈现器目标指针是否为 NULL。 如果是,请检索呈现区域的大小,并创建该大小的 ID2D1HwndRenderTarget 。 使用 ID2D1HwndRenderTarget 创建 ID2D1SolidColorBrush。
RECT rc; GetClientRect(hwnd_, &rc); D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top); if (!pRT_) { // Create a Direct2D render target. hr = pD2DFactory_->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties( hwnd_, size ), &pRT_ ); // Create a black brush. if (SUCCEEDED(hr)) { hr = pRT_->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Black), &pBlackBrush_ ); } }
在 SimpleText::D iscardDeviceResources 方法中,释放画笔和呈现目标。
SafeRelease(&pRT_); SafeRelease(&pBlackBrush_);
创建呈现目标和画笔后,可以使用它们来呈现文本。
第 4 部分:使用 Direct2D DrawText 方法绘制文本。
在类的 SimpleText::D rawText 方法中,通过检索呈现区域的尺寸来定义文本布局的区域,并创建具有相同尺寸的 Direct2D 矩形。
D2D1_RECT_F layoutRect = D2D1::RectF( static_cast<FLOAT>(rc.left) / dpiScaleX_, static_cast<FLOAT>(rc.top) / dpiScaleY_, static_cast<FLOAT>(rc.right - rc.left) / dpiScaleX_, static_cast<FLOAT>(rc.bottom - rc.top) / dpiScaleY_ );
使用 ID2D1RenderTarget::D rawText 方法和 IDWriteTextFormat 对象将文本呈现到屏幕。 ID2D1RenderTarget::D rawText 方法采用以下参数:
- 要呈现的字符串。
- 指向 IDWriteTextFormat 接口的 指针。
- Direct2D 布局矩形。
- 指向公开 ID2D1Brush 的接口的指针。
pRT_->DrawText( wszText_, // The string to render. cTextLength_, // The string's length. pTextFormat_, // The text format. layoutRect, // The region of the window where the text will be rendered. pBlackBrush_ // The brush used to draw the text. );
第 5 部分:使用 Direct2D 呈现窗口内容
若要在收到画图消息时使用 Direct2D 呈现窗口的内容,请执行以下操作:
- 通过调用第 3 部分中实现的 SimpleText::CreateDeviceResources 方法创建设备依赖资源。
- 调用呈现器的 ID2D1HwndRenderTarget::BeginDraw 方法。
- 通过调用 ID2D1HwndRenderTarget::Clear 方法清除呈现目标。
- 调用第 4 部分中实现的 SimpleText::D rawText 方法。
- 调用呈现器的 ID2D1HwndRenderTarget::EndDraw 方法。
- 如有必要,请放弃依赖于设备的资源,以便在重新绘制窗口时重新创建它们。
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
pRT_->BeginDraw();
pRT_->SetTransform(D2D1::IdentityMatrix());
pRT_->Clear(D2D1::ColorF(D2D1::ColorF::White));
// Call the DrawText method of this class.
hr = DrawText();
if (SUCCEEDED(hr))
{
hr = pRT_->EndDraw(
);
}
}
if (FAILED(hr))
{
DiscardDeviceResources();
}
SimpleText 类在 SimpleText.h 和 SimpleText.cpp 中实现。
使用多种格式绘制文本。
本部分介绍如何使用 DirectWrite 和 Direct2D 呈现多种格式的文本,如以下屏幕截图所示。
本部分的代码作为 DirectWrite HelloWorld 中的 MultiformattedText 类实现。 它基于上一部分中的步骤。
若要创建多格式文本,除上一部分介绍 的 IDWriteTextFormat 接口外,还使用 IDWriteTextLayout 接口。 IDWriteTextLayout 接口描述文本块的格式和布局。 除了 由 IDWriteTextFormat 对象指定的默认格式外,还可以使用 IDWriteTextLayout 更改特定文本区域的格式。 这包括字体系列名称、大小、粗细、样式、拉伸、删除线和下划线。
IDWriteTextLayout 还提供命中测试方法。 这些方法返回的命中测试指标相对于使用 IDWriteFactory 接口的 CreateTextLayout 方法创建 IDWriteTextLayout 接口对象时指定的布局框。
IDWriteTypography 接口用于向文本布局添加可选的 OpenType 版式功能,例如斜面和替代样式文本集。 可以通过调用 IDWriteTypography 接口的 AddFontFeature 方法,将版式功能添加到文本布局中的特定文本范围。 此方法接收 DWRITE_FONT_FEATURE 结构作为参数,其中包含 DWRITE_FONT_FEATURE_TAG 枚举常量和 UINT32 执行参数。 可在 microsoft.com 上的 OpenType 布局标记注册表 中找到已注册的 OpenType 功能列表。 有关等效DirectWrite枚举常量,请参阅 DWRITE_FONT_FEATURE_TAG。
第 1 部分:创建 IDWriteTextLayout 接口。
将 指向 IDWriteTextLayout 接口的指针声明为 MultiformattedText 类的成员。
IDWriteTextLayout* pTextLayout_;
在 MultiformattedText::CreateDeviceIndependentResources 方法的末尾,通过调用 CreateTextLayout 方法创建 IDWriteTextLayout 接口对象。 IDWriteTextLayout 接口提供了其他格式设置功能,例如,能够对所选文本部分应用不同格式。
// Create a text layout using the text format. if (SUCCEEDED(hr)) { RECT rect; GetClientRect(hwnd_, &rect); float width = rect.right / dpiScaleX_; float height = rect.bottom / dpiScaleY_; hr = pDWriteFactory_->CreateTextLayout( wszText_, // The string to be laid out and formatted. cTextLength_, // The length of the string. pTextFormat_, // The text format to apply to the string (contains font information, etc). width, // The width of the layout box. height, // The height of the layout box. &pTextLayout_ // The IDWriteTextLayout interface pointer. ); }
第 2 部分:使用 IDWriteTextLayout 应用格式设置。
格式设置(如字号、粗细和下划线)可以应用于使用 IDWriteTextLayout 界面显示的文本的子字符串。
通过声明DWRITE_TEXT_RANGE并调用IDWriteTextLayout::SetFontSize 方法,将“DirectWrite”子字符串“Di”的字号设置为 100。
// Format the "DirectWrite" substring to be of font size 100. if (SUCCEEDED(hr)) { DWRITE_TEXT_RANGE textRange = {20, // Start index where "DirectWrite" appears. 6 }; // Length of the substring "Direct" in "DirectWrite". hr = pTextLayout_->SetFontSize(100.0f, textRange); }
通过调用 IDWriteTextLayout::SetUnderline 方法为子字符串“DirectWrite”加下划线。
// Format the word "DWrite" to be underlined. if (SUCCEEDED(hr)) { DWRITE_TEXT_RANGE textRange = {20, // Start index where "DirectWrite" appears. 11 }; // Length of the substring "DirectWrite". hr = pTextLayout_->SetUnderline(TRUE, textRange); }
通过调用 IDWriteTextLayout::SetFontWeight 方法,将子字符串“DirectWrite”的字体粗细设置为粗体。
if (SUCCEEDED(hr)) { // Format the word "DWrite" to be bold. DWRITE_TEXT_RANGE textRange = {20, 11 }; hr = pTextLayout_->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange); }
第 3 部分:使用 IDWriteTypography 添加版式功能。
通过调用 IDWriteFactory::CreateTypography 方法声明和创建 IDWriteTypography 接口对象。
// Declare a typography pointer. IDWriteTypography* pTypography = NULL; // Create a typography interface object. if (SUCCEEDED(hr)) { hr = pDWriteFactory_->CreateTypography(&pTypography); }
通过声明指定了样式集 7 的 DWRITE_FONT_FEATURE 对象并调用 IDWriteTypography::AddFontFeature 方法添加字体功能。
// Set the stylistic set. DWRITE_FONT_FEATURE fontFeature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7, 1}; if (SUCCEEDED(hr)) { hr = pTypography->AddFontFeature(fontFeature); }
通过声明 DWRITE_TEXT_RANGE 变量并调用 IDWriteTextLayout::SetTypography 方法并传入文本范围,将文本布局设置为对整个字符串使用版式。
if (SUCCEEDED(hr)) { // Set the typography for the entire string. DWRITE_TEXT_RANGE textRange = {0, cTextLength_}; hr = pTextLayout_->SetTypography(pTypography, textRange); }
在 MultiformattedText::OnResize 方法中为文本布局对象设置新的宽度和高度。
if (pTextLayout_) { pTextLayout_->SetMaxWidth(static_cast<FLOAT>(width / dpiScaleX_)); pTextLayout_->SetMaxHeight(static_cast<FLOAT>(height / dpiScaleY_)); }
第 4 部分:使用 Direct2D DrawTextLayout 方法绘制文本。
若要使用 IDWriteTextLayout 对象指定的文本布局设置绘制文本,请将 MultiformattedText::D rawText 方法中的代码更改为使用 IDWriteTextLayout::D rawTextLayout。
Delcare 一 个D2D1_POINT_2F 变量,并将其设置为窗口的左上角点。
D2D1_POINT_2F origin = D2D1::Point2F( static_cast<FLOAT>(rc.left / dpiScaleX_), static_cast<FLOAT>(rc.top / dpiScaleY_) );
通过调用 Direct2D 呈现目标的 ID2D1RenderTarget::D rawTextLayout 方法并传递 IDWriteTextLayout 指针,将文本绘制到屏幕。
pRT_->DrawTextLayout( origin, pTextLayout_, pBlackBrush_ );
MultiformattedText 类在 MultiformattedText.h 和 MultiformattedText.cpp 中实现。