Rozdział XXV - Prosta grafika rastrowa


Na tej lekcji nauczymy się stosować grafikę rastrową (bitmap) w naszym programie. Konkretnie nauczymy się wyświetlać grafikę rastrową na obszarze roboczym naszego okna. Załaduj {ten przykład}.

 

 

Teoria

Grafikę rastrową można uważać za obraz zapisany w komputerze. Istnieje wiele różnych formatów graficznych stosowanych z komputerami, lecz system Windows wewnętrznie obsługuje jedynie pliki grafiki rastrowej (Windows Bitmnap Graphics files) posiadające rozszerzenie nazwy .BMP. Najprostszym sposobem użycia grafiki rastrowej jest umieszczenie jej w zasobach. Można to zrobić na dwa sposoby. Możesz dołączyć grafikę rastrową do pliku definicji zasobów (.RC) w sposób następujący:

 

#define IDB_MYBITMAP 100
IDB_MYBITMAP BITMAP "c:\project\example.bmp"

 

Metoda ta wykorzystuje stałą do reprezentowania grafiki rastrowej. Pierwszy wiersz po prostu tworzy stałą o nazwie IDB_MYBITMAP, Która ma wartość 100. Będziemy używać tej etykiety do odwoływania się do grafiki rastrowej w programie. Następny wiersz deklaruje zasób grafiki rastrowej. Informuje on kompilator zasobów, gdzie należy znaleźć właściwy plik .BMP.

W drugiej metodzie wykorzystujemy nazwę do reprezentowania grafiki rastrowej w sposób następujący:

 

MyBitMap BITMAP "c:\project\example.bmp"

 

Metoda ta wymaga odwołania się do grafiki rastrowej przy pomocy łańcucha znaków "MyBitMap" zamiast wartości.

Każda z tych metod jest dobra, chyba że się pogubisz.

Teraz gdy już wstawiliśmy grafikę rastrową do pliku z zasobami. możemy przejść dalej do kroków niezbędnych przy umieszczaniu jej na obszarze roboczym okna.

  1. Wywołaj LoadBitmap, aby otrzymać uchwyt grafiki rastrowej. Funkcja ta posiada następującą definicję:
LoadBitmap PROTO hInstance:HINSTANCE, lpBitmapName:LPSTR 
 

Funkcja zwraca uchwyt grafiki rastrowej. hInstance jest uchwytem egzemplarza naszego programu. lpBitmapName to wskazanie do łańcucha znaków określającego nazwę grafiki (w przypadku zastosowania drugiej metody odwoływania się do grafiki rastrowej). Jeśli do określania grafiki stosujesz stałą (np. IDB_MYBITMAP), możesz wstawić tutaj jej wartość (w przykładzie powyżej byłoby to 100). A oto krótkie przykłady kolejnych dwóch metod:

 

Metoda Pierwsza:

 

.386
.MODEL FLAT, STDCALL
...
.CONST
IDB_MYBITMAP EQU 100
...
.DATA?
hInstance DD ?
...
.CODE
    ...
    INVOKE GetModuleHandle, NULL    mov    hInstance, eax     INVOKE LoadBitmap, hInstance, IDB_MYBITMAP

 

Metoda Druga

 

.386
.MODEL FLAT, STDCALL
...
.DATA 
BitmapName DB "MyBitMap", 0
...
.DATA?
hInstance DD ?
...
.CODE
    ...
    INVOKE GetModuleHandle, NULL 
    mov    hInstance, eax 
    ...
    INVOKE LoadBitmap, hInstance, ADDR BitmapName
    ...
  1. Pobierz uchwyt do kontekstu urządzenia (DC - Device Context). Uchwyt ten możesz otrzymać przez wywołanie BeginPaint w odpowiedzi na wiadomość WM_PAINT lub wywołując gdziekolwiek funkcję GetDC.
  2. Utwórz kontekst urządzenia w pamięci (memory device context), które ma te same atrybuty, co właśnie pobrany kontekst. Celem jest tutaj utworzenie ukrytej powierzchni rysunkowej, na której możemy rysować grafikę rastrową. Gdy skończymy tę operację, po prostu kopiujemy zawartość ukrytej powierzchni rysunkowej do właściwego kontekstu urządzenia za pomocą jednego wywołania funkcji. Jest to przykład techniki podwójnego buforowania, stosowanej do szybkiego wyświetlania obrazków na ekranie. Tę ukrytą powierzchnię rysunkową możesz utworzyć przy pomocy wywołania CreateCompatibleDC o następującym prototypie:
