Skip to main content

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

更新日: 2009 年 8 月 28 日


ハンズオンラボ

ダウンロード

目次

  1. 演習: グラフィックス ~ DIRECT2D ~
  2. 練習 1: DIRECT2D インフラストラクチャ

    タスク 1 – Direct2D を使用して描画する

  3. 練習 2: 幾何学図形とグラデーションの描画

    タスク 1 – 砂時計の図形を描画する
    タスク 2 – 図形を変換し、グラデーション用のブラシを使用する
    タスク 3 – 図形を合成する

  4. 練習 3: GDI への DIRECT2D コンテンツの描画

    タスク 1 – GDI の描画領域に Direct2D の円グラフを追加する

  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. 演習: グラフィックス ~ DIRECT2D ~

Direct2D は、二次元グラフィックスを作成するためのネイティブ API です。Direct3D 10 の上に構築されており、次世代のグラフィックス ハードウェアの能力を活用する、解像度非依存の二次元グラフィックス API を、Win32 アプリケーション開発向けに提供しています。

アプリケーション全体を書き換えることなく、Direct2D のグラフィックス機能を用いて、既存の GDI アプリケーションを強化することができます。Direct2D は、GDI や他のグラフィックス テクノロジーと相互運用できるようにデザインされています。Direct2D と GDI との相互運用機能によって、既存のアプリケーションは、最先端のグラフィックスを利用することができます。Direct2D API を使用すると、GDI のサーフェイスに、 Direct2D ベースのコンテンツを書き込むことができます。

この演習では、単純なウィンドウに描画を行う Visual Studio 2008 プロジェクトが用意されています。最初の二つの練習では、Direct2D のいくつかの機能を説明し、基本的な描画方法を示しています。最後の 3 つ目の練習では、Direct2D と GDI の両方のコンテンツを描画するように、アプリケーションを修正します。

前提知識

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

  • 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 SP1 (英語版、x 86 用)
  • この演習で使用する付属のソース プログラム (サンプル プログラム)

この後は、上記の各項について、留意点をいつくか補足します。予めご覧ください。

Windows 7 では Visual Studio 2008 を動作させ、Direct2D の機能を確認することが目的なので、特に Windows 7 のエディションは問いません。

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

Windows SDK for Windows 7 and .NET Framework 3.5 SP1 (以降は 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

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

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

例 1. C:\HOLDirect2D

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

例 2. Ex1_Starter\Direct2D HOL.sln

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

例 3. C:\HOLDirect2D\Ex1_Starter\Direct2D HOL.sln

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

演習の目的

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

  • 描画用のターゲットを作成し、これを使用して、スクリーンへ描画する
  • 幾何学図形のオブジェクトを使用して、図形を描画し、これにグラデーションを付ける
  • GDI サーフェイスに Direct2D のコンテンツを描画する方法を用いて、GDI ベースのアプリケーションのグラフィックス描画を強化する

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: DIRECT2D インフラストラクチャ

この練習では、Direct2D を使用した、描画用ターゲットの作成方法と基本的な図形の描画方法を確認します。

Direct2D の各インターフェイス

ここでは、Direct2D の演習を行うにあたり、まずは、ID2D1FactoryID2D1HwndRenderTarget、および ID2D1Brush の各インターフェイスを使用することから始めます。

ID2D1Factory オブジェクト

ID2D1Factory インターフェイスは、Direct2D API 使用する上での、作業の開始地点としての役割を提供します。このインターフェイスが実装されたオブジェクト (ファクトリと呼ぶ) は、他の Direct2D 対応の描画リソースをインスタンス化するために使用する、デバイス非依存のリソースです。ID2D1Factory インターフェイスが実装された、このファクトリを作成するには、D2D1CreateFactory 関数を使用します。

D2D1CreateFactory 関数は、以下のコードに示すように定義されています。

HRESULT
D2D1CreateFactory(
    __in D2D1_FACTORY_TYPE factoryType,
    __out Factory **factory
    )
{
    return
        D2D1CreateFactory(
            factoryType,
            __uuidof(Factory), 
            reinterpret_cast<void **>(factory));
}

入力引数 factoryType には、シングル スレッドか、それともマルチ スレッドであるかを指定します。この関数から制御が戻るとき、出力引数 factory には、新しいファクトリを参照するポインタが設定されます。

Direct2D のコンテンツを描画するには、描画用ターゲットに向けて、描画コマンドを発行します。描画用ターゲットは、描画操作の指示を受取り、その結果を出力します。描画用ターゲットの種類が異なれば、受け取った描画コマンドに対して、異なる振舞いをします。たとえば、あるターゲットは、描画コマンドに応じてビットマップへ出力し、また別のターゲットでは、スクリーンに直接出力する場合もあります。

Direct2D を使用するメリットの一つは、高速な描画のために、ハードウェアを活用することができる点です。ハードウェア描画のためのターゲットを作成するには、ファクトリの CreateHwndRenderTarget メソッドを使用します。この CreateHwndRenderTarget 関数は、以下のように定義されています。

HRESULT CreateHwndRenderTarget(
        CONST D2D1_RENDER_TARGET_PROPERTIES &renderTargetProperties,
        CONST D2D1_HWND_RENDER_TARGET_PROPERTIES &hwndRenderTargetProperties,
        __deref_out ID2D1HwndRenderTarget **hwndRenderTarget 
    ) 
    {
        return CreateHwndRenderTarget(&renderTargetProperties,  
        &hwndRenderTargetProperties, hwndRenderTarget);
    }

引数 renderTargetProperties には、すべての描画用ターゲットに適用される、描画関連のプロパティを指定します。たとえば、関連するフラグや、ピクセル フォーマット、DPI などです。その次の引数 hwndRenderTargetProperties には、ハードウェア描画を行うターゲットに対して、オプションを指定します。たとえば、ターゲットとなる HWND オブジェクトの指定や、描画領域のピクセル単位のサイズなどです。

CreateHwndRenderTarget メソッドから制御が戻ると、出力引数 hwndRenderTarget には、ハードウェア描画の新しいターゲットを参照するポインタが設定されます。

ID2D1SolidColorBrush オブジェクト

Direct2D では、ウィンドウにコンテンツを描画する際に、ちょうど GDI と同じようにブラシを使用します。Direct2D には、様々なブラシがあります。たとえば、塗りつぶしのブラシや、一方向に変化するグラデーションを付けるブラシ、また、弧を描くようにグラデーションを付けるブラシ、ビットマップを使用するブラシなどです。ブラシを作成するには、描画したいターゲットの CreateSolidColorBrush メソッドを呼び出します。ブラシは、デバイス非依存のリソースです。特定の 1 つのブラシが使用可能なターゲットは、そのブラシを作成したターゲット、及び、同じ描画対象を扱うターゲットです。

BeginDraw メソッドと EndDraw メソッド (ID2D1RenderTarget オブジェクト)

描画コマンドを発行する前に、まず描画用ターゲットが Direct2D によって使用されることを示すために、BeginDraw メソッドを呼び出します。描画コマンドの発行が終了した後は、描画用ターゲットをもう使用しないことを示すため、EndDraw メソッドを呼び出します。描画コマンドはすぐには処理されず、内部バッファがいっぱいになるか、Flush メソッドを呼び出すか、または、EndDraw メソッドを呼び出すまでは処理されません。

ページのトップへ


タスク 1 – Direct2D を使用して描画する

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

この練習では、オレンジ色の四角形の枠に囲まれた中に、緑色で塗りつぶした四角形を描画します。そのためには、予め用意されたサンプル アプリケーションに含まれる DemoApp クラスを修正します。DemoApp クラスには、初期化に関する処理と描画コマンドを発行する処理が含まれます。DemoApp クラスは、プロジェクト ディレクトリ内の HandsOnLab.h と HandsOnLab.cpp ファイルに記述されています。

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

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

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

  • Ex1_Starter\Direct2D HOL.sln

1. 次の手順に従って、DemoApp::CreateDeviceIndependentResources メソッドの中で、ID2D1Factory オブジェクトを作成します。ファクトリはデバイス非依存なので、「CreateDeviceIndependentResources」という名前のメソッドの中で行うことにします。

a. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、ID2D1FactoryPtr オブジェクトを宣言します。

ID2D1FactoryPtr m_spD2DFactory;

b. HandsOnLab.cpp において、DemoApp::CreateDeviceIndependentResources メソッドの中から、ファクトリを作成するための D2D1CreateFactory メソッドを呼び出します。

IFR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &m_spD2DFactory));

