6. diel - SDĽ - Práca s 8bitovou grafikou
V dnešnom diele sa bližšie pozrieme na štruktúru
SDL_Surface
. Povieme si, ako môžeme pracovať s jednotlivými
pixely u 8-bitové grafiky. V ďalšom diele sa potom pozrieme na grafiku 16 a
32-bitovú.
Popis SDL_Surface
struct SDL_Surface { Uint32 flags; /**< Read-only */ SDL_PixelFormat *format; /**< Read-only */ int w, h; /**< Read-only */ int pitch; /**< Read-only */ void *pixels; /**< Read-write */ /** Application data associated with the surface */ void *userdata; /**< Read-write */ /** information needed for surfaces requiring locks */ int locked; /**< Read-only */ void *lock_data; /**< Read-only */ /** clipping information */ SDL_Rect clip_rect; /**< Read-only */ /** info for fast blit mapping to other surfaces */ struct SDL_BlitMap *map; /**< Private */ /** Reference count -- used when freeing surface */ int refcount; /**< Read-mostly */ }
Týmto spôsobom je SDL_Surface definovaný. Teraz si jednotlivé atribúty prejdeme a povieme si, čo robia.
flags
a map
slúži iba k interným účely SDL.
format
si popíšeme ďalej v článku. Slúži na uchovanie
informácií o tom, ako sú pixely v štruktúre uložené, kde sú umiestnené
jednotlivé farby a podobné veci. Atribúty w
a h
nás informujú o šírke a výške obrazu, ktorý štruktúra uchováva. Ďalej
máme atribút pitch
, ktorý obsahuje počet bytov v jednom riadku
pixelov. Spravidla sa teda jedná o šírku štruktúry vynásobenú počtom
bajtov na jeden pixel. Napríklad pre 32-bitovú farebnú hĺbku obsahuje jeden
pixel 4 bajty (1 bajt = 8 bitov). Najdôležitejšie je atribút
pixels
, ktorý uchováva samotná dáta jednotlivých pixlov.
Budeme sa ním zaoberať v ďalších odsekoch.
Ďalej máme ukazovateľ userdata
, do ktorého môžeme vložiť
ľubovoľný objekt. Atribúty locked
a lock_data
sú
určené len na interné použitie. Zamykaním štruktúry sa budeme ďalej v
texte venovať tiež, ale SDL má zamykanie riešené vlastnými funkciami.
Ďalším atribútom je clip_rect
. Tu už je situácia
zaujímavejšie. SDL definuje funkciu SDL_SetClipRect, ktorá tento
atribút nastaví (preto je tiež v štruktúre označený ako read-only).
Akékoľvek ďalšie vykreslenie bude prebiehať iba na tento obdĺžnik
(zostávajúca plocha vykreslená nebude). Pri testovaní som však prišiel na
to, že clip_rect
sa berie do úvahy len pri funkcii
SDL_BlitSurface
, u SDL_BlitScaled
je vyplnená celá
plocha, nehľadiac na nastavený clip_rect
.
Posledným atribútom je refcount
. Slúži pre aplikáciu,
ktorá s ním môže pracovať ľubovoľne. Je zamýšľaný ako prostriedok pre
počítanie použitie. Uvediem jednoduchý príklad. Jednu
SDL_Surface
používame na rôznych miestach aplikácie. Tieto
miesta sú na seba nezávislé. Čo sa však stane, keď už jedno zo
spomínaných miest našu SDL_Surface
nebude potrebovať? Ak je
stále používaná na inom mieste v programe, pri zmazanie program spadne,
pretože ukazovateľ na druhom mieste programu bude odkazovať na neexistujúce
miesto. Ovšem ak SDL_Surface
nevymaže a už ju nebude program
potrebovať, nastáva únik pamäte. Ako z tejto situácie von? Každé miesto,
ktoré naši SDL_Surface
potrebuje, inkrementuje
refcount
. Akonáhle ju už nepotrebuje, dekrementuje číslo a
zistí, aká je jeho aktuálna hodnota. Pokiaľ bude nula (žiadne iné miesto
už naši SDL_Surface
nepotrebuje), tak ju zmaže. Použitie
refcount
na tento účel je však na voľbe programátora. Môže
ho použiť k akémukoľvek inému účelu, ale nedoporučoval by som to. Pre
užívateľské dáta by malo byť využitý už spomínaný atribút
userdata
.
SDL_PixelFormat
SDL_PixelFormat má niekoľko atribútov, ktoré používa iba SDL, preto sa zameriame na atribúty, ktoré sú relevantné aj pre programátorov.
struct SDL_PixelFormat
{
Uint32 format;
SDL_Palette *palette;
Uint8 BitsPerPixel;
Uint8 BytesPerPixel;
Uint32 Rmask;
Uint32 Gmask;
Uint32 Bmask;
Uint32 Amask;
}
Formát je jedna hodnota z SDL_PixelFormatEnum. Hodnoty spravidla
udávajú počet bitov, ktoré farba zaberá. Napríklad
SDL_PIXELFORMAT_RGBA8888
má 8 bitov pre červenú farbu, 8 pre
farbu zelenú, 8 bitov pre farbu modrú a 8 bitov pre transparentnosť. Vo
výsledku teda máme 32 bitov az toho 24 bitov pre farby - True
color. Ak sa pozrieme napríklad na SDL_PIXELFORMAT_BGRA5551
vidíme, že máme 5 bitov pre modrú, 5 pre zelenú a 5 bitov pre červenú
farbu. Zostal nám jeden bit pre alfa kanál (priehľadnosť). To znamená, že
môžeme použiť priehľadnosť, ale iba jednostupňovú - farba buď vidieť
pôjde, alebo nie.
palette
využijeme iba pre 8 bitovú grafiku. Ak použijeme 16
alebo 32 bitovú grafiku, palette
sa nenastaví a bude prázdna.
Viac si dozvieme v ďalšom odseku. Ďalšie dva atribúty
(BitsPerPixel
a BytesPerPixel
) sú predpokladám
dostatočne samovysvetľujúce. Udávajú počet bitov / bajtov na jeden pixel.
Posledné 4 atribúty nastavujú masku, o ktorej si povieme ďalej.
Práca s 8bitovou grafikou
Práca s 8bitovou grafikou je rozdielna od práce s 16 alebo 32-bitovú, preto sa pozrieme najskôr na ňu.
8bitová grafika používa iba jeden bajt pre určenie farby. V skutočnosti
je však tá farba definovaná 4 bajty (z toho je jeden bajt alfa kanálu) - je
teda TrueColor a spomínaný bajt sa používa iba pre indexáciu v poli 256
nami definovaných farieb (SDL_Palette
). Po vytvorení
SDL_Surface
s 8 bitovou farebnou hĺbkou budú všetky farby
nastavené na bielu (R = 255, G = 255, B = 255). Ak budeme chcieť vložiť
vlastné farby, použijeme funkciu SDL_SetPaletteColors. Tá prijíma
paletu, ktorú bude nastavovať a ukazovateľ na pole SDL_Color
, z
ktorého bude farby kopírovať. Ďalej index prvej farby, ktorú má nastaviť
v pôvodnej palete. Odovzdáme Ak hodnotu 5, nastaví sa šiesta (indexácia od
nuly) farba na rovnakú, aká je prvá farba v poli. Ako posledný parameter
odovzdáme počet farieb, ktoré chceme nastaviť. Táto hodnota nesmie vyť
väčšia ako je dĺžka poľa, inak bude SDL čítať mimo rozsah poľa a
program môže spadnúť alebo prečítať zlú hodnotu.
Môžeme pracovať aj priamo s paletou. SDL má funkciu
SDL_AllocPalette, ktorá vytvorí novú paletu so zadaným počtom
farieb. Ďalej funkciu SDL_FreePalette, ktorá paletu zmaže. Niekoho
by mohlo napadnú vytvoriť paletu s viacerými farbami a tým pádom
rozšíriť počet farieb, ktoré môžeme použiť. To bohužiaľ nejde. SDL
má vo format->BitsPerPixel
uloženú hodnotu 8, rovnako tak vo
format->BytesperPixel
je uložená hodnota 1. Jeden pixel môže
nadobúdať maximálnej hodnoty 256. Ak by sme chceli použiť viac farieb, tak
ich nemáme ako adresovať (8 bitov nemôže nadobúdať vyššie hodnoty ako
255). Museli by sme zmeniť hodnoty BitsPerPixel
a
BytesPerPixel
. S tým by súviselo aj zväčšenie dát, ktoré
SDL_Surface musia ukladať. Ak máme SDL_Surface
o veľkosti
100x100 pixelov, pre 8 bitovú grafiku bude pixels ukazovať na miesto s
veľkosťou 100
100=10000 bajtů. Pokud by byl každý pixel o bajt větší, museli bychom toto místo zdvojnásobit, aby obsahovalo všechny pixely v obraze. Ovšem ve chvíli, kdy provedeme tyto změny, máme vlastně 16 bitovou grafiku, která bere barvy z palety. Jak jsem již ale zmínil, 16 a 32bitová grafika nepracuje s paletou a tak ji SDL ani nepoužije. SDL bude s takto modifikovanou `SDL_Surface
pracovať rovnako, ako keby bola vytvorená ako 16bitová.
Ale vráťme sa späť do reality. Vytvoríme si teda pole
SDL_Color
, ktoré naplníme hodnotami. Ďalej zavoláme spomínanú
funkciu SDL_SetPaletteColors
a nastavíme paletu. Teraz môžeme
pristupovať k pixelom ukazovateľom pixels
.
Zmena pixelov
SDL_Surface
ukladá pixely ako jednorozmerné pole. To znamená,
že do dvojrozmerného poľa sa budeme musieť dopočítať. Všimnime si však,
že ide len o ukazovateľ typu void
. To je z dôvodu, aby
SDL_Surface
fungovalo pre všetky typy bitových hĺbok. Najskôr
budeme musieť ukazovateľ pretypovať a potom sa dopočítať na potrebné
pixely. K tomu slúži aritmetika s ukazovateľmi a postup je nasledovný.
UmístěníPrvku=ZačátekPole + Řádek * PočetBajtůNaŘádku + Sloupec * PočetBajtůNaPixel;
Začiatok poľa je náš ukazovateľ pixels
. Počet bajtov na
riadku môžeme vypočítať z šírky SDL_Surface
vynásobenú
počtom bajtov na pixel (BytesPerPixel
) alebo priamo v
SDL_Surface
z atribútu pitch
. A počet bajtov na
pixel som už napísal - BytesPerPixel
.
Farba pixelu sa berie vzhľadom k palete. Pokiaľ je teda prvá farba (index 0) zelená, nastavíme ak pixelu hodnotu 0, bude jeho farba zelená. V ukážkovom programe dnes vytvoríme farebný prechod. Najzložitejšia časť aplikácie je vytvorenie palety a dopočet pixelov, preto vedľa zdrojových kódov túto časť programu prikladám tu.
SDL_Surface* MySurface = SDL_CreateRGBSurface(NULL, 256, 100, 8, 0, 0, 0, 0); SDL_Color color[256]; for (int a = 0; a < 256; a++) { color[a].r = a; color[a].g = color[a].a = color[a].b = 0; } SDL_SetPaletteColors(MySurface->format->palette, color, 0, 256); for (int a = 0; a < 256; a++) { for (int b = 0; b < 100; b++) { Uint8* BeginOfArray = (Uint8*)MySurface->pixels; int IndexOfRow = b*MySurface->pitch; int IndexOfColumn = a*MySurface->format->BytesPerPixel; *(BeginOfArray + IndexOfRow + IndexOfColumn) = a; } }
Teraz máme MySurface
naplnené a iba z nej vytvoríme
SDL_Texture
. Výsledok je nasledovný.
Pár ďalších dôležitých informácií
Vidíme, že prechod obsahuje všetky farby červenej, ktoré môžeme vykresliť. Výsledok sa teda nezdá ako 8 bitová grafika. Na rozdiel od 16 a 32 bitové grafiky, tu nemôžeme pridať žiadne ďalšie farby. Ak by sme chceli mať spodných desať pixelov zelených, museli by sme sa vzdať jedného odtieňa červenej. Ak by sme chceli viac odtieňov zelenej, lineárne by muselo ubudnúť aj odtieňov červenej. Tu sú ony limity 8 bitové grafiky.
Ak budeme chcieť použiť priehľadnosť (áno, 8 bitová grafika podporuje
i priehľadnosť), budeme musieť nastaviť Blend mode SDL_Surface
.
K tomu slúži funkcia SDL_SetSurfaceBlendMode. K blend módu sa
dostaneme v niektorom z nadchádzajúcich dielov. S použitím alfa kanálu
vyzerá výsledok nasledovne.
To je pre dnešný diel všetko. V tom budúcom sa pozrieme na prácu s 16 a 32 bitovú grafikou.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkamiStiahnuté 835x (9.58 MB)