   #Start Contents

                   Wyswietlanie obrazkow BMP pod Linuksem

   Jesli przejrzeliscie moj poprzedni kurs zwiazany z grafika, to umiecie
   juz cos samodzielnie narysowac.

   Ale przeciez w Internecie (i nie tylko) jest tyle ciekawych rysunkow,
   nie mowiac juz o tych, ktore moglibyscie stworzyc dla jakiegos
   specjalnego celu, na przyklad swojej wlasnej gry. Dlatego teraz
   pokaze, jak takie rysunki wyswietlac. Ze wzgledu na prostote formatu,
   wybralem pliki typu BMP (bitmapy). Plik, ktory wyswietlimy, powinien
   miec rozmiar 320x200 pikseli w 256 kolorach (mozna oczywiscie wziac
   dowolna inna rozdzielczosc, ale trzeba wtedy dobrac tryb graficzny).
   Wszystkie operacje na plikach zostaly juz przez mnie szczegolowo
   opisane w jednej z czesci mojego kursu, wiec tutaj nie bedziemy
   poswiecac im zbyt wiele uwagi.
   Ale przejdzmy wreszcie do interesujacych nas szczegolow.

   Powinniscie zaopatrzyc sie w cokolwiek, co opisuje format BMP.
   Informacje, z ktorych bede tutaj korzystal, znalazlem w Internecie
   (niestety, nie pamietam juz gdzie, ale mozecie poszukac na
   Wotsit.org).
   A oto naglowek pliku BMP (skladnia jezyka Pascal niestety, informacja:
   "Piotr Sokolowski, 6 maja 1998r"):
   (przeskocz opis naglowka)
        Type
         TBitMapHeader =
          Record
                bfType :             Word; (dwa bajty)
                bfSize :             LongInt; (cztery bajty)
                bfReserved :         LongInt;
                bfOffBits :          LongInt;
                biSize :             LongInt;
                biWidth :            LongInt;
                biHeight :           LongInt;
                biPlanes :           Word;
                biBitCount :         Word;
                biCompression :      LongInt;
                biSizeImage :        LongInt;
                biXPelsPerMeter :    LongInt;
                biYPelsPerMeter :    LongInt;
                biClrUsed :          LongInt;
                biClrImportant :     LongInt;
          End;

   Gdzie:
     * bftype - jest to dwubajtowa sygnatura "BM"
     * bfsize - czterobajtowy rozmiar pliku
     * bfreserved - pole zarezerwowane (0)
     * bfoffbits - przesuniecie (adres) poczatku danych graficznych
     * bisize - podaje rozmiar naglowka
     * biwidth - wysokosc bitmapy w pikselach
     * biheight - szerokosc bitmapy w pikselach
     * biplanes - liczba planow (prawie zawsze ma wartosc 1)
     * bibitcound - liczba bitow na piksel. Przyjmuje wartosc 1,4,8 lub
       24.
     * bicompression - sposob kompresji
     * bisizeimag - rozmiar obrazka w bajtach. W przypadku bitmapy
       nieskompresowanej rowne 0.
     * biXpelspermeter, biYpelspermeter - liczba pikseli na metr
     * biclrused - liczba kolorow istniejacej palety, a uzywanych wlasnie
       przez bitmape
     * biclrimporant - okresla, ktory kolor bitmapy jest najwazniejszy,
       gdy rowny 0 to wszystkie sa tak samo istotne.

   Ale spokojnie - nie musicie znac tych wszystkich pol, bo my nie
   bedziemy wszystkich uzywac. Scisle mowiac, nie bedziemy uzywac ani
   jednego z tych pol!
   No to po co to wszystko?
   Po to, aby znac dlugosc naglowka pliku (54 bajty), ktory ominiemy przy
   analizie pliku.

   Po naglowku idzie paleta 256 kolorow * 4 bajty/kolor = kolejny 1kB.
   Jesli macie jakies watpliwosci co do tego jednego kilobajta, to
   slusznie. Oczywiscie, do opisu koloru wystarcza 3 bajty (odpowiadajace
   kolorom czerwonemu, zielonemu i niebieskiemu - RGB), co daje razem 768
   bajtow. Co czwarty bajt nie zawiera zadnej istotnej informacji i
   bedziemy go pomijac (zmienna "z" w programie).

   Zaraz po palecie jest obraz, piksel po pikselu. Niestety, nie jest to
   tak logiczne ustawienie, jak bysmy sobie tego zyczyli. Otoz, pierwsze
   320 bajtow to ostatni wiersz obrazka, drugie 320 - przedostatni, itd.
   Dlatego trzeba bedzie troszke pokombinowac.

   W tym artykule tez wykorzystam mozliwosci biblioteki SVGAlib, ze
   wzgledu na prostote jej opanowania. Aby moc z niej korzystac, musicie
   zainstalowac pakiety svgalib oraz svgalib-devel lub po prostu samemu
   skompilowac biblioteke, jesli pakiety nie sa dostepne.

   Do dzialania programow pod X-ami potrzebne moga byc uprawnienia do
   pliku /dev/console a pod konsola tekstowa - do pliku /dev/mem.

   Zwroccie uwage na sposob kompilacji ponizszego programu. Korzystamy z
   bibliotek dostepnych dla programistow jezyka C, wiec do laczenia
   programu w calosc najlepiej uzyc GCC - zajmie sie on dolaczeniem
   wszystkich niezbednych bibliotek. A skoro uzywamy gcc, to funkcja
   glowna zamiast _start, musi sie nazywac main - tak samo jak funkcja
   glowna w programach napisanych w C. I tak samo, zamiast funkcji
   wychodzenia z programu, mozemy uzyc komendy RET, aby zamknac program.

   Ale dobierzmy sie wreszcie do kodu (skladnia FASM):
   (przeskocz program)