2. 次の手順に従って、DemoApp::CreateDeviceResources メソッドの中で、ID2D1HwndRenderTarget オブジェクトを作成します。描画用ターゲットはデバイス依存なので、「CreateDeviceResources」という名前のメソッドの中で行うことにします。

a. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、ID2D1HwndRenderTargetPtr オブジェクトを宣言します。

ID2D1HwndRenderTargetPtr m_spRT;

b. HandsOnLab.cpp において、DemoApp::CreateDeviceResources メソッドの中から、描画用ターゲットを作成するための CreateHwndRenderTarget メソッドを呼び出します。描画用ターゲット (ID2D1HwndRenderTarget) を作成するとき、サイズを指定できます。ここでは、ウィンドウのクライアント領域のサイズを、描画用ターゲットのサイズとして指定することにします。

RECT rc;
GetClientRect(
    m_hwnd,
    &rc
    );
D2D1_SIZE_U size = D2D1::SizeU(
    rc.right - rc.left,
    rc.bottom - rc.top
    );

// Create a D2D render target
IFR(m_spD2DFactory->CreateHwndRenderTarget(
    D2D1::RenderTargetProperties(),
    D2D1::HwndRenderTargetProperties(
        m_hwnd,
        size
        ),
    &m_spRT
    ));

前述のコードで使用されている GetClientRect 関数では、ウィンドウのクライアント領域の四角形を構成する座標を、トップ (top)、左 (left)、右 (right)、底 (bottom) の 4 つの値として取得することができます。
ここまでのところで、ファクトリと描画用ターゲットが作成できました。これで、描画用のリソース (ブラシなど) を作成することができます。

3. 2 つの ID2D1SolidColorBrush オブジェクト (緑色のブラシとオレンジ色のブラシ) を作成するため、CreateSolidColorBrush メソッドを使用します。次の要領でコードを追加します。

a. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、2 つの ID2D1SolidColorBrushPtr オブジェクトを宣言します。

ID2D1SolidColorBrushPtr m_spGreenBrush;
ID2D1SolidColorBrushPtr m_spOrangeBrush;

b. HandsOnLab.cpp において、CreateDeviceResources メソッドの中に、緑色のブラシとオレンジ色のブラシを作成する次のコードを追加します。

// Create a green brush 
IFR(m_spRT->CreateSolidColorBrush(
    D2D1::ColorF(D2D1::ColorF::Green),
    &m_spGreenBrush));
        
// Create an orange brush 
IFR(m_spRT->CreateSolidColorBrush(
   D2D1::ColorF(D2D1::ColorF::Orange),
   &m_spOrangeBrush));

4. ここまでに必要となる、すべてのデバイス非依存のリソースとデバイス依存のリソースを作成するコードの記述が済んだので、アプリケーションの起動時における初期化を行うコードを追加します。HandsOnLab.cpp において、DemoApp::Initialize メソッドの中から、CreateDeviceIndependentResources メソッドを呼び出すコードを追加します。

