教學課程:使用 DirectWrite 進行消費者入門
本檔說明如何使用DirectWrite和Direct2D來建立包含單一格式的簡單文字,以及包含多個格式的文字。
本教學課程包含下列部分:
原始程式碼
本概觀中顯示的原始程式碼取自DirectWrite Hello World範例。 每個元件都會在不同的類別中實作 (SimpleText 和 MultiformattedText) ,並顯示在個別的子視窗中。 每個類別都代表 Microsoft Win32 視窗。 除了 WndProc 方法之外,每個類別還包含下列方法:
函式 | 描述 |
---|---|
CreateDeviceIndependentResources | 建立與裝置無關的資源,使其可以在任何地方重複使用。 |
DiscardDeviceIndependentResources | 不再需要裝置獨立資源之後釋出它們。 |
CreateDeviceResources | 建立系結至特定裝置的資源,例如筆刷和轉譯目標。 |
DiscardDeviceResources | 不再需要裝置相依資源之後釋放它們。 |
DrawD2DContent | 使用 Direct2D 轉譯至畫面。 |
DrawText | 使用 Direct2D繪製文字字串。 |
OnResize | 變更視窗大小時,調整 Direct2D 轉譯目標的大小。 |
您可以使用提供的範例,或使用下列指示,將DirectWrite和Direct2D新增至您自己的 Win32 應用程式。 如需範例和相關專案檔的詳細資訊,請參閱DirectWrite HelloWorld。
繪製簡單文字
本節說明如何使用DirectWrite和Direct2D來轉譯具有單一格式的簡單文字,如下列螢幕擷取畫面所示。
將簡單的文字繪製到畫面需要四個元件:
- 要呈現的字元字串。
- IDWriteTextFormat的實例。
- 要包含文字的區域維度。
- 可以轉譯文字的物件。 在本教學課程中。 您使用 Direct2D 轉譯目標。
IDWriteTextFormat介面描述用來格式化文字的字型系列名稱、大小、粗細、樣式和延展,並描述地區設定資訊。 IDWriteTextFormat 也會定義設定和取得下列屬性的方法:
- 行距。
- 相對於版面配置方塊左邊緣和右邊緣的文字對齊方式。
- 相對於版面配置方塊頂端和底部的段落對齊方式。
- 閱讀方向。
- 超出版面配置方塊之文字的文字修剪細微性。
- 累加定位停駐點。
- 段落流程方向。
需要 IDWriteTextFormat 介面,才能使用本檔中所述的兩個處理常式來繪製文字。
您必須先有IDWriteFactory實例,才能建立IDWriteTextFormat物件或任何其他DirectWrite物件。 您可以使用IDWriteFactory來建立IDWriteTextFormat實例和其他DirectWrite物件。 若要取得 Factory 實例,請使用 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::CreateDeviceIndependentResources 方法內, (SimpleText.cpp) ,呼叫 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印刷樣式功能新增至文字版面配置,例如 swashes 和替代文體文字集。 您可以藉由呼叫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 並呼叫IDWriteTypography::AddFontFeature方法的DWRITE_FONT_FEATURE物件,以新增字型功能。
// 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 中實作。