czwartek, 28 maja 2015

[PHP][GD2] Prosty gradient i operacje na kolorach pikseli

TRUE
5943953589183166087
W bibliotece GD mamy pełną kontrolę nad każdym pikselem składającym się na wynikowy obrazek. Możemy dowolnie pobierać jego wartości i ustawiać nowe. Możliwość taka, w połączeniu z odpowiednio skonstruowanymi pętlami pozwala na bardzo wiele, m.in. na generowanie gradientów, czyli przejść pomiędzy kolorami.

Funkcje do tworzenia złożonych gradientów są dość obszerne, my tutaj postaramy się nauczyć podstaw tworzenia prostych gradientów polegających na liniowym przejściu pomiędzy kolorami. Nauczymy się też operować na pikselach - pobierać oraz modyfikować ich wartości.

Tworzymy gradient

Popatrzmy na poniższy obrazek:


To prosty gradient wygenerowany bezpośrednio w PHP za pomocą jednej prostej pętli for. Aby móc tworzyć podobne obrazki za pomocą swoich funkcji trzeba zrozumieć zasadę działania takiego mechanizmu.

Najprostrzym sposobem na wygenerowanie liniowego przejścia jest pętla przechodząca pionowo lub poziomo inkrementująca się o jeden piksel wraz z każdym kolejnym przebiegiem. W każdym przebiegu jednocześnie zmniejszana lub zwiększana musi być wartość zadanego przez nas koloru.

Jak wiemy - kolory definiujemy za pomocą podawania ich odpowiednich wartości liczbowych w palecie RGB. Skoro więc są to zwykłe dane liczbowe, nic nie stoi na przeszkodzie, aby wykonywać na nich dowolne operacje matematyczne. Aby zrozumieć zasadę działania, stwórzmy sobie obrazek o szerokości 600px i wysokości 256px:

  1. $width = 600;
  2.  
  3. $height = 256;
  4.  
  5. $image = imagecreatetruecolor($width, $height);
  6.  

następnie stwórzmy pętlę for, która będzie przebiegać pionowo po naszym obrazku:

  1. for($i=0; $i < $height; $i++)
  2.  
  3. {
  4.  
  5.   // ....
  6.  
  7. };
  8.  

Jak widzimy pętla przebiegać będzie po koleji po "wierszach" obrazka, począwszy od wiersza 0, na ostatnim wierszu (w tym przypadku 255) kończąc. Dodajmy teraz do naszej pętli następujący kod:

  1. for($i=0; $i < $height; $i++)
  2.  
  3. {
  4.  
  5.   $color = imagecolorallocate($image, $i, $i, $i);
  6.  
  7.   imageline($image, 0, $i, $width, $i, $color);
  8.  
  9. };
  10.  

W każdym kolejnym przebiegu definiujemy nowy kolor, którego wartość będzie rosła liniowo wraz z inkrementacją zmiennej $i. Następnie rysujemy linię po całej szerokości naszego obrazka, o współrzędnej Y pobieranej również ze zmiennej $i. W każdym wierszu naszego obrazka będzie więc rysowana pozioma linia o wartości koloru rosnącej wraz z każdym przebiegiem pętli, aż do uzyskania końcowego miejsca na obrazie (255px w pionie).

Przetestujmy w praktyce:

  1. <?php
  2.  
  3. $width = 600;
  4.  
  5. $height = 256;
  6.  
  7. $image = imagecreatetruecolor($width, $height);
  8.  
  9. for($i=0; $i < $height; $i++)
  10.  
  11. {
  12.  
  13.   $color = imagecolorallocate($image, $i, $i, $i);
  14.  
  15.   imageline($image, 0, $i, $width, $i, $color);
  16.  
  17. };
  18.  
  19. header('Content-Type: image/png');
  20.  
  21. imagepng($image);
  22.  
  23. imagedestroy($image);
  24.  
  25. ?>
  26.  

Wynikiem będzie najprostszy z możliwych gradient:


