튜토리얼 5: More about Text


이번 시간에는 폰트나 컬러와 같은 텍스트 속성들에 대해서 살펴볼 것입니다.
 



Theory:

Windows 의 색상 시스템은 RGB 값(R=Red, G=Green, B=Blue)을 바탕으로 하고 있습니다. Windows 에서 어떤 색상을 표현하려면, 이 세가지 색으로 표현해야만 합니다. 각각의 색은 0 에서 255 (byte 범위) 사이의 값입니다. 예를들어, 순수한 빨강은 255.0.0 입니다. 새하얀 색은 255.255.255 로 나타낼 수 있습니다. 예제를 통해 보신것 처럼, 색을 섞고 비교하는데 뛰어난 사람이 아닌 이상,  이 시스템 방식으로 원하는 색상을 만들어 내는 것이, 매우 어렵습니다.

글씨의 전경색과 배경색을 지정하기 위해서는 SetTextColor 와 SetBkColor 를 사용하면 됩니다. 이 함수를 사용하기 위해서는 32-bit RGB 값과 Device-Context 가 필요합니다. 32-bit RGB 값의 구조체는 다음과 같이 정의되어 있습니다.


 RGB_value struct     unused   db 0
     blue       db ?
     green     db ?
     red        db ?
 RGB_value ends 



반드시 기억해야 할 것은 첫번째 바이트는 사용하지 않으며, 값은 0 이 되어야 한다는 것입니다. 나머지 3 Bytes 는 파랑, 초록, 빨강으로 사용됩니다. 그러나 우리는 이 구조체를 초기화 하고 사용하는데 불편하기 때문에 사용하지 않고 대신 매크로를 만들어서 사용할 것입니다. 이 매크로는 빨강, 초록, 파랑의 값을 파라미터로 받아 32-bit RGB 값을 만들고 EAX 레지스터에 저장 합니다. 이 매크로는 다음과 같습니다.



<font color="#474747">RGB macro red,green,blue 
    xor    eax,eax 
    mov  ah,blue 
    shl     eax,8 
    mov  ah,green 
    mov  al,red 
endm</font>


사용하려면 include 파일에 이 매크로를 넣으면 됩니다.
폰트를 만드려면 CreateFont 나 CreateFontIndirect 함수를 사용하면 됩니다. 두 함수의 차이점은 CreateFontIndirect 의 파라미터는 logical font 구조체인 LOGFOT 구조체를 가리키는 포인터 하나라는 것입니다. 만약 프로그램에서 빈번하게 폰트를 변경해야 한다면, CreateFontIndirect 가 더 편리합니다. 그러나 이번 예제에서는 단 하나의 font 만 생성할 것이기 때문에, CreateFont 를 사용할 것입니다. CreateFont 를 호출하고 나면, 선택한 device context 의 폰트에 대한 핸들이 리턴됩니다. 이후 부터는 선택한 device context 안에서의 모든 텍스트 관련 API 함수 사용시, 해당 폰트가 적용됩니다.
 
 

Content:


.386 
.model flat,stdcall 
option casemap:none 
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD 

include \masm32\include\windows.inc 
include \masm32\include\user32.inc 
include \masm32\include\kernel32.inc 
include \masm32\include\gdi32.inc 
includelib \masm32\lib\user32.lib 
includelib \masm32\lib\kernel32.lib 
includelib \masm32\lib\gdi32.lib 

RGB macro red,green,blue 
        xor eax,eax 
        mov ah,blue 
        shl eax,8 
        mov ah,green 
        mov al,red 
endm 

.data 
ClassName db "SimpleWinClass",0 
AppName  db "Our First Window",0 
TestString  db "Win32 assembly is great and easy!",0 
FontName db "script",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 hfont:HFONT 

    .IF uMsg==WM_DESTROY 
        invoke PostQuitMessage,NULL 
    .ELSEIF uMsg==WM_PAINT 
        invoke BeginPaint,hWnd, ADDR ps 
        mov    hdc,eax 
        invoke CreateFont,24,16,0,0,400,0,0,0,OEM_CHARSET,\ 
                                       OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,\ 
                                       DEFAULT_QUALITY,DEFAULT_PITCH or FF_SCRIPT,\ 
                                       ADDR FontName 
        invoke SelectObject, hdc, eax 
        mov    hfont,eax 
        RGB    200,200,50 
        invoke SetTextColor,hdc,eax 
        RGB    0,0,255 
        invoke SetBkColor,hdc,eax 
        invoke TextOut,hdc,0,0,ADDR TestString,SIZEOF TestString 
        invoke SelectObject,hdc, hfont 
        invoke EndPaint,hWnd, ADDR ps 
    .ELSE 
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam 
        ret 
    .ENDIF 
    xor    eax,eax 
    ret 
WndProc endp 

end start 



 


Analysis:


invoke CreateFont,24,16,0,0,400,0,0,0,OEM_CHARSET,\ 
    OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,\ 
    DEFAULT_QUALITY,DEFAULT_PITCH or FF_SCRIPT,\ 
    ADDR FontName 


      

CreateFont 는 입력한 파라미터와 font 의 데이터가 가장 일치하는 logical 폰트를 만들어 줍니다. 이 함수는 다른 windows 함수들보다 훨씬 많은 파라미터를 가지고 있습니다. 그리고 SelectObject 함수에서 사용하는 logical 폰트의 핸들을 리턴해 줍니다. 이 함수의파라미터에 대해서 좀 더 알아 보도록 하겠습니다.


