似乎我已经学到了很难的方法,如果我们使用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
中删除的背景消失了!
if (ps.fErase)
处理程序中的检查OnPaint()
将不会执行,因为它是FALSE
,这是错误的,因为已删除WM_ERASEBKGND
内部的背景
结果是背景完全是黑色的,当我调整窗口大小时,我可以在几毫秒内看到我的原始背景,很快就会变成黑色和ofc。屏幕因色差而闪烁。
所以问题是,如何保留WM_ERASEBKGND
中设置的背景?从FALSE
返回WM_ERASEBKGND
真的是这里唯一的解决方案吗?
那么WM_ERASEBKGND
消息有什么意义?为什么BeginDraw()
会忽略WM_ERASEBKGND
中的背景,而将背景变成黑色?
其他图可见。只是背景丢失了。
使用GDI的经典窗口渲染围绕着对单个图像缓冲区的增量更新。通常,重新绘制(部分透明)的窗口将需要确保对图像缓冲区的写入访问权限,剪切更新区域,请求父窗口绘制其背景,使该窗口绘制其背景,然后最终使该窗口绘制一些更新的内容。
与DirectX有所不同。通常,渲染至少是双重缓冲的。这些缓冲区的内容被交换而不是被复制。那就是上一帧(当前呈现)的内容在渲染时不可用,然后在交换缓冲区后被丢弃。因此,每次从头开始执行渲染,而将WM_ERASEBKGND
的处理简化为什么都不做,仅返回TRUE
表示背景已被擦除,因此我们不会重复此消息。