Zmodfikujmy teraż nasz kod, tak aby pozwalał na wygenerowanie gradientu dla dowolnego rozmiaru obrazka - póki co mamy tu ograniczenie do 255px, bo tylko tyle maksymalnie może przyjąć wartość RGB. Musimy więc teraz w jakiś sposób obliczyć co ile pikeli mamy zwiększać wartość naszego koloru w zależności od całkowitej wysokości naszego obrazu.
Wykonany to poniższym sposobem:

  1. for($i=0; $i<$height; $i++)
  2.  
  3. {
  4.  
  5.   $color = floor($i * 255 / $height);
  6.  
  7.   $color = imagecolorallocate($image, $color, $color, $color);
  8.  
  9.   imageline($img, 0, $i, $width, $i, $color);
  10.  
  11. };
  12.  

Wartość koloru obliczamy algorytmem:

  1. współrzędnaY * 255 / całkowita_wysokość obrazka

Funkcja floor() zakokrągla nam wynik do najbliższej, najniższej wartości całkowitej, np. floor(3.14) da nam 3.

Zobaczmy teraz, czy to zadziała, np. dla obrazka o wymiarach 600x450px:

  1. <?php
  2.  
  3. $width = 600;
  4.  
  5. $height = 450;
  6.  
  7. $image = imagecreatetruecolor($width, $height);
  8.  
  9. $white = imagecolorallocate($image, 255, 255, 255);
  10.  
  11.  
  12.  
  13. for($i=0; $i<$height; $i++)
  14.  
  15. {
  16.  
  17.   $color = floor($i * 255 / $height);
  18.  
  19.   $color = imagecolorallocate($image, $color, $color, $color);
  20.  
  21.   imageline($image, 0, $i, $width, $i, $color);
  22.  
  23. };
  24.  
  25.  
  26.  
  27. imagestring($image, 5, 140, 210,  'RGB($color++, $color++, $color++)', $white);
  28.  
  29. header('Content-Type: image/png');
  30.  
  31. imagepng($image);
  32.  
  33. ?>
  34.  




Jak widać działa. Gradient rozciągnął się na całą wysokość obrazu.
Nasze linearne wypełnienie jest jednak trochę nudne prawda? Dodajmy mu trochę koloru.

Jak to zrobić? Otóż w chwii obecnej zwiększamy w naszej pętli wartości wszystkich składowych RGB jednocześnie, co za tym idzie nasz gradient generuje się liniowo od koloru czarnego (RGB = 0,0,0) do koloru białego (RGB = 255, 255, 255). Zostawmy więc np. wartości koloru zielonego i niebieskiego w spokoju, a zwiększajmy jedynie wartość składowej czerwonej:

  1. <?php
  2.  
  3. $width = 600;
  4.  
  5. $height = 450;
  6.  
  7. $image = imagecreatetruecolor($width, $height);
  8.  
  9. $white = imagecolorallocate($image, 255, 255, 255);
  10.  
  11.  
  12.  
  13. for($i=0; $i<$height; $i++)
  14.  
  15. {
  16.  
  17.   $color = floor($i * 255 / $height);
  18.  
  19.   $color = imagecolorallocate($image, $color, 0, 0);
  20.  
  21.   imageline($image, 0, $i, $width, $i, $color);
  22.  
  23. };
  24.  
  25.  
  26.  
  27. imagestring($image, 5, 200, 210,  'RGB($color++, 0, 0)', $white);
  28.  
  29. header('Content-Type: image/png');
  30.  
  31. imagepng($image);
  32.  
  33. ?>
  34.  

Wynik:

A teraz dla składowej zielonej:

  1. <?php
  2.  
  3. $width = 600;
  4.  
  5. $height = 450;
  6.  
  7. $image = imagecreatetruecolor($width, $height);
  8.  
  9. $white = imagecolorallocate($image, 255, 255, 255);
  10.  
  11.  
  12.  
  13. for($i=0; $i<$height; $i++)
  14.  
  15. {
  16.  
  17.   $color = floor($i * 255 / $height);
  18.  
  19.   $color = imagecolorallocate($image, 0, $color, 0);
  20.  
  21.   imageline($image, 0, $i, $width, $i, $color);
  22.  
  23. };
  24.  
  25.  
  26.  
  27. imagestring($image, 5, 200, 210,  'RGB(0, $color++, 0)', $white);
  28.  
  29. header('Content-Type: image/png');
  30.  
  31. imagepng($image);
  32.  
  33. imagedestroy($image);
  34.  
  35. ?>
  36.  



I niebieskiej:

  1. <?php
  2.  
  3. $width = 600;
  4.  
  5. $height = 450;
  6.  
  7. $image = imagecreatetruecolor($width, $height);
  8.  
  9. $white = imagecolorallocate($image, 255, 255, 255);
  10.  
  11.  
  12.  
  13. for($i=0; $i<$height; $i++)
  14.  
  15. {
  16.  
  17.   $color = floor($i * 255 / $height);
  18.  
  19.   $color = imagecolorallocate($image, 0, 0, $color);
  20.  
  21.   imageline($image, 0, $i, $width, $i, $color);
  22.  
  23. };
  24.  
  25.  
  26.  
  27. imagestring($image, 5, 200, 210,  'RGB(0, 0, $color++)', $white);
  28.  
  29. header('Content-Type: image/png');
  30.  
  31. imagepng($image);
  32.  
  33. imagedestroy($image);
  34.  
  35. ?>
  36.  



Jak widać, mamy tutaj duże pole do popisu odnośnie miksowania kolorów.
Warto też zauważyć, że tym sposobem możemy zabawić się również z przezroczystością modyfikujac liniowo wartość kanału alpha.

ImageColorAt() - pobieranie wartości koloru z piksela

Dzięki funkcji imagecolorat() możemy pobrać wartość koloru dla dowolnego piksela w obrazie. Zwracana wartość to indeks koloru w postaci liczby integer, musimy użyć przesunięć bitowych, aby uzyskać wartości poszczególnych składowych. Użycie funkcji przedstawia się następująco:

  1. $rgb = imagecolorat($image, $x, $y)

gdzie

  • $image - uchwyt do obrazka
  • $x - współrzędna X piksela
  • $y - współrzędna Y piksela


Wartość RGB pobierzemy tak:

  1. $image = imagecreatefromjpeg('angelina.jpg');
  2.  
  3. $rgb = imagecolorat($image, 10, 15);
  4.  
  5. $pixel['r'] = ($rgb >> 16) & 0xFF;
  6.  
  7. $pixel['g'] = ($rgb >> 8) & 0xFF;
  8.  
  9. $pixel['b'] = $rgb & 0xFF;
  10.  

Możemy stworzyć sobie do tego prostą funkcję:

  1. function getRgb($image, $x, $y)
  2.  
  3. {
  4.  
  5.   $rgb = imagecolorat($image, $x, $y);
  6.  
  7.   $pixel['r'] = ($rgb >> 16) & 0xFF;
  8.  
  9.   $pixel['g'] = ($rgb >> 8) & 0xFF;
  10.  
  11.   $pixel['b'] = $rgb & 0xFF;
  12.  
  13.   return $pixel;
  14.  
  15. }
  16.  

