次の方法で共有


Windows 7
ハンズオン ラボ: グラフィックス ~ DirectWrite ~

更新日: 2009 年 8 月 28 日


ハンズオンラボ

ダウンロード

目次

  1. 演習: グラフィックス ~ DIRECTWRITE ~
  2. 練習 1: 簡単なテキスト描画

    タスク 1- 簡単なテキストを描画する

  3. 練習 2: 複数の書式を持つテキスト描画

    タスク 1 - 複数の書式を持つテキストを描画する

  4. 練習 3: 見栄えのよいテキスト描画のカスタマイズ

    タスク 1 - テキストから図形へ変換するカスタム描画処理を実装する

  5. まとめ

このドキュメントに記載されている情報 (URL 等のインターネット Web サイトに関する情報を含む) は、将来予告なしに変更することがあります。このドキュメントに記載された内容は情報提供のみを目的としており、明示または黙示に関わらず、これらの情報についてマイクロソフトはいかなる責任も負わないものとします。

お客様が本製品を運用した結果の影響については、お客様が負うものとします。お客様ご自身の責任において、適用されるすべての著作権関連法規に従ったご使用を願います。このドキュメントのいかなる部分も、米国 Microsoft Corporation の書面による許諾を受けることなく、その目的を問わず、どのような形態であっても、複製または譲渡することは禁じられています。ここでいう形態とは、複写や記録など、電子的な、または物理的なすべての手段を含みます。

マイクロソフトは、このドキュメントに記載されている内容に関し、特許、特許申請、商標、著作権、またはその他の無体財産権を有する場合があります。別途マイクロソフトのライセンス契約上に明示の規定のない限り、このドキュメントはこれらの特許、商標、著作権、またはその他の無体財産権に関する権利をお客様に許諾するものではありません。

別途記載されていない場合、このソフトウェアおよび関連するドキュメントで使用している会社、組織、製品、ドメイン名、電子メール アドレス、ロゴ、人物、出来事などの名称は架空のものです。実在する会社名、組織名、商品名、個人名などとは一切関係ありません。

© 2009 Microsoft Corporation. All rights reserved.

Microsoft、MS-DOS、Windows、Windows NT、MSDN、Active Directory、BizTalk、Windows Server、SQL Server、SharePoint、Outlook、PowerPoint、FrontPage、Visual Basic、Visual C++、Visual C#、Visual Studio は、米国 Microsoft Corporation の米国およびその他の国における登録商標または商標です。

記載されている会社名、製品名には、各社の商標のものもあります。

1. 演習: グラフィックス ~ DIRECTWRITE ~

DirectWrite は、DirectX の新しい API の一つであり、アプリケーションにおいて、高品質なテキストの描画、また、解像度に依存しないアウトライン フォント、完全な Unicode やレイアウトなどのサポートを可能にします。DirectWrite は次に挙げる機能を提供します。

  • ドキュメントやユーザー インターフェイス (UI) において、テキストの可読性を高める、デバイスに依存しないレイアウト (配置)
  • GDI や Direct2D、アプリケーション固有の描画テクノロジで利用できる高品質なテキスト描画、サブピクセル、ClearType
  • Unicode に対応した API
  • DirectWrite を Direct2D と併用した際の、ハードウェア レンダリングによるテキスト描画
  • 様々な書式のサポート
  • OpenType® フォントで使用される高度なタイポグラフィのサポート
  • すべてのサポート対象の言語におけるレイアウトと描画のサポート
  • GDI に準じたレイアウトと描画

DirectWrite のフォントシステムでは、「場所を問わず、どんなフォントでも (any font anywhere)」という使い方を可能にしており、手動やプログラム操作でのフォントの検索に役立つ、強化された階層構造のフォントのグループ化を可能にします。API では、様々な書式のテキストについて、サイズの計測や描画、また、ヒットテキストなどをサポートします。DirectWrite は、Windows 7 の主要な国語の上に構築され、グローバリゼーションやローカリゼーションに対応したアプリケーション向けのすべての言語においてテキストを処理することができます。また、DirectWrite では、独自のレイアウトやグリフの処理を行いたい開発者向けに、低レベルのグリフのレンダリングを行う API も提供しています。

