人們在日常生活中一直與文字溝通。 這是人們取用大量資訊的主要方式。 過去曾透過印刷媒體,包括文件、報紙、書籍等。 越來越多是他們的 Windows 電腦上的線上內容。 典型的 Windows 使用者會花很多時間閱讀自己電腦螢幕上的內容。 他們可能正在瀏覽網站、查看電子郵件、撰寫報表、填寫試算表或撰寫程式碼,但他們真正所做的就是閱讀。 雖然文字和字型幾乎滲透到 Windows 用戶體驗的每個部分,但對大多數用戶來說,螢幕上的閱讀並不像閱讀列印輸出那麼愉快。
對於 Windows 應用程式開發人員來說,撰寫文字處理程式碼是一項挑戰,因為提高可讀性、複雜格式設定和版面配置控件的需求,以及應用程式必須顯示的多種語言支援。 即使是最基本的文字處理系統也必須允許文字輸入、版面配置、顯示、編輯和複製和貼上。 Windows 使用者通常預期比這些基本功能還多,甚至需要簡單的編輯器支援多種字型、各種段落樣式、內嵌影像、拼字檢查和其他功能。 現代UI設計也不再局限於單一格式、純文本,但需要提供更豐富的字型和文字版面配置體驗。
這是 DirectWrite
如何讓 Windows 應用程式增強 UI 和檔的文字體驗的簡介。
改善文字體驗
新式 Windows 應用程式對其 UI 和檔中的文字有複雜的需求。 這些包括更佳的可讀性、對各種語言和腳本的支援,以及更佳的轉譯效能。 此外,大部分現有的應用程式都需要一種方法,來延續在 WindowsWin32 程式碼基礎中的既有投資。
DirectWrite
提供下列三項功能,可讓 Windows 應用程式開發人員改善其應用程式中的文字體驗:與轉譯系統獨立、高品質印刷樣式和多層功能。
Rendering-System 獨立
DirectWrite
與任何特定圖形技術無關。 應用程式可以自由使用最適合其需求的轉譯技術。 這可讓應用程式彈性地透過 GDI 和 Direct3D 或
Direct2D
,繼續轉譯應用程式的某些部分。 事實上,應用程式可以透過專有的渲染堆疊來轉譯 DirectWrite。
High-Quality 印刷樣式
DirectWrite
利用
OpenType
Font 技術的進步,在 Windows 應用程式中啟用高品質的印刷樣式。 DirectWrite 字型系統提供處理字型列舉、字型後援和字型快取的服務,這些字型都是處理字型的應用程式所需的。
DirectWrite
所提供的
OpenType
支援,可讓開發人員新增至其應用程式的進階印刷功能和國際文字支援。
支援進階印刷樣式功能
DirectWrite
可讓應用程式開發人員解除鎖定無法在 WinForms 或 GDI 中使用的 OpenType 字型功能。 DirectWrite
IDWriteTypography
對象會公開 OpenType 字型的許多進階功能,例如文體替代字型和斜體。 Microsoft Windows 軟體開發工具包 (SDK) 提供一組範例
OpenType
字型,這些字型的設計具有豐富的功能,例如 Pericles 和 Pescadero 字型。 如需 OpenType 功能的詳細資訊,請參閱
OpenType 字型功能
。
支援國際文字
DirectWrite
會使用
OpenType
字型來廣泛支援國際文字。 支援代理、BIDI、斷行和UVS等Unicode功能。 語言引導的腳本分類化、數字替換和字形塑造可確保任何腳本中的文字都有正確的版面配置和呈現。
目前支援下列文稿:
對於標示為 *的腳本,沒有預設的系統字型。 應用程式必須安裝支持這些文稿的字型。
亞美尼亞文
加拿大原住民音節文字
中文(簡體中文 & 繁體)
科普特語*
Devanagari
埃塞俄比亞文字
格拉戈利特*
古吉拉特語
馬拉雅拉姆文
Ogham*
'Phags-pa
古敘利亞文
多層功能性
DirectWrite
提供要素化的功能層,每個層會順暢地與下一層互動。 API 設計可讓應用程式開發人員根據其需求和排程,自由和彈性地採用個別層。 下圖顯示這些圖層之間的關聯性。
文字版面配置 API 提供可從
DirectWrite
取得的最高層級功能。 它為應用程式提供服務,以測量、顯示及與格式豐富的文字字串互動。 此文字 API 可用於目前使用 Win32 的 DrawText 的應用程式,以建置具有豐富格式文字的新式 UI。
實作自己版面配置引擎的文字密集型應用程式可能會使用下一層:腳本處理器。 腳本處理器會將文字區塊細分成腳本區塊,並處理 Unicode 表示法與字型中適當字元表示法之間的對應,讓腳本的文字可以正確地以正確的語言顯示。 文字版面配置 API 層所使用的版面配置系統是以字型和腳本處理系統為基礎。
圖像轉譯層是功能的最低層,可為實作自己的文字版面配置引擎的應用程式提供圖像轉譯功能。 圖像轉譯層也適用於實作自定義轉譯器的應用程式,透過
directWrite
文字格式 API 中的回呼函式修改圖像繪製行為。
DirectWrite
字型系統可供所有 DirectWrite 功能層使用,並讓應用程式存取字型和字元資訊。 其設計目的是要處理常見的字型技術和數據格式。 DirectWrite 字型模型遵循支援相同字型系列中任意數目粗細、樣式和延展的常見印刷樣式做法。 此模型與 WPF 和 CSS 相同,指定只有字重(粗體、淺色等)、樣式(正體、斜體或倾斜体)或延展(窄、紧缩、宽等)不同的字型會被視為單一字體家族的成員。
使用 ClearType 改善文字顯示
改善螢幕上的可讀性是所有 Windows 應用程式的重要需求。 認知心理學研究的證據表明,我們需要能夠準確地辨識每個字母,甚至字母之間的間距對於快速處理至關重要。 不對稱的字母和字會被視為醜陋,並降低閱讀體驗。 凱文·拉爾森是Microsoft先進閱讀技術小組的成員,並撰寫了一篇文章,該文章發表在Spectrum IEEE上。 本文稱為「文字技術」。
DirectWrite
中的文字使用 Microsoft ClearType 渲染,以增強文字的清晰性和可讀性。 ClearType 利用了現代 LCD 顯示器每個像素具有可個別控制的 RGB 條紋的特性。 DirectWrite 使用 ClearType 的最新增強功能,這些增強功能首次隨 Windows Vista 中的 Windows Presentation Foundation 一起推出,使它不僅能評估個別字母,還能評估字母之間的間距。 在這些 ClearType 增強功能出現之前,顯示字體大小為 10 或 12 點的文字很困難:我們可以在字母之間放置 1 個像素,但這通常不夠,或 2 個像素,這通常又太多。 使用子像素中的額外解析度可讓我們使用小數間距,以改善整個頁面的均勻性和對稱性。
下列兩個圖解顯示使用子圖元定位時,字形可能在任何子圖元界限上開始的方式。
以下插圖使用的是 ClearType 轉譯器的 GDI 版本,此版本未使用子圖元定位技術。
下圖是使用
DirectWrite
版本中的 ClearType 轉譯器來渲染,該轉譯器使用子像素定位技術。
以子圖元定位呈現的「技術」
請注意,字母 h 和 n 之間的間距在第二個影像中比較均勻,而字母 o 會從字母 n 進一步間距,甚至與字母 l 更相等。 另外要注意,字母 l 的字幹看起來更自然。
Subpixel ClearType 定位技術在螢幕上提供最精確的字元排列,特別是在小尺寸時,子像素與整個像素之間的差異代表每個字形寬度的顯著比例。 它可以讓文字在理想的解析度空間中計算,並在液晶顯示器的色帶自然位置渲染,具備次像素精度。 根據定義,使用這項技術測量和轉譯的文字與解析度無關,這表示文字的版面配置在各種顯示器解析度的範圍中達成完全相同。
與任一類型的 GDI ClearType 呈現不同,次像素 ClearType 提供最精確的字元寬度呈現。
根據預設,文字字串 API 採用子像素文字渲染,這表示它會以不受當前顯示解析度影響的理想解析度測量文字,並根據真正縮放的字形進階寬度和定位位移來產生字形定位結果。
對於大型文字,
DirectWrite
也能夠啟用沿著 Y 軸的反鋸齒,讓邊緣更流暢,並按照字型設計師的原意呈現字母。 下圖顯示 y 方向的消除鋸齒。
雖然
DirectWrite
文字預設會使用子圖元 ClearType 來定位和轉譯,但其他轉譯選項可供使用。 許多現有的應用程式會使用 GDI 來轉譯大部分的 UI,有些應用程式會使用系統編輯控件來繼續使用 GDI 進行文字轉譯。 將 DirectWrite 文字新增至這些應用程式時,可能需要犧牲子圖元 ClearType 所提供的閱讀體驗改進,讓文字在應用程式上具有一致的外觀。
為了符合這些需求,
DirectWrite
也支援下列轉譯選項:
子圖元 ClearType (預設值)。
在水平和垂直維度上具有平滑處理的子像素 ClearType。
別名文字。
GDI 自然寬度(例如,Microsoft Word 閱讀檢視使用)。
GDI 相容寬度(包括東亞嵌入式位圖)。
每個轉譯模式都可以透過 DirectWrite API 和新的 Windows 7 收件匣 ClearType 微調器微調。
從 Windows 8 開始,您應該在大部分情況下使用灰階文字抗鋸齒。 如需詳細資訊,請參閱下一節。
支援自然版面配置
自然版面配置與解析度無關,因此字元的間距不會隨著您放大或縮小,或視顯示器的 DPI 而定而變更。 次要的優點是,間距能忠實呈現字型的設計。 DirectWrite 對自然渲染的支援使自然版面配置成為可能,這表示個別字形可以定位為圖元的一小部分。
雖然自然版面配置是預設值,但某些應用程式需要轉譯與 GDI 相同的間距和外觀的文字。 針對這類應用程式,DirectWrite 提供 GDI 傳統和 GDI 自然測量模式和對應的轉譯模式。
上述任何轉譯模式都可以與兩種反鋸齒模式之一結合:ClearType 或灰階。 ClearType 反鋸齒透過分別調整每個像素的紅色、綠色和藍色值,達到更高的解析度。 灰階反鋸齒只會計算每個圖元的一個涵蓋範圍(或 Alpha) 值。 ClearType 是預設值,但建議 Windows 市集應用程式使用灰階抗鋸齒,因為它的速度較快,且與標準的抗鋸齒技術相容,同時保持高度可讀性。
API 概觀
IDWriteFactory
介面是使用 DirectWrite 功能的起點。 Factory 是根物件,可建立一組可以一起使用的物件。
格式化和版面配置作業是作業的必要條件,因為文字必須正確格式化並配置至指定的條件約束集,才能進行繪製或點擊測試。 您可以為此目的使用
IDWriteFactory
建立的兩個主要物件,
IDWriteTextFormat
和
IDWriteTextLayout
。
IDWriteTextFormat
物件代表文字段落的格式資訊。
IDWriteFactory::CreateTextLayout
函式會採用輸入字串、要填入的空間維度等相關聯的條件約束,以及
IDWriteTextFormat
物件,並將完整分析和格式化的結果放入
IDWriteTextLayout
,以用於後續作業。
然後,應用程式可以使用
DrawTextLayout
函式來轉譯文字,
Direct2D
或實作可使用 GDI、Direct2D 或其他圖形系統的回呼函式來轉譯字元。 對於單一格式文字,Direct2D 上的
DrawText
函式提供更簡單的方式繪製文字,而不需要先建立
IDWriteTextLayout
物件。
下列程式代碼範例示範應用程式如何使用
IDWriteTextFormat
來格式化單一段落,並使用
Direct2D
DrawText
函式加以繪製。
HRESULT DemoApp::DrawHelloWorld(
ID2D1HwndRenderTarget* pIRenderTarget
HRESULT hr = S_OK;
ID2D1SolidColorBrush* pIRedBrush = NULL;
IDWriteTextFormat* pITextFormat = NULL;
IDWriteFactory* pIDWriteFactory = NULL;
if (SUCCEEDED(hr))
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&pIDWriteFactory));
if(SUCCEEDED(hr))
hr = pIDWriteFactory->CreateTextFormat(
L"Arial",
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
10.0f * 96.0f/72.0f,
L"en-US",
&pITextFormat
if(SUCCEEDED(hr))
hr = pIRenderTarget->CreateSolidColorBrush(
D2D1:: ColorF(D2D1::ColorF::Red),
&pIRedBrush
D2D1_RECT_F layoutRect = D2D1::RectF(0.f, 0.f, 100.f, 100.f);
// Actually draw the text at the origin.
if(SUCCEEDED(hr))
pIRenderTarget->DrawText(
L"Hello World",
wcslen(L"Hello World"),
pITextFormat,
layoutRect,
pIRedBrush
// Clean up.
SafeRelease(&pIRedBrush);
SafeRelease(&pITextFormat);
SafeRelease(&pIDWriteFactory);
return hr;
存取字型系統
除了使用上述範例中的 IDWriteTextFormat 介面來指定文字字串的字型系列名稱,DirectWrite 透過字型列舉提供應用程式對字型選取的更多控制權,以及根據內嵌檔字型建立自定義字型集合的能力。
IDWriteFontCollection 對像是字型系列集合。 DirectWrite 可讓您透過稱為系統字型集合的特殊字型集合,存取系統上安裝的字型集合。 這是藉由呼叫 IDWriteFactory 物件的 GetSystemFontCollection 方法來取得。 應用程式也可以從應用程式定義回呼所列舉的一組字型建立自定義字型集合,也就是應用程式所安裝的私人字型,或內嵌在檔中的字型。
然後,應用程式可以呼叫 getFontFamily GetFontFamily 以取得集合中的特定 FontFamily 對象,然後呼叫 IDWriteFontFamily::GetFirstMatchingFont,以取得特定 IDWriteFont 物件。
IDWriteFont 物件代表字型集合中的字型,並公開屬性和一些基本字型計量。
IDWriteFontFace 是另一個物件,代表字型,並在字型上公開一組完整的計量。
IDWriteFontFace 可以直接從字型名稱建立;應用程式不需要取得字型集合即可存取它。 它適用於文字版面配置應用程式,例如需要查詢特定字型詳細數據的Microsoft Word。
下圖說明這些對象之間的關聯性。
IDWriteFontFace 物件代表字型,並提供比 idWriteFont物件更詳細的字型資訊。
IDWriteFontFace 中的字型和字元計量對於實作文字配置的應用程式很有用。
大部分主流應用程式不會直接使用這些 API,而會改用 IDWriteFont 或直接指定字型系列名稱。
下表摘要說明這兩個物件的使用案例。
IDWriteFont
IDWriteFontFace
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&pDWriteFactory)
IDWriteFontCollection* pFontCollection = NULL;
// Get the system font collection.
if (SUCCEEDED(hr))
hr = pDWriteFactory->GetSystemFontCollection(&pFontCollection);
UINT32 familyCount = 0;
// Get the number of font families in the collection.
if (SUCCEEDED(hr))
familyCount = pFontCollection->GetFontFamilyCount();
for (UINT32 i = 0; i < familyCount; ++i)
IDWriteFontFamily* pFontFamily = NULL;
// Get the font family.
if (SUCCEEDED(hr))
hr = pFontCollection->GetFontFamily(i, &pFontFamily);
IDWriteLocalizedStrings* pFamilyNames = NULL;
// Get a list of localized strings for the family name.
if (SUCCEEDED(hr))
hr = pFontFamily->GetFamilyNames(&pFamilyNames);
UINT32 index = 0;
BOOL exists = false;
wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
if (SUCCEEDED(hr))
// Get the default locale for this user.
int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH);
// If the default locale is returned, find that locale name, otherwise use "en-us".
if (defaultLocaleSuccess)
hr = pFamilyNames->FindLocaleName(localeName, &index, &exists);
if (SUCCEEDED(hr) && !exists) // if the above find did not find a match, retry with US English
hr = pFamilyNames->FindLocaleName(L"en-us", &index, &exists);
// If the specified locale doesn't exist, select the first on the list.
if (!exists)
index = 0;
UINT32 length = 0;
// Get the string length.
if (SUCCEEDED(hr))
hr = pFamilyNames->GetStringLength(index, &length);
// Allocate a string big enough to hold the name.
wchar_t* name = new (std::nothrow) wchar_t[length+1];
if (name == NULL)
hr = E_OUTOFMEMORY;
// Get the family name.
if (SUCCEEDED(hr))
hr = pFamilyNames->GetString(index, name, length+1);
if (SUCCEEDED(hr))
// Print out the family name.
wprintf(L"%s\n", name);
SafeRelease(&pFontFamily);
SafeRelease(&pFamilyNames);
delete [] name;
SafeRelease(&pFontCollection);
SafeRelease(&pDWriteFactory);
文字轉譯 API 可讓 DirectWrite 中的字元 字型轉譯成 Direct2D 介面或 GDI 裝置獨立位圖,或轉換成外框或位圖。 DirectWrite 中的 ClearType 渲染支援子圖元定位,與 Windows 上先前的實作相比,具有改善的銳度和對比。 DirectWrite 也支持別名的黑白文字,以支援包含內嵌位圖之東亞字型的案例,或使用者已停用任何類型的字型平滑處理的情況。
所有選項都可透過 DirectWrite API 存取的所有可用 ClearType 旋鈕來調整,也透過新的 Windows 7 ClearType 微調程式控制面板小程序公開。
有兩個 API 可用於轉譯圖像,一個透過 Direct2D 提供硬體加速轉譯,另一個則提供軟體轉譯至 GDI 位圖。 使用 IDWriteTextLayout 並實作 IDWriteTextRenderer 回呼的應用程式,可以在回應 DrawGlyphRun 回呼時,呼叫這其中一個函式。 此外,實作自己版面配置或處理圖像層級數據的應用程式可以使用這些 API。
ID2DRenderTarget::DrawGlyphRun
應用程式可以使用 Direct2D API DrawGlyphRun,為使用 GPU 轉譯文字提供硬體加速。 硬體加速會影響文字轉譯管線的所有階段,從將字形合併為字形串、篩選字形串位圖,到將 ClearType 混合演算法套用至最終顯示輸出。 這是建議的 API,可取得最佳的轉譯效能。
IDWriteBitmapRenderTarget::DrawGlyphRun
應用程式可以使用 IDWriteBitmapRenderTarget::DrawGlyphRun 方法,將一串字形以軟體渲染到 32-bpp 位圖中。
IDWriteBitmapRenderTarget 物件會封裝位圖和可用於渲染字形的記憶體裝置環境。 如果您想要保留 GDI,此 API 會很有用,因為您有在 GDI 中轉譯的現有程式代碼基底。
如果您的應用程式具有使用 GDI 的現有文字版面配置程式代碼,而且您想要保留其現有的版面配置程式代碼,但只針對轉譯字元的最後一個步驟使用 DirectWrite,IDWriteGdiInterop::CreateFontFaceFromHdc 提供兩個 API 之間的網橋。 呼叫此函式之前,應用程式會使用 IDWriteGdiInterop::CreateFontFaceFromHdc 函式,從裝置內容取得字型參考。
在大部分情況下,應用程式可能不需要使用這些圖像轉譯 API。 應用程式建立 IDWriteTextLayout 物件之後,可以使用 ID2D1RenderTarget::DrawTextLayout 方法來轉譯文字。
自定義渲染模式
許多參數會影響文字轉譯,例如 gamma、ClearType 層級、圖元幾何,以及增強的對比。 轉譯參數由一個物件封裝,該物件會實作公用的 IDWriteRenderingParams 介面。 轉譯參數物件會根據 Windows 7 中 ClearType 控制面板小程式指定的硬體屬性和/或使用者喜好設定,自動初始化。 一般而言,如果用戶端使用 DirectWrite 版面配置 API,DirectWrite 會自動選取對應至指定測量模式的轉譯模式。
想要更多控件的應用程式可以使用 IDWriteFactory::CreateCustomRenderingParams 來實作不同的轉譯選項。 此函式也可以用來設定伽瑪、像素幾何和增強的對比。
以下是可用的各種轉譯選項:
子像素消除鋸齒技術
應用程式將 renderingMode 參數設定為 DWRITE_RENDERING_MODE_NATURAL,以指定在僅限水平維度中進行反鋸齒的繪圖。
次像素在水平和垂直維度的抗鋸齒。
應用程式會將 renderingMode 參數設定為 DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC,以指定在水平和垂直方向上使用抗鋸齒的渲染。 這使得曲線和對角線看起來更平滑,但會有些許模糊感,通常用於字體大小超過 16 ppem 的情況。
應用程式會將 renderingMode 參數設定為DWRITE_RENDERING_MODE_ALIASED來指定別名文字。
應用程式會將 pixelGeometry 參數設定為DWRITE_PIXEL_GEOMETRY_FLAT來指定灰階文字。
GDI 相容寬度(包括東亞內嵌位圖)
應用程式會將 renderingMode 參數設定為 DWRITE_RENDERING_MODE_GDI_CLASSIC,以指定使用 GDI 相容寬度的消除鋸齒模式。
GDI 自然寬度
應用程式會將 renderingMode 這個參數設定為 DWRITE_RENDERING_MODE_GDI_NATURAL,以指定相容於 GDI 自然寬度的抗鋸齒。
應用程式開發人員在進行大尺寸轉譯時,可能偏好使用字型外框,而不是點陣化為位圖。 應用程式會將 renderingMode 參數設定為 DWRITE_RENDERING_MODE_OUTLINE,以指定轉譯應該略過光柵化器,並直接使用外框。
GDI 互作性
IDWriteGdiInterop 介面提供與 GDI 的互作性。 這可讓應用程式繼續對 GDI 程式代碼基底的現有投資,並選擇性地使用 DirectWrite 進行轉譯或配置。
以下是可讓應用程式移轉至 GDI 字型系統的 API:
CreateFontFromLOGFONT
建立 IDWriteFont 物件,該物件符合 LOGFONT 結構所指定的屬性。
ConvertFontToLOGFONT
根據所指定 IDWriteFont的 GDI 相容屬性,初始化 LOGFONT 結構。
ConvertFontFaceToLOGFONT
根據指定之 IDWriteFontFace的 GDI 相容屬性,初始化 LOGFONT 結構。
CreateFontFaceFromHdc
建立一個 IDWriteFontFace 物件,該物件對應於目前所選的 HFONT。
改善閱讀體驗對用戶來說,無論是在螢幕上還是紙上,都有很大的價值。
DirectWrite 為應用程式開發人員提供易於使用和分層程式設計模型,以改善其 Windows 應用程式的文字體驗。 應用程式可以使用 DirectWrite,透過版面配置 API 為其 UI 和文件轉譯格式豐富的文字。 針對更複雜的案例,應用程式可以直接使用圖像、存取字型等,並利用 DirectWrite 的強大功能來提供高品質的印刷樣式。
DirectWrite 的互作性功能 可讓應用程式開發人員轉送現有的 Win32 程式代碼基底,並在其應用程式中選擇性地採用 DirectWrite。