Przejdź do zawartości
W tym miejscu miała znajdować się pusta plansza z jednym pikselem, ale się rozmyśliłem.

Opowieść o pikselu

Wyobraźmy sobie ekran telefonu dowolnego producenta. Posiada on swoją wysokość, szerokość i przekątną. Ta ostatnia najczęściej określana jest w calach, pozostałe dwie w centymetrach. Albo, co znacznie praktyczniejsze, w pikselach.

Uwaga na nisko przelatujące uproszczenia.

Dwie osie i jeden kolor

Najmniejszy punkt na ekranie to jeden piksel. Stanowi on budulec dla tekstu, grafik, filmików, animacji. Każda widoczna na ekranie rzecz to, w swojej najprostszej formie, zbiór pikseli. Piksel pikselowi nierówny, a jego wielkość uzależniona jest od takich rzeczy jak wartość PPI (ilości pikseli na cal) ekranu albo stopień jego zbliżenia. Ten sam piksel będzie wyglądał inaczej na antycznej Nokii, nowoczesnym Samsungu i monitorze komputera po 500% przybliżeniu.

Pojedynczy piksel może posiadać trzy właściwości – kolor oraz dwie współrzędne, X oraz Y, określające położenie na ekranie. Wartość koloru można zapisać na kilka sposobów, a dwa najpopularniejsze to:

  • RGB, od Red, Green, Blue. Opisany w ten sposób kolor składa się z trzech wartości w przedziale od 0 do 255, określających intensywność każdego składnika (dla uproszczenia pomijam odcień i nasycenie). Przykładowo, 0, 0, 255 da nam bardzo mocny, niebieski kolor, 255, 255, 0 żółć (mieszankę czerwieni i zieleni), a 0, 0, 0 głęboką czerń. RGB istnieje również w wersji RGBA, gdzie ostatnią wartość stanowi przeźroczystość (opacity). Wartość przeźroczystości znajduje się w przedziale od 1 do 0. Przy 1 kolor jest w pełni widoczny, przy 0 całkowicie znika.
  • Hex, który od poprzednika różni się głównie sposobem zapisu, w miejsce systemu dziesiętnego korzystając z szesnastkowego (zamiast 0-9 mamy 0-9 oraz a-f). W tej sytuacji kolor niebieski ma wartość 0000FF (FF = 255), żółty FFFF00, a czarny 000000. System ten jest trochę trudniejszy do zrozumienia od poprzedniego, więc na potrzeby tego tekstu będę się trzymał RGBA.

Położenie piksela na ekranie liczymy od lewego, górnego rogu. To punkt 0.0. Trochę na przekór szkolnej matematyce i jej układom współrzędnych, wartości dodatnie przesuną piksel w prawo i w dół, a ujemne w przeciwnych kierunkach. W razie potrzeby możemy go nawet wypchnąć poza ekran, a po chwili przywrócić, telefon nie będzie miał nam tego za złe.

Cztery kierunki

Ekran naszego telefonu jest pusty, więc stwórzmy jeden piksel. Nadajmy mu uroczy, zgniłozielony kolor (40, 170, 40, 1 w RGBA) oraz współrzędne 20, 20 (jak zawsze plusy są domyślne). Z racji niewielkich rozmiarów będzie on niewidoczny, chyba że ktoś ma naprawdę dobry wzrok i przysunie nos do ekranu. Mimo to wiemy, gdzie się znajduje i to nam póki co wystarczy.

Co możemy zrobić z naszym pikselem? Kilka rzeczy. Na przykład zmienić jego współrzędne, a co za tym idzie położenie. Możemy zrobić to gwałtownie, na przykład przerzucając go na drugą stronę ekranu. Możemy również zrobić to delikatniej, punkt po punkcie, dzięki czemu zyskamy wrażenie płynności. Tym w praktyce jest animacja – przesunięciem obiektu z punktu A do punktu B w określonym czasie. Im więcej czasu i punktów pośrednich, tym większe wrażenie płynności. To dlatego niektórzy ludzie zauważają różnicę między grami działającymi w 30 i 60 klatkach na sekundę.

Podepnijmy nasz piksel do klawiatury (nikt nie powiedział, że jej nie mamy). Niech klawisze 4 i 6 kontrolują wartość X piksela, a 2 i 8 wartość Y. Naciskamy szóstkę, X rośnie o 1. Naciskamy czwórkę, maleje o 1. Zmiana wartości trwa, póki klawisz nie zostanie puszczony, dokładnie jak z kursorem w dowolnym edytorze tekstowym. Możemy również przesunąć nasz piksel po skosie naciskając jednocześnie dwa klawisze, ale efekt będzie poszarpany. Dlaczego? Ponieważ z punktu widzenia maszyny piksel nie przesuwa się po skosie, tylko wpierw w górę / dół, a potem prawo / lewo. To daje dwa ruchy wykonywane jeden po drugim, które tylko udają, że są jednym. Rezultat jest co prawda ten sam, ale metoda niedoskonała.

