05 августа 2008

Easy double-buffering

Типичный код отрисовки окна:
LRESULT CMyWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CRect ClientRect;
GetClientRect(ClientRect);
WTL::CPaintDC DC(m_hWnd);

DrawBackground(DC, ClientRect);
DrawContent(DC, ClientRect);
...
}
Если все это перерисовывается довольно часто, то возникает мерцание. Например при изменении мышкой размера окна — на каждое движение мыши идет перерисовка, сначала рисуется фон, потом контент, легко застать момент когда фон отрисовался, а остальное - нет.

Типичный паттерн чтобы этого избежать — рисовать во время обратного хода луча использовать double buffering. Думаете, это сложно? На самом деле — как два пальца. Понадобится всего лишь небольшой вспомогательный класс:
LRESULT CMyWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CRect ClientRect;
GetClientRect(ClientRect);
WTL::CPaintDC PaintDC(m_hWnd);
CMemDC DC(PaintDC, ClientRect);

DrawBackground(DC, ClientRect);
DrawContent(DC, ClientRect);
...
}

Сам "помощник" выглядит так:
class CMemDC : public WTL::CDC
{
public:
CMemDC(HDC PaintDC, const RECT &Area) : _PaintDC(PaintDC), _Area(Area)
{
CreateCompatibleDC(PaintDC);

POINT Viewport;
::GetViewportOrgEx(PaintDC, &Viewport);
SetViewportOrg(Viewport);

_Bitmap.CreateCompatibleBitmap(PaintDC, _Area.Width(), _Area.Height());
SelectBitmap(_Bitmap);
};

~CMemDC()
{
POINT OriginalViewport;
SetViewportOrg(0, 0);
_PaintDC.SetViewportOrg(0, 0, &OriginalViewport);
_PaintDC.BitBlt(_Area.left, _Area.top, _Area.Width(), _Area.Height(), m_hDC, 0, 0, SRCCOPY);
_PaintDC.SetViewportOrg(OriginalViewport.x, OriginalViewport.y);
}

private:
WTL::CBitmap _Bitmap;
WTL::CDCHandle _PaintDC;
CRect _Area;
};

3 комментария:

e14 комментирует...

А еще в WTL-е есть CDoubleBufferImpl и CDoubleBufferWindowImpl.

Raider комментирует...

даже больше: оказывается там уже есть CMemoryDC

Мазов Гоша aka Carc комментирует...

Век живи, век учись!
Так вот ты какая двойная буферизация :)
По большей части, когда шаманю с собственной прорисовкой всегда (нет, пожалуй, будем честными до конца - весьма часто) делаю именно так с контекстом в памяти...
А то всё ходил, мучался - что за "двойная буферизация", как я так отстал от жисти :)
Спасибо за статью!