Grafická knihovna OpenGL (10): operace s bitmapami a pixmapami

2. 9. 2003
Doba čtení: 10 minut

Sdílet

V dnešním dílu seriálu o grafické knihovně OpenGL dokončíme část věnovanou operacím s rastrovými obrazy - bitmapami a pixmapami. Povíme si o dvou operacích, které lze kromě vykreslování posaného v předešlém dílu provádět s pixmapami. Tyto dvě operace umožňují provádět čtení pixmapy z framebufferu a kopírování pixmapy mezi jednotlivými částmi framebufferu.

Práce s pixmapami – čtení a kopírování pixmap

Čtení rastrových dat z paměti snímku

Čtení rastrových dat z paměti snímku (framebufferu) je operace v mnoha ohledech opačná k funkci vykreslování pixmap. Rastrová data jsou přečtena ze zvoleného bufferu a po případných konverzích a zabalení (packing) jsou uložena do paměti procesoru. Čtení lze provést z libovolného barvového bufferu, z paměti hloubky (depth buffer) nebo z paměti šablony (stencil buffer).

Tuto operaci provádíme v několika případech. Nejjednodušším a současně nejpoužívanějším případem je prosté získání výsledného rastrového obrázku představujícího vykreslenou 3D scénu. Dále se čtení z framebufferu používá v případě ruční modifikace paměti šablony nebo paměti hloubky u některých grafických efektů. Také je možné vykreslit celou 3D scénu z jednoho pohledu, přečíst výsledný rastrový obrázek a tento obrázek potom použít jako texturu v nově vykreslené scéně a vytvořit tak zrcadlící plochu či plochy.

Pro čtení pixmap je určena funkce void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels). Na následujícím obrázku je znázorněna cesta rastrových dat při zavolání této funkce, přičemž moduly a cesty použité při této operaci jsou zvýrazněny modrou barvou:

Cesta rastrových dat při zavolání funkce glReadPixels()


Význam jednotlivých parametrů funkce glReadPixels():

  • x: v tomto parametru je uložena x-ová souřadnice prvního pixelu, který má být přečten z framebufferu. Pixely jsou přečteny z oblasti vymezené osově orientovaným obdélníkem, jehož levá hranice je zadána v tomto parametru. Všimněte si rozdílu oproti funkci glDrawPixels(), kde se souřadnice počátku vykreslování přímo nezadávala, ale musela být použita funkce glRasterPos*().
  • y: v tomto parametru je uložena y-ová souřadnice prvního pixelu, který má být přečten z framebufferu. Jedná se tedy o dolní hranici osově orientovaného obdélníka ohraničujícího čtenou pixmapu.
  • width: v tomto parametru je uložena šířka čtené pixmapy. Tato hodnota je zadána v pixelech.
  • height: tento parametr udává výšku čtené pixmapy. Stejně jako u předchozích parametrů, i zde zadáváme hodnotu v pixelech.
  • format: v tomto parametru je uvedeno, z kterého bufferu se bude pixmapa číst, a pokud je vybrán některý z barevných bufferů, které barevné složky se budou číst. V případě potřeby je provedena konverze mezi jednotlivými barevnými složkami (např. mezi RGB a luminancí). Význam konkrétních hodnot tohoto parametru bude popsán v dalším textu.
  • type: v tomto parametru je zadán formát dat, ve kterých je uložena každá barevná složka. Významu tohoto parametru se taktéž budeme věnovat v další části textu.
  • pixels: tento parametr obsahuje ukazatel na oblast v paměti, do které se uloží přečtená pixmapa. Programátor musí zajistit, aby byl alokován dostatečný prostor na celou pixmapu, jinak může dojít k přepisu jiných proměnných či k pádu aplikace.

Na následujícím obrázku je naznačen význam některých parametrů funkce glReadPixels():

Význam některých parametrů funkce glReadPixels()


