WisdomSoft - for your serial experiences.

描画領域を塗りつぶす

ウィンドウを描画先とするレンダーターゲットを取得し、描画領域を塗りつぶします。

Direct2D ファクトリの生成

Direct2D を用いて画面に何らかのイメージを描画するには、最初に Direct2D オブジェクトを生成するためのファクトリを取得しなければなりません。ファクトリは、主要な Direct2D インターフェイスを実装するインスタンスを生成するために必要です。Direct2D のファクトリは ID2D1Factory インターフェイスで表されます。

ID2D1Factory インターフェイス
interface ID2D1Factory : public IUnknown

ID2D1Factory インターフェイスは Direct2D の機能を利用するための開始点であり、まず最初に、このインターフェイスのオブジェクトを取得する必要があります。ファクトリを生成するには D2D1CreateFactory() 関数を使います。

D2D1CreateFactory() 関数
template<class Factory>
HRESULT D2D1CreateFactory(
    __in D2D1_FACTORY_TYPE factoryType,
    __out Factory **factory
);
HRESULT WINAPI D2D1CreateFactory(D2D1_FACTORY_TYPE,REFIID,**void)(
  __in   D2D1_FACTORY_TYPE factoryType,
  __in   REFIID riid,
  __out  void **ppIFactory
);

関数は、オーバーロードされていますが、取得するファクトリの型をテンプレートパラメータで指定するか、GUID で指定するかの違いでしかありません。

factoryType パラメータには D2D1_FACTORY_TYPE 列挙型のいずれかの値でスレッドモデルを指定します。riid パラメータは ID2D1Factory インターフェイスを参照する IID を指定します。ppIFactory パラメータに、生成されたインスタンスを受け取るポインタへのポインタを指定します。関数は、成功すれば S_OK を返し、そうでなければエラーコードを返します。

D2D1_FACTORY_TYPE 列挙型は、マルチスレッド環境で Direct2D オブジェクトに安全にアクセスできるように同期処理を行うかどうかを設定します。

D2D1_FACTORY_TYPE 列挙型
typedef enum  {
  D2D1_FACTORY_TYPE_SINGLE_THREADED   = 0,
  D2D1_FACTORY_TYPE_MULTI_THREADED    = 1 
} D2D1_FACTORY_TYPE;

D2D1_FACTORY_TYPE_SINGLE_THREADED メンバはシングルスレッドに最適化して同期(排他制御)をしないことを表し、D2D1_FACTORY_TYPE_MULTI_THREADED メンバは同期することを表します。複数のスレッドから、ファクトリから生成した Direct2D オブジェクトにアクセスする可能性があり、自前で同期を管理しない場合は D2D1_FACTORY_TYPE_MULTI_THREADED メンバを指定し、そうでなければ D2D1_FACTORY_TYPE_SINGLE_THREADED を指定します。

riid パラメータに指定する REFIID 型の実体はインターフェイスを表す GUID です。これは __uuidof(ID2DFactory) という演算で取得できます。Factory テンプレートパラメータを受け取るオーバーロードされた関数は、以下の処理を内部で行っているだけです。

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

Factory テンプレートパラメータは ID2DFactory を既定の引数とするため省略可能です。

例えば、以下のような形で Direct2D のファクトリを生成します。

HRESULT hr;
ID2D1Factory *d2d1Factory;
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2d1Factory);

関数が成功すれば d2d1Factory 変数に生成されたファクトリへのポインタが格納されています。

レンダーターゲットの初期化

続いて、生成した ID2DFactory インターフェイスのオブジェクトからレンダーターゲットを取得します。レンダーターゲットはイメージの描画対象を表すオブジェクトで、レンダーターゲットが提供する様々なメソッドを用いてイメージを生成できます。

この場では、ウィンドウの領域にイメージを描画することが目的なので、ウィンドウのハンドル HWND を対象とするレンダーターゲットを生成します。HWND を対象とするレンダーターゲットは ID2D1HwndRenderTarget インターフェイスとして表します。

ID2D1HwndRenderTarget インターフェイス
interface ID2D1HwndRenderTarget : public ID2D1RenderTarget

ID2D1HwndRenderTarget インターフェイスは ID2D1RenderTarget インターフェイスを継承しています。この ID2D1RenderTarget インターフェイスが、レンダーターゲットの基本インターフェイスであり、描画対象の実体が何であっても ID2D1RenderTarget インターフェイスで宣言されているメソッドを用いて、共通の操作で描画できます。

ID2D1HwndRenderTarget インターフェイスのインスタンスを生成するには CreateHwndRenderTarget() メソッドを使います。

ID2D1Factory インターフェイス CreateHwndRenderTarget() メソッド
virtual HRESULT CreateHwndRenderTarget(
  [in]   D2D1_RENDER_TARGET_PROPERTIES *renderTargetProperties,
  [in]   D2D1_HWND_RENDER_TARGET_PROPERTIES *hwndRenderTargetProperties,
  [out]  ID2D1HwndRenderTarget **hwndRenderTarget
) = 0;
HRESULT CreateHwndRenderTarget(
  [ref]  const D2D1_RENDER_TARGET_PROPERTIES &renderTargetProperties,
  [ref]  const D2D1_HWND_RENDER_TARGET_PROPERTIES &hwndRenderTargetProperties,
  [out]  ID2D1HwndRenderTarget **hwndRenderTarget
);

メソッドはオーバーロードされていますが、引数をポインタで受け取るのか、参照で受け取るのかの違いしかありません。

