Direct2D 用 WM_ERASEBKGND 擦除背景?

Direct2D erasing the background with WM_ERASEBKGND?

看来我学得很辛苦,如果我们使用 Direct2D 进行绘画,那么一定不能使用 WM_ERASEBKGND 来处理背景擦除,但我在这一点上可能是错误的,所以这是问题所在:

我用空画笔注册我的画 window 让我手动处理背景擦除:

WNDCLASSEXW wcex;

wcex.hbrBackground = nullptr;
// etc...

这将使系统生成 WM_ERASEBKGND,这里是背景擦除的实现,顺便说一句。工作正常:

// case WM_ERASEBKGND:
BOOL DrawableWindow::OnEraseBackground(WPARAM wParam)
{
    HRESULT hr = CreateGraphicsResources();

    if (SUCCEEDED(hr))
    {
        RECT rc{ };
        GetClientRect(mhWnd, &rc);
        const HDC hDC = reinterpret_cast<HDC>(wParam);

        // ID2D1DCRenderTarget
        hr = mpContextRender->BindDC(hDC, &rc);

        if (FAILED(hr))
        {
            DiscardGraphicsResources();
            return FALSE;
        }

        mpContextRender->BeginDraw();
        mpContextRender->Clear(mBackground); // D2D1::ColorF background color (ie. green)
        hr = mpContextRender->EndDraw();

        if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET)
        {
            DiscardGraphicsResources();
            return FALSE;
        }

        return TRUE;
    }

    // NOTE: Return value sets the PAINTSTRUCT.fErase
    // which the OnPaint() handler can use to determine if painting the backgound is needed
    return FALSE;
}

好了,背景已经设置好了,现在让我们在背景上画画吧! 请参阅有条件擦除背景的代码中的注释。

// case WM_PAINT:
void DrawableWindow::OnPaint()
{
    HRESULT hr = CreateGraphicsResources();

    if (SUCCEEDED(hr))
    {
        PAINTSTRUCT ps{ };

        BeginPaint(mhWnd, &ps);

        // ID2D1HwndRenderTarget
        mpWindowRender->BeginDraw();

        // ***THE PROBLEM IS HERE***
        // This value is nonzero if WM_ERASEBKGND erased the background!
        if (ps.fErase)
        {
            // We clear client area only if WM_ERASEBKGND returned FALSE!
            mpWindowRender->Clear(mBackground); // D2D1::ColorF background color (ie. green)
        }

        // We do drawing routuine here...
        // ...
        // done painting

        hr = mpWindowRender->EndDraw();

        if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET)
        {
            DiscardGraphicsResources();
        }

        EndPaint(mhWnd, &ps);
    }
}

好的,这行不通,因为一旦调用 BeginDraw(),背景就会变黑,我们在 WM_ERASEBKGND 期间删除的背景就消失了!

OnPaint() 处理程序中的检查 if (ps.fErase) 将不会执行,因为它是 FALSE,它是错误的,因为背景已在 WM_ERASEBKGND[=28] 中被删除=]

结果是背景完全是黑色的,当我调整 window 大小时,我可以在几毫秒内看到我的原始背景很快变成黑色和 OFC。由于色差导致屏幕闪烁

所以问题是,如何保留在 WM_ERASEBKGND 中设置的背景? 从 WM_ERASEBKGND 返回 FALSE 真的是这里唯一的解决方案吗?

那么 WM_ERASEBKGND 消息有什么意义呢?为什么 BeginDraw()WM_ERASEBKGND 忽略我的背景并将背景变成黑色?

其他图纸可见。只是背景丢了

使用 GDI 的经典 window 渲染围绕着对单个图像缓冲区的增量更新。通常重绘(部分透明)window 需要确保对图像缓冲区的写入访问权限、裁剪更新区域、请求父 window 绘制其背景、使此 window 绘制其背景然后终于让这个 window 绘制了一些更新的内容。

对于 DirectX,情况有所不同。通常渲染至少是双缓冲的。这些缓冲区的内容被交换而不是被复制。也就是说,前一帧的内容(当前呈现的)在渲染时不可用,然后在交换缓冲区后被丢弃。因此每次渲染都是从头开始执行的,而 WM_ERASEBKGND 的处理被简化为什么也不做,只返回 TRUE 以指示背景已被擦除,因此我们不会重复此消息。