텍스트 그리기
이론:
Windows의 텍스트는 GUI 객체 유형입니다. 각 문자는 뚜렷한 패턴으로 함께 묶인 수많은 픽셀 (점)로 구성됩니다. 이것이 "글쓰기"라고 말하는 대신 "그리기"이라고 불리는 이유입니다. 일반적으로, 당신은 자신의 클라이언트 영역에 텍스트를 칠하고 있다고 보시면 됩니다
(사실, 클라이언트 영역 밖에서 칠할 수 있지만 그것은 또 다른 이야기가 될 것입니다).
Windows에서 텍스트를 화면에 놓는 것은 DOS와 크게 다릅니다. DOS에서는 80x25 크기로 화면을 생각할 수 있습니다. 그러나 Windows에서는 여러 프로그램에서 화면을 공유합니다. 프로그램이 서로의 화면을 덮지 않도록 일부 규칙을 적용 해야 합니다. Windows는 각 윈도우의 스크린 영역을 자체 클라이언트 영역으로 만 제한하여 이 작업을 할 수 있도록 보장합니다. 윈도우의 클라이언트 영역 크기도 일정하지 않습니다. 사용자는 언제든지 크기를 변경할 수 있기 때문이며, 따라서 자신의 클라이언트 영역의 크기를 동적으로 결정 해야 합니다
클라이언트 영역에서 무언가를 칠하기 전에 Windows에서 허가를 요청 해야 합니다. 맞습니다. DOS에서 한 것처럼 더 이상 화면을 완벽하게 제어 할 수 없습니다. Windows에 자신의 클라이언트 영역을 그리는 권한을 요청 해야만 합니다. Windows는 클라이언트 영역, 글꼴, 색 및 기타 GDI 특성의 크기를 결정하고 장치 컨텍스트 핸들을 프로그램으로 보냅니다. 그런 다음 장치 컨텍스트를 여권으로 사용하여 클라이언트 영역을 그릴 수 있습니다.
장치 컨텍스트 란 무엇일까요? 장치 컨텍스트란 Windows에서 내부적으로 유지 관리하는 데이터 구조 일뿐입니다. 장치 컨텍스트는 프린터 또는 비디오 디스플레이와 같은 특정 장치와 연관됩니다. 비디오 디스플레이의 경우 장치 컨텍스트는 일반적으로 디스플레이의 특정 창과 연관됩니다
디바이스 컨텍스트의 값 중 일부는 색상, 글꼴 등과 같은 그래픽 속성입니다. 이 값들은 사용자가 마음대로 변경할 수 있는 기본값입니다. 모든 GDI 함수 호출에서 이러한 특성을 지정하는 데 드는 로드를 줄이는 데 도움이 됩니다.
장치 컨텍스트는 Windows에서 준비한 기본 환경으로 생각할 수 있습니다. 원하는 경우 나중에 기본 설정을 무시할 수 있습니다.
프로그램이 그릴 필요가 있을 때, 장치 콘텍스트에 대한 핸들을 얻어야만 합니다.
일반적으로 이를 수행하는 데는 다음과 같이 여러 가지 방법들이 있습니다.
call BeginPaint in response to WM_PAINT message.
call GetDC in response to other messages.
call CreateDC to create your own device context
한 가지 기억해야 할 것은 장치 컨텍스트 핸들을 종료 되면, 단일 메시지 처리 중간에 해제해야 한다는 것입니다. 하나의 메시지에 대한 응답으로 핸들을 얻고 또 다른 메시지에 대한 응답으로 핸들을 해제하지 않도록 주의 해야 합니다.
Windows는 WM_PAINT 메시지를 윈도우에 게시하여 클라이언트 영역을 다시 칠할 시간임을 알립니다. Windows는 윈도우의 클라이언트 영역 내용을 저장하지 않습니다. 대신, 윈도우가 다른 윈도우에 의해 덮여 있다가 상위로 올라왔을 때와 같이 클라이언트 영역의 페인트가 필요 한 상황이 발생하면 Windows는 WM_PAINT 메시지를 해당 윈도우의 메시지 대기열에 넣습니다. 자체 클라이언트 영역을 다시 그리는 것은 각 창의 책임입니다. WM_PAINT 메시지가 도착하면 윈도우 프로시저가 클라이언트 영역을 다시 칠할 수 있도록 윈도우 프로시저의 WM_PAINT 섹션에서 클라이언트 영역을 다시 채우는 방법에 대한 모든 정보를 수집 해야 합니다
또 생각을 해봐야 할 개념은 유효하지 않은 사각형 영역입니다. Windows에서 유효하지 않은 사각형 영역은(invalid rectangle) 클라이언트 영역에서 다시 칠해야 하는 가장 작은 직사각형 영역으로 정의합니다. Windows에서는 윈도우의 클라이언트 영역에서 유효하지 않은 사각형을 감지하면 해당 창에 WM_PAINT 메시지를 게시합니다. WM_PAINT 메시지에 대한 응답으로 윈도우는 유효하지 않은 사각형의 좌표를 포함하는 paintstruct 구조체를 얻을 수 있습니다. 잘못된 사각형의 유효성을 검사하기 위해 WM_PAINT 메시지에 대한 응답으로 BeginPaint를 호출합니다. WM_PAINT 메시지를 처리하지 않으면 DefWindowProc 또는 ValidateRect를 호출하여 유효하지 않은 사각형의 유효성을 검사 할 수 있도록 합니다. 그렇지 않으면 Windows에서 WM_PAINT 메시지를 반복해서 보냅니다.
다음은 WM_PAINT 메시지에 대한 응답으로 수행 해야 하는 단계입니다.
Get a handle to device context with BeginPaint.
Paint the client area.
Release the handle to device context with EndPaint
유효하지 않은 사각형의 유효성을 명시 적으로 확인할 필요는 없습니다. 이것은 BeginPaint 호출에 의해 자동으로 수행됩니다. BeginPaint-Endpaint 쌍 사이에서 클라이언트 영역을 그리기 위해 모든 GDI 함수를 호출 할 수 있습니다. 거의 모든 함수들은 매개 변수로서 장치 컨텍스트에 대한 핸들을 요구합니다.
본문
우리는 클라이언트 영역의 중앙에 "Win32 assembly is great and easy!"이라는 텍스트 문자열을 표시하는 프로그램을 작성 할 것입니다.
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
코드 분석:
대부분의 코드는 자습서 3의 예제와 동일하기 때문에 중요한 변경 사항 만 설명합니다.
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
이 선언들은 우리의 WM_PAINT 섹션에서 GDI 함수에 의해 사용되는 지역 변수입니다. hdc는 BeginPaint 호출에서 반환 된 장치 컨텍스트에 대한 핸들을 저장하는 데 사용됩니다. ps는 PAINTSTRUCT 구조체입니다. 보통 ps에서는 값을 사용하지 않습니다. 그것은 BeginPaint 함수에 전달되고 Windows는 이 구조체에 적절한 값으로 채 웁니다. 그런 다음 클라이언트 영역 그리기를 마치면 ps를 EndPaint 함수에 전달합니다. rect는 다음과 같이 정의 된 RECT 구조체입니다.
RECT Struct
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends
왼쪽 및 위쪽은 사각형의 왼쪽 위 모서리의 좌표입니다. 오른쪽 및 아래쪽은 오른쪽 아래 모서리의 좌표입니다.
한가지 기억해야 할 점 :
x-y 축의 원점은 클라이언트 영역의 왼쪽 상단에 있습니다. 따라서
점 y = 10은 점 y = 0보다 아래입니다.
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
WM_PAINT 메시지에 응답으로 그리기를 시작 할 윈도우에 대한 핸들을 가진 BeginPaint와 매개 변수로 초기화되지 않은 PAINTSTRUCT 구조체를 호출합니다. 호출이 성공한 후 eax는 장치 컨텍스트에 대한 핸들을 포함합니다. 다음으로 GetClientRect를 호출하여 클라이언트 영역의 dimension을 호출합니다. dimension는 DrawText에 매개 변수 중 하나로 전달되는 rect 변수로 반환됩니다. DrawText의 구문은 다음과 같습니다.
DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD |
DrawText는 고 수준 텍스트 출력 API 함수입니다. 그것은 단어 감싸기, 중심 맞춤 등과 같은 일부 세부적인 그리기 위한 문자열에 집중할 수 있도록 세부 사항을 처리해 줍니다. 좀 더 저 수준 레벨 함수 인TextOut이 다음 자습서에서 검토 될 것입니다. DrawText는 사각형의 경계 내에 맞도록 텍스트 문자열의 서식을 지정합니다. 현재 선택한 글꼴, 색상 및 배경 (장치 컨텍스트에서)을 사용하여 텍스트를 그립니다. 선은 사각형의 경계 내에 맞게 줄 바꿈 됩니다. 장치 단위 (이 경우 픽셀)의 출력 텍스트의 높이를 반환합니다. 이 함수의 매개 변수들을 살펴 보도록 하겠습니다 :
hdc 장치 컨텍스트의 핸들 lpString 직사각형에 그리려는 문자열에 대한 포인터입니다. 문자열은 널로 끝나야합니다. 그렇지 않으면 다음 매개 변수 인 nCount에서 길이를 지정해야합니다. nCount 출력 할 문자 수. 문자열이 널 종료 (null-terminated)이면 nCount는 -1이어야합니다. 그렇지 않으면 nCount는 그릴 문자열의 문자 수를 포함 해야합니다. lpRect 문자열을 그리려는 직사각형 (RECT 유형의 구조체)에 대한 포인터입니다.이 직사각형은 또한 클리핑 직사각형입니다. 즉, 이 직사각형 바깥에는 문자열을 그릴 수 없습니다. uFormat 문자열이 사각형에 표시되는 방법을 지정하는 값입니다. "or"연산자로 결합 된 세 개의 값을 사용합니다. DT_SINGLELINE 문자열이 단일 라인임을 지정한다 DT_CENTER 문자열이 수평으로 가운데를 맞춘다 DT_VCENTER 텍스트를 세로 가운데에 배치합니다. DT_SINGLELINE과 함께 사용 해야합니다. |
클라이언트 영역 그리기을 마친 후에 EndPaint 함수를 호출하여 핸들을 장치 컨텍스트로 해제 해야만 합니다.
여기 까지 입니다. 그러면 주목 해야 할 부분을 요약 해 보도록 합니다:
- WM_PAINT 메시지에 대한 응답으로 BeginPaint-EndPaint 쌍을 호출합니다.
- BeginPaint 및 EndPaint 호출 사이의 클라이언트 영역에서 원하는 작업을 수행합니다.
다른 메시지에 대한 응답으로 클라이언트 영역을 다시 그리려는 경우 다음 두 가지 중에서 선택할 수 있습니다. - GetDC-ReleaseDC 쌍을 사용하고 이러한 호출 안에서 그림을 그립니다.
- InvalidateRect 또는 UpdateWindow를 호출하여 전체 클라이언트 영역을 무효화하고 윈도우의 메시지 대기열에
WM_PAINT 메시지를 넣도록 강제하고 WM_PAINT 섹션에서 그리도록 할 수 있습니다.
이상.
'프로그래밍' 카테고리의 다른 글
[C#] 바이너리 파일 표시기 만들기 - ByteViewer 클래스 (0) | 2022.04.10 |
---|---|
[자바]파일에서 BOM 문자 제거하기 (0) | 2022.04.10 |
Win32 어셈블리 프로그래밍 - 3. 간단한 윈도우 (0) | 2022.04.04 |
Win32 어셈블리 프로그래밍 - 2. 메시지 박스 (0) | 2022.04.04 |
Win32 어셈블리 프로그래밍 - 1. 기초 (0) | 2022.04.04 |