2004 07 Konsolowa przeglądarka plików graficznych [Programowanie]


dla programistów
Konsolowa
przeglądarka plików
graficznych
Marek Sawerwain
Linuksie, gdy chcemy zakładamy, że zawsze będzie on korzy-
pracować w trybie gra- stał z trybu o 32-bitowym kolorze. Posłu-
ficznym, zazwyczaj sto- giwanie się tymi funkcjami jest bardzo
Wsujemy system X Win- proste.
dow. Jądro Linuksa pozwala nam jednak Uzyskanie dostępu do bufora to zada-
w bardzo łatwy sposób uruchomić stan- nie funkcji open_fb, więc jedną z pierw-
dardową konsolę tekstową w dowol- szych czynności, którą trzeba wykonać,
nie wybranym przez nas trybie graficz- to wywołanie tej funkcji:
nym. Bardzo często nazywa się to trybem
bufora ramki z tego powodu, że mamy int h;
bezpośredni dostęp do pamięci karty h=open_fb();
graficznej. Mówiąc wprost, korzysta-
my z trybu graficznego, ale bez pośred- W zmiennej h znajdzie się uchwyt repre-
nictwa systemu X Window. W obecnej zentujący bufor ramki. Dzięki wartości,
chwili najpopularniejszym zastosowa- którą on zawiera, możemy łatwo spraw-
niem graficznej konsoli jest wyświetlanie dzić, czy udało się nam uzyskać dostęp
graficznego logo podczas startu systemu, do karty graficznej. Obsługa sytuacji,
ale niektórzy z czytelników z pewno- gdy nie udało się  otworzyć bufora
ścią stosowali przeglądarkę Links właśnie ramki, sprowadza się do jednej instruk-
w trybie graficznym konsoli (wystarczy cji if:
dodać podczas wywołania parametr  -g
 links -g). Programów korzystających if(!h) {
S
z takiego trybu graficznego jest naturalnie printf("Błąd otwarcia bufora
CD/DVD
więcej. Istnieją również gotowe biblioteki ramki!!!\n");
Po uruchomieniu dystrybucji
widgetów, jak choćby QT czy GTK+, które return -1;
Linux+ Live CD/DVD i przełą-
potrafią wykorzystać bufor ramki. }
czeniu się na konsolę tekstową,
W tym artykule chciałbym pokazać,
będzie można przetestować dzia-
jak napisać prosty, ale przydatny pro- Listing 1. Fragment programu  Hello
łanie omawianego programu.
gram, pracujący na poziomie graficznej World!
konsoli. Będzie to przeglądarka obraz-
Na płycie CD/DVD
ków w wielu formatach, takich jak png, for(i=0;i<9999999;i++){
Na płycie CD/DVD znajdują się
jpeg, tiff itd. Wbrew pozorom, programo- x=rand() % vinfo.xres;
pliki zródłowe i binarne bibliotek,
wanie na poziomie bufora ramki nie jest y=rand() % vinfo.yres;
kompletny kod omawianego pro-
trudne. Biblioteka podstawowych funkcji r=rand() % 255;
gramu oraz listingi z artykułu.
jest niewielka  to tylko 5 kB kodu zró- g=rand() % 255;
O autorze
dłowego. b=rand() % 255;
Autor zajmuje się tworzeniem
setpixel32(x,y, r,b,g, 0);
oprogramowania dla WIN32 i
Małe co nieco if( (i % 1000000)==0)
Linuksa. Zainteresowania: teoria
S
na rozgrzewkę draw_msg(vinfo.xres + 50,
języków programowania oraz
dobra literatura. Kontakt z auto- Do podstawowej obsługi bufora ramki (vinfo.yres / 2)-25 );
rem: autorzy@linux.com.pl.
wystarczy osiem funkcji. Ich spis znajdu- }
je się w Tabeli 1. W naszym programie
62
lipiec 2004
przeglądarka plików dla programistów
Tabela 1. Spis funkcji omawianych w artykule
Listing 2. Funkcja rysująca komunikat
Obsługa bufora ramki (fb_lib.c)
Nazwa funk- Krótki opis void draw_msg(int xx, int yy){
cji int x,y,i=0;
line,size_line=sizeof(msg)/5;
open_fb otwarcie bufora ramki
x=xx;
get_basic_ pobranie podstawowych informacji
y=yy;
info
for(i=1;i<=sizeof(msg);i++){
map_fb utworzenie odwzorowania pamięci graficznej
if(msg[i-1]==1)
unmap_fb usunięcie odwzorowania pamięci graficznej
whiteblock(x,y,block_size);
setpixel32 postawienie piksela pod podanymi współrzędnymi
x=x+block_size;
whiteblock narysowanie białego kwadratu pod wskazanymi współrzędnymi
if((i % size_line)==0)
{ x=xx; y=y+block_size; }
clear32 kasowanie ekranu
}
close_fb zamknięcie bufora ramki
}
get_buffer_ odczytanie aktualnego wskaznika na obszar pamięci graficznej
ptr
wyświetlenia. Fragment tej tablicy znaj-
Obsługa plików graficznych
duje się poniżej:
ilInit inicjalizacja podstawowych funkcji biblioteki DevIL
iluInit inicjalizacja dodatkowych funkcji biblioteki DevIL
char msg[]={1001...
ilEnable włączanie dodatkowych funkcji 1001...
1111...
ilOriginFunc ustalenie początku obrazu
1001...
iluImagePa- ustalanie dodatkowych parametrów
1001...
rameter np. filtru używanego podczas skalowania
};
ilGenImages generowanie identyfikatorów
ilBindImage ustalanie bieżącego identyfikatora
Jedynka oznacza, że ma zostać wyświe-
ilDeleteIma- usuwanie identyfikatora
tlony biały kwadrat, a zero  odwrotnie,
ges
ilGetInteger pobieranie parametrów o obrazie
Instalacja biblioteki
ilConvertPal konwersja palety
DevIL
iluScale skalowanie obrazu
Instalacja tej biblioteki w systemie jest
ilGetData pobieranie wskaznika na dane obrazu
zadaniem trywialnym, a to ze względu na
ilGetPalette pobieranie wskaznika na paletę kolorów obecność skryptu configure. Wobec tego
wydajemy tylko trzy polecenia:
Jeśli nie mieliśmy kłopotów, to możemy wyświetla losowo na ekranie punkty S
./configure  prefix=/katalog/do/
korzystać z funkcji setpixel32. Postawie- o dowolnych kolorach oraz co pewien
instalacji
nie białego piksela pod współrzędnymi czas wyświetla na ekranie komunikat tek-
make
100,100 wygląda tak: stowy, ale zbudowany z białych kwadra-
make install
tów (to dlatego w naszej  mikro bibliote-
Jeśli zastosowana została ostatnio naj-
setpixel32(100, 100, 255, 255, 255, 0); ce znajduje się funkcja whiteblock).
nowsza wersja 1.6.6, to podczas kompi-
Listing 1 przedstawia najważniej-
lacji naszego programu picview, a dokład-
Po zakończeniu pracy z trybem graficz- szą pętlę w naszym pierwszym progra-
niej w momencie łączenia, możemy otrzy-
nym możemy go zamknąć. Czynność tę mie. Pętla ta rysuje dziesięć milionów
mać błąd braku funkcji _vsnprintf. Roz-
wykonujemy funkcją close_fb podając pikseli, ale po każdym milionie punk-
wiązanie tego problemu wymaga niewiel-
za argument zmienną uchwytu. tów rysowany jest także napis  Hello
kiej modyfikacji kodu biblioteki DevIL,
W naszej docelowej aplikacji nie World! . To zadanie wykonuje funkcja
a dokładnej pliku in_tiff.c. Odszukujemy
potrzebujemy dodatkowych funkcji  draw_msg.
w nim wystąpienia funkcji _vsnprintf
wystarczy nam tylko stawianie punktów,
i zamieniamy je na poprawną nazwę bez
ale mamy jeszcze funkcję clear32, kasu- Jak działa draw_msg? podkreślenia, czyli vsnprintf. Po ponow-
jącą zawartość ekranu, oraz whiteblock, Następnym istotnym fragmentem nasze- nej kompilacji całej biblioteki wystarczy
zainstalować bibliotekę raz jeszcze i pro-
która  wie , jak narysować biały kwadrat go pierwszego programu jest funkcja
blem zniknie. Poprzednie wersje DevIL
o podanej długości boku. draw_msg. Jej pełny kod zawiera Listing
nie posiadają tego błędu, za to nie obsłu-
Uzbrojeni w taką wiedzę możemy 2. Jak widać, funkcja jest dość krótka
gują formatu GIF. Z tego powodu, jeśli
napisać niewielki program wyświetlają- i nie zawiera żadnego ciągu znaków,
chcemy, aby nasza przeglądarka wczy-
cy  Hello World! dla trybu bufora ramki. który stanowiłby treść naszego komuni-
tywała ten rodzaj pliku, musimy korzystać
Pełny kod zródłowy zawiera plik hw.c. katu. Korzystamy tylko z tablicy msg  to
z wersji 1.6.6 DevIL.
Program działa w następujący sposób: w niej został zakodowany tekst do
63
www.lpmagazine.org
dla programistów
Jak uruchomić obsługę
framebuffera w jądrze
systemu?
Gdy do jądra została dodana obsługa
trybu VESA, to wystarczy podczas startu
systemu podać numer trybu graficznego,
jaki chcemy uruchomić. Z poziomu lilo
piszemy np. linux vga=789. System uru-
chomi się w trybie 800x600-32bit. Tabela
2 zawiera spis najważniejszych trybów
i ich oznaczeń.
Gdyby okazało się, że jądro które-
go używamy, nie posiada obsługi stan-
dardu VESA, to możemy jeszcze spró-
bować załadować moduł sterownika dla
karty graficznej. Trzeba jednak pamię-
tać, że obsługiwanych jest zaledwie kilka
typów kart graficznych. Dla popularnych
kart NVIDIA wystarczy wydać polece-
nie modprobe rivafb i jeśli mamy star-
szy typ karty tego producenta, to powinni-
śmy już cieszyć się trybem bufora ramki.
Rysunek 1. Konfiguracja obsługi bufora ramki VESA dla jądra 2.4.x
Szczęśliwi posiadacze najnowszych kart,
np. GeForceFX 5900, obejdą się sma-
kiem, ponieważ moduł rivafb nie rozpo- W pętli odczytujemy kolejne znaki z tabli- przybliżyć sposób implementacji funkcji
znaje tego typu kart. Z tego powodu naj- cy msg, więc aby poznać, czy już prze- związanych z obsługą trybu graficznego.
lepiej korzystać z trybów VESA.
kroczyliśmy linię, musimy znać liczbę W Linuksie, a ogólnie w systemach unik-
W przypadku braku obsługi trybów
znaków w linii. W tablicy msg sam nie sowych, urządzenia są reprezentowane
VESA, czeka nas kompilacja jądra.
wiem, ile wpisałem znaków, ale każdy jako pliki. Nie inaczej jest w przypadku
W pierwszej kolejności należy odzna-
przeglądając kod zródłowy łatwo spraw- bufora ramki. Zanim zaczniemy z niego
czyć opcję Prompt for development
dzi, że mamy wpisane pięć linii, toteż obli- korzystać, należy otworzyć plik, który go
and/or incomplete code/drivers w sekcji
czenie, ile znaków jest w linii jest bardzo reprezentuje:
Code maturity level options. Następnie,
proste: size_line=sizeof(msg)/5. Gdy
w zależności od typu jądra, trzeba przejść
mamy tę informację, narysowanie zawar- fb_handle=open("/dev/fb0", O_RDWR);
do Console Drivers/Frame-buffer-options
tości tablicy msg jest już bardzo proste.
dla jądra w wersji 2.4.x bądz Device
Odczytując zawartość msg sprawdza- Następnie możemy uzyskać podsta-
drivers/Graphics support w przypadku
nowych jąder 2.6.x (należy zaznaczyć my, czy aktualny i-ty element tablicy to wowe informacje (np. rozdzielczość,
obsługę bufora ramki dla trybu VESA, ale
jedynka, a jeśli tak, to wyświetlamy biały sposób kodowania kolorów itd.). Tego
nie w trybie modułu, bowiem nie będzie-
kwadrat. W każdej iteracji pętli zwiększa- typu dane zawierają struktury o typie:
my mogli podać numeru trybu podczas
my wartość współrzędnej x o wielkość fb_var_screeninfo vinfo oraz fb_fix_
uruchamiania systemu). W artykule znaj-
bloku: x=x+block_size;. Oczywiście, screeninfo.
dują się dwa rysunki pokazujące, jakie
w przypadku, gdy osiągniemy koniec Aby wypełnić pierwszą strukturę
opcje trzeba zaznaczyć w przypadku
linii komunikatu, wykonujemy dwie potrzebnymi informacjami, stosujemy
tych dwóch serii jądra Linuksa.
czynności: powracamy na początek miej- funkcję ioctl w następującej postaci:
sca, od którego zaczęliśmy rysować nasz
S
czyli nic nie zostanie wyświetlone. Trzeba komunikat (x=xx;), oraz przechodzimy ioctl(fb_handle, FBIOGET_VSCREENINFO,
pamiętać o jednym fakcie: cały komunikat, linię niżej (y=y+block_size;). &vinfo)
choć został w kodzie zródłowym odpo-
wiednio sformatowany, nadal jest tabli- Programowanie bufora Dość trudno będzie operować na bufo-
cą jednowymiarową. Podczas wyświetla- ramki rze ramki z poziomu pliku. Dlatego, aby
nia komunikatu po wyświetleniu pierw- Zanim na dobre rozpoczniemy pisać normalnie operować pamięcią graficz-
szej linii musimy przejść linię niżej. naszą przeglądarkę, chciałbym jeszcze ną, musimy ją jeszcze odwzorować za
pomocą zwykłego wskaznika. W naszym
Tabela 2. Spis najważniejszych trybów VESA
przypadku będzie to typ char. Pomoże to
Bitów na kolor 640x480 800x600 1024x768 1280x1024 nam w implementacji funkcji setpixel32.
Odwzorowanie pliku w pamięci wyko-
8 769 771 773 775
nujemy za pomocą funkcji mmap:
15 784 787 790 793
16 785 788 791 794
S
char *fb_map=(char*)mmap(0,sc,PROT_READ
24/32 786 789 792 795
| PROT_WRITE, MAP_SHARED, fb_handle, 0);
64
lipiec 2004
przeglądarka plików dla programistów
blue_idx32 = vinfo.blue.offset / 8;
Listing 3. Funkcja setpixel32
transp_idx32 = vinfo.transp.offset / 8;
void setpixel32(int x, int y, int r, Stawiamy piksele
int g, int b, int a){ Podczas wyświetlania obrazu na ekra-
int pos; nie będziemy używać tylko jednej
S
pos = (x+vinfo.xoffset) * pomocniczej funkcji, a mianowicie set-
S
(vinfo.bits_per_pixel/8) + pixel32. Kod tej funkcji przedstawia
(y+vinfo.yoffset) * finfo.line_length; Listing 3. Jedynym bardziej skompli-
*(fb_map + pos + red_idx32 ) = r; kowanym elementem, jaki tam napo-
*(fb_map + pos + green_idx32 ) = g; tykamy, jest wyznaczenie miejsca
*(fb_map + pos + blue_idx32 ) = b; w pamięci na podstawie współrzęd-
*(fb_map + pos + transp_idx32 ) = a; nych docelowych x i y. Współrzędna y
} to numer linii, w jakiej chcemy umieścić
punkt. Wyznaczenie, o którą linię nam
W wywołaniu mmap znajduje się szereg
parametrów. W pierwszym można podać
adres początkowy, od którego pamięć
zostanie odwzorowana. W przykła-
dzie podaliśmy zero, więc system sam
wygeneruje odpowiedni adres. Następ-
nie podaje się wielkość obszaru  jest to
zmienna sc. Dalej określany tryb dostę-
pu  interesuje nas odczyt i zapis. Kolej-
ny parametr to tryb dzielenia pamięci.
Wartość MAP_SHARED oznacza, że pamięć
będzie dzielona z innymi procesami.
Następnym parametrem jest uchwyt
pliku, co oznacza, że za pomocą mmap
możemy odwzorować w pamięci także
inne zwykłe pliki. Ostatni parametr
to tzw. przesunięcie: zero oznacza, że
nie będziemy pomijać żadnych danych
z początku odwzorowywanego pliku.
Jak widać, czynności przygoto-
wawcze do korzystania z bufora ramki
nie są zbyt skomplikowane, ale trzeba
zwrócić uwagę na kolejność odwzo-
rowania kolorów w buforze ramki.
Nie zawsze kolory są ułożone w trójki
RGB bądz, tak jak w przypadku nasze-
go trybu 32-bitowego, w czwórkę RGBA
(A oznacza kanał alfa, nazywany kana-
łem przezroczystości). Bardzo często
spotyka się tryb BGRA. Rada na różne
ułożenia kolorów jest bardzo prosta.
Wspominana powyżej struktura vinfo
zawiera numer bitu, pod którym roz-
poczyna się odpowiednia składo-
wa koloru. Nam potrzebne są bajty.
Wystarczy wartości przesunięcia
podzielić przez osiem. Możemy tak
zrobić, ponieważ działamy w trybie 32-
bitowym, gdzie na każdy kolor przypa-
da osiem bitów:
red_idx32 = vinfo.red.offset / 8;
Rysunek 2. Schemat działania przeglądarki plików graficznych
green_idx32 = vinfo.green.offset / 8;
65
www.lpmagazine.org
dla programistów
podano argument (nazwę pliku) do
wyświetlenia:
picview obrazek.jpg
Następnie uzyskujemy dostęp do bufora
ramki, wczytujemy plik i, to bardzo
ważny moment, staramy się dokonać
takich konwersji za pomocą API biblio-
teki DevIL, aby format obrazu był iden-
tyczny z formatem trzydziestodwubi-
towego koloru, co ułatwi nam proces
wyświetlania obrazu. Dodatkowo, jeśli
rysunek, który wczytaliśmy, jest większy
niż dostępna rozdzielczość, dokonujemy
jego przeskalowania. Gdy obraz znajdzie
się na ekranie, oczekujemy na wciśnięcie
klawisza [Enter] (wystarczy zastosować
funkcję getchar).
Test, czy podano dodatkowy argu-
ment w momencie wywołania naszego
Rysunek 3. Konfiguracja obsługi bufora ramki VESA dla jądra 2.6.x
programu, sprowadza się tylko do spraw-
dzenia, czy parametr argc funkcji main
chodzi, wykonuje ten fragment kodu: numerów składowych kolorów, aby jest mniejszy bądz większy od dwóch. Po
(y+vinfo.yoffset) * finfo.line_length. poprawnie postawić punkt w potrzeb- tym teście dokonujemy inicjalizacji biblio-
W pierwszej części wyznaczamy odpo- nym kolorze. Przykładowo, dla zielo- teki DevIL:
wiednie przesunięcie w pamięci y+vin- nej składowej koloru przedstawia się to
fo.yoffset, a następnie mnożymy tę następująco: ilInit();
wielkość przez całkowitą długość linii iluInit();
w bajtach finfo.line_length. W ten *(fb_map + pos + green_idx32) = g;
sposób wiemy, w którym miejscu zaczy- Pierwsza linia odpowiada za podstawo-
na się linia, w której chcemy umieścić Zadanie główne wy zbiór instrukcji, a druga uruchamia
nasz punkt. Wystarczy do tej wielkości  przeglądarka plików dodatkowy zestaw funkcji, w którym
dodać wyrażenie (x+vinfo.xoffset) * graficznych znajduje się bardzo wygodna funkcja do
(vinfo.bits_per_pixel/8), aby popraw- Nasz program ma obsługiwać wiele róż- przeskalowania obrazu. Nie martwimy
nie odszukać odpowiedni adres w nych formatów graficznych, a jak już się o deinicjalizację, ponieważ obydwie
pamięci bufora ramki. Jak widać, w tej wspomniałem, sam kod zródłowy jest funkcje podłączają się do łańcucha funk-
części obliczeń ponownie wyznaczamy niewielki. Oznacza to, że korzysta- cji wyjściowych za pomocą atexit.
wartość przesunięcia x+vinfo.xoffset, my z dodatkowej biblioteki do obsłu- Następnym elementem jest ustale-
ale mnożymy to przez liczbę bajtów gi plików graficznych o nazwie DevIL. nie początku obrazu tak, aby znajdował
vinfo.bits_per_pixel/8 (dzielimy całko- Zanim przystąpimy do tworzenia kodu, się on w lewym górnym rogu. Musimy
witą liczbę bitów na jeden piksel warto zastanowić się, jakie czynno- tak postąpić, ponieważ niektóre forma-
przez osiem) jaka przypada na jeden ści będzie wykonywać nasz program. ty, np. bmp i tga, zapamiętują obraz  do
piksel. Ogólny schemat działania przeglądar- góry nogami . Z tego powodu za pomocą
Pozostaje nam tylko wykorzy- ki prezentuje Rysunek 2. Na samym ilEnable i parametru IL_ORIGIN_SET oraz
stać wcześniej wyznaczoną wartość początku programu sprawdzamy, czy funkcji ilOriginFunc nakazujemy biblio-
tece DevIL, aby obraz miał swój początek
w lewym górnym rogu (co jest zgodne
Listing 4. Konwersja formatu palety kolorów
z działaniem funkcji setpixel32; gdy
if(ilGetInteger(IL_PALETTE_TYPE)==IL_PAL_BGR24){ podamy współrzędne 0,0, to postawimy
ilConvertPal(IL_PAL_RGB24); punkt w lewym górnym rogu). Ważną
} operacją jest także ustalenie typu filtro-
if(ilGetInteger(IL_PALETTE_TYPE)==IL_PAL_BGRA32 || wania obrazu podczas zmiany jego roz-
ilGetInteger(IL_PALETTE_TYPE)==IL_PAL_RGB32 || miarów. Filtrem, który daje obraz wyso-
ilGetInteger(IL_PALETTE_TYPE)==IL_PAL_BGR32 || kiej jakości, jest LANCZOS. Z tego
ilGetInteger(IL_PALETTE_TYPE)==IL_PAL_BGRA32){ powodu w naszej aplikacji stosujemy ten
ilConvertPal(IL_PAL_RGBA32); typ filtru. Pamiętajmy jednak, że choć
} daje on bardzo dobry i ostry obraz, to
działa dość wolno, więc przy skalowaniu
66
lipiec 2004
przeglądarka plików dla programistów
Listing 5. Przenoszenie danych obrazu do bufora ramki RGB24. W drugim przypadku, jeśli mamy
do czynienia z kolorami ułożonymi
data=ilGetData(); w trójkę BGR, ale zapisanymi na peł-
if(ilGetInteger(IL_PALETTE_TYPE)==IL_PAL_NONE){ nych 32 bitach, nakazujemy ich ułożenie
for(y=0;y for(x=0;x pos=(y * bytes_per_line) + ( x * bytes_per_pixel ); przenosi dane obrazu do bufora ramki.
r=*(data + pos + off_r); Po wykonaniu konwersji sprawdza-
g=*(data + pos + off_g); my, czy wczytany obraz nie jest przy-
b=*(data + pos + off_b); padkiem większy niż rozdzielczość
setpixel32(fromx + x, fromy +y, r, g, b, 0); dostępna dla bufora ramki. Korzy-
} stamy z funkcji ilGetInteger, która
} w zależności od podanych parametrów
} zwraca różne ważne informacje o obra-
zie. Jeśli podamy w argumencie war-
większych obrazów proces ten może i odczytujemy podstawowe informacje. tość IL_IMAGE_WIDTH, to otrzymamy sze-
zabrać kilka sekund: Istotą naszej przeglądarki jest jednak rokość obrazu. Dwa pierwsze argumenty
funkcja DevILLoad  to ona wykonuje naj- iluScale z pewnością nikogo nie dziwią
ilEnable(IL_ORIGIN_SET); ważniejsze zadanie, czyli wczytuje obraz,  są to wymiary bufora ramki, ale trzeci,
ilOriginFunc(IL_ORIGIN_UPPER_LEFT); poddaje go niezbędnej konwersji oraz w którym podano zmienną depth, z pew-
S
iluImageParameter(ILU_FILTER, wyświetla na ekranie. nością jest zaskakujący. Nie jest to liczba
ILU_SCALE_LANCZOS3); bitów przeznaczonych na opis koloru, ale
Aadowanie obrazu oraz wymiar obrazu zazwyczaj równy jeden.
Po ustaleniu tych parametrów możemy konwersja obrazu i palety DevIL potrafi obsługiwać obrazy 3D, tzw.
wygenerować identyfikator obrazu. Pierwszą czynnością funkcji DevILLo- volume images  wówczas ta wartość
Każdy, kto napisał choćby najmniej- ad jest wczytanie pliku. Nie musimy obrazu może się zmieniać:
szy program w OpenGL, zobaczy, że ta nawet podawać jakiego typu jest to
S
część interfejsu DevIL jest bardzo podob- plik, bowiem biblioteka samodzielnie if(ilGetInteger(IL_IMAGE_WIDTH) >
S
na do API OpenGL. W pierwszej kolejno- go wykryje. Wczytanie pliku wykonu- vinfo.xres && ilGetInteger
S
ści generujemy identyfikator: ilGenIma- jemy za pomocą funkcji ilLoadImage, (IL_IMAGE_HEIGHT) >
S
ges(1, &ImgId). Oczywiście, gdy chcemy np. w taki sposób: vinfo.yres) iluScale(vinfo.xres,
wygenerować większą liczbę identyfika- vinfo.yres, depth);
torów, wystarczy, aby drugi argument był if (!ilLoadImage(fname)){
tablicą o typie ILuint. Gdy identyfikator printf("Nie mozna otworzyc pliku !!!\n"); Po wykonaniu skalowania koniecz-
został wygenerowany, musimy go uczy- return -1; nie trzeba uaktualnić podstawowe dane
nić aktualnym. Wykonuje się to w nastę- } o obrazie, takie jak wymiary, liczba
pujący sposób: ilBindImage(ImgId). Jeśli bajtów na jeden piksel oraz całkowita
w programie przetwarzamy więcej roż- Jeśli nie wystąpiły problemy, to dokonuje- liczba bajtów w jednej linii obrazu:
nych obrazów, to należy pamiętać o tym, my konwersji kolorów zapisanych w pale-
aby za pomocą ilBindImage wybrać wła- cie, o ile oczywiście dany plik posiada w=ilGetInteger(IL_IMAGE_WIDTH);
ściwy obraz. paletę. Listing 4 zawiera kod, który wyko- h=ilGetInteger(IL_IMAGE_HEIGHT);
S
Po tych wstępnych czynnościach nuje tę czynność. W pierwszej instrukcji, bytes_per_pixel=ilGetInteger
w naszej aplikacji za pomocą open_fb jeśli typ palety to BGR24 (bez kanału (IL_IMAGE_BYTES_PER_PIXEL);
uzyskujemy dostęp do bufora ramki alfa), to konwertujemy kolory do formatu bytes_per_line=w * bytes_per_pixel;
Jeśli nasz program wczyta obraz mniejszy
niż dostępna rozdzielczość, to umieszcza
obraz na środku ekranu. Aby przesunąć
obraz na środek do zmiennych fromx
i fromy, należy wstawić odpowiednie war-
tości. Dla wartości x (oraz analogicznie
dla y) należy podzielić przez dwa szero-
kość bufora ramki (wartość vinfo.xres)
i odjąć połowę szerokości obrazu.
W ten sposób otrzymamy wartość,
o jaką trzeba w poziomie przesunąć
obraz, aby wyświetlać go na środku.
Kod wyznaczający te wartości jest
Rysunek 2. Logo DevIL (inaczej OpenIL) wyświetlane w trybie konsoli
następujący:
67
www.lpmagazine.org
dla programistów
cokolwiek przenosić, należy odczytać I to stanowi o podstawowej różnicy
Listing 6. Przenoszenie danych obrazu
wskaznik na dane o obrazie. Zwraca go pomiędzy obrazem z paletą i bez palety.
do bufora ramki  wersja obrazu z paletą
funkcja o nazwie ilGetData: W pierwszym przypadku każdy piksel
kolorów
miał własny opis kolorów. W przypad-
if(ilGetInteger(IL_PALETTE_TYPE)== data=ilGetData(); ku palety, piksel w obrazie to tylko
IL_PAL_RGBA32){ numer koloru w palecie. Aby odczytać,
pal=ilGetPalette(); Postawmy sobie teraz pytanie: co to jakie są wartość trójki RGB, musimy się-
for(y=0;y for(x=0;xS
pos=*(data + ((y * sany bezpośrednio za pomocą trójki (inaczej mówiąc numer koloru) mno-
S
bytes_per_line) + RGB (bądz czwórki z kanałem alfa) żymy przez cztery, bowiem tyle bajtów
( x * bytes_per_pixel ))); w obrazie (przypadek z paletą omówię opisuje piksel  RGBA32. Po tej opera-
r=*(pal + (pos * 4) + 0 ); w następnym punkcie). Oznacza to cji, wykorzystując arytmetykę wskazni-
g=*(pal + (pos * 4) + 1 ); również, że nasza wiedza zdobyta pod- kową, przesuwamy się pod potrzebne
b=*(pal + (pos * 4) + 2 ); czas implementacji funkcji setpixel32 miejsce w palecie. Na koniec tej opera-
S
setpixel32(fromx + x, może zostać ponownie wykorzystana, cji dodajemy odpowiednio zero, jeden
fromy + y, r, g, b, 0); gdyż wiemy, w jaki sposób należy obli- i dwa, aby odczytać wartości dla koloru
} czyć pozycję w obrazie na podstawie czerwonego, zielonego i niebieskiego.
} dwóch współrzędnych. W tym konkret- W kodzie ta operacja przedstawia się
} nym przypadku pozycję wyznaczamy następująco:
w następujący sposób:
r=*(pal + (pos * 4) + 0 );
S
fromx=0; fromy=0; pos=(y * bytes_per_line) + ( x * g=*(pal + (pos * 4) + 1 );
if(w < vinfo.xres && h < vinfo.yres) { bytes_per_pixel ); b=*(pal + (pos * 4) + 2 );
S
fromx=(int) ( ((float)vinfo.xres / 2.0)
- ((float)w / 2.0) ); Jedynym problemem jest odczyt kolo- Pozostaje już tylko postawić punkt
S
fromy=(int) ( ((float)vinfo.yres / 2.0) rów. Pod koniec poprzedniego punktu w buforze ramki za pomocą funkcji set-
- ((float)h / 2.0) ); wyznaczyliśmy indeksy kolorów, pixel32. A jaka jest różnica w przypad-
} więc skorzystamy z tych wartości doda- ku palety RGB24? Mamy tu trzy bajty
jąc je do wyznaczonej pozycji w obra- zamiast czterech, zatem pozycji nie
Pozostał nam jeszcze jeden ważny ele- zie: mnożymy przez cztery, ale przez trzy.
ment, a mianowicie opis kolorów Reszta kodu pozostaje oczywiście bez
w samym obrazie. Biblioteka DevIL zakła- r=*(data + pos + off_r); zmian.
da, iż kolory mogą być opisane za pomocą g=*(data + pos + off_g);
dwóch układów: RGB bądz BGR, podob- b=*(data + pos + off_b); Podsumowanie
nie jak przy palecie. Z tego powodu za Jak widać, napisanie prostej przeglądar-
pomocą ilGetInteger z parametrem IL_ Jak widać, proces odczytu jest niemal ki plików graficznych okazało się dosyć
IMAGE_FORMAT sprawdzamy, z jakim typem identyczny ze stawianiem punktów. łatwe. Jedyny kłopot, jaki należało
ułożenia kolorów mamy do czynienia rozwiązać, to kolory. Oczywiście,
i odpowiednio ustalamy indeksy, np. dla Wyświetlanie obrazu jeśli stosujemy inny model kolorów,
kolorów BGR wygląda to następująco: opartego o paletę np. tryb szesnastobitowy, to operacje
W naszej aplikacji obsługiwane się dwa na bitach będą bardziej skompliko-
S
if(ilGetInteger(IL_IMAGE_FORMAT)== przypadki palety: RGB24 oraz RGBA32. wane. Program możemy wyposażyć
S
IL_BGR || Zajmiemy się tylko jednym przypad- w więcej opcji, np.: zapis obrazu
ilGetInteger(IL_IMAGE_FORMAT)==IL_BGRA) { kiem (RGBA32), ponieważ różnią się do innego formatu. Jest to dość
off_r=2; off_g=1; off_b=0; one od siebie zaledwie jednym szczegó- łatwe zadanie, gdyż taką potencjalną
} łem. Wskaznik do danych został juz przez możliwość daje nam biblioteka DevIL.
nas odczytany, ale potrzebny jest jeszcze Zachęcam do modyfikacji naszej prze-
W przypadku ułożenia RGB, indeksy wskaznik do palety: glądarki i wprowadzania nowych ele-
kolorów w pojedynczym pikselu przyj- mentów.
mują wartości odpowiednio: 0, 1, 2. pal=ilGetPalette();
W Internecie:
Wyświetlanie obrazu Pozycję w obrazie wyznaczamy tak jak
Pozostała nam do wykonania ostatnia poprzednio, ale tym razem do zmien-
" Informacje na temat polecenia mmap:
czynność, ale jakże ważna, a mianowicie nej pos przepisujemy wartość, jaka jest
http://www.opengroup.org/onlinepubs/
przeniesienie danych obrazu do bufora zawarta pod tym adresem:
009695399/functions/mmap.html
ramki. Pętlę przenoszącą dane z obrazu
" Strona domowa biblioteki DevIL:
S
do bufora dla przypadku bez palety pos=*(data + ((y * bytes_per_line) +
http://openil.sourceforge.net/
zawiera Listing 5. Zanim zaczniemy ( x * bytes_per_pixel )));
68
lipiec 2004


Wyszukiwarka