renderTargetProperties パラメータには描画に関する基本的な設定情報を、hwndRenderTargetProperties は描画対象のウィンドウに関する設定情報を指定します。hwndRenderTarget パラメータには、生成したインスタンスを受け取るポインタへのポインタを指定します。戻り値は、成功すれば S_OK を返し、そうでなければエラーコードです。

renderTargetProperties パラメータには、適切に初期化された D2D1_RENDER_TARGET_PROPERTIES 構造体の値が必要です。

D2D1_RENDER_TARGET_PROPERTIES 構造体
struct D2D1_RENDER_TARGET_PROPERTIES {
  D2D1_RENDER_TARGET_TYPE  type;
  D2D1_PIXEL_FORMAT        pixelFormat;
  FLOAT                    dpiX;
  FLOAT                    dpiY;
  D2D1_RENDER_TARGET_USAGE usage;
  D2D1_FEATURE_LEVEL       minLevel;
};

type メンバは、ハードウェアによる描画とソフトウェアによる描画のどちらを利用するかを D2D1_RENDER_TARGET_TYPE 列挙型の値で指定します。多くの場合、GPU によるハードウェアアクセラレーションが有効であればハードウェアで描画したほうが高速です。 

pixelFormat メンバはピクセル形式とアルファモードを D2D1_PIXEL_FORMAT 構造体で指定します。

dpiX メンバと dpiY メンバには、それぞれ水平方向と垂直方向の DPI (Donts per Inch、ピクセル密度)を指定します。既定の DPI を使用するには 0 を指定します。

usage メンバにはレンダーターゲットのリモート処理と GDI との互換性を表す D2D1_RENDER_TARGET_TYPE 列挙型の値を指定します。

minLevel メンバは、ハードウェア描画に必要な Direct3D の機能レベルを表します。GPU が対応する DirectX の最小限のバージョンを D2D1_FEATURE_LEVEL 列挙型の値で指定します。

いろいろな設定が必要ですが、詳細を順に見ていきましょう。最初の type メンバに設定するのは D2D1_RENDER_TARGET_TYPE 列挙型の値です。

D2D1_RENDER_TARGET_TYPE 列挙型
typedef enum  {
  D2D1_RENDER_TARGET_TYPE_DEFAULT     = 0,
  D2D1_RENDER_TARGET_TYPE_SOFTWARE    = 1,
  D2D1_RENDER_TARGET_TYPE_HARDWARE    = 2 
} D2D1_RENDER_TARGET_TYPE;

D2D1_RENDER_TARGET_TYPE_DEFAULT メンバはハードウェアが利用可能であればハードウェアで描画し、そうでなければソフトウェアで描画します。D2D1_RENDER_TARGET_TYPE_SOFTWARE メンバはソフトウェアでの描画を、D2D1_RENDER_TARGET_TYPE_HARDWARE メンバはハードウェアの描画を強制します。通常は D2D1_RENDER_TARGET_TYPE_DEFAULT を指定します。

pixelFormat メンバに指定する D2D1_PIXEL_FORMAT 構造体は、ピクセル形式とアルファ値の扱いを表します。

D2D1_PIXEL_FORMAT 構造体
struct D2D1_PIXEL_FORMAT {
  DXGI_FORMAT     format;
  D2D1_ALPHA_MODE alphaMode;
};

format メンバには DXGI_FORMAT 列挙型の値を指定します。この列挙型には、ピクセル要素のビット数や型に応じた多くのメンバが宣言されています。

