簡単な Direct2D アプリケーションを作成する

このトピックでは、ウィンドウを作成し、Direct2D を使用してコンテンツを描画する DemoApp クラスを作成するプロセスについて説明します。 このチュートリアルでは、Direct2D リソースを作成し、基本的な図形を描画する方法について説明します。 また、リソースの作成を最小限に抑えて、パフォーマンスを向上させるためにアプリケーションを構築する方法についても説明します。

このチュートリアルに従うには、Microsoft Visual Studio を使用して Win32 プロジェクトを作成し、メイン アプリケーション ヘッダーと.cppファイルのコードを、このチュートリアルで説明されているコードに置き換えます。

GitHub の Simple Direct2D アプリケーション サンプル アプリも参照してください。

注意

Direct2D を使用するユニバーサル Windows プラットフォーム (UWP) アプリを作成する場合は、「Windows 8の Direct2D クイックスタート」トピックを参照してください。

Direct2D コンテンツの作成に使用できるインターフェイスの概要については、 Direct2D API の概要に関するページを参照してください。

チュートリアルが完了すると、 DemoApp クラスによって次の図に示す出力が生成されます。

グリッド背景上の 2 つの四角形の図

パート 1: DemoApp ヘッダーを作成する

この手順では、必要なヘッダーとマクロを追加して、Direct2D を使用するようにアプリケーションを設定します。 また、このチュートリアルの後の部分で使用するメソッドとデータ メンバーも宣言します。

  1. アプリケーション ヘッダー ファイルに、よく使用される次のヘッダーを含めます。

    // 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>
    
  2. インターフェイスを解放するための追加の関数と、モジュールのベース アドレスのエラー処理と取得のためのマクロを宣言します。

    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
    
  3. クラスの初期化、リソースの作成と破棄、メッセージ ループの処理、コンテンツのレンダリング、および 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
            );
    };
    
  4. クラス メンバーとして、 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 リソースが作成されます。

  1. クラス実装ファイルで、クラス コンストラクターとデストラクターを実装します。 コンストラクターは、そのメンバーを に初期化する 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);
    }
    
  2. メッセージを変換してディスパッチする DemoApp::RunMessageLoop メソッドを実装します。

    void DemoApp::RunMessageLoop()
    {
        MSG msg;
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    
  3. ウィンドウを作成し、それを表示し、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;
    }
    
  4. アプリケーション エントリ ポイントとして機能する 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 種類のリソースが用意されています。これは、アプリケーションの期間中に使用できるデバイスに依存しないリソースと、デバイスに依存するリソースです。 デバイスに依存するリソースは、特定のレンダリング デバイスに関連付けられます。そのデバイスが削除されると機能しなくなります。

  1. 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;
    }
    
  2. 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);
    
  3. レンダー ターゲットを使用して、灰色 の 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
            );
    }
    
  4. このメソッドは繰り返し呼び出されるため、レンダー ターゲット (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;
    }
    
  5. DemoApp::D iscardDeviceResources メソッドを実装します。 このメソッドでは、 DemoApp::CreateDeviceResources メソッドで作成したレンダー ターゲットと 2 つのブラシを解放します。

    void DemoApp::DiscardDeviceResources()
    {
        SafeRelease(&m_pRenderTarget);
        SafeRelease(&m_pLightSlateGrayBrush);
        SafeRelease(&m_pCornflowerBlueBrush);
    }
    

パート 4: Direct2D コンテンツをレンダリングする

このパートでは、ウィンドウ プロシージャ、 OnRender メソッド (コンテンツを塗りつぶす)、 OnResize メソッド (ウィンドウのサイズ変更時にレンダー ターゲットのサイズを調整する) を実装します。

  1. 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;
    }
    
  2. DemoApp::OnRender メソッドを実装します。 まず、 HRESULT を定義します。 次に、 CreateDeviceResource メソッドを 呼び出します。 このメソッドは、ウィンドウが描画されるたびに呼び出されます。 パート 3 の手順 4 では、レンダー ターゲットが既に存在する場合にメソッドが何も行わないようにする ステートメントを追加 if したことを思い出してください。

    HRESULT DemoApp::OnRender()
    {
        HRESULT hr = S_OK;
    
        hr = CreateDeviceResources();
    
  3. CreateDeviceResource メソッドが成功したことを確認します。 そうでない場合は、描画を実行しないでください。

    if (SUCCEEDED(hr))
    {
    
  4. 追加したステートメント内 if で、レンダー ターゲットの BeginDraw メソッドを呼び出して描画を開始します。 レンダー ターゲットの変換を ID マトリックスに設定し、ウィンドウをクリアします。

    m_pRenderTarget->BeginDraw();
    m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
    m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    
  5. 図面領域のサイズを取得します。

    D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
    
  6. ループとレンダー ターゲットの 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
            );
    }
    
  7. 画面の中央に配置された 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
        );
    
  8. レンダー ターゲットの FillRectangle メソッドを使用して、最初の四角形の内部を灰色のブラシで塗りつぶします。

    // Draw a filled rectangle.
    m_pRenderTarget->FillRectangle(&rectangle1, m_pLightSlateGrayBrush);
    
  9. レンダー ターゲットの DrawRectangle メソッドを使用して、2 番目の四角形の輪郭を cornflower ブルー ブラシで描画します。

    // Draw the outline of a rectangle.
    m_pRenderTarget->DrawRectangle(&rectangle2, m_pCornflowerBlueBrush);
    
  10. レンダー ターゲットの EndDraw メソッドを呼び出します。 EndDraw メソッドは、描画操作が成功したかどうかを示す HRESULT を返します。 手順 3 で開始した if ステートメントのスコープを閉じます。

        hr = m_pRenderTarget->EndDraw();
    }
    
  11. EndDraw によって返される HRESULT を確認します。 レンダー ターゲットを再作成する必要があることを示す場合は、DemoApp::D iscardDeviceResources メソッドを呼び出して解放します。次回ウィンドウがWM_PAINTまたはWM_DISPLAYCHANGEメッセージを受信すると再作成されます。

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    
  12. HRESULT を返し、メソッドのスコープを閉じます。

        return hr;
    }
    
  13. 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.hd2d1.lib

まとめ

このチュートリアルでは、Direct2D リソースを作成し、基本的な図形を描画する方法について説明しました。 また、リソースの作成を最小限に抑えて、パフォーマンスを向上させるためにアプリケーションを構築する方法についても学習しました。