Search
Duplicate

LV02 DC란 무엇인가?

숙제

여러 GDI 오브젝트를 이용하여 다양한 도형과 다양한 색상을 출력해보세요.
윈도우즈는 세가지 동적 연결 라이브러리 이루어져 있는데
메모리를 관리하고 실행시키는 KERNEL, 유저 인터페이스와 윈도우를 관리하는 USER 그리고
화면 처리와 그래픽을 담당하는 GDI이다.
출력을 하려면 우리는 GDI(Graphic Device Interface)모듈에 특별히 관심을 기울여야 한다. 화면으로 출력되는 모든 글자와 그림은 GDI를 통해야 하기 때문이다.
DC (Device Context)란 출력에 필요한 모든 정보를 가지는 데이터 구조체이며 GDI모듈에 의해 관리된다. 어떤 폰트를 사용할 것인가, 선의 색상과 굵기, 채움 무늬와 색상, 출력방법 등등이 모두 출력에 필요한 정보들이다. 화면 출력에 DC가 필요한 이유를 이해가 위해서 몇가지 상황을 들어보자
상황1
우선 화면에 선을 긋는 LineTo라는 함수를 생각해 보자. 선을 긋기 위해서는 최소한 시작점과 끝점의 좌표가 필요하다는 것을 상식적으로 쉽게 이해할 수 있을 것이다. 그러면 LineTo(x1, y1, x2, y2)와 같은 식으로 함수를 호출하여 선을 그을 수 있을 것이다. 그러나 조금 더 생각 해보면 두 점의 좌표외에도 여러가지 정보가 더 필요하다. 선의 색상, 굵기, 모양, 선을 그리는 방법, 좌표값을 해석하는 방법 등의 추가 정보가 있어야 비로서 완벽한 선을 그을 수 있다. 이런 정보들을 모두 인수로 넘겨준다면 LineTo함수는 다음과 같은 모양이 될것이다.
LineTo(StartX, StartY, EndX, EndY, Color, Width, Shape, ROP, mode,......)
이런 정보들을 일일이 인수로 전달할 것이 아니라 한 곳에 모아두고 그 값들을 사용하는 방법이 훨씬 더 편리하고 효율적이다. 그래서 이런 정보들을 모두 모아 DC라는 것을 만들고 그리기 함수에서는 DC의 핸들을만을 넘겨받아 그리기에 필요한 추가 정보는 모두 DC에 정의 되어 있는 값을 사용한다.
이런 방식을 사용하면 LineTo 함수는 다음과 같이 간단해질수 있을 것이다.
LineTo(hdc, x, y)
상황2
DC가 필요한 또다른 예는 윈도우즈는 여러개의 프로그램이 동시에 실행되는 멀티태스킹 시스템이기 때문에 그리기 함수에 의해 실제 출력되는 모양은 주변환경에 따라 다르다. LineTo(hdc, 100, 100)을 호출 했을때 화면상의 (100, 100)까지의 점을 찍는 것이 아니라 실제로 점이 찍혀야 할 부분은 현재 윈도우가 차지하고 있는 영역이 되어야한다.
또한 2개의 윈도우가 있을대 화면에 그려져야 할부분은 해당 코드가 실행된 윈도우영역에서만 그림이 그려져야 한다.
이러한 이유외에도 DC의 존재 이유는 여러가지가 있지만 여기서는 DC를 왜 사용하는가 정도만 직감적으로 이해하고 넘어가도록 하자.
GDI 오브젝트
GDI 오브젝트란 그래픽 출력에 사용되는 도구를 말하며 펜, 브러시, 비트맵, 폰트 등등이 모두 GDI오브젝트이다.
사람이 그림을 그릴 때 연필, 붓 등의 도구를 사용하는 것과 마찬가지로 GDI가 그래픽을 출력할 때는 GDI오브젝트를 사용한다. 즉 선을 그을 때는 펜을 사용하며 면을 채울 때는 브러시를 사용하고 문자열을 출력할 때는 폰트를 사용한다.
GDI 오브젝트를 모아놓은 것이 DC이며 GDI는 현재 DC에 선택된 GDI오브젝트를 사용한다.
그래서 사용자는 그래픽을 출력하기 전에 DC에 원하는 오브젝트를 선택해 줌으로써 그래픽을 다른 모양으로 변경할수 있다.
예를 들어 그냥 선을 그으면 디폴트 펜인 검정색 펜으로 그려지지만 파란색 펜을 만들어 DC에 선택한 후 선을 그으면 GDI는 이 파란색 펜을 사용하여 선을 긋게 되므로 파란색 선이 그려지게 된다. 마찬가지로 브러시나 폰트를 변경하면 채워지는 색상이나 문자열의 글꼴 모양을 변경할 수 있다. GDI 오브젝트는 GDI가 그래픽 출력을 위해 사용하는 도구임과 동시에 사용자가 GDI의 출력을 조정할 수 있는 도구이기도 하다.
GDI 오브젝트는 내부적으로 일종의 구조체이겠지만 우리가 사용할 때는 모두 핸들로 괸리된다. GDI 오브젝트를 만들 때 핸들을 발급받으며 선택하거나 삭제할때는 이 핸들만 가지고 GDI 오브젝트를 사용하게 된다. DC가 BeginPaint나 GetDC 함수에 의해 처음 만들어졌을 때 디폴트로 선택된 GDI 오브젝트는 다음과 같다.
GDI 오브젝트
핸들 타입
설명
디폴트
HPEN
선을 그을 때 사용된다.
검정색의 가는 선
브러시
HBRUSH
면을 채울 때 사용된다.
흰색
폰트
HFONT
문자 출력에 사용되는 글꼴
시스템 글꼴
비트맵
HBITMAP
비트맵 이미지
선택되지 않음
팔레트
HPALETTE
팔레트
선택되지 않음
영역
HRGN
영역
선택되지 않음
스톡 오브젝트
스톡 오브젝트(stock object)는 윈도우즈가 기본적으로 제공해 주는 GDI 오브젝트를 말한다.
운영체제가 제공해 주므로 일부러 만들지 않아도 언제든지 사용할 수 있으며 사용하고 난 후에 파괴시켜 줄 필요도 없다.
다음 함수로 핸들을 얻어 사용하기만 하면 된다.
HGDIOBJ GetStockObject( int fnObject );
fnObject 인수에 사용하고자 하는 스톡 오브젝트를 기입해 주면 된다. 사용 가능한 스톡 오브젝트는 다음과 같다. 주로 브러시와 펜이 스톡 오브젝트로 제공된다. 2장에서 처음 공부했던 WHITE_BRUSH도 알고보면 스톡 오브젝트의 일종이다.
fnObject
설명
BLACK_BRUSH
검정색 브러시
GRAY_BRUSH
회색 브러시
NULL_BRUSH
투명 브러시
WHITE_BRUSH
흰색 브러시
DKGRAY_BRUSH
짙은 회색 브러시
LTGRAY_BRUSH
옅은 회색 브러시
BLACK_PEN
검정색 펜
WHITE_PEN
흰색 펜
NULL_PEN
투명 펜
ANSI_FIXED_FONT
고정폭 폰트
ANSI_VAR_FONT
가변폭 폰트
DEFAULT_PALETTE
시스템 팔레트
색상
잠시 후에 다양한 색상과 모양의 펜, 브러시를 만들어 볼텐데 그 전에 윈도우즈에서 색상을 표현하는 방법에 대해 알아보자. 도스에서는 WHITE, YELLOW, RED등의 매크로 상수로 색상을 표현하였고 이 매크로들의 실제값은 0~15까지의 정수 였다. 윈도우즈에서는 색상값을 표현하기 위해 COLORREF라는 데이터형을 사용하는데 이는 다음과 같이 정의되어 있다.
상위 8비트는 사용되지 않는다. 각 색상의 요소는 1바이트의 크기를 가지므로 0~255까지의 농도를 표현할 수 있다.
COLORREF형은 32비트 정수일 뿐이므로 직접 16진수로도 표현할수 있다. 예를 들어 0은 검정색이 되며 0xff는 빨간색, 0xff0000은 파랑색이 된다. 하지만 이 방법은 너무 기계적이고 초보자가 쓰기는 힘들므로 색상값을 만들 때는 통상 RGB 매크로 함수를 사용하며 이 매크로는 다음과 같이 정의 되고 있다.
세 개의 인수를 가지는데 각각 빨간색, 초록색, 파란색의 농도이며 이 세갑을 조립하며 하나의 32비트 색상값을 만들어내는 간단한 비트 연산을 하고 있다. 비트 쉬프트, 비트 OR 연산자를 적절히 혼합한 문장인데 그리 어렵지 않게 이해가 될것이다.
각 색상의 요소가 얼마만큼 혼합되어 있는가에 따라 실제 색상이 결정되는데 RGB(255, 0, 0)는 빨간색, RGB(0, 0, 255)는 파란색이다. 세 요소가 모두 다 최대치인 RGB(255, 255, 255)는 흰색이며 반대로 RGB(0, 0, 0)은 검정색이다. 윈도우즈에서는 이와 같이 RGB 매크로를 사용하여 색상값을 표현하며 COLORREF형의 인수 자리에는 RGB매크로를 사용하면 된다. 다음 세 매크로 함수는 COLORREF형 변수 값에서 각 색상 요소의 농도를 분리해내는 함수이다.
펜은 선을 그을때 사용되는 GDI 오브젝트이다. 펜을 변경하면 그려지는 선의 모양을 마음대로 변경할 수 있다.
그런데 윈도우즈가 제공하는 스톡 펜은 흰색, 검은색, 투명색, 세가지뿐이며 파란색, 노란색 등의 원색 펜은 없다.
이런 펜을 사용하고자 할 때는 직접 만들어서 사용해야 한다. 펜을 만들 때는 다음 함수를 사용한다.
fnPenStyle
그려질 선의 모양을 정의한다. 이 값을 변경하면 실선뿐만 아니라 다양한 형태의 선을 만들 수 있다. 실선, 점선, 일점 쇄선 등등의 선 모양이 있다.
nWidth
선의 폭을 지정한다. 디폴트 선의 굵기는 1이지만 이 값을 2나 3으로 변경해 주면 두꺼운 선을 그릴 수 있다. 단 이 값이 0일 경우는 맵핑 모드에 상관없이 무조건 1픽셀 두께의 선이 만들어진다.
crColor
선의 색상을 지정한다. COLORREF 형이므로 RGB 매크로 함수를 사용하면 된다.
리턴값으로는 만들어진 펜의 핸들을 돌려주므로 이 값을 잘 보관해 두어야 만들어진 펜을 사용할 수 있다. 모양, 굵기, 색상 세 가지 속성을 조합하면 아주 다양한 형태의 펜을 만들 수 있을 것이다. 그럼 펜을 만들어 사용해 보도록 하자. 별도의 예제를 만들 필요없이 GdiObj 프로젝트를 다음과 같이 수정한다.
CreatePen 함수를 호출하여 굵기 5의 파란색 실선 펜을 만든 후 이 펜의 핸들을 MyPen에 대입하였다.
그리고 SelectObject로 이 펜을 DC에 선택한 후 사각형을 그렸으므로 사각형의 테두리는 분명히 굵은 파란색으로
그려질 것이다. CreatePen 함수 자체가 그다지 어렵지 않으므로 이 코드는 아주 쉽게  이해가 갈것이다.
GDI 오브젝트는 사용한 후 반드시 삭제 해주어야 한다. 왜냐하면 GDI 오브젝트도 메모리를 사용하기 때문이다.
메모리를 할당 한 후 반드시 해제해 주어야 하는 것과 마찬가지로 GDI 오브젝트도 사용이 끝나면 해재해 주어야 하는것이 원칙이다. 만약 해제를 해주지 않으면 시스템의 메모리를 갉아먹게 될 것이다.
삭제하고자 하는 GDI 오브젝트의 핸들만 인수로 넘겨주면 된다. 단 이때 주의할 것은 DC에 현재 선택되어 있는  GDI오브젝트는 삭제할 수 없다는 점이다.  현재 사용되고 있는 객체를 함부로 삭제하도록 내버려 둘 수는 없기 때문에 생긴 일종의 안전 장치 역할을 하는 규정이다. 그래서 삭제를 하기 전에 먼저 DC에 선택된 객체를 선택 해제해 주어야 하는데 선택을 해제시켜주는 별도의 함수는 제공되지 않으므로 다른 GDI오브젝트를 선택해 주는 방법을 사용한다. 이런 이유로 OldPen이라는 핸들을 만든 후 이 핸들에 MyPen이 선택되기전 펜의 핸들을 저장해 두고 MyPen을 삭제하기 전에 OldPen을 다시 선택해주는 것이다.
OldPen이 DC에 선택되면 자연히 현재 선택되어 있는 MyPen이 선택해제될 것이고 따라서 MyPen을 안전하게 삭제할 수 있다.
펜뿐만 아니라 브러시, 폰트 등 모든 GDI오브젝트는 일반적으로 이런 절차를 거쳐 만들어지고 사용된다.
물론 속도를 위해 약간의 다른 형식을 사용하는 방법이 있기는 하지만 여기서는 원론적인 내용만 이해하도록 하자.
브러시
브러시는 채워지는 면을 채색하는 용도로 사용된다. 사각형의 안쪽이나 원의 내부 또는 다각형의 내부를 채색 할때 현재 DC 에 선택된 브러시가 사용된다. 스톡 브러시에는 회색, 흰색, 검정색 등의 단색 브러시가 있으므로 이 브러시들은 별도로 만들지 않아도 사용할 수 있다.
첫번째 함수는 단색의 브러시만을 만들 수있으며 브러시의 색상만 인수로 전달해 주면 된다. 두번째 함수는 색상뿐만 아니라 무늬도 같이 지정할 수 있다. 지정할 수 있는 무늬의 종류는 다음과 같다.
설명
HS_BDIAGONAL
좌하향 줄무늬
HS_CROSS
바둑판 모양
HS_DIACROSS
좌하향 및 우하향 줄무늬
HS_FDIAGONAL
우하향 줄무늬
HS_HORIZONTAL
수평선
HS_VERTICAL
수직선
두 함수 모두 리턴하는 값은 만들어진 브러시의 핸들이다. 그럼 이제 브러시를 만드는 함수도 배웠으니 펜과 브러시를 동시에 만들어서 사용해 보도록 하자. GdiObj 프로젝트의 WM_PAINT 메시지를 다음과 같이 수정한다.
이런식으로 펜과 브러시를 바꾸어가며 그림을 그리면 얼마든지 다양한 모양의 그래픽을 그릴 수 있다. 코드가 워낙 간단하기 때문에 더 이상의 설명은 필요없을 것이다. 이 외에도 비트맵, 패턴 브러시를 만드는 함수들과 브러시에 관련된 좀 더 복잡한 이론들이 있지만 다음 기회에 배우기로 한다.