2004 09 Stereogramy [Programowanie]


dla programistów
Stereogramy
Marek Sawerwain
ielu Czytelników z pew- Inaczej postąpimy podczas genero-
nością zetknęło się ze wania stereogramu. Gdy użytkownik
stereogramami w posta- wybierze plik, który chce zamienić na
Wci różnych pocztówek, stereogram, to poznamy jego pełną ścież-
a być może oglądało je na ekranie kę. Wtedy wczytamy ten plik do pamięci
komputera. Chociaż nie są one już tak wykorzystując bibliotekę DevIL.
popularne, jak kilka lat temu, to z pew- Nasz program będzie przetwarzał
nością warto wiedzieć, jak powstają tego tylko pliki z tzw. kolorem indeksowanym
typu obrazy. Wbrew pozorom, technika (dlaczego stosujemy taki tryb koloru,
rysowania stereogramów, np. takich jak zaraz się wyjaśni), natomiast wewnętrzne
na dołączonych do artykułu rysunkach, mechanizmy GNOME bądz GTK skupiają
jest zaskakująco prosta. Algorytm postę- się raczej na kolorach typu RGB. Niestety,
powania można zapisać w kilkunastu ten rodzaj opisu kolorów nie nadaje się
linijkach kodu. do łatwego generowania stereogramów.
W tym artykule postaram się przed- Z tego powodu korzystamy z DevIL,
stawić, jak napisać program do genero- gdzie ładowany plik nie jest poddawany
wania prostych stereogramów. dodatkowej wewnętrznej konwersji.
Po wczytaniu pliku graficznego
Na początek kilka i wykonaniu stereogramu, gotowy obraz
postanowień zapisujemy do pliku tymczasowego, który
Nasz przyszły program napiszemy wczytujemy do widgetu GtkImage. Zapis
w oparciu o środowisko GTK oraz stereogramu do ostatecznego pliku nastą-
GNOME. Będziemy stosować najnowszą pi dopiero, gdy użytkownik wybierze
stabilną wersje GNOME o numerze 2.6, opcję Zapisz w naszym programie. Może
a bibliotekę GTK w wersji 2.4. Nie będzie- to wydać się działaniem naokoło, ale
my mozolnie kodować interfejsu, gdyż w ten sposób oszczędzamy sobie sporo
użyjemy w tym celu programu GLADE. pracy i kłopotów związanych z  żonglo-
Sam program będzie realizował tylko waniem typami kolorów oraz żmudnym
podstawowe funkcje, takie jak wczy- kopiowaniem bloków pamięci.
tanie obrazu, konwersję na stereogram Powyższe uwagi najlepiej jest zoba-
oraz jego zapis. Dodamy jeszcze funkcję czyć w formie diagramu, znajdującego się
wydruku, ponieważ dość łatwo zreali- na Rysunku 1. Jak można się domyśleć,
Na płycie CD/DVD
zujemy ją przy pomocy API biblioteki interfejs nie będzie trudny do zrealizowa-
Na płycie CD/DVD znajduje się
libgnomeprint. nia. Jedynym bardziej skomplikowanym
kod zródłowy i wykonywalny
programu oraz Pomyślmy, jakie najważniejsze zda- elementem wydaje się sam algorytm
wszystkie listingi z artykułu.
rzenia będą zachodzić w naszym pro- generowania stereogramu.
gramie. Jedną z pierwszych czynności Ostatnie założenie, jakie chciałbym
O autorze:
jest wczytanie pliku graficznego do przedstawić, jest bodaj najważniej-
Autor zajmuje się tworzeniem
programu. Można to zrealizować w łatwy sze: stereogramy najlepiej generować
oprogramowania dla WIN32
sposób, wykorzystując gotowy widget na podstawie czarno-białych ilustra-
i Linuksa. Zainteresowania:
GtkImage. Załadowanie do niego pliku cji, a dokładniej z rysunków z paletą
teoria języków programowania
nie będzie wymagało dużej ilości kodu. w odcieniach szarości. Stosując taki rodzaj
oraz dobra literatura.
Podobnie w przypadku zapisu gotowego palety można osiągnąć świetne wyniki.
Kontakt z autorem:
autorzy@lpmagazine.org stereogramu, znajdującego się w GtkIma- Niezbędną konwersję obrazu możemy
ge  wystarczą dwie linie kodu. wykonać programem GIMP, zamieniając
70
wrzesień 2004
dla programistów
stereogramy
kolory na odcienie szarości. Należy rów-
nież wybrać tryb indeksowy, w którym
podamy ilość kolorów. Standardowo
powinniśmy deklarować 256 kolorów,
a raczej poziomów szarości. Dodam jesz-
cze, że rysunki nie powinny posiadać
dużej liczby szczegółów.
Algorytm generowania
stereogramu
Tworzenie stereogramu opiera się na
dość prostej zasadzie. Jako pierw-
szy tworzymy tzw. wzór bazowy. W
naszym programie będzie to losowy
układ pikseli, ale w istocie może to
być cokolwiek, np. fragment obrazu
bądz litery.
Gdy mamy już gotowy wzór
bazowy, możemy przystąpić do
generowania stereogramu. Bierzemy
pierwszy piksel obrazu i sprawdzamy
jego wartość. Jeśli jest równa zeru,
Oglądanie stereogramów
to przenosimy w to miejsce pierwszy
piksel (albo element) wzoru bazowe-
Podstawowa zasada oglądania stereo- zobaczyć przestrzenny obraz. Dość
go. Następnie badamy kolejny piksel
gramów polega na skupianiu wzroku często łatwiej ogląda się stereogram na
obrazu. Jeśli i on będzie równy zeru,
poza obrazem. Powinniśmy niejako ekranie monitora. Istnieje jeszcze jeden
to kopiujemy następny piksel naszego spoglądać przez kartkę i skupiać wzrok sposób, który może pomóc, a mianowicie
wzorca. Jeśli nadal będą występować mniej więcej 40-50 cm za kartką. przeniesienie stereogramu na przezro-
Jednym z łatwiejszych sposób oglądania czystą folię i patrzenie przez folię skupia-
zera, a nasz wzór jest znacznie krót-
stereogramów jest przybliżenie kartki do jąc wzrok na jakimś przedmiocie.
szy niż przetwarzany obraz, to pózniej
nosa i powolne jej oddalanie. Jeśli będzie- Powyższy rysunek prezentuje pewien
będziemy kopiować piksele z już prze-
my utrzymywać to samo skupienie i kieru- obraz do testów. Nie zdradzę w tym miej-
tworzonego obrazu. Z tego powodu,
nek wzroku, po paru próbach powinniśmy scu, co przedstawia.
jeśli linia obrazu składa się wyłącznie
z zer, to zobaczymy powtarzający się
wzór bazowy  po przeniesieniu całego
wzoru bazowego, analizując linię, Niech wzorem bazowym będzie ciąg literę  e . Po wszystkich zmianach otrzy-
będziemy już przenosić nie elementy znaków: abcde. Pierwszy znak naszego mamy następujący obraz:
wzorca, ale piksele zawarte w linii obrazu to zero, więc przenosimy pierw-
obrazu. szy znak wzorca, czyli  a . Przechodzi- abcde
Gdyby piksel miał wartość jeden, to my do następnego punktu, zarówno acdee
skopiujemy następny punkt ze wzorca, w obrazie, jak i we wzorcu. Ponownie acdee
np. jeśli aktualnie wskazujemy na trzeci mamy zero, więc przenosimy literę  b . acdee
piksel wzorca, to przenosimy piksel Ponieważ pierwszy wiersz składa się abcde
czwarty. Gdyby badany punkt miałby wyłącznie z zer, to nastąpi dokładne
wartość dwa, to przeniesiemy piąty przepisanie wzorca. Przejdzmy teraz Ostatecznie, aby ułatwić oglądanie ste-
piksel wzorca. Ogólnie, jeśli badany do drugiej linii. Wartość pierwsza - zero reogramu, dodamy wzorzec  abcde na
piksel ma wartość n, a pozycja we wzorcu - przeniesie nam pierwszy znak wzorca, początek i koniec każdej linii obrazu
ma numer i, to przenosimy piksel z pozy- ale drugi badany element zawiera jedyn- powyżej. Nasz tekstowy stereogram
cji i+n. kę. Zgodnie z naszym algorytmem, prze- przyjmie następującą postać:
Wypróbujmy teraz nasz sposób niesiemy następny znak po aktualnie
generowania stereogramów na pro- wskazywanym, więc nie będzie to litera abcdeabcdeabcde
stym przykładzie. Mamy następujący  b , ale kolejna we wzorcu litera  c . abcdeacdeeabcde
rysunek: W następnym badanym elemencie abcdeacdeeabcde
sytuacja powtarza się. Ponownie mamy abcdeacdeeabcde
00000 jedynkę, a we wzorcu jesteśmy na pozy- abcdeabcdeabcde
01110 cji litery  c , więc kopiujemy następny
01110 znak  literę  d . Gdyby zamiast jedynki Generowanie stereogramów jest, jak
01110 była dwójka, a we wzorze bylibyśmy widać z powyższego przykładu, łatwym
00000 na pozycji  c , to należałoby przenieść zadaniem. Widać również, dlaczego
71
www.lpmagazine.org
dla programistów
sekcji: XResolution/sections. Liczba
sekcji ma również duże znaczenie i tak
naprawdę nie powinna być większa niż 8
(w ostateczności może być ich 10).
Oznacza to ponadto, że wzór bazowy
jest umieszczany na początku każdej linii
bezpośrednio w obrazie. Z tego powodu
lepiej tworzyć obrazy wyśrodkowane
i nieprzylegające do krawędzi.
Sam proces generowania wzoru spro-
wadza się do następującej linii kodu:
in_buf[x]=rand() % 256;
Losujemy tu liczbę z zakresu od 0 do
255, wykorzystując funkcję rand. Ponie-
waż paleta jest w odcieniach szarości
(a przynajmniej taka powinna być, zgod-
nie z naszymi wcześniejszymi założe-
niami), to wylosowane punkty będą się
układać w czarno-biały wzór. Za każdym
razem wzór będzie inny, więc nawet gdy
tworzymy po raz drugi stereogram z tego
samego obrazu, to otrzymamy inny wzór
bazowy. W efekcie nowy stereogram
będzie różnił się od poprzedniego.
Zadaniem drugiej pętli, jak głosi
poprzedzający ją komentarz, jest utwo-
rzenie stereogramu. Podobnie jak
w przypadku generowania wzoru, odczy-
tujemy wiersz obrazu i dokonujemy na
Rysunek 1. Schemat zdarzeń w programie generującym stereogramy
nim odpowiednich operacji. Stereogram
powstaje z danych tuż po wzorze bazo-
powinniśmy stosować paletę o 256 nego wiersza to zadanie dla funkcji get_ wym, dlatego w pętli wewnętrznej warto-
poziomach szarości. Jeśli w takiej palecie raster_line. Po wykonaniu czynności ścią początkową zmiennej x jest wartość
zero będzie oznaczać kolor czarny, a war- na wierszu, musimy przetworzone dane, XResolution/sections. Zwróćmy uwagę
tość 256 kolor biały, to pozostałe wartości zawarte w buforze, umieścić z powrotem na to, że używamy też drugiej zmiennej i
reprezentują kolory pośrednie. Jest zupeł- w obrazie i to zadanie wykonuje funkcja (jej wartością początkową jest zero).
nie naturalne, że możemy traktować sza- put_raster_line. Możemy teraz przystąpić do genero-
rości jako poziomy głębi. Wykorzystując Na początku każdej linii umiesz- wania stereogramu. Odczytujemy aktual-
odpowiednie przesunięcia wzorca, kodu- czamy wzór bazowy, a są to po prostu nie badany piksel (jego numer zawiera
jemy wrażenie głębi. losowe punkty z palety. Działamy tu zmienna x). Jeśli piksel posiada wartość
Jednak, aby dostrzec stereogram, w trochę inny sposób niż w przedstawio- zero (jest czarny), to na jego miejsce
należy na niego spojrzeć w odpowiedni nym wcześniej opisie algorytmu, gdyż jest przenoszony piksel o indeksie i.
sposób. Kilka przydatnych uwag zawiera każda linia posiada swój własny wzór Zwróćmy uwagę, że i na początku wska-
ramka Oglądanie stereogramów. Czy- bazowy. Ponieważ jest to losowy układ zuje na wzór bazowy. Jeśli jednak piksel
telników zainteresowanych dokładniej- pikseli, to nie wpływa to na ostateczną jest większy od zera, to wybieramy
szymi informacjami odsyłam do książki postać stereogramu. piksel, zgodnie z naszym algorytmem,
 Grafika PC bez tajemnic , wydanej przez Gdy spojrzymy na stereogramy dołą- przesunięty o jeden plus wartość piksela
