簡単な Direct2D アプリケーションを作成する
このトピックでは、ウィンドウを作成し、Direct2D を使用してコンテンツを描画する DemoApp クラスを作成するプロセスについて説明します。 このチュートリアルでは、Direct2D リソースを作成し、基本的な図形を描画する方法について説明します。 また、リソースの作成を最小限に抑えて、パフォーマンスを向上させるためにアプリケーションを構築する方法についても説明します。
このチュートリアルに従うには、Microsoft Visual Studio を使用して Win32 プロジェクトを作成し、メイン アプリケーション ヘッダーと.cpp
ファイルのコードを、このチュートリアルで説明されているコードに置き換えます。
GitHub の Simple Direct2D アプリケーション サンプル アプリも参照してください。
注意
Direct2D を使用するユニバーサル Windows プラットフォーム (UWP) アプリを作成する場合は、「Windows 8の Direct2D クイックスタート」トピックを参照してください。
Direct2D コンテンツの作成に使用できるインターフェイスの概要については、 Direct2D API の概要に関するページを参照してください。
チュートリアルが完了すると、 DemoApp クラスによって次の図に示す出力が生成されます。
パート 1: DemoApp ヘッダーを作成する
この手順では、必要なヘッダーとマクロを追加して、Direct2D を使用するようにアプリケーションを設定します。 また、このチュートリアルの後の部分で使用するメソッドとデータ メンバーも宣言します。
アプリケーション ヘッダー ファイルに、よく使用される次のヘッダーを含めます。
// Windows Header Files: #include <windows.h> // C RunTime Header Files: #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <wchar.h> #include <math.h> #include <d2d1.h> #include <d2d1helper.h> #include <dwrite.h> #include <wincodec.h>
インターフェイスを解放するための追加の関数と、モジュールのベース アドレスのエラー処理と取得のためのマクロを宣言します。
template<class Interface> inline void SafeRelease( Interface **ppInterfaceToRelease) { if (*ppInterfaceToRelease != NULL) { (*ppInterfaceToRelease)->Release(); (*ppInterfaceToRelease) = NULL; } } #ifndef Assert #if defined( DEBUG ) || defined( _DEBUG ) #define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0) #else #define Assert(b) #endif //DEBUG || _DEBUG #endif #ifndef HINST_THISCOMPONENT EXTERN_C IMAGE_DOS_HEADER __ImageBase; #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase) #endif
クラスの初期化、リソースの作成と破棄、メッセージ ループの処理、コンテンツのレンダリング、および Windows プロシージャのメソッドを宣言します。
class DemoApp { public: DemoApp(); ~DemoApp(); // Register the window class and call methods for instantiating drawing resources HRESULT Initialize(); // Process and dispatch messages void RunMessageLoop(); private: // Initialize device-independent resources. HRESULT CreateDeviceIndependentResources(); // Initialize device-dependent resources. HRESULT CreateDeviceResources(); // Release device-dependent resource. void DiscardDeviceResources(); // Draw content. HRESULT OnRender(); // Resize the render target. void OnResize( UINT width, UINT height ); // The windows procedure. static LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); };
クラス メンバーとして、 ID2D1Factory オブジェクト、 ID2D1HwndRenderTarget オブジェクト、および 2 つの ID2D1SolidColorBrush オブジェクトのポインターを宣言します。
private: HWND m_hwnd; ID2D1Factory* m_pDirect2dFactory; ID2D1HwndRenderTarget* m_pRenderTarget; ID2D1SolidColorBrush* m_pLightSlateGrayBrush; ID2D1SolidColorBrush* m_pCornflowerBlueBrush;
パート 2: クラス インフラストラクチャを実装する
このパートでは、 DemoApp コンストラクターとデストラクター、その初期化メソッドとメッセージ ループ メソッド、および WinMain 関数を実装します。 これらの方法のほとんどは、他の Win32 アプリケーションで見られる方法と同じように見える。 唯一の例外は、CreateDeviceIndependentResources メソッド (次の部分で定義します) を呼び出す Initialize メソッドです。これにより、複数の Direct2D リソースが作成されます。
クラス実装ファイルで、クラス コンストラクターとデストラクターを実装します。 コンストラクターは、そのメンバーを に初期化する
NULL
必要があります。 デストラクターは、クラス メンバーとして格納されているすべてのインターフェイスを解放する必要があります。DemoApp::DemoApp() : m_hwnd(NULL), m_pDirect2dFactory(NULL), m_pRenderTarget(NULL), m_pLightSlateGrayBrush(NULL), m_pCornflowerBlueBrush(NULL) {} DemoApp::~DemoApp() { SafeRelease(&m_pDirect2dFactory); SafeRelease(&m_pRenderTarget); SafeRelease(&m_pLightSlateGrayBrush); SafeRelease(&m_pCornflowerBlueBrush); }
メッセージを変換してディスパッチする DemoApp::RunMessageLoop メソッドを実装します。
void DemoApp::RunMessageLoop() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
ウィンドウを作成し、それを表示し、DemoApp::CreateDeviceIndependentResources メソッドを呼び出す Initialize メソッドを実装します。 次のセクションでは、 CreateDeviceIndependentResources メソッドを実装します。
HRESULT DemoApp::Initialize() { HRESULT hr; // Initialize device-independent resources, such // as the Direct2D factory. hr = CreateDeviceIndependentResources(); if (SUCCEEDED(hr)) { // Register the window class. WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = DemoApp::WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(LONG_PTR); wcex.hInstance = HINST_THISCOMPONENT; wcex.hbrBackground = NULL; wcex.lpszMenuName = NULL; wcex.hCursor = LoadCursor(NULL, IDI_APPLICATION); wcex.lpszClassName = L"D2DDemoApp"; RegisterClassEx(&wcex); // In terms of using the correct DPI, to create a window at a specific size // like this, the procedure is to first create the window hidden. Then we get // the actual DPI from the HWND (which will be assigned by whichever monitor // the window is created on). Then we use SetWindowPos to resize it to the // correct DPI-scaled size, then we use ShowWindow to show it. m_hwnd = CreateWindow( L"D2DDemoApp", L"Direct2D demo application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, HINST_THISCOMPONENT, this); if (m_hwnd) { // Because the SetWindowPos function takes its size in pixels, we // obtain the window's DPI, and use it to scale the window size. float dpi = GetDpiForWindow(m_hwnd); SetWindowPos( m_hwnd, NULL, NULL, NULL, static_cast<int>(ceil(640.f * dpi / 96.f)), static_cast<int>(ceil(480.f * dpi / 96.f)), SWP_NOMOVE); ShowWindow(m_hwnd, SW_SHOWNORMAL); UpdateWindow(m_hwnd); } } return hr; }
アプリケーション エントリ ポイントとして機能する WinMain メソッドを実装します。 DemoApp クラスのインスタンスを初期化し、そのメッセージ ループを開始します。
int WINAPI WinMain( HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR /* lpCmdLine */, int /* nCmdShow */ ) { // Use HeapSetInformation to specify that the process should // terminate if the heap manager detects an error in any heap used // by the process. // The return value is ignored, because we want to continue running in the // unlikely event that HeapSetInformation fails. HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (SUCCEEDED(CoInitialize(NULL))) { { DemoApp app; if (SUCCEEDED(app.Initialize())) { app.RunMessageLoop(); } } CoUninitialize(); } return 0; }
パート 3: Direct2D リソースを作成する
このパートでは、描画に使用する Direct2D リソースを作成します。 Direct2D には、2 種類のリソースが用意されています。これは、アプリケーションの期間中に使用できるデバイスに依存しないリソースと、デバイスに依存するリソースです。 デバイスに依存するリソースは、特定のレンダリング デバイスに関連付けられます。そのデバイスが削除されると機能しなくなります。
DemoApp::CreateDeviceIndependentResources メソッドを実装します。 メソッドで、他の Direct2D リソースを作成するためのデバイスに依存しないリソースである ID2D1Factory を作成します。 クラス メンバーを
m_pDirect2DdFactory
使用してファクトリを格納します。HRESULT DemoApp::CreateDeviceIndependentResources() { HRESULT hr = S_OK; // Create a Direct2D factory. hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory); return hr; }
DemoApp::CreateDeviceResources メソッドを実装します。 このメソッドは、ウィンドウのデバイスに依存するリソース、レンダー ターゲット、および 2 つのブラシを作成します。 クライアント領域のサイズを取得し、ウィンドウの HWND にレンダリングされるのと同じサイズの ID2D1HwndRenderTarget を作成します。 レンダー ターゲットをクラス メンバーに
m_pRenderTarget
格納します。RECT rc; GetClientRect(m_hwnd, &rc); D2D1_SIZE_U size = D2D1::SizeU( rc.right - rc.left, rc.bottom - rc.top); // Create a Direct2D render target. hr = m_pDirect2dFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(m_hwnd, size), &m_pRenderTarget);
レンダー ターゲットを使用して、灰色 の ID2D1SolidColorBrush とコーンフラワーブルー ID2D1SolidColorBrush を作成します。
if (SUCCEEDED(hr)) { // Create a gray brush. hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::LightSlateGray), &m_pLightSlateGrayBrush ); } if (SUCCEEDED(hr)) { // Create a blue brush. hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::CornflowerBlue), &m_pCornflowerBlueBrush ); }
このメソッドは繰り返し呼び出されるため、レンダー ターゲット (
m_pRenderTarget
) が既に存在するかどうかをチェックに ステートメントを追加if
します。 次のコードは、 CreateDeviceResources メソッド全体を示しています。HRESULT DemoApp::CreateDeviceResources() { HRESULT hr = S_OK; if (!m_pRenderTarget) { RECT rc; GetClientRect(m_hwnd, &rc); D2D1_SIZE_U size = D2D1::SizeU( rc.right - rc.left, rc.bottom - rc.top ); // Create a Direct2D render target. hr = m_pDirect2dFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(m_hwnd, size), &m_pRenderTarget ); if (SUCCEEDED(hr)) { // Create a gray brush. hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::LightSlateGray), &m_pLightSlateGrayBrush ); } if (SUCCEEDED(hr)) { // Create a blue brush. hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::CornflowerBlue), &m_pCornflowerBlueBrush ); } } return hr; }
DemoApp::D iscardDeviceResources メソッドを実装します。 このメソッドでは、 DemoApp::CreateDeviceResources メソッドで作成したレンダー ターゲットと 2 つのブラシを解放します。
void DemoApp::DiscardDeviceResources() { SafeRelease(&m_pRenderTarget); SafeRelease(&m_pLightSlateGrayBrush); SafeRelease(&m_pCornflowerBlueBrush); }
パート 4: Direct2D コンテンツをレンダリングする
このパートでは、ウィンドウ プロシージャ、 OnRender メソッド (コンテンツを塗りつぶす)、 OnResize メソッド (ウィンドウのサイズ変更時にレンダー ターゲットのサイズを調整する) を実装します。
DemoApp::WndProc メソッドを実装して、ウィンドウ メッセージを処理します。 WM_SIZE メッセージの場合は、DemoApp::OnResize メソッドを呼び出し、新しい幅と高さを渡します。 WM_PAINTメッセージとWM_DISPLAYCHANGE メッセージの場合は、DemoApp::OnRender メソッドを呼び出してウィンドウを描画します。 次の手順では、 OnRender メソッドと OnResize メソッドを実装します。
LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT result = 0; if (message == WM_CREATE) { LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam; DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams; ::SetWindowLongPtrW( hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pDemoApp) ); result = 1; } else { DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>( ::GetWindowLongPtrW( hwnd, GWLP_USERDATA ))); bool wasHandled = false; if (pDemoApp) { switch (message) { case WM_SIZE: { UINT width = LOWORD(lParam); UINT height = HIWORD(lParam); pDemoApp->OnResize(width, height); } result = 0; wasHandled = true; break; case WM_DISPLAYCHANGE: { InvalidateRect(hwnd, NULL, FALSE); } result = 0; wasHandled = true; break; case WM_PAINT: { pDemoApp->OnRender(); ValidateRect(hwnd, NULL); } result = 0; wasHandled = true; break; case WM_DESTROY: { PostQuitMessage(0); } result = 1; wasHandled = true; break; } } if (!wasHandled) { result = DefWindowProc(hwnd, message, wParam, lParam); } } return result; }
DemoApp::OnRender メソッドを実装します。 まず、 HRESULT を定義します。 次に、 CreateDeviceResource メソッドを 呼び出します。 このメソッドは、ウィンドウが描画されるたびに呼び出されます。 パート 3 の手順 4 では、レンダー ターゲットが既に存在する場合にメソッドが何も行わないようにする ステートメントを追加
if
したことを思い出してください。HRESULT DemoApp::OnRender() { HRESULT hr = S_OK; hr = CreateDeviceResources();
CreateDeviceResource メソッドが成功したことを確認します。 そうでない場合は、描画を実行しないでください。
if (SUCCEEDED(hr)) {
追加したステートメント内
if
で、レンダー ターゲットの BeginDraw メソッドを呼び出して描画を開始します。 レンダー ターゲットの変換を ID マトリックスに設定し、ウィンドウをクリアします。m_pRenderTarget->BeginDraw(); m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity()); m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
図面領域のサイズを取得します。
D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
ループとレンダー ターゲットの DrawLine メソッドを
for
使用してグリッドの背景を描画し、一連の線を描画します。// Draw a grid background. int width = static_cast<int>(rtSize.width); int height = static_cast<int>(rtSize.height); for (int x = 0; x < width; x += 10) { m_pRenderTarget->DrawLine( D2D1::Point2F(static_cast<FLOAT>(x), 0.0f), D2D1::Point2F(static_cast<FLOAT>(x), rtSize.height), m_pLightSlateGrayBrush, 0.5f ); } for (int y = 0; y < height; y += 10) { m_pRenderTarget->DrawLine( D2D1::Point2F(0.0f, static_cast<FLOAT>(y)), D2D1::Point2F(rtSize.width, static_cast<FLOAT>(y)), m_pLightSlateGrayBrush, 0.5f ); }
画面の中央に配置された 2 つの四角形プリミティブを作成します。
// Draw two rectangles. D2D1_RECT_F rectangle1 = D2D1::RectF( rtSize.width/2 - 50.0f, rtSize.height/2 - 50.0f, rtSize.width/2 + 50.0f, rtSize.height/2 + 50.0f ); D2D1_RECT_F rectangle2 = D2D1::RectF( rtSize.width/2 - 100.0f, rtSize.height/2 - 100.0f, rtSize.width/2 + 100.0f, rtSize.height/2 + 100.0f );
レンダー ターゲットの FillRectangle メソッドを使用して、最初の四角形の内部を灰色のブラシで塗りつぶします。
// Draw a filled rectangle. m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
レンダー ターゲットの DrawRectangle メソッドを使用して、2 番目の四角形の輪郭を cornflower ブルー ブラシで描画します。
// Draw the outline of a rectangle. m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
レンダー ターゲットの EndDraw メソッドを呼び出します。 EndDraw メソッドは、描画操作が成功したかどうかを示す HRESULT を返します。 手順 3 で開始した
if
ステートメントのスコープを閉じます。hr = m_pRenderTarget->EndDraw(); }
EndDraw によって返される HRESULT を確認します。 レンダー ターゲットを再作成する必要があることを示す場合は、DemoApp::D iscardDeviceResources メソッドを呼び出して解放します。次回ウィンドウがWM_PAINTまたはWM_DISPLAYCHANGEメッセージを受信すると再作成されます。
if (hr == D2DERR_RECREATE_TARGET) { hr = S_OK; DiscardDeviceResources(); }
HRESULT を返し、メソッドのスコープを閉じます。
return hr; }
DemoApp::OnResize メソッドを実装して、レンダー ターゲットのサイズをウィンドウの新しいサイズに変更します。
void DemoApp::OnResize(UINT width, UINT height) { if (m_pRenderTarget) { // Note: This method can fail, but it's okay to ignore the // error here, because the error will be returned again // the next time EndDraw is called. m_pRenderTarget->Resize(D2D1::SizeU(width, height)); } }
これでチュートリアルが完了しました。
注意
Direct2D を使用するには、アプリケーションにヘッダー ファイルが含まれていることを d2d1.h
確認し、ライブラリに d2d1.lib
対してコンパイルします。
と は、Windows SDK で見つけることができますd2d1.h
d2d1.lib
。
まとめ
このチュートリアルでは、Direct2D リソースを作成し、基本的な図形を描画する方法について説明しました。 また、リソースの作成を最小限に抑えて、パフォーマンスを向上させるためにアプリケーションを構築する方法についても学習しました。