【问题标题】:Direct2D erasing the background with WM_ERASEBKGND?Direct2D 用 WM_ERASEBKGND 擦除背景?
【发布时间】:2020-09-02 08:46:48
【问题描述】:

看来我学得很辛苦,如果我们使用 Direct2D 进行绘画,则不能使用WM_ERASEBKGND 来处理背景擦除,但我可能错了,所以这里是问题:

我用空画笔注册我的绘图窗口,让我手动处理背景擦除:

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

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

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

那么WM_ERASEBKGND 消息的意义何在?为什么BeginDraw() 会忽略我来自WM_ERASEBKGND 的背景并将背景变为黑色?

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

【问题讨论】:

标签: c++ winapi directx


【解决方案1】:

使用 GDI 的经典窗口渲染是围绕对单个图像缓冲区的增量更新进行的。通常,重绘(部分透明)窗口需要保护对图像缓冲区的写入访问权限、剪切更新区域、请求父窗口绘制其背景、使该窗口绘制其背景,然后最终使该窗口绘制一些更新的内容。

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

【讨论】:

  • 谢谢你的具体回答,我有一些感觉一定是这样但不知道为什么,你的确认是这个问题的结束。
猜你喜欢
  • 2015-05-08
  • 2015-11-13
  • 1970-01-01
  • 2011-07-09
  • 1970-01-01
  • 2010-10-01
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多