wydawnictwo Intersoftland w 1995 roku. czone do artykułu, to widać, że mają podzieloną przez szesnaście. Następnie,
one powtarzające się paski. Owe paski w obydwu przypadkach, przesuwamy
Implementacja algorytmu to sekcje obrazu. Ich zadaniem jest uła- się we wzorze o jeden piksel, czyli
W poprzednim punkcie przedstawiłem twienie oglądania stereogramu  łatwiej zwiększamy wartość i o jeden.
działanie algorytmu, a teraz zajmiemy się skupić wzrok na centrum stereogramu, Wcześniej, omawiając algorytm, nie
jego implementacją. gdy powtarzają się na nim pewne wzory. wykonywaliśmy żadnych dodatkowych
Na początku należy utworzyć wzór Z tego powodu cały obraz został podzie- operacji, ale dzielenie jest dość ważną
podstawowy. Na Listingu 1 jest to pierw- lony na sekcje i długość wzoru bazowego operacją, ponieważ zabezpiecza nas
sza pętla for. W pętli tej przeglądamy jest równa długości sekcji, którą wyzna- przed zbyt dużymi przesunięciami.
cały obraz linia po linii. Odczytanie jed- czamy dzieląc rozdzielczość przez liczbę Operację dzielenia traktujemy także jako
72
wrzesień 2004
dla programistów
stereogramy
Listing 1. Procedura tworząca stereogram
Krótki przepis
na stereogram
void my_make_sirds_image(){
int x,y,i;
Tworzenie stereogramu rozpoczy-
/* tworzenie wzoru */
namy od utworzenia rysunku z tzw.
for(y=0;ymapą głębokości. Zgodnie z naszymi
get_raster_line(0, Xresolution, y, &in_buf[0]);
założeniami, jest to obraz w odcie-
for( x = 0 ; x < ( XResolution / sections ) ; x++ ){
niach szarości. Kolor czarny oznacza
in_buf[x]=rand() % 255;
najbliżej położony obszar, a kolor
}
biały  najdalej. Tego rodzaju rysunek
put_raster_line(0, XResolution, y, &in_buf[0]);
z powodzeniem można przygotować }
/* tworzenie stereogramu */
samodzielnie, czego przykładem jest
for(y=0;yponiższe  dzieło :
get_raster_line(0, XResolution, y, &in_buf[0]);
i=0;
for( x = XResolution / sections; x < XResolution ; x++ ){
if(in_buf[x]==0){
in_buf[x]=in_buf[i];
i++;
continue;
}
if(in_buf[x]>0){
in_buf[x]=in_buf[i + 1 + (in_buf[x]/16)];
i++;
continue;
}
}
Gdy dysponujemy rysunkiem, to
put_raster_line(0, XResolution, y, &in_buf[0]);
możemy go wczytać do naszej aplikacji
}
i wygenerować stereogram. Powinniśmy
}
otrzymać obraz podobny do poniższe-
go:
pewnego rodzaju skalowanie kolorów  Funkcją memcpy przenosimy dane ze
zamiast 256 stosujemy już tylko 16. Pole- zmiennej srids_image do zmiennej
cam poeksperymentować z tą wartością, __data. Dane, jak widać, są odpo-
aby przekonać się samemu, jak wpływa wiednio adresowane. Wyrażenie
na ostateczny obraz stereogramu. y*XResolution przenosi nas na począ-
W taki sposób przedstawia się imple- tek wiersza o numerze y. Następnie
mentacja naszego algorytmu. Jak widać, przesuwamy się o from_x bajtów,
generowanie stereogramów nie jest trud- aby wskazać na początek potrzeb-
nym zadaniem. Algorytm można unowo- nego nam obszaru. Funkcja memcpy
cześnić wprowadzając kilka innowacji, w ostatnim argumencie oczekuje
Otrzymany stereogram można dalej
np. dodać kolorowe piksele albo określać podania ilości bajtów do skopiowa-
przetwarzać, tzn. takie operacje, jak
własny wzór bazowy. nia. Będzie to różnica pomiędzy to_x
skalowanie (w szczególności powięk-
i from_x, czyli końcem fragmentu
szanie), obracanie czy kolorowanie,
Odczytywanie i zapis wiersza, który nas interesuje, a jego
nie niszczą zawartości stereogramu.
wiersza obrazu początkiem.
Prostym sposobem na uatrakcyjnienie
W implementacji algorytmu używamy W drugiej funkcji wszystkie
stereogramu jest nałożenie deseniu np.
dwóch sprytnych funkcji do odczytu obliczenia pozostają niezmienione.
prostego gradientu na bazie koloru zie-
wiersza (get_raster_line) i do jego Zamiana dotyczy dwóch pierw-
lonego. W efekcie otrzymamy kolorowy
stereogram: zapisu (put_raster_line). Obydwie szych argumentów. Zamieniamy je
funkcje to właściwie tylko jedna linia miejscami, ponieważ tym razem
kodu, zawierająca wywołanie memcpy. przenosimy dane ze zmiennej
Treść pierwszej funkcji przedstawia się __data pod odpowiednie miejsce
następująco: w sirds_image:
S S
void get_raster_line void put_raster_line
S S
(int from_x, int to_x, int y, (int from_x, int to_x, int y,
unsigned char *__data){ unsigned char *__data){
S S
memcpy((void*)__data, (void*) memcpy((void*)(sirds_image +
S S
(sirds_image + (y*XResolution) + (y*XResolution) + from_x),
from_x), to_x  from_x); (void*)__data, to_x  from_x);
} }
73
www.lpmagazine.org
dla programistów
ge, więc musimy uzyskać wskazanie okna wybrania katalogu i utworzenia
Listing 2. Treść funkcji
na widget o nazwie ImageWidget. Tym katalogu. Nas będzie interesować na
on_SirdsGenerate_MNU_activate
zajmuje się trzecia linijka kodu z funkcji razie pierwszy typ okna. Z tego powodu
srand(time(0));
ilInit(); main: podaliśmy identyfikator GTK_FILE_CHO-
iluInit();
OSER_ACTION_OPEN. Następne argumenty
ilEnable(IL_ORIGIN_SET);
S
ImageWidget=glade_xml_get_widget precyzują wartości, jakie są zwracane
ilOriginFunc(IL_ORIGIN_UPPER_LEFT);
(xml, "ImageWidget"); w zależności od wciśniętego przycisku:
ilGenImages(1, &ImgId);
podajemy je parami. Wartość GTK_STOCK_
ilBindImage(ImgId);
load_image(&filename[0]); Zmienną ImageWidget zadeklarowaliśmy OPEN oznacza przycisk Otwórz. Po niej
my_make_sirds_image();
w sposób normalny dla GTK: GtkWidget została podana wartość, która zostanie
save_sirds_image
*ImageWidget;. przekazana, gdy użytkownik wciśnie
("/tmp/tmp_image.png");
Pozostały nam jeszcze dwie linie. ten przycisk. Sytuacja wygląda podobnie
ilDeleteImages(1, &ImgId);
Pierwszą podłączamy wszystkie zdefinio- dla następnej pary argumentów: GTK_
gtk_image_set_from_file
(GTK_IMAGE(ImageWidget), wane sygnały: STOCK_CANCEL oznacza przycisk Anuluj
"/tmp/tmp_image.png");
i jeśli użytkownik wciśnie ten przycisk,
glade_xml_signal_autoconnect(xml); jako wynik zostanie przekazana wartość
Program dla środowiska GTK_RESPONSE_CANCEL.
GNOME/GTK a drugą (najważniejszą instrukcją) uru- Pozostało nam uruchomienie okna
Gdy mamy już gotowy algorytm gene- chamiamy nasz program: dialogowego funkcją gtk_dialog_run.
rujący stereogram, to napisanie samego Gdy użytkownik wciśnie jeden z dwóch
programu nie będzie trudnym zadaniem. gtk_main() wspomnianych przycisków, funkcja
Tworzenie programu rozpoczynamy gtk_dialog_run zakończy swoje działa-
od zbudowania interfejsu w programie Wczytanie nie i zwróci jako wynik jedną z dwóch
GLADE. Rysunek 2 przedstawia główne oraz zapis obrazu podanych wartości: GTK_RESPONSE_ACCEPT
okno naszej aplikacji. Tworzenie inter- Tak naprawdę, aby załadować obraz albo GTK_RESPONSE_CANCEL. Przy pomocy
fejsu jest łatwym zadaniem, więc nie z pliku do komponentu GtkImage, wystar- instrukcji warunkowej sprawdzamy, czy
będę go tutaj szerzej omawiał. Zwróćmy czy wywołać jedną funkcję o nazwie jest to pierwsza z wartości. Przedstawia
jednak uwagę na to, że menu oraz pasek gtk_image_set_from_file. Wcześniej użyt- się to następująco:
z przyciskami są umieszczone w kompo- kownik musi jednak wybrać plik, który
nentach GtkHandleBox. Ostatecznie, nasz chce wczytać. Nasz program piszemy if (gtk_dialog_run
S
program będzie korzystał głównie z kom- w oparciu o najnowszą wersję środowiska (GTK_DIALOG (dialog)) == GTK_RESPONSE_
ponentów GTK. Elementy środowiska GNOME, więc skorzystamy z nowego ACCEPT){
GNOME wykorzystamy do implementa- okna dialogowego do wyboru plików. ...
cji wydruku oraz w oknie dialogowym Pierwszym krokiem jest zadeklarowa- }
O Programie. nie zmiennej okna dialogowego: GtkWid-
Utworzony plik, zawierający get *dialog;. Oprogramowanie nowego Sam proces ładowania obrazu do widgetu
interfejs, np. sirds_app.glade, zosta- okna dialogowego jest znacznie mniej GtkImage wymaga poznania nazwy pliku.
nie wczytany przez naszą aplikację kłopotliwe niż poprzednio stosowane W naszym programie nazwę przenosimy
dzięki bibliotece libglade. Bibliote- rozwiązanie  wystarczy utworzyć nowy do zmiennej filename:
ka libglade posiada cenną cechę, widget wywołaniem gtk_file_chooser_
S
a mianowicie potrafi samodzielnie dialog_new: strcpy(&filename[0],
S
podłączyć sygnały (zdarzenia) do ist- gtk_file_chooser_get_filename
S
niejących funkcji, jeśli tylko zgadzają dialog = gtk_file_chooser_dialog_new (GTK_FILE_CHOOSER (dialog)));
się nazwy zdarzeń oraz funkcji. To ("Wybierz plik", NULL,
bardzo upraszcza proces ładowania GTK_FILE_CHOOSER_ACTION_OPEN, Teraz wystarczy wywołanie gtk_image_
i podłączania zdarzeń, bowiem wszyst- GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, set_from_file, gdzie w drugim argu-
kie niezbędne czynności dokonywane GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
w funkcji main sprowadzają się do NULL);
pięciu linii kodu. W dwóch pierwszych
dokonujemy inicjalizacji biblioteki GTK Funkcja przyjmuje wiele parametrów.
oraz ładujemy plik z opisem interfejsu: W pierwszym podajemy oczywiście tytuł
okna. W drugim powinniśmy wskazać
gtk_init(&argc, &argv); okno, które jest właścicielem naszego
S
xml = glade_xml_new("glade/sirds_ okienka dialogowego, ale podanie war-
app.glade", NULL, NULL); tości NULL w niczym nie przeszkadza.
W trzecim parametrze podajemy, jaki
W naszej aplikacji będziemy ładować typ okna dialogowego nas interesuje.
obraz z pliku do widgetu GtkImage bądz Istnieją cztery podstawowe typy: okno
Rysunek 2. Interfejs programu w GLADE
zapisywać na dysk zawartość GtkIma- otwarcia pliku, okno zapisu pliku oraz
74
wrzesień 2004
dla programistów
stereogramy
mencie podajemy zmienną z nazwą jest odpowiedzialna za wzór bazowy. Potrzebujemy również informacji
pliku: Następie wykonujemy czynności zwią- o wymiarach przetwarzanego rysunku:
zane z korzystaniem z biblioteki DevIL.
S
gtk_image_set_from_file Uruchamiamy bibliotekę wywołaniem Xresolution=ilGetInteger(IL_IMAGE_WIDTH);
S
(GTK_IMAGE(ImageWidget), &filename[0]); funkcji ilInit oraz iluInit. Następne Yresolution=ilGetInteger(IL_IMAGE_
dwie funkcje zapobiegają ładowaniu HEIGHT);
Zapis obrazu jest dość podobny. się niektórych plików graficznych  do
Okno dialogowe tworzymy w niemal góry nogami , co mogłoby utrudnić Zapis danych, wykonywany w save_
identyczny sposób, podając w pierw- pózniejsze oglądanie wygenerowanych sirds_image, wymaga jeszcze mniej kodu,
szym argumencie tekst Zapis pliku, stereogramów. bo sprowadza się tylko do dwóch linii:
natomiast w trzecim argumencie Funkcja load_image zajmuje się
umieszczając wartość GTK_FILE_CHO- wczytaniem pliku. Wcześniej, jak widać, ilEnable(IL_FILE_OVERWRITE);
OSER_ACTION_SAVE. musimy wygenerować odpowiedni iden- ilSaveImage(fname);
Instrukcja warunkowa jest iden- tyfikator, bowiem tego wymaga bibliote-
tyczna, ale sama procedura zapisu jest ka DevIL. Identyfikator usuniemy pózniej Pierwsza funkcja pozwala nadpisać plik,
oczywiście inna. W pierwszej kolejności funkcją ilDeleteImages. Po załadowaniu jeśli istnieje, natomiast druga dokonu-
należy uzyskać dostęp do bufora pikseli, danych z pliku (funkcja load_image), je zapisu danych do pliku o podanej
czyli do struktury GdkPixBuf. Robimy to tworzymy stereogram. Zajmuje się tym nazwie. Wewnętrzne mechanizmy DevIL
w następujący sposób: funkcja my_make_sirds_image. Jej pełna samodzielnie określą format pliku po
treść jest zawarta na omówionym wcze- rozszerzeniu.
GdkPixbuf *pixbuf; śniej Listingu 1.
S
pixbuf=gtk_image_get_pixbuf Po wygenerowaniu stereogramu zapi- Podsumowanie
(GTK_IMAGE(ImageWidget)); sujemy obraz wywołaniem save_sirds_ Pełny kod zródłowy generatora ste-
image do katalogu tymczasowego. Potem reogramów to tylko siedem kilobajtów
Teraz jesteśmy gotowi, aby zapisać plik, wczytujemy stereogram do kontrolki tekstu. Jak widać, udało się nam napisać
np. w formacie PNG, pod nazwą podaną ImageWidget. Oczywiście, można napisać niewielki, ale pożyteczny program. Nie-
przez użytkownika. Wystarczy jedno kod, który całą tę procedurę wykonał- wiele istotnych funkcji możemy dodać
wywołanie instrukcji gdk_pixbuf_save: by poprzez przepisanie odpowiednich do naszego programu, chociaż np.
bloków pamięci, ale ten sposób jest nie- warto dać użytkownikowi możliwość
S
gdk_pixbuf_save wątpliwie znacznie łatwiejszy. podania własnego wzoru bazowego.
(pixbuf, &filename[0], "png", NULL, NULL); Pomimo tego, wzór losowy, zastosowa-
Funkcja load_image ny w programie, sprawdza się bardzo
Pierwsze trzy parametry są oczywiste. i save_sirds_image dobrze. Ponadto, można wprowadzić
Wartości NULL mają nieco inne prze- Załadowanie pliku graficznego w biblio- zmienną liczbę sekcji oraz zmienną
znaczenie. Pierwsza z nich oznacza, tece DevIL sprowadza się do jednego wartość współczynnika, przez który
że nie będziemy interesować się błę- polecenia: ilLoadImage(filename[0]);. dzielimy numer piksela.
dami, które mogą wystąpić. ;-) Druga Wykonujemy jeszcze kilka dodatkowych Czytelnikom zainteresowanych pro-
oznacza, że nie będziemy przekazy- operacji. Jeśli wczytywany obraz nie jest blemem stereogramów proponuje przej-
wać żadnych dodatkowych informacji, w formacie koloru indeksowanego, to rzeć strony WWW znajdujące się w Inter-
np. w przypadku formatu JPG warto wykonujemy odpowiednią konwersję: necie (najlepiej za pomocą Google'a
byłoby podać jakość, z jaką ma być i hasła sirds). I jeszcze jedna rzecz na
S
zapisany plik. Wyglądałoby to w nastę- if(ilGetInteger(IL_IMAGE_FORMAT)!= zakończenie: obraz w ramce Oglądanie
pujący sposób: IL_COLOR_INDEX){ stereogramów to fotel.
S
ilConvertImage(IL_COLOR_INDEX,
S
gdk_pixbuf_save IL_UNSIGNED_BYTE);
S
(pixbuf, &filename[0], "jpeg", NULL, }
W Internecie:
"quality", "100", NULL);
Tę czynność lepiej jest wykonać za
" Spis stron WWW poświęconych stereo-
Generujemy stereogram pomocą dedykowanego programu do
gramom:
Ponieważ już wcześniej omówiłem obsługi grafiki. GIMP będzie nadawał się http://www.ccc.nottingham.ac.uk/
~etzpc/sirds.html
implementację algorytmu, to teraz zaj- do tego znakomicie.
" Dokument omawiający zagadnienie
miemy się tylko czynnościami pomoc- W kodzie tej funkcji wykonujemy
stereogramów:
niczymi. Generowanie stereogramu to dodatkowo konwersję palety, jednak waż-
http://www.cg.tuwien.ac.at/~mroz/
domena funkcji on_SirdsGenerate_MNU_ niejsze czynności to odczytanie trzech
sirds/index.html
activate. Listing 2 zawiera jej pełną istotnych dla nas informacji. Pierwszą jest
" Inna strona poświęcona stereogra-
implementację. Pierwszym elementem wskazanie na bufor danych i wykorzystu-
mom:
jest inicjalizacja generatora pseudolo- jemy do tego funkcję ilGetData:
http://www.cs.waikato.ac.nz/~singlis/
sowego: srand(time(0));. Wbrew pozo-
sirds.html
rom, to dość istotna czynność, gdyż sirds_image=(unsigned char*)ilGetData();
75
www.lpmagazine.org


Wyszukiwarka