Direct2D 独自Effectの作成

下記の記事にて、Direct2Dの組み込みのEffectを利用してみました。
Direct2D Effectの利用 - 何でもプログラミング

今回は独自のEffectを作成してみたいと思います。

組み込みで用意されているOffset効果を利用して、描画結果をずらしてみます。

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

本記事は上記記事をベースとしていますので、特に言及のない関数は上記記事を参照してみてください。

独自Effectクラス

ID2D1EffectImplを実装して独自のEffectクラスを作成します。

基本的にはInitialize関数とRegister関数の実装がメインとなります。

#include <initguid.h>
#include <d2d1effectauthor.h>

DEFINE_GUID(CLSID_MyEffect, ...); // 独自のGUIDを定義してください。

class MyEffect : public ID2D1EffectImpl
{
public:
    // ここから3つの関数は、ID2D1EffectImplに必要な関数です。
    IFACEMETHODIMP Initialize(_In_ ID2D1EffectContext* pContextInternal, _In_ ID2D1TransformGraph* pTransformGraph)
    {
        // Offsetを行う効果(組み込み)の作成&登録
        ComPtr<ID2D1OffsetTransform> transform;
        auto hr = pContextInternal->CreateOffsetTransform(D2D1::Point2L(10, 10), &transform);
        if (SUCCEEDED(hr))
            hr = pTransformGraph->SetSingleTransformNode(transform.Get());

        return hr;
    }
    IFACEMETHODIMP PrepareForRender(D2D1_CHANGE_TYPE changeType)
    {
        return S_OK;
    }
    IFACEMETHODIMP SetGraph(_In_ ID2D1TransformGraph* pGraph)
    {
        return S_OK;
    }

    // ここから2つのstatic関数は、このクラスをID2D1Factory1に登録するのに利用します。(関数名は何でも大丈夫です。)
    static HRESULT Register(_In_ ID2D1Factory1* pFactory)
    {
        // 1画像を入力とするEffectの登録
        auto xml = 
            L"<?xml version = '1.0'?>                                                       "
            L"<Effect>                                                                      "
            L"    <Property name='DisplayName' type='string' value='MyEffect' />            "
            L"    <Property name='Author' type='string' value='Author' />                   "
            L"    <Property name='Category' type='string' value='Category' />               "
            L"    <Property name='Description' type='string' value='Description' />         "
            L"    <Inputs>                                                                  "
            L"        <Input name='Source' />                                               "
            L"    </Inputs>                                                                 "
            L"</Effect>                                                                     "
            ;
        return pFactory->RegisterEffectFromString(CLSID_MyEffect, xml, nullptr, 0, CreateEffect);
    }
    static HRESULT __stdcall CreateEffect(_Outptr_ IUnknown** ppEffectImpl)
    {
        *ppEffectImpl = (ID2D1EffectImpl*)(new MyEffect());

        if (*ppEffectImpl == nullptr)
            return E_OUTOFMEMORY;

        return S_OK;
    }

    // ここからはCOMに必要な関数です。特別な実装はありません。
    IFACEMETHODIMP_(ULONG) AddRef()
    {
        m_refCount++;
        return m_refCount;
    }
    IFACEMETHODIMP_(ULONG) Release()
    {
        m_refCount--;

        if (m_refCount == 0)
            delete this;

        return m_refCount;
    }
    IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _Outptr_ void** ppOutput)
    {
        *ppOutput = nullptr;
        HRESULT hr = S_OK;

        if (riid == __uuidof(ID2D1EffectImpl))
            *ppOutput = (ID2D1EffectImpl*)this;
        else if (riid == __uuidof(IUnknown))
            *ppOutput = this;
        else
            hr = E_NOINTERFACE;

        if (*ppOutput != nullptr)
            AddRef();

        return hr;
    }
private:
    LONG m_refCount;
    MyEffect() : m_refCount(1) {}
};


Effectの作成

独自のGUIDでCreateEffectします。

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

    return effect;
}


main関数

作成したEffectの登録が追記されています。

ID2D1Factory1を作成した時に同時に登録したほうが簡潔だと思われます。

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());

    ComPtr<ID2D1Factory> factory;
    g_context->GetFactory(&factory);
    ComPtr<ID2D1Factory1> factory1;
    AssertHR(factory.As(&factory1));
    AssertHR(MyEffect::Register(factory1.Get()));

    g_effect = CreateEffect(g_context.Get());

    Run(hwnd);

    return 0;
}