MFC :: Chapter 7 (그래픽 객체의 사용) - 97번째 이야기
GDI와 DC의 개념
윈도우에서의 그래픽 출력은 크게 GDI(Graphics Device Interface)와 DC(Device Context)라는 두 가지 개념으로 설명될 수 있다.
1) 그래픽 디바이스 인터페이스(GDI : Graphics Device Interface)
윈도우를 설치하면 비디오 카드와 맞는 디바이스 드라이버를 윈도우가 설정해준다. 그래픽을 출력하고자 하는 응용 프로그램은 윈도우에게 요청을 하고 윈도우는 디바이스 드라이버를 호출하면 디바이스 드라이버가 하드웨어를 구동시킨다. 이렇게 하면 하드웨어의 종류에 관계없이 항상 동일한 명령어를 이용하여 그래픽을 출력할 수 있어 하드웨어에 독립적인 작업을 할 수 있다.
이 때, 윈도우가 하드웨어를 제어할 수 있도록 응용 프로그램에 제공하는 모든 기능을 일컬어 그래픽 디바이스 인터페이스라고 한다. 즉, 그래픽 디바이스 인터페이스는 응용 프로그램과 디바이스 드라이버의 중간 다리 역할을 하는 것으로 응용 프로그램에 대한 장치 독립적인 그래픽 동작을 수행한다.
윈도우에서의 그래픽 출력은 크게 GDI(Graphics Device Interface)와 DC(Device Context)라는 두 가지 개념으로 설명될 수 있다.
1) 그래픽 디바이스 인터페이스(GDI : Graphics Device Interface)
윈도우를 설치하면 비디오 카드와 맞는 디바이스 드라이버를 윈도우가 설정해준다. 그래픽을 출력하고자 하는 응용 프로그램은 윈도우에게 요청을 하고 윈도우는 디바이스 드라이버를 호출하면 디바이스 드라이버가 하드웨어를 구동시킨다. 이렇게 하면 하드웨어의 종류에 관계없이 항상 동일한 명령어를 이용하여 그래픽을 출력할 수 있어 하드웨어에 독립적인 작업을 할 수 있다.
이 때, 윈도우가 하드웨어를 제어할 수 있도록 응용 프로그램에 제공하는 모든 기능을 일컬어 그래픽 디바이스 인터페이스라고 한다. 즉, 그래픽 디바이스 인터페이스는 응용 프로그램과 디바이스 드라이버의 중간 다리 역할을 하는 것으로 응용 프로그램에 대한 장치 독립적인 그래픽 동작을 수행한다.
Program <==> Windows <==> Device Driver <==> Device
(그래픽 디바이스 인터페이스의 개념)
(그래픽 디바이스 인터페이스의 개념)
2) 디바이스 컨텍스트(DC : Device Context)
윈도우 프로그램은 윈도우의 화면에 출력되는 모든 내용을 DC를 이용하여 출력하게 된다. DC는 그래픽 객체들의 속성과 그래픽 모드를 정의하는 자료의 집합체로 출력 장치에 정보를 표시할 때 필요한 정보를 저장하는 자료구조이다.
DC는 애플리케이션과 출력 장치를 연결하는 역할을 하며 애플리 케이션이 출력에 대한 허가를 얻도록 하고 또한 그려지는 영역을 결정하는 역할을 한다. DC를 사용하는 이유는 하드웨어 독립적인 출력을 할 수 있어 출력 장치에 상관없이 동일한 방법으로 출력을 설정할 수 있기 때문이다.
그래픽 객체의 속성에 관련된 모든 옵션이 디폴트로 설정되어 있어 그냥 간단한 출력을 하고자 할 때는 디폴트로 설정된 DC를 이용하여 바로 출력하면 되고 세세한 것을 제어하고자 할 때는 DC에 설정된 내용을 바꾼 후에 출력한다. DC는 한 순간에 하나의 펜, 브러시만 가질 수 있어 다른 펜이나 다른 브러시를 사용하려면 새로운 것을 만들어서 DC에 설정해주어야 한다. 윈도우에서 출력을 하려면 먼저 DC를 얻어야 한다. DC를 얻는 방법은 다음과 같이 4가지 방법이 있다.
▶ DC를 얻는 방법
1) OnDraw() 함수나 OnPaint() 함수를 이용하는 방법
OnDraw() 함수에서 인자로 넘어온 CDC 클래스를 받거나 OnPaint() 함수에서 CPaintDC 클래스를 받아 이용한다. 화면의 변화가 생겼을 때 계속적으로 OnDraw() 함수나 OnPaint() 함수를 호출하기 때문에 윈도우의 크기가 변하거나 다른 프로그램에 의해 가려졌다가 다시 출력되어도 화면 출력이 변하지 않는다.
ex)
void CPractice6_1View::OnDraw(CDC* pDC)
{
.... // 그래픽 출력
}
void CPractice6_1View::OnPaint()
{
CPaintDC dc(this);
.... // 그래픽 출력
}
2) GetDC() 함수를 이용하는 방법
GetDC() 함수를 이용하여 CDC 클래스의 인스턴스를 포인터 형태로 넘겨받아 이용한다. 그래픽 작업을 종료한 후에 반드시 ReleaseDC() 함수를 호출하여 시스템에 DC를 반환해야 한다. 이 방법으로 DC를 얻는 것은 일시적인 것으로 윈도우의 크기가 변하면 출력한 내용이 사라진다. 그 이유는 화면의 변화가 생겼을 때 WM_PAINT 메시지가 호출되고 OnDraw() 함수나 OnPaint() 함수가 호출되면서 화면이 갱신되기 때문이다.
ex)
CDC *pDC = GetDC(); // DC 얻음
...... // 그래픽 출력
ReleaseDC(pDC); // DC 반환
3) CWindowDC 클래스를 이용하는 방법
위의 3가지 방법은 클라이언트 영역을 얻는 방법이다. CWindowDC 클래스는 클라이언트의 영역이 아닌 윈도우 영역에 그래픽을 출력하고자 할 때 이용한다. 이 방법은 GetWindowDC() 함수를 이용하여 CWindowDC 클래스의 인스턴스를 포인터 형태로 넘겨받아 이용한다. 그래픽 작업을 종료한 후에 반드시 ReleaseDC() 함수를 호출하여 시스템에 DC를 반환해야 한다.
ex)
CWindowDC *pDC = GetWindowDC(); // 윈도우 DC 얻음
........ // 그래픽 출력
ReleaseDC(pDC); // DC 반환
GDI 객체
GDI 객체는 화면에 그림을 그리거나 문자를 출력할 때 사용하는 객체를 의미한다. 즉 펜, 브러시, 폰트, 비트맵 등을 핸들링 하는 핸들을 GDI 객체라고 하며 이 GDI 객체의 핸들을 가지고 있는 클래스를 GDI 클래스라 한다.
GDI 객체의 종류, GDI 객체 클래스의 종류, 기본 값, 사용 용도는 다음과 같다.
GDI 객체는 그래픽 옵션을 저장하기 위한 것들이기 때문에 어떤 그래픽 옵션을 변경하고자 하는 경우에는 먼저 GDI 객체의 클래스를 변경하여 생성한 후에 이것을 DC에 넣고 이 DC를 이용하여 그림을 그리도록 한다. GDI 객체를 사용하는 방법을 정리해보면 다음과 같다.
1. GDI 객체를 생성한다. => GDI 객체 클래스의 Create() 계열 함수 사용
2. 객체를 DC에 등록시킨다. => SelectObject() 함수 이용 (DC를 쓰고 난 다음 원래 상태로 복원하기 위해 기존에 설정되어 있는 객체를 포인터로 받아둔다.)
3. DC를 사용하여 그래픽을 출력한다.
4. 이전 객체로 되돌린다. => DC를 원래 상태로 복원
5. 객체를 삭제한다. => GDI 객체 클래스의 DeleteObject() 함수 이용
이제부터 GDI 객체에서 가장 많이 쓰이는 펜, 브러시, 비트맵, 폰트에 대하여 알아보자.
1) 펜(Pen)
펜은 CPen 클래스를 이용하는 객체로 선이나 영역의 경계선을 그릴 때 사용하며 선의 두께, 선의 색상, 선의 스타일이 설정된다. 다음은 펜을 이용하는 방법이다.
1. 펜을 생성한다.
ex) CPen pen, *oldpen; // Pen 클래스의 인스턴스 생성
pen. CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); // 실선이고 두께가 1인 검정 색의 펜을 생성
또는 간편하게 CPen 생성자에서 펜 속성을 초기화 해줘도 된다.
ex) CPen pen(PS_SOLID, 1, RGB(0, 0, 0))
Cpen::CreatePen() 함수
Creae() 함수는 Pen을 생성하는 함수로 원형은 다음과 같다.
BOOL CreatePen(int nPenStyle, int nWidth, COLORREF crColor);
( nPenStyle : 펜의 스타일
nWidth : 펜의 굵기
crColor : 펜의 색상 )
* 펜의 스타일
PS_SOLID : 실선, PS_DASH : 파선, PS_DOT : 점선
PS_DASHDOT : 일점쇄선, PS_DASHDOTDOT : 이전쇄선, PS_NULL : 선을 그리지 않음
2. 펜을 DC에 등록한다.
ex) oldpen = pDC->SelectObject(&pen);
3. DC를 이용하여 그래픽을 출력한다.
ex) pDC->Ellipse (0, 0, 10, 10);
4. 이전 펜으로 되돌린다.
ex) pDC->SelectObject(oldpen);
5. 펜을 삭제한다.
ex) pen.DeleteObject();
2) 브러시
브러시는 CBrush 클래스를 사용하는 객체로 영역의 내부를 채울 때 사용되며, 채울 때, 패턴 등이 설정된다. 다음은 브러시를 사용하는 방법이다.
1. 브러시를 생성한다.
a. 단일 색으로 칠하는 브러시를 생성한다.
ex) CBrush brush, *oldbrush; // Brush 클래스의 인스턴스 생성
brush.CreateSolidBrush(RGB(0, 0, 0)); // 검정색의 브러시를 생성
또는 간단하게 CBrush 클래스의 생성자에서 브러시 속성을 초기화 해줘도 된다.
ex) CBrush brush(RGB(0, 0, 0));
b. 일정한 패턴을 가진 해치 브러시를 생성한다.
ex) CBrush brush; // Brush 클래스의 인스턴스 생성
brush.CreateHatchBrush(HS_CROSS, RGB(0, 0, 0)); // 십자가 형태의 빗금을 가진 검정 색 브러시 생성
2. 브러시를 등록하여 사용하고 삭제하는 방법은 위의 펜의 사용 법 2,3,4,5번과 같다.
CBrush::CreateSolidBrush() 함수
CreateSolidBrush() 함수는 단일 색으로 칠하는 브러시를 생성하는 함수로 원형은 다음과 같다.
BOOL CreateSolidBrush(COLORREF crColor);
( crColor : 브러시의 색상 )
CBrush::CreateHatchBrush() 함수
CreateHatchBrush() 함수는 일정한 패턴을 가진 해치 브러시를 생성하는 함수로 원형은 다음과 같다.
BOOL CreateHatchBrush (int nIndex, COLORREF crColor);
( nIndex : 해치브러시 스타일
crColor : 브러시의 색상 )
* 해치브러시의 스타일
HS_BDIAGONAL : 오른쪽에서 왼쪽으로 45도 내려가는 빗금
HS_CROSS : 십자가 형태의 빗금
HS_DIAGCROSS : X자 형태의 빗금
HS_FDIAGONAL : 왼쪽에서 오른쪽 45도 내려가는 빗금
HS_HORIZONTAL : 수평으로 빗금
HS_VERTICAL : 수직으로 빗금
● 펜과 브러시를 이용한 그래픽 함수
다음은 펜과 브러시를 이용한 그래픽 함수이다. 이 함수들은 모두 현재 DC에 설정되어 있는 펜을 이용하여 경계선을 그리고, 도형인 경우에는 안쪽을 현재 DC에 설정되어 있는 브러시로 치한다. 펜과 브러시를 등록하지 않으면 기본적으로 등록되어있는 검정색 펜과 흰색 브러시를 이용하여 도형을 그린다. 다음은 자주 사용하는 그래픽 함수이다.
1. 선 그리기
선을 그리기 위해 두 가지 함수를 사용하는데 MoveTo() 함수는 선을 그리기 위해 시작 위치로 이동하는 함수이고 LineTo() 함수는 시작 위치에서 선을 그릴 끝 위치로 선을 그리는 함수이다. 또한 LineTo() 함수는 그려진 선의 끝 위치에 현재 지점을 새로 설정한다. 따라서 연결된 직선을 그릴 때는 처음 한번만 MoveTo() 함수를 호출하고 LineTo() 함수만 계속 호출하면 될것이다. 함수의 원형은 다음과 같다.
CPoint MoveTo(int x, int y);
CPoint MoveTo(POINT point);
( x, y : 선을 그릴 시작 위치로 이동하는 x, y 좌표
point : 선을 그릴 시작 위치로 이동하는 x, y 좌표를 가지고 있는 구조체 )
BOOL LineTo(int x, int y);
BOOL LineTo(POINT point);
( x, y : 선을 그릴 끝 위치로 이동하는 x, y 좌표
point : 선을 그릴 끝 위치로 이동하는 x, y 좌표를 가지고 있는 구조체 )
2. 사각형 그리기
사각형을 그리는 함수로서 함수의 원형은 다음과 같다.
BOOL Rectangle(int x1, int y1, int x2, int y2);
BOOL Rectangle(LPCRECT lpRect);
( x1, y1 : 그려질 사각형의 좌측 상단 x, y 좌표
x2, y2 : 그려질 사각형의 우측 하단 x, y 좌표
lpRect : 그려질 사각형의 x1, y1, x2, y2의 값을 저장하고 있는 객체 )
ex) pDC->Rectangle(0, 0, 50, 50); // (0, 0) 과 (50, 50)을 꼭짓점으로 하는 사각형 그림
3. 원 그리기
사각형 영역 안에 원을 그리는 함수로서 함수의 원형은 다음과 같다.
BOOL Ellipse(int x1, int y1, int x2, int y2);
BOOL Ellipse(LPCRECT lpRect);
( x1, y1 : 원이 그려질 사각형의 좌측 상단 x, y 좌표
x2, y2 : 원이 그려질 사각형의 우측 하단 x, y 좌표
lpRect : 원을 그리는 사각형의 x1, y1, x2, y2의 값을 저장하고 있는 객체 )
ex) pDC->Ellpse(0, 0, 50, 50); // (0, 0) 과 (50, 50)을 꼭짓점으로 하는 사각형에 내접하는 타원을 그림
4. 다각형 그리기
다각형을 그리는 함수에는 닫히지 않은 다각형의 외곽선만을 그리는 Polyline() 함수와 닫힌 다각형을 그리는 Polygon() 함수가 있다. Polyline() 함수를 이용하면 MoveTo() 함수와 LineTo() 함수를 이용할 때처럼 연속적인 직선을 그릴 수 있다. 그러나 닫힌 다각형을 그리는 것이 아니므로 Polyline() 함수로 직선을 연결해서 닫히 다각형을 그려도 닫힌 다각형처럼 내부에 자동적으로 색상이 칠해지지 않는다. Polygon() 함수는 Polyline() 함수와 달리 항상 닫힌 다각형을 그려준다. 닫힌 도형을 그리기 위해 이 함수는 마지막 꼭짓점에서 첫 꼭짓점까지 직선을 그린다. 함수의 원형은 다음과 같다.
BOOL Polyline(LPPOINT lpPoints, int nCount);
BOOL Polygon(LPPOINT lpPoints, int nCount);
( lpPoints : 다각형의 좌표들을 가지고 있는 POINT 구조체 배열의 이름
nCount : 다각형 꼭짓점의 개수(배열내의 점의 개수 )
ex) CPOINT ptData[3];
ptData[0].x = 200;
ptData[0].y = 100;
ptData[1].x = 100;
ptData[1].y = 300;
ptData[2].x = 300;
ptData[2].y = 300;
ptData[3].x = 300;
ptData[3].y = 300;
pDC->Polyline(ptData, 3); 혹은 pDC->Polygon(ptData, 3);
5. 베지어 곡선 그리기
베지어 곡선이란 다각형의 끝점을 지나며 중간 꼭짓점에 근접한 곡선이다. Cubic 베지어 곡선이란 control point가 4개가 있는 베지어 곡선을 말한다. Cubic 베지어 곡선을 그리려면 PolyBezier() 함수를 이용하며 함수의 원형은 다음과 같다.
BOOL PolyBezier(const POINT* lpPoints, int nCount);
( lpPoints : control pointdp eogks POINT 구조체 배열의 포인터
nCount : 배열내의 점의 개수 )
첫 번째 지점은 곡선의 시작 위치이고 두 번째와 세 번째 지점은 곡선의 모양을 조정하는데 사용되며 네 번째 지점은 곡선의 끝 위치를 나타낸다. 베지어 곡선에 또 하나의 베지어 곡선을 추가하려면 배열에 세 지점만 추가하면 된다. 그러면 cubic 베지어 곡선의 끝 위치를 시작점으로 하고 세 번째 지점이 새 베지어 곡선의 끝 지점이된다. 나머지 두 개의 지점은 곡선의 모양을 조절한다. 일반적으로 여러개 연결된 cubic 베지어 곡선을 그리려면 배열의 요소수는 그리기를 원하는 베지어곡선수 x 3 + 1이 되어야 한다.
ex) CPOINT m_ptData[4];
m_ptData[0].x = 200;
m_ptData[0].y = 100;
m_ptData[1].x = 100;
m_ptData[1].y = 300;
m_ptData[2].x = 300;
m_ptData[2].y = 300;
m_ptData[3].x = 300;
m_ptData[3].y = 300;
pDC->PolyBezier(m_ptData, 4);
● 래스터 오퍼레이션(Raster Operation)
래스터 오퍼레이션이란 새로 그려져야 할 그림과 기존에 화면에 그려져 있는 그림을 합성하는 것을 말한다. 래스터 오퍼레이션은 펜과 브러시에 적용되며 SetROP2() 함수로 설정한다.
ex) SetROP2(R2_XORPEN); // 펜과 브러시에 XORPEN으로 설정
다음은 자주 사용하는 래스터 오퍼레이션 코드이다.
R2_COPYPEN
일반적으로 사용하는 방법으로 배경의 화면을 무시하고 새로 그려지는 그림이 출력된다. 기존에 어떤 그림이 그려있는지 상관없이 새로 그려지는 그림이 화면을 덮는다. 기본 옵션으로 선택되어 있다.
R2_XORPEN
배경의 화면을 유지하면서 움직이는 그림을 그릴 때 사용한다. R2_XORPEN으로 한번 그려주면 그림이 그려지고 같은 방법으로 다시 한번 그리면 원래의 바탕색이 복원된다.
● 내장(Stock) GDI 객체
자주 쓰이는 스타일의 GDI 객체는 윈도우가 내장하고 있어 따로 만들지 않고 윈도우에서 얻어서 사용한다. 윈도우로부터 내장 GDI 객체를 얻으려면 GetStockObject() 함수를 이용한다.
ex) dc.SelectObject(GetStockObject(WHITE_PEN));
내장 GDI 객체는 다음과 같다.
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
3) 비트맵(Bitmap)
비트맵은 CBitmap 클래스를 이용하는 객체로 비트맵을 생성하거나 읽어서 비트맵을 출력할 때 사용한다. 비트맵은 리소스에서 비트맵을 생성하는 방법과 별개의 드로잉 프로그램에서 비트맵을 생성하여 리소스에 등록하여 사용하는 방법이 있다. 일반적으로 비트맵은 별개의 드로잉 프로그램에서 미리 생성해서 파일로 저장해두고 비트맵을 읽어와 리소스에 등록하여 사용한다. 다음은 비트맵을 사용하는 방법이다.
1. 화면 DC와 메모리 DC를 생성한다.
비트맵을 출력하려면 비트맵이 출력될 화면 윈도우의 DC와 메모리에서 만들어진 DC 두 가지가 있어야 한다. 메모리에서 만들어진 DC에서 화면 윈도우 DC에게 비트맵 블록을 전송해야 화면에 비트맵이 출력된다.
ex) CClientDC dc(this);
CDC memdc;
2. 화면 DC와 호환성을 갖는 메모리 DC를 만든다.
DC를 두 개 생성했다고 해서 비트맵 블록을 전송할 수 있는 것이 아니라 두 DC가 호환성이 있어야 한다. 화면 윈도우의 DC의 포인터를 인자로 넘겨주어 메모리에서 만들어진 DC 인스턴스에 대해 CDC 클래스의 멤버 함수인 CreateCompatibleDC() 함수를 호출해주면 두 DC가 호환성이 있게 한다.
ex) memdc. CreateCompatibleDC(&dc); // 현재 DC와 호환적인 메모리 DC 생성
3. 비트맵을 읽어온다. LoadBitmap() 함수의 인자로 비트맵 ID를 입력한다.
ex) Cbitmap bitmap, *oldbitmap; // 비트맵의 인스턴스 생성
bitmap.LoadBitmap(IDB_BITMAP1); // 비트맵을 읽어옴
4. 메모리 DC에 비트맵을 설정한다.
ex) oldbitmap = memdc.SelectObject(&bitmap); // 메모리 DC에 비트맵을 설정
5. 비트맵 블록을 전송한다.
ex) dc.BitBlt (0, 0, 450, 85, &memdc, 0 , 0, SRCCOPY); // 비트맵을 출력
6. DC를 복원한다.
ex) memdc.SelectObject(oldbitmap);
● 비트맵의 래스터 오퍼레이션
비트맵의 래스터 오퍼레이션은 펜과 브러시처럼 SetROP2() 함수를 사용하지 않고 BitBlt() 함수와 StretchBlt() 함수의 마지막 인자로 비트맵의 래스터 오퍼레이션 코드를 넣어주면 된다. 다음은 자주 사용하는 비트맵의 래스터 오퍼레이션 코드이다.
SRCCOPY : 배경 그림을 무시하고 출력될 비트맵을 화면에 덮는다.
SRCAND : 배경 그림과 출력될 비트맵을 AND 연산으로 합성한다.
SRCPAINT : 배경 그림과 출력될 비트맵을 OR 연산으로 합성한다.
4) 폰트(Font)
폰트는 CFont 클래스를 이용하는 객체로 문자를 출력할 때 사용하며 글자의 모양, 크기가 설정된다. 폰트에는 논리적인 폰트와 물리적인 폰트가 있다. 논리적인 폰트는 이상적인 폰트에 대한 표현으로 실제로 존재하는 것은 아니며 가장 유사한 물리적인 폰트를 얻기 위해 사용한다. 물리적인 폰트는 실제로 시스템에 설치되어 있는 폰트를 의미하고 실제로 화면에 나타낸다. 우리가 폰트를 출력하기 위해서는 원하는 폰트에 대한 논리적인 폰트를 LOGFONT 타입으로 기술하여 생성하고 DC에 폰트를 선택하여 넣는다. 그러면 윈도우 GDI 폰트 맵퍼가 시스템에 설치되어 있는 폰트들 중에서 논리적인 폰트와 가장 가까운 물리적인 폰트를 찾아내어 출력한다. 다음은 폰트를 사용하는 방법이다.
1. 폰트를 생성한다.
ex) CFont font, *oldfont; // Font 클래스의 인스턴스 생성
font.CreateFont(20, 20, // 폰터의 너비와 높이
0, 0, // 기울어진 각도
FW_DONTCARE, // 폰트의 굵기
FALSE, FALSE, // 폰트의 기울임꼴과 밑줄
FALSE, DEFAULT_CHARSET, // 폰트의 취소선과 문자 세트
OUT_DEFAULT_PRECIS, // 출력 정확도
CLIPDEFAULT_PRECIS, // 클리핑 정확도
DEFAULT_QUALITY, DEFAULT_PITCH, // 폰트의 질과 자간
"굴림체"); // 폰트의 이름
또 다른 폰트를 생성하는 방법은 CreateFontIndirect() 함수를 이용하여 LONGFONT 구조체를 설정하여 폰트를 생성하는 것이다.
ex) char fontname[50];
strcpy(fontname, "굴림체");
LONGFONT logfont = {20, 20, // 폰터의 너비와 높이
0, 0, // 기울어진 각도
FW_DONTCARE, // 폰트의 굵기
FALSE, FALSE, // 폰트의 기울임꼴과 밑줄
FALSE, DEFAULT_CHARSET, // 폰트의 취소선과 문자 세트
OUT_DEFAULT_PRECIS, // 출력 정확도
CLIPDEFAULT_PRECIS, // 클리핑 정확도
DEFAULT_QUALITY, DEFAULT_PITCH, // 폰트의 질과 자간
fontname[0]}; // 폰트의 이름
2. 폰트를 등록하고 사용하고 삭제하는 방법은 위의 펜 사용법 2,3,4,5번과 같다.
● 폰트 관련 그래픽 함수
폰트 관련 그래픽 함수에는 텍스트 출력 함수와 텍스트의 모양에 영향을 미치는 속성을 지정하는 함수가 있다.
1. 텍스트 출력 함수
TextOut() : DC에 선택되어 있는 폰트를 참조하여 지정된 위치에 문자열를 출력
ex) dc.TextOut(0, 0, "안녕하세요?") // (0, 0) 좌표에 "안녕하세요?" 출력
TabbedTextOut() : TextOut() 함수에 탭 위치를 지정하는 기능이 추가된 함수
ExtTextOut() : TextOut() 함수의 기능에 문자열이 출력될 영역의 범위를 지정하는 기능이 추가된 함수
DrawText() : 지정된 사각형 안에 문자열 출력
2. 텍스트의 모양에 영향을 미치는 속성을 지정하는 함수
SetTextColor() : 배경색은 그대로 두고 텍스트의 전경색을 설정
ex) dc.SetTextColor(RGB(0, 0, 255)); // 텍스트의 색을 파란색으로 설정
SetBkColor() : 텍스트의 배경색을 설정
ex) dc.SetBkColor(RGB(255, 0, 0)); // 텍스트 배경의 색을 빨간색으로 설정
SetBkMode() : 텍스트가 출력될 때 사각형으로 텍스트를 둘러싸는 배경색을 설정
SetTextAlign() : 텍스트의 정렬방법을 지정
ex) dc.SetTextAlign(TA_LEFT | TA_TOP); // 가로기준-왼쪽, 세로기준-위쪽 정렬
● 폰트 다이얼로그 출력하여 폰트를 선택하는 방법
다음 예는 "폰트 변경" 메뉴가 선택되었을 때 명령 메시지 핸들러 함수를 만들어 폰트 변경 대화상자에서 폰트를 선택하는 코드를 추가하는 것이다.
ex) void CTestFontView::OnFontSelect()
{
CFontDialog dlg(&logfon) // CFontDialog 인스턴스 생성
if(dlg.DoModal() == IDOK) // OK 버튼을 클릭하면
{
dlg.GetCurrentFont(&logfont); // 사용자가 선택한 폰트를 logfont에 저장
Invalidate(); // 화면 갱신
}
}
COLORREF 데이터형과 RGB 매크로
윈도우에서는 색상을 COLORREF 데이터형의 32비트 값으로 표현한다. COLORREF는 RGB 매크로를 이용하여 조작할 수 있으며 RGB 매크로의 원형은 다음과 같다.
COLORREF RGB(BYTE bRed, BYTE bGreen, BYTE bBlue);
( bRed : red 색상의 값으로 범위는 0 ~ 255이다.
bGreen : green 색상의 값으로 범위는 0 ~ 255이다.
bBlue : blue 색상의 값으로 범위는 0 ~ 255이다. )
SetCapture()와 ReleaseCapture() 함수
마우스에 대한 모든 이벤트를 가져오고자 할 때 사용되며, 러버밴드 구현시에는 왼쪽 마우스 버튼을 누를 때 호출하면 유용하다. 반환 값은 마우스 입력에 대한 윈도우 객체 포인터로 함수의 원형은 다음과 같다.
Cwnd* SetCapture();
SetCapture() 함수는 나중에 ReleaseCapture() 함수로 해제시켜 주어야 마우스 이벤트 외에 다른 이벤트를 사용할 수 있다. ReleaseCapture() 함수로 해제하지 않는다면 다른 이벤트가 사용되지 않을 것이다. 반환 값은 이 함수가 제대로 실행된다면 0이 아니고 제대로 실행되지 않는다면 0이 된다. 함수의 원형은 다음과 같다.
BOOL ReleaseCapture(VOID);
ClipCursor() 함수
매개 변수로 전달된 사각형 구조체 영역을 마우스의 이동 범위를 제한하는데 사용되며 함수의 원형은 다음과 같다.
BOOL ClipCursor(CONST RECT *lpRect);
( lpRect : 제한하고자 하는 이동범위를 나타내는 RECT 포인터 )
마우스 이동 범위를 다시 해제하기 위해서는 매개 변수에 NULL 값을 넣고 호출하면 된다.
ClientToScreen() 함수
매개 변수로 전달되는 사각형 구조체의 좌표가 클라이언트 좌표일 경우, 자동으로 스크린 좌표로 변환시켜주는 데 사용되며 함수의 원형은 다음과 같다.
void ClientToScreen(LPRECT lpRect) const;
( lpRect : 변환될 클라이언트 좌표를 나타내는 RECT 포인터 )
GetRValue(), GetGValue(), GetBValue()
GetRValue() 함수는 RGB 값을 가진 변수(EX. COLORREF 변수 등등)에서 Red 값을 추출하여 반환하여 준다. 나머지 GetGValue()함수는 Green색, GetBValue() 함수는 Blue색을 추출하여 준다.
dBYTE GetRValue( DWORD rgb );
dBYTE GetGValue( DWORD rgb );
dBYTE GetBValue( DWORD rgb );
( rgb : 32Bit의 RGB값을 가진 변수이다. )
CBitmap::LoadBitmap() 함수
LoadBitmap() 함수는 애플리케이션의 실행 파일로부터 명명된 비트맵 리소스를 로드하는 함수로 원형은 다음과 같다.
BOOL LoadBitmap(LPCTSTR lpszResourceName);
BOOL LoadBitmap(UINT nIDResource);
( lpszResourceName : 비트맵 리소스의 이름을 내포하는 널 문자열의 위치
nIDResource : 비트맵 리소스의 ID를 명시 )
CDC::BitBlt() 함수
BitBlt() 함수는 비트맵 객체를 참조하는 그래픽 함수로서 어떤 DC에서 다른 DC로 비트맵 블록을 전송하는 함수이다. 이 함수의 원형은 다음과 같다.
BOOL CDC::BitBlt (int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int nXSrc, int nYSrc, DWORD dwRop);
( x, y : 출력할 화면의 x 위치, y 위치
nWidth, nHeight : 화면에 출력할 비트맵의 가로 크기, 세로 크기
pSrcDC : 비트맵을 전송할 소스 DC의 포인터
nXSrc, nYSrc : 소스 비트맵에서 전송할 DC의 시작 X 위치, Y 위치
dwRop : 래스터 오퍼레이션 코드(ROP) )
CDC::StretchBlt() 함수
StretchBlt() 함수는 비트맵 객체를 참조하는 그래픽 함수로서 어떤 DC에서 다른 DC로 비트맵 블록을 전송하되 확대 또는 축소를 해서 전송하는 함수이다. StretchBlt() 함수는 BitBlt() 함수와 비슷한데 소스 비트맵의 가로크기와 세로크기를 인자로 더 받는다. 화면에 출력할 비트맵의 크기가 소스 비트맵의 크기보다 크면 그림이 확대 출력되고 반대인 경우 그림이 축소되어 출력된다. nWidthSrc이 음수이면 그림이 좌우로 뒤집히고 nHeightSrc가 음수이면 그림이 상하로 뒤집힌다. 이 함수의 원형은 다음과 같다.
BOOL CDC::StretchBlt (int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int nXSrc, int nYSrc, nWidthSrc, nHeightSrc, DWORD dwRop);
( x, y : 출력할 화면의 x 위치, y 위치
nWidth, nHeight : 화면에 출력할 비트맵의 가로 크기, 세로 크기
pSrcDC : 비트맵을 전송할 소스 DC의 포인터
nXSrc, nYSrc : 소스 비트맵에서 전송할 DC의 시작 X 위치, Y 위치
nWidthSrc, nHeightSrc : 소스 비트맵의 가로 크기, 세로 크기
dwRop : 래스터 오퍼레이션 코드(ROP) )
윈도우 프로그램은 윈도우의 화면에 출력되는 모든 내용을 DC를 이용하여 출력하게 된다. DC는 그래픽 객체들의 속성과 그래픽 모드를 정의하는 자료의 집합체로 출력 장치에 정보를 표시할 때 필요한 정보를 저장하는 자료구조이다.
DC는 애플리케이션과 출력 장치를 연결하는 역할을 하며 애플리 케이션이 출력에 대한 허가를 얻도록 하고 또한 그려지는 영역을 결정하는 역할을 한다. DC를 사용하는 이유는 하드웨어 독립적인 출력을 할 수 있어 출력 장치에 상관없이 동일한 방법으로 출력을 설정할 수 있기 때문이다.
그래픽 객체의 속성에 관련된 모든 옵션이 디폴트로 설정되어 있어 그냥 간단한 출력을 하고자 할 때는 디폴트로 설정된 DC를 이용하여 바로 출력하면 되고 세세한 것을 제어하고자 할 때는 DC에 설정된 내용을 바꾼 후에 출력한다. DC는 한 순간에 하나의 펜, 브러시만 가질 수 있어 다른 펜이나 다른 브러시를 사용하려면 새로운 것을 만들어서 DC에 설정해주어야 한다. 윈도우에서 출력을 하려면 먼저 DC를 얻어야 한다. DC를 얻는 방법은 다음과 같이 4가지 방법이 있다.
▶ DC를 얻는 방법
1) OnDraw() 함수나 OnPaint() 함수를 이용하는 방법
OnDraw() 함수에서 인자로 넘어온 CDC 클래스를 받거나 OnPaint() 함수에서 CPaintDC 클래스를 받아 이용한다. 화면의 변화가 생겼을 때 계속적으로 OnDraw() 함수나 OnPaint() 함수를 호출하기 때문에 윈도우의 크기가 변하거나 다른 프로그램에 의해 가려졌다가 다시 출력되어도 화면 출력이 변하지 않는다.
ex)
void CPractice6_1View::OnDraw(CDC* pDC)
{
.... // 그래픽 출력
}
void CPractice6_1View::OnPaint()
{
CPaintDC dc(this);
.... // 그래픽 출력
}
2) GetDC() 함수를 이용하는 방법
GetDC() 함수를 이용하여 CDC 클래스의 인스턴스를 포인터 형태로 넘겨받아 이용한다. 그래픽 작업을 종료한 후에 반드시 ReleaseDC() 함수를 호출하여 시스템에 DC를 반환해야 한다. 이 방법으로 DC를 얻는 것은 일시적인 것으로 윈도우의 크기가 변하면 출력한 내용이 사라진다. 그 이유는 화면의 변화가 생겼을 때 WM_PAINT 메시지가 호출되고 OnDraw() 함수나 OnPaint() 함수가 호출되면서 화면이 갱신되기 때문이다.
ex)
CDC *pDC = GetDC(); // DC 얻음
...... // 그래픽 출력
ReleaseDC(pDC); // DC 반환
3) CWindowDC 클래스를 이용하는 방법
위의 3가지 방법은 클라이언트 영역을 얻는 방법이다. CWindowDC 클래스는 클라이언트의 영역이 아닌 윈도우 영역에 그래픽을 출력하고자 할 때 이용한다. 이 방법은 GetWindowDC() 함수를 이용하여 CWindowDC 클래스의 인스턴스를 포인터 형태로 넘겨받아 이용한다. 그래픽 작업을 종료한 후에 반드시 ReleaseDC() 함수를 호출하여 시스템에 DC를 반환해야 한다.
ex)
CWindowDC *pDC = GetWindowDC(); // 윈도우 DC 얻음
........ // 그래픽 출력
ReleaseDC(pDC); // DC 반환
GDI 객체
GDI 객체는 화면에 그림을 그리거나 문자를 출력할 때 사용하는 객체를 의미한다. 즉 펜, 브러시, 폰트, 비트맵 등을 핸들링 하는 핸들을 GDI 객체라고 하며 이 GDI 객체의 핸들을 가지고 있는 클래스를 GDI 클래스라 한다.
GDI 객체의 종류, GDI 객체 클래스의 종류, 기본 값, 사용 용도는 다음과 같다.
|
GDI 객체 |
GDI 객체 클래스 |
기본 값 |
사용 용도 |
| 펜 | CPen | 검정색, 실선, 1픽셀 크기 | 점, 선, 테두리 |
| 브러시 | CBrush | 무늬 없는 흰색 | 내부 채우기 |
| 폰트 | CFont | 시스템 폰트 | 문자의 폰트 |
| 비트맵 | CBitmap | 없음 | 비트맵 출력 |
| 영역 | CRgn | 없음 | 영역 만들기, 변경하기 |
| 팔레트 | CPallete | 없음 | 팔레트 조작 |
GDI 객체는 그래픽 옵션을 저장하기 위한 것들이기 때문에 어떤 그래픽 옵션을 변경하고자 하는 경우에는 먼저 GDI 객체의 클래스를 변경하여 생성한 후에 이것을 DC에 넣고 이 DC를 이용하여 그림을 그리도록 한다. GDI 객체를 사용하는 방법을 정리해보면 다음과 같다.
1. GDI 객체를 생성한다. => GDI 객체 클래스의 Create() 계열 함수 사용
2. 객체를 DC에 등록시킨다. => SelectObject() 함수 이용 (DC를 쓰고 난 다음 원래 상태로 복원하기 위해 기존에 설정되어 있는 객체를 포인터로 받아둔다.)
3. DC를 사용하여 그래픽을 출력한다.
4. 이전 객체로 되돌린다. => DC를 원래 상태로 복원
5. 객체를 삭제한다. => GDI 객체 클래스의 DeleteObject() 함수 이용
이제부터 GDI 객체에서 가장 많이 쓰이는 펜, 브러시, 비트맵, 폰트에 대하여 알아보자.
1) 펜(Pen)
펜은 CPen 클래스를 이용하는 객체로 선이나 영역의 경계선을 그릴 때 사용하며 선의 두께, 선의 색상, 선의 스타일이 설정된다. 다음은 펜을 이용하는 방법이다.
1. 펜을 생성한다.
ex) CPen pen, *oldpen; // Pen 클래스의 인스턴스 생성
pen. CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); // 실선이고 두께가 1인 검정 색의 펜을 생성
또는 간편하게 CPen 생성자에서 펜 속성을 초기화 해줘도 된다.
ex) CPen pen(PS_SOLID, 1, RGB(0, 0, 0))
Cpen::CreatePen() 함수
Creae() 함수는 Pen을 생성하는 함수로 원형은 다음과 같다.
BOOL CreatePen(int nPenStyle, int nWidth, COLORREF crColor);
( nPenStyle : 펜의 스타일
nWidth : 펜의 굵기
crColor : 펜의 색상 )
* 펜의 스타일
PS_SOLID : 실선, PS_DASH : 파선, PS_DOT : 점선
PS_DASHDOT : 일점쇄선, PS_DASHDOTDOT : 이전쇄선, PS_NULL : 선을 그리지 않음
2. 펜을 DC에 등록한다.
ex) oldpen = pDC->SelectObject(&pen);
3. DC를 이용하여 그래픽을 출력한다.
ex) pDC->Ellipse (0, 0, 10, 10);
4. 이전 펜으로 되돌린다.
ex) pDC->SelectObject(oldpen);
5. 펜을 삭제한다.
ex) pen.DeleteObject();
2) 브러시
브러시는 CBrush 클래스를 사용하는 객체로 영역의 내부를 채울 때 사용되며, 채울 때, 패턴 등이 설정된다. 다음은 브러시를 사용하는 방법이다.
1. 브러시를 생성한다.
a. 단일 색으로 칠하는 브러시를 생성한다.
ex) CBrush brush, *oldbrush; // Brush 클래스의 인스턴스 생성
brush.CreateSolidBrush(RGB(0, 0, 0)); // 검정색의 브러시를 생성
또는 간단하게 CBrush 클래스의 생성자에서 브러시 속성을 초기화 해줘도 된다.
ex) CBrush brush(RGB(0, 0, 0));
b. 일정한 패턴을 가진 해치 브러시를 생성한다.
ex) CBrush brush; // Brush 클래스의 인스턴스 생성
brush.CreateHatchBrush(HS_CROSS, RGB(0, 0, 0)); // 십자가 형태의 빗금을 가진 검정 색 브러시 생성
2. 브러시를 등록하여 사용하고 삭제하는 방법은 위의 펜의 사용 법 2,3,4,5번과 같다.
CBrush::CreateSolidBrush() 함수
CreateSolidBrush() 함수는 단일 색으로 칠하는 브러시를 생성하는 함수로 원형은 다음과 같다.
BOOL CreateSolidBrush(COLORREF crColor);
( crColor : 브러시의 색상 )
CBrush::CreateHatchBrush() 함수
CreateHatchBrush() 함수는 일정한 패턴을 가진 해치 브러시를 생성하는 함수로 원형은 다음과 같다.
BOOL CreateHatchBrush (int nIndex, COLORREF crColor);
( nIndex : 해치브러시 스타일
crColor : 브러시의 색상 )
* 해치브러시의 스타일
HS_BDIAGONAL : 오른쪽에서 왼쪽으로 45도 내려가는 빗금
HS_CROSS : 십자가 형태의 빗금
HS_DIAGCROSS : X자 형태의 빗금
HS_FDIAGONAL : 왼쪽에서 오른쪽 45도 내려가는 빗금
HS_HORIZONTAL : 수평으로 빗금
HS_VERTICAL : 수직으로 빗금
● 펜과 브러시를 이용한 그래픽 함수
다음은 펜과 브러시를 이용한 그래픽 함수이다. 이 함수들은 모두 현재 DC에 설정되어 있는 펜을 이용하여 경계선을 그리고, 도형인 경우에는 안쪽을 현재 DC에 설정되어 있는 브러시로 치한다. 펜과 브러시를 등록하지 않으면 기본적으로 등록되어있는 검정색 펜과 흰색 브러시를 이용하여 도형을 그린다. 다음은 자주 사용하는 그래픽 함수이다.
1. 선 그리기
선을 그리기 위해 두 가지 함수를 사용하는데 MoveTo() 함수는 선을 그리기 위해 시작 위치로 이동하는 함수이고 LineTo() 함수는 시작 위치에서 선을 그릴 끝 위치로 선을 그리는 함수이다. 또한 LineTo() 함수는 그려진 선의 끝 위치에 현재 지점을 새로 설정한다. 따라서 연결된 직선을 그릴 때는 처음 한번만 MoveTo() 함수를 호출하고 LineTo() 함수만 계속 호출하면 될것이다. 함수의 원형은 다음과 같다.
CPoint MoveTo(int x, int y);
CPoint MoveTo(POINT point);
( x, y : 선을 그릴 시작 위치로 이동하는 x, y 좌표
point : 선을 그릴 시작 위치로 이동하는 x, y 좌표를 가지고 있는 구조체 )
BOOL LineTo(int x, int y);
BOOL LineTo(POINT point);
( x, y : 선을 그릴 끝 위치로 이동하는 x, y 좌표
point : 선을 그릴 끝 위치로 이동하는 x, y 좌표를 가지고 있는 구조체 )
2. 사각형 그리기
사각형을 그리는 함수로서 함수의 원형은 다음과 같다.
BOOL Rectangle(int x1, int y1, int x2, int y2);
BOOL Rectangle(LPCRECT lpRect);
( x1, y1 : 그려질 사각형의 좌측 상단 x, y 좌표
x2, y2 : 그려질 사각형의 우측 하단 x, y 좌표
lpRect : 그려질 사각형의 x1, y1, x2, y2의 값을 저장하고 있는 객체 )
ex) pDC->Rectangle(0, 0, 50, 50); // (0, 0) 과 (50, 50)을 꼭짓점으로 하는 사각형 그림
3. 원 그리기
사각형 영역 안에 원을 그리는 함수로서 함수의 원형은 다음과 같다.
BOOL Ellipse(int x1, int y1, int x2, int y2);
BOOL Ellipse(LPCRECT lpRect);
( x1, y1 : 원이 그려질 사각형의 좌측 상단 x, y 좌표
x2, y2 : 원이 그려질 사각형의 우측 하단 x, y 좌표
lpRect : 원을 그리는 사각형의 x1, y1, x2, y2의 값을 저장하고 있는 객체 )
ex) pDC->Ellpse(0, 0, 50, 50); // (0, 0) 과 (50, 50)을 꼭짓점으로 하는 사각형에 내접하는 타원을 그림
4. 다각형 그리기
다각형을 그리는 함수에는 닫히지 않은 다각형의 외곽선만을 그리는 Polyline() 함수와 닫힌 다각형을 그리는 Polygon() 함수가 있다. Polyline() 함수를 이용하면 MoveTo() 함수와 LineTo() 함수를 이용할 때처럼 연속적인 직선을 그릴 수 있다. 그러나 닫힌 다각형을 그리는 것이 아니므로 Polyline() 함수로 직선을 연결해서 닫히 다각형을 그려도 닫힌 다각형처럼 내부에 자동적으로 색상이 칠해지지 않는다. Polygon() 함수는 Polyline() 함수와 달리 항상 닫힌 다각형을 그려준다. 닫힌 도형을 그리기 위해 이 함수는 마지막 꼭짓점에서 첫 꼭짓점까지 직선을 그린다. 함수의 원형은 다음과 같다.
BOOL Polyline(LPPOINT lpPoints, int nCount);
BOOL Polygon(LPPOINT lpPoints, int nCount);
( lpPoints : 다각형의 좌표들을 가지고 있는 POINT 구조체 배열의 이름
nCount : 다각형 꼭짓점의 개수(배열내의 점의 개수 )
ex) CPOINT ptData[3];
ptData[0].x = 200;
ptData[0].y = 100;
ptData[1].x = 100;
ptData[1].y = 300;
ptData[2].x = 300;
ptData[2].y = 300;
ptData[3].x = 300;
ptData[3].y = 300;
pDC->Polyline(ptData, 3); 혹은 pDC->Polygon(ptData, 3);
5. 베지어 곡선 그리기
베지어 곡선이란 다각형의 끝점을 지나며 중간 꼭짓점에 근접한 곡선이다. Cubic 베지어 곡선이란 control point가 4개가 있는 베지어 곡선을 말한다. Cubic 베지어 곡선을 그리려면 PolyBezier() 함수를 이용하며 함수의 원형은 다음과 같다.
BOOL PolyBezier(const POINT* lpPoints, int nCount);
( lpPoints : control pointdp eogks POINT 구조체 배열의 포인터
nCount : 배열내의 점의 개수 )
첫 번째 지점은 곡선의 시작 위치이고 두 번째와 세 번째 지점은 곡선의 모양을 조정하는데 사용되며 네 번째 지점은 곡선의 끝 위치를 나타낸다. 베지어 곡선에 또 하나의 베지어 곡선을 추가하려면 배열에 세 지점만 추가하면 된다. 그러면 cubic 베지어 곡선의 끝 위치를 시작점으로 하고 세 번째 지점이 새 베지어 곡선의 끝 지점이된다. 나머지 두 개의 지점은 곡선의 모양을 조절한다. 일반적으로 여러개 연결된 cubic 베지어 곡선을 그리려면 배열의 요소수는 그리기를 원하는 베지어곡선수 x 3 + 1이 되어야 한다.
ex) CPOINT m_ptData[4];
m_ptData[0].x = 200;
m_ptData[0].y = 100;
m_ptData[1].x = 100;
m_ptData[1].y = 300;
m_ptData[2].x = 300;
m_ptData[2].y = 300;
m_ptData[3].x = 300;
m_ptData[3].y = 300;
pDC->PolyBezier(m_ptData, 4);
● 래스터 오퍼레이션(Raster Operation)
래스터 오퍼레이션이란 새로 그려져야 할 그림과 기존에 화면에 그려져 있는 그림을 합성하는 것을 말한다. 래스터 오퍼레이션은 펜과 브러시에 적용되며 SetROP2() 함수로 설정한다.
ex) SetROP2(R2_XORPEN); // 펜과 브러시에 XORPEN으로 설정
다음은 자주 사용하는 래스터 오퍼레이션 코드이다.
R2_COPYPEN
일반적으로 사용하는 방법으로 배경의 화면을 무시하고 새로 그려지는 그림이 출력된다. 기존에 어떤 그림이 그려있는지 상관없이 새로 그려지는 그림이 화면을 덮는다. 기본 옵션으로 선택되어 있다.
R2_XORPEN
배경의 화면을 유지하면서 움직이는 그림을 그릴 때 사용한다. R2_XORPEN으로 한번 그려주면 그림이 그려지고 같은 방법으로 다시 한번 그리면 원래의 바탕색이 복원된다.
● 내장(Stock) GDI 객체
자주 쓰이는 스타일의 GDI 객체는 윈도우가 내장하고 있어 따로 만들지 않고 윈도우에서 얻어서 사용한다. 윈도우로부터 내장 GDI 객체를 얻으려면 GetStockObject() 함수를 이용한다.
ex) dc.SelectObject(GetStockObject(WHITE_PEN));
내장 GDI 객체는 다음과 같다.
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
3) 비트맵(Bitmap)
비트맵은 CBitmap 클래스를 이용하는 객체로 비트맵을 생성하거나 읽어서 비트맵을 출력할 때 사용한다. 비트맵은 리소스에서 비트맵을 생성하는 방법과 별개의 드로잉 프로그램에서 비트맵을 생성하여 리소스에 등록하여 사용하는 방법이 있다. 일반적으로 비트맵은 별개의 드로잉 프로그램에서 미리 생성해서 파일로 저장해두고 비트맵을 읽어와 리소스에 등록하여 사용한다. 다음은 비트맵을 사용하는 방법이다.
1. 화면 DC와 메모리 DC를 생성한다.
비트맵을 출력하려면 비트맵이 출력될 화면 윈도우의 DC와 메모리에서 만들어진 DC 두 가지가 있어야 한다. 메모리에서 만들어진 DC에서 화면 윈도우 DC에게 비트맵 블록을 전송해야 화면에 비트맵이 출력된다.
ex) CClientDC dc(this);
CDC memdc;
2. 화면 DC와 호환성을 갖는 메모리 DC를 만든다.
DC를 두 개 생성했다고 해서 비트맵 블록을 전송할 수 있는 것이 아니라 두 DC가 호환성이 있어야 한다. 화면 윈도우의 DC의 포인터를 인자로 넘겨주어 메모리에서 만들어진 DC 인스턴스에 대해 CDC 클래스의 멤버 함수인 CreateCompatibleDC() 함수를 호출해주면 두 DC가 호환성이 있게 한다.
ex) memdc. CreateCompatibleDC(&dc); // 현재 DC와 호환적인 메모리 DC 생성
3. 비트맵을 읽어온다. LoadBitmap() 함수의 인자로 비트맵 ID를 입력한다.
ex) Cbitmap bitmap, *oldbitmap; // 비트맵의 인스턴스 생성
bitmap.LoadBitmap(IDB_BITMAP1); // 비트맵을 읽어옴
4. 메모리 DC에 비트맵을 설정한다.
ex) oldbitmap = memdc.SelectObject(&bitmap); // 메모리 DC에 비트맵을 설정
5. 비트맵 블록을 전송한다.
ex) dc.BitBlt (0, 0, 450, 85, &memdc, 0 , 0, SRCCOPY); // 비트맵을 출력
6. DC를 복원한다.
ex) memdc.SelectObject(oldbitmap);
● 비트맵의 래스터 오퍼레이션
비트맵의 래스터 오퍼레이션은 펜과 브러시처럼 SetROP2() 함수를 사용하지 않고 BitBlt() 함수와 StretchBlt() 함수의 마지막 인자로 비트맵의 래스터 오퍼레이션 코드를 넣어주면 된다. 다음은 자주 사용하는 비트맵의 래스터 오퍼레이션 코드이다.
SRCCOPY : 배경 그림을 무시하고 출력될 비트맵을 화면에 덮는다.
SRCAND : 배경 그림과 출력될 비트맵을 AND 연산으로 합성한다.
SRCPAINT : 배경 그림과 출력될 비트맵을 OR 연산으로 합성한다.
4) 폰트(Font)
폰트는 CFont 클래스를 이용하는 객체로 문자를 출력할 때 사용하며 글자의 모양, 크기가 설정된다. 폰트에는 논리적인 폰트와 물리적인 폰트가 있다. 논리적인 폰트는 이상적인 폰트에 대한 표현으로 실제로 존재하는 것은 아니며 가장 유사한 물리적인 폰트를 얻기 위해 사용한다. 물리적인 폰트는 실제로 시스템에 설치되어 있는 폰트를 의미하고 실제로 화면에 나타낸다. 우리가 폰트를 출력하기 위해서는 원하는 폰트에 대한 논리적인 폰트를 LOGFONT 타입으로 기술하여 생성하고 DC에 폰트를 선택하여 넣는다. 그러면 윈도우 GDI 폰트 맵퍼가 시스템에 설치되어 있는 폰트들 중에서 논리적인 폰트와 가장 가까운 물리적인 폰트를 찾아내어 출력한다. 다음은 폰트를 사용하는 방법이다.
1. 폰트를 생성한다.
ex) CFont font, *oldfont; // Font 클래스의 인스턴스 생성
font.CreateFont(20, 20, // 폰터의 너비와 높이
0, 0, // 기울어진 각도
FW_DONTCARE, // 폰트의 굵기
FALSE, FALSE, // 폰트의 기울임꼴과 밑줄
FALSE, DEFAULT_CHARSET, // 폰트의 취소선과 문자 세트
OUT_DEFAULT_PRECIS, // 출력 정확도
CLIPDEFAULT_PRECIS, // 클리핑 정확도
DEFAULT_QUALITY, DEFAULT_PITCH, // 폰트의 질과 자간
"굴림체"); // 폰트의 이름
또 다른 폰트를 생성하는 방법은 CreateFontIndirect() 함수를 이용하여 LONGFONT 구조체를 설정하여 폰트를 생성하는 것이다.
ex) char fontname[50];
strcpy(fontname, "굴림체");
LONGFONT logfont = {20, 20, // 폰터의 너비와 높이
0, 0, // 기울어진 각도
FW_DONTCARE, // 폰트의 굵기
FALSE, FALSE, // 폰트의 기울임꼴과 밑줄
FALSE, DEFAULT_CHARSET, // 폰트의 취소선과 문자 세트
OUT_DEFAULT_PRECIS, // 출력 정확도
CLIPDEFAULT_PRECIS, // 클리핑 정확도
DEFAULT_QUALITY, DEFAULT_PITCH, // 폰트의 질과 자간
fontname[0]}; // 폰트의 이름
2. 폰트를 등록하고 사용하고 삭제하는 방법은 위의 펜 사용법 2,3,4,5번과 같다.
● 폰트 관련 그래픽 함수
폰트 관련 그래픽 함수에는 텍스트 출력 함수와 텍스트의 모양에 영향을 미치는 속성을 지정하는 함수가 있다.
1. 텍스트 출력 함수
TextOut() : DC에 선택되어 있는 폰트를 참조하여 지정된 위치에 문자열를 출력
ex) dc.TextOut(0, 0, "안녕하세요?") // (0, 0) 좌표에 "안녕하세요?" 출력
TabbedTextOut() : TextOut() 함수에 탭 위치를 지정하는 기능이 추가된 함수
ExtTextOut() : TextOut() 함수의 기능에 문자열이 출력될 영역의 범위를 지정하는 기능이 추가된 함수
DrawText() : 지정된 사각형 안에 문자열 출력
2. 텍스트의 모양에 영향을 미치는 속성을 지정하는 함수
SetTextColor() : 배경색은 그대로 두고 텍스트의 전경색을 설정
ex) dc.SetTextColor(RGB(0, 0, 255)); // 텍스트의 색을 파란색으로 설정
SetBkColor() : 텍스트의 배경색을 설정
ex) dc.SetBkColor(RGB(255, 0, 0)); // 텍스트 배경의 색을 빨간색으로 설정
SetBkMode() : 텍스트가 출력될 때 사각형으로 텍스트를 둘러싸는 배경색을 설정
SetTextAlign() : 텍스트의 정렬방법을 지정
ex) dc.SetTextAlign(TA_LEFT | TA_TOP); // 가로기준-왼쪽, 세로기준-위쪽 정렬
● 폰트 다이얼로그 출력하여 폰트를 선택하는 방법
다음 예는 "폰트 변경" 메뉴가 선택되었을 때 명령 메시지 핸들러 함수를 만들어 폰트 변경 대화상자에서 폰트를 선택하는 코드를 추가하는 것이다.
ex) void CTestFontView::OnFontSelect()
{
CFontDialog dlg(&logfon) // CFontDialog 인스턴스 생성
if(dlg.DoModal() == IDOK) // OK 버튼을 클릭하면
{
dlg.GetCurrentFont(&logfont); // 사용자가 선택한 폰트를 logfont에 저장
Invalidate(); // 화면 갱신
}
}
COLORREF 데이터형과 RGB 매크로
윈도우에서는 색상을 COLORREF 데이터형의 32비트 값으로 표현한다. COLORREF는 RGB 매크로를 이용하여 조작할 수 있으며 RGB 매크로의 원형은 다음과 같다.
COLORREF RGB(BYTE bRed, BYTE bGreen, BYTE bBlue);
( bRed : red 색상의 값으로 범위는 0 ~ 255이다.
bGreen : green 색상의 값으로 범위는 0 ~ 255이다.
bBlue : blue 색상의 값으로 범위는 0 ~ 255이다. )
SetCapture()와 ReleaseCapture() 함수
마우스에 대한 모든 이벤트를 가져오고자 할 때 사용되며, 러버밴드 구현시에는 왼쪽 마우스 버튼을 누를 때 호출하면 유용하다. 반환 값은 마우스 입력에 대한 윈도우 객체 포인터로 함수의 원형은 다음과 같다.
Cwnd* SetCapture();
SetCapture() 함수는 나중에 ReleaseCapture() 함수로 해제시켜 주어야 마우스 이벤트 외에 다른 이벤트를 사용할 수 있다. ReleaseCapture() 함수로 해제하지 않는다면 다른 이벤트가 사용되지 않을 것이다. 반환 값은 이 함수가 제대로 실행된다면 0이 아니고 제대로 실행되지 않는다면 0이 된다. 함수의 원형은 다음과 같다.
BOOL ReleaseCapture(VOID);
ClipCursor() 함수
매개 변수로 전달된 사각형 구조체 영역을 마우스의 이동 범위를 제한하는데 사용되며 함수의 원형은 다음과 같다.
BOOL ClipCursor(CONST RECT *lpRect);
( lpRect : 제한하고자 하는 이동범위를 나타내는 RECT 포인터 )
마우스 이동 범위를 다시 해제하기 위해서는 매개 변수에 NULL 값을 넣고 호출하면 된다.
ClientToScreen() 함수
매개 변수로 전달되는 사각형 구조체의 좌표가 클라이언트 좌표일 경우, 자동으로 스크린 좌표로 변환시켜주는 데 사용되며 함수의 원형은 다음과 같다.
void ClientToScreen(LPRECT lpRect) const;
( lpRect : 변환될 클라이언트 좌표를 나타내는 RECT 포인터 )
GetRValue(), GetGValue(), GetBValue()
GetRValue() 함수는 RGB 값을 가진 변수(EX. COLORREF 변수 등등)에서 Red 값을 추출하여 반환하여 준다. 나머지 GetGValue()함수는 Green색, GetBValue() 함수는 Blue색을 추출하여 준다.
dBYTE GetRValue( DWORD rgb );
dBYTE GetGValue( DWORD rgb );
dBYTE GetBValue( DWORD rgb );
( rgb : 32Bit의 RGB값을 가진 변수이다. )
CBitmap::LoadBitmap() 함수
LoadBitmap() 함수는 애플리케이션의 실행 파일로부터 명명된 비트맵 리소스를 로드하는 함수로 원형은 다음과 같다.
BOOL LoadBitmap(LPCTSTR lpszResourceName);
BOOL LoadBitmap(UINT nIDResource);
( lpszResourceName : 비트맵 리소스의 이름을 내포하는 널 문자열의 위치
nIDResource : 비트맵 리소스의 ID를 명시 )
CDC::BitBlt() 함수
BitBlt() 함수는 비트맵 객체를 참조하는 그래픽 함수로서 어떤 DC에서 다른 DC로 비트맵 블록을 전송하는 함수이다. 이 함수의 원형은 다음과 같다.
BOOL CDC::BitBlt (int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int nXSrc, int nYSrc, DWORD dwRop);
( x, y : 출력할 화면의 x 위치, y 위치
nWidth, nHeight : 화면에 출력할 비트맵의 가로 크기, 세로 크기
pSrcDC : 비트맵을 전송할 소스 DC의 포인터
nXSrc, nYSrc : 소스 비트맵에서 전송할 DC의 시작 X 위치, Y 위치
dwRop : 래스터 오퍼레이션 코드(ROP) )
CDC::StretchBlt() 함수
StretchBlt() 함수는 비트맵 객체를 참조하는 그래픽 함수로서 어떤 DC에서 다른 DC로 비트맵 블록을 전송하되 확대 또는 축소를 해서 전송하는 함수이다. StretchBlt() 함수는 BitBlt() 함수와 비슷한데 소스 비트맵의 가로크기와 세로크기를 인자로 더 받는다. 화면에 출력할 비트맵의 크기가 소스 비트맵의 크기보다 크면 그림이 확대 출력되고 반대인 경우 그림이 축소되어 출력된다. nWidthSrc이 음수이면 그림이 좌우로 뒤집히고 nHeightSrc가 음수이면 그림이 상하로 뒤집힌다. 이 함수의 원형은 다음과 같다.
BOOL CDC::StretchBlt (int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int nXSrc, int nYSrc, nWidthSrc, nHeightSrc, DWORD dwRop);
( x, y : 출력할 화면의 x 위치, y 위치
nWidth, nHeight : 화면에 출력할 비트맵의 가로 크기, 세로 크기
pSrcDC : 비트맵을 전송할 소스 DC의 포인터
nXSrc, nYSrc : 소스 비트맵에서 전송할 DC의 시작 X 위치, Y 위치
nWidthSrc, nHeightSrc : 소스 비트맵의 가로 크기, 세로 크기
dwRop : 래스터 오퍼레이션 코드(ROP) )


Practice7.rar
댓글을 달아 주세요.