I drugie tyle

Żeby stała się doskonała, musimy zaprząc do pracy kolejne cztery klawisze – 1, 3, 7 oraz 9. Osiem klawiszy to dwa razy więcej możliwości, ale i dwukrotny wzrost złożoności. Wcześniej cztery klawisze modyfikowały tylko po jednej wartości. 4 i 6 wpływały jedynie na wartość X, podczas gdy wartość Y pozostawała bez zmian. Teraz naciśnięcie 9 zwiększy o 1 zarówno X, jak i Y. W zamian ruch po skosie staje się faktycznie ruchem po skosie, miast jego podróbką. Nie ma przy tym znaczenia, czy naciśnięcie klawisza wpierw zmienia X, a potem Y, czy odwrotnie. Obie operacje są tak proste, że telefon wykona je błyskawicznie, a ludzki mózg dostrzeże tylko ich efekt.

Przesuńmy nasz piksel maksymalnie w prawo. Zajmie to chwilę, ale w pewnym momencie nasz zielony punkt zniknie. Przynajmniej dla nas, gdyż telefon będzie o nim pamiętał, nawet jeśli wartość X będzie liczona w dziesiątkach tysięcy. Możemy to zmienić z pomocą nowej reguły – X oraz Y nie mogą przekroczyć określonych wartości. Niech nasz przykładowy telefon posiada ekran rozmiaru 320 na 480 pikseli (pierwszy iPhone). W takiej sytuacji X musi być większy od -1 (pamiętacie o punkcie 0.0?), a mniejszy od 481. Ewentualnie większy bądź równy od 0 i mniejszy bądź równy od 480. Zależy czy chcemy piksel blokować na samej granicy wyświetlacza, czy tuż przed nią Od teraz jeśli na wskutek naciśnięcia klawisza wartość X ma szansę przekroczyć wartości graniczne, X nie ulegnie zmianie. Identycznie w przypadku wartości Y.

Trzej przyjaciele z boiska

Nasz ekran jest depresyjnie pusty, więc umieśćmy na nim kolejny piksel (współrzędne 50, 50), tym razem czerwony (RGBA 200, 30, 30, 1). Przesuńmy w jego stronę nasz pierwszy, zielony piksel. Oba piksele mogą zajmować sąsiednie współrzędne bez żadnych konsekwencji. Co się jednak stanie, gdy po naciśnięciu dowolnego klawisza ich współrzędne staną się identyczne? Odpowiedź zależy od wielu czynników, ale najprawdopodobniej jeden zostanie przykryty przez drugi. Przykryty, lecz nie zlikwidowany. Gdy ponownie naciśniemy dowolny klawisz, zasłonięty piksel okaże się być cały i zdrowy. Jego wielokrotne zasłanianie i odsłanianie będzie dawało takie same rezultaty.

Czerwony piksel nie ma możliwości ruchu. Nie poruszymy nim sami, gdyż brak nam wolnych klawiszy. Nie podepniemy go do już zajętych, gdyż byłoby to na dłuższą metę kłopotliwe, a na zamontowanie mu ruchu samodzielnego jesteśmy za leniwy. Pozostają nam dwie możliwości. Po pierwsze, możemy potraktować czerwony piksel jak granicę ekranu i uniemożliwić innym pikselom wejście na jego pozycję. W przeciwieństwie do granicy ekranu ta przeszkoda będzie doskonale widoczna. Możemy również ustawić, że po przesunięciu zielonego piksela na współrzędne czerwonego, ten drugi zniknie. Z ekranu oraz pamięci urządzenia. Gdy przesuniemy zielony piksel o jedno miejsce dalej, czerwony nie wróci. Oba pomysły brzmią dobrze, więc realizujemy oba. Od teraz na ekranie mamy trzy piksele – ruchomy zielony, znikalny czerwony i blokujący drogę niebieski (40, 40, 255).

Pożeracz światów

Na ekranie zrobiło się nieco ciaśniej, ale wciąż za mało, dlatego skomplikujmy sprawę. Powiedzmy, że za każdy usunięty czerwony piksel, na ekranie przybędzie jeden zielony. Będzie pojawiał się tuż obok już istniejącego, w miejscu, które ten zajmował przed usunięciem piksela czerwonego. Jeśli czerwony piksel stał na 30, 30, a zielony przesunął się do niego z 29, 30, to drugi zielony pojawi się na 29, 30.

Mamy teraz dwa zielone piksele, co oznacza, że używając klawiatury przesuwamy oba. Chwilowo nie jest to problem, ale stanie się nim przy bardzo długim sznurze pikseli. Dlatego musimy zmodyfikować reguły ruchu. Od tej pory przesuwany jest tylko pierwszy piksel, a kolejne tylko za nim podążają. To oznacza, że każdy piksel otrzymuje trzy nowe cechy – współrzędne z poprzedniego ruchu, współrzędne obecne oraz miejsce w kolejce. Dzięki nim mamy pewność, że ogonek będzie zawsze karnie przesuwał się za zielonym liderem.