この演習では、テキスト向けの新しい API である DirectWrite の使用方法、テキストの整形や配置の方法、また、Direct2D を併用してテキストを描画する方法について取り上げます。この演習は、3 つのパート (3 つの練習) に分かれています。最初の練習では、単一の書式の、一行分のテキストを作成します。2 つ目の練習では、複数の書式を使用したテキストを作成します。3 つ目の練習では、グラフィックスとして、テキストに特別な方法で塗りつぶしを行います。それぞれの練習は、1 つ前の練習のアプリケーションを元に、作業を行います。それぞれ練習が終わった段階で、アプリケーションを実行し、練習で行った成果を確認できます。

前提知識

この演習のドキュメントでは、次に挙げる知識を既にお持ちであることを前提に解説しています。ただし、C++ の言語文法等に自信のない方でも、この演習付属のサンプルの完成品をコンパイルし、実行することで、DirectWrite が提供する機能を体感することができます。

  • Visual Studio 2008 (Visual C++ 2008) における基本的な操作。たとえば、ソリューションを開く方法、エディタでの編集、ビルド (コンパイル) や実行の方法など。
  • C++ の言語文法や、オブジェクト指向プログラミングに関する基本的な用語。
  • COM の基本的な用語。たとえば、COM インターフェイスなど。

演習のシステム要件

この演習を行うには、あらかじめ以下の環境を用意する必要があります。

  • Windows 7 (日本語 32 ビット版)
  • Visual C++ 2008 SP1 (Visual Studio 2008 SP1) 日本語版、Express Edition も可能
  • Windows SDK for Windows 7 and .NET Framework 3.5 SP 1 (英語版、x 86 用)
  • この演習で使用する付属のソース プログラム (サンプル プログラム)

この後は、上記の各項について、留意点をいつくか補足します。予めご覧ください。
Windows 7 では Visual Studio 2008 を動作させ、Direct2D の機能を確認することが目的なので、特に Windows 7 のエディションは問いません。

Visual C++ 2008 のエディションは問いません。Visual C++ 2008 Express Edition も演習を行うことが可能です。ただし、SP 1 をご利用ください。今回使用する Windows SDK は、SP 1 環境が前提になります。なお、Visual C++ 2008 Express Edition は、Visual Studio 2008 Express Edition SP 1 の一部として、以下の URL のダウンロード センターから無償で入手できます。(2009 年 8 月現在)