IFR(CreateDeviceIndependentResources());

5. スクリーンに対して、緑色の四角形とオレンジ色の四角形を描画するため、描画コマンドを発行します。次の要領でコードを記述します。

a. HandsOnLab.cpp において、DemoApp::OnRender メソッドの中から DemoApp::DrawD2DContent メソッドを呼び出すコードを追加します。この DrawD2DContent メソッドの中では、このあとの手順で、Direct2D の描画操作を行うことになります。

IFR(DrawD2DContent(ps));

b. DrawD2DContent メソッドの中で、CreateDeviceResources メソッドを呼び出すコードを追加します。

IFR(CreateDeviceResources());

c. DrawD2DContent メソッドの中で、BeginDraw メソッドと EndDraw メソッドの呼び出しを追加し、両者の間で DemoApp::DrawD2DRectangle メソッドを呼び出すコードを追加します。DrawD2DRectangle メソッドには、このあとの手順で、四角形を描画するコマンドを追加します。また、拡大や縮小などの特別な座標変換は行ないません (倍率は 1 倍)。よって、引数に行列データを渡して変換方法を指定する SetTransform メソッドには、予め用意された単位行列 (D2D1::Matrix3x2F::Identity())を引数として指定します。この指定を描画する前に行います。さらに、Clear メソッドを呼び出して、背景を白色にします。

// Begin the rendering
m_spRT->BeginDraw();

// Set the world transform to identity
m_spRT->SetTransform(
    D2D1::Matrix3x2F::Identity()
    );

// Clear the background
m_spRT->Clear(
    D2D1::ColorF(D2D1::ColorF::White)
    );

// Call DrawD2DRectangle() 
// Exercise 1
DrawD2DRectangle();

// Finish rendering
hr = m_spRT->EndDraw();

d. DrawD2DRectangle メソッドでは、2 つの四角形を描画します。四角形の縦と横を定義するためには、D2D1_RECT_F 構造体を使用します。この構造体を作成するには、Direct2D のヘルパ関数である D2D1::RectF を利用できます。また、GetClientRect 関数を呼び出して、ターゲット ウィンドウの位置や大きさを求め、それぞれの四角形をウィンドウの中央に表示するようにします。次に示すように、2 つの構造体を用意します。

RECT rc;

GetClientRect(
    m_hwnd,
    &rc
    );

D2D1_SIZE_U size = D2D1::SizeU(
    rc.right - rc.left,
    rc.bottom - rc.top
    );

D2D1_RECT_F rectangle1 = D2D1::RectF(
    size.width/2 - 50.0f,
    size.height/2 - 50.0f,
    size.width/2 + 50.0f,
    size.height/2 + 50.0f
    );

D2D1_RECT_F rectangle2 = D2D1::RectF(
    size.width/2 - 100.0f,
    size.height/2 - 100.0f,
    size.width/2 + 100.0f,
    size.height/2 + 100.0f
    );

e. ハードウェア描画を行うターゲットの FillRectangle メソッドを呼び出し、引数には既に用意した四角形を表す構造体と緑色のブラシを渡して、緑色の四角形を描画します。

// Draw a filled rectangle
m_spRT->FillRectangle(
    &rectangle1, 
    m_spGreenBrush
    );

f. ハードウェア描画を行うターゲットの FillRectangle メソッドを呼び出し、引数には既に用意した四角形を表す構造体とオレンジ色ブラシを渡して、オレンジ色の四角形を描画します。

// Draw an outline rectangle
m_spRT->DrawRectangle(
    &rectangle2, 
    m_spOrangeBrush
    );
// Draw an outline rectangle
m_spRT->DrawRectangle(
    &rectangle2, 
    m_spOrangeBrush
    );

ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。
この練習では、Direct2D を使用して、二つの四角形を描画しました。ここでは、Direct2D のすべての操作の開始地点になるファクトリ オブジェクトの作成方法や、ハードウェア ベースの描画を行うための描画用ターゲットを作成する方法、また、スクリーンへコンテンツを描画するために、描画コマンドの使用方法について学びました。

なお、この練習 1 を行った完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。

  • Ex1_Solution\Direct2D HOL.sln

ページのトップへ


3. 練習 2: 幾何学図形とグラデーションの描画

この練習では、Direct2D を使用して任意の幾何学図形を描画する方法を取り上げます。そのほか、図形を変換する方法や、複数の図形を合成する方法、また、グラデーション用のブラシを用いて塗りつぶす方法について取り上げます。この練習は、次の 3 つのタスクから構成されています。

  • 図形を描画する
  • 図形を変換し、一方向のグラデーションを表すブラシを使用する
  • 2 つの図形を合成する

Direct2D の幾何学図形のための各オブジェクト

この練習では、ID1D1PathGeometryID2D1GeometrySinkID2D1TransformedGeometry、および ID2D1LinearGradientBrush のインターフェイスを実装した各オブジェクトを使用します。

ID2D1PathGeometry オブジェクト

ID2D1PathGeometry オブジェクトを使用すると、幾何学図形の輪郭を形成するための、軌跡 (path) を表現することができます。このID2D1PathGeometry オブジェクト (以降は path オブジェクトとも表記) の軌跡を表すために、具体的には ID2D1GeometrySink オブジェクト (以降は sink オブジェクトとも表記) を使用します。この sink オブジェクトを使用して描画することで、図形が形成されます。

このような図形の軌跡は、複雑な図形を表現する上で役立ちます。軌跡を表す D2D1PathGeometry オブジェクトを作成するには、ID2D1Factory::CreatePathGeometry メソッドを使用します。このオブジェクトは、デバイス非依存のリソースであるので、描画用ターゲットのメソッドではなく、ファクトリのメソッドを使用します。CreatePathGeometry メソッドから制御が戻ると、出力引数を介して、空のID2D1PathGeometry オブジェクトへの参照を取得できます。

