Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Janusz Ganczarski
Biblioteki C++

Mapy pikselowe

[lekcja] Rozdział 11. Mapy pikselowe: rysowanie, odczyt, kopiowanie, skalowanie; transfer pikseli; przykładowy program odczytujący pliki TARGA.
O mapach pikselowych wspomnieliśmy już w poprzednim odcinku kursu. W odróżnieniu od map bitowych pozwalają one na przechowywanie znacznie większej ilości informacji o obrazie, w szczególności mapa pikselowa może zawierać dane o obrazie w formacie RGB i RGBA. Ponadto biblioteka OpenGL zawiera znacznie większe możliwości operacji na mapach pikselowych niż na mapach bitowych.

Rysowanie mapy pikselowej

Rysowanie mapy pikselowej w bieżącej pozycji rastra realizuje funkcja:
C/C++
void glDrawPixels( GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * pixels )
której parametry oznaczają odpowiednio:
  • width - szerokość mapy w pikselach,
  • height - wysokość mapy w pikselach,
  • format - format danych mapy pikselowej,
  • type - format pikseli mapy,
  • pixels - wskaźnik na dane mapy pikselowej.
Do określania pozycji rastra przy rysowaniu map pikselowych OpenGL wykorzystuje opisane już przy mapach bitowych funkcje z grup glRasterPos i glWindowPos. Dane mapy pikselowej domyślnie ułożone są podobnie jak dane mapy bitowej tj. kolejno w wierszach od najniższego do najwyższego. Obowiązują także te same reguły dotyczące wyrównywania i organizacji każdego wiersza danych, regulowane przez funkcje z grupy glPixelStore.
Funkcja glDrawPixels umożliwia transfer danych mapy pikselowej nie tylko do bufora koloru, ale także do buforów szablonu i głębokości. Format zapisywanych danych (parametr format) określają poniższe stałe:
  • GL_COLOR_INDEX - indeksy do mapy kolorów,
  • GL_STENCIL_INDEX - dane bufora szablonu,
  • GL_DEPTH COMPONENT - dane bufora głębokości,
  • GL_RED - wyłącznie składowe czerwone pikseli bufora koloru,
  • GL_GREEN - wyłącznie składowe zielone pikseli bufora koloru,
  • GL_BLUE - wyłącznie składowe niebieskie pikseli bufora koloru,
  • GL_ALPHA - wyłącznie składowe alfa pikseli bufora koloru,
  • GL_RGB - składowe RGB pikseli bufora koloru,
  • GL_RGBA - składowe RGBA pikseli bufora koloru,
  • GL_BGR - składowe BGR pikseli bufora koloru,
  • GL_BGRA - składowe BGRA pikseli bufora koloru,
  • GL_LUMINANCE - składowe L (odcienie szarości) pikseli bufora koloru,
  • GL_LUMINANCE_ALPHA - składowe LA (odcienie szarości i kanał alfa) pikseli bufora koloru.
