Direct2D Effectの利用

下記記事にて、ID2D1DeviceContextを利用して楕円を描画するところまで実装しました。
Direct2D導入(ID2D1DeviceContext) - 何でもプログラミング

今回はID2D1DeviceContextで利用できるようになったEffectを実装してみたいと思います。

上記記事で描画した楕円画像に、赤と青を入れ替えるEffectを適用してみます。

f:id:any-programming:20171027153731p:plain

記事中に特に説明のない関数は、上記記事を参照してみてください。

Effect作成

既存のEffectを利用して、赤と青を入れ替えるEffectを作成します。

#include <initguid.h>
#include <d2d1_1.h>
#pragma comment(lib, "d2d1.lib")

ComPtr<ID2D1Effect> CreateEffect(ID2D1DeviceContext* context)
{
    ComPtr<ID2D1Effect> effect;
    AssertHR(context->CreateEffect(CLSID_D2D1ColorMatrix, &effect));

    D2D1_MATRIX_5X4_F matrix = D2D1::Matrix5x4F(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0);
    AssertHR(effect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX, matrix));

    return effect;
}


OffscreenBufferの作成

Effectの入力としてBackBufferは利用できないため、一時描画先としてOffscreenBufferを作成します。

ComPtr<ID2D1Bitmap1> CreateTargetBitmap(ID2D1DeviceContext* context, D2D1_SIZE_U size)
{
    auto prop = D2D1::BitmapProperties1(
        D2D1_BITMAP_OPTIONS_TARGET,
        D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)
    );

    ComPtr<ID2D1Bitmap1> bitmap;
    AssertHR(context->CreateBitmap(size, nullptr, size.width * 4, prop, &bitmap));

    return bitmap;
}


アプリケーションコード

まずOffscreenBufferに楕円を描画し、次にEffectを適用した結果をBackBufferに描画しています。

サイズが変更された場合は、BackBuffer、OffscreenBuffer共に作り直しています。(BackBufferを解放しておかないと、ResizeBuffersで例外が発生します。)

ComPtr<ID2D1DeviceContext> g_context;
ComPtr<IDXGISwapChain1> g_swapChain;
ComPtr<ID2D1Bitmap1> g_backBuffer;
ComPtr<ID2D1Bitmap1> g_offscreenBuffer;
ComPtr<ID2D1Effect> g_effect;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_PAINT:
        {
            g_context->BeginDraw();

            g_context->SetTarget(g_offscreenBuffer.Get());

            g_context->Clear(D2D1::ColorF(D2D1::ColorF::LightYellow));

            // 楕円描画
            D2D1_SIZE_F size = g_context->GetSize();
            float rx = size.width / 2;
            float ry = size.height / 2;
            D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(rx, ry), rx, ry);
            ComPtr<ID2D1SolidColorBrush> brush;
            AssertHR(g_context->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Orange), &brush));
            g_context->FillEllipse(ellipse, brush.Get());

            // Effect適用(赤青いれかえ)
            g_context->SetTarget(g_backBuffer.Get());
            g_effect->SetInput(0, g_offscreenBuffer.Get());
            g_context->DrawImage(g_effect.Get());
            g_effect->SetInput(0, nullptr);

            AssertHR(g_context->EndDraw());
            g_context->SetTarget(nullptr);
                
            DXGI_PRESENT_PARAMETERS params = { 0 };
            AssertHR(g_swapChain->Present1(1, 0, &params));
        }
        return 0;
    case WM_SIZE:
        g_backBuffer = nullptr;
        AssertHR(g_swapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0)); // サイズ0で、自動でWindowのサイズが設定される
        g_backBuffer = CreateBackBufferBitmap(g_context.Get(), g_swapChain.Get());
        g_offscreenBuffer = CreateTargetBitmap(g_context.Get(), g_backBuffer->GetPixelSize());
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}
int main()
{
    HWND hwnd = CreateHWND(WndProc);

    auto dxgiDevice = CreateDXGIDevice();
    g_context = CreateD2DContext(dxgiDevice.Get());
    g_swapChain = CreateSwapChain(dxgiDevice.Get(), hwnd);
    g_backBuffer = CreateBackBufferBitmap(g_context.Get(), g_swapChain.Get());
    g_offscreenBuffer = CreateTargetBitmap(g_context.Get(), g_backBuffer->GetPixelSize());
    g_effect = CreateEffect(g_context.Get());

    Run(hwnd);

    return 0;
}