DXGI_FORMAT 列挙型
typedef enum DXGI_FORMAT { 
  DXGI_FORMAT_UNKNOWN                      = 0,
  DXGI_FORMAT_R32G32B32A32_TYPELESS        = 1,
  DXGI_FORMAT_R32G32B32A32_FLOAT           = 2,
  DXGI_FORMAT_R32G32B32A32_UINT            = 3,
  DXGI_FORMAT_R32G32B32A32_SINT            = 4,
  DXGI_FORMAT_R32G32B32_TYPELESS           = 5,
  DXGI_FORMAT_R32G32B32_FLOAT              = 6,
  DXGI_FORMAT_R32G32B32_UINT               = 7,
  DXGI_FORMAT_R32G32B32_SINT               = 8,
  DXGI_FORMAT_R16G16B16A16_TYPELESS        = 9,
  DXGI_FORMAT_R16G16B16A16_FLOAT           = 10,
  DXGI_FORMAT_R16G16B16A16_UNORM           = 11,
  DXGI_FORMAT_R16G16B16A16_UINT            = 12,
  DXGI_FORMAT_R16G16B16A16_SNORM           = 13,
  DXGI_FORMAT_R16G16B16A16_SINT            = 14,
  DXGI_FORMAT_R32G32_TYPELESS              = 15,
  DXGI_FORMAT_R32G32_FLOAT                 = 16,
  DXGI_FORMAT_R32G32_UINT                  = 17,
  DXGI_FORMAT_R32G32_SINT                  = 18,
  DXGI_FORMAT_R32G8X24_TYPELESS            = 19,
  DXGI_FORMAT_D32_FLOAT_S8X24_UINT         = 20,
  DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS     = 21,
  DXGI_FORMAT_X32_TYPELESS_G8X24_UINT      = 22,
  DXGI_FORMAT_R10G10B10A2_TYPELESS         = 23,
  DXGI_FORMAT_R10G10B10A2_UNORM            = 24,
  DXGI_FORMAT_R10G10B10A2_UINT             = 25,
  DXGI_FORMAT_R11G11B10_FLOAT              = 26,
  DXGI_FORMAT_R8G8B8A8_TYPELESS            = 27,
  DXGI_FORMAT_R8G8B8A8_UNORM               = 28,
  DXGI_FORMAT_R8G8B8A8_UNORM_SRGB          = 29,
  DXGI_FORMAT_R8G8B8A8_UINT                = 30,
  DXGI_FORMAT_R8G8B8A8_SNORM               = 31,
  DXGI_FORMAT_R8G8B8A8_SINT                = 32,
  DXGI_FORMAT_R16G16_TYPELESS              = 33,
  DXGI_FORMAT_R16G16_FLOAT                 = 34,
  DXGI_FORMAT_R16G16_UNORM                 = 35,
  DXGI_FORMAT_R16G16_UINT                  = 36,
  DXGI_FORMAT_R16G16_SNORM                 = 37,
  DXGI_FORMAT_R16G16_SINT                  = 38,
  DXGI_FORMAT_R32_TYPELESS                 = 39,
  DXGI_FORMAT_D32_FLOAT                    = 40,
  DXGI_FORMAT_R32_FLOAT                    = 41,
  DXGI_FORMAT_R32_UINT                     = 42,
  DXGI_FORMAT_R32_SINT                     = 43,
  DXGI_FORMAT_R24G8_TYPELESS               = 44,
  DXGI_FORMAT_D24_UNORM_S8_UINT            = 45,
  DXGI_FORMAT_R24_UNORM_X8_TYPELESS        = 46,
  DXGI_FORMAT_X24_TYPELESS_G8_UINT         = 47,
  DXGI_FORMAT_R8G8_TYPELESS                = 48,
  DXGI_FORMAT_R8G8_UNORM                   = 49,
  DXGI_FORMAT_R8G8_UINT                    = 50,
  DXGI_FORMAT_R8G8_SNORM                   = 51,
  DXGI_FORMAT_R8G8_SINT                    = 52,
  DXGI_FORMAT_R16_TYPELESS                 = 53,
  DXGI_FORMAT_R16_FLOAT                    = 54,
  DXGI_FORMAT_D16_UNORM                    = 55,
  DXGI_FORMAT_R16_UNORM                    = 56,
  DXGI_FORMAT_R16_UINT                     = 57,
  DXGI_FORMAT_R16_SNORM                    = 58,
  DXGI_FORMAT_R16_SINT                     = 59,
  DXGI_FORMAT_R8_TYPELESS                  = 60,
  DXGI_FORMAT_R8_UNORM                     = 61,
  DXGI_FORMAT_R8_UINT                      = 62,
  DXGI_FORMAT_R8_SNORM                     = 63,
  DXGI_FORMAT_R8_SINT                      = 64,
  DXGI_FORMAT_A8_UNORM                     = 65,
  DXGI_FORMAT_R1_UNORM                     = 66,
  DXGI_FORMAT_R9G9B9E5_SHAREDEXP           = 67,
  DXGI_FORMAT_R8G8_B8G8_UNORM              = 68,
  DXGI_FORMAT_G8R8_G8B8_UNORM              = 69,
  DXGI_FORMAT_BC1_TYPELESS                 = 70,
  DXGI_FORMAT_BC1_UNORM                    = 71,
  DXGI_FORMAT_BC1_UNORM_SRGB               = 72,
  DXGI_FORMAT_BC2_TYPELESS                 = 73,
  DXGI_FORMAT_BC2_UNORM                    = 74,
  DXGI_FORMAT_BC2_UNORM_SRGB               = 75,
  DXGI_FORMAT_BC3_TYPELESS                 = 76,
  DXGI_FORMAT_BC3_UNORM                    = 77,
  DXGI_FORMAT_BC3_UNORM_SRGB               = 78,
  DXGI_FORMAT_BC4_TYPELESS                 = 79,
  DXGI_FORMAT_BC4_UNORM                    = 80,
  DXGI_FORMAT_BC4_SNORM                    = 81,
  DXGI_FORMAT_BC5_TYPELESS                 = 82,
  DXGI_FORMAT_BC5_UNORM                    = 83,
  DXGI_FORMAT_BC5_SNORM                    = 84,
  DXGI_FORMAT_B5G6R5_UNORM                 = 85,
  DXGI_FORMAT_B5G5R5A1_UNORM               = 86,
  DXGI_FORMAT_B8G8R8A8_UNORM               = 87,
  DXGI_FORMAT_B8G8R8X8_UNORM               = 88,
  DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM   = 89,
  DXGI_FORMAT_B8G8R8A8_TYPELESS            = 90,
  DXGI_FORMAT_B8G8R8A8_UNORM_SRGB          = 91,
  DXGI_FORMAT_B8G8R8X8_TYPELESS            = 92,
  DXGI_FORMAT_B8G8R8X8_UNORM_SRGB          = 93,
  DXGI_FORMAT_BC6H_TYPELESS                = 94,
  DXGI_FORMAT_BC6H_UF16                    = 95,
  DXGI_FORMAT_BC6H_SF16                    = 96,
  DXGI_FORMAT_BC7_TYPELESS                 = 97,
  DXGI_FORMAT_BC7_UNORM                    = 98,
  DXGI_FORMAT_BC7_UNORM_SRGB               = 99,
  DXGI_FORMAT_AYUV                         = 100,
  DXGI_FORMAT_Y410                         = 101,
  DXGI_FORMAT_Y416                         = 102,
  DXGI_FORMAT_NV12                         = 103,
  DXGI_FORMAT_P010                         = 104,
  DXGI_FORMAT_P016                         = 105,
  DXGI_FORMAT_420_OPAQUE                   = 106,
  DXGI_FORMAT_YUY2                         = 107,
  DXGI_FORMAT_Y210                         = 108,
  DXGI_FORMAT_Y216                         = 109,
  DXGI_FORMAT_NV11                         = 110,
  DXGI_FORMAT_AI44                         = 111,
  DXGI_FORMAT_IA44                         = 112,
  DXGI_FORMAT_P8                           = 113,
  DXGI_FORMAT_A8P8                         = 114,
  DXGI_FORMAT_B4G4R4A4_UNORM               = 115,
  DXGI_FORMAT_FORCE_UINT                   = 0xffffffffUL 
} DXGI_FORMAT;

