5.7. Komponowanie obrazów

Prymitywy rysowania z poprzedniej strony nanoszą na obraz znaki geometryczne – linię, prostokąt, fragment tekstu. Pokrywa to większość adnotacji, które algorytm musi uwidocznić, ale nie wszystkie. Czasami adnotacja sama jest obrazem: przechwyconą ramką referencyjną wyświetlaną obok bieżącej, miniaturą wcześniejszego ujęcia pokazaną w rogu podglądu albo wcześniej zapisanym szablonem zwizualizowanym na żywej ramce do celów kalibracji. Mechanizmem rysowania jednego obrazu na drugim jest pojedyncza metoda – draw_image() – z wystarczającą liczbą parametrów, aby obsłużyć pozycję, skalowanie, paletę kolorów oraz przezroczystość, których wymaga prawdziwa kompozycja.

5.7.1. Podstawowe wywołanie

W najprostszej formie draw_image przyjmuje inny obiekt Image oraz pozycję, w której ma go narysować:

reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)

Celem jest img; źródłem jest reference; lewy górny piksel źródła trafia w punkt (10, 10) obrazu img, a pozostałe piksele źródła rozmieszczają się od tego miejsca w prawo i w dół. Piksele celu, które źródło zakrywa, zostają nadpisane odpowiadającymi pikselami źródła; piksele poza zasięgiem źródła pozostają nienaruszone.

Jeśli źródło wykracza poza krawędź celu, części, które wypadają poza obszar, są po cichu przycinane – to samo wyrozumiałe zachowanie, jakie set_pixel wykazuje wobec pozycji spoza zakresu. Kod aplikacji nie musi z góry ograniczać pozycji do wymiarów obrazu; może przekazać żądaną pozycję i pozostawić przycinanie metodzie.

5.7.2. Wczytywanie pliku w jednym wierszu

draw_image przyjmuje ścieżkę pliku w miejsce argumentu Image i wczytuje plik przed jego skomponowaniem:

img.draw_image("/sdcard/reference.bmp", x=10, y=10)

Wygląda to na udogodnienie – jeden wiersz zamiast dwóch – i tak jest, ale różnica wykracza poza składnię. Utworzenie obiektu Image z pliku alokuje bufor na zdekodowane piksele, a ten bufor istnieje aż do momentu, gdy odśmiecanie pamięci go zwolni. Przekazanie ścieżki bezpośrednio do draw_image pozwala modułowi zdekodować plik do bufora roboczego, skomponować z niego obraz i zwolnić bufor po zakończeniu wywołania, bez konieczności utrzymywania przez kod aplikacji odwołania do osobnego obiektu Image między ramkami.

5.7.3. Skalowanie

Gdy źródło i cel mają różne rozmiary – ujęcie o niskiej rozdzielczości komponowane na płótnie o wyższej rozdzielczości albo miniatura, której rozmiar musi stanowić określoną część ramki – dwa parametry skali odpowiadają za zmianę rozmiaru źródła podczas rysowania:

img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)

x_scale i y_scale to niezależne liczby zmiennoprzecinkowe; przekazanie obu o tej samej wartości skaluje równomiernie, a przekazanie różnych wartości rozciąga lub zwęża źródło wzdłuż jednej osi. Skalowanie odbywa się w czasie rysowania; samo źródło reference nie jest modyfikowane.

Maska bitowa flag hint decyduje o tym, jak skalowanie faktycznie interpoluje między pikselami. image.BILINEAR daje gładsze wyniki kosztem większej liczby obliczeń; image.BICUBIC daje jeszcze gładsze wyniki i kosztuje jeszcze więcej; domyślnie używana jest metoda najbliższego sąsiada, która jest najtańsza i właściwa wtedy, gdy źródło ma już rozdzielczość pikselową celu. Flagi obsługi proporcji – SCALE_ASPECT_KEEP, SCALE_ASPECT_EXPAND, SCALE_ASPECT_IGNORE – decydują o tym, co zrobić, gdy proporcje źródła nie pasują do prostokąta, w którym jest rysowane.

5.7.4. Mieszanie alfa

Domyślnie draw_image zastępuje piksele celu pikselami źródła. Gdy celem jest półprzezroczysta nakładka – tak aby cel prześwitywał przez źródło – parametr alpha steruje sposobem ich zmieszania. alpha=0 pokazuje wyłącznie cel (brak źródła); alpha=255 jest wartością domyślną i pokazuje wyłącznie źródło (pełne zastąpienie); wartości pośrednie mieszają oba proporcjonalnie:

img.draw_image(overlay, x=0, y=0, alpha=128)

Osobny argument alpha_palette to jedyny w module mechanizm alfa na poziomie pojedynczego piksela. Przyjmuje obraz GRAYSCALE, którego wartości są używane jako alfa w odpowiadającej pozycji w źródle – na przykład mapa cieplna, której alfa zmienia się wraz z jej intensywnością. Alfa musi być dostarczona jako ten osobny argument w skali szarości; obraz źródłowy zawierający własny kanał alfa (np. PNG z przezroczystością) nie przenosi go automatycznie.

5.7.5. Źródłowy ROI i paleta

Dwa kolejne parametry dopełniają mechanizm kompozycji:

  • roi=(x, y, w, h) ogranicza źródło do jego własnego podprostokąta, tak że tylko ten prostokąt zostaje skomponowany na celu. Przydatne do przycinania w obrębie tego samego wywołania, bez przygotowywania przyciętego obrazu pośredniego.

  • color_palette zastępuje wartość każdego piksela źródła przez tablicę przeglądową przed rysowaniem – to ten sam mechanizm, którego używają to_rainbow() i to_ironbow(), udostępniony tutaj, aby nakładka mogła zostać poddana paletyzacji w drodze na cel, bez osobnego przebiegu konwersji.

Oba komponują się ze wszystkim pozostałym w wywołaniu: ze skalowaniem, alfą, argumentem mask po stronie celu oraz parametrem roi po stronie celu, który ogranicza zapis do prostokąta celu.