CreateCompatibleDC PROTO hdc:HDC

Jeśli funkcja się powiedzie, to zwróci w rejestrze eax uchwyt do kontekstu urządzenia w pamięci. Parametr hdc jest uchwytem kontekstu urządzenia, dla którego chcemy utworzyć kompatybilny kontekst DC w pamięci.

  1. Gdy mamy już ukrytą powierzchnię rysunkową, można wprowadzić do niej naszą grafikę rastrową. Dokonujemy tego wywołując SelectObject z uchwytem DC w pamięci jako pierwszy parametr i uchwytem grafiki rastrowej jako drugi parametr. Funkcja ta posiada następujący prototyp:
SelectObject PROTO hdc:HDC, hGdiObject:DWORD
  1. Teraz grafika rastrowa zostanie narysowana na DC w pamięci. Musimy jedynie skopiować ją do właściwego urządzenia wyświetlającego, mianowicie do prawdziwego kontekstu urządzenia. Istnieje kilka funkcji, które mogą wykonać tę operację, takich jak BitBlt i StretchBlt. Funkcja BitBlt po prostu kopiuje zawartość jednego DC do innego, zatem jest szybka, natomiast StretchBlt może rozciągać lub ścieśniać grafikę rastrową, aby dopasować ją do obszaru wyjściowego. Tutaj dla prostoty wykorzystamy funkcję BitBlt o następującej składni:
BitBlt PROTO hdcDest: DWORD,\
             nxDest:  DWORD,\
             nyDest:  DWORD,\
             nWidth:  DWORD,\
             nHeight: DWORD,\
             hdcSrc:  DWORD,\
             nxSrc:   DWORD,\
             nySrc:   DWORD,\
             dwROP:   DWORD
  1. Gdy grafika przestanie ci być potrzebna, usuń ją za pomocą wywołania funkcji API o nazwie DeleteObject.

To wszystko. Podsumujmy: musisz umieścić grafikę rastrową w skrypcie zasobów. Następnie ładujesz ją z zasobów przy pomocy funkcji LoadBitmap. Dostaniesz uchwyt grafiki. Dalej pobierasz uchwyt kontekstu urządzenia obszaru, na którym chcesz umieścić tę grafikę rastrową. Tworzysz kontekst urządzenia w pamięci, który jest kompatybilny z kontekstem urządzenia właśnie otrzymanym. Umieszczasz grafikę w DC w pamięci, a później kopiujesz zawartość DC w pamięci do rzeczywistego DC.

Przykład

Plik SIMPLEBITMAP.ASM

 

.386

.MODEL FLAT, STDCALL

OPTION CASEMAP:NONE

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

WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD

.CONST

IDB_MAIN EQU 1

.DATA

ClassName DB "SimpleWin32ASMBitmapClass", 0
AppName   DB "Grafika rastrowa w Win32ASM", 0

.DATA?

hInstance   HINSTANCE ?
CommandLine LPSTR     ?
hBitmap     DD        ?

.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   hInstance
    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, 295, 310, 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 ps:     PAINTSTRUCT