アプリケーションで扱う映像データに合わせてピクセル形式を指定する場合は、上記のメンバから適切な形式を選択します。通常、ウィンドウを描画対象とする場合は、フォーマットが不明であることを表す DXGI_FORMAT_UNKNOWN メンバを指定すれば問題ありません。この場合は既定の形式が採用されます。

alphaMode メンバにはD2D1_ALPHA_MODE 列挙型の値を指定します。

D2D1_ALPHA_MODE 列挙型
typedef enum  {
  D2D1_ALPHA_MODE_UNKNOWN         = 0,
  D2D1_ALPHA_MODE_PREMULTIPLIED   = 1,
  D2D1_ALPHA_MODE_STRAIGHT        = 2,
  D2D1_ALPHA_MODE_IGNORE          = 3 
} D2D1_ALPHA_MODE;

D2D1_ALPHA_MODE_UNKNOWN メンバはアルファ値の扱いが不明(意味のないアルファ値)であることを表します。D2D1_ALPHA_MODE_PREMULTIPLIED メンバは計算済み(プリマルチプライ済み)であり、色要素が事前にアルファ値によって調整されている表します。D2D1_ALPHA_MODE_STRAIGHT メンバは逆で、アルファ値による調整(プリマルチプライ処理)がされていないことをあらわします。最後の D2D1_ALPHA_MODE_IGNORE メンバは、アルファ値を無視することを表します。

D2D1_ALPHA_MODE_UNKNOWN メンバを指定することで描画対象の既定値が得られるので、一般には D2D1_ALPHA_MODE_UNKNOWN メンバを指定することになるでしょう。ウィンドウが描画対象の場合は D2D1_ALPHA_MODE_IGNORE が既定値です。

レンダーターゲットのリモート処理と GDI との互換性は D2D1_RENDER_TARGET_USAGE 列挙型で表されます。これはリモートデスクトップなどを通じで描画された場合や、GDI と相互運用する場合に影響します。

D2D1_RENDER_TARGET_USAGE 列挙型
typedef enum  {
  D2D1_RENDER_TARGET_USAGE_NONE                    = 0x00000000,
  D2D1_RENDER_TARGET_USAGE_FORCE_BITMAP_REMOTING   = 0x00000001,
  D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE          = 0x00000002 
} D2D1_RENDER_TARGET_USAGE;

D2D1_RENDER_TARGET_USAGE_NONE メンバはリモート処理が可能であればリモートで描画し、リモート処理が失敗する場合はローカルで描画することを表します。GDI との互換性はありません。D2D1_RENDER_TARGET_USAGE_FORCE_BITMAP_REMOTING メンバはローカル環境で描画し、ビットマップとしてクライアントに送信することを表します。D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE メンバ GDI と組み合わせて使用することを表します。

ハードウェアによる描画に必要な Direct3D の最小限の機能レベルは D2D1_FEATURE_LEVEL 列挙型の値で設定します。

D2D1_FEATURE_LEVEL 列挙型
typedef enum  {
  D2D1_FEATURE_LEVEL_DEFAULT   = 0,
  D2D1_FEATURE_LEVEL_9         = D3D10_FEATURE_LEVEL_9_1,
  D2D1_FEATURE_LEVEL_10        = D3D10_FEATURE_LEVEL_10_0 
} D2D1_FEATURE_LEVEL;

D2D1_FEATURE_LEVEL_DEFAULT メンバは、Direct3D 機能レベルが適切かどうか判断する必要があることを表します。D2D1_FEATURE_LEVEL_9 メンバは DirectX 9 に対応している必要があることを表し、D2D1_FEATURE_LEVEL_10 メンバは DirectX 10 に対応している必要があることを表します。通常、D2D1_FEATURE_LEVEL_DEFAULT を指定します。

環境が指定した機能レベルを満たしていない場合で、ソフトウェアの描画が許可されている場合、レンダーターゲットはソフトウェアで描画されます。ハードウェアの描画が強制(type メンバに D2D1_RENDER_TARGET_TYPE_HARDWARE が設定)されている場合、レンダーターゲットの生成は失敗します。

続いて CreateHwndRenderTarget() 関数の第 2 パラメータに指定する D2D1_HWND_RENDER_TARGET_PROPERTIES 構造体について見てみましょう。

D2D1_HWND_RENDER_TARGET_PROPERTIES 構造体
struct D2D1_HWND_RENDER_TARGET_PROPERTIES {
  HWND                 hwnd;
  D2D1_SIZE_U          pixelSize;
  D2D1_PRESENT_OPTIONS presentOptions;
};

hwnd メンバには、描画対象となるウィンドウのハンドルを指定します。レンダーターゲットを通じて描画コマンドを発行すると、このウィンドウにイメージが描画されます。

