教學課程:開始使用 DirectWrite
本文件說明如何使用 DirectWrite 和 Direct2D 來建立包含單一格式的簡單文字,以及包含多個格式的文字。
本教學課程包含下列部分:
原始程式碼
本概觀中顯示的原始程式碼取自 DirectWrite Hello World 範例。 每個元件都會在不同的類別中實作(SimpleText 和 MultiformattedText),並顯示在個別的子視窗中。 每個類別都代表Microsoft Win32 視窗。 除了 WndProc 方法之外,每個類別都包含下列方法:
功能 | 描述 |
---|---|
CreateDeviceIndependentResources | 建立與裝置無關的資源,使其可在任何地方重複使用。 |
丟棄設備無關資源 | 不再需要裝置獨立資源之後,釋放它們。 |
CreateDeviceResources | 創建與特定裝置相關聯的資源,例如筆刷和渲染目標。 |
丟棄設備資源 | 不再需要時,釋放裝置相依資源。 |
DrawD2DContent | 使用 Direct2D 轉譯至畫面。 |
DrawText | 使用 Direct2D繪製文字字串。 |
OnResize | 當視窗大小變更時,調整 Direct2D 轉譯目標的大小。 |
您可以使用提供的範例,或使用下列指示,將 DirectWrite 和 Direct2D 新增至您自己的 Win32 應用程式。 如需範例和相關聯項目檔的詳細資訊,請參閱 DirectWrite HelloWorld。
繪製簡單文字
本節說明如何使用 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::DiscardDeviceResources 方法中,釋放筆刷和渲染目標。
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::DrawText 方法和 IDWriteTextFormat 物件將文字渲染到畫面。 ID2D1RenderTarget::DrawText 方法接受以下參數:
- 要轉譯的字串。
- 一個指向 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 的 DrawText 方法。
- 呼叫渲染目標的 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::DrawText 方法中的程式代碼變更為使用 IDWriteTextLayout::DrawTextLayout。
Declare D2D1_POINT_2F 變數,並將它設為視窗的左上角點。
D2D1_POINT_2F origin = D2D1::Point2F( static_cast<FLOAT>(rc.left / dpiScaleX_), static_cast<FLOAT>(rc.top / dpiScaleY_) );
使用 ID2D1RenderTarget::DrawTextLayout 方法,呼叫 Direct2D 轉譯目標,並傳遞 IDWriteTextLayout 指標,將文字繪製至畫面。
pRT_->DrawTextLayout( origin, pTextLayout_, pBlackBrush_ );
MultiformattedText 類別會在 MultiformattedText.h 中實作,MultiformattedText.cpp。