Zapis do buforów głębokości i szablonu wymaga oczywiście ich obecności w buforze ramki. W przeciwnym wypadku biblioteka OpenGL wygeneruje błąd GL INVALID OPERATION. Taki sam błąd zostanie wygenerowany, jeżeli wystąpi niezgodność pomiędzy trybem pracy bufora kolorów (RGB/indeksowy a wartością parametru format.
Bardzo przydatne w praktyce formaty danych pikseli opisane stałymi
GL BGR i GL BGRA zostały wprowadzone w wersji 1.2 biblioteki OpenGL. Wcześniej tą samą funkcjonalność oferowało rozszerzenie EXT bgra, które definiowało stałe: GL BGR EXT i GL BGRA EXT. Warto także wspomnieć o rozszerzeniu EXT abgr, które dodaje kolejny format danych pikseli opisany stałą GL ABGR EXT, gdzie kolejność składowych RGBA jest odwrócona.
Obsługiwane formaty pikseli mapy pikselowej (parametr type funkcji glDrawPixels) przedstawia tabela 1.

Tabela 1: Formaty pikseli map pikselowych

parametrtyp danych OpenGL
GL_UNSIGNED_BYTEGLubyte
GL_BITMAPGLubyte
GL_BYTEGLbyte
GL_UNSIGNED_SHORTGLushort
GL_SHORTGLshort
GL_UNSIGNED_INTGLuint
GL_INTGLint
GL_FLOATGLfloat
GL_UNSIGNED_BYTE_3_3_2GLubyte
GL_UNSIGNED_BYTE_2_3_3_REVGLubyte
GL_UNSIGNED_SHORT_5_6_5GLushort
GL_UNSIGNED_SHORT_5_6_5_REVGLushort
GL_UNSIGNED_SHORT_4_4_4_4GLushort
GL_UNSIGNED_SHORT_4_4_4_4_REVGLushort
GL_UNSIGNED_SHORT_5_5_5_1GLushort
GL_UNSIGNED_SHORT_1_5_5_5_REVGLushort
GL_UNSIGNED_INT_8_8_8_8GLuint
GL_UNSIGNED_INT_8_8_8_8_REVGLuint
GL_UNSIGNED_INT_10_10_10_2GLuint
GL_UNSIGNED_INT_2_10_10_10_REVGLuint
Typowo składowe pikseli są w formacie GL_UNSIGNED_BYTE. Pozostałe formaty, umownie nazwijmy je liczbowymi (GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, GL_FLOAT), są stosunkowo rzadko wykorzystywane. Spowodowane jest m.in. tym, że jedynie nieliczne formaty plików graficznych pozwalają na przechowywanie danych w takich formatach. Także niewiele programów graficznych potrafi operować na tego rodzaju plikach graficznych.
Odrębny format składowych pikseli określa stała GL BITMAP. Piksel mapy bitowej jest w tym przypadku reprezentowany przez jeden bit i nie zawiera żadnych informacji o kolorze (piksele są czarne lub białe). Mapa pikselowa w tym formacie ma taką samą budowę jak mapa bitowa.
Osobną grupę stałych zawartych w tabeli 1 stanowią tzw. upakowane formaty pikseli RGB, RGBA i BGRA. Zostały one wprowadzone w wersji 1.2 biblioteki OpenGL, a wcześniej część upakowanych formatów pikseli definiowało rozszerzenie EXT packed pixels. Idea upakowanego formatu pikseli sprowadza się do umieszczenia składowych pikseli w formacie RGB, RGBA lub BGRA w ściśle określonej ilości bitów. Ilość użytych bitów oraz ilość składowych określa bezpośrednio nazwa stałej. Nazwa informuje jednocześnie jakiego rodzaju typ liczbowy jest używany do przechowywania całości informacji o pikselu. Elastyczność rozwiązania zwiększają formaty reprezentowane przez stałe z przyrostkiem REV, w których odwrócono kolejność składowych pikseli. Zestawienie dozwolonych kombinacji parametrów type i format dla upakowanych formatów pikseli przedstawia tabela 2.

Tabela 2: Upakowane formaty pikseli

parametrilość składowychformat pikseli
(parametr format)
GL_UNSIGNED_BYTE_3_3_23GL_RGB
GL_UNSIGNED_BYTE_2_3_3_REV3GL_RGB
GL_UNSIGNED_SHORT_5_6_53GL_RGB
GL_UNSIGNED_SHORT_5_6_5_REV3GL_RGB
GL_UNSIGNED_SHORT_4_4_4_44GL_RGBA, GL_BGRA
GL_UNSIGNED_SHORT_4_4_4_4_REV4GL_RGBA, GL_BGRA
GL_UNSIGNED_SHORT_5_5_5_14GL_RGBA, GL_BGRA
GL_UNSIGNED_SHORT_1_5_5_5_REV4GL_RGBA, GL_BGRA
GL_UNSIGNED_INT_8_8_8_84GL_RGBA, GL_BGRA
GL_UNSIGNED_INT_8_8_8_8_REV4GL_RGBA, GL_BGRA
GL_UNSIGNED_INT_10_10_10_24GL_RGBA, GL_BGRA
GL_UNSIGNED_INT_2_10_10_10_REV4GL_RGBA, GL_BGRA
Kolejność i ilość bitów przeznaczonych na każdą ze składowych RGB w pikselach formatu GL_UNSIGNED_BYTE_3_3_2 i GL_UNSIGNED_BYTE_3_3_2 - REV przedstawiają tabele 3 i 4. Warto zwrócić uwagę, że biblioteka OpenGL nie przewiduje umieszczenia w 8 bitowych danych pikseli w formacie GL_RGBA i GL_BGRA.

Tabela 3: Bity pikseli GL_UNSIGNED_BYTE_3_3_2

format piksela76543210
GL_RGBRRRGGGBB

Tabela 4: Bity pikseli GL_UNSIGNED_BYTE_2_3_3_REV

format piksela76543210
GL_RGBBBGGGRRR
W przypadku liczb 16-bitowych biblioteka OpenGL zawiera kilka różnych schematów upakowania składowych RGB, RGBA i BGRA pikseli. Różnice polegają przede wszystkim na ilości bitów przeznaczonych na poszczególne składowe. Całość przedstawiają tabele: 5, 6, 7, 8, 9 i 10.

Tabela 5: Bity pikseli GL_UNSIGNED_SHORT_5_6_5

format piksela15 - 1110 - 54 - 0
GL_RGBRGB

Tabela 6: Bity pikseli GL_UNSIGNED_SHORT_5_6_5_REV

format piksela15 - 1110 - 54 - 0
GL_RGBBGR

Tabela 7: Bity pikseli GL_UNSIGNED_SHORT_4_4_4_4

format piksela15 - 1211 - 67 - 43 - 0
GL_RGBARGBA
GL_BGRABGRA

Tabela 8: Bity pikseli GL_UNSIGNED_SHORT_4_4_4_4_REV

format piksela15 - 1211 - 67 - 43 - 0
GL_RGBAABGR
GL_BGRAARGB

Tabela 9: Bity pikseli GL_UNSIGNED_SHORT_5_5_5_1

format piksela15 - 1110 - 65 - 10
GL_RGBARGBA
GL_BGRABGRA

Tabela 10: Bity pikseli GL_UNSIGNED_SHORT_1_5_5_5_REV

format piksela1514 - 109 - 54 - 0
GL_RGBAABGR
GL_BGRAARGB
Ostatnia grupa schematów upakowania składowych RGBA i BGRA pikseli wykorzystuje liczby 32-bitowe. Warto zauważyć, że schematy przedstawione w tabelach 11 i 12 odpowiadają typowym danym zawartym w plikach graficznych. Zdecydowanie mniej popularne są natomiast schematy opisane w tabelach 13 i 14.

Tabela 11: Bity pikseli GL_UNSIGNED_INT_8_8_8_8

format piksela31 - 2423 - 1615 - 87 - 0
GL_RGBARGBA
GL_BGRABGRA

Tabela 12: Bity pikseli GL_UNSIGNED_INT_8_8_8_8_REV

format piksela31 - 2423 - 1615 - 87 - 0
GL_RGBAABGR
GL_BGRAARGB

Tabela 13: Bity pikseli GL_UNSIGNED_INT_10_10_10_2

format piksela31 - 2221 - 1211 - 21 - 0
GL_RGBARGBA
GL_BGRABGRA

Tabela 14: Bity pikseli GL_UNSIGNED_INT_2_10_10_10_REV

format piksela31 - 3029 - 2019 - 109 - 0
GL_RGBAABGR
GL_BGRAARGB

Wybór docelowego bufora kolorów

Domyślnie jeżeli bufor ramki zawiera pojedynczy bufor koloru zapis pikseli mapy dokonywany jest w przednim buforze (GL FRONT). W trybie z podwójnym buforowaniem zapis dokonywany jest domyślnie w tylnym buforze koloru (GL BACK). Selektywny wybór docelowego bufora kolorów (lub bufora pomocniczego) umożliwia funkcja:
C/C++
void glDrawBuffer( GLenum mode )
której parametr mode przyjmuje jedną z poniższych wartości:
  • GL_NONE - wyłączenie rysowania w buforze koloru,
  • GL_FRONT - rysowanie w przednim (widocznym) buforze,
  • GL_BACK - rysowanie w tylnym (niewidocznym) buforze,
  • GL_FRONT_AND_BACK - rysowanie w przednim i tylnym buforze; bufor ramki musi posiadać podwójne buforowanie,
  • GL_LEFT - rysowanie w lewym buforze stereoskopowym; w przypadku podwójnego buforowania rysowanie następuje w przednim i tylnym buforze; bufor ramki musi oczywiście posiadać bufory stereoskopowe,
  • GL_RIGHT - rysowanie w prawym buforze stereoskopowym; w przypadku podwójnego buforowania rysowanie następuje w przednim i tylnym buforze; bufor ramki musi oczywiście posiadać bufory stereoskopowe,
  • GL_BACK_LEFT - rysowanie w lewym tylnym buforze stereoskopowym; bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_BACK_RIGHT - rysowanie w prawym tylnym buforze stereoskopowym;
  • bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_FRONT_LEFT - rysowanie w lewym przednim buforze stereoskopowym;
  • bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_FRONT_RIGHT - rysowanie w prawym przednim buforze stereoskopowym; bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_AUXi - rysowanie w pomocniczym i-tym buforze kolorów; ilość pomocniczych buforów kolorów zależy od implementacji biblioteki OpenGL; wartość i należy do przedziału [0, GL_AUX_BUFFERS − 1]; standardowo dostępne są predefiniowane stałe: GL_AUX0, GL_AUX1, GL_AUX2 i GL - AUX3.
Tabela 15 przedstawia docelowe bufory kolorów dla poszczególnych wartości parametru mode funkcji glDrawBuffer. Zauważmy, że w przypadku dostępności podwójnych buforów stereoskopowych stałe GL FRONT, GL BACK, GL LEFT, GL RIGHT i GL FRONT AND BACK oznaczają więcej niż jeden docelowy bufor kolorów. Gdy dana implementacja udostępnia jedynie monoskopowe bufory kolorów, zapis dokonywany jest w lewym (lewych) buforach koloru.

Tabela 15: Docelowe bufory kolorów

buforprzedni
lewy
bufor
przedni
prawy
bufor
tylny
lewy
bufor
tylny
prawy
bufor
i-ty
bufor
pomocniczy
GL_NONE
GL_FRONT_LEFT+
GL_FRONT_RIGHT+
GL_BACK_LEFT+
GL_BACK_RIGHT+
GL_FRONT++
GL_BACK++
GL_LEFT++
GL_RIGHT++
GL_FRONT AND BACK++++
GL_AUX0+
GL_AUX1+
GL_AUX2+
GL_AUX3+

Odczyt mapy pikselowej

Odczyt mapy pikselowej do bufora pamięci realizuje funkcja:
C/C++
void glReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * pixels )
której parametry oznaczają:
  • x, y - współrzędne położenia lewego dolnego rogu odczytywanej mapy,
  • width - szerokość mapy w pikselach,
  • height - wysokość mapy w pikselach,
  • format - format danych mapy pikselowej,
  • type - format pikseli mapy,
  • pixels - wskaźnik na bufor mapy pikselowej.
Parametry format i type przyjmują takie same wartości jak analogiczne parametry funkcji glDrawPixels. Oczywiście bufor, na który wskazuje parametr pixels musi być wystarczająco duży aby pomieścić wszystkie dane odczytanej mapy pikselowej.
Podobnie jak w przypadku funkcji glDrawPixels, odczyt z buforów głębokości i szablonu wymaga ich obecności w buforze ramki. W przeciwnym wypadku biblioteka OpenGL wygeneruje błąd GL INVALID OPERATION. Taki sam błąd zostanie wygenerowany, jeżeli wystąpi niezgodność pomiędzy trybem pracy bufora kolorów (indeksowy/RGB) a wartością parametru format.
Domyślnie jeżeli bufor koloru jest pojedynczy odczyt pikseli mapy jest dokonywany z przedniego bufora (GL FRONT). W trybie z podwójnym buforowaniem odczyt dokonywany jest domyślnie z tylnego bufora koloru (GL BACK). Selektywny wybór źródłowego bufora kolorów (lub bufora pomocniczego) umożliwia funkcja:
  • void glReadBuffer (GLenum mode)
której parametr mode przyjmuje jedną z poniższych wartości:
  • GL_FRONT_LEFT - odczyt z lewego przedniego bufora stereoskopowego;
  • bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_FRONT_RIGHT - odczyt z prawego przedniego bufora stereoskopowego;
  • bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_BACK_LEFT - odczyt z lewego tylnego bufora stereoskopowego; bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_BACK_RIGHT - odczyt z prawego tylnego bufora stereoskopowego; bufor ramki musi oczywiście posiadać podwójne bufory stereoskopowe,
  • GL_FRONT - odczyt z przedniego (widocznego) bufora,
  • GL_BACK - odczyt z tylnego (niewidocznego) bufora,
  • GL_LEFT - odczyt z lewego bufora stereoskopowego; bufor ramki musi oczywiście posiadać bufory stereoskopowe,
  • GL_RIGHT - odczyt z prawego bufora stereoskopowego; bufor ramki musi oczywiście posiadać bufory stereoskopowe,
  • GL_AUXi - odczyt z pomocniczego i-tego bufora kolorów; ilość pomocniczych buforów kolorów zależy od implementacji biblioteki OpenGL; wartość i należy do przedziału [0, GL_AUX_BUFFERS − 1]; standardowo dostępne są predefiniowane stałe: GL_AUX0, GL_AUX1, GL_AUX2 i GL_AUX3.

Kopiowanie mapy pikselowej

Kopiowanie prostokątnego obszaru z jednego obszaru bufora ramki do drugiego realizuje funkcja:
C/C++
void glCopyPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum type )
Parametry x i y oznaczają współrzędne lewego dolnego wierzchołka kopiowanego obszaru, a width i height odpowiednio jego szerokość i wysokość. Parametr type określa bufor, z którego mają być kopiowane dane:
  • GL_COLOR - bufor koloru,
  • GL_DEPTH - bufor głębokości,
  • GL_STENCIL - bufor szablonu.
W przypadku, gdy bufor ramki zawiera pojedynczy bufor koloru kopiowanie mapy realizowane jest w przednim buforze (GL_FRONT). W trybie z podwójnym buforowaniem kopiowanie realizowane jest domyślnie w tylnym buforze koloru (GL_BACK). Selektywny wybór bufora kolorów (lub bufora pomocniczego) umożliwia opisana wyżej funkcja glReadBuffer.
Kopiowanie z buforów głębokości i szablonu wymaga ich obecności w buforze ramki. W przeciwnym wypadku biblioteka OpenGL wygeneruje błąd GL_INVALID_OPERATION. Docelowe położenie kopiowanej mapy pikselowej określa bieżąca pozycja rastra wyznaczana za pomocą funkcji z grupy glRasterPos i glWindowPos.

Skalowanie mapy pikselowej

Współczynnik skalowania mapy pikselowej podczas odczytu, zapisu i kopiowania określa funkcja:
C/C++
void glPixelZoom( GLfloat xfactor, GLfloat yfactor )
Parametr xfactor zawiera współczynnik skalowania mapy pikselowej w poziomie a yfactor współczynnik skalowania w pionie. Oba argumenty mogą przyjmować wartości ujemne, co powoduje zmianę orientacji współrzędnych położenia rastra. Domyślnie współrzędne położenia rastra odpowiadają lewemu dolnemu wierzchołkowi mapy pikselowej. Przykładowo użycie ujemnej wartości parametru yfactor, przy dodatniej wartości xfactor oznacza, że mapa bitowa rysowana jest „do góry nogami”, a pozycja rastra odpowiada lewemu górnemu wierzchołkowi mapy.
Do skalowania danych mapy pikselowej biblioteka OpenGL używa prostego i bardzo szybkiego algorytmu najbliższego sąsiada. Algorytm ten daje najlepsze rezultaty, gdy współczynniki skalowania mają wartości całkowite.

Transfer pikseli

Obsługę map pikselowych w bibliotece OpenGL uzupełniają funkcje wykonujące podstawowe operacje na obrazie tj. skalowanie i przesuwanie wartości składowych pikseli. Operacje te wykonywane są w trakcie transferu pikseli m.in. przy użyciu funkcji glCopyPixels, glDrawPixels i glReadPixels, a realizują to funkcje z grupy glPixelTransfer:
C/C++
void glPixelTransferf( GLenum pname, GLfloat param )
void glPixelTransferi( GLenum pname, GLint param )
Parametr pname określa tryb transferu pikseli czyli wykonywane na składowych pikseli operacje. Przyjmuje on jedną z poniższych wartości:
  • GL_MAP_COLOR - parametr param określa czy do przekształceń składowych pikseli zostanie zastosowana mapa przekształceń (opis dalej),
  • GL_MAP_STENCIL - parametr param określa czy do przekształceń elementów bufora szablonu zastosowana mapa przekształceń (opis dalej),
  • GL_INDEX_SHIFT - parametr param określa wartość przesunięcia bitowego indeksu koloru; wartość dodatnia oznacza przesunięcie w prawo, wartość ujemna przesunięcie w lewo,
  • GL_INDEX_OFFSET - parametr param zawiera wartość przesunięcia (arytmetycznego) indeksu koloru,
  • GL_RED_SCALE - parametr param określa współczynnik skalowania składowych R pikseli,
  • GL_RED_BIAS - parametr param określa wartość przesunięcia składowych R pikseli,
  • GL_GREEN_SCALE - parametr param określa współczynnik skalowania składowych G pikseli,
  • GL_GREEN_BIAS - parametr param określa wartość przesunięcia składowych G pikseli,
  • GL_BLUE_SCALE - parametr param określa współczynnik skalowania składowych B pikseli,
  • GL_BLUE_BIAS - parametr param określa wartość przesunięcia składowych B pikseli,
  • GL_ALPHA_SCALE - parametr param określa współczynnik skalowania składowych A pikseli,
  • GL_ALPHA_BIAS - parametr param określa wartość przesunięcia składowych A pikseli,
  • GL_DEPTH_SCALE - parametr param określa współczynnik skalowania elementów bufora głębokości,
  • GL_DEPTH_BIAS - parametr param określa wartość przesunięcia elementów bufora głębokości.
Tabela 16 zawiera zestawienie wartości początkowych oraz zakresy dopuszczalnych wartości funkcji glPixelTransferf i glPixelTransferi. Wartości początkowe współczynników skalowania i wartości przesunięcia są neutralne dla wartości składowych pikseli.

Tabela 16: Parametry funkcji z grupy glPixelTransfer

parametr pnametyp OpenGLwartość początkowazakres wartości
GL_MAP_COLORGLbooleanGL_FALSEGL_TRUE, GL_FALSE
GL_MAP_STENCILGLbooleanGL_FALSEGL_TRUE, GL_FALSE
GL_INDEX_SHIFTGLint0[−∞, +∞]
GL_INDEX_OFFSETGLint0[−∞, +∞]
GL_RED_SCALEGLfloat1[−∞, +∞]
GL_GREEN_SCALEGLfloat1[−∞, +∞]
GL_BLUE_SCALEGLfloat1[−∞, +∞]
GL_ALPHA_SCALEGLfloat1[−∞, +∞]
GL_DEPTH_SCALEGLfloat1[−∞, +∞]
GL_RED_BIASGLfloat0[−∞, +∞]
GL_GREEN_BIASGLfloat0[−∞, +∞]
GL_BLUE_BIASGLfloat0[−∞, +∞]
GL_ALPHA_BIASGLfloat0[−∞, +∞]
GL_DEPTH_BIASGLfloat0[−∞, +∞]
Mapę (tablicę) przekształceń elementów mapy pikselowej określają funkcje z grupy glPixelMap:
C/C++
void glPixelMapfv( GLenum map, GLint mapsize, const GLfloat * values )
void glPixelMapuiv( GLenum map, GLint mapsize, const GLuint * values )
void glPixelMapusv( GLenum map, GLint mapsize, const GLushort * values )
Parametr map określa rodzaj definiowanej mapy przekształceń i przyjmuje jedną z poniższych wartości:
  • GL_PIXEL_MAP_I_TO_I - mapa przekształceń indeksów kolorów,
  • GL_PIXEL_MAP_S_TO_S - mapa przekształceń wartości bufora szablonu,
  • GL_PIXEL_MAP_I_TO_R - mapa przekształceń indeksu koloru do składowej R piksela,
  • GL_PIXEL_MAP_I_TO_G - mapa przekształceń indeksu koloru do składowej G piksela,
  • GL_PIXEL_MAP_I_TO_B - mapa przekształceń indeksu koloru do składowej B piksela,
  • GL_PIXEL_MAP_I_TO_A - mapa przekształceń indeksu koloru do składowej A piksela,
  • GL_PIXEL_MAP_R_TO_R - mapa przekształceń składowej R piksela,
  • GL_PIXEL_MAP_G_TO_G - mapa przekształceń składowej G piksela,
  • GL_PIXEL_MAP_B_TO_B - mapa przekształceń składowej B piksela,
  • GL_PIXEL_MAP_A_TO_A - mapa przekształceń składowej A piksela.
