|
列挙に定義された定数 | 説明 |
DWRITE_FONT_FEATURE_TAG_ALTERNATIVE_FRACTIONS | スラッシュで区切られた構成テーブルを元に設定する |
DWRITE_FONT_FEATURE_TAG_KERNING | グリフの間に一貫性あるスペースを提供するため、グリフの間を調整する |
DWRITE_FONT_FEATURE_TAG_SWASH | 既定のグリフを、スワッシュ (swash) グリフに置き換える |
上記の表の詳細やタイポグラフィに関する詳細は、次のアドレスを参照してください。
ページのトップへ
タスク1- 複数の書式を持つテキストを描画する
Note: 練習で使用する Visual Studio のプロジェクトには、コメントアウトされたコードが含まれます。このあとの作業では、コードの実装にあたっては、コメント アウトしている既存コードのコメントを解除してもよいですし、自身で代替のコードを記述しても構いません。
この練習では、練習 1 で作成したコードを修正して、テキストの複数箇所に異なる書式を適用し、タイポグラフィも追加します。
このタスクを終了してアプリケーションを実行すると、次図のようにアプリケーションのウィンドウに表示されます。
このあとの作業のために、演習用フォルダ内にある次のソリューション ファイルを開いてください。
- Ex2_Starter\DWrite HOL.sln
1. HandsOnLab.cpp において、DemoApp::CreateDeviceIndependentResources メソッドの中で、ファクトリの CreateTextLayout メソッドを呼び出して、IDWriteTextLayout オブジェクトを作成します。IDWriteTextLayout オブジェクトは、特定のテキスト部分に異なる書式を適用できるなど、より詳細な書式整形の機能を提供します。CreateTextLayout メソッドの引数には、以下の情報を持つ引数が含まれます。
- 練習 1 の手順 2 の a で用意したテキスト文字列 (m_wszHelloWorld)
- 練習 1 の手順 2 の a で用意したテキスト文字列の長さ (m_cHelloWorldLength)
- 練習 1 の手順 2 の b で用意した IDWriteTextFormat オブジェクト (m_spTextFormat)
- デバイス非依存のピクセル単位で表した、テキストを配置する領域の幅
- デバイス非依存のピクセル単位で表した、テキストを配置する領域の高さ
IFR(m_spDWriteFactory->CreateTextLayout(
m_wszHelloWorld,
m_cHelloWorldLength,
m_spTextFormat,
640.0f,
480.0f,
&m_spTextLayout
));
2. 前述の作成した IDWriteTextLayout オブジェクトに対して、次の要領で、特定の文字列の部分に異なる書式を適用します。
a. 前述のコードに続けて、DemoApp::CreateDeviceIndependentResources メソッドの中で、文字列の「DirectWrite」の「Di」の部分に対して、フォント サイズを 100 にします。「Di」のインデックスは 18 から始まり、長さは 2 です。
// Format the "DirectWrite" substring to be font size 100
{
DWRITE_TEXT_RANGE textRange = {18, /* start index where ...
2 /* length of the "Di" ...*/ };
IFR(m_spTextLayout->SetFontSize(100.0f, textRange));
}
b. 引き続き、文字列の「DirectWrite」の部分に、下線を付けて太字にします。
// Format the word "DirectWrite" to be underlined and bold.
{
DWRITE_TEXT_RANGE textRange = {18, /* start index where ...
11 /* length of the substring ... */};
IFR(m_spTextLayout->SetUnderline(TRUE, textRange));
IFR(m_spTextLayout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange));
}
3. 次の要領で、テキストの一部分に特定のタイポグラフィを追加します。
a. 前述のコードに続けて、DemoApp::CreateDeviceIndependentResources メソッドの中で、練習 1 で作成した IDWriteFactory オブジェクト (ファクトリ) を使用して、IDWriteTypography オブジェクトを作成します。
// Create a typography object.
IDWriteTypographyPtr spTypography;
IFR(m_spDWriteFactory->CreateTypography(&spTypography));
b. DWRITE_FONT_FEATURE オブジェクトを作成して「stylistic set 7」で初期化し、これを IDWriteTypography オブジェクトに追加します。
// Set the stylistic set
DWRITE_FONT_FEATURE fontFeature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7,
1};
IFR(spTypography->AddFontFeature(fontFeature));
c. テキスト全体に、この IDWriteTypography オブジェクトを適用するように、IDWriteTextLayout オブジェクトに指定します。
DWRITE_TEXT_RANGE textRange = {0,
m_cHelloWorldLength};
IFR(m_spTextLayout->SetTypography(spTypography, textRange));
4. DemoApp::OnResize メソッドの中で、描画すべき領域の高さと幅の最大値を指定します。このようにすると、テキストの配置対象の領域を、ウィンドウと同じ大きさにすることができます。
m_spTextLayout->SetMaxWidth(static_cast<FLOAT>(width));
m_spTextLayout->SetMaxHeight(static_cast<FLOAT>(height))
5. 次の要領で、テキストを配置し描画する領域の原点を指定します。
a. DemoApp::DrawTextLayout メソッドの中で、以下の Direct2D ベースの構造体を追加します。
D2D1_POINT_2F origin = D2D1::Point2F(
static_cast<FLOAT>(rc.top),
static_cast<FLOAT>(rc.left)
);
b. Direct2D の描画用ターゲットの DrawTextLayout メソッドを使用し、引数には、先述の手順で作成した IDWriteTextLayout オブジェクトを渡し、スクリーンにテキストを描画します。この引数を含め、このメソッドの引数には、以下の 3 つを渡します。
- 前の手順で作成した位置を表す構造体
- 手順 1 で作成した IDWriteTextLayout
- Direct2D ベースの黒色のブラシ
m_spRT->DrawTextLayout(origin,
m_spTextLayout,
m_spBlackBrush
);
6. これで配置と書式の指定は済んだので、DemoApp::DrawD2DContent メソッドの中から、DemoApp::DrawTextLayout メソッドを呼び出して、コンテンツを描画します。
DemoApp::DrawD2DContent メソッドでは、既存の練習 1 の DrawText メソッド呼び出しをコメントアウトし、DemoApp::DrawTextLayout メソッドを呼び出すように修正します。
DrawTextLayout();
ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。
なお、この練習 2 を行って完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。
- Ex2_Solution\DWrite HOL.sln
ページのトップへ
4. 練習 3: 見栄えのよいテキスト描画のカスタマイズ
この練習では、カスタム描画処理を作り、イメージデータでテキスト部分を塗りつぶすようにします。GDI や Direct2D を併用したいとき、また、OpenGL を使いたいときなど、描画に通常とは異なる API を用いる際には、カスタム描画処理を用いると便利です。
カスタム描画処理を実装するため、ここでは次の手順を行います。
- テキストのカスタム描画処理を行う実装 (custom text renderer) を用意する
- カスタム描画処理の実装の中でグリフを構成する際に、テキストのアウトラインを生成するために、Direct2D と DirectWrite を使用する
- 配置の指定とカスタム描画処理を利用して、テキストを描画する
IDWriteTextRenderer オブジェクト
IDWriteTextRenderer インターフェイスには、テキストのカスタム描画向けに、クライアント アプリケーションが実装するコールバック関数が、数多く定義されています。また、IDWriteTextRenderer オブジェクトは、 IDWriteTextLayout::Draw メソッドを呼び出す際に、引数として利用できます。
ここで扱うカスタム描画処理のサンプルでは、アウトラインの描画と Direct2D による描画を行います。高度な実装として、ビットマップを描画するブラシを使用すれば、ビットマップ描画も可能です。
ページのトップへ
タスク 1 - テキストから図形へ変換するカスタム描画処理を実装する
Note: 練習で使用する Visual Studio のプロジェクトには、コメントアウトされたコードが含まれます。このあとの作業では、コードの実装にあたっては、コメント アウトしている既存コードのコメントを解除してもよいですし、自身で代替のコードを記述しても構いません。
この練習では、練習 2 で作成したコードを修正して、カスタム描画処理の実装 (CustomTextRenderer クラス) を作成し、テキストを Direct2D の図形に変換して、イメージ ブラシで塗りつぶし、見栄えのよいテキスト描画を行います。
このタスクを終了してアプリケーションを実行すると、次図のようにアプリケーションのウィンドウに表示されます。
このあとの作業のために、演習用フォルダ内にある次のソリューション ファイルを開いてください。
- Ex3_Starter\DWrite HOL.sln
この練習では、前述までの練習で使用してきた HandsOnLab.cpp と HandsOnLab.h に加え、CustomTextRenderer.cpp と CustomTextRenderer.h という名前のソース ファイルを使用します。カスタム描画用の実装を作成するには、実装すべき多くのメソッドがありますが、この練習では、DrawGlyphRun という名前のメソッドに焦点を当てます。その他のメソッドは、既に実装されています。
CustomTextRenderer クラスは、IDWriteTextRenderer インターフェイスが既に実装されており、ヘッダー CustomTextRenderer.h には、このクラスの定義があります。 CustomTextRenderer クラスのコンストラクタには、次のように定義されています。
CustomTextRenderer(
ID2D1Factory* pD2DFactory,
ID2D1HwndRenderTarget* pRT,
ID2D1SolidColorBrush* pOutlineBrush,
ID2D1BitmapBrush* pFillBrush
);
CustomTextRenderer クラスのコンストラクタには、次の 4 つの引数を渡します。
- Direct2D のファクトリ
- Direct2D の描画用ターゲット
- テキストのアウトラインを描画する Direct2D のブラシ
- テキストを塗りつぶす Direct2D のブラシ
DrawGlyphRun メソッドは、CustomTextRenderer.cpp に定義されています。このメソッドは、一連のグリフを描画する際に使用される情報として、複数の引数を受け取ります。たとえば、ベースラインの原点、グリフの詳細情報、グリフのオフセットなどです。このメソッドは、次のように定義されています。
STDMETHODIMP CustomTextRenderer::DrawGlyphRun(
__maybenull void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_TEXT_MEASURING_METHOD measuringMethod,
__in DWRITE_GLYPH_RUN const* glyphRun,
__in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
IUnknown* clientDrawingEffect
)
1. CustomTextRenderer.cpp において、DrawGlyphRun メソッドの中で、Direct2D の ID2D1PathGeometry オブジェクト (path オブジェクト) を作成します。
// Create the path geometry.
ID2D1PathGeometryPtr pPathGeometry;
IFR(m_spD2DFactory->CreatePathGeometry(
&pPathGeometry
));
2. 前述のコードに引き続き、path オブジェクトから sink オブジェクトを作成します。この sink オブジェクトを使用して、path オブジェクトに対して図形を構築することができます。
// Write to the path geometry using the geometry sink.
// We are going to create an hour glass.
ID2D1GeometrySinkPtr pSink;
IFR(pPathGeometry->Open(
&pSink
));
3. IDWriteFontFace::GetGlyphRunOutline メソッドを使用して、テキストのアウトラインに関する形状の情報を取得し、これを sink オブジェクトへ設定します。
// Get the glyph run outline geometries back from DirectWrite and place ...
// geometry sink
IFR(glyphRun->fontFace->GetGlyphRunOutline(
glyphRun->fontEmSize,
glyphRun->glyphIndices,
glyphRun->glyphAdvances,
glyphRun->glyphOffsets,
glyphRun->glyphCount,
glyphRun->isSideways,
glyphRun->bidiLevel%2,
pSink
));
4. sink オブジェクトを閉じます。
IFR(pSink->Close());
5. グリフの原点を表す変換用の行列データを作成します。DrawGlyphRun メソッドでは、引数を介して、描画すべき領域に対するベースラインの原点座標が提供されます。これによって、グリフを描画すべき相対的なオフセットが決定します。このことは、ベースラインの原点座標を使用して、path オブジェクト (前述の手順でグリフのアウトライン情報は保持済み) に対して変換が必要であることを意味します。
// Initialize a matrix to translate the origin of the glyph run.
D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(
1.0f, 0.0f,
0.0f, 1.0f,
baselineOriginX, baselineOriginY
);
6. path オブジェクト (pPathGeometry) と前述の行列 (matrix) を使用して変換処理を行い、ID2D1TransformedGeometry オブジェクトを作成します。
ID2D1TransformedGeometryPtr pTransformedGeometry;
IFR(m_spD2DFactory->CreateTransformedGeometry(
pPathGeometry,
&matrix,
&pTransformedGeometry
));
7. 前述の変換後の ID2D1TransformedGeometry オブジェクトと黒色のブラシを引数にして、DrawGeometry メソッドを呼び出し、テキストのアウトラインをターゲット (m_spRT) に描画します。
// Draw the outline of the glyph run
m_spRT->DrawGeometry(
pTransformedGeometry,
m_spOutlineBrush
);
8. 同様に、ID2D1TransformedGeometry オブジェクトと塗りつぶしブラシを引数にして、FillGeometry メソッドを呼び出し、テキストの塗りつぶし部分をターゲット (m_spRT) に描画します。
// Fill in the glyph run
m_spRT->FillGeometry(
pTransformedGeometry,
m_spFillBrush
);
この他にも、コールバック関数として、DrawUnderline や DrawStrikethrough、DrawInlineObject、IsPixelSnappingDisabled、GetCurrentTransform、また、GetPixelsPerDip などがあります。関連情報として、このあとの「参考」欄も参照してください。
9. 前述のカスタム描画処理の実装を使用してテキスト描画を行うため、HandsOnLab.cpp に以下のコードを追加します。
a. HandsOnLab.cpp において、DemoApp::CreateDeviceResources メソッドの中で、CustomTextRenderer クラスのインスタンスを作成します。それを参照するアドレスを、m_spTextRenderer に格納します。
// Create the text renderer
m_spTextRenderer = new CustomTextRenderer(
m_spD2DFactory,
m_spRT,
m_spBlackBrush,
m_spBitmapBrush
);
b. DemoApp::DrawTextLayoutUsingGeometries メソッドの中で、IDWriteTextLayout オブジェクトに対して、前述のカスタム描画用のオブジェクト (CustomTextRenderer) を使用して、描画します。
HRESULT DemoApp::DrawTextLayoutUsingGeometries()
{
HRESULT hr;
RECT rc;
GetClientRect(
m_hwnd,
&rc);
D2D1_POINT_2F origin = D2D1::Point2F(
static_cast<FLOAT>(rc.top),
static_cast<FLOAT>(rc.left)
);
// Draw the text layout using DirectWrite
IFR(m_spTextLayout->Draw(
NULL,
m_spTextRenderer,
origin.x,
origin.y
));
return S_OK;
}
c. DrawD2DContent メソッドの中から、前述の DrawTextLayoutUsingGeometries メソッドを呼び出すように変更します。今まで練習で使用した DrawText メソッド呼び出しや DrawTextLayout メソッド呼び出しは、コメントアウトします。
// Exercise 1
//DrawText();
// Exercise 2
// DrawTextLayout();
// Exercise 3
IFR(DrawTextLayoutUsingGeometries());
ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。
なお、この練習 3 を行って完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。
- Ex3_Solution\DWrite HOL.sln
参考
CustomTextRenderer クラスに含まれる CustomTextRenderer::DrawStrikethrough メソッドや、CustomTextRenderer::DrawUnderline メソッドは、それぞれ、「取り消し線」や「下線」を描画するサンプルです。これらのメソッドは、それぞれ取り消し線や下線の描画が必要なときに呼び出されます。
今回のサンプルの実装では、既に下線があるので、CustomTextRenderer::DrawUnderline メソッドが呼び出されます。一方、CustomTextRenderer::DrawStrikethrough メソッドが呼び出されるようにするには、出力するテキストの修飾として「取り消し線」を付ける必要があります。
たとえば、HandsOnLab.cpp の DemoApp::CreateDeviceIndependentResources メソッドの中で、練習 2 の手順 2 の b で行った下線を付ける設定 (SetUnderline メソッド) のほか、次のように取り消し線を設定すれば (SetStrikeThrough メソッド)、取り消し線を描画する際に、カスタム描画を実装した CustomTextRenderer::DrawStrikethrough メソッドが呼び出されます。
// Format the word "DWrite" to be underlined and bold
{
DWRITE_TEXT_RANGE textRange = {18, /* start index where ... */
11 /* length of the substring ... */};
IFR(m_spTextLayout->SetUnderline(TRUE, textRange));
IFR(m_spTextLayout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, extRange));
IFR(m_spTextLayout->SetStrikethrough(TRUE, textRange)); ← 取り消し線
}
ページのトップへ
5. まとめ
この演習では、次に挙げる点を取り上げました。
- DirectWrite API を使用して、テキストの整形と配置を行う
- Direct2D API を使用して、テキストの描画を行う
- DirectWrite API のいつくかの異なる方法で描画する
- Direct2D API を介して、テキストをグラフィックスとして扱う
- テキストのカスタム レンダリング (custom text renderer) を実装する
ページのトップへ