본문 바로가기

Library/Windows Programming

Window의 화면 깜빡임을 줄이는 방법

WM_PAINT 메세지를 처리할 때, API를 직접 사용한다면 BeginPaint()를 사용하여 디바이스 컨텍스트(DC)를 얻어내겠고, MFC를 사용한다면 CPaintDC를 통해 DC를 얻을 것이다. 그런데, 이 DC는 현재 보여지고 있는 스크린에 대한 DC를 얻어낸 것이기 때문에, 이것을 사용하여 GDI 조작을 하게되면 빠른 화면 갱신이 발생할 경우 깜빡임이 발생하게 된다. 이 깜빡임을 없애기 위해 다음 3가지를 주로 사용하게 된다.

첫번째, 윈도우가 자식 윈도우를 많이 가지고 있고, 부모 윈도우에 발생한 WM_PAINT 메세지가 자식들에게 전달되는 경우, 자식 윈도우들 역시 불필요한 WM_PAINT 메세지를 처리하는 것을 방지하는 WS_CLIPSIBLINGS 스타일을 준다. WS_CLIPSIBLINGS 스타일을 주면, 자식 윈도우 사이에서 겹쳐지는 영역에 대해 무효화가 발생해도 다시 그리지 않는다. 또는 WS_CLIPCHILDREN 스타일을 줄 수 있는데, 이것은 부모 윈도우의 무효화 영역에 자식 윈도우가 포함되어도 이들에게 WM_PAINT 메세지를 보내지 않는다.

두번째, WM_ERASEBKGND 메세지를 처리하지 않고 return FALSE 한다. WM_ERASEBKGND 메세지는, 화면 갱신이 발생했을때 윈도우 클래스에서 지정된 브러시를 사용해서 배경을 다시 그려준다. 즉, 화면 전체가 그려질 필요가 없는데도 배경 브러시를 사용하여 화면 전체를 다시 그리는데, 여기서 깜빡임이 발생할 수 있다. 따라서, 이 메세지를 처리하지 않고 그냥 FALSE해버린다면, 결과적으로 필요없는 배경을 그리지 않는다.

세번째, 이게 핵심인데, CPaintDC로 얻어진 DC에 직접 그리는 대신, CDC를 하나 생성하여 메모리 상에 필요한 작업을 한뒤, 스크린 DC에 BitBlt()를 통하여 한번에 전송하는 방법이다. 과정을 설명하면 다음과 같다.

1. 현재 화면 DC와 호환되는 CDC 개체를 하나 생성한다.
2. 현재 윈도우 사이즈에 맞는 메모리 비트맵을 하나 생성한다.
3. CDC에 SelectObject()로 생성된 비트맵을 붙인다.
4. 이 비트맵에 필요한 모든 작업을 한다.
5. BitBlt()를 사용하여 모든 데이터를 전송한다. 화면 DC가 타겟, CDC로 생성한 메모리 DC가 소스가 된다.

Sample Code:
CPaintDC dc(this);
CDC memdc;
memdc.CreateCompatibleDC(&dc);

CRect ClientRect;
GetClientRect(ClientRect);

CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, ClientRect.Width(), ClientRect.Height());
memdc.SelectObject(bitmap);

... 원하는 작업

dc.BitBlt(0, 0, ClientRect.Width(), ClientRect.Height(), &memdc, 0, 0, SRCCOPY);
memdc.DeleteDC();
membm.DeleteObject();


만약, WTL을 사용한다면 이런 목적으로 설계된, 훨씬 편리한 WTL::CMemoryDC를 사용할 수 있다. WTL::CMemoryDC는 위의 작업을 내부에서 자동으로 해준다.