Windows SDK for Windows 7 and .NET Framework 3.5 SP 1 (以降は Windows SDK と表記) は、MSDN サブスクリプション サイト、または、上記のサイトから入手できます (英語版なので、上記サイトで、検索する際には「Microsoft U.S. ダウンロードセンターも検索する」オプションを付けて検索してください。なお、使用にあたり、Visual C++ 2008 から Windows SDK を参照可能にする設定が必要です (次頁参照)。

演習に使用する付属のソース プログラムの入手方法については、このドキュメントを入手されたサイト等でご確認ください。ソース プログラムの使用方法は、後述の「演習で使用する付属のソース プログラム (サンプル プログラム) のインストール方法」の項を参照してください。

Visual C++ 2008 から Windows SDK を参照する方法

Note: Visual C++ 2008 (Visual Studio 2008)を使用して、演習付属のプログラムを正常にビルドするには、あらかじめ、Visual C++ と Windows SDK を統合する設定をしなければなりません。次に示す方法で、この設定を行ってください。これを行わないと、Visual C++ 上でビルドを行った際、Windows SDK のヘッダー ファイルの参照に失敗する場合があります。

Visual C++ 2008 (Visual Studio 2008) と Windows SDK をインストールした後、次の操作を行います。

  1. スタートメニューから、[すべてのプログラム]、[Microsoft Windows SDK v7.0]、[Visual Studio Registration] の順にメニューを展開して、[Windows SDK Configuration Tool] をクリックします。
  2. もし、[ユーザー アカウント制御] ダイアログ ボックスが表示されたら、[はい] をクリックして、管理者権限へ昇格します。
  3. Windows SDK Configuration Tool が起動するので、右上部のドロップダウンリストでは、[v7.0] を選択して、下部の [Make Current] ボタンをクリックします。

    Windows SDK Configuration Tool

  4. 設定が完了した旨を示す、以下のメッセージボックスが表示されたら、[OK] をクリックします。すると、このメッセージ ボックスと上記のダイアログ ボックスが閉じ、設定は完了します。

    Windows SDK Configuration Tool

演習で使用する付属のソース プログラム (サンプル プログラム) のインストール方法

演習で使用するソース プログラムには、特別なインストール方法はありません。入手されたソース プログラムのフォルダ「HOLDirectWrite」全体を、任意のパスにコピーしてください。たとえば、C:\ にコピーすれば、演習で作業を行うソース プログラムのパスは、次のようになります。

例1. C:\HOLDirectWrite

なお、演習の本文では、ソース プログラム等の位置を示す際、次のように、ソース プログラムのルート フォルダに対する相対パスで表記しています。

例2. Ex1_Starter\DWrite HOL.sln

この場合、ソース プログラムのフォルダが例 1 の場所であるなら、絶対パスは次の意味になります。

例3. C:\HOLDirectWrite\Ex1_Starter\DWrite HOL.sln

なお、演習作業の中で、ソースプログラムに書き込む場合もあるので、作業を行うユーザー アカウントには、ソース プログラムに対して、書き込み可能なアクセス許可を与えてください。

演習の目的

この演習では、次に挙げる点を取り上げます。

  • DirectWrite API を使用して、テキストの整形と配置を行う
  • Direct2D API を使用して、テキストの描画を行う
  • DirectWrite API のいつくかの異なる方法で描画する
  • Direct2D API を介して、テキストをグラフィックスとして扱う
  • テキストのカスタム レンダリング (custom text renderer) を実装する

Note: この演習の中のいつくかのタスクには、その時点まで行ったソース プログラムもあります。演習の途中から始めたい場合や、作業中のソース プロクラムを壊してしまった場合、それらを使用すると便利です。また、コードの実装にあたっては、コメント アウトしている既存コードのコメントを解除してもよいですし、自身で代替のコードを記述しても構いません。

演習用サンプルでのコーディングの留意点

<C++ スマートポインタの使用>

スマートポインタは、COM オブジェクトを参照するポインタをラップした C++ のテンプレート クラスです。これを使用すると、使用しなくなった COM オブジェクトを自動的に解放することができます。次の例は、SomeInterface という名前のインターフェイスについて、スマート ポインタを宣言した例です。

_COM_SMARTPTR_TYPEDEF(SomeInterface, __uuidof(SomeInterface));
SomeInterfacePtr someInterface;

1 行目は型定義を示しており、型としてスマート ポインタのテンプレート クラスを定義しています。また、2 行目では、そのスマート ポインタの実装を表す変数を宣言しています。

<例外処理>

この演習では、例外を処理するために、以下に示す IFR マクロを使用しています。

#define IFR(expr) do {{hr = (expr); Assert(SUCCEEDED(hr)); if (FAILED(hr)) return(hr);}} while(0)

ページのトップへ


2. 練習 1: 簡単なテキスト描画

この練習では、DirectWrite と Direct2D を使用して、簡単なテキストを描画する方法を確認します。ここで扱うテキストは、単一の書式で整形し、一行で表示するようにします。

スクリーンに簡単なテキストを表示するためには、以下の 4 つの要素が必要です。

  • 描画すべきテキストの文字列
  • テキストのフォント、サイズ、およびスタイルからなる既定の書式情報
  • テキストを配置する領域に関する情報
  • テキスト描画の対象となるオブジェクト、ここでは Direct2D ベースの描画用ターゲットを使用

IDWriteFactory インターフェイス

IDWriteFactory インターフェイスは、DirectWrite で使用するオブジェクトのためのファクトリを実装する基本インターフェイスです。そして、このファクトリを使用して、他の DirectWrite のリソースをインスタンス化します。IDWriteFactory インターフェイスを実装したファクトリ自体をインスタンス化するには、DWriteCreateFactory 関数を使用します。この関数は、次のように定義されています。

HRESULT DWriteCreateFactory(
    __in   DWRITE_FACTORY_TYPE factoryType,
    __in   REFIID iid,
    __out  IUnknown **factory
);

入力引数 factoryType は、ファクトリを共有するか、独立して使用するかを指定します。共有されたファクトリでは、プロセス内の複数のコンポーネントをまたいで、キャッシュされたフォント データを使用することができます。この関数から制御が戻ると、出力引数 factory には、新しいファクトリを参照するポインタが設定されています。

IDWriteTextFormat オブジェクト

IDWriteTextFormat オブジェクトは、テキストの書式を設定する際に、既定のフォント ファミリ名や、Weight、Style および Stretch (WSS プロパティ)、また、フォントサイズやロケール情報などを表します。

Direct2D の描画用ターゲットとブラシ

DirectWrite のコンテンツを描画するとき、Direct2D ベースの描画用ターゲットとブラシを使用することができます。描画用ターゲットは、デバイスに対して、描画コマンドを発行します。ブラシは、塗りつぶしやアウトラインの描画に使用します。Direct2D に関する情報は、別途、Direct2D のハンズオンを参照してください。

ページのトップへ


タスク 1 - 簡単なテキストを描画する

Note: 練習で使用する Visual Studio のプロジェクトには、コメントアウトされたコードが含まれます。このあとの作業では、コードの実装にあたっては、コメント アウトしている既存コードのコメントを解除してもよいですし、自身で代替のコードを記述しても構いません。

この練習では、簡単なテキストを作成します。このテキストは、単一の書式で整形され、1 行のデータとして扱います。

このタスクを終了してアプリケーションを実行すると、次図のようにアプリケーションのウィンドウに表示されます。

アプリケーションのウィンドウ

このあとの作業のために、演習用フォルダ内にある次のソリューション ファイルを開いてください。

  • Ex1_Starter\DWrite HOL.sln

1. DirectWrite のファクトリを作成するため、DWriteCreateFactory 関数を使用します。HandsOnLab.cpp において、DemoApp::CreateDeviceIndependentResources メソッドの中に、この関数の呼び出しコードを次のように追加します。

// Create a shared DirectWrite factory
IFR(DWriteCreateFactory(
    DWRITE_FACTORY_TYPE_SHARED,
    __uuidof(IDWriteFactory),
    reinterpret_cast<IUnknown**>(&m_spDWriteFactory)
    ));

2. 引き続き CreateDeviceIndependentResources メソッドの中に、 DirectWrite を用いて書式設定や配置を行うための Unicode 文字列を、次の要領で準備します。

a. 文字列を確保して、そのアドレスを変数に設定します。また、その文字列の長さを別の変数に設定します。

// The string to display
m_wszHelloWorld = L"Hello World using DirectWrite!";
m_cHelloWorldLength = wcslen(m_wszHelloWorld);

b. CreateTextFormat メソッドを用いて、IDWriteTextFormat オブジェクトを作成します。

// Create a text format using Gabriola with a font size of 72
// we use this to set the default font, weight, stretch, style, and locale
IFR(m_spDWriteFactory->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, // sets the font size
    L"en-us",
    &m_spTextFormat
    ));