HRESULT CreatePathGeometry(
    __deref__out ID2D1PathGeometry **pathGeometry
) PURE;

ID2D1GeometrySink オブジェクト

ID2D1GeometrySink オブジェクト (sink オブジェクト) を使用して、図形の軌跡 (path) に対して、一つ以上の図の情報 (figure) を追加します。この図の情報 (figure) は、複数のセグメントから構成されており、閉じた図の場合もあれば、閉じてない図の場合もあります。セグメントとして、直線のセグメントやベジエ曲線のセグメント、弧のセグメントなどを追加できます。sink オブジェクトを取得するには、図を表現したい対象となる ID2D1PathGeometry オブジェクトの Open メソッドを呼び出します。sink オブジェクトを使用して、図 (figure) を作成した後、sink オブジェクトの Close メソッドを呼び出して、このオブジェクトを閉じます。すると、ID2D1PathGeometry オブジェクトには必要な図の情報が蓄えられ、図の描画操作で利用できるようになります。

BeginFigure メソッドと EndFigure メソッド (ID2DGeometrySink オブジェクト)

図の軌跡 (path) は、複数の図 (figure) を含むことができます。1 つの図 (figure) は、複数のセグメントを含むことができます。1 つのセグメントは、直線やベジエ曲線、または弧です。図 (figure) の作成を開始するには、sink オブジェクトの BeginFigure メソッドを呼び出します。図 (figure) の作成を終了するには、EndFigure メソッドを呼び出します。

ID2D1TransformedGeometry オブジェクト

特定の幾何学図形に、変換処理を適用したい場合もあるでしょう。その際、描画領域全体の変換ではなく、部分的に行いたい場合もあるでしょう。描画領域全体の変換では、図形の輪郭を表す線や、図形内の塗りつぶす内容に影響を与えます。一方、ID2D1TransformedGeometry オブジェクトを使用すると、線や塗りつぶし面が描画される前に、図の軌跡(座標情報)に対して、変換を適用できます。

ID2D1TransformedGeometry オブジェクトを作成するには、ファクトリが持つ ID2D1Factory::CreateTransformedGeometry メソッドを呼び出します。このメソッドは、次のように定義されています。

HRESULT
    CreateTransformedGeometry(
        __in ID2D1Geometry *sourceGeometry,
        CONST D2D1_MATRIX_3X2_F &transform,
        __deref_out ID2D1TransformedGeometry **transformedGeometry 
        ) 
    {
        return CreateTransformedGeometry(
            sourceGeometry, &transform, transformedGeometry);
    }

このメソッドの引数には、1 つ目の入力引数として、path を表す ID2D1PathGoemetry オブジェクトなど、変換の元になる図形情報を渡し (引数の型にある ID2D1Geometry ID2D1PathGeometry の継承元のインターフェイス) 、2 つ目の入力引数として、変換のための行列情報を渡します。このメソッドから実行制御が戻ると、出力引数には、変換後の図である ID2D1TransformedGeometry オブジェクトへの参照を取得することができます。

ID2D1LinearGradientBrush オブジェクト

このオブジェクトを使用すると、任意の方向に、任意の数の色を指定してグラデーションを表現した、塗りつぶしを行うことができます。グラデーションの開始地点と終了地点を指定して、線の方向を定義し、その線の途中の各地点に、特定の色をマッピングします。この色を指定する各地点は「gradient stop」と呼ばれます。この地点 (gradient stop) の指定には、専用の位置表現を使用しており、0 から 1 までの範囲で表し、開始地点から終了地点までの直線における、距離の比率を表しています。0 から 1 の範囲外の値の場合、ブラシの描画モードによっては、外側の領域の描画に使用されます。グラデーションの変化は、この線に沿って行われ、指定に応じてブラシの変換がなされます。上図のグラデーションのサンプルでは、開始地点は四角形の左上隅であり、終了地点は右下隅です。各地点 (gradient stop) の色は、順に黄色、赤、青、そして緑を指定しています。

任意の数の色を指定してグラデーションを表現

グラデーションのためのブラシを作成するには、複数の gradient stop の定義から始めます。1 つの gradient stop の定義には、D2D1_GRADIENT_STOP 構造体を用います。この構造体は、次のように定義されています。

typedef struct D2D1_GRADIENT_STOP
{
    FLOAT position;
    D2D1_COLOR_F color;

} D2D1_GRADIENT_STOP;

複数の gradient stop を用意した後、ID2D1RenderTarget::CreateGradientStopCollection メソッドを呼び出して、gradient stop のコレクションを作成します。このメソッドは、次のように定義されています。

HRESULT
    CreateGradientStopCollection(
        __in_ecount(gradientStopsCount)
        CONST D2D1_GRADIENT_STOP *gradientStops,
        UINT gradientStopsCount,
        __deref_out ID2D1GradientStopCollection **gradientStopCollection 
        ) 
    {
        return CreateGradientStopCollection(
            gradientStops, gradientStopsCount, D2D1_GAMMA_2_2,
            D2D1_EXTEND_MODE_CLAMP, gradientStopCollection);
    }

引数 gradientStops には、D2D1_GRADIENT_STOP 構造体の配列 (を参照するポインタ) を渡します。引数 gradientStopsCount には、その配列の要素数 (gradient stop の数) を指定します。このメソッドの呼び出しから制御が戻ると、出力引数 gradientStopCollection には、新規に作成されたコレクションである ID2D1GradientStopCollection オブジェクトへの参照が設定されます。