pixelSize メンバには、描画範囲となる D2D_SIZE_U 構造体の値を指定します。ウィンドウを描画対象とする場合、ウィンドウのクライアント領域のサイズを指定することになります。

presentOptions メンバには、描画された内容を表示するときの動作を D2D1_PRESENT_OPTIONS 列挙型の値を組み合わせて指定します。

D2D_SIZE_U 構造体は、幅と高さからなる 2 つの値の組み合わせでサイズを表します。

D2D_SIZE_U 構造体
struct D2D_SIZE_U {
  UINT32 width;
  UINT32 height;
};

width メンバは幅を、height メンバは高さを表す整数です。

レンダーターゲット(ウィンドウ)が描画された内容を表示するときの動作は D2D1_PRESENT_OPTIONS 列挙型で設定できます。

D2D1_PRESENT_OPTIONS 列挙型
typedef enum  {
  D2D1_PRESENT_OPTIONS_NONE              = 0x00000000,
  D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS   = 0x00000001,
  D2D1_PRESENT_OPTIONS_IMMEDIATELY       = 0x00000002 
} D2D1_PRESENT_OPTIONS;

D2D1_PRESENT_OPTIONS_NONE メンバは既定の動作で、画面を更新するまで処理を待機し、表示後にフレームを破棄します。したがって、描画は垂直同期(画面のリフレッシュレート)に合わせられます。標準的な環境であれば 1 秒間に 60 回の更新となるでしょう。D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS メンバは表示時に内容を破棄しないことを表します。D2D1_PRESENT_OPTIONS_IMMEDIATELY メンバは画面更新を待機しないことを表します。

多くの場合は D2D1_PRESENT_OPTIONS_NONE メンバを設定することになりますが、描画のベンチマークなど垂直同期が不要な場合は D2D1_PRESENT_OPTIONS_IMMEDIATELY メンバを設定します。

以上のことをまとめて、一般的な形でウィンドウを描画対象とする場合、次のようにレンダーターゲットを初期化することになるでしょう。

HRESULT hr;
ID2D1HwndRenderTarget * renderTarget;
D2D1_SIZE_U renderTargetSize = { RENDER_WIDTH, RENDER_HEIGHT };
D2D1_PIXEL_FORMAT pixelFormat;
D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties;
D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRenderTargetProperties;

pixelFormat.format = DXGI_FORMAT_UNKNOWN;
pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;

renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
renderTargetProperties.pixelFormat = pixelFormat;
renderTargetProperties.dpiX = 0;
renderTargetProperties.dpiY = 0;
renderTargetProperties.usage = D2D1_RENDER_TARGET_USAGE_NONE;
renderTargetProperties.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;

hwndRenderTargetProperties.hwnd = hwnd;
hwndRenderTargetProperties.pixelSize = renderTargetSize;
hwndRenderTargetProperties.presentOptions = D2D1_PRESENT_OPTIONS_NONE;

hr = d2d1Factory->CreateHwndRenderTarget(
	renderTargetProperties, hwndRenderTargetProperties,
	&renderTarget
);

これでレンダーターゲットを取得できました。次は、いよいよ描画です。

描画領域の塗りつぶし

レンダーターゲットへの描画を開始するには、最初に BeginDraw() メソッドを呼び出さなければなりません。

ID2D1RenderTarget インターフェイス BeginDraw() メソッド
virtual void BeginDraw() = 0;

BeginDraw() は描画処理を開始することを表します。描画操作はこのメソッドの呼び出しから EndDraw() メソッドを呼び出すまでの間で実行しなければなりません。後述する Draw~() メソッドや Fill~() メソッドなどの描画処理は、必ず BeginDraw() メソッドと EndDraw() メソッドの間に記述します

ID2D1RenderTarget インターフェイス EndDraw() メソッド
virtual HRESULT EndDraw(
  [out, optional]  D2D1_TAG *tag1 = NULL,
  [out, optional]  D2D1_TAG *tag2 = NULL
) = 0;

EndDraw() メソッドは描画処理の終了を表します。通常、このメソッドが呼び出された時点で、指定された描画命令に基づいて画面が作られます。

tag1 パラメータと tag2 パラメータには、エラーが発生した時の原因となった描画操作のタグと呼ばれる値を受け取る変数へのポインタを指定します。D2D1_TAG 型は単純な整数です。これらのパラメータは省略可能です。この場では使わないので、引数を渡す必要はありません。

多くの場合、描画処理を開始する前に画面をクリアする必要があります。レンダーターゲットの描画範囲全体を指定した色で塗りつぶすには Clear() メソッドを使います。

ID2D1RenderTarget インターフェイス Clear() メソッド
virtual void Clear(
  [in, optional]  const D2D1_COLOR_F *clearColor = NULL
) = 0;
void Clear(
  [ref]  const D2D1_COLOR_F &clearColor
);

メソッドはオーバーロードされていますが、ポインタで受け取るか参照で受け取るかの違いだけです。

cleaColor パラメータに、塗りつぶす色を指定します。色は D2D1_COLOR_F 型で表されますが、この名前は別名であり、実体は D3DCOLORVALUE 構造体です。

D3DCOLORVALUE 構造体
struct D3DCOLORVALUE {
  FLOAT r;
  FLOAT g;
  FLOAT b;
  FLOAT a;
};

r メンバは赤成分、g メンバが緑成分、b メンバが青成分、a メンバがアルファ成分を表します。各色の成分は 0.0 ~ 10. までの範囲の浮動小数点数であらわされ、0 に近いほど色が弱く、1 に近いほど色が強くなります。

