Tutorial 4: Kreslení textu

V tomto tutorialu se naučíme, jak "kreslit" text do klientské oblasti okna a také se dozvíme o kontextu zařízení(device context).
Můžete si stáhnout zdroj: zde.

Teorie:

Text ve Windows je typem GUI objektu.  Každý znak je tvořen mnoha pixely, které dohramady dávají určitý vzor. Proto mluvíme o "kreslení" namísto "psaní". Většinou kreslíte text do vlastní klientské oblasti, (ve skutečnosti můžete kreslit i mimo, ale to už je jiné povídání).  Dostat text na obrazovku ve Windows je podstatně rozdílné od DOSu. V DOSu jste mohli brát obrazovku jako oblast o rozměrech 80x25. Ale ve Windows je obrazovka sdílena několika programy. Proto musí být prosazena určitá pravidla, aby se zabránilo, aby si programy navzájem překreslovaly obrazovku. Windows to zajišťuje tak, že omezuje plochu kreslení pro každé okno na oblast své vlastní klientské oblasti. Velikost klientské oblasti okna není konstanta. Uživatel může velikost oblasti změnit kdykoliv, takže ji musíte určovat dynamicky.
Předtím než budete moci kreslit cokoliv do své klientské oblasti, musíte požádat o povolení od Windows. Správně, již nejste absolutním vládcem obrazovky jako jste bývali v DOSu.  Musíte požádat Windows, aby jste mohli kreslit po své oblasti. Windows určí velikost klientské oblasti, font, barvy a další GDI attributy a pošle vám handle ke kontextu zařízení, který pak můžete použít jako jakýsi pas pro kreslení po své oblasti.
Co je kontext zařízení ? Je to pouze datová struktura, kterou interně spravuje samotné Windows. Device kontext je pak asociován s jednotlivými zařízeními, jako je tiskárna nebo grafický adapter. Pro grafický adapter je kontext zařízení obvykle asociován s jednotlivým oknem na obrazovce.
Některé z hodnot kontextu zařízení jsou grafické atributy jako je barva, font apod. To jsou standardní hodnoty, které můžete měnit. Existují jenom pro úsporu, abychom je nemuseli specifikovat při každém volání funkcí GDI.
Můžete také vidět kontext zařízení jako standardní prostředí, které pro vás Windows připravilo. Můžete přepsat některá nastavení později, jestli budete chtít.
Když potřebuje program kreslit, musí získat handle ke kontextovému zařízení. Je několik způsobů, jak to udělat: Jednu věc si ale musíte pamatovat, jakmile využijete handel ke kontextu zařízení pro své účely, musíte ho uvolnit v rámci zpracování jedné zprávy. Nesnažte se získávat handle jako reakci na jednu zprávu a potom ho uvolnit při reakci na jinou zprávu.
Windows posílá WM_PAINT zprávy oknu, aby ho upozornilo, že je čas překreslit svou klientskou oblast. Windows neukládá obsah klientské oblasti okna.   Místo toho, když nastane situace, která hlásí potřebu překreslit klientskou oblast, (když bylo okno překryto jiným a nyní je odkrýváno např.), Windows vloží WM_PAINT zprávu do fronty zpráv tomu oknu. Je zodpovědností toho okna, aby si překreslilo svoji oblast. Musíte získat všechny informace o tom jak překreslit oblast ve WM_PAINT sekci vaší procedury okna, aby mohla procedura okna překreslit oblast správně, když WM_PAINT zpráva dorazí.
Dalším konceptem, který musíte pochopit je invalid rectangle (neplatný obdélník). Windows definuje tento neplatný obdélník jako nejmenší obdélníkovou plochu v klientské oblasti, která potřebuje být překreslena. Když Windows detekuje tuto plochu v klientské oblasti okna, pošle oknu WM_PAINT zprávu. Jako reakci na WM_PAINT zprávu může okno získat paintstruct strukturu, která mimo jiné obsahuje souřadnice této plochy neplatného obdélníku. Vy pak voláte BeginPaint jako reakci na WM_PAINT zprávu, aby jste učinili neplatný obdélník platným. Pokud nezpracujete WM_PAINT zprávu, přinejmenším musíte zavolat DefWindowProc nebo ValidateRect, aby jste učinili neplatnou oblast platnou, jinak vám windows budou opakovaně posílat WM_PAINT zprávu.
Zde jsou kroky, které by jste měli učinit jako odpověď na WM_PAINT zprávu: Všimněte si, že explicitně nemusíte učinit neplatnou oblast platnou. To automaticky udělá BeginPaint funkce. Mezi BeginPaint-Endpaint můžete volat jakoukoliv GDI funkci pro kreslení do klientské oblasti. Téměř všechny vyžadují handel ke kontextu zařízení.

Obsah:

Napíšeme program, který zobrazí řetězec "Win32 assembly is great and easy!" ve středu klientské oblasti.
 
.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

Analysis:

Většina kódu je stejná jako je v Tutorialu 3. Vysvětlím pouze důležité změny.

    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT
    LOCAL rect:RECT

Toto jsou lokální proměnné, které používají GDI funkce v naší WM_PAINT části. hdc ukládá handel ke kontextu zařízení obdržený od funkce BeginPaint, ps je PAINTSTRUCT struktura. Většinou nepoužíváte hodnoty uložené v ps. Je předána BeginPaint funkci a Windows ji naplní vhodnými hodnotami. Vy poté předáte ps EndPaint funkci, když skončíte s kreslením do klientské oblasti. rect je RECT struktura definována takto:
 

RECT Struct
    left           LONG ?
    top           LONG ?
    right        LONG ?
    bottom    LONG ?
RECT ends
Left a top jsou souřadnice horního levého rohu, Right a bottom jsou souřadnice pravého dolního rohu obdélníku. Jedna věc je důležitá: Počátek os x-y leží v horním levém rohu klientské oblasti, takže např. bod y=10 je POD bodem 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

Jako reakci na WM_PAINT zprávu voláte BeginPaint s handelem k oknu, do kterého chcete kreslit a neinicializovanou PAINTSTRUCT strukturu jako parametry. Po úspěšném zavolání obsahuje eax handle ke kontextu zařízení. Dále voláme GetClientRect, abychom získali rozměry klientské oblasti. Rozměry jsou vráceny v proměnné rect, kterou předáváme funkci DrawText jako jeden z jejich parametrů. DrawText syntaxe je:

DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD

DrawText je vyšší API funkce pro výstup textu. Stará se o některé detaily jako je zarovnání slov, centrování apod., aby jste se mohli zaměřit na řetězec, který chcete nakreslit. Její nižší (low-level) bratříček, TextOut, bude objektem našeho zkoumání příště :). DrawText zformátuje řetězec tak, aby se vešel do obdélníku. Použije současně zvolený font, barvu a pozadí (v kontextu zařízení) a nakreslí text. Vrací výšku textu v jednotkách zařízení, v našem případě v pixelech. Pojďme si prohlédnout její parametry:

Po skončení kreslení musíte zavolat EndPaint funkci k uvolnění handelu ke kontextu zařízení.
A je to. Shrňme si znovu důležité body:
[Iczelion's Win32 Assembly HomePage]
Překlad Shaldan 2006, fx.s@seznam.cz - při četbě a využívání nových poznatků z četby opravdu za nic neručím :))