Is it possible to use layers and effect together in Direct2d?
See the question and my original answer on StackOverflowOf course it's possible, as the doc says:
You use a layer by "pushing" it onto a render target. Subsequent drawing operations by the render target are directed to the layer
Here is some demo code (no error check for brevity):
#include <windows.h>
#include <wrl.h>
#include <d2d1.h>
#include <d2d1_1.h>
#include <d2d1effects.h>
#pragma comment(lib, "d2d1")
#pragma comment(lib, "dxguid")
using namespace Microsoft::WRL;
ComPtr<ID2D1SolidColorBrush> colorBrush;
ComPtr<ID2D1HwndRenderTarget> renderTarget;
ComPtr<ID2D1DeviceContext> deviceContext;
ComPtr<ID2D1Effect> effect;
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
renderTarget->BeginDraw();
renderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
// create & push layer
ComPtr<ID2D1Layer> layer;
auto hr = renderTarget->CreateLayer(nullptr, layer.GetAddressOf());
RECT rc; GetClientRect(hwnd, &rc);
renderTarget->PushLayer(D2D1::LayerParameters(D2D1::RectF(rc.right / 4, rc.bottom / 4, rc.right * 3 / 4, rc.bottom * 3 / 4)), layer.Get());
// draw checkerboard
const float squares = 8.0f;
auto dx = rc.right / squares;
auto dy = rc.bottom / squares;
for (auto i = 0; i < squares; i++)
{
for (auto j = i % 2; j < squares; j += 2)
{
renderTarget->FillRectangle(D2D1::RectF(dx * i, dy * j, dx * (i + 1), dy * (j + 1)), colorBrush.Get());
}
}
// draw fx (doesn't need input image)
deviceContext->DrawImage(effect.Get());
renderTarget->PopLayer();
hr = renderTarget->EndDraw();
}
break;
case WM_SIZE:
if (renderTarget)
{
// resize render target
RECT rc; GetClientRect(hwnd, &rc);
renderTarget->Resize(D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top));
// resize effect
auto hr = effect->SetValue(D2D1_TURBULENCE_PROP_SIZE, D2D1::Point2F(rc.right, rc.bottom));
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
// init & create window
WNDCLASSEX cls = { sizeof(WNDCLASSEX) };
cls.style = CS_HREDRAW | CS_VREDRAW;
cls.lpfnWndProc = WndProc;
cls.hInstance = hInstance;
cls.hCursor = LoadCursor(nullptr, IDI_APPLICATION);
cls.lpszClassName = L"D2DDemoApp";
RegisterClassEx(&cls);
auto hwnd = CreateWindow(cls.lpszClassName, L"Effect + Layer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hwnd, SW_SHOWNORMAL);
// create D2D factory
ComPtr<ID2D1Factory> factory;
auto hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, factory.GetAddressOf());
// create render target & device context
RECT rc;
GetClientRect(hwnd, &rc);
auto size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
hr = factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd, size), &renderTarget);
hr = renderTarget->QueryInterface(deviceContext.GetAddressOf());
// create fx & color
hr = deviceContext->CreateEffect(CLSID_D2D1Turbulence, effect.GetAddressOf());
hr = renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Orange), colorBrush.GetAddressOf());
// force compute size-related resources
SendMessage(hwnd, WM_SIZE, 0, 0);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
If you run as is you should see this:
But you can play with the code and remove the effect (turbulence aka noise) and/ or the layer: