artykuły

Delphi - Transformacje grafiki - Wstęp

21:36
pią, 10 grudzień 2004
Pierwszy artykuł z serii "Transformacje grafiki". Wprowadza czytelnika w zagadnienia związane z operowaniem na pikselach.

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.

12345
Delphi - Transformacje grafiki - Wstęp Autor opinii: Czytelnicy, data przesłania: 5

Podobne artykuly:

Skomentuj

Aby zamieścić komentarz, proszę włączyć JavaScript - niestety roboty spamujące dają mi niezmiernie popalić.






Komentarze czytelników

    Nie ma jeszcze żadnych komentarzy.
    Dexter