Natomiast parametr mapsize określa ilość elementów mapy przekształceń, do której wskaźnik zawiera parametr values. Wielkość map przekształceń związanych z indeksami kolorów oraz wartościami bufora szablonu musi być potęgą liczby 2. W przeciwnym wypadku rezultat przekształceń jest nieokreślony a biblioteka OpenGL generuje błąd GL INVALID VALUE. Wielkość każdej mapy przekształceń jest określana indywidualnie, ale nie może być większa niż wartość zwrócona przez funkcję z grupy glGet z parametrem GL MAX PIXEL MAP TABLE. Początkowo rozmiar każdej z map przekształceń wynosi 1.

Aktualny rozmiar wybranej mapy przekształceń zwraca funkcja z grupy glGet z odpowiednim parametrem: GL PIXEL MAP I TO I SIZE, GL PIXEL - MAP S TO S SIZE, GL PIXEL MAP I TO R SIZE, GL PIXEL MAP I TO G SIZE, GL PIXEL MAP I TO B SIZE, GL PIXEL MAP I TO A SIZE, GL PIXEL MAP R - TO R SIZE, GL PIXEL MAP G TO G SIZE, GL PIXEL MAP B TO B SIZE, GL PIXEL MAP A TO A SIZE.
Odczyt skłądowych mapy przekształceń umożliwiają funkcje z grupy glGetPixelMap:
C/C++
void glGetPixelMapfv( GLenum map, GLfloat * values ) void glGetPixelMapuiv( GLenum map, GLuint * values ) void glGetPixelMapusv( GLenum map, GLushort * values )
których parametr map przyjmuje takie same wartości jak parametr map funkcji z grupy glPixelMap. Wartości elementów mapy przekształceń umieszczane są do tablicy przekazywanej w parametrze values. Oczywiście program musi zapewnić odpowiednią wielkość tej tablicy.
Czytelnik znający podstawowe techniki cyfrowego przetwarzania obrazów zauważył z pewnością podobieństwo map przekształceń do tablic LUT (ang. LookUp Table). Jest to to faktycznie ta sama technika. W wersji 1.2 biblioteki OpenGL dodano opcjonalny zbiór funkcji odpowiedzialnych za przetwarzanie obrazów (ang. imaging subset), który wcześniej opisany był w rozszerzeniu ARB imaging. W jego skład wchodzą zarówno zupełnie nowe funkcje jak też i rozszerzenia wcześniejszych funkcji (m.in. opisywanych funkcji z grupy glPixelTransfer). Z uwagi na swoją obszerność funkcje te zostaną omówione oddzielnie.

Przykładowy program

Przykładowy program (plik targa view.cpp pozwala na odczyt i zapis plików graficznych w formacie TARGA oraz proste operacje na obrazie odczytanym z pliku. Wybór tego formatu pliku graficznego jest nieprzypadkowy. Pliki TARGA mają bardzo prostą budowę i standardowo przechowują dane obrazu w postaci nieskompresowanej. Opcjonalnie w plikach TARGA można wykorzystywać algorytm kompresji długości serii RLE (ang. Run-Length Encoding). Czytelnika zainteresowanego budową pliku TARGA odsyłam do oficjalnej specyfikacji zawartej w pracy [13].
Niestety biblioteka OpenGL ani opisywane biblioteki narzędziowe nie zawierają funkcji obsługujących pliki graficzne. Jedynie nieprzedstawiana bliżej, a wspomniana na początku kursu biblioteka AUX, poprzedniczka biblioteki GLUT, posiada dwie funkcje pozwalające na odczyt plików w formacie BMP (DIB) i SGI RGB:
C/C++
AUX_RGBImageRec * auxDIBImageLoad( const char * filename )
AUX_RGBImageRec * auxRGBImageLoad( const char * filename )
których parametr filename oznacza nazwę wczytywanego pliku. Obie funkcje zwracają wskaźnik do struktury AUX RGBImageRec, która posiada następujące pola:
  • sizeX - szerokość wczytanej mapy bitowej,
  • sizeY - wysokość wczytanej mapy bitowej,
  • data - wskaźnik na bufor z danymi mapy bitowej.
Funkcje obsługujące grafikę w formacie TARGA zawarte są w plikach targa.h i targa.cpp. Pierwsza z dostępnych funkcji służy do odczytu pliku:
C/C++
GLboolean load_targa( const char * filename, GLsizei & width, GLsizei & height, GLenum & format, GLenum & type, GLvoid *& pixels )
Parametr filename zawiera oczywiście nazwę odczytywanego pliku, a pozostałe parametry są dokładnym odpowiednikiem parametrów funkcji glDrawPixels. Druga z dostępnych funkcji służy do zapisu plików TARGA:
C/C++
GLboolean save_targa( const char * filename, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * pixels )
Jej parametry są takie same jak parametry funkcji load targa. Obie funkcje zwracają wartość GL TRUE w przypadku pomyślnego wykonania operacji odczytu/zapisu pliku lub GL FALSE w przypadku błędu. W prezentowanej postaci funkcje obsługują wyłącznie nieskompresowane pliki TARGA z wyłączeniem plików posiadających mapę kolorów. Czytelnik może oczywiście rozszerzyć ich funkcjonalność stosownie do swoich potrzeb.
Poza możliwością odczytu i wyświetlenia zawartości pliku graficznego w formacie TARGA przykładowy program pozwala na dwie proste operacje na obrazie: negatyw i konwersję obrazu RGB do odcieni szarości. Przy konwersji do odcieni szarości wykorzystywane jest przekształcenie składowych RGB pikseli określone wzorem:
0.229*R + 0.587*G + 0.114*B
które realizowane jest poprzez odpowiednie skalowanie składowych pikseli:
C/C++
glPixelTransferf( GL_RED_SCALE, 0.229 );
glPixelTransferf( GL_GREEN_SCALE, 0.587 );
glPixelTransferf( GL_BLUE_SCALE, 0.114 );
Po dokonaniu skalowania składowych pikseli konieczny jest powrót do przekształcenia neutralnego:
C/C++
glPixelTransferf( GL_RED_SCALE, 1.0 );
glPixelTransferf( GL_GREEN_SCALE, 1.0 );
glPixelTransferf( GL_BLUE_SCALE, 1.0 );
Całość operacji zawiera funkcja Menu. Wygląd obrazu testowego „Lena” (rysunek 1) w odcieniach szarości przedstawia rysunek 2.
Rysunek 1. Plik testowy „Lena”
Rysunek 1. Plik testowy „Lena”
Przy tworzeniu negatywu obrazu wykorzystywana jest mapa przekształceń (tablica inverse map), która zawiera kolejnych 256 wartości składowych pikseli, przy czym pierwszy jej element wynosi 1, a ostatni 0. Zawartość tablicy tworzy poniższy kod:
C/C++
GLfloat inverse_map[ 256 ];
inverse_map[ 0 ] = 1.0;
for( int i = 1; i < 256; i++ )
     inverse_map[ i ] = 1.0 - 1.0 /( GLfloat ) 255 * i;

Rysunek 2. Plik testowy „Lena” w odcieniach szarości
Rysunek 2. Plik testowy „Lena” w odcieniach szarości
Po utworzeniu mapy przekształceń należy ustalić które składowe będą podlegały transformacji:
C/C++
glPixelMapfv( GL_PIXEL_MAP_R_TO_R, 256, inverse_map );
glPixelMapfv( GL_PIXEL_MAP_G_TO_G, 256, inverse_map );
glPixelMapfv( GL_PIXEL_MAP_B_TO_B, 256, inverse_map );
oraz określić tryb transferu pikseli:
C/C++
glPixelTransferi( GL_MAP_COLOR, GL_TRUE );
Po zakończeniu kopiowania pikseli należy wyłączyć tryb transferu pikseli z użyciem mapy przekształceń:
C/C++
glPixelTransferi( GL_MAP_COLOR, GL_FALSE );
Całość operacji generujących negatyw obrazu zawiera funkcja Menu. Wygląd negatywu obrazu testowego „Lena” zawiera rysunek 3), a negatyw tego samego obrazu w odcieniach szarości przedstawia rysunek 4.
Rysunek 3. Plik testowy „Lena” (negatyw)
Rysunek 3. Plik testowy „Lena” (negatyw)
Kolejną cechą programu testowego jest automatyczne dopasowanie rozmiarów rysowanego obrazu do rozmiarów okna, przy czym wielkość początkowa okna programu odpowiada wielkości obrazu zawartego we wczytanym pliku TARGA. Powiększanie obrazu wykonywane jest w funkcji Display:
C/C++
glPixelZoom(( float ) glutGet( GLUT_WINDOW_WIDTH ) /( float ) width,( float ) glutGet( GLUT_WINDOW_HEIGHT ) /( float ) height );
gdzie width i height to zmienne globalne zawierające rozmiary obrazu odczytanego z pliku TARGA.
Rysunek 4. Plik testowy „Lena” w odcieniach szarości (negatyw)
Rysunek 4. Plik testowy „Lena” w odcieniach szarości (negatyw)
Poza tradycyjnym sposobem dowolnej zmiany rozmiarów okna programu przy pomocy myszki, użytkownik ma także możliwość wyboru kilku domyślnych wielkości tego okna. Są to powiększenie dwu i czterokrotne oraz rozmiar odpowiadający rozmiarom obrazu zawartego ww wczytanym pliku graficznym. Wybór rozmiaru okna programu w dowolnym momencie umożliwia funkcja biblioteki GLUT:
C/C++
void glutReshapeWindow( int width, int height )
której parametry width i height określają odpowiednio nową szerokość i wysokość okna.
Ciekawym efektem zaimplementowanym w programie jest podręczna „lupa” powiększająca fragment obrazu wskazany lewym przyciskiem myszki. Przykład działania „lupy” przedstawia rysunek 5. Efekt powiększenia wybranego fragmentu obrazu wykorzystuje funkcję glCopyPixels w połączeniu ze skalowaniem pikseli za pomocą funkcji glPixelZoom. Całość jest zawarta w funkcji ZoomImage.
Rysunek 5. Program targa view - efekt powiększenia fragmentu obrazu
Rysunek 5. Program targa view - efekt powiększenia fragmentu obrazu
Do prawidłowego działania programu wymagana jest obsługa rozszerzenia EXT bgra lub co najmniej wersja 1.3 biblioteki OpenGL. Sprawdzenie tego odbywa się w funkcji CheckImageFormat programu. Dlatego też moduł obsługujący pliki graficzne TARGA do kompilacji wymaga pliku nagłówkowego glext.h. Jako ćwiczenie może Czytelnik przygotować wersję programu, która jest niezależna od wersji biblioteki OpenGL.

