Práce s pixmapami – vlastnosti, vykreslování
Co značí pojem pixmapa
Jak již bylo několikrát napsáno v předchozích dvou dílech tohoto seriálu, v OpenGL se termínem bitmapa označují pouze jednobarevné rastrové obrázky, které mají na každý pixel rezervovaný jen jeden bit určující, zda se má daný pixel vykreslit, či nikoli. V OpenGL však můžeme pracovat i s rastrovými obrázky, které mají větší bitovou hloubku (tj. počet bitů na pixel může být větší než jedna), a umožňují tedy pracovat i s větším počtem barev a případnou průhledností. Tyto obrázky potom v OpenGL označujeme pojmem pixmapa. Někdy se také používá označení image, ale to je velmi obecný a zavádějící pojem, proto ho v tomto textu dále nebudeme používat.
Vykreslovací řetězec OpenGL
Operace s pixmapami využívá stejné části vykreslovacího řetězce, jaké jsou použity při práci s bitmapami. Narozdíl od bitmap však mohou pixely proudit více cestami a při průchodu jednotlivými částmi vykreslovacího řetězce může být provedeno několik modifikací těchto pixelů, například zvětšení celého obrázku nebo kombinace starého obrázku s obrázkem novým.
Na následujícím obrázku je zobrazen schematický nákres té části vykreslovacího řetězce (jak jsme se již zmínili v předminulém dílu seriálu, nazývá se tato část imaging pipeline), kde probíhá zpracování rastrových dat:
Zatímco cesta rastrových dat při vykreslování bitmap byla pouze jednosměrná, tj. z hlavní paměti procesoru do paměti snímku (framebufferu), u pixmap existují celkem tři cesty, kterými mohou rastrová data projít. Každá z těchto tří cest představuje jednu základní operaci s pixmapami:
- Vykreslení pixmap, tj. přenesení rastrového obrázku z paměti procesoru do paměti snímku s případnou konverzí mezi různými formáty pixelů a/nebo modifikací dat při průchodu vykreslovacím řetězcem.
- Přečtení rastrových dat z paměti snímku do paměti procesoru s případnou konverzí mezi různými formáty pixelů.
- Kopie rastrových dat, kdy jsou rastrová data přečtena z paměti snímku, modifikována a poté zapsána nazpět do paměti snímku. Při této operaci se nekomunikuje s pamětí procesoru, protože se veškeré operace provádějí přímo na grafickém akcelerátoru.
Všechny tyto tři operace s pixmapami si detailně popíšeme v dalších částech tohoto textu a v následujícím dílu.
Vykreslování pixmap
Vykreslování pixmap je základní a často jediná operace, kterou při práci s pixmapami v aplikacích provádíme. Při vykreslování, které je zahájeno zavoláním funkce glDrawPixels(), putují rastrová data stejnou cestou jako při vykreslování bitmap, ale oproti bitmapám zde přibylo několik dalších operací, které lze s pixely provádět:
- Zdrojem rastrových dat je paměť procesoru. OpenGL nikak neřeší načítání ani ukládání rastrových dat do souborů, proto musí tyto operace buď provést programátor aplikace sám, nebo může využít některou z knihoven specializovaných na práci s různými formáty souborů s rastrovými obrázky.
- Z paměti procesoru jsou rastrová data kopírována do vykreslovacího řetězce, přičemž může být provedeno dekódování barevných hodnot jednotlivých pixelů a případné rozbalení dat (to se provádí stejně jako u bitmap). Interně jsou barvy pixelů uloženy ve formátu pevné řádové čárky (fixed point) nebo pohyblivé řádové čárky (floating point), kde každá barevná složka nabývá hodnot od 0.0 do 1.0. Bitová šířka zpracovávaného slova není normou určena.
- Každá barevná složka může být vynásobena hodnotou zadanou ve funkciglPixelTransfer(). Ke složce také může být přičten offset zadaný pomocí stejné funkce. Pomocí těchto dvou operací lze tedy měnit kontrast a jas výsledné pixmapy.
- Pokud jsou zadána data pro přemapování barevných hodnot pixelů (tzv. look-up table), je toto přemapování provedeno. Pomocí přemapování lze změnit barevné podání celé pixmapy.
- Pokud je specifikováno, že se má vykreslit pouze šedotónový obrázek (grayscale, luminance), je proveden převod z barevného režimu RGB na hodnotu luminance.
- Pixmapa může být zvětšena, zmenšena nebo převrácena. K zadání těchto modifikací slouží funkce glPixelZoom(), jejíž popis bude uveden níže.
- Poté jsou již jednotlivé pixely vykresleny do framebufferu a další operace jsou identické pro všechny vykreslované entity (viz další díly, které se framebufferem podrobně zabývají).
Na dalším obrázku je znázorněna cesta rastrových dat (pixelů) při zavolání funkce glDrawPixels(). Použité části vykreslovacího řetězce jsou zobrazeny modrou barvou, nepoužité části zůstávají černé.
Vykreslení pixmapy zahájíme funkcí void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels.). Parametry této funkce specifikují rozměry pixmapy, formát pixelů a jejich určení, typ dat a ukazatel na vlastní pixmapu.
V parametru width je uložena šířka a v parametru height výška vykreslované pixmapy. Všimněte si, že nikde není specifikováno, odkud se bitmapa začíná vykreslovat. Pro zadání začátku vykreslování musíme ještě před zahájením vykreslování použít nám známou funkci glRasterPos*().
V parametru format je uveden formát dat. Tento parametr může nabývat několika hodnot reprezentovaných symbolickými konstantami definovanými v souboru gl.h:
- GL_COLOR_INDEX: index barvy v index-color (paletovém) režimu
- GL_RGB: tři barevné složky barvového modelu RGB (Red, Green, Blue)
- GL_RGBA: tři barevné složky barvového modelu RGB (Red, Green, Blue) následované průhledností A (Alpha)
- GL_RED: pouze červená barevná složka R (Red)
- GL_GREEN: pouze zelená barevná složka G (Green)
- GL_BLUE: pouze modrá barevná složka B (Blue)
- GL_ALPHA: pouze hodnota průhlednosti A (Alpha)
- GL_LUMINANCE: světlost pixelů L (Luminance)
- GL_LUMINANCE_ALPHA: světlost pixelů L (Luminance) následovaná průhledností A (Alpha)
- GL_STENCIL_INDEX: hodnoty určené pro zápis do paměti šablony (stencil bufferu)
- GL_DEPTH_COMPONENT: hodnoty určené pro zápis do paměti hloubky (depth bufferu)
V parametru type je uveden datový typ každé barevné (Red, Green, Blue) nebo jiné (Luminance, Alpha apod.) složky pixelu. Tento parametr opět může nabývat několika hodnot:
- GL_BITMAP: na každou složku je rezervovaný pouze jeden bit, vždy osm bitů je uloženo v jednom bytu
- GL_UNSIGNED_BYTE: každá složka je reprezentována osmibitovým bezznaménkovým číslem v rozsahu 0..255
- GL_BYTE: každá složka je reprezentována osmibitovým číslem se znaménkem v rozsahu –128..127
- GL_UNSIGNED_SHORT: každá složka je reprezentována šestnáctibitovým bezznaménkovým číslem v rozsahu 0..65535
- GL_SHORT: každá složka je reprezentována šestnáctibitovým číslem se znaménkem v rozsahu –32768..32767
- GL_UNSIGNED_INT: každá složka je reprezentována 32bitovým bezznaménkovým číslem v rozsahu 0..4294967295
- GL_INT: každá složka je reprezentována 32bitovým číslem se znaménkem v rozsahu –2147483648..2147483647
- GL_FLOAT: každá složka je reprezentována 32bitovým číslem v pohyblivé řádové čárce
Je zapotřebí upozornit na to, že parametr type udává typ každé složky samostatně. Pokud tedy máme obrázek uložený v paměti procesoru ve formátu RGBA s barevnou hloubkou 32 bitů na pixel, bude formát dat nastaven na GL_RGBA a typ dat na GL_UNSIGNED_BYTE a ne na GL_UNSIGNED_INT!
V posledním parametru pixels je uložen ukazatel na první pixel vykreslované pixmapy. Pokud není nastaveno zrcadlení bitmapy, je první pixel vykreslen v levém dolním rohu – viz následující obrázek.
Specifikace režimu uložení pixelů
U pixmap lze (podobně jako u bitmap) zadat režimy uložení pixelů, čímž se v OpenGL zapínají různé operace, které provádí zakódování a rozkódování barevných hodnot jednotlivých pixelů. Režimy uložení pixelů se zadávají příkazem void glPixelStorei(GLenum pname, GLint param) nebo void glPixelStoref(GLenum pname, GLfloat param). Většina parametrů je u bitmap i pixmap stejná, některé parametry jsou však použity pouze u pixmap:
- GL_UNPACK_SWAP_BYTES může být nastaveno na hodnotu GL_TRUE, nebo GL_FALSE. Touto vlastností se řídí prohození bytů v operačním slově počítače. Vlastnost je implicitně nastavena na hodnotu GL_FALSE, prohození se tedy neprovádí.
- GL_UNPACK_LSB_FIRST může být taktéž nastaveno na hodnotuGL_TRUE, nebo GL_FALSE. Tato vlastnost řídí otočení pořadí bitů v každém bytu. Implicitně je nastavena hodnota GL_FALSE, otáčení se tedy neprovádí. Tuto vlastnost má smysl měnit pouze u bitmap (i u těch vykreslovaných jako pixmapy).
- GL_UNPACK_ALIGNMENT specifikuje zarovnání každého řádku pixelů v paměti. Možné hodnoty tohoto parametru jsou 1 (zarovnání na byte), 2 (zarovnání na sudé byty), 4 (zarovnání na 32 bitů) a 8 (zarovnání na 64 bitů). Je potřeba dát pozor na to, že implicitně je nastavena hodnota 4, tedy zarovnání na 32 bitové slovo.
- GL_UNPACK_ROW_LENGTH specifikuje skutečnou šířku pixmapy v pixelech tak, jak je uložena v paměti procesoru. Někdy totiž potřebujeme vykreslit pouze obdélníkovou část pixmapy. Potom zadáme rozměry obdélníku přímo funkci glDrawPixels() a skutečnou šířku bitmapy specifikujeme pomocí funkce glPixelStore*() s touto vlastností. Pokud je hodnota této vlastnosti nulová, vykreslí se celá pixmapa.
- GL_UNPACK_SKIP_ROWS udává počet nevykreslených řádků. Tato vlastnost, podobně jako přechozí vlastnost, je použita při vykreslování obdélníkového výřezu z větší pixmapy. Prvních n řádků pixmapy zůstane nevykresleno, vykreslení začíná až od řádku zadaného v tomto parametru.
- GL_UNPACK_SKIP_PIXELS udává počet nevykreslených pixelů na začátku každého řádku. Opět se jedná o vlastnost použitou při vykreslování obdélníkového výřezu z větší pixmapy. Pokud je hodnota této vlastnosti nenulová, vždy prvních n pixelů na každém řádku zůstává nevykresleno.
Zadání bufferu, do kterého se pixmapa bude vykreslovat
Před vlastním vykreslováním je možné zadat, do kterého z barvových bufferů se má vykreslování provádět. Specifikace bufferu se provede příkazem void glDrawBuffer(GLenum mode), kde parametr mode může nabývat jedné z těchto konstant: GL_NONE, GL_FRONT, GL_FRONT_LEFT,GL_FRONT_RIGHT, GL_BACK, GL_BACK_LEFT, GL_BACK_RIGHT, GL_LEFT, GL_RIGHT, GL_FRONT_AND_BACK a GL_AUXi (rozsah i záleží na konfiguraci systému). Podrobnější informace o bufferech bude uvedena v dalších dílech tohoto seriálu.
Specifikace počátku vykreslování pixmapy
Podobně jako u bitmap, i u pixmap musíme před vlastním vykreslením zadat souřadnice počátku vykreslování pixmapy. Pomocí příkazu void glRasterPos*() lze zadat 2D, 3D nebo 4D souřadnice levého dolního rohu pixmapy. Tato souřadnice však může být ořezána (například pokud by byla umístěna mimo okno) a po ořezání nebude vykreslena ani část pixmapy.
Narozdíl od bitmap nemůžeme u pixmap zadat posun počátku vykreslování další pixmapy ani relativní souřadnice pixmapy. Obě tyto operace se tedy musí provádět programově.
Změna měřítka pixmapy, zrcadlení pixmapy
Pomocí příkazu void glPixelZoom(GLfloat zoom_x, GLfloat zoom_y) lze specifikovat faktory zvětšení nebo zmenšení pixmapy při vykreslování. Parametrzoom_x udává zvětšení či zmenšení ve směru osy X. Parametr zoom_y udává zvětšení či zmenšení ve směru osy Y. Pokud jsou parametry větší než jedna, dochází ke zvětšení, pro parametry v rozsahu 0–1 dochází ke zmenšení.
Pokud je jeden nebo oba parametry záporné, provede se zrcadlení pixmapy buď v jednom, nebo v obou směrech. Při zrcadlení však musíme dát pozor na to, že se změní i směr vykreslování pixmap, tj. nezačíná se od levého dolního rohu, ale od některého dalšího rohu (konkrétní roh samozřejmě záleží na směru zrcadlení).
Demonstrační příklady
V demonstračním příkladu číslo 16 je ukázáno vykreslení pixmapy, kde každý pixel je uložen ve formátu RGB (true-color). Každá z barevných složek je uložena na osmi bitech, celkově je tedy na pixel potřeba 24 bitů. Za povšimnutí stojí způsob indexace pole, který musí být zvolen tak, aby reflektoval postupné uložení pixelů v paměti. K dispozici je izdrojový kód šestnáctého příkladu se zvýrazněnou syntaxí.
V následujícím demonstračním příkladu číslo 17 je ukázáno vykreslení pixmapy se změnou měřítka a zrcadlením. Do okna je několikrát vykreslena táž bitmapa, vždy se však použije jiné měřítko a/nebo zrcadlení. K dispozici je i zdrojový kód tohoto příkladu se zvýrazněnou syntaxí.
V dnešním dílu si ještě ukážeme další demonstrační příklad číslo 18, kde se vykreslí několik pixmap a je přitom měněn režim ukládání pixelů, tj. otočení bitů, prohození bytů, změna zarovnání řádků pixmapy a výřezy z pixmapy. K dispozici je i zdrojový kód tohoto příkladu se zvýrazněnou syntaxí.
V dalším dílu se budeme věnovat zbývajícím dvěma operacím s pixmapami, tj. čtení a kopírování pixmap.