一方向のグラデーション用のブラシを作成するには、ID2D1RenderTarget::CreateLinearGradientBrush メソッドを呼び出します。

HRESULT
    CreateLinearGradientBrush(
        CONST D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES 
        &linearGradientBrushProperties,
        CONST D2D1_BRUSH_PROPERTIES &brushProperties,
        __in ID2D1GradientStopCollection *gradientStopCollection,
        __deref_out ID2D1LinearGradientBrush **linearGradientBrush 
        ) 
    {
        return CreateLinearGradientBrush(
            &linearGradientBrushProperties, 
            &brushProperties, gradientStopCollection, linearGradientBrush);
    }

引数 linearGradientBrushProperties には、グラデーションの開始地点と終了地点を指定します。引数 brushProperties には、ブラシの透明度や変換処理に関する指定などを行います。3 つ目の引数である gradientStopCollection には、ブラシで描画する際の gradient stop を表す、先述の gradient stop のコレクションへのポインタを渡します。このメソッドから制御が戻ると、引数 linearGradientBrush には、新しいID2D1LinearGradientBrush オブジェクトへの参照が設定されます。

ページのトップへ


タスク 1 – 砂時計の図形を描画する

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

このタスクでは、砂時計の形をした図形を含む ID2DPathGeometry オブジェクト (path オブジェクト) を使用して描画を行います。

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

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

このあとの作業のために、演習用フォルダ内にある次のソリューション ファイルを開いてください。(このソリューション ファイルは、練習 1 の作業を終了した時点のものと同じです。)

  • Ex2_Starter\Direct2D HOL.sln

1. 砂時計を表す ID2D1PathGeomtry オブジェクト (path オブジェクト) を作成します。

a. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、ID2D1PathGeometryPtr オブジェクトを宣言します。

ID2D1PathGeometryPtr m_spPathGeometry;

b. HandsOnLab.cpp において、DemoApp::CreateDeviceIndependentResources メソッドの中で、ID2D1PathGeometry オブジェクトを作成します。この作成は、デバイス非依存である CreateDeviceIndependentResources メソッドの中で行います。というのは、図形の情報は、特定のハードウェアに依存するものではなく、同じファクトリから作成された描画用ターゲットであれば、いずれのターゲットでも利用できるからです。

// Create the path geometry
IFR(m_spDirect2DFactory->CreatePathGeometry(
    &m_spPathGeometry
));

2. (この手順は、既に完了しています。) 同じ CreateDeviceIndependentResources メソッドの中で、path オブジェクトに対して情報を設定するために使用する sink オブジェクトを生成する宣言をします。(メソッドの中の先頭に宣言されています。)

ID2D1GeometrySinkPtr spSink;

3. path オブジェクトの Open メソッドを呼び出し、引数には sink オブジェクトを渡します。また、sink オブジェクトの fill モードを指定します。(fill モードは、特に path (軌跡) が複雑に交差するときに、どの部分が図形の内側で、どの部分が図形の外側であるかを決定するルールを指定するためのものです。これによって、どの部分が塗りつぶされるかが決まります。詳細は、ライブラリレファレンスの D2D1_FILL_MODE 構造体を参照してください。この例では、軌跡が砂時計の形状に囲むような単純な図なので、モードの設定は特に影響しません。)

IFR(m_spPathGeometry->Open(
    &spSink)
    );

spSink->SetFillMode(
    D2D1_FILL_MODE_ALTERNATE
    );

4. 上記に続けて BeginFigure メソッドを呼び出し、図 (figure) の作成を開始します。さらに、2 つの直線と 2 つのベジエ曲線を追加し、最後に EndFigure メソッドを呼び出します。

// Open the sink to add a figure (we are making an hour glass)
spSink->BeginFigure(
    D2D1::Point2F(0, 0),
    D2D1_FIGURE_BEGIN_FILLED
    );

// The first segment is a line
spSink->AddLine(
    D2D1::Point2F(200, 0)
    );

// From the end point of the line, add a bezier and continue in this fashion
spSink->AddBezier(
    D2D1::BezierSegment(
        D2D1::Point2F(150, 50),
        D2D1::Point2F(150, 150),
        D2D1::Point2F(200, 200)
    ));

spSink->AddLine(
    D2D1::Point2F(0, 200)
);

spSink->AddBezier(
    D2D1::BezierSegment(
        D2D1::Point2F(50, 150),
        D2D1::Point2F(50, 50),
        D2D1::Point2F(0, 0)
    ));

// The hour glass is done!! Now close the figure
spSink->EndFigure(
    D2D1_FIGURE_END_CLOSED
    );

5. 上記に続けて、sink オブジェクトを閉じるコードを追加します。

IFR(spSink->Close());

6. path オブジェクトを使用して描画するコードを、次の要領で追加します。

a. アプリケーションにおける描画を担当する OnRender メソッドの中で、DrawD2DContent メソッドを呼び出すコードを追加します。練習1を完了していれば、既にこのコードは記述されています。

IFR(DrawD2DContent(ps));

b. 前述の DrawD2DContent メソッドの中で、BeginDraw メソッド呼び出しと EndDraw メソッド呼び出しの間において、以下のように DrawD2DGeometry メソッドを呼び出すように修正します。すなわち、練習 1 で使用した DrawD2DRectangle メソッド呼び出しはコメントアウトし、代わりに DrawD2DGeometry メソッドを呼び出すようにします。DrawD2DGeometry メソッドは、幾何学図形の描画すべてを担当しており、次の手順で取り上げます。

// Exercise 1
// DrawD2DRectangle();