コード1
#include <windows.h>
#include <d2d1_1.h>

#pragma comment(lib, "d2d1.lib")

#define WINDOWS_CLASS_NAME TEXT("WisdomSoft.Sample.Window")
#define APP_TITLE TEXT("Direct2D サンプル")

//ウィンドウスタイル
#define WINDOW_STYLE WS_OVERLAPPEDWINDOW  ^ WS_SIZEBOX | WS_VISIBLE

//画面の幅と高さ
#define APP_VIEW_WIDTH 800
#define APP_VIEW_HEIGHT 480

//Windows 既定の DPI
#define DEFAULT_DPI 96

//ウィンドウに追加するデータ
typedef struct WINDOW_DATA
{
	ID2D1Factory *d2d1Factory;
	ID2D1HwndRenderTarget * renderTarget;
	ID2D1SolidColorBrush * brush;
} WindowData ;

//データを拡張ウィンドウメモリから入出力する
#define SetWindowData(hwnd, value) (SetWindowLongPtr((hwnd), 0, (LONG_PTR)(value)))
#define GetWindowData(hwnd) ((WindowData *)(GetWindowLongPtr((hwnd), 0)))

//アプリケーションを初期化する
void Initialize(HWND hwnd, WindowData *data)
{
	HRESULT hr;
	D2D1_SIZE_U renderTargetSize = { APP_VIEW_WIDTH, APP_VIEW_HEIGHT };
	D2D1_PIXEL_FORMAT pixelFormat;
	D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties;
	D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRenderTargetProperties;


	//Direct2D ファクトリを生成
	hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &data->d2d1Factory);
	if (FAILED(hr)) 
	{
		OutputDebugString(TEXT("Error: Direct2D の初期化に失敗。\n"));
		return;
	}

	//ウィンドウを出力先とするレンダリングターゲットを生成
	pixelFormat.format = DXGI_FORMAT_UNKNOWN;
	pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;

	renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
	renderTargetProperties.pixelFormat = pixelFormat;
	renderTargetProperties.dpiX = 0;
	renderTargetProperties.dpiY = 0;
	renderTargetProperties.usage = D2D1_RENDER_TARGET_USAGE_NONE;
	renderTargetProperties.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;

	hwndRenderTargetProperties.hwnd = hwnd;
	hwndRenderTargetProperties.pixelSize = renderTargetSize;
	hwndRenderTargetProperties.presentOptions = D2D1_PRESENT_OPTIONS_NONE;

	hr = data->d2d1Factory->CreateHwndRenderTarget(
		renderTargetProperties, hwndRenderTargetProperties,
		&data->renderTarget
	);
	if (FAILED(hr))
	{
		OutputDebugString(TEXT("Error: Direct2D のレンダーターゲットを生成できません。\n"));
		return;
	}
}

//アプリケーションをを終了する
void Exiting(HWND hwnd, WindowData *data)
{
	data->renderTarget->Release();
	data->d2d1Factory->Release();
}

//画面を描画する
void Draw(HWND hwnd, WindowData *data)
{
	D2D1_COLOR_F backgroundColor = { 1.0F, 0.9F, 0.9F, 1.0F };
	data->renderTarget->BeginDraw();
	data->renderTarget->Clear(backgroundColor);
	data->renderTarget->EndDraw();
}

//ウィンドウプロシージャ
LRESULT CALLBACK SampleWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg) 
	{
	case WM_DESTROY:
	{
		WindowData *data = GetWindowData(hwnd);
		Exiting(hwnd, data);

		delete data;
		PostQuitMessage(0);
		return 0;
	}
	case WM_CREATE:
	{
		WindowData *data = new WindowData();
		SetWindowData(hwnd, data);
		Initialize(hwnd, data);
		return 0;
	}
	case WM_PAINT:
		Draw(hwnd, GetWindowData(hwnd));
		ValidateRect(hwnd, NULL);
		return 0;
	default:
		return DefWindowProc(hwnd , uMsg , wParam , lParam);
	}

	return 0;
}

//エントリーポイント
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND window;
	HDC hdc;
	WNDCLASSEX wcx;
	int dpiX;
	int dpiY;
	DWORD windowWidth;
	DWORD windowHeight;
	int returnCode = 0;
	
	//ウィンドウクラスの登録
	wcx.cbSize = sizeof(WNDCLASSEX);
	wcx.style = CS_HREDRAW | CS_VREDRAW;
	wcx.lpfnWndProc = SampleWindowProc;
	wcx.cbClsExtra = 0;
	wcx.cbWndExtra = sizeof(WindowData*);
	wcx.hInstance = hInstance;
	wcx.hIcon =  LoadIcon(NULL , IDI_APPLICATION);
	wcx.hCursor = LoadCursor(NULL , IDC_ARROW);
	wcx.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
	wcx.lpszMenuName = NULL;
	wcx.lpszClassName = WINDOWS_CLASS_NAME;
	wcx.hIconSm = NULL;

	if (!RegisterClassEx(&wcx)) 
	{
		OutputDebugString(TEXT("Error: ウィンドウクラスの登録ができません。\n"));
		return 0;
	}

	//高DPIへの対応
	hdc = GetDC(NULL);
	dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
	dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
	ReleaseDC(NULL, hdc);

	//ウィンドウサイズの計算
	windowWidth = (APP_VIEW_WIDTH * dpiX / DEFAULT_DPI) + GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
	windowHeight = (APP_VIEW_HEIGHT * dpiY / DEFAULT_DPI) + GetSystemMetrics(SM_CYCAPTION) + 
		GetSystemMetrics(SM_CYFIXEDFRAME) * 2;

	//ウィンドウの生成
	window = CreateWindowEx(
		WS_EX_LEFT, WINDOWS_CLASS_NAME, APP_TITLE, WINDOW_STYLE,
		CW_USEDEFAULT, CW_USEDEFAULT, windowWidth, windowHeight, 
		NULL, NULL, hInstance ,NULL
	);
	if (!window)
	{
		OutputDebugString(TEXT("Error: ウィンドウが生成できません。\n"));
		return 0;
	}
	
	//メッセージループ
	while(TRUE)
	{
		MSG msg;
		int r = GetMessage(&msg, NULL, 0, 0);
		if (r > 0) DispatchMessage(&msg);
		else if(r == -1)
		{
			OutputDebugString(TEXT("Error: メッセージの取得に失敗しました。\n"));
			break;
		}
		else
		{
			returnCode = msg.wParam;
			break;
		}
	}

	return returnCode;
}
実行結果
コード1 実行結果