W przypadku miejsca w kolejce musimy jednak pamiętać, że komputery – a nowoczesny telefon to w praktyce komputer – liczą nie od jedynki, tylko od zera. Przykładowo, jeśli damy komputerowi zestaw liter A, B, C, D, E, to dla niego A będzie oznaczone cyferką 0, B cyferką 1 i tak dalej. Nieintuicyjny, ale łatwy do zapamiętania drobiazg.

Tworzymy nowy czerwony piksel i przesuwamy do niego oba zielone. Zgodnie z ustalonymi przez nas regułami czerwony znika. Mimo to wciąż mamy na ekranie tylko dwa zielone, chociaż powinniśmy mieć trzy. Tak naprawdę mamy trzy, przynajmniej według pamięci urządzenia, ale dwa z nich zajmują te same współrzędne. Dlaczego? Ponieważ ustaliliśmy, że nowy zielony piksel ma pojawiać się obok pierwszego. Wprowadzamy więc poprawkę uwzględniającą miejsce w kolejce. Od teraz każdy nowy piksel ma się pojawić „za” ostatnim w kolejce, gdzie „za” oznacza współrzędne zajmowane przez niego przed chwilą. Jeśli czerwony piksel stał na 50, 50, pierwszy zielony wszedł z 49, 50, a zielone są łącznie dwa, to kolejny pojawi się na 48, 50 (50, 50; 49, 50; 48, 50).

Coś z niczego

Wszystko działa dobrze, ale do tej pory musieliśmy ręcznie tworzyć czerwone piksele. Póki co to nie problem, ale stanie się nim, gdy zechcemy mieć koło setki naraz, co pochłonie masę czasu. Poza tym nijak nie uda się nam rozstawić ich losowo, chyba że będziemy to robić z zamkniętymi oczyma. W tym celu potrzebujemy prostego algorytmu. Będzie on musiał uwzględniać trzy rzeczy.

Po pierwszewspółrzędne nowego piksela. To się wydaje proste, ale natychmiast trafiamy na problem. Musimy bowiem uwzględniać już istniejące piksele, tak by przypadkiem nie stworzyć czerwonego tam, gdzie znajdują się już zielone albo inne czerwone. W tym celu zbieramy koordynaty wszystkich pikseli w jednym miejscu i nakazujemy algorytmowi je sprawdzać. Jeśli wylosowane przez niego pole jest puste, to ma zostać na nim stworzony nowy piksel. Jeśli nie, to ma zostać powtórzone losowanie.

Tu jednak może pojawić się niewielki problem, w zależności od tego, z jakich narzędzi korzystamy. Niektóre z nich poproszone o podanie wartości losowej będą wybierać tylko między 1 i 0. Czy też, by być dokładniejszym, między 1 i 0 z szesnastoma miejscami po przecinku, na przykład 0.5314524195143038. Jak to naprawić? Dzięki mnożeniu. Potrzebujemy liczby z zakresu 0 do 100? Prosimy urządzenie o rzucenie kością, mnożymy wynik przez 100 i zaokrąglamy do pełnych wartości. Dokładnie w tej kolejności, inaczej maszyna będzie nam zwracać jedynie zera i setki. Nieintuicyjne, ale działa.

Co trzy sekundy

Po drugie, musimy ustalić częstotliwość pojawiania się nowych pikseli. Robimy to z pomocą sekund, a dokładniej milisekund, czyli tysięcznych części sekundy. Tu sprawa jest znacznie prostsza, wystarczy podczas wpisywania wartości dodać na końcu trzy zera. Ustalamy, że nowy czerwony piksel pojawi się w losowych współrzędnych co trzy sekundy (3000 milisekund), a żółty co trzydzieści (30000 milisekund).

Została nam trzecia składowa, czyli granice, w jakich piksel ma się pojawiać. Tutaj sprawa jest bardzo prosta i ogranicza się do pamiętania o wielkości ekranu. Przy losowaniu współrzędnych dla pikseli ignorujemy wartości mniejsze od 0 i większe od 320 dla X i 480 dla Y. Dzięki temu nowe piksele będą pojawiały się na ekranie, a nie poza nim.

Po upewnieniu się, że wszystkie obliczenia są poprawne, puszczamy nasze dzieło w ruch. Po trzydziestu sekundach otrzymujemy ekran z dziesięcioma znikalnymi, czerwonymi pikselami, jednym blokującym ruch żółtym i jednym ruchomym zielonym. W ten sposób stworzyliśmy prymitywną i niedokończoną (co się stanie, gdy zielone piksele się spotkają?), ale z grubsza funkcjonalną wersję Węża.

PS: Powyższy wpis jest eksperymentalny. Wszelkie uwagi bardzo mile widziane.