api에서 paint를 호출 할때 사용하는 InvalidateRect 함수는 특정 영역(또는 전 화면)을 지우고 다시 그려주는 일을 하게 되는데, 그 순서는
기존의 이미지 -> 특정 영역(모든 영역)을 화면에서 지운다 -> 다시 그려준다
(물론 그려주기 위해서 MemDC가 필요 하다.)
이런 구조를 지니게 되는데,
그렇기 때문에 가끔씩 느껴지는 깜박임 현상을 보게 된다.
그때 그 문제를 해결하기 위해서 더블 버퍼링이라는 기술을 사용하게 되는데,
따로 더블버퍼링 함수를 제공한다거나 하는것이 아니기 때문에 처음엔 많이 해맬 수 있다.
기존에 영역을 지우고 다시 그려주는 것이 아닌,
hdc가 뿌려준 기존의 화면 -> 다음에 그려줄 이미지를 BackDC에 임시 저장 -> BackDC에 내용을 MemDC에 삽입한다. -> MemDC에 내용을 hdc로 넘긴다. -> hdc에서 화면에 뿌려준다.
의 순서로 돌아간다.
그렇기 때문에 화면의 깜박임 현상을 제거 할 수 있는데, 그리고 지우는게 아니라
그리고 덮허씌운다, 라는 표현이 맞을것 같다.
예제를 보도록 하자.
#include <windows.h>
#include "resource.h"
HBITMAP hIcon;
HBITMAP g_BackBitMap;
int x;
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("더블버퍼링");
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow )
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst = hInstance;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hbrBackground = (HBRUSH)GetStockObject( COLOR_WINDOW+1 );
WndClass.hCursor = LoadCursor( NULL, IDC_ARROW );
WndClass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
WndClass.hInstance = hInstance;
WndClass.lpfnWndProc = WndProc;
WndClass.lpszClassName = lpszClass;
WndClass.lpszMenuName = NULL;
WndClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &WndClass );
hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, (HMENU)NULL, hInstance, NULL );
ShowWindow( hWnd, nCmdShow );
while( GetMessage( &Message, NULL, 0, 0 ) )
{
TranslateMessage( &Message );
DispatchMessage( &Message );
}
return (int)Message.wParam;
}
LRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
HDC hdc, MemDC, BackDC;
HBITMAP hPreBit;
PAINTSTRUCT ps;
switch( iMessage )
{
case WM_CREATE:
SetTimer( hWnd, 1, 100, NULL );
x = 0;
// 비트맵 로드
hIcon = LoadBitmap( g_hInst, MAKEINTRESOURCE( IDB_BITMAP1 ) );
return 0;
case WM_TIMER:
InvalidateRect( hWnd, NULL, FALSE );
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
// hdc에 그려 주기 위헤 필요한 MemDC
MemDC = CreateCompatibleDC( hdc );
// 임시로 그림을 그리기 위해 BackBitMap을 생성한다.
g_BackBitMap = CreateCompatibleBitmap( hdc, 800, 600 );
// BackBitMap을 MemDC에 넣어 준다.
hPreBit = (HBITMAP)SelectObject( MemDC, g_BackBitMap );
// 마찬가지로 BackDC도 생성해 준다.
BackDC = CreateCompatibleDC( hdc );
// BackDC에 실제 이미지를 삽입한다.
SelectObject( BackDC, hIcon );
// BackDC에 있는 이미지를 MemDC로 옮긴다.
BitBlt( MemDC, 300+x, 300, 48, 48, BackDC, 0, 0, SRCCOPY );
// 그 후 BackDC를 삭제
DeleteObject( BackDC );
// 마지막으로 MemDC에 있는 것을 hdc로 옮긴다.
BitBlt( hdc, 0, 0, 800, 600, MemDC, 0, 0, SRCCOPY );
// 그 후 MemDC 이전값으로 복원
SelectObject( MemDC, hPreBit );
// 관련 데이터 삭제
DeleteObject( g_BackBitMap );
DeleteDC( MemDC );
EndPaint( hWnd, &ps );
return 0;
case WM_KEYDOWN:
switch( wParam )
{
case VK_LEFT:
--x;
break;
case VK_RIGHT:
++x;
break;
}
return 0;
}
return( DefWindowProc( hWnd, iMessage, wParam, lParam));
}
이 처럼, 해주면 된다.
여기서 더이상 어떻게 설명을 해야 할지 모르겠다.
이해가 가지 않는다면, bitmap을 그릴때 어떤식으로 그리는지 부터 다시 파악하는 것을 추천한다.
프로그램을 실행하면, 검은 화면에 깜박이지 않는 어떠한 리소스가 좌우로 움직이는 것을 확인 할 수 있다.
// 실행 화면
'프로그래밍 > API' 카테고리의 다른 글
API-애니메이션 (0) | 2010.05.10 |
---|---|
API-PlaySound (0) | 2010.05.10 |
API-ChildWnd (0) | 2010.04.24 |
API-WindowLong (0) | 2010.04.24 |
API-WNDCLASSEX (0) | 2010.04.24 |