1. 화면 그리기의 구조
    1. CWnd::RedrawWindow()
      - WM_PAINT 메시지를 발생시켜 윈도우를 다시 그리도록 한다.
    2. 윈도우의 크기를 변경하여 빠른 속도로 윈도우가 다시 그려지도록 하면 화면이 깜빡이거나 현상 발생
    3. WM_ERASEBKGND 메시지
      - WM_PAINT 메시지와 더불어 윈도우를 다시 그릴 때 발생
      - WM_ERASEBKGND 메시지 핸들러 함수는 윈도우의 배경을 그리는 코드 수행
      - 클라이언트 영역을 다시 그릴 때 WM_PAINT 메시지에 앞서 WM_ERASEBKGND 메시지 발생
        (비클라이언트 영역을 다시 그리는 메시지는 WM_NCPAINT)
      - 윈도우가 깜빡이는 주요 원인 중 하나
      - WM_ERASEBKGND 메시지 핸들러 함수를 등록하여 상위 클래스의 멤버를 호출하지 않도록 하면 깜박임이 줄어듬
    4. BOOL CRedrawDemoView::OnEraseBkgnd(CDC* pDC) 
      {
          return TRUE;
          // return CView::OnEraseBkgnd(pDC);
      }
      

    - 위 기법과 더블 버퍼링을 함께 사용하면 깜빡임이 전혀 없는 인터페이스를 구현 가능

  2. 눈속임의 미학
    1. 클라이언트 뷰의 자식 윈도우로 있는 윈도우가 배경을 그릴 때(WM_ERASEBKGND 메시지가 발생했을 때) 자신의 부모 윈도우의 OnEraseBkgnd() 메시지 핸들러 함수를 명시적으로 호출하여 배경 이미지 획득 후 이미지 출력하여 투명해 보이는 효과 얻음
    2. 클라이언트 뷰의 OnEraseBkgnd() 함수
      - 전달받은 DC에 이미지를 로드하여 출력
    3. BOOL CTransparentDemoView::OnEraseBkgnd(CDC* pDC)
      {
      	CRect Rect;
      	GetClientRect(&Rect);
      	pDC->FillSolidRect(&Rect, RGB(255, 255, 255));
      
      	CImage ImageBackground;
      	ImageBackground.LoadFromResource(AfxGetInstanceHandle(), IDB_Background);
      	ImageBackground.BitBlt(pDC->m_hDC, 0, 0);
      
      	return TRUE;
      //	return CView::OnEraseBkgnd(pDC);
      }
    4. 클라이언트 뷰의 자식 윈도우의 OnEraseBkgnd() 함수
      - 메모리 DC에 부모 윈도우의 배경을 획득 후 자식 윈도우에 출력
    5. BOOL CTransparentWnd::OnEraseBkgnd(CDC* pDC)
      {
      	// 자식 윈도우의 왼쪽 위가 부모 윈도우 기준으로 어딘지 좌표 계산
      	CRect Rect, ParentRect;
      	GetClientRect(&Rect);
      	GetParent()->GetClientRect(&ParentRect);
      
      	CPoint ptLeftTop = CPoint(0, 0);
      	ClientToScreen(&ptLeftTop);
      	GetParent()->ScreenToClient(&ptLeftTop);
      
      	// 메모리 DC에 적절한 CBitmap 클래스 객체 생성하여 선택
      	CDC MemDC;
      	CBitmap Bmp;
      
      	MemDC.CreateCompatibleDC(NULL);
      	Bmp.CreateBitmap(ParentRect.Width(), ParentRect.Height(),
      						MemDC.GetDeviceCaps(PLANES),
      						MemDC.GetDeviceCaps(BITSPIXEL), NULL);
      	CBitmap* pOldBmp = MemDC.SelectObject(&Bmp);
      
      	// 메모리 DC의 핸들을 메시지 파라미터로 부모 윈도우에 WM_ERASEBKGND 메시지 송신
      	GetParent()->SendMessage(WM_ERASEBKGND, (WPARAM)MemDC.m_hDC);
      	pDC->BitBlt(0, 0, Rect.Width(), Rect.Height(),
      						&MemDC, ptLeftTop.x, ptLeftTop.y, SRCCOPY);
      
      	MemDC.SelectObject(pOldBmp);
      
      	return TRUE;
      //	return CWnd::OnEraseBkgnd(pDC);
      }
      
  3. 더블 버퍼링(Double Buffering)
    1. 윈도우의 깜빡임을 제거하는 최선의 방법
    2. 메모리 DC(버퍼 DC)에 모든 그리기 작업을 수행한 후, 메모리 DC의 내용을 화면 DC로 복사하는 기법
    3. 그리는 과정이 화면에 출력되지 않으므로 깜빡임이 사라짐
    4. CBufferDC 클래스
      - BufferDC.h
    5. class CBufferDC : public CDC  
      {
      
      private:
      	CBufferDC() { }
      	CBufferDC(const CBufferDC &src) { }
      	CBufferDC& operator=(const CBufferDC &src) { }
      
      protected:
      	BOOL Attach(HDC hDC);
      	HDC Detach();
      
      private:
      	CWnd*			m_pParent;						//대상 윈도우에 대한 포인터
      	CDC*			m_pTarget;						//대상 윈도우 DC에 대한 포인터
      	PAINTSTRUCT		m_PaintStruct;
      	CRect			m_RcClient, m_RcWindow;			//대상 윈도우의 크기 정보
      
      	CDC				m_MemoryDC;						//버퍼 DC
      	CBitmap			m_MemoryBmp, *m_pOldMemoryBmp;	//버퍼링을 위한 비트맵
      
      public:
      	CBufferDC(CWnd *pParent);
      	~CBufferDC();
      
      public:
      	inline CRect ClientRect() const { return m_RcClient; }
      	inline CRect WindowRect() const { return m_RcWindow; }
      	inline CRect UpdateRect() const { return m_PaintStruct.rcPaint; }
      
      	operator HDC() const { return m_MemoryDC.m_hDC; }       //  DC handle for API functions
      };
      

      - BufferDC.cpp

      //////////////////////////////////////////////////////////////////////
      // Construction/Destruction
      //////////////////////////////////////////////////////////////////////
      CBufferDC::CBufferDC(CWnd *pParent)
          : m_pParent(pParent)
      {
      	ASSERT(pParent);
      
      	//대상 윈도우에 대한 정보를 수집한다.
      	m_pTarget = m_pParent->BeginPaint(&m_PaintStruct);
      	m_pParent->GetClientRect(&m_RcClient);
      	m_pParent->GetWindowRect(&m_RcWindow);
      
      	//대상 윈도우에 대한 DC를 생성한다.
      	m_MemoryDC.CreateCompatibleDC(m_pTarget);
      	//대상 DC에 대한 메모리 비트맵을 생성하여 Select 한다.
      	m_MemoryBmp.CreateBitmap(m_RcClient.Width(), m_RcClient.Height(), 
      		m_MemoryDC.GetDeviceCaps(PLANES),
      		m_MemoryDC.GetDeviceCaps(BITSPIXEL), 0);
      	m_pOldMemoryBmp = m_MemoryDC.SelectObject(&m_MemoryBmp);
      
      	//메모리 버퍼에 Attach한다.
      	Attach(m_MemoryDC);
      }
      
      //////////////////////////////////////////////////////////////////////
      CBufferDC::~CBufferDC()
      {
      	//메모리 DC의 내용을 대상 윈도우에 출력한다.
      	//내부적으로 비트맵에 출력한 것이므로 해당 비트맵을 1:1로 복사한다.
      	m_pTarget->BitBlt(
      		m_PaintStruct.rcPaint.left,
      		m_PaintStruct.rcPaint.top, 
      		m_PaintStruct.rcPaint.right - m_PaintStruct.rcPaint.left, 
      		m_PaintStruct.rcPaint.bottom - m_PaintStruct.rcPaint.top, 
      		&m_MemoryDC,
      		m_PaintStruct.rcPaint.left,
      		m_PaintStruct.rcPaint.top, SRCCOPY);
      
      	m_MemoryDC.SelectObject(m_pOldMemoryBmp);
      	m_pParent->EndPaint(&m_PaintStruct);
      
      	Detach();
      }
      
      //////////////////////////////////////////////////////////////////////
      BOOL CBufferDC::Attach(HDC hDC)
      {
          return CDC::Attach(hDC);
      }
      
      //////////////////////////////////////////////////////////////////////
      HDC CBufferDC::Detach()
      {
          return CDC::Detach();
      }
      
    6. WM_ERASEBKGND 메시지 핸들러 함수를 등록하여 아무런 처리도 하지 않도록 코드를 수정하고 OnPaint() 함수에서 CBufferDC 클래스를 활용하여 더블 버퍼링을 구현한다면 화면 깜빡임을 완벽히 해결할 수 있음
      - 1.(3) 참조

+ Recent posts