c. 垂直方向と水平方向に文字列をセンタリングするため、IDWriteTextFormat オブジェクトが持つ SetTextAlignment メソッドとSetParagraphAlignment メソッドを、次のように使用します。

//center align (horizontally) the text
IFR(m_spTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));    
//center align (vertically) the text
IFR(m_spTextFormat->SetParagraphAlignment(
    DWRITE_PARAGRAPH_ALIGNMENT_CENTER));

3. DemoApp::DrawText メソッドの中で、次の要領で、テキストを描画する Direct2D ベースのコンポーネントを作成します。

a. テキストを配置する領域を定義します。DemoApp::DrawText メソッドの中で、描画領域の情報を求めた後、その情報 (RECT 構造体の変数rc)をもとに、同じ領域を表す Direct2D ベースの四角形領域を作成します。

D2D1_RECT_F layoutRect = D2D1::RectF(
    static_cast<FLOAT>(rc.top),
    static_cast<FLOAT>(rc.left),
    static_cast<FLOAT>(rc.right - rc.left),
    static_cast<FLOAT>(rc.bottom - rc.top)
    );

b. スクリーンにテキストを描画するために、Direct2D の描画用ターゲットの DrawText メソッドを呼び出します。このメソッドの引数には、次の引数を含みます。

  1. 手順 2 の a で用意した、描画すべきテキスト文字列 (m_wszHelloWorld)
  2. 手順 2 の b で作成した文字列の書式 (m_spTextFormat)
  3. 手順 3 の a で用意した描画すべき領域 (layoutRect)
  4. Direct2D ベースの黒色のブラシ (m_spBlackBrush)
