Delphi - Transformacje grafiki - Wstęp
Fri, 10 December 2004
Wstęp
Witam wszystkich! Skończywszy cykl "Delphi - strumienie
danych" rozpoczynamy kolejny, myślę, że nieco ciekawszy. Zastanawiacie się pewnie co dziś będzie?
Ostatnio w moich artykułach zagościła grafika. Również i dziś zajmiemy się
obrazem, efektownie go modyfikując. Przetwarzanie grafiki rastrowej nie jest
wcale takie trudne. Pokażę Wam jak szybko zacząć i na co zwrócić uwagę,
aby samodzielnie "programować grafikę".
Niniejszy artykuł jest kierowany dla tych troszkę bardziej zaawansowanych
programistów, aczkolwiek wszystko starałem się tłumaczyć w sposób zrozumiały
nawet dla tych mniej doświadczonych koderów.
Podstawy
Zapewne słyszałeś o różnorodnych filtrach oferowanych
przez popularne aplikacje graficzne, jak Jasc Paint Shop Pro, Adobe Photoshop
czy nawet IrfanView. Być może nawet ich używałeś. Przychodzi jednak chwila,
kiedy chcielibyśmy czymś urozmaicić nowo-napisany program bądź
nowo-stworzoną grę. Przecież każdy programista zawsze dodaje do kodu
"coś od siebie". Jak działają owe filtry? Postaram się odpowiedzieć
na to pytanie.
Podstawą prostych filtrów graficznych jest pętla, a właściwie zwykle dwie pętle.
"Przechodzą" one przez każdy piksel obrazka odpowiednio go modyfikując
(czasami możecie się również spotkać z rozwiązaniami bazującymi na
zmianie palety kolorów). Jak zapisać pętlę, która przechodziłaby przez wszystkie piksele obrazka ?
For y := 0 To Obrazek.Height-1 Do
For x := 0 To Obrazek.Width-1 Do
begin
{tu jakieś instrukcje}
end;
Prawda, że proste? I tu początkujący w tej dziedzinie natykają się na
pewien problem, który został rozwiązany w powyższym kodzie. Mianowicie,
zwykle pomijają odejmowanie ( Obrazek.Height-1 oraz Obrazek.Width-1 ). Co
najgorsze, system nie zakomunikuje nam żadnego błądu, a program nie będzie funkcjonował w
100% prawidłowo. Dlaczego tak się dzieje? Należy najpierw spytać :
"Dlaczego należy odejmować 1 od szerokości i wysokości obrazka?".
Pomyślmy chwilkę. Delphi, aby podać nam już gotową wartość wysokości
obrazka musi policzyć ilość pikseli w pionie składających się na obrazek.
Właściwość Height będzie więc ilością pikseli w pionie, a Width w
poziomie. Wszystko byłoby w jak najlepszym porządku gdyby pierwszy piksel
oznaczany był współrzędnymi (1,1). Ale tak nie jest ;) Pierwszy piksel
obrazka (lewy, górny) jest oznaczany współrzędnymi (0,0), dlatego Delphi
licząc ilość pikseli w pionie czy w poziomie liczy również piksel (0,0). Z
tąd ta różnica. Jak wiadomo, my, ludzie, rozpoczynamy liczenie od 1 - z tąd
ta rozbieżność. Dlatego, obrazek o właściwości Height równej 5 pikseli, będzie
miał ich tak naprawdę:
0,1,2,3,4 (gdy wliczymy 0 to suma wyniesie pięć).
Trudno to w tej chwili zmienić, a nawet jeżeliby można było,
to nie ma sensu, bowiem w parze za zlikwidowaniem zera idzie nieoszczędność
pamięci. Po drugie, nawet nie chce myśleć co by się stało z kompatybilnością
wsteczną oprogramowania!
Kilka małych rad
- Pisząc filtr, zwracajmy uwagę, aby był zarówno możliwie najszybszy jak i możliwie najprostszy (czasami jedno nie idzie w parze z drugim). Taka kostrukcja pozwoli: po pierwsze szybko wykonać daną operację, po drugie w przyszłości nie będziemy tracili czasu na zrozumienie setek linii zagmatwanego kodu.
- Pomagajmy sobie komentarzami, stosując je wszędzie gdzie tylko możemy. Nie tylko przy tych "cięższych" linijkach. Gdy opiszemy kod linijka po linijce, to po upływie paru miesięcy będziemy go mogli dosłownie "przeczytać", nie tracąc czasu na próby zrozumienia.
- Bardziej zaawansowane operacje dzielmy na kawałki, umieszczając je w osobnych procedurach. Dzięki temu łatwiej będziemy mogli wykryć błąd, a konstrukcja modułowa pozwoli na efektywną konserwację kodu.
- W pętlach starajmy się zwracać choć na chwilkę kontrolę systemowi za
pomocą instrukcji Application.ProcessMessages. Zabezpieczy to nasz program,
na wypadek przetwarzania większej ilości danych, przed sytuacją gdy na
ekranie pojawia się monit "Program nieodpowiada", a my widzimy w
miejscu okna naszego programu białą plamę.
Uwaga! Program może przez to nieco spowolnić pracę. - Korzystaj z dopalaczy sprzętowych, jak : DelphiX (DirectX) czy OpenGL (na początek polecam jednak DelphiX), aby program pracował jak najwydajniej.
- Typ byte (bajt), mieści się w zakresie od 0..255 (czyli 256 elementów
:). Na każdą składową RGB potrzebujemy liczby z zakresu od 0 do 255,
czyli właśnie typu byte. Są to czerwony (0..255), zielony (0..255),
niebieski (0..255). Tym na co chciałbym zwrócić uwagę, jest fakt, że
zmienna typu byte, po przekroczeniu swojego zakresu nie zwraca żadnego błędu,
ale liczy od początku ! To znaczy, że gdy zapiszemy do zmiennej typu byte
wartość przekraczającą jej zakres, np.: 300, wówczas otrzymamy wynik
45.
Oczywiście nie możemy bezpośrednio przypisać zmiennej wartości która przekracza jej zakres, ponieważ kompilator na to nie pozwoli. Możemy jednak "oszukać" to stworzenie w ten sposób:
var r : byte; i : integer; begin r := 0; for i := 0 To 300 Do Inc( r ); ShowMessage( IntToStr( r ) );
Taka ciekawostka, którą możemy wykorzystać.
Zakończenie
Myśle, że wprowadzenie minęło "bezboleśnie". Przyszedł czas na "przyrządzenie" pierwszego filtra graficznego, czym zajmiemy się w następnym odcinku tego cyklu.





Podobne artykuly:
- Delphi - Budowa modułu
- Delphi - Transformacje grafiki - Przyjaśnianie i przyciemnianie
- Delphi - Transformacje grafiki - Efekt fali cosinusoidalnej
- Delphi - Transformacje grafiki - Przezroczystość
- Delphi - Transformacje grafiki - Odcienie szarości
- Delphi - Strumienie - Wstęp
- Delphi - Transformacje grafiki - Odbicia lustrzane