CreateFont proto nHeight:DWORD,\ 
    nWidth:DWORD,\ 
    nEscapement:DWORD,\ 
    nOrientation:DWORD,\ 
    nWeight:DWORD,\ 
    cItalic:DWORD,\ 
    cUnderline:DWORD,\ 
    cStrikeOut:DWORD,\ 
    cCharSet:DWORD,\ 
    cOutputPrecision:DWORD,\ 
    cClipPrecision:DWORD,\ 
    cQuality:DWORD,\ 
    cPitchAndFamily:DWORD,\ 
    lpFacename:DWORD 



nHeight   원하는 문자의 높이. 0 은 기본 사이즈. 
nWidth   원하는 문자의 너비. 보통 이 값은 windows 가 높이에 따라 자동으로 맞추도록 0 을 사용합니다. 그러나  예제에서는 기본 너비값으로는 문자를 읽기 힘듦니다. 그래서 너비를 16 으로 하였습니다.
nEscapement   앞 문자 대비 다음 문자의 출력방향을 정의합니다. 이때 값은 '0.1 도' 단위입니다. 일반적으로 0 을 사용합니다. 만약 900 이라면 처음 문자 이후로는 문자들이 위로 올라갈 것입니다. 그리고 1800 이라면 문자들이 뒤로 쓰여질 것이고, 2700 이라면 아래로 쓰여지겠죠.   
nOrientation   출력할 때, 문자의 회전각을 정의합니다. 이때 값은 '0.1 도' 단위입니다. 예를들어 값이 900 이라면 문자는 직각으로 눕여지고, 1800 이라면 위아래가 뒤집혀 질 것입니다.
nWeight   각 문자의 라인 두께를 지정합니다. windows 아래와 같이 값을 정의하고 있습니다.
 


FW_DONTCARE     equ 0 
FW_THIN                  equ 100 
FW_EXTRALIGHT  equ 200 
FW_ULTRALIGHT  equ 200 
FW_LIGHT                equ 300 
FW_NORMAL          equ 400 
FW_REGULAR         equ 400 
FW_MEDIUM           equ 500 
FW_SEMIBOLD       equ 600 
FW_DEMIBOLD      equ 600 
FW_BOLD                 equ 700 
FW_EXTRABOLD   equ 800 
FW_ULTRABOLD   equ 800 
FW_HEAVY              equ 900 
FW_BLACK              equ 900


cItalic   일반적으로 0. 다른 값이 들어갈 경우 이태릭 문자로 표시됩니다.
cUnderline   일반적으로 0. 다른 값이 들어갈 경우 문자에 밑줄이 그어집니다. 
cStrikeOut   일반적으로 0. 다른 값이 들어갈 경우 문자의 가운데에 가로선(취소선)이 그어집니다.
cCharSet  폰트의 문자집합(Character-set). 일반적으로 Windows 가 O/S 에 종속적으로 선택하도록 OEM_CHARSET 을 사용합니다. 
cOutputPrecision  얼마나 우리가 지정한 특성에 맞게 표현해야 하는지 정의합니다. 일반적으로 기본적인 폰트 맵핑을 이미하는 OUT_DEFAULT_PRECIS 를 사용합니다. 
cClipPrecision  얼마나 정밀하게 클리핑(clipping) 할 것인지 정밀도를 정의합니다. 이 값은 문자가 클리핑 영역 밖으로 잘려나갔을 때, 어떻게 처리할 것인지를 결정합니다. 기본값으로 CLIP_DEFAULT_PRECIS 를 사용할 수 있습니다.
cQuality   출력의 퀄리티(quality)를 정의합니다. 출력의 퀄리티는 GDI 가 얼마나 정교하게 논리적 폰트(logical-font)의 속성을 물리적 폰트(physical-font)의 속성과 일치시킬 것인지에 따라 달라집니다. 여기에는 DEFAULT_QUALITY, PROOF_QUALITY,  DRAFT_QUALITY 중 하나가 사용됩니다.
cPitchAndFamily  폰트의 피치(pitch)와 군(family) 를 나타냅니다. "or" 연산자를 통해 pitch 와 family 값을 합쳐서 표현할 수 있습니다.  
lpFacename  폰트의 이름을 나타내는 문자열(null-terminated)을 가리키는 포인터.

위에서는 간단히 핵심만 설명했습니다. 더 자세히 알고 싶다며 Win32 API 레퍼런스를 참고하시기 바랍니다.


invoke SelectObject, hdc, eax 
mov    hfont,eax 


logical 폰트의 핸들을 구한 뒤에는, SelectObject 를 호출하여 device context 내에 폰트를 설정해 주어야 합니다. SelectObject 는 펜, 브러쉬, 폰트와 같은 GDI object 들을 GDI 함수가 사용하는 devce context 안에 지정해 줍니다.  이 함수는 저장해 두었다가 나중에 복구할 때 SelectObject 함수를 통해 사용할 수 있도록, 교체될(현재의) object 핸들을 리턴해 줍니다. SelectObjec 함수를 호출하고 나면, 이 device cotext 에 대한 모든 Text 출력 함수는 방금 지정한 폰트 사용하게 됩니다.    


RGB    200,200,50 
invoke SetTextColor,hdc,eax 
RGB    0,0,255 
invoke SetBkColor,hdc,eax 



 

SetColorText 와 SetBkColor 에서 사용하는 32-bit RGB 값을 만들기 위해서는 RGB 매크로를 사용하세요. 


invoke TextOut,hdc,0,0,ADDR TestString,SIZEOF TestString 



Client 영역에 문자를 나타내기 위해서는 TextOut 함수를 사용하세요. 텍스트가 우리가 앞서 지정한 폰트와 색깔로 나타날 것입니다. 


invoke SelectObject,hdc, hfont  



폰트를 사용하다보면, device context 안에서 이전 폰트로  되돌려야 할 때가 있습니다. 이럴때는 device context 에서 변경했던 이전 object 로 복구시키면 됩니다.