4. 기본적인 그리기
1. GDI 철학 비디오 출력과 프린터에 그래픽을 책임지고 있는 Windows의 하위 시스템은 그래픽 디바이스 인터페이스(GDI)이다. Windows 98과 Microsoft NT에서의 그래픽은 주로 동적 링크 라이브러리 GDI32.DLL로부터 얻어진 함수들에 의해 처리된다. GDI의 목적 중의 하나는 장치에 구애 받지 않고 그래픽을 지원하는 것이다. Windows는 디폴트로 픽셀을 기반으로 하는 좌표계를 사용 가로축과 세로축이 0에서 32,767까지 가능한 가상 좌표계를 사용
2. GDI함수 호출 장치 컨텍스트를 얻고 해제하는 함수들 장치 컨텍스트에 대한 정보를 획득하는 함수들 그리는 함수들 BeginPaint EndPaint GetDC ReleaseDC 장치 컨텍스트에 대한 정보를 획득하는 함수들 GetTextMetrics 그리는 함수들 TextOut 장치 컨텍스트의 속성을 설정하고 얻어내는 함수들 SetTextColor SetTextAlign GDI개체를 다루는 함수들 CreatePen CreatePenIndirect
3. 장치 컨텍스트 핸들 얻기 hdc = Beginpaint(hwnd,&ps) hdc = GetDC(hwnd) WM_PAINT hdc = Beginpaint(hwnd,&ps) EndPaint(hwnd,&ps); WM_PAINT가 아닌 다른 메시지에서 hdc = GetDC(hwnd) ReleaseDC(hwnd,hdc); 전체 윈도우에 대한 hDC얻기 hdc = GetWindowDC(hwnd) ReleaseDC(hwnd,hdc); 화면전체에 출력을 하려면 hdc = CreateDC (pszDriver,pszDevice,pszOutput,pData); [다른 명령들] DeleteDC(hdc);
3. 장치 컨텍스트 핸들 얻기 hdc=CreateDC(“DISPLAY”,NULL,NULL,NULL); Ex) hdc=CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL); TextOut(hdc,0,0,TEXT("여기는 처음"),12); DeleteDC(hdc); hdcMem=CreateCompatibleDC(hdc); [다른 명령들] DeleteDC(hdc);
4. 점과 선 그리기 픽셀 정하기 직선 Rectangle : 사각형을 그린다. Ellipse : 타원을 그린다. SetPixcel(hdc,x,y,crColor); crColor는 COLORREF형식으로 색상을 지정 crColor = GetPixcel(hdc,x,y) 지정된 좌표의 픽셀 색상을 반환한다. 직선 LineTo : 직선을 그린다. Polyline와 PolylineTo : 연결된 직선을 그린다. PolyPolyline : 여러 개의 Polyline을 그린다. Arc : 호를 그린다. PolyBezier와 PolyBezierTo : 베지어 스플라인을 그린다. Rectangle : 사각형을 그린다. Ellipse : 타원을 그린다. RoundRect : 모서리가 둥근 사각형을 그린다. Pie : 파이모양을 그린다. Chord : 현 형태의 다원 부분을 그린다.
5. 직선 그리기 직선을 그리려면 MoveToEx(hdc,xBeg,yBeg,NULL); MoveToEx의 마지막 인자는 POINT 구조체에 대한 포인터이다. 함수로부터 반환 시에 POINT구조체의 x,y 필드는 이전의 위치를 체운다. Windows 98에서의 좌표값이 32bit이지만 오직 하위 16비트만 사용한다. Windows NT는 32비트를 모두 사용한다. LineTo(hdc,xEnd,yEnd); LineTo함수는 현재의 위치로 부터 xEnd,yEnd까지 선을 그린다. GetCurrentPositionEx(hdc,&pt); 현재의 위치를 알아 내려면 pt는 POINT구조체이다.
5. 직선 그리기 GetClientRect(hwnd,&rect); for (x = 0; x < rect.right; x+=100) { MoveToEx (hdc,x,0,NULL); LineTo(hdc,x,rect.bottom); } for (y = 0; y < rect.bottom; y+=100) MoveToEx (hdc,0,y,NULL); LineTo(hdc,rect.right,y); POINT apt[5] = {100,100,200,100,200,200,100,200,100,100}; MoveToEx(hdc,apt[0].x,apt[0].y); for (I = 1; I < 5;I++) { LineTo(hdc,apt[I].x,apt[I].y); } Polyline(hdc,apt,5); MoveToEx(hdc,apt[0].x,apt[0].y,NULL); PolylineTo(hdc,apt+1,4);
5. 경계 상자 함수 Rectangle(hdc,xLeft,yTop,xRight,yBottom); 채워진 사각형을 그린다. Ellipse(hdc,xLeft,yTop,xRight,yBottom); 채워진 타원을 그린다. RoundRect(hdc,xLeft,yTop,xRight,yBottom,xCornerEllipse,yCornerEllipse); 모서리가 둥근 채워진 사각형을 그린다. Arc(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd); 호를 그린다. Chord(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd); 현을 그린다. Pie(hdc,xLeft,yTop,xRight,yBottom,xStart,yStart,xEnd,yEnd); 파이 모양의 도형을 그린다.
5. 경계 상자 함수 case WM_LBUTTONDOWN: hdc=GetDC(hwnd); LineTo(hdc,LOWORD(lParam),HIWORD(lParam)); m_OldPT.x = LOWORD(lParam); m_OldPT.y = HIWORD(lParam); ReleaseDC(hwnd,hdc); return 0; case WM_MOUSEMOVE: if (wParam & MK_LBUTTON) { SelectObject(hdc,GetStockObject(WHITE_PEN)); LineTo(hdc,m_OldPT.x,m_OldPT.y); MoveToEx(hdc,0,0,NULL); SelectObject(hdc,GetStockObject(BLACK_PEN)); }
6. 스톡 펜 사용하기 Windows가 기본적으로 제공하는 스톡 펜 디폴트 장치 컨텍스트 펜 PEN 정의 BLACK_PEN,WHITE_PEN,NULL_PEN 디폴트 장치 컨텍스트 펜 BLACK_PEN PEN 정의 HPEN hPen; 윈도우 스톡에 있는 Pen가져오기 hPen = GetStockObject(WHITE_PEN); 장치 컨텍스트에 지정하려면 SelectObject(hdc,hPen) SelectObject(hdc,GetStockObject(WHITE_PEN));
7. 펜의 생성과 선택, 그리고 삭제 펜의 사용 GDI객체 사용 규칙 펜은 CreatePen과 CreatePenIndirect를 이용하여 펜을 생성한다. SelectObject을 이용하여 만들어진 이 펜을 사용할 수 있다. 새로운 펜으로 그린다. 오직 하나의 펜만이 장치 컨텍스트에서 선택될 수 있다. SelectObject를 이용하여 이전의 펜으로 되돌린다. DeleteObject를 이용하여 만들어진 펜을 삭제한다 GDI객체 사용 규칙 마지막에 항상 생성한 GDI객체를 삭제해야 한다. 유효한 장치 컨텍스트에서 선택되어 있는 GDI객체를 삭제하지 않는다. 스톡 객체는 삭제하지 않는다. hPen = CreatePen(iPenStyle,iWidth,crColor); iPenStyle :PS_SOLID,PS_DASH,PS_DOT,PS_DASHDOT,PS_DASHDOTDOT,PS_NULL,PS_INSIDEFRAMER iWidth : 펜의 두께 , crColor : COLORREF의 펜의 Color CreatePenIndirect 먼저 LOGPEN형식의 구조체를 정의한다. LOGPEN logpen; ( lopnStyle => pen의 스타일, lopnWidht => pen의 두께, lopnColor => pen의 칼라 ) hPen = CreatePenIndirect(&logpen);
7. 펜의 생성과 선택, 그리고 삭제 -프로그램에서 3개의 Pen을 사용하면 HPEN hPen1,hPen2,hPen3; hPen1 = CreatePen(PS_SOLID,1,0); hPen2 = CreatePen(PS_SOLID,1,RGB(255,0,0)); hPen3 = CreatePen (PS_DOT,0,0); SelectObject(hdc,hPen1); [선그리기] SelectObject(hdc,hPen2); SelectObject(hdc,hPen3); DeleteObject (hPen1); DeleteObject (hPen2); DeleteObject (hPen3);
8. 틈새 채우기 도트 펜과 대쉬 펜 사이의 틈새는 어떻게 처리할까? 틈새의 색을 바꾸려면 틈새를 칠하지 않게 할 수도 있다 틈새의 색은 Windows디폴트 배경색인 흰색으로 틈을 칠한다. (OPAQUE) 틈새의 색을 바꾸려면 SetBkColor(hdc,crColor); 틈새를 칠하지 않게 할 수도 있다 배경모드를 TRANSPARENT로 지정한다. SetBkMode(hdc,TRANSPARENT);
9. 그리기모드 디스플레이에 그려진 선의 외형은 장치 컨텍스트에서 정의된 그리기 모드에 영향을 받는다. R2_COPYPEN 펜을 사용하여 선을 그릴 때 실제로는 펜의 픽셀과 목표가 되는 디스플레이 픽셀의 bitwise 2진 연산을 수행한다. 픽셀을 가지고 2진 연산을 수행하는 것을 “래스터 연산” 또는 “ROP”라고 한다. R2_COPYPEN 디폴트 그리기 모드 단순히 펜의 픽셀을 목표에 복사하는 것을 나타낸다. R2_BLACK 항상 검은 선을 그린다. R2_NOTMERGEPEN 펜과 배경이 검정색일 때 선은 흰색으로 그린다. R2_NOT 펜의 색상과는 상관없이 항상 목표 색상을 반전하여 선의 색을 결정한다. 그리기 모드 지정 SetROP2(hdc,iDrawMode); 그리기 모드를 읽어 온다. iDrawMode = GetROP2(hdc)
9. 그리기모드 원본 그림 COPY OR AND XOR 그리기 모드 설명 R2_BLACK 항상 검정색이다. R2_WHITE 항상 흰색이다. R2_NOP 아무런 그리기도 하지 않는다. R2_NOT 원래의 그림을 반전시킨다. R2_COPYPEN 원래의 그림을 덮어버리고 새 그림을 그린다. R2_NOTCOPYPEN 새 그림을 반전시켜 그린다. R2_MERGEPEN OR연산으로 두 그림을 합친다. R2_MASKPEN AND연산으로 겹치는 부분만 그린다. R2_XORPEN XOR연산으로 겹치는 부분만 반전 시킨다.
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; static int xPos, yPos, xOldPos, yOldPos; static bool bNowDraw = FALSE; switch (message) case WM_LBUTTONDOWN: xPos = LOWORD(lParam); yPos = HIWORD(lParam); xOldPos = xPos; yOldPos = yPos; bNowDraw = true; return 0; case WM_LBUTTONUP: bNowDraw = false; hdc = GetDC(hwnd); MoveToEx(hdc,xPos,yPos,NULL); LineTo(hdc,xOldPos,yOldPos); ReleaseDC(hwnd,hdc); case WM_MOUSEMOVE: if (bNowDraw) SetROP2(hdc,R2_NOT); int xNewPos = LOWORD(lParam); int yNewPos = HIWORD(lParam); LineTo(hdc,xNewPos,yNewPos); xOldPos = xNewPos; yOldPos = yNewPos; } case WM_DESTROY: PostQuitMessage (0) ; return 0 ; return DefWindowProc (hwnd, message, wParam, lParam) ;
10. 채워진 영역 그리기 Rectangle : 직각 모서리를 가지는 사각형 Ellipse : 타원 RoundRect :둥근 모서리를 가지는 사각형 Chord : 종료점은 현에 의해 연결되는 타원 외곽의 호 Pie : 타원 외곽선으로 정의되는 Pie Polygon : 다변체 그림 PolyPolygon : 다중 다변체 그림 현재 장치 컨텍스트에서 선택된 브러쉬로 채워진다. 디폴트 : WHITE_BURSH 6개의 스톡 브러쉬 : WHITE_BRUSH , LTGRAY_BRUSH,GRAY_BRUSH, DKGRAY_BRUSH,BLACK_BRUSH,NULL_BRUSH
10. 채워진 영역 그리기 HBRUSH hBrush,hOldBrush; hBrush = GetStockObject(GRAY_BRUSH); hOldBrush = SelectObject(hdc,hBrush); [도형 그리기] SelectObject(hdc, hOldBrush); 경계가 없는 그림을 그리려면 SelectObject(hdc,GetStockObject(NULL_PEN)); 내부를 채우지 않고 그림의 외곽선 그리려면 SelectObject(hdc,GetStockObject(NULL_BRUSH));
11. 내부를 브러쉬로 채우기 단색의 브러쉬를 만든다. hBrush = CreateSolidBrush(crColor); 수평,수직,혹은 대각선으로 구성되는 hatch표시를 이용하여 브러쉬를 생성 hBrush = CreateHatchBrush(iHatchStyle,crColor); PS_HORIZONTAL,PS_BDIAGONAL,PS_VERTICAL,PS_CROSS, PS_FDIAGONAL,PS_DIAGCROSS 값 설명 HS_BDIAGONAL 좌 하향 줄 무늬 HS_CROSS 바둑판 모양 HS_DIAGCROSS 좌 하향 및 우 하향 줄무늬 HS_FDIAGONAL 우 하향 줄무늬 HS_HORIZONTAL 수평선 HS_VERTICAL 수직선
11. 내부를 브러쉬로 채우기 Bitmap으로 도형을 채우려면 FillRect(hdc, &rect, hBrush) hBitMap = LoadBitmap(hinst,MAKEINTRESOURCE(IDB_BITMAP1)); hNewBrush = CreatePatternBrush(hBitMap); FillRect(hdc, &rect, hBrush) 지정된 브러쉬로 사각형을 채운다. FrameRect(hdc, &rect, hBrush); 사각형의 Frame을 그리지만 채우지는 않는다. InvertRect(hdc,&rect); 사각형 내의 모든 픽셀들을 반전하여 1은 0으로 0은 1로 만든다. Rect구조체를 채운다. SetRect (&rect,xLeft,yTop,xRight,yBottom); 사각형 내에 포인트가 있는지를 결정한다. bInRect = PtInRect(&rect,point);
12. 랜덤 사각형 PeekMessage(&msg,NULL,0,0,PM_REMOVE); 처음 4개의 매개변수는 GetMessage와 동일하다. PM_REMOVE는 메시지를 읽은 후 제거한다. PM_NOREMOVE는 메시지를 읽은 후 제거하지 않는다. PeekMessage()의 Return 값 메시지를 읽었는지 읽지 못 했는지를 Return한다. /*------------------------------------------ RANDRECT.C -- Displays Random Rectangles (c) Charles Petzold, 1998 ------------------------------------------*/ #include <windows.h> #include <stdlib.h> // for the rand function LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; void DrawRectangle (HWND) ; int cxClient, cyClient ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("RandRect") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"),szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Random Rectangles"),WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (TRUE) if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) if (msg.message == WM_QUIT) break ; TranslateMessage (&msg) ; DispatchMessage (&msg) ; else DrawRectangle (hwnd) ; return msg.wParam ; LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) switch (iMsg) case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; case WM_DESTROY: PostQuitMessage (0) ; return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
12. 랜덤 사각형 WM_SIZE 메시지 윈도우의 크기가 변경될 때 보내진다. lParam에는 하위 워드에 윈도우의 폭, 상위 워드에 윈도우의 높이 wParam에는 메시지가 발생한 이유를 나타내는 플래그 전달 플래그 설명 SIZE_MAXHIDE 다른 윈도우가 최대화되어 이 윈도우가 가려졌다. SIZE_MAXIMIZED 최대화되었다. SIZE_MAXSHOW 다른 윈도우가 원래 크기로 복구되어 이 윈도우가 나타났다. SIZE_MINIMIZED 최소화되었다. SIZE_RESTORED 크기가 변경되었다.
13. 영역을 생성하고 색칠하기 영역은 사각형,다각형,타원의 조합으로 이루어진다. 영역을 사용하여 그리거나 잘라내기를 할 수 있다. 영역도 GDI객체이다. 생성된 모든 영역은 DeleteObject을 통하여 삭제한다. 영역에 대한 Handle은 HRGN을 반환한다. 영역 생성 hRgn = CreateRectRgn(xLeft,yTop,xRight,yBottom); hRgn = CreateRectRgnIndirect(&rect); hRgn = CreateEllipticRgn(xLeft, yTop,xRight,yBottom); hRgn = CreateEllipticRgnIndirect(&rect); CreateRoundRectRgn은 둥근 모양의 사각형 영역을 생성 다각형영역을 생성 hRgn=CreatePolygonRgn(&point,iCount,iPolyFillMode); point : POINT형 구조체 iCount : 포인트의 개수 iPolyFillMode : ALTERNATE또는 WINDING
13. 영역을 생성하고 색칠하기 iRgnType = CombineRgn(hDestRgn,hSrcRgn1,hSrcRgn2,iCombine); 2개의 원본 영역을 조합하여 목표 핸들이 조합된 영역을 나타내도록 한다. iCombine 매개변수는 hSrcRgn1과 hSrcRgn2의 영역이 조합되는 방식이다. RGN_AND 두개의 소스 영역의 교차영역 RGN_OR 두개의 소스 영역의 전영역 RGN_XOR 두개의 소스 영역에서 교차 부분을 뺀 영역 RGN_DIFF hSrcRgn2에 속하지 않는 hSrcRgn1영역 RGN_COPY hSrcRgn1전부 (hSrcRgn2무시) Return값 빈 영역이면 NULLREGION; 간단한 사각형,원,다각형이면 SIMPLEREGION 사각형,타원,혹은 다각형들의 조합이면COMPLEXREGION ERROR면 ERROR
14. 맵핑모드 맵핑모드란 주어진 좌표가 화면상의 실제 어디에 해당하는지를 결정하는 방법을 말한다. 윈도우에서 사용하는 좌표는 논리 좌표와 물리 좌표 두 가지가 있다. 논리 좌표 : 윈도우 내부에서 사용되는 좌표를 말한다. TextOut,DrawText등에서 사용하는 좌표를 말한다. DC핸들을 인수로 받아들이는 모든 함수는 논리 좌표이다. 물리 좌표 : 실제 화면에 출력되는 좌표이며 픽셀 단위를 사용한다. 물리좌표 100,100은 그 위치가 정해져 있다. 논리 좌표의 (20,20)은 물리 좌표의 (20,20)일 수도 있고 다른 곳 일 수도 있다. 윈도우가 사용하는 디폴트 맵핑모드는 물리좌표와 논리좌표가 일치한다. int SetMapMode(HDC hdc, int iMapMode); int GetMapMode(HDC hdc);
14. 맵핑모드 맵핑모드 단위 X축 증가 Y축 증가 MM_TEXT 픽셀 오른쪽 아래쪽 MM_LOMETRIC 0.1 mm 위쪽 MM_HIMETRIC 0.01 mm MM_LOENGLISH 0.01인치 MM_HIENGLISH 0.001인치 MM_TWIPS 1/1440 인치 MM_ISOTROPIC 가변 MM_ANISOTROPIC
15. 윈도우와 뷰 포트 윈도우는 논리 좌표가 사용되는 표면을 말한다. 뷰 포트는 물리 좌표가 사용되는 영역을 말한다. 그래픽 출력 함수는 윈도우에 그래픽을 출력한다. 뷰 포트는 물리 좌표가 사용되는 영역을 말한다. 실제로 사용자의 눈에 보이는 좌표 영역이다. SetViewportOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint); 윈도우의 원점을 이동시킨다. SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINT lpPoint); 뷰 포트의 원점을 이동시킨다.
16. 가변 비율 윈도우 확장을 조정할 수 있는 맵핑모드에는 MM_ISOTROPIC과 MM_ANISOTROPIC 두 가지가 있다. BOOL SetWindowExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize); 논리적인 좌표 범위를 지정한다. BOOL SetViewportExtEx(HDC hdc, int nXExtent, int nYExtent, LPSIZE lpSize); 물리적인 좌표 범위를 지정한다.