- 발표에 꼭 들어가야 할 것들
- 더블버퍼링이 무엇인가?
- 더블버퍼링은 어떻게 하는가?
- 더블버퍼링이 무엇인가?
더블버퍼링이란>?? ¶
더블 버퍼링은 이미지를 화면에 바로 그리는 것이 아니라, 메모리(버퍼)에 먼저 그리고 화면에 나중에 그리는 방법이다. 더블 버퍼링은 화면의 깜빡임을 줄이고, 자연스러운 애니메이션을 위해서 많이 사용된다.
그렇다면 더블 버퍼링을 과연 언제 어떻게 사용해야 할까? 더블 버퍼링의 용도는 꼭 화면 깜박임을 제거하는데만 있는 것은 아니다. 내부 버퍼에서 틈틈이 작업을 할 수 있으므로 아이들(Idle) 시간을 활용하기 위해서 사용할 수도 있고 내부 버퍼를 외부 버퍼보다 더 크게 만들어 스크롤에 활용할 수도 있다.
여기서는 더블 버퍼링의 원리에 대해서만 이해하도록 하고 실무를 할 때 더블 버퍼링을 쓰면 좋겠다는 생각이 들면 적극적으로 활용해 보기 바란다. 다음 예제는 더블 버퍼링을 활용한 갱 화면이다. 갱(Gang) 화면이란 프로그램 제작자를 소개하는 용도를 가지며 일반적으로 숨겨져 있지만 제작자 자신을 표현한다는 면에 있어 다소 멋을 좀 부리는 경향이 있다. 이 예제는 배경 비트맵을 깔고 그 위에서 제작자 목록을 위로 스크롤하는 예를 보여준다.
~cpp #include "resource.h" HBITMAP hBit, hBaby; TCHAR szGang[]="Gang Version 1.0\r\n\r\n총 감독 : 김 정수\r\n" "개발자 : 김 상형\r\n사진 모델 : 김 한슬\r\n협찬 : 박 미영"; int my; void DrawBitmap(HDC hdc,int x,int y,HBITMAP hBit) { HDC MemDC; HBITMAP OldBitmap; int bx,by; BITMAP bit; MemDC=CreateCompatibleDC(hdc); OldBitmap=(HBITMAP)SelectObject(MemDC, hBit); GetObject(hBit,sizeof(BITMAP),&bit); bx=bit.bmWidth; by=bit.bmHeight; BitBlt(hdc,0,0,bx,by,MemDC,0,0,SRCCOPY); SelectObject(MemDC,OldBitmap); DeleteDC(MemDC); } void OnTimer() { RECT crt; HDC hdc,hMemDC; HBITMAP OldBit; HFONT font, oldfont; RECT grt; int i,j; GetClientRect(hWndMain,&crt); hdc=GetDC(hWndMain); if (hBit==NULL) { hBit=CreateCompatibleBitmap(hdc,crt.right,crt.bottom); } hMemDC=CreateCompatibleDC(hdc); OldBit=(HBITMAP)SelectObject(hMemDC,hBit); DrawBitmap(hMemDC,0,0,hBaby); SetBkMode(hMemDC,TRANSPARENT); font=CreateFont(30,0,0,0,0,0,0,0,HANGEUL_CHARSET,3,2,1, VARIABLE_PITCH | FF_ROMAN,"궁서"); oldfont=(HFONT)SelectObject(hMemDC,font); my--; if (my==20) { KillTimer(hWndMain,1); } SetTextColor(hMemDC,RGB(192,192,192)); for (i=-1;i<=1;i++) { for (j=-1;j<=1;j++) { SetRect(&grt,10+i,my+j,400+i,my+300+j); DrawText(hMemDC,szGang,-1,&grt,DT_WORDBREAK); } } SetTextColor(hMemDC,RGB(32,32,32)); SetRect(&grt,10,my,400,my+300); DrawText(hMemDC,szGang,-1,&grt,DT_WORDBREAK); SelectObject(hMemDC,oldfont); DeleteObject(font); SelectObject(hMemDC,OldBit); DeleteDC(hMemDC); ReleaseDC(hWndMain,hdc); InvalidateRect(hWndMain,NULL,FALSE); } LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) { HDC hdc,hMemDC; PAINTSTRUCT ps; HBITMAP OldBit; RECT crt; switch(iMessage) { case WM_CREATE: hBaby=LoadBitmap(g_hInst,MAKEINTRESOURCE(IDB_BITMAP1)); case WM_LBUTTONDOWN: SetTimer(hWnd,1,70,NULL); my=310; return 0; case WM_TIMER: OnTimer(); return 0; case WM_PAINT: hdc=BeginPaint(hWnd, &ps); GetClientRect(hWnd,&crt); hMemDC=CreateCompatibleDC(hdc); OldBit=(HBITMAP)SelectObject(hMemDC, hBit); BitBlt(hdc,0,0,crt.right,crt.bottom,hMemDC,0,0,SRCCOPY); SelectObject(hMemDC, OldBit); DeleteDC(hMemDC); EndPaint(hWnd, &ps); return 0; case WM_DESTROY: if (hBit) { DeleteObject(hBit); } DeleteObject(hBaby); PostQuitMessage(0); KillTimer(hWnd,1); return 0; } return(DefWindowProc(hWnd,iMessage,wParam,lParam)); }
좀 더 코드를 작성한다면 글자들이 오른쪽에서 왼쪽으로 한 줄씩 날라 오도록 할 수도 있고 점점 확대되는 모양으로 만들 수도 있다. 또는 약간의 애니메이션을 첨가한다거나 글자의 색상을 조작하여 Fade In, Fade Out 등의 장면 전환 효과를 낼 수도 있다. 아뭏든 더블 버퍼링을 쓰기만 하면 어떠한 모양도 깔끔하게 화면으로 구현할 수 있으므로 기발한 상상력을 발휘해 볼만하다.
~cpp public: CBitmap MemBitmap; CDC MemDC; CPoint Position;
~cpp CTestView::CTestView() { // TODO: add construction code here Position = CPoint(0, 0); }
~cpp void CTestView::OnDraw(CDC* pDC) { CTestDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); MemDC.FillSolidRect(0, 0, 1024, 768, RGB(255, 255, 255)); MemDC.SelectStockObject(NULL_BRUSH); for(int i = 5 ; i <= 10 ; i++) { MemDC.Ellipse(Position.x - i, Position.y - i, Position.x + i, Position.y + i); MemDC.Rectangle(Position.x + 10 - i, Position.y + 10 - i, Position.x + 10 + i, Position.y + 10 + i); } //pDC->StretchBlt(0, 0, 102, 77, &MemDC, 0, 0, 1024, 768, SRCCOPY); pDC->BitBlt(0, 0, 1024, 768, &MemDC, 0, 0, SRCCOPY); // TODO: add draw code for native data here }
~cpp int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; CDC *pDC = GetDC(); MemDC.CreateCompatibleDC(pDC); MemBitmap.CreateCompatibleBitmap(pDC, 1024, 768); MemDC.SelectObject(&MemBitmap); ReleaseDC(pDC); SetTimer(1, 1000, NULL); // TODO: Add your specialized creation code here return 0; } void CTestView::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default Invalidate(); Position.x += 2; Position.y += 1; CView::OnTimer(nIDEvent); }