화면의 이미지의 변경시에 깜빡임을 없애는 기법 ---- [[TableOfContents]] == 정의 == * 화면에 그림을 그릴때 일일히 그림을 화면에다 찍어주게 되면 깜박거림이 너무 심해진다. * 그래서 화면에 그림을 찍어줄 영역과 똑같은 크기의 가상의 버퍼를 만들어서 거기다 다 찍은다음 한번에 화면으로 옮겨주는 기법이다. * 해보면 굉장히 부드럽다. == 정의2 == 1. 화면에 그림 중 일부를 변경, 이동 할때(공의 움직임 따위) 기존의 화면을 일부 지우고 새로운 그림을 그려야 한다. * 만약 새로 그려질 그림이 기존의 지워질 화면에 그려진다면, 화면을 보는 사용자는 지워진 순간을 느끼게 된다.(개념 예제 참고) 이런 공백의 순간을 없애기 위하여 새로 그려질 그림과 배경을 동시에 그리는 기법이다. === 개념 예제 === A. 예1) 더블버퍼링 미사용시 {{{~cpp ▣▣▣▣▣ }}} 이전 이미지를 지운다. (배경만 나오는 공백 순간, 깜빡임 유발, 이 순간을 없애는 것이 더블 버퍼링 목적) {{{~cpp }}} 변경된 위치에 그림을 그린다. {{{~cpp ▣▣▣▣▣ }}} * 예2) 더블버퍼링 사용시 {{{~cpp ▣▣▣▣▣ }}} 배경과 함께 그림을 그린다. {{{~cpp ▣▣▣▣▣ }}} == 이 글을 남기게 된 이유 == * ["[Lovely]boy^_^"]가 뻑하면 짜논 소스를 날려버리는 위험한 넘이라서..--; 문서로 남겨놓으면 나중에 다시 할때 도움이 되지 않을까라는 생각--; == 예제 == * 제가 더블 버퍼링을 제대로 이해못했었더군요. 또 오랜만에 보니까 제가 뭘 써논건지도 모르겠다는 ..--; 다시 씁니다. 알카노이드가 없어져서 그냥 대충 만들어봤습니다. {{{~cpp class CArcanoidView : public CView { private: CArcanoidDoc* pDoc; CDC m_MemDC; // 메모리 DC CBitmap m_MemBitmap; // 메모리 비트맵 CDC m_BackgroundDC; // 배경 DC CBitmap m_BackgroundBitmap; // 배경 비트맵 CDC m_ShuttleDC; // 비행기 DC CBitmap m_ShuttleBitmap; // 비행기 비트맵 .... .... void CArcanoidView::OnInitialUpdate() { CView::OnInitialUpdate(); // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다. pDoc = GetDocument(); ASSERT_VALID(pDoc); CClientDC dc(this); m_MemDC.CreateCompatibleDC(&dc); // 현재 DC와 호환된 메모리 DC m_MemBitmap.CreateCompatibleBitmap(&dc, 1000, 700); // 호환되는 메모리 비트맵 m_MemDC.SelectObject(&m_MemBitmap); m_BackgroundBitmap.LoadBitmap(IDB_BACKGROUND); m_BackgroundDC.CreateCompatibleDC(&dc); m_BackgroundDC.SelectObject(&m_BackgroundBitmap); m_ShuttleDC.CreateCompatibleDC(&dc); m_ShuttleDC.SelectObject(m_ShuttleBitmap); } }}} * 이렇게 자원 할당 다 해주고, 더블 버퍼링을 하고 싶다고 한다면.. 일단 메모리 DC에 필요한걸 다 그린 다음에, 화면 DC로 BitBlt 해주는겁니다. 이게 더블 버퍼링인데.. 저는 잘못 이해하고 있었거든요. 개념은 알았지만.. 무슨 이상한 생각을 조금 더 해버려서.. 지난번의 그 이상한 코드가 나오게 되었던 겁니다. 훗날 본인도 못 알아보는..--; {{{~cpp // Timer가 호출하는 함수 내부 m_MemDC.BitBlt(0,0,1000,700,&m_BackgroundDC,0,0,SRCCOPY); // 메모리 DC로 BitBlt m_MemDC.BitBlt(x,y,width,height,&m_ShuttleDC,0,0,SRCCOPY); Invalidate(FALSE); }}} * 이렇게 Timer내부에서는 메모리 DC에다 다 그려주고, Invalidate(FALSE)를 호출합니다. FALSE 이거 중요합니다. {{{~cpp // OnDraw 함수 내부 pDC->BitBlt(0,0,1000,700,&m_MemDC,0,0,SRCCOPY); // 메모리 DC에 있는걸 화면 DC로 BitBlt }}} == Thread == ["데기"]: 소스코드는 저런 식으로 하면 더 보기 좋은 것 같아서 고쳐봤어. 맘에 안들면 다시 돌려놓길. ^^; 그런데 이거... 공이 있는 영역만 더블버퍼링 하는거야?[[BR]] ["[Lovely]boy^_^"]]: 앗. 무슨 그런 말씀을..^^;; 저야 고쳐 주시면 좋져. 공이랑 막대기 배경 처리 다 더블버퍼링 했는데여. 걍 예를 들라고..^^;[[BR]] ["데기"]: 난 화면 전체를 한꺼번에 랜더링한 다음에 버퍼를 바꿔주는 방식만 보아왔기에... 독특하다고 생각하는중. 움직이는 영역이 많지 않다면 효과적인 방법인듯해. 공을 그려주는 루틴이 CBall 에 있는것도 독특하고...[[BR]] ["1002"] : 더블 버퍼링을 하는 이유는, Main Memory <-> Main Memory 간의 메모리복사(Blt하는 것) 이 Main Memory -> Video Memory 간의 메모리 복사보다 빠르기 때문에 하죠. [[BR]] 화면 전체를 한꺼번에 렌더링 한 다음 버퍼를 바꿔주는 방식을 이야기하는 것 보면 아마 Page Fliping 을 이야기하시는듯. 단, 이것은 GDI 로는 불가능하지 않을까요? ^^ DC 핸들을 우리가 직접 조작할 수는 없는 것이고.. 말 그대로, 버퍼를 바꾼다는 것은 화면에 표시해 주는 메모리를 가리키는 포인터의 값을 바꾸는 거니까. Page Fliping 은 DOS나 DX에서는 가능할지 몰라도 GDI 에서는 불가능한 방법일것이라는 개인적 생각. (DC에 Select 되어있는 Bitmap 을 다시 셋팅해주는 방법은 어떨까. 한번도 안해봤지만. --;) [[BR]] 그리고, 전체 그리기 관련 루틴의 경우는 애매한데, 왜냐하면 저렇게 object 별로 그리기 루틴이 있는 경우 사람들 실수하는 것이.. 각각의 Draw에 더블버퍼링하고 또 메인 루틴부분에 더블버퍼링을 중복하는 경우가 있어서리.. (뭐. 요새는 하드웨어가 빨라서 별 속도 저하 없긴 한것 같지만.) 개인적으로는 각각의 Draw부분에는 일반적인 Blt. 그리고 Main 부분에 더블버퍼링 한번이 맞지 않을까 하는. 뭐.. 그냥 생각나서 주저리주저리. --; [[BR]] ["데기"] : ㅋㅋ, 표현이 조금 문제를 일으킬줄 알았어요. 화면 전체라 함은 클라이언트 영역을 얘기한 것이고, 버퍼를 바꾼다는 얘기는 포인터만 바꾼다는게 아니라 디바이스 버퍼 내용을 바꾼다는 얘기한거예요. 인수야, 내 애매한 표현땜에 페이지 플리핑이랑 헷갈리지 말어. ^^; [[BR]] ["neocoin"] : 결론은 요즘 하드웨어 짱이야? 인거야? [[BR]] ["snowflower"] : 음.. 나의 경우엔.. 화면 전체를 BufferDC에 그려서 나중에 그걸 DC로 옮겼는데... 좀 틀린걸까? [[BR]] ["데기"] : 보통의 경우는 선호가 하는 방법으로 하지. 렌더링되는 과정이 전혀 안 보이니까... [[BR]] ["zennith"] : 뜬금없는 소리이고, 고루한 이야기 입니다만, PCI 란 기술이 처음 소개되었을때 꽤 미래지향적인 기술로 각광받았던 것이 PCI bus mastering 이란 기술인데.. 무엇인고 하니, pci 채널로 연결되어있는 기기들끼리 서로의 메모리에 DMA 를 할 수 있었던 것이었죠. 대표적으로 이 기술이 사용된 예(라기보단 제가 알고있는 단 하나의 예)는 TV수신카드에서 사용되는 것이었는데요. TV 어플리케이션에서 TV 가 표시될 부분의 region 을 정해놓으면 TV 수신카드에서 그부분에 해당하는 비디오카드 메모리로 직접 쏴주는.. 그런 기술이었는데.. 더블버퍼링을 보니 갑자기 그 생각이 나는군요. 음.. 요즈음은 다들 agp 를 써서.. 저 pci bus mastering 이란 기술이 아직도 살아남아있는건지.. 잘 모르겠군요. ---- ["홈페이지분류"]