Sprawdźmy w praktyce jak to działa.
Wczytajmy zdjęcie z Angeliną, nad którym pastwiliśmy się w tym artykule i pobierzmy wartości kilku pixeli, po czym wyświetlmy te wartości na obrazku:

  1. <?php
  2.  
  3. function getRgb($image, $x, $y, $show = true)
  4.  
  5. {
  6.  
  7.   $white = imagecolorallocate($image, 255, 255, 255);
  8.  
  9.  
  10.  
  11.   $rgb = imagecolorat($image, $x, $y);
  12.  
  13.   $pixel['r'] = ($rgb >> 16) & 0xFF;
  14.  
  15.   $pixel['g'] = ($rgb >> 8) & 0xFF;
  16.  
  17.   $pixel['b'] = $rgb & 0xFF;
  18.  
  19.   // pobieramy wartości RGB do tablicy
  20.  
  21.  
  22.  
  23.   if($show)
  24.  
  25.   {
  26.  
  27.     imageellipse($image, $x, $y, 10, 10, $white);
  28.  
  29.     // rysujemy elipsę w miejscu badanego pixela
  30.  
  31.  
  32.  
  33.     $str = 'R:'.$pixel['r'].' G:'.$pixel['g'].' B:'.$pixel['b'];
  34.  
  35.     imagestring($image, 5, $x + 5, $y + 5,  $str, $white);
  36.  
  37.     // wyświetlamy wartość w miejscu badanego pixela
  38.  
  39.   }
  40.  
  41.   return $pixel; // zwracamy tablicę z wartościami RGB
  42.  
  43. }
  44.  
  45.  
  46.  
  47. $image = imagecreatefromjpeg("angelina.jpg");
  48.  
  49. $pixel_color[1] = getRgb($image, 100, 100);
  50.  
  51. $pixel_color[2] = getRgb($image, 400, 340);
  52.  
  53. $pixel_color[3] = getRgb($image, 130, 30);
  54.  
  55. // pobieramy RGB pixeli
  56.  
  57.  
  58.  
  59. header('Content-type: image/png');
  60.  
  61. ImagePNG($image);
  62.  
  63. imagedestroy($image);
  64.  
  65. ?>
  66.  

Wynik:


ImageSetPixel() - ustawianie wartości RGB piksela

Analogicznie do funkcji pobierającej dane o wartości piksela mozemy zadziałać w drugą stronę - ustawić wartości pikseli. Służy do tego omawiana funkcja, której użycie wygląda tak:

  1. imagesetpixel($image, $x, $y, $color)

gdzie:

  • $image - uchwyt do obrazka
  • $x - współrzędna X piksela
  • $y - współrzędna Y piksela
  • $color - kolor jaki ustawiamy


Przykładowo, zmieńmy wartości kilku pikseli w lewym górnym rogu obrazka.
Jest tam akurat czarne tło - zmienimy kilka pixeli na kolor biały.

  1. <?php
  2.  
  3. $image = imagecreatefromjpeg("angelina.jpg");
  4.  
  5. $white = imagecolorallocate($image, 255, 255, 255);
  6.  
  7.  
  8.  
  9. imagesetpixel($image, 20, 10, $white);
  10.  
  11. imagesetpixel($image, 30, 20, $white);
  12.  
  13. imagesetpixel($image, 4, 50, $white);
  14.  
  15. imagesetpixel($image, 50, 70, $white);
  16.  
  17. imagesetpixel($image, 10, 17, $white);
  18.  
  19. imagesetpixel($image, 44, 2, $white);
  20.  
  21. imagesetpixel($image, 80, 4, $white);
  22.  
  23. imagesetpixel($image, 65, 12, $white);
  24.  
  25.  
  26.  
  27. header('Content-type: image/png');
  28.  
  29. ImagePNG($image);
  30.  
  31. imagedestroy($image);
  32.  
  33. ?>
  34.  

Tym sposobem postawiliśmy na obrazie kilka małych "kropek":

Oczywiście takich modyfikacji nie wykonuje się ręcznie dla każdego piksela oddzielnie - do tego buduje się funkcje i pętle robiące to "hurtowo". Zaprezentowałem tutaj jedynie prosty przykład mający na celu pokazać, że funkcja taka istnieje i jak jej używać.

Przy wykorzystaniu faktu pobierania wartości dowolnych pikseli i ustawiania im własnych wartości można uzyskać naprawdę rozudowane możliwości. Zachęcam do eksperymentowania we własnym zakresie.

Brak komentarzy:

Prześlij komentarz

Masz sugestię? Znalazłeś błąd? Napisz komentarz! :)

webmaester.pl - profesjonalne projektowanie WWW i webaplikacji
webmaester.pl - profesjonalne projektowanie WWW i webaplikacji