Jak již bylo uvedeno v předchozím textu, je v parametru format zadáno, ze kterého bufferu se mají číst pixely tvořící výslednou pixmapu a popř. jaké barevné složky (v případě čtení z barvového bufferu) se budou číst. Do tohoto parametru lze zapsat následující symbolické konstanty, které jsou definovány v hlavičkovém souboru gl.h:

  • GL_COLOR_INDEX: index barvy v index-color (paletovém) režimu. Data se čtou z některého barvového bufferu.
  • GL_RGB: tři barvové složky barvového modelu RGB (Red, Green, Blue) z některého barvového bufferu.
  • GL_RGBA: tři barvové složky barvového modelu RGB (Red, Green, Blue) následované průhledností A (Alpha).
  • GL_RED: pouze červená barvová složka R (Red) z modelu RGB či RGBA přečtená z barvového bufferu.
  • GL_GREEN: pouze zelená barvová složka G (Green) z modelu RGB či RGBA přečtená z barvového bufferu.
  • GL_BLUE: pouze modrá barvová složka B (Blue) z modelu RGB či RGBA přečtená z barvového bufferu.
  • GL_ALPHA: pouze hodnota průhlednosti A (Alpha) z modelu RGBA přečtená z barvového bufferu.
  • GL_LUMINANCE: světlost pixelů L (Luminance), která je většinou získána přepočtem z modelu RGB či RGBA.
  • GL_LUMINANCE_ALPHA: světlost pixelů L (Luminance) následovaná průhledností A (Alpha).
  • GL_STENCIL_INDEX: hodnoty přečtené z paměti šablony (stencil bufferu).
  • GL_DEPTH_COMPONENT: hodnoty přečtené z paměti hloubky (depth bufferu).

V parametru type lze určit formát dat, která budou přečtena z některého vybraného bufferu. Grafický subsystém poté provede automatickou konverzi barevných složek každého pixelu z interního formátu grafického subsystému do formátu zadaného v tomto parametru. Možné hodnoty, které zde můžeme zadat, jsou:

  • 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.

Podobně jako u funkce glDrawPixels() určuje parametr type bitovou hloubku jedné barevné složky. Pokud tedy například potřebujeme přečíst pixmapu v režimu RGBA s hloubkou 32 bitů na pixel, musíme typ každé složky nastavit na hodnotu GL_BYTE nebo GL_UNSIGNED_BYTE. Kontrétní způsob uložení dat v paměti adresované parametrem pixels závisí kromě výše uložených parametrů také na hodnotách specifikovaných příkazemglPixel­Store*(). Parametry modifikující způsob čtení pixelů začínají řetězcem GL_PACK_ (u funkce glDrawPixels() to byly parametry začínající řetězcem GL_UNPACK_) s tímto významem:

  • GL_PACK_SWAP_BYTES řídí prohození (swap) bytů v jednotlivých barevných složkách. Pokud je tento parametr nastavený na hodnotuGL_FALSE, prohazování bytů se neprovádí, pokud je parametr nastavený na GL_TRUE, bude prohození aplikováno. Hodnota tohoto parametru nemá vliv na pořadí barevných složek (tedy RGB nebo BGR) ale pouze na byty v každé barevné složce. Pro typy složek GL_BITMAP, GL_BYTE a GL_UNSIGNED_BYTE nemá hodnota tohoto parametru žádný vliv na uložená data. Tento parametr je implicitně nastavený na hodnotu GL_FALSE.
  • GL_PACK_LSB_FIRST řídí otočení pořadí bitů v bytu uchovávajícím osm hodnot pixelů. Pokud je tento parametr nastavený na hodnotu

    GL_FALSE, otáčení bitů se neprovádí, pokud je nastaven na hodnotu GL_TRUE, bude otáčení bitů provedeno – pořadí bitů se tedy změní z b7b6b5b4b32b1b0

    na b0b1b2b3b4b56b7. Význam tohoto parametru se uplatní pouze u typu GL_BITMAP. Tento parametr je implicitně nastavený na hodnotu GL_FALSE.
  • GL_PACK_ALIGNMENT řídí zarovnání bytů pro každý řádek zapisované pixmapy. Možné hodnoty zarovnání jsou 1 (zarovnání na byte), 2 (zarovnání na šestnáctibitové slovo), 4 (zarovnání na 32bitové slovo) a 8 (zarovnání na 64bitové slovo). Implicitně je tento parametr nastavený na hodnotu 4, tj. zarovnání každého řádku pixmapy na 32bitové slovo.
  • GL_PACK_ROW_LENGTH určuje počet pixelů na řádek. Pokud je tento parametr nastavený na implicitní nulovou hodnotu, je počet pixelů roven parametru width funkce glReadPixels(). Pokud je tento parametr větší než nula, specifikuje skok mezi dvěma po sobě následujícími řád­ky.
  • GL_PACK_SKIP_PIXELS určuje, o kolik pixelů bude výsledná pixmapa v paměti posunuta. Tento parametr je vcelku zbytečný, protože posun lze také provést jednoduchým přičtením vhodné konstanty k ukazateli na pole pixelů předávanému ve funkci glReadPixels(). Tuto konstantu lze vypočítat pomocí výrazu ni, kde i je posun pixmapy udaný v pixelech an je počet barevných složek.
  • GL_PACK_SKIP_ROWS určuje, o kolik řádků bude výsledná pixmapa v paměti posunuta. Podobně jako u předchozího parametru, i zde je možné stejného efektu dosáhnout přičtením vhodné konstanty k ukazateli na pole ukládaných pixelů. Tuto konstantu lze vypočítat pomocí výrazu jk, kde j představuje počet řádků, o které má být pixmapa posunuta, a k je délka řádku nastavená pomocí parametru GL_PACK_ROW_LENGTH.

Při čtení rastrových dat lze dále změnit režim přenášení jednotlivých pixelů (pixel transfer mode). Pro změnu režimu se používá příkaz void glPixelTransfe­ri(GLenum pname, GLint param) nebo void glPixelTransfer­f(GLenum pname, GLint param), kde v parametru pname je symbolické jméno vlastnosti, která se má změnit, a v paramatru param potom hodnota této vlastnosti. Význam jednotlivých vlastností:

  • GL_MAP_COLOR zapíná nebo vypíná mapování barev, tedy překódování barev podle předem zadané tabulky (mapy). Implicitně je mapování vypnuto (hodnota GL_FALSE).
  • GL_MAP_STENCIL zapíná nebo vypíná mapování hodnot uložených v paměti šablony (stencil bufferu). Stejně jako u předchozího parametru implicitně je mapování vypnuto (hodnota GL_FALSE).
  • GL_INDEX_SHIFT specifikuje bitový posun barvy zadané svým indexem (platí pro paletové režimy). Pokud je tato hodnota kladná, je proveden posun doleva (násobení mocninou dvou), pro záporné hodnoty je proveden posun doprava (dělení mocninou dvou). Implicitně je posun vypnut, protože hodnota posunu je nastavena na nulu.
  • GL_INDEX_OFFSET specifikuje hodnotu, která je přičtena ke každé barvě zadané svým indexem. Tato hodnota má smysl pouze u paletových režimů. Implicitně je nastavena na nulu.
  • GL_RED_SCALE červená barvová složka každého pixelu se vynásobí zadanou hodnotou. Implicitně je nastavena hodnota 1.0f.
  • GL_GREEN_SCALE zelená barvová složka každého pixelu se vynásobí zadanou hodnotou. Implicitně je nastavena hodnota 1.0f.
  • GL_BLUE_SCALE modrá barvová složka každého pixelu se vynásobí zadanou hodnotou. Implicitně je nastavena hodnota 1.0f.
  • GL_ALPHA_SCALE specifikuje konstantu, kterou je vynásobena alfa složka (průhlednost). Implicitně je nastavena hodnota 1.0f.
  • GL_DEPTH_SCALE specifikuje konstantu, kterou je vynásobena hloubka fragmenu v paměti hloubky (depth bufferu). Opět je implicitně nastavena hodnota 1.0f.
  • GL_RED_BIAS k červené barvové složce každého pixelu se přičte zadaná hodnota. Implicitně je nastavena hodnota 0.0f.
  • GL_GREEN_BIAS k zelené barvové složce každého pixelu se přičte zadaná hodnota. Implicitně je nastavena hodnota 0.0f.
  • GL_BLUE_BIAS k modré barvové složce každého pixelu se přičte zadaná hodnota. Implicitně je nastavena hodnota 0.0f.
  • GL_ALPHA_BIAS k alfa složce (hodnota průhlednosti) se přičte zadaná hodnota. Opět je implicitně nastavena hodnota 0.0f.
  • GL_DEPTH_BIAS ke složce představující hloubku fragmentu v paměti hloubky (depth bufferu) se přičte zadaná hodnota. Implicitně je nastavena hodnota 0.0f.