Plik targa.h

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#ifndef __TARGA__H__
#define __TARGA__H__

#include <GL/gl.h>

// odczyt pliku graficznego w formacie TARGA
// filename - nazwa pliku
// width - szerokość obrazu
// height - wysokość obrazu
// format - format danych obrazu
// type - format danych pikseli obrazu
// pixels - wskaźnik na tablicę z danymi obrazu

GLboolean load_targa( const char * filename, GLsizei & width, GLsizei & height,
GLenum & format, GLenum & type, GLvoid *& pixels );

// zapis pliku graficznego w formacie TARGA
// filename - nazwa pliku
// width - szerokość obrazu
// height - wysokość obrazu
// format - format danych obrazu
// type - format danych pikseli obrazu
// pixels - wskaźnik na tablicę z danymi obrazu

GLboolean save_targa( const char * filename, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid * pixels );

#endif // __TARGA__H__

Plik targa.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include "targa.h"
#include <GL/glext.h>
#include <stdio.h>
#include <string.h>

// stałe używane przy obsłudze plików TARGA:

// rozmiar nagłówka pliku
#define TARGA_HEADER_SIZE 0x12

// nieskompresowany obraz RGB(A)
#define TARGA_UNCOMP_RGB_IMG 0x02

// nieskompresowany obraz w odcieniach szarości
#define TARGA_UNCOMP_BW_IMG 0x03

// odczyt pliku graficznego w formacie TARGA
// filename - nazwa pliku
// width - szerokość obrazu
// height - wysokość obrazu
// format - format danych obrazu
// type - format danych pikseli obrazu
// pixels - wskaźnik na tablicę z danymi obrazu

GLboolean load_targa( const char * filename, GLsizei & width, GLsizei & height,
GLenum & format, GLenum & type, GLvoid *& pixels )
{
    // początkowe wartości danych wyjściowych
    pixels = NULL;
    width = 0;
    height = 0;
   
    // otwarcie pliku do odczytu
    FILE * tga = fopen( filename, "rb" );
   
    // sprawdzenie poprawności otwarcia pliku
    if( !tga )
         return GL_FALSE;
   
    // tablica na nagłówek pliku TGA
    unsigned char header[ TARGA_HEADER_SIZE ];
   
    // odczyt nagłówka pliku
    fread( header, TARGA_HEADER_SIZE, 1, tga );
   
    // ominięcie pola ImageID
    fseek( tga, header[ 0 ], SEEK_CUR );
   
    // szerokość obrazu
    width = header[ 12 ] +( header[ 13 ] << 8 );
   
    // wysokość obrazu
    height = header[ 14 ] +( header[ 15 ] << 8 );
   
    // obraz w formacie BGR - 24 bity na piksel
    if( header[ 2 ] == TARGA_UNCOMP_RGB_IMG && header[ 16 ] == 24 )
    {
        pixels = new unsigned char[ width * height * 3 ];
        fread(( void * ) pixels, width * height * 3, 1, tga );
        format = GL_BGR;
        type = GL_UNSIGNED_BYTE;
    }
    else
   
    // obraz w formacie BGRA - 32 bity na piksel
    if( header[ 2 ] == TARGA_UNCOMP_RGB_IMG && header[ 16 ] == 32 )
    {
        pixels = new unsigned char[ width * height * 4 ];
        fread(( void * ) pixels, width * height * 4, 1, tga );
        format = GL_BGRA;
        type = GL_UNSIGNED_BYTE;
    }
    else
   
    // obraz w odcieniach szarości - 8 bitów na piksel
    if( header[ 2 ] == TARGA_UNCOMP_BW_IMG && header[ 16 ] == 8 )
    {
        pixels = new unsigned char[ width * height ];
        fread(( void * ) pixels, width * height, 1, tga );
        format = GL_LUMINANCE;
        type = GL_UNSIGNED_BYTE;
    }
    else
         return GL_FALSE;
   
    // zamknięcie pliku
    fclose( tga );
   
    // sukces
    return GL_TRUE;
}

// zapis pliku graficznego w formacie TARGA
// filename - nazwa pliku
// width - szerokość obrazu
// height - wysokość obrazu
// format - format danych obrazu
// type - format danych pikseli obrazu
// pixels - wskaźnik na tablicę z danymi obrazu

GLboolean save_targa( const char * filename, GLsizei width, GLsizei height,
GLenum format, GLenum type, GLvoid * pixels )