コード1は Direct2D のファクトリを生成し、ウィンドウをレンダーターゲットとする単純な Direct2D のサンプルです。この時点で図形やイメージの描画は行っておらず、Clear() メソッドで画面を指定の色で塗りつぶしているだけです。

ファクトリの生成とレンダーターゲットの初期化は Initialize() 関数で行っています。この関数はウィンドウが初期化されたタイミングでウィンドウプロシージャから呼び出されます。

ウィンドウプロシージャで WM_PAINT メッセージが発生すると、ウィンドウを描画する必要があると判断して Draw() 関数を呼び出します。Draw() 関数内では、Initialize() 関数で生成したレンダーターゲットをクリアしています。

複数の関数の間でファクトリやレンダーターゲットなどのオブジェクトを保持するために WindowData 構造体を用意し、この構造体へのポインタをウィンドウの拡張メモリに設定しています。ウィンドウ生成時に WindowData 構造体のインスタンスを生成し、Initialize() 関数や Draw() 関数のパラメータに渡しています。

最後に Exiting() 関数で生成したインスタンスの解放を行っています。DirectX は COM であり、すべての COM オブジェクトは IUnknown インターフェイスを実装しています。COM オブジェクトは参照カウント方式で管理されており、新しく参照する場合は AddRef() メソッドを呼び出し、オブジェクトが不要になれば Release() メソッドを呼び出さなければなりません。

もう少し簡単な初期化方法

CreateHwndRenderTarget() 関数に渡す設定が多いので、初期化が面倒と思うかもしれません。そこで、便宜的に多くのレンダーターゲットの生成で共通して利用できる、基本的な既定値の D2D1_RENDER_TARGET_PROPERTIES 構造体の値、および D2D1_HWND_RENDER_TARGET_PROPERTIES 構造体の値を返す関数が D2D1 名前空間内に用意されています。

既定値で初期化された D2D1_RENDER_TARGET_PROPERTIES 構造体の値を得るには D2D1::RenderTargetProperties() 関数を使います

D2D1::RenderTargetProperties() 関数
D2D1_RENDER_TARGET_PROPERTIES RenderTargetProperties(
        D2D1_RENDER_TARGET_TYPE type =  D2D1_RENDER_TARGET_TYPE_DEFAULT ,
  __in  const PIXEL_FORMAT pixelFormat =  D2D1::PixelFormat() ,
        FLOAT dpiX = 0.0,
        FLOAT dpiY = 0.0,
        D2D1_RENDER_TARGET_USAGE usage =  D2D1_RENDER_TARGET_USAGE_NONE ,
        D2D1_FEATURE_LEVEL minLevel =  D2D1_FEATURE_LEVEL_DEFAULT 
);

この関数のパラメータは、D2D1_RENDER_TARGET_PROPERTIES 構造体のメンバに対応しています。引数を省略した場合は既定値が設定されます。

同様に、既定値で初期化された D2D1_HWND_RENDER_TARGET_PROPERTIES 構造体の値を得るには D2D1::HwndRenderTargetProperties() 関数を使います。

D2D1::HwndRenderTargetProperties() 関数
D2D1_HWND_RENDER_TARGET_PROPERTIES HwndRenderTargetProperties(
  __in  HWND hwnd,
  __in  D2D1_SIZE_U pixelSize =  D2D1::Size(static_cast<UINT>(0), static_cast<UINT>(0)) ,
  __in  D2D1_PRESENT_OPTIONS presentOptions =  D2D1_PRESENT_OPTIONS_NONE 
);

この関数のパラメータは D2D1_HWND_RENDER_TARGET_PROPERTIES 構造体のメンバに対応しています。引数を省略した場合は既定値が設定されます。

コード2
#include <windows.h>
#include <d2d1_1.h>

#pragma comment(lib, "d2d1.lib")

#define WINDOWS_CLASS_NAME TEXT("WisdomSoft.Sample.Window")
#define APP_TITLE TEXT("Direct2D サンプル")

//ウィンドウスタイル
#define WINDOW_STYLE WS_OVERLAPPEDWINDOW  ^ WS_SIZEBOX | WS_VISIBLE

//画面の幅と高さ
#define APP_VIEW_WIDTH 800
#define APP_VIEW_HEIGHT 480

//Windows 既定の DPI
#define DEFAULT_DPI 96

//ウィンドウに追加するデータ
typedef struct WINDOW_DATA
{
	ID2D1Factory *d2d1Factory;
	ID2D1HwndRenderTarget * renderTarget;
	ID2D1SolidColorBrush * brush;
} WindowData ;