Kopie rastrových dat s případnou modifikací

Operace, která provádí kopii rastrových dat ve framebufferu, je poslední operací, kterou lze s pixmapami provádět. Tuto operaci si můžeme zjednodušeně představit tak, že nejdříve zadáme funkci glReadPixels(), pomocí které přečteme rastrová data z framebufferu do paměti procesoru, a poté zadáme funkci glDrawPixels(), která zapíše data z paměti procesoru do framebufferu. Vlastní průběh kopie rastrových dat se však liší tím, že všechny přenosy dat se provádí pouze v grafickém subsystému a není tedy zapotřebí alokovat místo v paměti procesoru a současně není zatěžována datová sběrnice. Na následujícím obrázku jsou naznačeny ty moduly grafického subsystému, kterými prochází rastrová data při jejich kopírování.

Cesta rastrových dat při zavolání funkce glCopyPixels()

Vlastní kopie rastrových dat se provede zavoláním funkce void glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type), kde jednotlivé parametry této funkce mají následující význam:

  • x udává spolu s následujícím parametrem y levý dolní roh oblasti ve framebufferu, ze které bude kopírování provedeno. Cílová oblast kopírování je zadaná aktuální rastrovou pozicí (viz příkaz glRasterPos*() popisovaný v předchozích dílech).
  • y viz předchozí parametr x.
  • width specifikuje šířku kopírované oblasti zadané v pixelech.
  • height specifikuje výšku kopírované oblasti taktéž zadané v pixelech.
  • type vybírá tu část framebufferu, která se bude kopírovat. Je možné kopírovat barvy fragmentů (GL_COLOR), jejich hloubku (GL_DEPTH) nebo hodnoty z paměti šablony (GL_STENCIL).

Na následujícím obrázku je ilustrován význam některých parametrů popisované funkce:

Význam některých parametrů funkce glCopyPixels()

Na hodnoty fragmentů, které kopírujeme, jsou aplikovány všechna mapování a modifikace uvedené u funkcí glDrawPixels() i glReadPixels(). Modifikace jsou však provedeny pouze jednou. Také si můžeme všimnout, že vzhledem k tomu, že ke kopírovaným datům nemají aplikace přístup (kopírování se provádí pouze v grafickém subsystému), nemusíme specifikovat formát dat, protože se vždy použije interní formát používaného grafického subsystému.

Demonstrační příklady

V demonstračním příkladu číslo 19 (zdrojový text a verze se zvýrazněnou syntaxí) je ukázáno čtení pixelů z framebufferu a uložení přečtené pixmapy do souboru. Uložení pixmapy se provede pomocí klávesy „S“ jako Save. Všimněte si, že při ukládání pixmapy do souboru typu TGA je zapotřebí otočit pořadí řádků a pro každý pixel se musí provést konverze z formátu RGB na formát BGR (u některých implementací OpenGL je však možné číst pixely i v tomto formátu – v parametru format lze zadat GL_BGR).

V demonstračním příkladu číslo 20 (zdrojový text a verze se zvýrazněnou syntaxí) je ukázáno kopírování pixelů v barvových rovinách framebufferu.

bitcoin_skoleni

V demonstračním příkladu číslo 21 (zdrojový text a verze se zvýrazněnou syntaxí) je předchozí příklad upraven tak, aby demonstroval některé operace prováděné nad kopírovanými daty.

V další části se budeme podrobněji zabývat vykreslovacím řetězcem OpenGL.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.