; Program wyswietlajacy obrazek BMP pod Linuksem z wykorzystaniem SVGAlib
;
; Autor: Bogdan D., bogdandr (at) op.pl
;
; kompilacja:
;   fasm bmp.fasm
;   gcc -o bmp bmp.o -lvga

format ELF
section ".text" executable

public  main

G320x200x256    = 5
TEXT            = 0

extrn   vga_setmode
extrn   vga_drawscansegment
extrn   vga_setpalvec


main:
        mov     eax, 5          ; otwieranie pliku
        mov     ebx, plik       ; adres nazwy
        mov     ecx, 0          ; odczyt
        mov     edx, 400o       ; - R-- --- ---
        int     80h

        cmp     eax, 0          ; czy blad?
        jng     koniec

        mov     ebx, eax        ; EBX = deskryptor

        mov     eax, 19         ; zmiana pozycji w pliku
        mov     ecx, 54         ; idziemy tyle bajtow...
        mov     edx, 0          ; ...od poczatku pliku
        int     80h

        cmp     eax, 0
        jge     seek_ok

problem:                        ; tu trafiamy po bledzie obslugi pliku
        push    eax
        mov     eax, 6
        int     80h             ; zamykamy plik
        pop     eax

        jmp     koniec

seek_ok:
        xor     esi, esi        ; indeks do tablicy "paleta"

czytaj_pal:
        mov     eax, 3          ; czytaj z pliku
        mov     ecx, b          ; pod ten adres
        mov     edx, 4          ; 4 bajty (zmienne b, g, r i z)
        int     80h

        cmp     eax, 0          ; czy blad?
        jl      problem

                                ; ustawiamy palete:
        mov     al, [r]
        shr     al, 2           ; dzielimy przez 4, ograniczajac liczby do
                                ; przedzialu 0-63
        and     eax, 3fh        ; zerujemy pozostale bity
        mov     [paleta+esi], eax     ; paleta[esi] = [r] / 4

        mov     al, [g]
        shr     al, 2
        and     eax, 3fh
        mov     [paleta+esi+1*4], eax ; paleta[esi] = [g] / 4

        mov     al, [b]
        shr     al, 2
        and     eax, 3fh
        mov     [paleta+esi+2*4], eax ; paleta[esi] = [b] / 4

        add     esi, 3*4        ; przejdz o 3 miejsca dalej -
                                ; na kolejne wartosci RGB
                                ; kazde miejsce zajmuje 4 bajty

        cmp     esi, 256*3*4    ; sprawdz, czy nie zapisalismy
                                ; juz wszystkich kolorow
        jb      czytaj_pal

wyslij_palete:
        push    dword G320x200x256
        call    vga_setmode     ; ustawiamy tryb graficzny:
                                ; 320x200 w 256 kolorach
        add     esp, 1*4        ; zdejmujemy argument ze stosu

        push    dword paleta
        push    dword 256
        push    dword 0
        call    vga_setpalvec   ; ustawiamy palete barw
        add     esp, 3*4        ; zdejmujemy argumenty ze stosu

        mov     edi, 200        ; tyle linii wyswietlimy
obrazek:
        push    edi             ; zachowaj EDI
        mov     eax, 3          ; czytaj z pliku
        mov     ecx, kolor      ; do tej zmiennej
        mov     edx, 320        ; 320 bajtow (jedna linie obrazu)
        int     80h

        cmp     eax, 0
        jge     .dalej

                                ; w razie bledu wylaczamy tryb graficzny
        push    dword TEXT
        call    vga_setmode     ; ustawiamy tryb tekstowy 80x25
        add     esp, 1*4

        jmp     problem
.dalej:

        push    dword 320       ; tyle bajtow na raz wyswietlic
        dec     edi             ; teraz EDI = numer linii na ekranie (0-199)
        push    edi             ; numer linii, na ktorej wyswietlic dane
        push    dword 0         ; numer kolumny
        push    dword kolor     ; dane do wyswietlenia
        call    vga_drawscansegment
        add     esp, 4*4

        pop     edi             ; przywroc EDI

        dec     edi             ; zmniejsz numer wyswietlanej linii
        jnz     obrazek         ; jesli EDI rozne od zera, rob kolejne linie

        mov     eax, 6
        int     80h             ; zamknij plik

        mov     eax, 3
        xor     ebx, ebx
        mov     ecx, z
        mov     edx, 1
        int     80h             ; czekamy na klawisz

        push    dword TEXT
        call    vga_setmode     ; ustawiamy tryb tekstowy 80x25
        add     esp, 1*4

        xor     eax, eax        ; kod bledu=0 (brak bledu)
koniec:
        mov     ebx, eax
        mov     eax, 1
        int     80h             ; wyjscie z programu


section ".data" writeable

plik            db      "test.bmp", 0
paleta:         times 768*4 db 0
b               db 0
g               db 0
r               db 0
z               db 0
kolor:          times 320 db 0

   Mam nadzieje, ze kod jest dosc jasny. Nawet jesli znacie asemblera
   tylko w takim stopniu, w jakim to jest mozliwe po przeczytaniu mojego
   kursu, zrozumienie tego programu nie powinno sprawic Wam klopotow.

   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
