목표
펜과 브러쉬를 활용해 화면에 도형을 출력해보고, Old의 의미에 대해서 알아보도록 하겠습니다.
목차 클릭하면 해당 목차로 이동합니다.
개요
저번 포스팅에서 GDI, DC 등 그래픽 출력을 위한 기본적인 이론과 스톡 오브젝트에 대해 다루었습니다.
[Windows API] Win32 API의 그래픽, GDI와 스톡 오브젝트(Stock Object)
이번 포스팅에선 펜과 브러쉬를 활용해 화면에 도형을 출력하는 실습을 진행할 것입니다. 기본적으로 GDI 오브젝트를 만들어 사용하는 원리는 다음과 같습니다. 펜을 사용하는 것을 예시로 들어보겠습니다.
HPEN MyPen, OldPen; // 1.핸들을 선언한다.
MyPen = CreatePen(~); // 2.GDI 오브젝트를 생성한다.
OldPen = SelectObject(~); // 3.OldPen에 이전에 사용하던 핸들을 저장한다.
Rectangle, Ellipse ~ // 4.GDI 오브젝트를 사용한다.
SelectObject(hdc, OldPen); // 5.선택을 해제한다.
DelectObject(MyPen); // 6.메모리에서 삭제한다.
위와 같은 과정을 거쳐 Rectangle을 이용해 사각형을 그리게 된다면 펜을 생성할 때 설정한 것이 적용되어 사각형이 그려집니다. 펜의 역할은 테두리를 그리는 것이므로 사각형의 테두리가 변경됩니다. 펜을 활용해 사각형을 그리는 과정을 보면, 핸들을 선언하고, GDI 오브젝트를 생성한 다음, OldPen에 이전에 사용하던 오브젝트를 저장합니다. 이후, 사용 후 선택을 해제하고 메모리에서 삭제하는 것을 볼 수 있습니다. 왜 이런 과정을 거쳐야 하는지는 뒤에서 다루도록 하고, 먼저 실습을 진행하겠습니다.
1. 펜(Pen)
저번 포스팅에서 스톡 오브젝트라는 것에 대해서 배웠습니다. Windows API에서 제공하는 스톡 펜(Stock Pen)은 흰색, 검은색, 투명색입니다. 다른 색상의 펜은 개발자가 직접 만들어서 사용해야 합니다. 새로운 펜을 만들기 위한 함수는 다음과 같습니다.
HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);
만들어지는 펜의 핸들을 반환하며, 인수로는 펜으로 그려질 선의 종류, 굵기, 색상이 들어갑니다.
색상은 저번 포스팅에서 배운 RGB(R,G,B)를 이용해서 넣어주시면 됩니다. 선의 종류는 다양하게 존재합니다. 대표적으로 PS_SOLID(실선), PS_DOT(점선) 등이 있습니다. 더 많은 펜의 종류는 다음 링크에서 Microsoft의 문서를 확인해주세요.
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createpen
먼저, 펜을 생성해 파란색 테두리를 가진 사각형을 출력해보도록 하겠습니다.
Gdi_Blue_Pen 프로젝트
윈도우 프로시저 함수의 코드를 확인하면 다음과 같습니다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HPEN MyPen, OldPen;
switch (iMessage) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
MyPen = CreatePen(PS_SOLID, 5, RGB(0, 0, 255));
OldPen = (HPEN)SelectObject(hdc, MyPen);
Rectangle(hdc, 30, 30, 200, 100);
SelectObject(hdc, OldPen);
DeleteObject(MyPen);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
함수 내부에 MyPen, OldPen이라는 핸들(HPEN)을 선언했습니다. WM_PAINT 메세지 처리 부분에서 각 핸들에 값을 세팅하여 Rectangle 함수를 통해 사각형을 출력했습니다.
사용이 끝난 GDI 오브젝트는 메모리에서 해지하는 모습을 볼 수 있습니다. 사용자가 생성한 GDI 오브젝트는 메모리를 사용하고 있기 때문에 사용 후 반드시 DeleteObject 함수를 이용해 해제해야 합니다. 단, 현재 사용하고 있는 오브젝트는 삭제할 수 없습니다. 그렇기 때문에 OldPen을 생성해 값을 저장하는 것입니다.
SelectObject 함수는 대부분의 GDI 오브젝트에서 사용하므로, 각 오브젝트에 맞게 캐스팅(형변환)하여 사용해야 합니다.
1-1. 결과
2. 브러쉬(Brush)
펜과 마찬가지로 브러쉬를 통해 사각형의 내부를 설정할 수 있습니다. 펜과 다르게 브러쉬는 2가지 종류가 존재합니다. 내부를 색깔로 채우는 브러쉬와 무늬를 지정할 수 있는 브러쉬가 존재합니다. 각각의 함수는 다음과 같습니다.
HBRUSH CreateSolidBrush(COLORREF crColor); // 단색 브러쉬
HBRUSH CreateHatchBrush(int fnStyle, COLORREF clrref); // 색상과 무늬를 지정하는 브러쉬
브러쉬에 적용할 수 있는 무늬는 여러 가지가 존재합니다. 수평(HS_HORIZONTAL), 수직선(HS_VERTICAL)으로 채울 수 있는 브러쉬부터 바둑판 모양(HS_CROSS), 좌하향 줄무늬(HS_BDIAGONAL) 등이 있습니다.
마찬가지로 자세한 내용은 Microsoft의 문서를 확인해주세요.
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createhatchbrush
위에서 그린 파란색 사각형의 내부를 초록색 우하향 줄무늬 모양으로 채워보도록 하겠습니다.
Brush 프로젝트
바로 윈도우 프로시저 함수의 코드를 확인해보도록 하겠습니다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HBRUSH MyBrush, OldBrush;
HPEN MyPen, OldPen;
switch (iMessage) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
MyBrush = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 255, 0));
OldBrush = (HBRUSH)SelectObject(hdc, MyBrush);
MyPen = CreatePen(PS_SOLID, 5, RGB(0, 0, 255));
OldPen = (HPEN)SelectObject(hdc, MyPen);
Rectangle(hdc, 30, 30, 200, 100);
SelectObject(hdc, OldBrush);
SelectObject(hdc, OldPen);
DeleteObject(MyBrush);
DeleteObject(MyPen);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
펜과 마찬가지로 브러쉬도 핸들을 선언하여 사용하고 있습니다. 사용한 GDI 오브젝트는 모두 메모리를 해지하고 있는 것을 볼 수 있습니다.
2-1. 결과
3. Old의 의미
펜과 브러쉬 둘 다 My~, Old~로 2개씩 선언하여 사용하고 있습니다. 위에서도 간략히 설명했지만, 사용할 GDI 오브젝트 한 개만 사용한다면 문제가 발생합니다.
먼저, 한 개만 사용한다고 가정하고 다음 코드를 확인해보겠습니다.
case WM_LBUTTONDOWN:
hdc = GetDC(hWnd);
for(i=0; i<1000; i++) {
hPen = CreatePen(PS_SOLID, 2, RGB(255,0,0));
SelectObject(hdc, hPen);
Rectangle();
}
ReleaseDC(hWnd, hdc);
return 0;
}
반복문이 돌면서 Pen을 계속해서 생성합니다. 생성하는 그 순간에도 메모리는 계속해서 사용되고 있습니다. 이것은, 메모리를 해지하지 않았기 때문에 발생하는 문제입니다. 반복문 내부에서 생성과 삭제를 계속하면 된다고 생각할 수 있지만, 사용 중인 GDI 오브젝트는 DeleteObject 함수를 사용해 메모리를 해지할 수 없습니다. 가령, 삭제한다고 하더라도, 디폴트가 삭제되는 문제가 발생합니다. 그렇기 때문에, DC에 선택된 GDI 오브젝트는 반드시 하나는 존재해야 합니다. 그렇기 때문에 Old가 존재하는 것입니다.
정리
이렇게 스톡 오브젝트를 활용하여 원하는 그래픽을 출력하는 방법에 대해서 알아보았습니다. 다음 포스팅에서는 투명 오브젝트와 ROP 모드에 대해서 알아보도록 하겠습니다.
'개발 > Win32 API Programming' 카테고리의 다른 글
[Windows API] Win32 API에서 비트맵 출력하기 (1) | 2022.04.21 |
---|---|
[Windows API] Win32 API의 그리기 모드와 ROP2 모드, 선 그리기 (0) | 2022.02.15 |
[Windows API] Win32 API의 그래픽, GDI와 스톡 오브젝트(Stock Object) (0) | 2022.02.08 |
[Windows API] Win32 API를 활용해 작업 영역 얻기 (0) | 2022.01.27 |
[Windows API] Win32 API의 백그라운드 작업과 콜백 함수 (0) | 2022.01.20 |