6. díl - SDL - Práce s 8bitovou grafikou

C++ SDL SDL - Práce s 8bitovou grafikou

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_PixelForma­tEnum. 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_SetPalette­Colors. 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ý.

8bitovy přechod bez alfa kanálu

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_SetSurface­BlendMode. 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ě.

Přechod s průhledností

To je pro dnešní díl vše. V tom příštím se podíváme na práci s 16 a 32 bitovou grafikou.


 

Stáhnout

Staženo 159x (9.58 MB)

 

  Aktivity (1)

Článek pro vás napsal patrik.valkovic
Avatar
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu.

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 


Miniatura
Předchozí článek
SDL - Vykreslení textu
Miniatura
Všechny články v sekci
SDL
Miniatura
Následující článek
SDL - Práce s 16 a 32bitovou grafikou

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!