// Exercise 2
DrawD2DGeometry();
7. DrawD2DGeometry メソッドの中では、ID2D1RenderTarget::DrawGeometry メソッドを呼び出します。このメソッドの引数には、path オブジェクトと、前の練習で作成した緑色のブラシを渡します。
// Draw the hour glass geometry at the top left corner of the client area 
    m_spRT->DrawGeometry(
    m_spPathGeometry,
    m_spGreenBrush
    );

ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。輪郭が緑色の砂時計が表示されます。

なお、このタスク 1 まで行った完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。

  • Ex2_Solution_Task1\Direct2D HOL.sln

ページのトップへ


タスク 2 – 図形を変換し、グラデーション用のブラシを使用する

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

このタスクでは、砂時計を中央に移動し、一方向にグラデーションを付けるブラシを使用して、図形の中を塗りつぶします。

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

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

このあとの作業のために、演習用フォルダ内にある次のソリューション ファイルを開いてください。(このソリューション ファイルは、タスク 1 の作業を終了した時点のものと同じです。

  • Ex2_Starter_Task2\Direct2D HOL.sln

1. HandsOnLab.cpp において、DrawD2DGeometry メソッドの中で、タスク 1 の砂時計を表示するコードに続き、変換を行う次のコードを追加します。D2D1::Matrix3x2F::Translation ヘルパ関数を使用して、変換のための行列データを作成します。

// Perform a translation about both X and Y axis
m_spRT->SetTransform(
    D2D1::Matrix3x2F::Translation(
        size.width/2-100.0f,
        size.height/2-100.0f
        )
    );

2. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、一方向のグラデーションを付けるブラシを宣言します。

ID2D1LinearGradientBrushPtr m_spLGBrush;

3. 次の要領で、gradient stop のコレクションを定義します。

a. (この手順は、既に完了しています。) HandsOnLab.cpp にある CreateDeviceResources メソッドの中に、gradient stop のコレクションを表す変数を宣言します。(メソッド内の先頭付近にあります。)

ID2D1GradientStopCollectionPtr spGradientStops;

b. 同様に、CreateDeviceResources メソッドの中で、次のように gradient stop の配列を作成します。

// Create a linear gradient brush
static const D2D1_GRADIENT_STOP stops[] =
{
     {   0.f,  { 0.f, 1.f, 1.f, 1.f }  },
     {   1.f,  { 0.f, 0.f, 1.f, 1.f }  },
};

c. 前述の配列を元に、gradient stop のコレクションを作成します。

IFR(m_spRT->CreateGradientStopCollection(
     stops,
     ARRAY_SIZE(stops),
     &spGradientStops
     ));

4. 前述のコードに続けて、グラデーション用のブラシを作成するコードを追加します。

IFR(m_spRT->CreateLinearGradientBrush(
    D2D1::LinearGradientBrushProperties(
        D2D1::Point2F(100, 0),
       D2D1::Point2F(100, 200)),
    D2D1::BrushProperties(),
    spGradientStops,
    &m_spLGBrush
    ));

5. DrawD2DGeometry メソッドの中で、手順 1 の変換を行うコードの後に、ID2D1RenderTarget::FillGeometry メソッドを使用して、塗りつぶしを伴う図形を描画します。引数には、砂時計の形の path オブジェクトと、先の手順で作成したグラデーション用のブラシを渡します。

m_spRT->FillGeometry(
    m_spPathGeometry,
    m_spLGBrush
    );

ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。ウィンドウの中央に、グラデーションを伴う砂時計が表示されます。

なお、このタスク 2 まで行った完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。

  • Ex2_Solution_Task2\Direct2D HOL.sln

ページのトップへ


タスク 3 – 図形を合成する

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

このタスクでは、タスク 2 で作成した砂時計を45° だけ傾けて、元の砂時計と合成します。

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

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

このあとの作業のために、演習用フォルダ内にある次のソリューション ファイルを開いてください。(このソリューション ファイルは、タスク 2 の作業を終了した時点のものと同じです。

  • Ex2_Starter_Task3\Direct2D HOL.sln

1. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、変換された図形を表す変数を宣言します。合成には、タスク 2 で作成した図形のほか、変換処理として同じ図形を45° 傾けた図形を使用するので、変換処理後の図形を変数で表すようにすると便利です。

ID2D1TransformedGeometryPtr m_spTransformedGeometry;

2. HandsOnLab.cpp において、CreateDeviceIndependentResources メソッドの中で、45 ° 傾けた変換後の図形を作成します。

// Create a transformed geometry which is tilted at an angle to ...
IFR(m_spD2DFactory->CreateTransformedGeometry(
    m_spPathGeometry, 
    D2D1::Matrix3x2F::Rotation(
        45.0f, 
        D2D1::Point2F(100.0f, 100.0f)
        ),
    &m_spTransformedGeometry
    ));

3. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、合成結果の図形を表す変数を宣言します。

ID2D1PathGeometryPtr m_spCombinedPathGeometry;

4. HandsOnLab.cpp において、CreateDeviceIndependentResources メソッドの中で、前述の変数に対して、合成結果の図形のための path オブジェクトを作成します。

// Create another path geometry that will contain the result of geometry ...
IFR(m_spD2DFactory->CreatePathGeometry(
    &m_spCombinedPathGeometry
    ));

5. 前述のコードに続けて、この path オブジェクトに対する sink オブジェクトを作成し、その sink オブジェクトの fill モードを設定します。

// Write to the path geometry using the geometry sink. We are going to ...
// hour glass with an hour glass rotated at an angle of 45 degrees.
IFR(m_spCombinedPathGeometry->Open(
    &spSink
    ));

spSink->SetFillMode(
    D2D1_FILL_MODE_ALTERNATE
    );

6. 元の砂時計と 45 ° 傾けた砂時計との図形に対して合成を行います。

m_spPathGeometry->CombineWithGeometry(
    m_spTransformedGeometry,
    D2D1_COMBINE_MODE_UNION,
    D2D1::Matrix3x2F::Identity(),
    0.25f,
    spSink
    );

7. 合成が済んだので、sink オブジェクトを閉じます。

IFR(spSink->Close());

8. 最後に、合成した図形を描画します。DrawD2DGeometry メソッドの中で、描画用ターゲットの ID2D1RenderTarget::DrawGeometry メソッドを呼び出します。引数には、合成した図形 (path オブジェクト) と、既に練習 1 で作成したオレンジ色のブラシを渡します。また、タスク 1 で行った輪郭だけの砂時計の描画と、タスク 2 で行ったグラデーション付きの砂時計の描画を行わなくするため、それらの部分をコメントアウトします。

// Draw the hour glass geometry at the top left corner of the client area
// m_spRT->DrawGeometry(
//     m_spPathGeometry,
//     m_spGreenBrush
//     );

//perform a translation about both X and Y axis
m_spRT->SetTransform(
    D2D1::Matrix3x2F::Translation(
        size.width/2-100.0f,
        size.height/2-100.0f
    )
);

//m_spRT->FillGeometry(
//    m_spPathGeometry,
//    m_spLGBrush
//    );

// Draw the combined geometry
m_spRT->DrawGeometry(
    m_spCombinedPathGeometry,
    m_spOrangeBrush
    );

ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。2 つの砂時計が合成された輪郭が表示されます。

なお、このタスク 3 まで行った完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。

  • Ex2_Solution_Task3\Direct2D HOL.sln

この練習 2 では、Direct2D での幾何学図形の描画方法や、一方向のグラデーションを付けるブラシの使い方、そして図形の合成方法について取り上げました。

参考資料

このような合成ができるようになると、もう少し凝ったものも作りたくなります。図形に対するヒットテストを組み込む課題は大きな領域であり、Direct2D の図形に関する関数のいつくかは、この実装を容易にするものもあります。たとえば、単純なヒットテストが可能な領域と、視覚的に表示された図形の領域との合成には、次のようなシナリオが考えられます。

ヒットテストが可能な領域と、対象となるビジュアルオブジェクトの交差

ヒットテストが可能な領域と、対象となるビジュアルオブジェクトの交差

上記のシナリオに関する一般的な情報が、以下のアドレスにあります。

これらを実装するにあたり Direct2D では、ID2D1Geometry:CompareWithGeometry メソッドがヒントになります。

ページのトップへ


4. 練習 3: GDI への DIRECT2D コンテンツの描画

ここまでのところで、Direct2D を使用した描画ができるようになりました。さらに、GDI サーフェイスに Direct2D のコンテンツを描画することによって、同じアプリケーションの中で、Direct2D と GDI を併用することもできます。この練習では、GDI を使用して描画する Visual Studio のプロジェクトを変更して、GDI ベースの描画領域に Direct2D のコンテンツを描画するようにします。

GDI サーフェイスに描画するには、ID2D1RenderTarget インターフェイス (描画用ターゲット) から継承した ID2D1DCRenderTarget インターフェイスを使用します。以下の記述は、この描画用ターゲットと、そのインターフェイスが持つ BindDC メソッドの解説です。

ID2D1DCRenderTarget オブジェクト

この演習の前半では、スクリーンに対してハードウェア描画を行うターゲットを使用しました。一方、ID2D1DCRenderTarget インターフェイスを実装したオブジェクトは、GDI で使用する DC (Device Context) に対して描画行うターゲットです。つまり、GDI の描画用サーフェイスにあたる HDC (ハンドル) に対して描画するための、Direct2D の描画用ターゲット (ID2DRenderTarget) の一種です。

BindDC メソッド (ID2D1DCRenderTarget インターフェイス)

BindDC メソッドは、描画用ターゲットを HDC に関連付ける (バインドする) メソッドです。BindDC メソッドは、次のように定義されています。

HRESULT BindDC(
    __in const HDC hDC,
    __in const RECT *pSubRect
) PURE;

BindDC メソッドには、二つの引数があります。引数 hDC には、描画用ターゲットの出力を受け取るデバイスコンテキストのハンドル (HDC) を指定します。引数 pSubRect には、デバイスコンテキストの対象となる四角形の描画領域を指定します。引数 pSubRect で表されたデバイス コンテキストの領域に合わせて、Direct2D の描画用ターゲットのサイズも更新されます。

次のコードは、描画用ターゲットと HDC とをバインドする例です。この例では、ps.hdc が GDI のデバイス コンテキストのハンドルを表し、rc がハンドルの対象となる四角形の領域を表しています。

IFR(m_spDCRT->BindDC(
    ps.hdc,
    &rc
));

ページのトップへ


タスク 1 – GDI の描画領域に Direct2D の円グラフを追加する

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

この練習では、Direct2D を使用して、3 つに分割された円グラフを描画します。既存の GDI を使用した円グラフも描画します。

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

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

このあとの作業のために、演習用フォルダ内にある次のソリューション ファイルを開いてください。(このソリューション ファイルは、練習 2 のタスク 3 の作業を終了した時点のものと同じです。

  • Ex3_Starter\Direct2D HOL.sln

1. (この手順は、既に完了しています。) HandsOnLab.h において、DemoApp クラスのプライベート メンバとして、ID2D1DCRenderTargetPtr オブジェクトを宣言します。

ID2D1DCRenderTargetPtr m_spDCRT;

2. HandsOnLab.cpp において CreateDeviceResources メソッドの中で、練習 1 で作成したファクトリを使用して、HDC 向けの描画用ターゲットを作成します。

// Create a DC render target 
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
    D2D1_RENDER_TARGET_TYPE_DEFAULT,
    D2D1::PixelFormat(
        DXGI_FORMAT_B8G8R8A8_UNORM,
        D2D1_ALPHA_MODE_IGNORE
        ) , 0.0, 0.0,
    D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE
    );

IFR(m_spD2DFactory->CreateDCRenderTarget(
    &props,
    &m_spDCRT
    ));

3. HDC 向けの描画用ターゲットを作成した後、このターゲットで使用する黒色のブラシを作成します。

// Create a black brush
IFR(m_spDcRT->CreateSolidColorBrush(
    D2D1::ColorF(D2D1::ColorF::Black),
    &m_spBlackBrush
    ));

4. この演習のアプリケーションでは、Direct2D と GDI の相互運用に関する操作は、DrawDCInterop メソッドの中に記述します。HandsOnLab.cpp の OnRender メソッドの中から、このメソッドを呼び出すように修正します。ここでは、GDI の描画領域に Direct2D のコンテンツを描画することに焦点を当てています。よって、OnRender メソッドの中で、前の練習で既に記述した Draw2D2DContent メソッドの呼び出しをコメント アウトします。

//IFR(DrawD2DContent(ps));	

IFR(DrawDCInterop(ps));

5. DemoApp::DrawDCInterop メソッドの中で、デバイスコンテキスト (HDC) と描画用ターゲットをバインドし、Direct2D を使用して円グラフを描画します。

RECT rc;

// Create the DC render target
IFR(CreateDeviceResources());

// Get the dimensions of the client drawing area
GetClientRect(
    m_hwnd,
    &rc
    );

// Bind the DC to the DC render target 
IFR(m_spDCRT->BindDC(
    ps.hdc,
    &rc
    ));

// Start drawing to the DC render target
m_spDCRT->BeginDraw();

m_spDCRT->SetTransform(
    D2D1::Matrix3x2F::Identity()
    );

m_spDCRT->Clear(
    D2D1::ColorF(D2D1::ColorF::White)
    );

m_spDCRT->DrawEllipse(
    D2D1::Ellipse(
        D2D1::Point2F(150.0f, 150.0f),
        100.0f,
        100.0f
        ),
    m_spBlackBrush,
    3.0
    );

m_spDCRT->DrawLine(
    D2D1::Point2F(150.0f, 150.0f),
    D2D1::Point2F(
        (150.0f + 100.0f * 0.15425f),
        (150.0f - 100.0f * 0.988f)
        ), 
    m_spBlackBrush,
    3.0
    );

m_spDCRT->DrawLine(
    D2D1::Point2F(150.0f, 150.0f),
    D2D1::Point2F(
        (150.0f + 100.0f * 0.525f),
        (150.0f + 100.0f * 0.8509f)
        ),
    m_spBlackBrush,
    3.0
    );

m_spDCRT->DrawLine(
    D2D1::Point2F(150.0f, 150.0f),
    D2D1::Point2F(
        (150.0f - 100.0f * 0.988f),
        (150.0f - 100.0f * 0.15425f)
        ),
    m_spBlackBrush,
    3.0
    );

hr = m_spDCRT->EndDraw();

6. エラー処理のコードを追加します。DrawDCInterop メソッドの実行中にエラーが発生した場合、メモリ リークを防止するために、作成したすべてのリソースを解放するようにします。そのためには、DrawDCInterop メソッドの中の最後に、以下のコードを追加します。

if (hr == D2DERR_RECREATE_TARGET)
{
    DiscardDeviceResources();
}

7. 既に記述された GDI を使用して円グラフを描画するコードのコメントを解除します。

// Let's save the original object first
HGDIOBJ original = NULL;
original = SelectObject(
    ps.hdc,
    GetStockObject(DC_PEN)
    );

HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
SelectObject(ps.hdc, blackPen);

::Ellipse(
    ps.hdc,
    300,
    50,
    500,
    250
    );

POINT pntArray1[2];
pntArray1[0].x = 400;
pntArray1[0].y = 150;
pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);

POINT pntArray2[2];
pntArray2[0].x = 400;
pntArray2[0].y = 150;
pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);


