640 likes | 830 Views
Chapter 5 Windows GDI 작업 : 텍스트 및 그래픽 출력. 이 장에서는. - WM_PAINT 메시지 사용하기 - GDI 그리기 표면 (drawing surface) 검사하기 - 텍스트 그리기 - 펜 , 브러시 , 비트맵 사용하기 - 다양한 모양 그리기. 윈도우즈 GDI(Graphic Device Interface). 그래픽의 출력을 담당 화면에 그리는 그래픽만 지원하는 것이 아니라 프린터에 프린트하는 기능도 담당. 클라이언트 영역. 유효화 (Validating)
E N D
이 장에서는... • - WM_PAINT 메시지 사용하기 • - GDI 그리기 표면(drawing surface) 검사하기 • - 텍스트 그리기 • - 펜, 브러시, 비트맵 사용하기 • - 다양한 모양 그리기
윈도우즈 GDI(Graphic Device Interface) • 그래픽의 출력을 담당 • 화면에 그리는 그래픽만 지원하는 것이 아니라 프린터에 프린트하는 기능도 담당
클라이언트 영역 • 유효화(Validating) • 사용자 혹은, 자동 윈도우 프로세스와 같은 무엇인가가 윈도우의 클라이언트 영역을 훼손하면, 윈도우즈는 WM_PAINT 메시지를 응용 프로그램으로 보낸다. • 프로그램은 윈도우즈에게 그 메시지를 받아서 처리했다는 것을 알려야 한다. 이렇게 윈도우즈에게 알리는 과정을 일컬어서 사각형(그려질 영역)을 유효화한다고 말한다. • 그려야 할 사각형 정의 • 그릴 필요가 없는 영역은 그리지 않도록 한다. • 따라서 그릴 필요가 있는 영역인 무효 사각형(invalid rectangle)만 신경을 쓰면 되며, • 이것은 WM_PAINT 메시지에 따라오는 자료 구조에 포함되어 있다.
WM_PAINT 메시지를 처리하는 두 가지 방법 • BeginPaint() .. EndPaint(). • 장점: 이 함수 쌍은 어떤 부가적인 코드의 추가 없이 자동으로 유효화(validating)을 수행한다. • 단점: 무효 사각형을 나타내는 rcPaint 속성은 또한 현재 윈도우의 클리핑 영역이기도 하다. 클리핑 영역 외부에는 어떤 것도 그릴 수 없다. 이는 즉, 무효 영역이 아닌 클라이언트 영역에 무언가를 그리고 싶다면 나머지 클라이언트 영역에 접근하기 위해 rcPaint를 다시 정의해야 한다는 것을 뜻한다. • GetDC() .. ReleaseDC() • 장점: 반환된 그리기 표면은 전체 클라이언트 영역을 클리핑 사각형으로 삼는다. 따라서 클라이언트 영역을 그리기 위해 매번 무효 사각형을 다시 정의할 필요가 없다. • 단점: 무효 사각형을 유효화하기 위해 직접 ValidateRect()를 호출해야 한다. 그렇지 않으면 윈도우즈는 언제까지나 응용 프로그램에 WM_PAINT 메시지를 보낼 것이다. 그러나 WM_PAINT 메시지를 받지 않고 윈도우를 그리고자 할 때에는 GetDC()를 사용하는 것이 최선이다
BeginPaint() PAINTSTRUCT ps; // a Windows paint structure HDC hdc; // handle to graphics device context case WM_PAINT: { // obtain handle to device context hdc = BeginPaint(hwnd, &ps); // paint client area // release handle and validate window EndPaint(hwnd, &ps); // always tell Windows that you processed the message return(0); } break;
PAINTSTRUCT typedef struct tagPAINTSTRUCT { HDC hdc; // handle to device context BOOL fErase; // flags whether app should redraw background RECT rcPaint; // clipping rectangle BOOL fRestore; // reserved BOOL fIncUpdate; // reserved BYTE rgbReserved[32]; // reserved } PAINTSTRUCT;
rcPaint • 직사각형의 네 점을 가지는 RECT 구조체 자료형 • 직사각형이 무효(invalid) 사각형을 나타낸다 • 무효 사각형은 다시 그릴 필요가 있는 유일한 영역 typedef struct _RECT { LONG left; // leftmost edge of the RECT LONG top; // topmost edge of the RECT LONG right; // rightmost edge of the RECT LONG bottom; // bottom-most edge of the RECT } RECT;
그냥 전체 client 영역을 그리면 안될까? • rcPaint는 단지 무효 사각형일 뿐만 아니라, 윈도우의 클리핑 사각형이기도 하다. • 따라서 클리핑 사각형을 벗어나는 것은 그린다 해도 그려지지 않을 것이다. • 특히 전체 클라이언트 영역을 그리고 싶다면, 문제가 있다.
GetDC() 는 도대체? • 클리핑 사각형은 전체 클라이언트 영역 • 더 골치가 아픈데도 불구하고 GetDC() .. Release()보다 WM_PAINT 메시지 내에 BeginPaint() .. EndPaint()를 사용하는 이유는 무엇인가? • 그 이유는 GetDC() .. ReleaseDC()는 무효 사각형을 유효화시키지 않기 때문이다. • BeginPaint() .. EndPaint() 쌍은 이 유효화를 자동으로 수행
InvalidateRect() BOOL InvalidateRect( HWND hWnd, // handle of window to invalidate CONST RECT *lpRect, // ptr to rect area to invalidate BOOL bErase); // erase flag for BeginPaint() • 강제로 lpRect 부분을 invalidate 시킴 • 원하는 부분을 그리고 싶을 때 사용 • 만약 언제나 전체 클라이언트 영역을 처리하고 싶다면, lpRect에 NULL를 넣으면 된다.
BeginPaint()..EndPaint()를 사용전체 윈도우 클라이언트 영역을 무효화 case WM_PAINT: { // invalidate the whole window InvalidateRect(hwnd,NULL,TRUE); // start painting hdc = BeginPaint(hwnd,&ps); // do graphics here // stop painting EndPaint(hwnd, &ps); return(0); } break;
만약 어떤 그래픽 작업도 직접 하고 싶지 않다면 case WM_PAINT: { // validate whole window; basically ignore message ValidateRect(hwnd,NULL); return(0); } break;
GetDC() • GetDC()를 이용하면 클리핑 사각형은 전체 클라이언트 영역이 되기 때문 • 무효 사각형을 유효화하지 않는다: ValidateRect()함수를 사용. BOOL ValidateRect( HWND hWnd, // handle of window CONST RECT *lpRect); // pointer to // RECT area to validate
WM_PAINT 메시지 처리에서 GetDC()..ReleaseDC()를 사용 case WM_PAINT: { // get the device context from Windows hdc = GetDC(hwnd); // do graphics here // release the device context to Windows ReleaseDC(hwnd,hdc); // validate the window ValidateRect(hwnd, NULL); return(0); } break;
출력-표면(Display-Surface)의 기초 • 해상도(Resolution): 이 수치는 디스플레이 상의 픽셀의 숫자를 의미한다. 일반적인 해상도는 640x480, 800x600 그리고 1,024x768이다. • 색깊이(Color Depth): 이 수치는 한 색상에 사용할 수 있는 비트 수를 의미하며, 비디오 카드의 메모리 크기에 좌우된다. 일반적인 비디오 카드는 8, 16, 24 개의 픽셀당 비트 수(bits per pixel; bpp)를 가지며, 이는 픽셀의 색상을 결정하기 위해 1, 2, 또는 3 바이트가 필요하다는 뜻이다. 표 5-1은 여러 비트 깊이에 따른 색상수가 나와있다. • 색공간(Color Space): 이 수치는 색깊이에 따른 실제 사용 가능한 색상의 수를 말한다. 예를 들어, 8비트는 256 색이 가능하고, 16비트는 65,536 색이 가능하다.
비디오 카드와 color depth • 대부분의 비디오 카드 • 15비트나 16비트 컬러 모드에 픽셀당 2바이트를 사용 • 24비트나 32비트 컬러 모드의 경우에는 픽셀당 4바이트 • 따라서 모든 픽셀은 WORD (32bit=4bytes)나 QUAD (32bit=4bytes) 바이트로 정렬되어있다. 이러한 정렬로 인해 디스플레이 메모리 접근과 래스터화(rasterization)의 속도가 향상될 수 있다.
bpp • 픽셀당 비트(bits per pixel) or • 픽셀당 바이트(bytes per pixel) • 문맥에 따라서 적절히 파악해야 한다. • 예를 들어, 24 bpp는 당연히 픽셀당 비트 • 3 bpp는 픽셀당 바이트
비디오 모드와 색깊이와 메모리 요구사항 • 2MB 비디오 카드는 대부분의 해상도와 색깊이를 지원할 수 있지만 • 1MB 카드는 가까스로 하이컬러(16bpp) 모드에서 800x600을 처리할 수 있다.
RGB 모드 • 디스플레이 카드가 8bpp를 넘는 비디오 모드 • 순수한 RGB(Red, Green, Blue) 모드
팔레트 모드 • 색상 간접지정(color indirection)이라는 형식을 사용 • 8 비트(1 바이트)로 각 픽셀을 나타낸다. • 픽셀당 총 256개의 서로 다른 색상이나 수치를 가질 수 있다. • 색상조사표(Color Look-Up Table)즉, CLUT • 256개의 항목 각각은 모두 빨강, 초록, 파랑을 위해 8비트씩을 가지고 있다 • 따라서, 모든 픽셀은 24비트를 가진다. • 윈도우즈가 약 20개 정도를 가져가서 윈도우들이나 컨트롤들을 고유한 방법으로 그린다. 그러니까 응용 프로그램이 정의할 수 있는 색상은 겨우 236개만 남는 셈이다. • 각 응용 프로그램이 자신의 236개의 색을 정의할 수 있는 게 아니라, 모든 응용 프로그램이 같은 236개의 색상을 공유해야 한다.
윈도우즈 색상 관리 • 두 개의 주요한 자료 구조 • COLORREF • 빨강, 초록, 파랑 요소로 구성된 간단한 4 바이트 구조체. 색상을 그때그때 만들고자 할 때 사용할 수 있다. • PALETTEENTRY • 좀더 복잡한 4 바이트 구조체로 팔레트를 생성하는 데 사용된다. 표준 RGB 요소에 부가하여 Flags 요소가 들어있다.
윈도우즈 색상 관리 typedef struct tagCOLORREF { BYTE bRed; // red component BYTE bGreen; // green component BYTE bBlue; // blue component BYTE bDummy; // always 0 } COLORREF; typedef struct tagPALETTEENTRY { BYTE peRed; // red component BYTE peGreen; // green component BYTE peBlue; // blue component BYTE peFlags; // control flags set to PC_NOCOLLAPSE } PALETTEENTRY;
RGB macro의 사용 // to create white COLORREF white = RGB(255,255,255); // to create pure red COLORREF red = RGB(255,0,0);
텍스트 출력 • DirectX는 폰트나 텍스트를 지원하지 않는다. • 단어를 출력하기 위해서는 직접 텍스트를 코딩 하거나, 자신의 텍스트 시스템을 만들거나 (보기 좋게 만들려면 다중 크기 폰트를 포함해야 함) GDI를 사용해야 한다.
고정피치(fixed pitch)와 가변피치(proportional pitch) 폰트
텍스트 출력 • TextOut() • 제일 간단한 방법으로 형식화(formatting)나 처리 능력은 없다. 이 함수는 간단히 정적인 문자열을 출력한다. • DrawText() • 제일 복잡한 방법이며, 조정, 클리핑 등의 더 많은 제어가 가능하다. 이 책에서는 이 함수를 사용하지 않는다. 그러나, 이 절의 말미에 프로토타입을 볼 수 있을 것이다.
TextOut() BOOL TextOut( HDC hdc, // handle of device context int nXStart, // x-coordinate of starting position int nYStart, // y-coordinate of starting position LPCTSTR lpString, // address of string to print int cbString); // number of characters in string
TextOut() 예제 • 카운터의 수치를 윈도우 클라이언트 영역의 (0,0)에 출력 char buffer[80]; // used to build up strings static int counter = 0; // used to count case WM_PAINT: { // start painting hdc = BeginPaint(hwnd,&ps); // create the output string and get its length int length = sprintf(buffer,”The Count is %d “,(int)counter++); // print the text TextOut(hdc,0,0,buffer,length); // end painting EndPaint(hwnd,&ps); return(0); } break;
매 사이클마다 출력되기를 원한다면 • 앞의 예제의 한가지 문제점은 WM_PAINT 메시지가 보내질 때만 텍스트가 출력된다는 것이다. • 매 사이클마다 출력되기를 원한다면, // enter main event loop while(1) { if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { // test whether the message is WM_QUIT if (msg.message == WM_QUIT) break; // translate any accelerator keys TranslateMessage(&msg); // send the message to the window proc DispatchMessage(&msg); } // end if // main game processing goes here // get the graphics device context hdc = GetDC(hwnd); // create the output string and get its length int length = sprintf(buffer,”The Count is %d “,(int)counter++); // print the text TextOut(hdc,0,0,buffer,length); // release the device context ReleaseDC(hwnd,hdc); } // end while
DrawText() • 더 많은 제어를 (조정, 클리핑 등) 가능케 한다. int DrawText( HDC hDC, // handle to device context LPCTSTR lpString, // pointer to string to draw int nCount, // string length LPRECT lpRect, // clipping rectangle UINT uFormat); // formatting flags
텍스트에 색깔 넣기 • 전경과 배경 • 각 텍스트 문자는 배경과 전경이 있다. 전경은 문자 자체고, 배경은 문자를 포함하는 구획이다. • 투명도 • 텍스트 배경을 투명하게 하면 텍스트는 날인(문자열을 출력할 때 더 선호되는 방식)되기보다는 그려진 것처럼 보인다. • 불투명도(투명도 없이) • 문자의 배경 색깔이 아래의 모든 것들을 가리게 한다.
세 단계 • 텍스트의 투명도를 정한다. • 텍스트의 전경 색상을 정한다. • 텍스트의 배경 색상을 정한다.
SetBkMode() int SetBkMode( HDC hdc, // handle of device context int iBkMode); // flag specifying background mode • iBkMode는 배경 모드로 TRANSPARENT나 OPAQUE 중 하나
SetTextColor() 와 SetBkColor() • SetTextColor() • 텍스트 자체의 색상을 정하는 데 사용 • SetBkColor() • 텍스트 아래의 배경색을 (그리고 다른 GDI 객체의 배경색도) 정하는 데 사용 COLORREF SetTextColor( HDC hdc, // handle of device context COLORREF crColor); // text color COLORREF SetBkColor( HDC hdc, // handle of device context COLORREF crColor); // background color
저장-변경-사용-복원 사이클 - 1 • 대부분의 GDI 함수는 지금 변경하고자 하는 것의 원래 값을 반환 • 빨간 텍스트와 파란 텍스트를 두 가지 배경 모드(투명과 불투명)에서 출력하는 예 COLORREF old_text_color, old_back_color; // save the old colors // get the device context hdc = GetDC(hwnd); // set transparency mode SetBkMode(hdc,TRANSPARENT); // set colors and save old old_text_color = SetTextColor(hdc,RGB(255,0,0)); old_back_color = SetBkColor(hdc,RGB(0,0,0));
저장-변경-사용-복원 사이클 - 2 // print the text TextOut(hdc,0,0,”I’m the red text!”,strlen(“I’m the red text!”)); // switch background mode to opaque SetBkMode(hdc,OPAQUE); // change text color; no need to save old color SetTextColor(hdc,RGB(0,0,255)); // print the text TextOut(hdc,0,0,”I’m the blue text!”,strlen(“I’m the blue text!”)); // restore the old colors SetTextColor(hdc,old_text_color); SetBkColor(hdc,old_back_color); // release dc back ReleaseDC(hwnd, hdc);
TEXTMETRIC 검색하기 • 윈도우즈는 가변피치 폰트를 사용한다 • 텍스트 문자열의 폭과 높이를 정확하게 아는 것은 어려운 일이다.
TEXTMETRIC structure typedef struct tagTEXTMETRIC { LONG tmHeight; // * the height of the font LONG tmAscent; // the ascent of the font LONG tmDescent; // the descent of the font LONG tmInternalLeading; // the internal leading LONG tmExternalLeading; // the external leading LONG tmAveCharWidth; // * the average width LONG tmMaxCharWidth; // * the maximum width LONG tmWeight; // the weight of the font LONG tmOverhang; // the overhang of the font LONG tmDigitizedAspectX; // the average x-aspect ratio LONG tmDigitizedAspectY; // the average y-aspect ratio BCHAR tmFirstChar; // * first character font defines BCHAR tmLastChar; // * last character font defines BCHAR tmDefaultChar; // used when desired char not in set BCHAR tmBreakChar; // the break character BYTE tmItalic; // is this an italic font BYTE tmUnderlined; // is this an underlined font BYTE tmStruckOut; // is this a strikeout font BYTE tmPitchAndFamily;//family and technology BYTE tmCharSet; // what is the character set } TEXTMETRIC;
GetTextMetrics() BOOL GetTextMetrics( HDC hdc, // handle of device context LPTEXTMETRIC lptm); // ptr to text metrics struct TEXTMETRIC tm; // holds the data // get the textmetrics GetTextMetrics(hdc,&tm); // use tm data to center a string given the horizontal // width of screen is WINDOW_WIDTH int x_pos = WINDOW_WIDTH - strlen(“Center This String”)*tm.tmAveCharWidth/2; // print the text at the centered position TextOut(hdc,x_pos,0,”Center This String”, strlen(“Center This String”));
작은 글자는 무엇으로 만들어지는가? • 높이: 문자의 전체 높이를 나타내는 픽셀 수 • 기준선: 참고. 보통 대문자의 바닥. • 어센트: 기준선에서 액센트 표시가 위치하는 상단까지의 픽셀 수. • 디센트: 기준선에서 소문자의 바닥까지의 픽셀 수. • 내부 간격: 액센트 표시를 위한 픽셀 수. • 외부 간격: 문자 위의 다른 문자까지의 픽셀 수.
각종 모양 그리기 • 점 • 선 • 원, 타원 • 곡선 • 다각형 • 비트맵 • 메타파일(더 기본적인 다른 명령들의 디지타이즈된 기록들 - 다시 볼 수 있는 명령어 목록 같은 것)
펜으로 그리기 • 펜은 외곽선 객체(선, 곡선, 채워지지 않은 사각형)를 그리는 데 사용되는 객체 • 색상: 표준 RGB 색상 • 스타일: 선이 그려지는 방법 - 실선, 점선 등 HPEN pen_1; // a handle to a pen // 시스템 pen 이용 pen_1 = (HPEN)GetStockObject(WHITE_PEN); // 자신만의 pen 만들기 HPEN CreatePen(int fnPenStyle, // pen style int nWidth, // pen width COLORREF crColor); // pen color
펜의 스타일 HPEN red_pen = CreatePen(PS_SOLID, 0, RGB(255,0,0)); HPEN green_pen = CreatePen(PS_SOLID, 0, RGB(0,255,0)); HPEN blue_pen = CreatePen(PS_SOLID, 0, RGB(0,0,255));
펜을 사용 • 장치 컨텍스트안에 펜을 선택하도록 SelectObject() 함수를 사용 HGDIOBJ SelectObject(HDC hdc, // handle of device context HGDIOBJ hgdiobj); // handle of object // select the red pen in and save old one HPEN old_pen = (HPEN)SelectObject(hdc, red_pen); // code to draw with the pen goes here
Delete Pen // delete all the objects // which is usually done at end of program in WM_DESTROY DeleteObject(red_pen); DeleteObject(green_pen); DeleteObject(blue_pen);
필요한 브러시로 작업하기 • 브러시는 그려진 다른 객체의 내부를 칠하는 데 사용 • 단색(solid) 브러시, 패턴 브러시나 비트맵 이미지 브러시 등을 생성할 수 있다. • 브러시의 핸들은 HBRUSH이고 • 펜을 사용하는 것처럼 GetStockObject()로 다음과 같이 브러시를 불러올 수 있다 HBRUSH brush_1; // a brush hbrush_1 = (HBRUSH)GetStockObject(WHITE_BRUSH);
브러시 패턴 HBRUSH blue_brush = CreateSolidBrush(RGB(0,0,255)); HBRUSH CreateHatchBrush(int fnStyle, // hatch style COLORREF clrref);// color value Patterns: • HS_BDIAGONAL • HS_CROSS • HS_DIAGCROSS • HS_FDIAGONAL • HS_HORIZONTAL • HS_VERTICAL