Reputation: 2184
It seems like I've learn it hard way, if we use Direct2D for painting then one must not use WM_ERASEBKGND
to handle background erasing, but I may be wrong on this so here is the problem:
I register my drawing window with null brush to let me manually handle background erasure:
WNDCLASSEXW wcex;
wcex.hbrBackground = nullptr;
// etc...
This will let system generate WM_ERASEBKGND
and here is the implementation for background erasure which btw. works just fine:
// 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;
}
OK, background has been set up, now let's paint over that background! See comment in code where background is conditionally erased.
// 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);
}
}
Ok, this does not work, because once BeginDraw()
is called the background will turn black and our background which was erased during WM_ERASEBKGND
is gone!
The check if (ps.fErase)
inside OnPaint()
handler will not be executed because it is FALSE
, it is false because background has been erased inside the WM_ERASEBKGND
The result is that background is completely black, and when I resize the window I can see in miliseconds my original background which soon turns into black and ofc. the screen flickers due to color difference.
So the question is, how do I retain background which was set up in WM_ERASEBKGND
?
Is returning FALSE
from WM_ERASEBKGND
really the only solution here?
What's the point of WM_ERASEBKGND
message then? and why does the BeginDraw()
ignore my background from WM_ERASEBKGND
and turn the background black?
Other drawings are visible ofc. it's just that background is lost.
Upvotes: 0
Views: 400
Reputation: 37549
The classic window rendering with GDI is revolving around incremental updates to the single image buffer. Typically redrawing a (partially transparent) window would require securing write access to the image buffer, clipping update region, requesting parent window to draw its background, making this window draw its background and then finally making this window draw some updated content.
With DirectX things are different. Typically rendering is at least dual buffered. And the content of those buffers is swapped rather than copied. That is content of the previous frame (that is currently presented) is not available when rendering and then gets discarded after buffers are swapped. So rendering is performed from scratch every time while handling of WM_ERASEBKGND
is simplified to doing nothing and just returning TRUE
to indicate that background has been erased so we won't get this message repeated.
Upvotes: 3