使用 Direct2D 和 DirectWrite 轉譯文字
不同於其他 API,例如 GDI、GDI+ 或 WPF,Direct2D 會與另一個 API DirectWrite 互操作,以操作和轉譯文字。 本主題描述這些個別元件的優點和互操作。
本主題包含下列各節。
Direct2D 啟用漸進採用
將應用程式從一個圖形 API 移至另一個圖形 API 可能會因為各種原因而難以或不是您想要的。 這可能是因為您必須支援仍然採用較舊介面的外掛程式,因為應用程式本身太大而無法移植到一個版本中的新 API,或因為較新的 API 的某些部分是可取的,但較舊的 API 對於應用程式的其他部分而言運作良好。
因為 Direct2D 和 DirectWrite 會實作為個別元件,所以您可以升級整個 2D 圖形系統,或只升級其文字部分。 例如,您可以更新應用程式以使用 DirectWrite 進行文字,但仍使用 GDI 或 GDI+ 進行轉譯。
文字服務與文字轉譯
隨著應用程式的發展,其文字處理需求變得越來越複雜。 一開始,文字通常會受限於靜態佈局的使用者介面,而且會顯示在定義完善的方塊中,例如按鈕。 隨著應用程式開始以越來越多的語言提供,這種方法變得更加難以維持,因為翻譯文字的寬度和高度在語言之間可能會有很大的差異。 為了適應,應用程式開始動態配置其UI,以根據文字的實際轉譯大小,而不是相反的方式。
為了協助應用程式完成這項工作,DirectWrite 會提供IDWriteTextLayout 介面。 此 API 可讓應用程式指定具有複雜特性的文字片段,例如不同的字型和字型大小、底線、刪除線、雙向文字、效果、省略號,甚至內嵌的非字元字元(例如位圖圖圖或圖示)。 然後,應用程式可以變更文字的各種特性,因為它反覆判斷其UI配置。 下圖所示的 DirectWrite Hello World 範例,以及教學課程:開始使用 DirectWrite 主題會顯示許多這些效果。
佈局可以將字形根據其寬度進行理想位置的放置(如 WPF 所示),或者可以將字形貼齊到最接近的畫素位置(如 GDI 所示)。
除了取得文字量測之外,應用程式還可以進行文字各部分的命中測試。 例如,它可能想要知道已按下文字中的超連結。 (如需點擊測試的詳細資訊,請參閱 如何在文字版面配置 主題上執行點擊測試。
文字配置介面會與應用程式所使用的轉譯 API 分離,如下圖所示:
因為 DirectWrite 提供轉譯介面 (IDWriteTextRenderer), 應用程式可以使用您想要的任何圖形 API 來實作轉譯文字,因此可能會進行此分隔。 當轉譯文字配置時,DirectWrite 會呼叫應用程式所實作的 IDWriteTextRenderer::DrawGlyphRun 回呼方法。 此方法負責執行繪圖操作或將其傳遞給其他方法。
對於繪製字形,Direct2D 提供 ID2D1RenderTarget::DrawGlyphRun 來在 Direct2D 表面上繪製,而 DirectWrite 提供 IDWriteBitmapRenderTarget::DrawGlyphRun 來在 GDI 表面上繪製,然後可以使用 GDI 將其傳送至視窗。 方便使用,Direct2D 和 DirectWrite 中的 DrawGlyphRun 都有與應用程式在 IDWriteTextRenderer 上實作的 DrawGlyphRun 方法完全相同的參數。
在類似的分隔之後,文字特定功能(例如字型列舉和管理、圖像分析等等)是由 DirectWrite 處理, 而不是 Direct2D。 Direct2D 會直接接受 DirectWrite 物件。 為了協助現有的 GDI 應用程式利用 DirectWrite,它會提供 IDWriteGdiInterop 方法介面,並提供方法來執行下列動作:
- 從 GDI 邏輯字型建立 DirectWrite 字型(CreateFontFromLOGFONT)。
- 從 DirectWrite 字型面轉換成 GDI 邏輯字型 (ConvertFontFaceToLOGFONT)。
- 從 HDC 擷取選取的 DirectWrite 字體樣式。 (CreateFontFaceFromHdc)
- 在系統記憶體中建立 DirectWrite 位圖轉譯目標 (CreateBitmapRenderTarget)。
字元對文字
文字是一組 Unicode 字碼點(字元),具有各種文體修飾詞(字型、粗細、底線、刪除線等等),以矩形配置。 相反地,字元是特定字型檔案中的特定索引。 字形定義了一組可渲染的曲線,但沒有任何文本意義。 字形與字元之間可能存在多對多的對應關係。 來自相同字型且在基準線上循序排列的字元序列稱為GlyphRun。 DirectWrite 和 Direct2D 都會呼叫其最精確的圖像轉譯 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;
此版本包含更多當您實 作自定義文字轉譯器時很有用的參數。 最後一個參數用於應用程式實作的自定義繪圖效果。 (如需用戶端繪圖效果的詳細資訊,請參閱 如何將用戶端繪圖效果新增至文字版面配置。
每個字形串都從原點開始,並從這個原點放置在一條線上。 目前世界轉換和相關聯轉譯目標上選取的文字轉譯設定會變更字元。 此 API 通常只會由執行自己版面配置的應用程式(例如 Word Processor)或已實 作 IDWriteTextRenderer 介面的應用程式直接呼叫。
DirectWrite 和 Direct2D
Direct2D 透過 DrawGlyphRun 提供圖像層級轉譯服務。 不過,為了達成這一點,應用程式需要實作繪製的細節,這基本上要求獨立地重現 GDI 的 DrawText API 功能。
因此,Direct2D 提供接受文字而非字元的 API:ID2D1RenderTarget::DrawTextLayout 和 ID2D1RenderTarget::DrawText。 這兩種方法都會渲染到 Direct2D 表面。 若要轉譯至 GDI 表面,將提供 IDWriteBitmapRenderTarget::DrawGlyphRun 以供使用。 但此方法需要應用程式實作自定義文字轉譯器。 (如需詳細資訊,請參閱 呈現到 GDI 表面 主題。)
例如,應用程式的文字使用方式通常會很簡單:例如在固定版面配置按鈕上放上 確定 或 取消。 不過,隨著國際化和其他功能的加入,隨著時間推移,它變得更加複雜。 最後,許多應用程式都必須使用 DirectWrite 的 文字配置物件,並實作文字轉譯器。
因此,Direct2D 會提供分層 API,讓應用程式能夠簡單地啟動並變得更複雜,而不需要回溯或放棄其工作程序代碼。 下圖顯示簡化的檢視:
繪製文字
DrawText 是使用的最簡單 API。 它需要 Unicode 字串、前景筆刷、單一格式物件和目的矩形。 它會在佈局矩形中排版並呈現整個字串,並選擇性地裁剪它。 當您將一段簡單的文字放入固定布局的 UI 中時,這將非常有用。
繪製文字佈局
藉由建立 IDWriteTextLayout 物件,應用程式可以開始測量及排列文字和其他UI元素,並支援多個字型、樣式、底線和刪除線。 Direct2D 提供 DrawTextLayout API,可直接接受這個物件,並在指定時間點呈現文字。 (寬度和高度是由版面配置物件提供)。 除了實作所有預期的文字版面配置功能之外,Direct2D 還會將任何效果物件解譯為筆刷,並將該筆刷套用至選取的字元範圍。 它也會呼叫任何內嵌物件。 然後,如果應用程式想要的話,可以將非字元字元(圖示)插入文字中。 使用文字配置物件的另一個優點是字形位置會快取於其中。 因此,藉由針對多個繪製呼叫重複使用相同的版面配置物件,並避免重新計算每個呼叫的字元位置,就可以取得較大的效能提升。 GDI 的 DrawText 不存在這項功能。
DrawGlyphRun
最後,應用程式可以實作IDWriteTextRenderer介面本身,並呼叫 DrawGlyphRun 和 FillRectangle 本身或任何其他轉譯 API。 與文字版面配置物件的所有現有互動都會保持不變。
如需如何實作自定義文字轉譯器的範例,請參閱 使用自定義文字轉譯器來轉譯 主題。
字形渲染
將 DirectWrite 新增至現有的 GDI 應用程式可讓應用程式使用 IDWriteBitmapRenderTarget API 來轉譯字元。 DirectWrite 提供的 IDWriteBitmapRenderTarget::DrawGlyphRun 方法會將純色渲染至記憶體 DC,而不需要任何其他 API,例如 Direct2D。
這可讓應用程式取得進階文字轉譯功能,例如:
- 子像素 ClearType 可讓應用程式將字形置於子像素位置,以實現既清晰的字形渲染又精準的字形配置。
- Y 方向的抗鋸齒可讓您在較大的字形上更順暢地呈現曲線。
移至 Direct2D 的應用程式也會取得下列功能:
- 硬體加速:
- 能夠使用任意 Direct2D 筆刷填滿文字,例如星形漸層、線性漸層和點陣陣圖。
- 更多支援透過PushAxisAlignedClip、PushLayer和CreateCompatibleRenderTarget API 進行分層和裁剪。
- 具備支援灰階文字顯示的能力。 這會根據文字筆刷的不透明度和文字的抗鋸齒效果,正確填入目標 Alpha 通道。
為了有效地支持硬體加速,Direct2D 使用一種稍微不同的 Gamma 校正近似值,稱為Alpha 校正。 這不需要 Direct2D 在轉譯文字時檢查轉譯目標色彩圖元。
結論
本主題說明 Direct2D 與 DirectWrite 之間的差異和相似性,以及提供它們作為個別合作 API 的架構動機。