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) 참조
  1. 윈도우 영역
    1. 윈도우 영역의 구분
      1. 클라이언트(Client) 영역
      2. 비클라이언트(Non-client) 영역
    2. TextRgn 예제
      - 별도의 영역 만들기
    3. void CTextRgnView::OnPaint()

      {

      CPaintDC dc(this); // device context for painting

      // TODO: 여기에 메시지 처리기 코드를 추가합니다.

      // 그리기 메시지에 대해서는 CView::OnPaint()을(를) 호출하지 마십시오.

      //임의의 두 사각형을

      //각기 다른 색상(붉은색과 회색)으로 칠한다.

      CRect rectLeft = CRect(50, 50, 250, 150);

      CRect rectRight = CRect(250, 50, 450, 150);

      dc.FillSolidRect(&rectLeft, RGB(192, 0, 0));

      dc.FillSolidRect(&rectRight, RGB(192, 192, 192));


      //앞서 만든 두 사각형을 별도의 영역으로 만든다.

      CRgn rgnLeft, rgnRight;

      rgnLeft.CreateRectRgnIndirect(rectLeft);

      rgnRight.CreateRectRgnIndirect(rectRight);


      LOGFONT lf;

      ::ZeroMemory(&lf, sizeof(lf));

      lf.lfHeight = 72;

      wsprintf(lf.lfFaceName, _T("%s"), _T("Arial Black"));

      CFont NewFont;

      NewFont.CreateFontIndirect(&lf);

      CFont* pOldFont = dc.SelectObject(&NewFont);


      dc.SetBkMode(TRANSPARENT);

      //dc.TextOut(60, 65, TEXT("TEST STRING"));


      //왼쪽 영역을 선택하여 문자열을 출력한다.

      dc.SetTextColor(RGB(192, 192, 192));

      dc.SelectClipRgn(&rgnLeft);

      dc.TextOut(60, 65, TEXT("TEST STRING"));


      //오른쪽 영역을 선택하여 문자열을 출력한다.

      dc.SetTextColor(RGB(192, 0, 0));

      dc.SelectClipRgn(&rgnRight);

      dc.TextOut(60, 65, TEXT("TEST STRING"));


      //영역해제

      dc.SelectClipRgn(NULL);


      dc.SelectObject(pOldFont);

      }

      • CreateRectRgnIndirect()
        • 인자로 주어진 RECT 구조체의 정보를 근거로 새로운 윈도우 영역을 생성
      • CreateRectRgn()
        • CreateRectRgnIndirect()와 동일하지만 RECT 구조체의 주소가 아니라 네 개의 int형 값을 받으며, 각각은 사각형을 그리기 위한 좌표
      • SelectClipRgn()
        • 인자로 주어진 CRgn 클래스 객체로 윈도우 영역을 변경
        • SelectClipRgn() 함수 호출 코드를 수행한 직후에는 특정 영역을 벗어난 그리기 결과는 화면에 출력되지 않음
        • SelectClipRgn(NULL) 코드로써 영역해제
    4. FrameRgn 예제
      - 영역을 조합하기
    5. int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

      {

      if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

      return -1;


      if (!m_wndStatusBar.Create(this))

      {

      TRACE0("상태 표시줄을 만들지 못했습니다.\n");

      return -1;      // 만들지 못했습니다.

      }

      m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));


      // 두 영역 및 조합된 영역을 주어진 좌표로 생성한다.

      m_RgnRect1.CreateRectRgn(0, 0, 100, 100);

      m_RgnRect2.CreateRectRgn(70, 70, 170, 170);


      // 이 영역은 조합된 결과가 저장될 영역이므로 좌표는 의미가 없다.

      m_RgnTotal.CreateRectRgn(0, 0, 0, 0);


      // 두 영역을 조합한 새로운 영역을 만든다.

      m_RgnTotal.CombineRgn(&m_RgnRect1, &m_RgnRect2, RGN_XOR);


      // 조합된 영역이 CMainFrame 윈도우의 영역이 되도록 한다.

      SetWindowRgn((HRGN)m_RgnTotal, TRUE);


      return 0;

      }

      • CombineRgn()
        • 두 영역을 조합한다.
        • RGN_XOR, RGN_OR, RGN_AND, RGN_DIFF, RGN_COPY 등이 있다.
      • SetWindowRgn()
        • 윈도우 자체의 영역을 바꾼다.
        • 반면에 SelectClipRgn()함수는 CDC 클래스의 멤버로 윈도우 자체의 영역을 변경하는 것이 아니라 그리기 결과가 출력되는 DC의 영역을 일시적으로 변경한다.
        • 메인 프레임 윈도우는 응용 프로그램에서 최상위 부모 윈도우가 되므로 이 윈도우의 영역을 변경하면 자식 윈도우들에게도 직접적으로 영향을 주게 된다.
  2. 그리기(Drawing) 모드
    - 펜을 이용한 그리기 코드를 실행할 때 출력 대상인 화면 DC의 이미지와 펜이 그린 이미지를 어떻게 조합할 것인지 설정
    1. DrawModeDemo 예제
      • SetCapture()
        • 마우스가 해당 윈도우를 벗어나도 메시지를 받을 수 있다.
      • GetCapture()
        • 마우스를 캡쳐하고 있는 윈도우의 핸들을 리턴한다.
        • 캡쳐하는 윈도우가 없으면 NULL
      • ReleaseCapture()
        • 캡쳐 해제
      • SetROP2()
  3. 매핑(Mapping) 모드
    - 좌표계 변경
    1. MapModeDemo 예제
      • SetMapMode()
      • SetWindowExt()
        • 윈도우와 연결된 DC의 논리적인 폭과 높이를 설정
      • SetViewportExt()
        • 뷰포트의 크기를 설정
        • SetWindowExt()함수로 DC의 크기를 가로/세로 100으로 설정했는데 뷰포트의 크기를 가로/세로 300으로 했다면 300% 확대하는 이치
      • SetViewportOrg()
        • 물리적으로 (0, 0)에 해당하는 좌표를 설정
        • 기본 값은 (0, 0) (왼쪽 위 구석)
        • SetViewportOrg(300, 300)으로 설정했다면 왼쪽 위 구석은 (-300, -300)이 됨
  4. 프린터 출력
    1. 기본적으로 모니터 출력과 프린터 출력은 크게 다르지 않다
    2. MFC의 문서/뷰(Document/View) 구조를 통한 구현
    3.  함수명

      설명 

       OnPaint()

      모니터 화면에 출력 

       OnPrint()프린터에 출력 
       OnDraw()

      모니터 화면과 프린터 모두에 출력 

    4. 프린터 출력과 상관없는 화면 출력은 OnPaint() 함수를 이용하는 것이 MFC가 권고하는 프로그래밍 기법
    5. 관련 함수들
      • IsPrinting()
        • CDC 클래스의 메서드로서 DC가 프린터 DC인지 아닌지를 BOOL형으로 반환
      • 문서/뷰 구조의 SDI 형식의 프로젝트 생성 시 기본 재정의 함수들
        • OnPreparePrinting()
          • 프린터 출력에 앞서 인쇄 대화 상자를 출력하기 직전에 호출
          • DoPreparePrinting() 함수를 호출하여 인쇄 대화 상자가 화면에 나타남
          • 프린터 출력에 앞서 인쇄할 전체 문서의 길이가 얼마나 되는지 계산하는 코드가 주로 들어감
          • CPrintInfo 구조체의 멤버 중에 SetMaxPage() 함수를 통해 출력 문서의 전체 길이 설정 가능
        • OnBeginPrinting()
          • 문서 출력 시작 시점에 호출
        • OnEndPrinting()
          • 문서 출력 완료 시점에 호출
    6. MFC의 프린터 출력 구조


CDC 클래스의 Get/SetPixel() 함수나 CImage 클래스의 Get/SetPixel() 함수는 속도가 매우 느리다.

그 이유는 두 함수가 내부적으로 비트맵 정보를 분석하여 DDB를 DIB로 변경한 후 비트맵 데이터를 추출하고 거기서 주어진 인덱스에 해당하는 픽셀 정보를 얻는다.

이 문제를 해결하려면 한 픽셀의 정보를 가져오고 저장할 때마다 비트맵을 분석하는 것이 아니라 한 번 분석을 끝낸 후에는 더 그럴 필요가 없게 하면 된다.

 

Get/SetPixel() 함수를 사용할 때마다 비트맵을 분석하지 말고 한 번만 분석하도록 하자.

 

Get/SetPixel() 성능 향상은 아래 참조

http://dal2iya.tistory.com/126

 

  1. 전형적인 이미지 출력
    1. 윈도우 프로그래밍에서 비트맵 이미지를 출력하는 전형적인 방법
      - 리소스로 등록된 비트맵 이미지를 메모리 DC에 로드하여 화면 DC로 출력(BitBlt())
      - 이미지의 크기를 변경(StretchBlt())
      - 일부 영역을 투명하게 처리(TransparentBlt())
      - 반투명하게 처리(AlphaBlend())
    2. 비트맵(Bitmap)에 대한 이론
      1. 장치 의존적인 비트맵(DDB, Device Dependent Bitmap) & 장치 독립적인 비트맵(DIB, Device Independent Bitmap)으로 나뉨
      2. 우리가 아는 비트맵 파일은 모두 DIB라고 보아도 무방...
      3. 비트맵을 우리 눈으로 보려면 DDB로 변환이 필요
        - 변환 작업은 운영체제가 한다
    3. 비트맵 리소스 출력
    4. void CBmpDisplayDemoView::OnPaint()

      {

      CPaintDC dc(this);

       

      CDC MemDC;

      BITMAP bmpInfo;

       

      //화면 DC와 호환되는 메모리 DC를 생성

      MemDC.CreateCompatibleDC(&dc);

       

      //비트맵 리소스를 로딩

      CBitmap bmp;

      CBitmap* pOldBmp = NULL;

      bmp.LoadBitmap(IDB_Test_Image);//IDB_Test_Image 비트맵 리소스를 미리 등록해야 함.

       

      //로딩된 비트맵의 정보를 알아본다.

      bmp.GetBitmap(&bmpInfo);

       

      //메모리 DC에 선택한다.

      pOldBmp = MemDC.SelectObject(&bmp);

       

      //메모리 DC에 들어 있는 비트맵을 화면 DC로 복사하여 출력

      dc.BitBlt(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &MemDC, 0, 0, SRCCOPY);

       

      //두 배 확대하여 출력

      //dc.StrectchBlt(200, 200, 250 * 2, 300 * 2, &MemDC, 350, 200, 250, 300, SRCCOPY);

       

      MemDC.SelectObject(pOldBmp);

      }

      • 화면 DC와 호환되는 메모리 DC
        • 가령 현재 모니터가 32비트 트루 컬러라면 메모리 DC도 같게 32비트 트루 컬러로 설정
      • BitBlt()
        • 메모리 DC에 로드된 비트맵 이미지를 화면 DC로 전송하면 내부적으로 DIB가 DDB로 변환되어 모니터 화면에 출력
        • 마지막 인자 : 래스터 연산(Raster Operation)을 어떻게 할지 명시
          • SRCCOPY - 원본 그대로 화면 DC에 복사
          • SRCAND - 원본과 대상을 AND 연산
          • NOTSRCCOPY - 원본 이미지의 색상을 반전
          • DSTINVERT - 원본 이미지와는 상관없이 대상 이미지를 반전
  2. 고급 이미지 출력 함수
    1. TransparentBlt()
    2. void CBmpDisplayDemoView::OnPaint()

      {

      CPaintDC dc(this);

       

      CDC MemDC;

      BITMAP bmpInfo;

      MemDC.CreateCompatibleDC(&dc);

       

      CBitmap bmp;

      CBitmap* pOldBmp = NULL;

      bmp.LoadBitmap(IDB_Test_Image);

       

      bmp.GetBitmap(&bmpInfo);

      pOldBmp = MemDC.SelectObject(&bmp);

       

      dc.StrectchBlt(20, 20, bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2, &MemDC, 0, 0, bmpInfo.bmWidth, bmInfo.bmHeight, SRCCOPY);

       

      dc.TransparentBlt(20, 100,

      bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2,

      &MemDC,

      0, 0,

      bmpInfo.bmWidth, bmInfo.bmHeight,

      RGB(0, 0, 0));    //투명 처리될 색상의 RGB 값

       

      MemDC.SelectObject(pOldBmp);

      }

      TransprentBlt의 마지막 인자 값이 RGB(0, 0, 0) 이므로 검정색이 투명하게 출력
    3. AlphaBlend()
    4. void CBmpDisplayDemoView::OnPaint()

      {

      CPaintDC dc(this);

       

      CDC MemDC;

      BITMAP bmpInfo;

      MemDC.CreateCompatibleDC(&dc);

       

      CBitmap bmp;

      CBitmap* pOldBmp = NULL;

      bmp.LoadBitmap(IDB_Test_Image);

       

      bmp.GetBitmap(&bmpInfo);

      pOldBmp = MemDC.SelectObject(&bmp);

       

      dc.StrectchBlt(20, 20, bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2, &MemDC, 0, 0, bmpInfo.bmWidth, bmInfo.bmHeight, SRCCOPY);

       

      BLENDFUNCTION bf;

      bf.BlendOp = AC_SRC_OVER;

      bf.BlendFlags = 0;

      bf.SourceConstantAlpha = 50;

      bf.AlphaFormat = 0;

       

      dc.AlphaBlend(20, 100,

      bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2,

      &MemDC,

      0, 0,

      bmpInfo.bmWidth, bmInfo.bmHeight,

      bf);

       

      MemDC.SelectObject(pOldBmp);

      }

      BLENDFUNCTION 구조체 

      BlendOp 멤버 : 원본/대상 이미지를 서로 섞음

      BlendFlags : 사용 안 함(반드시 0)

      SourceConstantAlpha : 투명도(0~255), 0에 가까울수록 투명

      AlphaFormat : 0 or AC_SRC_ALPHA, AC_SRC_ALPHA인 경우는 진정한 32비트 비트맵인 경우

  3. CImage 클래스의 활용
    1. Visual C++ 6.0에선 외부 이미지 파일을 로드하는게 번거로웠다. 비트맵 파일을 제외한 JPEG, PNG, GIF 와 같은 이미지 파일을 로드하려면 별도의 외부 라이브러리를 사용해야 했다.
    2. CImage는 다양한 형식의 파일을 지원한다.
    3. CImage 클래스를 사용하려면 반드시 atlimage.h 파일을 인클루드해야 한다.
    4. void CImgOutDemoView::OnPaint()

      {

      CPaintDC dc(this);

       

      //로드할 이미지 파일 경로

      CString strImagePath = _T("IMG_0076.jpg");

       

      //이미지 파일 로드

      CImage Image;

      HRESULT hResult = Image.Load(strImagePath);

       

      if(FAILED(hResult))

      {

      CString strtmp = _T("ERROR: Failed to load");

      strtmp += strImagePath + _T("\n");

      TRACE(strtmp);

      return;

      }

       

      //화면 DC에 출력

      Image.BitBlt(dc.m_hDC, 0, 0);

      }

      • CImage 클래스는 MFC가 제공하는 ATL(ActiveX Template Library) 클래스 중 하나이다. 그래서 내부적으로 COM(Component Object Model) 객체로 구현되어 있다.
      • C++의 클래스가 논리적인 코드를 객체화한 개념이라면 COM은 실행 바이너리 파일 단위(모듈 단위)로 객체화한 것이라 할 수 있다. 그래서 메서드가 반환하는 값은 HRESULT형이며, FAILED() 매크로나 SUCCEEDED() 매크로를 이용해 오류를 확인한다. 

      void CImgOutDemoView::OnPaint()

      {

      CPaintDC dc(this);

       

      CImage Image;

       

      //비트맵 리소스를 로드하여 DIB 생성

      Image.LoadFromResource(AfxGetInstanceHandle(), IDB_Image_Test);

       

      //비트맵 이미지에 대한 DC 생성

      CDC* pDC = CDC::FromHandle(Image.GetDC());

       

      //이 이미지 DC에 문자열 출력

      pDC->SetBkMode(TRANSPARENT);

      pDC->TextOut(200, 30, ,TEXT("CImage sample!"));

       

      Image.ReleaseDC();

       

      //이미지를 화면 DC에 출력

      Image.BitBlt(dc.m_hDC, 0, 0);

       

      //문자열을 화면 DC에 출력

      //dc.SetBkMode(TRANSPARENT);

      //dc.TextOut(200, 30, ,TEXT("CImage sample!"));

      }

      • LoadFromResource() 메서드는 비트맵 리소스를 로드하여 비트맵 이미지를 생성한다.
  4. 색상(Color)의 변환
    1. RGB 컬러를 흑백으로 변환
    2. #endif //_DEBUG

      inline void RGBtoGray(COLORREF& rgb)
      {
       BYTE byGray = (GetRValue(rgb) * 30
          + GetGValue(rgb) * 59
          + GetBValue(rgb) * 11) / 100
      ;

       rgb = RGB(byGray, byGray, byGray);
      }


      // CSaveGrayDemoView 메시지 처리기 

    3. 바탕화면의 일부를 변환하여 출력
    4. void CSaveGrayDemoView::OnLButtonDown(UINT nFlags, CPoint point)
      {

      // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

      //바탕화면 윈도우 객체에 대한 포인터를 얻음
      CWnd* pWndDesktop = GetDesktopWindow();
      CWindowDC SrcDC(pWndDesktop);
      CClientDC dc(this);

       

      //바탕화면 크기 및 색상수와 동일한 비트맵 이미지를 만든다.
      CImage Image;
      Image.Create(300, 300, SrcDC.GetDeviceCaps(BITSPIXEL));

       

      //이미지 DC와 화면 DC에 바탕화면 윈도우 DC를 출력한다.
      CDC* pDC = CDC::FromHandle(Image.GetDC());
      pDC->BitBlt(0, 0, 300, 300, &SrcDC, 0, 0, SRCCOPY);
      Image.ReleaseDC();

       

      //일부(200 * 200)를 흑백 이미지로 변환
      COLORREF rgb;
      for(int x = 0; x < 200; x++)
      {
          for(int y = 0; y < 200; y++)
         {
             rgb = Image.GetPixel(x, y);
             //Gray RGB 값으로 변환
             RGBtoGray(rgb);
             Image.SetPixel(x, y, rgb);
         }
      }

       

      //흑백으로 변환된 이미지를 화면 DC에 출력

      Image.BitBlt(dc.m_hDC, 0, 0);

      CView::OnLButtonDown(nFlags, point);

      • GetDesktopWindow() 메서드는 바탕화면 윈도우 객체의 포인터 반환
      • CWindowDC 클래스는 비클라이언트 영역을 포함한 윈도우 전체에 대한 DC
      • CDC 클래스의 GetDeviceCaps() 메서드는 인자로 전달받은 인덱스(BITPIXEL, HORZSIZE, VERTSIZE 등)에 해당하는 DC 정보를 반환
    5. 바탕화면을 별도의 이미지 파일로 저장
    6. void CSaveGrayDemoView::OnRButtonDown(UINT nFlags, CPoint point)
      {

      // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.

      //바탕화면 윈도우 객체에 대한 포인터를 얻음
      CWnd* pWndDesktop = GetDesktopWindow();
      CWindowDC SrcDC(pWndDesktop);
      CClientDC dc(this);

       

      //바탕화면 윈도우의 크기를 알아낸다.
      CRect Rect;
      pWndDesktop->GetWindowRect(&Rect);

       

      //바탕화면 크기 및 색상수와 동일한 비트맵 이미지를 만든다.
      CImage Image;
      int cx = Rect.Width();
      int cy = Rect.Height();
      Image.Create(cx, cy, SrcDC.GetDeviceCaps(BITSPIXEL));

       

      //이미지 DC와 화면 DC에 바탕화면 윈도우 DC를 출력한다.
      CDC* pDC = CDC::FromHandle(Image.GetDC());
      pDC->BitBlt(0, 0, cx, cy, &SrcDC, 0, 0, SRCCOPY);
      dc.BitBlt(0, 0, cx, cy, pDC, 0, 0, SRCCOPY);
      Image.ReleaseDC();

       

      //JPEG 형식으로 바탕 화면 이미지를 저장한다.
      Image.Save(TEXT("desktop.jpg"), Gdiplus::ImageFormatJPEG);

       

      //저장된 이미지를 뷰어를 실행하여 보여준다.
      ::ShellExecute(NULL, TEXT("open"), TEXT("desktop.jpg"), NULL, NULL, SW_SHOW);
       
      CView::OnRButtonDown(nFlags, point);

      }   

      • GetWindowRect()로 바탕화면 윈도우 크기 얻어옴
      • CImage 클래스의 Save() 함수는 현재 선택한 비트맵 이미지를 주어진 파일 형식으로 변환하여 저장 
      • ::ShellExecute() API 함수를 이용하여 이미지 파일 실행


  1. GDI와 DC에 대해서
    1. GDI(Graphics Device Interface)는 윈도우 운영체제가 제공하는 그래픽 출력 서비스 구조(인터페이스)
    2. 개발자는 제조사별 VGA 사용법을 알 필요 없이 GDI만 알면 된다.
    3. DC(Device Context)는 GDI를 추상화한 객체
    4. 개발자는 DC를 이용해 각종 그래픽 정보를 모니터에 출력
  2. WM_PAINT 메시지와 DC
    1. WM_PAINT 메시지는 윈도우를 다시 그려야 할 때 발생
    2. WM_PAINT 메시지 핸들러 함수(OnPaint())에서 그림 출력 코드 수행
    3. CDC 클래스는 DC를 객체화한 클래스
    4. GetDC() <-> ReleaseDC()
    5. DC는 특정 윈도우와 연결 됨
    6. CDC 클래스
       파생 클래스

      의미 

       CClientDC

       윈도우의 클라이언트 영역으로 한정되는 DC
       CWindowDC

       윈도우의 모든 영역(비클라이언트 영역 포함)에 대한 DC. 이 DC를 이용하면 다른 윈도우에 대해서 그리기 가능.

       CPaintDC

       CClientDC 클래스와 유사하지만, 내부적으로 BefinPaint()/EndPaint() 함수를 호출하여 WM_ERASEBKGND 메시지 발생.

       CMetaFileDC

       WMF(Window Meta File)와 EMF(Enhanced Meta File) 파일에 대한 DC

  3. 펜(Pen)과 브러시(Brush)
    1. DC의 대표적인 속성인 펜과 브러시
    2. 펜과 브러시의 속성을 변경할 수 있다.
      -> dc.SelectObject()
      1. CPen  클래스는 세 가지로 다중 정의가 되어 있다.
      2. PS_SOLID 등의 기본 펜 스타일 존재
      3. PS_ENDCAP_ROUND 등의 끝 부분 처리 스타일 존재
    3. 브러시
      1. HS_BDIAGONAL 등의 스타일 존재
      2. 비트맵 브러시, 패턴 브러시
    4. 각종 도형 그리기
      1. 각각의 선이 모여 하나의 도형을 이루도록 하려면 코드상으로 명시를 해주어야 도형으로 인식한다.
      2. PS_JOIN_XXX 펜 스타일, dc.BeginPath(), dc.EndPath(), dc.StrokeAndFillPath()
      3. 같은 원리로 CDC 클래스의 Polygon() 메서드를 사용해도 된다.
      4. dc.Rectangle(), dc.Ellipse(), dc.Pie(), dc.RoundRect()
    5. 글꼴(Font)과 문자열 다루기
      1. dc.TexOut()
      2. CFont 클래스는 글꼴을 객체화한 클래스
      3. CFont 클래스의 CreateFontIndirect() 메서드는 LOGFONT 구조체의 정보를 기반으로 CFont 클래스 객체를 생성
      4. LOGFONT 구조체의 멤버를 통해 글꼴의 크기, 장평, 기울기, 굵기, 이탤릭 등의 속성을 줄 수 있다.
      5. 탭을 반영하기 위한 dc.TabbedTextOut()
    6. 직접 버튼 컨트롤 구현하기
      1. dc.FillSolidRect(), ::GetSysColor(), dc.Draw3dRect(), DrawText()

    + Recent posts