LOCAL hdc:    HDC
LOCAL hMemDC: HDC
LOCAL rect:   RECT

    .IF uMsg==WM_CREATE
        INVOKE LoadBitmap, hInstance, IDB_MAIN
        mov    hBitmap, eax
    .ELSEIF uMsg==WM_PAINT
        INVOKE BeginPaint, hWnd, ADDR ps
        mov    hdc, eax
        INVOKE CreateCompatibleDC, hdc
        mov    hMemDC, eax
        INVOKE SelectObject, hMemDC, hBitmap
        INVOKE GetClientRect, hWnd, ADDR rect
        INVOKE BitBlt, hdc, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY
        INVOKE DeleteDC, hMemDC
        INVOKE EndPaint, hWnd, ADDR ps
    .ELSEIF uMsg==WM_DESTROY
        INVOKE DeleteObject, hBitmap
        INVOKE PostQuitMessage, NULL
    .ELSE
        INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .ENDIF
    xor eax, eax
    ret

WndProc ENDP

END start

 

Plik zasobów SIMPLEBITMAP.RC

 

#define IDB_MAIN 1
IDB_MAIN BITMAP "TWEETY78.bmp"

 

Analiza

Na tej lekcji nie pozostaje nam zbyt wiele do analizowania ;)

 

#define IDB_MAIN 1
IDB_MAIN BITMAP "TWEETY78.bmp"

 

Zdefiniuj stałą o nazwie IDB_MAIN, przypisz jej jako wartość liczbę 1. A następnie wykorzystaj tę stałą w charakterze identyfikatora zasobu grafiki rastrowej. Plik z grafiką rastrową, który należy dołączyć do zasobów, posiada nazwę "TWEETY78.bmp" i znajduje się w tym samym katalogu, co skrypt zasobów.

 

    .IF uMsg==WM_CREATE
        INVOKE LoadBitmap, hInstance, IDB_MAIN
        mov    hBitmap, eax

 

W odpowiedzi na wiadomość WM_CREATE, wywołujemy LoadBitmap do załadowania grafiki rastrowej z zasobów przekazując w drugim parametrze jej identyfikator. Dostajemy przy powrocie funkcji uchwyt do grafiki.

Po załadowaniu grafiki rastrowej możemy ją namalować w obszarze roboczym naszego okna głównego.

 

    .ELSEIF uMsg==WM_PAINT
        INVOKE BeginPaint, hWnd, ADDR ps
        mov    hdc, eax
        INVOKE CreateCompatibleDC, hdc
        mov    hMemDC, eax
        INVOKE SelectObject, hMemDC, hBitmap
        INVOKE GetClientRect, hWnd, ADDR rect       
        INVOKE BitBlt, hdc, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY
        INVOKE DeleteDC, hMemDC
        INVOKE EndPaint, hWnd, ADDR ps

 

Malowanie grafiki przeprowadzamy w odpowiedzi na wiadomość WM_PAINT. Najpierw wywołujemy BeginPaint aby otrzymać uchwyt do kontekstu urządzenia. Następnie tworzymy kompatybilny kontekst urządzenia w pamięci za pomocą CreateCompatibleDC. Dalej wprowadzamy grafikę do DC w pamięci przy pomocy SelectObject. Określamy wymiary obszaru roboczego za pomocą GetClientRect. Teraz jesteśmy gotowi do wyświetlenia grafiki w obszarze roboczym wywołując funkcję BitBlt, która skopiuje grafikę z pamięci do właściwego kontekstu urządzenia. Po zakończeniu malowania nie potrzebujemy DC w pamięci, zatem usuwamy go przy pomocy DeleteDC. Kończymy sesję malowania wywołaniem EndPaint.

 

    .ELSEIF uMsg==WM_DESTROY
        INVOKE DeleteObject, hBitmap
        INVOKE PostQuitMessage, NULL

 

Gdy grafika rastrowa przestanie nam być już potrzebna, usuwamy ją wywołując DeleteObject

 

Autorem kursu jest Iczelion. Kurs programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną zgodą autora.
Tłumaczenie z języka angielskiego, opracowanie HTML i konwersję przykładów programów wykonał mgr Jerzy Wałaszek.



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2017 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.