Lekce 6 - SDL - Práce s 8bitovou grafikou
V minulé lekci, SDL - Vykreslení textu, jsme si ukázali, jakým způsobem budeme vykreslovat text na obrazovku.
V dnešním díle se blíže podíváme na strukturu
SDL_Surface
. Řekneme si, jak můžeme pracovat s jednotlivými
pixely u 8bitové grafiky. V dalším díle se poté podíváme na grafiku 16 a
32bitovou.
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 způsobem je SDL_Surface definovaný. Nyní si jednotlivé atributy projdeme a řekneme si, co dělají.
flags
a map
slouží pouze k interním účelům
SDL. format
si popíšeme dále v článku. Slouží k uchování
informací o tom, jak jsou pixely ve struktuře uloženy, kde jsou umístěny
jednotlivé barvy a podobné věci. Atributy w
a h
nás informují o šířce a výšce obrazu, který struktura uchovává. Dále
máme atribut pitch
, který obsahuje počet bytů v jednom řádku
pixelů. Zpravidla se tedy jedná o šířku struktury vynásobenou počtem
bajtů na jeden pixel. Například pro 32bitovou barevnou hloubku obsahuje jeden
pixel 4 bajty (1 bajt = 8 bitů). Nejdůležitější je atribut
pixels
, který uchovává samotná data jednotlivých pixelů.
Budeme se jím zabývat v dalších odstavcích.
Dále máme ukazatel userdata
, do kterého můžeme vložit
libovolný objekt. Atributy locked
a lock_data
jsou
určené pouze pro interní použití. Zamykáním struktury se budeme dál v
textu věnovat také, ale SDL má zamykání řešené vlastními funkcemi.
Dalším atributem je clip_rect
. Tady už je situace
zajímavější. SDL definuje funkci SDL_SetClipRect, která
tento atribut nastaví (proto je také ve struktuře označen jako read-only).
Jakékoliv další vykreslení bude probíhat pouze na tento obdélník
(zbývající plocha vykreslena nebude). Při testování jsem ovšem přišel
na to, že clip_rect
se bere v úvahu pouze u funkce
SDL_BlitSurface
, u SDL_BlitScaled
je vyplněna celá
plocha, nehledě na nastavený clip_rect
.
Posledním atributem je refcount
. Slouží pro aplikaci, která
s ním může pracovat libovolně. Je zamýšlený jako prostředek pro
počítání použití. Uvedu jednoduchý příklad. Jednu
SDL_Surface
používáme na různých místech aplikace. Tato
místa jsou na sebe nezávislá. Co se ovšem stane, když už jedno ze
zmíněných míst naši SDL_Surface
nebude potřebovat? Pokud je
stále používána na jiném místě v programu, při smazání program spadne,
protože ukazatel na druhém místě programu bude odkazovat na neexistující
místo. Ovšem pokud SDL_Surface
nevymaže a už ji nebude program
potřebovat, nastává únik paměti. Jak z této situace ven? Každé místo,
které naši SDL_Surface
potřebuje, inkrementuje
refcount
. Jakmile ji už nepotřebuje, dekrementuje číslo a
zjistí, jaká je jeho aktuální hodnota. Pokud bude nula (žádné jiné
místo už naši SDL_Surface
nepotřebuje), tak ji smaže.
Použití refcount
k tomuto účelu je ovšem na volbě
programátora. Může ho použít k jakémukoliv jinému účelu, ale
nedoporučoval bych to. Pro uživatelské data by mělo být využit již
zmiňovaný atribut userdata
.
SDL_PixelFormat
SDL_PixelFormat má několik atributů, které používá pouze SDL, proto se zaměříme na atributy, které jsou relevantní i pro programátory.
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 zpravidla udávají počet bitů, které barva zabírá. Například
SDL_PIXELFORMAT_RGBA8888
má 8 bitů pro červenou barvu, 8 pro
barvu zelenou, 8 bitů pro barvu modrou a 8 bitů pro průhlednost. Ve výsledku
tedy máme 32 bitů a z toho 24 bitů pro barvy – True
color. Jestliže se podíváme například na
SDL_PIXELFORMAT_BGRA5551
vidíme, že máme 5 bitů pro modrou, 5
pro zelenou a 5 bitů pro červenou barvu. Zbyl nám jeden bit pro alfa kanál
(průhlednost). To znamená, že můžeme použít průhlednost, ale pouze
jednostupňovou - barva buď vidět půjde, nebo ne.
palette
využijeme pouze pro 8 bitovou grafiku. Pokud použijeme
16 nebo 32 bitovou grafiku, palette
se nenastaví a bude prázdná.
Více si dozvíme v dalším odstavci. Další dva atributy
(BitsPerPixel
a BytesPerPixel
) jsou předpokládám
dostatečně samovysvětlující. Udávají počet bitů / bajtů na jeden
pixel. Poslední 4 atributy nastavují masku, o které si řekneme dále.
Práce s 8bitovou grafikou
Práce s 8bitovou grafikou je rozdílná od práce s 16 nebo 32bitovou, proto se podíváme nejdříve na ni.
8bitová grafika používá pouze jeden bajt pro určení barvy. Ve
skutečnosti je ovšem ta barva definovaná 4 bajty (z toho je jeden bajt alfa
kanálu) - je tedy TrueColor a zmíněný bajt se používá pouze pro indexaci
v poli 256 námi definovaných barev (SDL_Palette
). Po vytvoření
SDL_Surface
s 8 bitovou barevnou hloubkou budou všechny barvy
nastavené na bílou (R=255,G=255,B=255). Pokud budeme chtít vložit vlastní
barvy, použijeme funkci SDL_SetPaletteColors.
Ta přijímá paletu, kterou bude nastavovat a ukazatel na pole
SDL_Color
, ze kterého bude barvy kopírovat. Dále index první
barvy, kterou má nastavit v původní paletě. Předáme-li hodnotu 5, nastaví
se šestá (indexace od nuly) barva na stejnou, jaká je první barva v poli.
Jako poslední parametr předáme počet barev, které chceme nastavit. Tato
hodnota nesmí výt větší než je délka pole, jinak bude SDL číst mimo
rozsah pole a program může spadnout nebo přečíst špatnou hodnotu.
Můžeme pracovat i přímo s paletou. SDL má funkci SDL_AllocPalette,
která vytvoří novou paletu se zadaným počtem barev. Dále funkci SDL_FreePalette,
která paletu smaže. Někoho by mohlo napadnou vytvořit paletu s více barvami
a tím pádem rozšířit počet barev, které můžeme použít. To bohužel
nejde. SDL má ve format->BitsPerPixel
uloženou hodnotu 8,
stejně tak ve format->BytesperPixel
je uložena hodnota 1.
Jeden pixel může nabývat maximální hodnoty 256. Pokud bychom chtěli
použít víc barev, tak je nemáme jak adresovat (8 bitů nemůže nabývat
vyšší hodnoty než 255). Museli bychom změnit hodnoty
BitsPerPixel
a BytesPerPixel
. S tím by souviselo i
zvětšení dat, které SDL_Surface musí ukládat. Jestliže máme
SDL_Surface
o velikosti 100x100 pixelů, pro 8 bitovou grafiku bude
pixels ukazovat na místo o velikosti
100100=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
pracovat stejně, jako kdyby byla vytvořena jako 16bitová.
Ale vraťme se zpět do reality. Vytvoříme si tedy pole
SDL_Color
, které naplníme hodnotami. Dále zavoláme zmíněnou
funkci SDL_SetPaletteColors
a nastavíme paletu. Nyní můžeme
přistupovat k pixelům ukazatelem pixels
.
Změna pixelů
SDL_Surface
ukládá pixely jako jednorozměrné pole. To
znamená, že do dvourozměrného pole se budeme muset dopočítat. Všimněme
si ale, že se jedná pouze o ukazatel typu void
. To je z důvodu,
aby SDL_Surface
fungovalo pro všechny typy bitových hloubek.
Nejdřív budeme muset ukazatel přetypovat a poté se dopočítat na potřebné
pixely. K tomu slouží aritmetika s ukazateli a postup je následovný.
UmístěníPrvku=ZačátekPole + Řádek * PočetBajtůNaŘádku + Sloupec * PočetBajtůNaPixel;
Začátek pole je náš ukazatel pixels
. Počet bajtů na
řádku můžeme vypočítat z šířky SDL_Surface
vynásobenou
počtem bajtů na pixel (BytesPerPixel
) nebo přímo v
SDL_Surface
z atributu pitch
. A počet bajtů na pixel
jsem už napsal – BytesPerPixel
.
Barva pixelu se bere vzhledem k paletě. Pokud je tedy první barva (index 0) zelená, nastavíme-li pixelu hodnotu 0, bude jeho barva zelená. V ukázkovém programu dnes vytvoříme barevný přechod. Nejsložitější část aplikace je vytvoření palety a dopočet pixelů, proto vedle zdrojových kódů tuto část programu přikládám zde.
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; } }
Nyní máme MySurface
naplněné a pouze z ní vytvoříme
SDL_Texture
. Výsledek je následovný.

Pár dalších důležitých informací
Vidíme, že přechod obsahuje všechny barvy červené, které můžeme vykreslit. Výsledek se tedy nezdá jako 8 bitová grafika. Na rozdíl od 16 a 32 bitové grafiky, zde nemůžeme přidat žádné další barvy. Pokud bychom chtěli mít spodních deset pixelů zelených, museli bychom se vzdát jednoho odstínu červené. Pokud bychom chtěli víc odstínů zelené, lineárně by muselo ubýt i odstínů červené. Zde jsou ony limity 8 bitové grafiky.
Pokud budeme chtít použít průhlednost (ano, 8 bitová grafika podporuje i
průhlednost), budeme muset nastavit Blend mode SDL_Surface
. K tomu
slouží funkce SDL_SetSurfaceBlendMode.
K blend modům se dostaneme v některém z nadcházejících dílů. S
použitím alfa kanálu vypadá výsledek následovně.

To je pro dnešní díl vše.
V příští lekci, SDL - Práce s 16 a 32bitovou grafikou, se podíváme na práci s 16 a 32 bitovou grafikou.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkamiStaženo 883x (9.58 MB)