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
以指示背景已被擦除,因此我们不会重复此消息。
看来我学得很辛苦,如果我们使用 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
以指示背景已被擦除,因此我们不会重复此消息。