{
    // sprawdzenie formatu danych obrazu
    if( format != GL_BGR && format != GL_BGRA && format != GL_LUMINANCE )
         return GL_FALSE;
   
    // sprawdzenie formatu pikseli obrazu
    if( type != GL_UNSIGNED_BYTE )
         return GL_FALSE;
   
    // otwarcie pliku do zapisu
    FILE * tga = fopen( filename, "wb" );
   
    // sprawdzenie poprawności otwarcia pliku
    if( tga == NULL )
         return GL_FALSE;
   
    // nagłówek pliku TGA
    unsigned char header[ TARGA_HEADER_SIZE ];
   
    // wyzerowanie pól nagłówka
    memset( header, 0, TARGA_HEADER_SIZE );
   
    // pole Image Type
    if( format == GL_BGR || format == GL_BGRA )
         header[ 2 ] = TARGA_UNCOMP_RGB_IMG;
    else
    if( format == GL_LUMINANCE )
         header[ 2 ] = TARGA_UNCOMP_BW_IMG;
   
    // pole Width
    header[ 12 ] =( unsigned char ) width;
    header[ 13 ] =( unsigned char )( width >> 8 );
   
    // pole Height
    header[ 14 ] =( unsigned char ) height;
    header[ 15 ] =( unsigned char )( height >> 8 );
   
    // pole Pixel Depth
    if( format == GL_BGRA )
         header[ 16 ] = 32;
    else
    if( format == GL_BGR )
         header[ 16 ] = 24;
    else
    if( format == GL_LUMINANCE )
         header[ 16 ] = 8;
   
    // zapis nagłówka pliku TARGA
    fwrite( header, TARGA_HEADER_SIZE, 1, tga );
   
    // zapis danych obrazu
    if( format == GL_BGRA )
         fwrite( pixels, width * height * 4, 1, tga );
    else
    if( format == GL_BGR )
         fwrite( pixels, width * height * 3, 1, tga );
    else
    if( format == GL_LUMINANCE )
         fwrite( pixels, width * height, 1, tga );
   
    // zamknięcie pliku
    fclose( tga );
   
    // sukces
    return GL_TRUE;
}

Plik targa_view.cpp

C/C++
/*
(c) Janusz Ganczarski
http://www.januszg.hg.pl
JanuszG@enter.net.pl
*/

#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
#include <GL/glext.h>
#include "targa.h"

// mapa przekształceń do utworzenia negatywu obrazu

GLfloat inverse_map[ 256 ] =
{
    1.000000, 0.996078, 0.992157, 0.988235, 0.984314, 0.980392, 0.976471, 0.972549,
    0.968627, 0.964706, 0.960784, 0.956863, 0.952941, 0.949020, 0.945098, 0.941176,
    0.937255, 0.933333, 0.929412, 0.925490, 0.921569, 0.917647, 0.913725, 0.909804,
    0.905882, 0.901961, 0.898039, 0.894118, 0.890196, 0.886275, 0.882353, 0.878431,
    0.874510, 0.870588, 0.866667, 0.862745, 0.858824, 0.854902, 0.850980, 0.847059,
    0.843137, 0.839216, 0.835294, 0.831373, 0.827451, 0.823529, 0.819608, 0.815686,
    0.811765, 0.807843, 0.803922, 0.800000, 0.796078, 0.792157, 0.788235, 0.784314,
    0.780392, 0.776471, 0.772549, 0.768627, 0.764706, 0.760784, 0.756863, 0.752941,
    0.749020, 0.745098, 0.741176, 0.737255, 0.733333, 0.729412, 0.725490, 0.721569,
    0.717647, 0.713726, 0.709804, 0.705882, 0.701961, 0.698039, 0.694118, 0.690196,
    0.686275, 0.682353, 0.678431, 0.674510, 0.670588, 0.666667, 0.662745, 0.658824,
    0.654902, 0.650980, 0.647059, 0.643137, 0.639216, 0.635294, 0.631373, 0.627451,
    0.623529, 0.619608, 0.615686, 0.611765, 0.607843, 0.603922, 0.600000, 0.596078,
    0.592157, 0.588235, 0.584314, 0.580392, 0.576471, 0.572549, 0.568627, 0.564706,
    0.560784, 0.556863, 0.552941, 0.549020, 0.545098, 0.541176, 0.537255, 0.533333,
    0.529412, 0.525490, 0.521569, 0.517647, 0.513726, 0.509804, 0.505882, 0.501961,
    0.498039, 0.494118, 0.490196, 0.486275, 0.482353, 0.478431, 0.474510, 0.470588,
    0.466667, 0.462745, 0.458824, 0.454902, 0.450980, 0.447059, 0.443137, 0.439216,
    0.435294, 0.431373, 0.427451, 0.423529, 0.419608, 0.415686, 0.411765, 0.407843,
    0.403922, 0.400000, 0.396078, 0.392157, 0.388235, 0.384314, 0.380392, 0.376471,
    0.372549, 0.368627, 0.364706, 0.360784, 0.356863, 0.352941, 0.349020, 0.345098,
    0.341176, 0.337255, 0.333333, 0.329412, 0.325490, 0.321569, 0.317647, 0.313726,
    0.309804, 0.305882, 0.301961, 0.298039, 0.294118, 0.290196, 0.286275, 0.282353,
    0.278431, 0.274510, 0.270588, 0.266667, 0.262745, 0.258824, 0.254902, 0.250980,
    0.247059, 0.243137, 0.239216, 0.235294, 0.231373, 0.227451, 0.223529, 0.219608,
    0.215686, 0.211765, 0.207843, 0.203922, 0.200000, 0.196078, 0.192157, 0.188235,
    0.184314, 0.180392, 0.176471, 0.172549, 0.168627, 0.164706, 0.160784, 0.156863,
    0.152941, 0.149020, 0.145098, 0.141176, 0.137255, 0.133333, 0.129412, 0.125490,
    0.121569, 0.117647, 0.113725, 0.109804, 0.105882, 0.101961, 0.098039, 0.094118,
    0.090196, 0.086275, 0.082353, 0.078431, 0.074510, 0.070588, 0.066667, 0.062745,
    0.058824, 0.054902, 0.050980, 0.047059, 0.043137, 0.039216, 0.035294, 0.031373,
    0.027451, 0.023529, 0.019608, 0.015686, 0.011765, 0.007843, 0.003922, 0.000000
};

// dane opsujące obraz odczytany z pliku TARGA

GLsizei width; // szerokość obrazu
GLsizei height; // wysokość obrazu
GLenum format; // format danych obrazu
GLenum type; // format danych pikseli obrazu
GLvoid * pixels; // wskaźnik na tablicę z danymi obrazu

// stałe do obsługi menu podręcznego

enum
{
    ZOOM_X2 = 2, // powiększenie X2 wybranego fragmentu obrazu
    ZOOM_X3, // powiększenie X3 wybranego fragmentu obrazu
    ZOOM_X4, // powiększenie X4 wybranego fragmentu obrazu
    ZOOM_X5, // powiększenie X5 wybranego fragmentu obrazu
    SIZE_X05, // pomniejszenie X2
    SIZE_X1, // rozmiar normalny
    SIZE_X2, // powiększenie X2
    SIZE_X4, // powiększenie X4
    GRAY_CONVERT, // obraz w odcieniach szarości
    NEGATIVE_CONVERT, // negatyw obrazu
    SAVE_FILE, // zapis pliku TARGA
    EXIT // wyjście
};

// wskaźnik naciśnięcia lewego przycisku myszki

int button_state = GLUT_UP;

// położenie kursora myszki

int button_x, button_y;

// wielkość powiększenia wybranego fragmentu obrazu

int zoom = ZOOM_X2;

// wczytanie pliku graficznego w formacie TARGA

void LoadTARGA( int argc, char * argv[] )
{
    // sprawdzenie czy jest parametr
    if( argc < 2 )
    {
        printf( "Brak nazwy pliku TARGA\n" );
        exit( 1 );
    }
   
    // odczyt pliku TARGA i ewentualny komunikat o błędzie
    if( !load_targa( argv[ 1 ], width, height, format, type, pixels ) )
    {
        #ifdef WIN32
        printf( "Błąd odczytu lub błędny format pliku: %s\n", argv[ 1 ] );
        #else
       
        printf( "Blad odczytu lub bledny format pliku: %s\n", argv[ 1 ] );
        #endif
       
        exit( 1 );
    }
}

// sprawdzenie czy dany format pliku TARGA jest obsługiwany