POINT pntArray3[2];
pntArray3[0].x = 400;
pntArray3[0].y = 150;
pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);

::Polyline(
    ps.hdc,
    pntArray1,
    2
    );

::Polyline(
    ps.hdc,
    pntArray2,
    2
    );

::Polyline(
    ps.hdc,
    pntArray3,
    2
    );

DeleteObject(blackPen);

// Restore the original object
SelectObject(ps.hdc, original);

ここまでのところでビルドを行って実行すると、このタスクの冒頭で挙げた実行結果と同様のウィンドウが表示されるはずです。Direct2D を使用して描画した円グラフと、GDI を使用して描画した円グラフには違いが見られます。特に斜めの線は、Direct2D のほうが GDI に比べて滑らかな線になっています。

なお、この練習 3 を行って完成したサンプル アプリケーション (ソリューション ファイル) は、次のパスに用意されています。

  • Ex3_Solution\Direct2D HOL.sln

参考

この練習では、GDI サーフェイスに対して、Direct2D の円グラフを描画しました。余力があれば、今度は逆に、Direct2D の描画用ターゲットに対して、GDI の円グラフを描画してみてください。

ページのトップへ


5. まとめ

この演習では、Direct2D を用いた基本的な描画方法や、GDI サーフェイスに対して Direct2D のコンテンツを描画する方法を学びました。

まず、塗りつぶしのブラシを用いて、四角形を描画する基本的な方法を取り上げました。また、任意の幾何学図形の描画方法や複数の図形の合成、グラデーションを伴うブラシを用いて塗りつぶす方法も確認しました。最後に、GDI サーフェイスに Direct2D のコンテンツを描画する方法を取り上げました。

ページのトップへ

評価してください: