본문 바로가기

윈도우프로그래밍

[윈도우 프로그래밍 7장] 그래픽 객체의 사용

1. GDI와 DC의 개념

GDI (Graphic Device Interface)

  • 윈도우를 설치하면 비디오 카드와 맞는 디바이스 드라이버를 윈도우가 설정해 줌
  • 그래픽을 출력하고자 하는 응용 프로그램은 윈도우에게 요청 을 하고, 윈도우는 디바이스 드라이버를 호출하면 디바이스 드라이버가 하드웨어를 구동시킴
  • 하드웨어 종류에 관계없이 항상 동일한 명령어를 이용하여 그래픽을 출력할 수 있음 -> 하드웨어에 독립적인 작업 가능
  • 윈도우가 하드웨어를 제어할 수 있도록 응용 프로그램에 제공 하는 모든 기능
  • 응용 프로그램과 디바이스 드라이버의 중간 역할
  • 응용 프로그램에 대한 장치 독립적인 그래픽 동작 수행
  • (Application -> Windows -> Device Driver -> Graphic Card)
  • GDI는 Windows 운영체제가 응용프로그램에 제공하는 그래픽 출력 API 입니다. (Windows가 하드웨어를 제어할 수 있도록 응용 프로그램에 제공하는 모든 기능[함수, 구조 등]을 일컬어 GDI라고 합니다.)

여기서 Device가 하드웨어잖아 즉 비디오 카드란 소리야. 비디오 카드는 컴퓨터 시스템에서 그래픽 출력을 담당하는 하드웨어 장치야. 그래픽 출력하고자 하는 응용 프로그램 -> 윈도우 -> 디바이스 드라이버 -> hw 에서 그래픽 출력 해줌

간편화 하기 위해 DC(Device Context)라는 개념을 도입 -> 

Device Context(DC)

  • 윈도우 프로그램은 윈도우 화면에 출력되는 모든 내용을 DC를 이용하여 출력 
  • 그래픽 객체들의 속성과 그래픽 모드를 정의하는 자료의 집합체 
  • 응용프로그램과 출력 장치(Hw)를 연결하는 역할
  • 응용프로그램이 출력에 대한 허가를 얻고, 또한 그려지는 영역을 결정 하는 역할
  • GDI에 의해 내부적으로 유지되는 데이터 구조-GDI 함수의 매개변수
  • DC를 사용하는 이유? 독립적인 하드웨어 출력을 할 수 있어 출력장치에 상관없이 동일한 방법으로 출력을 설정
  • DC란 윈도우 응용프로그램에서 화면 출력을 위해 출력 속성을 정의하는 구조체(struct). 즉, 화면 출력을 위해 여러 속성값들을 모아놓은 상자

GetDC(), OnDraw() 같은 GDI 함수가 매개변수로 그래픽 속성 등 그래픽과 관련된 정보를 담고 있는 구조체를 넣어서 호출 -> 그래픽 출력 가능해짐 !!! GDI는 api이고, DC는 그래픽과 관련된 속성 담고 있는 객체임

DC를 얻는 방법 

  • DC는 한 순간에 하나의 펜, 브러시만 가질 수 있으므로 다른 펜/브러시를 사용하려면 새로운 것을 만들어서 DC를 설정해 야 함
  • DC를 얻고 나면, 작업 완료 시 해제해야 함 

4가지 DC를 얻는 방법

OnDraw()함수, OnPaint()함수를 이용

  • 그리기 작업을 수행하기 위해 DC를 인자로 받습니다. 예를 들어, MFC에서의 OnDraw() 함수는 CDC* pDC 매개변수를 가지며, 해당 매개변수를 통해 DC를 얻을 수 있습니다.
  • 윈도우나 클라이언트 영역이 다시 그려져야 할 경우 수행 (Invalidate() 에 의해 호출됨 -> 화면 갱신)
  • DC의 해제는 MFC 내부 코드에 존재(설정 하지 않아도 됨)
void CMyView::OnDraw(CDC* pDC) //인자로 CDC 클래스 
{
   // pDC를 사용하여 그리기 작업 수행
   // ...
}

void CMyView::OnPaint() //인자는 없고 CPaintDC 클래스에서 객체 생성
{
   CPaintDC dc(this); // 윈도우의 DC를 얻음

   // dc를 사용하여 그리기 작업 수행
   // ...
}

 

GetDC() 함수를 이용

  • GDI 함수 중 하나인 GetDC() 함수를 호출하여 DC를 얻을 수 있습니다. 일반적으로 윈도우 핸들(HWND)을 인자로 전달하여 해당 윈도우와 연결된 DC를 얻습니다.
  • CDC 클래스의 인스턴스를 포인터 형태로 넘겨받아서 이용
  • ReleaseDC() 함수를 호출하여 반드시 해제
  • 이 방법으로 DC를 얻는 것은 일시적인 것으로 윈도우의 크기가 변하면 출력된 내용이 사라짐
  • 왜??? 화면 변화가 생겼을 때 OnDraw() 함수나 OnPaint() 함수가 호출되면서 화면이 갱신되기 때문
   CDC* pDC = GetDC(); // GetDC() 함수를 사용하여 DC를 얻음

   // pDC를 사용하여 그리기 작업 수행
   // ...

   ReleaseDC(pDC); // DC 해제

CClientDC 클래스를 이용

  • MFC에서 제공하는 CClientDC 클래스를 사용하여 DC를 얻을 수 있습니다. CClientDC 클래스는 클라이언트 영역을 그리기 위한 DC를 제공합니다. CClientDC 객체를 생성하고, 해당 객체의 멤버 변수로 DC를 얻을 수 있습니다.
  • CDC 클래스의 파생 클래스
  • Devie Context의 생성/해제 자동적 수행

       -> 생성자 함수에서 GetDC() 함수 호출

       -> 소멸자 함수에서 ReleaseDC() 함수 호출

  • 윈도우에 대한 핸들 값 필요
  • 일시적으로 윈도우의 클라이언트 영역에서 그래픽 개체를 사용할 경우 이용
  • DC를 얻는 것은 일시적인 것으로 윈도우가 크기가 변하면 출력한 내용이 사라짐
//위의 예시에서 CMyView는 MFC의 CView 클래스를 상속받은 클래스로 가정
//SomeFunction() 함수에서는 CClientDC 객체를 생성하여 DC를 얻은 후, 해당 객체를 사용하여 클라이언트 영역에 그리기 작업을 수행
//함수를 빠져나가면서 CClientDC 객체가 소멸되면, DC는 자동으로 해제

void CMyView::SomeFunction()
{
   CClientDC dc(this); // CClientDC를 사용하여 DC를 얻음
   //현재 객체(this)를 통해 윈도우 핸들을 얻고, 그 핸들을 사용하여 CClientDC 객체를 생성하여 DC를 얻는 역할

   // dc를 사용하여 클라이언트 영역에 그리기 작업 수행
   // ...
}

CWindowDC 클래스를 이용

  • 윈도우(프레임) 영역에 그래픽 요소를 출력하고자 할 때 사용
  • GetWindowDC() 함수를 이용하여 CWindowDC 클래스의 인스턴스를 포인터 형태로 넘겨 받아 이용
  • 작업 수행 후, ReleaseDC() 함수로 해제
CWindowDC *pDC = GetWindowDC(); // 윈도우 DC 얻음
… // 그래픽 출력
ReleaseDC(); // DC 반환

 

 

2. GDI 객체

  • 화면에 그림을 그리거나 문자를 출력할 때 사용하는 객체
  • 펜, 브러시, 폰트, 비트맵 등을 핸들링하는 핸들

GDI 기본 구성 요소

  • 선과 곡선(Lines and Curves): 직선, 사각형, 타원, 호, 베지어 곡 선
  • 채워진 영역(Filled Areas): 브러시 개체를 이용 – 색상, 패턴, 비 트맵 이미지
  • 비트맵(Bitmaps): 디스플레이 장치의 픽셀과 일치하는 직사각 형 배열, 래스터 그래픽의 기본적인 도구
  • 텍스트(Text)

화면에 그래픽 출력하기 위한 요소

 

객체를 사용하는 방법

① GDI 객체를 생성 - 펜, 브러쉬...

  • 객체 클래스의 Create계열 함수 이용

② 객체를 DC에 등록 

  • SelectObject()함수를 이용 
  • DC를 쓰고 난 다음 원래 상태로 복원하기 위해 기존 설정된 객체는 포인터로 저장

③ DC를 사용하여 그래픽 출력

④ 이전 객체로 환원

⑤ 객체를 삭제 

  • GDI 객체 클래스의 DeleteObject() 함수 이용

 

GDI 객체-펜

  • 선이나 영역의 경계선을 그릴 때 사용
  • 선의 두께, 선이 색상, 선의 스타일을 설정

펜을 사용하는 방법

① CPen pen, *oldpen; // Pen 클래스의 인스턴스 생성, 기존 설정된 객체는 포인터로 저장
pen.CreatePen(PS_SOLID, 1,RGB(0,0,0)); // 실선이고 두께가 1인 검은색의 펜을 생성(객체 생성)
② oldpen = pDC->SelectObject(&pen);//객체 등록
③ pDC->Ellipse(0,0,10,10);//DC 사용해서 그래픽 출력
④ pDC->SelectObject(oldpen);//이전 객체로 환원
⑤ pen.DeleteObject();//객체 삭제

CreatePen() 함수

  • CreatePen(): Pen을 생성하는 함수
  • BOOL CreatePen(int nPenStyle, int nWidth,COLORREF crColor);
  • nPenStyle : 펜의 스타일 
  • nWidth : 펜의 굵기
  • crColor : 펜의 색상

펜의 스타일

 

GDI 객체-브러시

  • 영역의 내부를 채울 때 사용
  • 채울 색, 패턴 등이 사용

브러시를 사용하는 방법

① CBrush brush, *oldbrush; // Brush 클래스의 인스턴스 생성
brush.CreateSolidBrush(RGB(0,0,0)); // 검은색의 브러쉬를 생성
bush.CreateHatchBrush(HS_CROSS,RGB(0,0,0)); // 십자가 형태의 빗금을 가진 검은색 브러쉬 생성
② oldbrush = pDC->SelectObject(&brush);
③ pDC->Ellipse(0,0,10,10);
④ pDC->SelectObject(oldbrush);
⑤ brush.DeleteObject();

CreateSolidBrush() 함수

  • 단일 색으로 칠하는 브러시를 생성하는 함수

CreateHatchBrush() 함수

  • 일정한 패턴을 가진 해치 브러시를 생성하는 함수.
  • 해치 브러쉬의 스타일

그래픽 함수들

  • 선 그리기 MoveTo(int x, int y) 함수(시작 위치), LineTo(int x, int y)함수(끝 위치)
  • 사각형 그리기 Rectangle(int x1, int y1, int x2, int y2) x1, y1=좌상단, x2, y2=우하단
  • 원 그리기 Ellipse(int x1, int y1, int x2, int y2)
  • 다각형 그리기 Polyline(LPPOINT lpPoints, int nCount) , Polygon(LPPOINT lpPoints, int nCount)
  • 베지어 곡선 그리기  PolyBezier(const POINT* lpPoints, int nCount)

베지어 곡선 그리기

  • 임의의 형태의 곡선을 표현하기 위해 수학적으로 만든 곡선 
  • 최초의 제어점인 시작점과 최후의 제어점인 끝점 그리고 그 사이에 위 치하는 내부 제어점의 이동에 의해 다양한 자유 곡선을 얻는 방법

래스터 오퍼레이션(Raster Operation)

  • 새로 그려야 할 그림과 기존의 화면에 그려져 있는 그림을 합성하는 것
  • 펜과 브러시에 적용되며 SetROP2()함수로 설정 
  • R2_COPYPEN : 배경의 화면을 무시하고 새로 그려지는 그림을 출력 (기본 옵션) 
  • R2_XORPEN : 배경의 화면을 유지하면서 움직이는 그림을 그릴 때 사용함

내장 GDI 객체

  • 자주 사용되는 스타일의 GDI 객체는 윈도우가 내장하고 있어 따로 만들지 않고 윈도우에서 얻어서 사용 dc.SelectObject(GetStockObject(WHITE_BRUSH): 흰색 브러시 설정 
  • PEN: BLACK_PEN, WHITE_PEN, NULL_PEN 
  • BRUSH: BLACK_BRUSH, WHITE_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH, LTGRAY_BRUSH, HOLLOW_BRUSH, NULL_BRUSH 
  • FONT: ANSI_FIXED_FONT, ANSI_VAR_FONT, DEVICE_DEFAULT_FONT, OEM_FIXED_FONT, SYSTEM_FONT

 GDI 객체-비트맵

  • CBitmap클래스를 이용하는 객체
  • 비트맵을 생성하거나 읽어서 비트맵을 출력할 때 사용
  • 비트맵의 래스터 오퍼레이션: BitBlt(), StretchBlt() ◼
  • SRCCOPY : 배경 그림을 무시하고 비트맵 출력 
  • SRCAND : 배경 그림과 AND 연산 
  • SRCPAINT : 배경 그림과 OR 연산

비트맵을 사용하는 방법

① 화면 DC와 메모리 DC를 생성

② 화면 DC와 호환성을 갖는 DC를 만듬

③ 비트맵을 읽어 옴

④ 메모리 DC에 비트맵을 설정

⑤ 비트맵 블록을 전송

⑥ DC를 복원 

① CClinetDC dc(this); // 비트맵이 출력된 화면 윈도우 DC 생성
CDC memdc; // 메모리의 DC 생성
② memdc.CreateCompatibleDC(&dc); // 현재 DC와 호환적인 메모리 DC 생성
③ CBitmap bitmap, *oldbitmap; // 비트맵의 인스턴스 생성
bitmap.LoadBitmap(IDB_BITMAP1); // 비트맵을 읽어 옴
④ Oldbitmap = memdc.SelectObject(&bitmap); // 메모리 DC에 비트맵을 설정
⑤ dc.BitBlt(0, 0, 450, 85, &memdc, 0, 0, SRCCOPY); // 비트맵을 출력
⑥ memdc.SelectObject(oldbitmap);

GDI 객체-폰트

  • CFont 클래스를 이용하는 객체
  • 문자를 출력할 때 사용하며 글자의 모양, 크기가 설정
  • 폰트 관련 그래픽 함수

◼ TextOut(0, 0, “안녕하세요?”);

◼ SetTextColor(RGB(0,0,255)): 배경색은 그대로 두고 텍스트의 전경색을 설정

◼ SetBkColor(RGB(255,0,0)): 텍스트의 배경색을 설정

◼ SetBkMode(): 텍스트가 출력될 때 사각형으로 텍스트를 둘러싸는 배경색을 설정

◼ SetTextAlign(TA_LEFT|TA_TOP): 텍스트의 정렬방법 지정

 

직선, 곡선, 도형 그리기 실습