void CheckImageFormat()
{
    // obraz w formacie BGR i BGRA
    if( format == GL_BGR || format == GL_BGRA )
    {
        // odczyt wersji OpenGL
        const char * version =( char * ) glGetString( GL_VERSION );
        int major = 0, minor = 0;
        if( sscanf( version, "%d.%d", & major, & minor ) != 2 )
        {
            #ifdef WIN32
            printf( "Błędny format wersji OpenGL\n" );
            #else
           
            printf( "Bledny format wersji OpenGL\n" );
            #endif
           
            exit( 1 );
        }
       
        // sprawdzenie czy jest co najmniej wersja 1.2 OpenGL
        if( major <= 1 && minor < 2 )
        {
            // jeżeli jest starsza wersja OpenGL sprawdzenie
            // czy jest obsługa rozszerzenia GL_EXT_bgra
            if( !glutExtensionSupported( "GL_EXT_bgra" ) )
            {
                // komunikat o błędzie - w tym miejscu można wykonać
                // konwersję danych z formatu BGR/BGRA na RGB/RGBA
                printf( "Brak rozszerzenia GL_EXT_bgra\n" );
                exit( 1 );
            }
        }
    }
}

// zapis pliku graficznego w formacie TARGA

void SaveTARGA( GLenum format )
{
    // wyrównywanie wiersza mapy do pojedyńczego bajta
    glPixelStorei( GL_PACK_ALIGNMENT, 1 );
   
    // wskaźnik na bufor mapy pikselowej
    GLvoid * pixels;
   
    // szerokość i wysokość mapy pikselowej
    GLsizei width = glutGet( GLUT_WINDOW_WIDTH );
    GLsizei height = glutGet( GLUT_WINDOW_HEIGHT );
   
    // utworzenie bufora mapy pikselowej
    if( format == GL_BGRA )
         pixels = new unsigned char[ width * height * 4 ];
    else
    if( format == GL_BGR )
         pixels = new unsigned char[ width * height * 3 ];
    else
    if( format == GL_LUMINANCE )
         pixels = new unsigned char[ width * height ];
    else
         return;
   
    // tylko plik w odcieniach szarości
    if( format == GL_LUMINANCE )
    {
        // określenie przekształcenia składowych RGB na odcienie szarości
        glPixelTransferf( GL_RED_SCALE, 0.229 );
        glPixelTransferf( GL_GREEN_SCALE, 0.587 );
        glPixelTransferf( GL_BLUE_SCALE, 0.114 );
    }
   
    // skopiowanie zawartości bufora koloru do bufora mapy pikselowej
    glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels );
   
    // tylko plik w odcieniach szarości
    if( format == GL_LUMINANCE )
    {
        // powrót do neutralnego przekształcenia składowych
        glPixelTransferf( GL_RED_SCALE, 1.0 );
        glPixelTransferf( GL_GREEN_SCALE, 1.0 );
        glPixelTransferf( GL_BLUE_SCALE, 1.0 );
    }
   
    // zapis mapy pikselowej do pliku TARGA
    if( format == GL_BGRA )
         save_targa( "test_BGRA.tga", width, height, format, type, pixels );
    else
    if( format == GL_BGR )
         save_targa( "test_BGR.tga", width, height, format, type, pixels );
    else
    if( format == GL_LUMINANCE )
         save_targa( "test_LUMINANCE.tga", width, height, format, type, pixels );
   
    // porządki
    delete[]( unsigned char * ) pixels;
}

// efekt powiększenia wybranego fragmentu obrazu

void ZoomImage()
{
    // normalizacja współrzędnych położenia wybranego punktu obrazu
    // do powiększenia; okno z powiększeniem ma rozmiary 200 x 200 pikseli
    // niezależnie od wybranej skali
    int x = button_x - 100;
    int y = glutGet( GLUT_WINDOW_HEIGHT ) - button_y - 100;
    if( x < 0 )
         x = 0;
   
    if( y < 0 )
         y = 0;
   
    if( x > glutGet( GLUT_WINDOW_WIDTH ) - 200 )
         x = glutGet( GLUT_WINDOW_WIDTH ) - 200;
   
    if( y > glutGet( GLUT_WINDOW_HEIGHT ) - 200 )
         y = glutGet( GLUT_WINDOW_HEIGHT ) - 200;
   
    // pozycja rastra
    glRasterPos2i( x, y );
   
    // skala powiększenia
    glPixelZoom( zoom, zoom );
   
    // normalizacja współrzędnych początku powiększanego obszaru
    int posx = button_x - 100 / zoom;
    int posy = glutGet( GLUT_WINDOW_HEIGHT ) - button_y - 100 / zoom;
    if( posx < 0 )
         posx = 0;
   
    if( posy < 0 )
         posy = 0;
   
    if( posx > glutGet( GLUT_WINDOW_WIDTH ) - 100 )
         posx = glutGet( GLUT_WINDOW_WIDTH ) - 100;
   
    if( posy > glutGet( GLUT_WINDOW_HEIGHT ) - 100 )
         posy = glutGet( GLUT_WINDOW_HEIGHT ) - 100;
   
    // kopiowanie pikseli
    glCopyPixels( posx, posy, 200 / zoom, 200 / zoom, GL_COLOR );
}

// wyświetlenie i skalowanie obrazu

void Display()
{
    // kolor tła - zawartość bufora koloru
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
   
    // // czyszczenie bufora koloru
    glClear( GL_COLOR_BUFFER_BIT );
   
    // pozycja mapy pikselowej
    glRasterPos2i( 0, 0 );
   
    // wyrównywanie wiersza mapy pikselowej do pojedyńczego bajta
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
   
    // skalowanie mapy pikselowej do rozmiarów okna
    glPixelZoom(( float ) glutGet( GLUT_WINDOW_WIDTH ) /( float ) width,
    ( float ) glutGet( GLUT_WINDOW_HEIGHT ) /( float ) height );
   
    // wyświetlenie obrazu zawartego w mapie pikselowej
    glDrawPixels( width, height, format, type, pixels );
   
    // efekt powiększenia wybranego fragmentu obrazu
    if( button_state == GLUT_DOWN )
         ZoomImage();
   
    // skierowanie poleceń do wykonania
    glFinish();
   
    // zamiana buforów koloru
    glutSwapBuffers();
}

// zmiana wielkości okna

void Reshape( int Width, int Height )
{
    // obszar renderingu - całe okno
    glViewport( 0, 0, Width, Height );
   
    // wybór macierzy rzutowania
    glMatrixMode( GL_PROJECTION );
   
    // macierz rzutowania = macierz jednostkowa
    glLoadIdentity();
   
    // parametry bryły obcinania
    gluOrtho2D( 0.0, Width, 0.0, Height );
   
    // generowanie sceny 3D
    Display();
}

// obsługa przycisków myszki

void MouseButton( int button, int state, int x, int y )
{
    if( button == GLUT_LEFT_BUTTON )
    {
        // zapamiętanie stanu lewego przycisku myszki
        button_state = state;
       
        // zapamiętanie położenia kursora myszki
        if( state == GLUT_DOWN )
        {
            button_x = x;
            button_y = y;
        }
        glutPostRedisplay();
    }
}

// obsługa ruchu kursora myszki

void MouseMotion( int x, int y )
{
    if( button_state == GLUT_DOWN )
    {
        button_x = x;
        button_y = y;
        glutPostRedisplay();
    }
}

// obsługa menu podręcznego

