間が開きましたが、Direct2D版です。以下はコードの一部を抜粋して取り上げるので、完全なコードも併せて参照ください。ビルドにはMicrosoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA、実行にはWindows 7 βが今のところ必要です。

HWND Create()
{
    if (FAILED(D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory)))
    {
        return 0;
    }
    return CreateWindow(
        reinterpret_cast<LPCTSTR>(static_cast<UINT_PTR>(wncClassAtom)),
        TEXT("Direct2D Test"), WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        0, 0, 0, this);
}

ウィンドウを作るのと前後して、Direct2Dのファクトリを作る必要があります。
この例ではそれだけで済んでいますが、MSのサンプルでは、ほかのオブジェクトを作る操作を併せてCreateDeviceIndependentResourcesという名前の関数になっており、例えば、画像ファイルの読み込みなどが行われています。

HRESULT CreateDeviceResources()
{
    HRESULT hr = S_OK;
    if (prthw == 0)
    {
        RECT rc;
        GetClientRect(hwnd, &rc);
        D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
        hr = pD2DFactory->CreateHwndRenderTarget(
            D2D1::RenderTargetProperties(),
            D2D1::HwndRenderTargetProperties(hwnd, size),
            &prthw);
        if (FAILED(hr))
        {
            return hr;
        }
        hr = prthw->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pscb);
        if (FAILED(hr))
        {
            return hr;
        }
    }
    return hr;
}

CreateDeviceIndependentResourcesと違って、CreateDeviceResourcesではデバイスに依存するオブジェクトの生成を行っています。ここでのデバイスとは、レンダーターゲット、ここではウィンドウということになります。もう少し読めば、CreateDeviceIndependentResourcesとCreateDeviceResourcesに分かれている実務上の理由が見えてきます。

なお、CreateWindowの引数hInstanceは、Windows 2000以上の条件を当然満たすためNULLで構いません。

void OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    OnRender();
    EndPaint(hwnd, &ps);
}

WM_PAINTが来たときの処理です。BeginPaint/EndPaintは呼んでいますが、それだけです。実際の描画処理は行いません。

void OnRender()
{
    HRESULT hr = CreateDeviceResources();
    if (prthw && !(prthw->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))
    {
        prthw->BeginDraw();
        prthw->Clear(D2D1::ColorF(D2D1::ColorF::White));
        Render(prthw);
        hr = prthw->EndDraw();
    }
    if (hr == D2DERR_RECREATE_TARGET)
    {
        DiscardDeviceResources();
    }
}

OnPaint内で呼んでいます。BeginDrawとEndDrawで挟んだ部分がGDIでのBeginPaintとEndPaintに相当し、つまりその中が実際の描画するコードです。prthwは、レンダーターゲット、およそデバイスコンテキストにあたるもので、CreateDeviceResourcesの中で作成しています。OnRenderが呼ばれる度にCreateDeviceResourcesも呼んでいますが、中でNULLチェックをしているので、毎回作り直されるわけではありません。

EndDrawの戻り値がD2DERR_RECREATE_TARGETだったときの処理、DiscardDeviceResourcesでは、CreateDeviceResourcesで作ったオブジェクトをすべて破棄しています。これらは次回のOnRender時に作り直されます。このために、CreateDeviceIndependentResourcesとCreateDeviceResourcesに分かれていると言った感じです。

void DiscardDeviceResources()
{
    pscb.Release();
    prthw.Release();
}

pscbとprthwは_com_ptr_tなので、pscb->Release()などとしてはいけないことに注意です。

void OnSize(HWND hwnd, UINT /*state*/, int cx, int cy)
{
    D2D1_SIZE_U size = {static_cast<UINT>(cx), static_cast<UINT>(cy)};
    if (prthw != 0)
    {
        HRESULT hr = prthw->Resize(size);
        if (FAILED(hr))
        {
            DiscardDeviceResources();
            InvalidateRect(hwnd, 0, FALSE);
        }
    }
}

もう1ヶ所、D2D関連の処理を行っているのがWM_SIZEのときです。サイズ変更を伝え、エラーだったらオブジェクトを破棄して、InvalidateRectしています。OnRender内で作り直させるつもりでしょう。

ほか、ここで取り上げなかった細かいことをいくつか。

  • WinMainの中でCoInitialize/CoUninitializeしています。
  • 私はWM_DESTROYでDiscardDeviceResourcesなどを呼ぶべきではないかと最初思いましたが、
    前述したとおり_com_ptr_tを使っているので、終了時は自動でReleaseされます。
  • ウィンドウクラスの登録時、WNDCLASS(EX)のwc.hbrBackgroundはNULLにしています。背景の塗り潰しもD2Dで行うため不要なのです。具体的には、BeginDrawの直後prthw->Clear(D2D1::ColorF(D2D1::ColorF::White));で背景を塗り潰しています。

スポンサード リンク

この記事のカテゴリ

  • ⇒ コッホ曲線: Direct2D
  • ⇒ コッホ曲線: Direct2D