//データを拡張ウィンドウメモリから入出力する
#define SetWindowData(hwnd, value) (SetWindowLongPtr((hwnd), 0, (LONG_PTR)(value)))
#define GetWindowData(hwnd) ((WindowData *)(GetWindowLongPtr((hwnd), 0)))

//アプリケーションを初期化する
void Initialize(HWND hwnd, WindowData *data)
{
	HRESULT hr;
	D2D1_SIZE_U renderTargetSize = { APP_VIEW_WIDTH, APP_VIEW_HEIGHT };
	D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties;
	D2D1_HWND_RENDER_TARGET_PROPERTIES hwndRenderTargetProperties;


	//Direct2D ファクトリを生成
	hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &data->d2d1Factory);
	if (FAILED(hr)) 
	{
		OutputDebugString(TEXT("Error: Direct2D の初期化に失敗。\n"));
		return;
	}

	//ウィンドウを出力先とするレンダリングターゲットを生成
	renderTargetProperties = D2D1::RenderTargetProperties();
	hwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(hwnd, renderTargetSize);
	
	hr = data->d2d1Factory->CreateHwndRenderTarget(
		renderTargetProperties, hwndRenderTargetProperties,
		&data->renderTarget
	);
	if (FAILED(hr))
	{
		OutputDebugString(TEXT("Error: Direct2D のレンダーターゲットを生成できません。\n"));
		return;
	}
}

//アプリケーションをを終了する
void Exiting(HWND hwnd, WindowData *data)
{
	data->renderTarget->Release();
	data->d2d1Factory->Release();
}

//画面を描画する
void Draw(HWND hwnd, WindowData *data)
{
	D2D1_COLOR_F backgroundColor = { 1.0F, 0.9F, 0.9F, 1.0F };
	data->renderTarget->BeginDraw();
	data->renderTarget->Clear(backgroundColor);
	data->renderTarget->EndDraw();
}

//ウィンドウプロシージャ
LRESULT CALLBACK SampleWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg) 
	{
	case WM_DESTROY:
	{
		WindowData *data = GetWindowData(hwnd);
		Exiting(hwnd, data);

		delete data;
		PostQuitMessage(0);
		return 0;
	}
	case WM_CREATE:
	{
		WindowData *data = new WindowData();
		SetWindowData(hwnd, data);
		Initialize(hwnd, data);
		return 0;
	}
	case WM_PAINT:
		Draw(hwnd, GetWindowData(hwnd));
		ValidateRect(hwnd, NULL);
		return 0;
	default:
		return DefWindowProc(hwnd , uMsg , wParam , lParam);
	}

	return 0;
}

//エントリーポイント
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND window;
	HDC hdc;
	WNDCLASSEX wcx;
	int dpiX;
	int dpiY;
	DWORD windowWidth;
	DWORD windowHeight;
	int returnCode = 0;
	
	//ウィンドウクラスの登録
	wcx.cbSize = sizeof(WNDCLASSEX);
	wcx.style = CS_HREDRAW | CS_VREDRAW;
	wcx.lpfnWndProc = SampleWindowProc;
	wcx.cbClsExtra = 0;
	wcx.cbWndExtra = sizeof(WindowData*);
	wcx.hInstance = hInstance;
	wcx.hIcon =  LoadIcon(NULL , IDI_APPLICATION);
	wcx.hCursor = LoadCursor(NULL , IDC_ARROW);
	wcx.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
	wcx.lpszMenuName = NULL;
	wcx.lpszClassName = WINDOWS_CLASS_NAME;
	wcx.hIconSm = NULL;

	if (!RegisterClassEx(&wcx)) 
	{
		OutputDebugString(TEXT("Error: ウィンドウクラスの登録ができません。\n"));
		return 0;
	}

	//高DPIへの対応
	hdc = GetDC(NULL);
	dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
	dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
	ReleaseDC(NULL, hdc);

	//ウィンドウサイズの計算
	windowWidth = (APP_VIEW_WIDTH * dpiX / DEFAULT_DPI) + GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
	windowHeight = (APP_VIEW_HEIGHT * dpiY / DEFAULT_DPI) + GetSystemMetrics(SM_CYCAPTION) + 
		GetSystemMetrics(SM_CYFIXEDFRAME) * 2;

	//ウィンドウの生成
	window = CreateWindowEx(
		WS_EX_LEFT, WINDOWS_CLASS_NAME, APP_TITLE, WINDOW_STYLE,
		CW_USEDEFAULT, CW_USEDEFAULT, windowWidth, windowHeight, 
		NULL, NULL, hInstance ,NULL
	);
	if (!window)
	{
		OutputDebugString(TEXT("Error: ウィンドウが生成できません。\n"));
		return 0;
	}
	
	//メッセージループ
	while(TRUE)
	{
		MSG msg;
		int r = GetMessage(&msg, NULL, 0, 0);
		if (r > 0) DispatchMessage(&msg);
		else if(r == -1)
		{
			OutputDebugString(TEXT("Error: メッセージの取得に失敗しました。\n"));
			break;
		}
		else
		{
			returnCode = msg.wParam;
			break;
		}
	}

	return returnCode;
}
実行結果
コード2 実行結果

コード2コード1と同じ結果ですが、D2D1_RENDER_TARGET_PROPERTIES 構造体と D2D1_HWND_RENDER_TARGET_PROPERTIES 構造体の値を RenderTargetProperties() 関数と HwndRenderTargetProperties() 関数から生成しています。