void Menu( int value )
{
    switch( value )
    {
        // pomniejszenie X2
    case SIZE_X05:
        glutReshapeWindow( width / 2, height / 2 );
        Display();
        break;
       
        // rozmiar normalny
    case SIZE_X1:
        glutReshapeWindow( width, height );
        Display();
        break;
       
        // powiększenie X2
    case SIZE_X2:
        glutReshapeWindow( width * 2, height * 2 );
        Display();
        break;
       
        // powiększenie X4
    case SIZE_X4:
        glutReshapeWindow( width * 4, height * 4 );
        Display();
        break;
       
        // powiększenie fragmentu obrazu X2
    case ZOOM_X2:
        zoom = ZOOM_X2;
        break;
       
        // powiększenie fragmentu obrazu X3
    case ZOOM_X3:
        zoom = ZOOM_X3;
        break;
       
        // powiększenie fragmentu obrazu X4
    case ZOOM_X4:
        zoom = ZOOM_X4;
        break;
       
        // powiększenie fragmentu obrazu X5
    case ZOOM_X5:
        zoom = ZOOM_X5;
        break;
       
        // zapis ekranu do pliku graficznego
    case SAVE_FILE:
        SaveTARGA( format );
        break;
       
        // konwersja mapy pikselowej RGB do mapy
        // w odcieniach szarości (GL_LUMINANCE)
    case GRAY_CONVERT:
       
        // sprawdzenie czy mapa bitowa nie jest w odcieniach szarości
        if( format == GL_LUMINANCE )
             break;
       
        // powrót do domyślnego skalowania mapy
        glPixelZoom( 1.0, 1.0 );
       
        // wyrównywanie wiersza mapy pikselowej do pojedyńczego bajta
        glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
       
        // określenie przekształcenia składowych RGB na odcienie szarości
        glPixelTransferf( GL_RED_SCALE, 0.229 );
        glPixelTransferf( GL_GREEN_SCALE, 0.587 );
        glPixelTransferf( GL_BLUE_SCALE, 0.114 );
       
        // wyświetlenie w buforze pomocniczym
        // obrazu zawartego w mapie pikselowej
        glDrawPixels( width, height, format, type, pixels );
       
        // powrót do neutralnego przekształcenia składowych
        glPixelTransferf( GL_RED_SCALE, 1.0 );
        glPixelTransferf( GL_GREEN_SCALE, 1.0 );
        glPixelTransferf( GL_BLUE_SCALE, 1.0 );
       
        // wyrównywanie wiersza mapy do pojedyńczego bajta
        glPixelStorei( GL_PACK_ALIGNMENT, 1 );
       
        // skopiowanie zawartości bufora pomocniczego do bufora mapy pikselowej
        glReadPixels( 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels );
       
        // zmiana formatu mapy bitowej, nie jest zmieniany rozmiar bufora
        format = GL_LUMINANCE;
       
        // wyświetlenie sceny
        Display();
        break;
       
        // negatyw obrazu
    case NEGATIVE_CONVERT:
       
        // powrót do domyślnego skalowania mapy
        glPixelZoom( 1.0, 1.0 );
       
        // wyrównywanie wiersza mapy pikselowej do pojedyńczego bajta
        glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
       
        // mapy przeształceń składowych pikseli
        glPixelMapfv( GL_PIXEL_MAP_R_TO_R, 256, inverse_map );
        glPixelMapfv( GL_PIXEL_MAP_G_TO_G, 256, inverse_map );
        glPixelMapfv( GL_PIXEL_MAP_B_TO_B, 256, inverse_map );
       
        // włączenie przekształceń za pomocą map przekształceń
        glPixelTransferi( GL_MAP_COLOR, GL_TRUE );
       
        // wyświetlenie w buforze pomocniczym
        // obrazu zawartego w mapie pikselowej
        glDrawPixels( width, height, format, type, pixels );
       
        // wyłączenie przekształceń za pomocą map przekształceń
        glPixelTransferi( GL_MAP_COLOR, GL_FALSE );
       
        // wyrównywanie wiersza mapy do pojedyńczego bajta
        glPixelStorei( GL_PACK_ALIGNMENT, 1 );
       
        // tylko mapa w odcieniach szarości
        if( format == GL_LUMINANCE )
        {
            // określenie przekształcenia składowych RGB na odcienie szarości
            glPixelTransferf( GL_RED_SCALE, 0.229 );
            glPixelTransferf( GL_GREEN_SCALE, 0.587 );
            glPixelTransferf( GL_BLUE_SCALE, 0.114 );
        }
       
        // skopiowanie zawartości bufora pomocniczego do bufora mapy pikselowej
        glReadPixels( 0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels );
       
        // tylko mapa w odcieniach szarości
        if( format == GL_LUMINANCE )
        {
            // powrót do neutralnego przekształcenia składowych
            glPixelTransferf( GL_RED_SCALE, 1.0 );
            glPixelTransferf( GL_GREEN_SCALE, 1.0 );
            glPixelTransferf( GL_BLUE_SCALE, 1.0 );
        }
       
        // wyświetlenie sceny
        Display();
        break;
       
        // wyjście
    case EXIT:
        exit( 0 );
        break;
    }
}

int main( int argc, char * argv[] )
{
    // odczyt pliku graficznego TARGA
    LoadTARGA( argc, argv );
   
    // inicjalizacja biblioteki GLUT
    glutInit( & argc, argv );
   
    // inicjalizacja bufora ramki
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_ALPHA );
   
    // rozmiary głównego okna programu - równe wymiarom
    // odczytanego pliku graficznego TARGA
    glutInitWindowSize( width, height );
   
    // utworzenie głównego okna programu - nazwa okna taka
    // sama jak nazwa odczytanego pliku graficznego TARGA
    glutCreateWindow( argv[ 1 ] );
   
    // dołączenie funkcji generującej scenę 3D - obraz zawarty w pliku TARGA
    glutDisplayFunc( Display );
   
    // dołączenie funkcji wywoływanej przy zmianie rozmiaru okna
    glutReshapeFunc( Reshape );
   
    // obsługa przycisków myszki
    glutMouseFunc( MouseButton );
   
    // obsługa ruchu kursora myszki
    glutMotionFunc( MouseMotion );
   
    // sprawdzenie czy dany format pliku TARGA jest obsługiwany
    CheckImageFormat();
   
    // podmenu "Wielkość okna"
    int MenuWindowScale = glutCreateMenu( Menu );
    glutAddMenuEntry( "Pomniejszenie X2", SIZE_X05 );
    glutAddMenuEntry( "Rozmiar normalny", SIZE_X1 );
   
    #ifdef WIN32
   
    glutAddMenuEntry( "Powiększenie X2", SIZE_X2 );
    glutAddMenuEntry( "Powiększenie X4", SIZE_X4 );
    #else
   
    glutAddMenuEntry( "Powiekszenie X2", SIZE_X2 );
    glutAddMenuEntry( "Powiekszenie X4", SIZE_X4 );
    #endif
   
    // podmenu "Efekt zoom"
    int MenuZoom = glutCreateMenu( Menu );
    #ifdef WIN32
   
    glutAddMenuEntry( "Powiększenie X2", ZOOM_X2 );
    glutAddMenuEntry( "Powiększenie X3", ZOOM_X3 );
    glutAddMenuEntry( "Powiększenie X4", ZOOM_X4 );
    glutAddMenuEntry( "Powiększenie X5", ZOOM_X5 );
    #else
   
    glutAddMenuEntry( "Powiekszenie X2", ZOOM_X2 );
    glutAddMenuEntry( "Powiekszenie X3", ZOOM_X3 );
    glutAddMenuEntry( "Powiekszenie X4", ZOOM_X4 );
    glutAddMenuEntry( "Powiekszenie X5", ZOOM_X5 );
    #endif
   
    // utworzenie menu podręcznego
    glutCreateMenu( Menu );
   
    #ifdef WIN32
   
    glutAddSubMenu( "Wielkość okna", MenuWindowScale );
    glutAddSubMenu( "Efekt zoom", MenuZoom );
    glutAddMenuEntry( "Odcienie szarości", GRAY_CONVERT );
    glutAddMenuEntry( "Negatyw", NEGATIVE_CONVERT );
    glutAddMenuEntry( "Zapisz", SAVE_FILE );
    glutAddMenuEntry( "Wyjście", EXIT );
    #else
   
    glutAddSubMenu( "Wielkosc okna", MenuWindowScale );
    glutAddSubMenu( "Efekt zoom", MenuZoom );
    glutAddMenuEntry( "Odcienie szarosci", GRAY_CONVERT );
    glutAddMenuEntry( "Negatyw", NEGATIVE_CONVERT );
    glutAddMenuEntry( "Zapisz", SAVE_FILE );
    glutAddMenuEntry( "Wyjscie", EXIT );
    #endif
   
    // określenie przycisku myszki obsługującej menu podręczne
    glutAttachMenu( GLUT_RIGHT_BUTTON );
   
    // wprowadzenie programu do obsługi pętli komunikatów
    glutMainLoop();
    return 0;
}
Poprzedni dokument Następny dokument
Mapy bitowe Bufor głębokości