m_spRT->DrawText(
    m_wszHelloWorld,
    m_cHelloWorldLength,
    m_spTextFormat,
    layoutRect,
    m_spBlackBrush
    );

4. これで、書式と配置を指定してターゲットに対して描画する DemoApp::DrawText メソッドができました。次に、DemoApp::DrawD2DContent メソッドの中から、この DemoApp::DrawText メソッドを呼び出すようにコードを追加します。

DrawText();

ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。
なお、この練習 1 を行って完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。

  • Ex1_Solution\DWrite HOL.sln

ページのトップへ


3. 練習 2: 複数の書式を持つテキスト描画

この練習では、テキスト文字列の特定の各部分に対して、異なる書式を適用する方法を確認します。複数の異なる書式を適用するには、IDWriteTextLayout インターフェイス、および IDWriteTypography インターフェイスを使用します。

スクリーンに複数の書式を持つテキストを表示するには、次の要素を用意します。

  • 練習 1 で扱った要素
    • a. 文字列
    • b. 文字列の長さ
    • c. 既定の書式
  • テキストを配置する領域
  • テキストの部分に適用する追加の書式
  • テキストを描画するための Direct2D ベースの描画用ターゲット

IDWriteTextLayout オブジェクト

IDWriteTextLayout インターフェイスを実装したオブジェクトは、テキスト ブロックの書式と配置方法を表します。前の練習で取り上げた IDWriteTextFormat オブジェクトで表現される書式情報に加えて、IDWriteTextLayout オブジェクトでは、部分的な領域についてのテキストの配置方法についても表します。テキストの一部分に対して異なる書式を指定でき、オプションとしてタイポグラフィも指定することができます。

IDWriteTextLayout オブジェクトを作成するには、IDWriteFactory::CreateTextLayout メソッドを使用します。このメソッドは、次のように定義されています。

HRESULT CreateTextLayout(
    __in   const WCHAR * string,
           UINT32  stringLength,
           IDWriteTextFormat * textFormat,
           FLOAT  maxWidth,
           FLOAT  maxHeight,
    __out  IDWriteTextLayout ** textLayout
);

引数 string には、書式を適用するテキストを指定します。引数 stringLength には、文字列の文字数を指定します。引数 textFormat には、既定のフォント ファミリ名や、Weight、Style および Stretch の各プロパティ、フォントサイズやロケール情報などの情報を持つ IDWriteTextFormat オブジェクトを指定します。引数 maxWidth と maxHeight には、テキストを配置する領域の大きさを指定します。このメソッドから制御が戻ると、出力引数 textLayout には、新しい IDWriteTextLayout オブジェクトを参照するポインタが設定されます。

IDWriteTypography オブジェクト

DirectWrite には、オプションとしてタイポグラフィもサポートします。これらタイポグラフィの機能をテキストに適用するには、IDWriteTypography オブジェクトを作成し、AddFontFeature メソッドを使用して、DWRITE_FONT_FEATURE_TAG 列挙型に定義された、一つ以上のタイポグラフィを追加します。この列挙には、次の機能が定義されています。

列挙に定義された定数 説明
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. 練習 1 の手順 2 の a で用意したテキスト文字列 (m_wszHelloWorld)
  2. 練習 1 の手順 2 の a で用意したテキスト文字列の長さ (m_cHelloWorldLength)
  3. 練習 1 の手順 2 の b で用意した IDWriteTextFormat オブジェクト (m_spTextFormat)
  4. デバイス非依存のピクセル単位で表した、テキストを配置する領域の幅
  5. デバイス非依存のピクセル単位で表した、テキストを配置する領域の高さ
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. 前の手順で作成した位置を表す構造体
  2. 手順 1 で作成した IDWriteTextLayout
  3. 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
    );

この他にも、コールバック関数として、DrawUnderlineDrawStrikethroughDrawInlineObjectIsPixelSnappingDisabledGetCurrentTransform、また、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) を実装する

ページのトップへ