SDL: Hry nejen pro Linux (23)

4. 8. 2005
Doba čtení: 9 minut

Sdílet

V dnešním, (téměř) závěrečném dílu o knihovně SDL se pokusím shrnout všechny věci, na které jsem během psaní seriálu pozapomněl, popř. kterým jsem se z důvodu své neznalosti nevěnoval pozornost. Mimo jiné se budeme věnovat SDL_RWops, YUV video overlay, nahrávání sdílených knihoven za běhu aplikace a proměnným prostředí.

SDL_RWops

SDL_RWops je technika, kterou SDL poskytuje pro načítání obrázků a zvuků (obecně libovolných dat) z paměti namísto z diskových souborů.

Pokud umíte používat např. některou z knihoven pro komprimaci, díky SDL_RWops je možné importovat obrázky z archivu úplně stejně, jako by byly uloženy přímo na disku. Nebo, jste-li schopni napojit aplikaci k vysílání internetového rádia, naprogramování přehrávače bude otázkou pouhé chvíle. Fantazii se meze opravdu nekladou.

Základní funkcí pro vytvoření SDL_RWops je SDL_RWFromFile(), která slouží pro práci s klasickými soubory. První parametr specifikuje diskovou cestu a druhý označuje mód otevření analogický parametru standardní funkce fopen() – „r“ pro čtení, „w“ pro zápis atd.

SDL_RWops *SDL_RWFromFile(const char *file,
            const char *mode); 

Funkce SDL_RWFromFP() je analogií SDL_RWFromFile(), v prvním parametru přebírá namísto řetězce se jménem deskriptor otevřeného souboru. Pokud nebude druhý parametr nulový, SDL soubor po skončení práce automaticky uzavře.

SDL_RWops *SDL_RWFromFP(FILE *fp, int autoclose); 

Pozn.: Dokumentace uvádí, že SDL_RWFromFP() není pod Win32 dostupná. Na této platformě údajně nemohou být soubory otevřené aplikací použity dynamicky linkovanou knihovnou.

Jádrem SDL_RWops jsou funkce SDL_RWFromMem() a SDL_RWFromCon­stMem(), které vytvářejí SDL_RWops z dat uložených v paměti, resp. v konstantní paměti. Předává se jim ukazatel na tuto paměť a její velikost.

SDL_RWops *SDL_RWFromMem(void *mem, int size);
SDL_RWops *SDL_RWFromConstMem(const void *mem, int size); 

Funkce SDL_AllocRW() alokuje paměť pro prázdnou SDL_RWops strukturu a SDL_FreeRW() slouží pro její uvolnění. Používají se téměř výhradně při vytváření SDL_RWops z nějakého nestandardního zdroje. Všechna vnitřní data vráceného objektu se musí inicializovat manuálně, příklad je možné najít v dnešním ukázkovém programu, pracuje se v něm se ZIP archivem.

SDL_RWops *SDL_AllocRW(void);
void SDL_FreeRW(SDL_RWops *context); 

Struktura SDL_RWops obsahuje ve svém nitru atribut rozlišující typ obsahu a union ukládající data. Verze stdio slouží pro souborové SDL_RWops a mem pro paměťové. S poslední položkou, unknown, by se mělo operovat při uživatelské alokaci pomocí výše zmíněné funkce SDL_AllocRW().

typedef struct SDL_RWops
{
    Uint32 type;

    union
    {
        struct
        {
            int autoclose;
            FILE *fp;
        } stdio;
        struct
        {
            Uint8 *base;
            Uint8 *here;
            Uint8 *stop;
        } mem;
        struct
        {
            void *data1;
        } unknown;
    } hidden;

    int (*read)(struct SDL_RWops *context, void *ptr, int size, int maxnum);
    int (*write)(struct SDL_RWops *context, const void *ptr, int size, int num);
    int (*seek)(struct SDL_RWops *context, int offset, int whence);
    int (*close)(struct SDL_RWops *context);
} SDL_RWops; 

Poslední čtyři položky struktury jsou ukazatele na funkce, které poskytují přesuny na jiná místa v paměti, čtení, zápis a uvolnění dat. Nemusí se volat přímo, lze použít makra níže.

#define SDL_RWread(ctx, ptr, size, n)   (ctx)->read(ctx, ptr, size, n)
#define SDL_RWwrite(ctx, ptr, size, n)  (ctx)->write(ctx, ptr, size, n)
#define SDL_RWseek(ctx, offset, whence) (ctx)->seek(ctx, offset, whence)
#define SDL_RWtell(ctx)         (ctx)->seek(ctx, 0, SEEK_CUR)
#define SDL_RWclose(ctx)        (ctx)->close(ctx) 

Makra se chovají v podstatě stejně jako standardní funkce ze stdio. Parametr ctx je ukazatel na SDL_RWops, ptr adresa bufferu, z/do kterého se čte/zapisuje, size počet bytů v bloku a n počet načítaných/za­pisovaných bloků. Návratovou hodnotou je počet načtených/zapsaných bloků dat nebo –1 při chybě. Parametr whence ze SDL_RWseek() může nabývat konstant SEEK_SET, SEEK_CUR, SEEK_END.

Jenom pro pořádek: poslední z maker, SDL_RWclose(), by mělo být zavoláno po skončení práce s libovolným SDL_RWops. Jedinou výjimkou jsou taková SDL_RWops, u kterých bylo požádáno o automatické uzavření.

Otevřete-li si některý z hlavičkových souborů SDL, zjistíte, že v podstatě všechny funkce pracující se soubory představují pouze aliasy pro načítání ze SDL_RWops. To samé platí pro rozšiřující knihovny, jako jsou SDL_image, SDL_sound, SDL_ttf a další. Například SDL_LoadBMP() je pouze souborová specializace SDL_LoadBMP_RW().

SDL_Surface *SDL_LoadBMP_RW(SDL_RWops *src, int freesrc);

#define SDL_LoadBMP(file) \
    SDL_LoadBMP_RW(SDL_RWFromFile(file, "rb"), 1) 

Vypisovat seznam všech těchto funkcí je v podstatě zbytečné. Většinou by mělo stačit přidat ke jménu příponu ‚_RW‘ a místo řetězce se jménem předat ukazatel na SDL_RWops. Pokud nebude tato technika úspěšná, v některém z hlavičkových souborů lze vždy najít přesnou deklaraci.

Než se se SDL_RWops úplně rozloučíme, nelze neuvést odkaz na výborný tutoriál na kekkai.org (anglicky).

YUV video overlay

YUV video overlay je grafická struktura, která poskytuje hardwaru přímý přístup do paměti obrázku. Zjednodušeně řečeno, místo aby se při zobrazování všechny pixely zdlouhavě kopírovaly na určité místo na grafické kartě, program pouze oznámí jejich adresu v paměti a o nic dalšího se nestará. Mnohem vyšší rychlost předurčuje použití při přehrávání videa, jak je patrné už z názvu.

typedef struct
{
    Uint32 format;      // Formát
    int w, h;       // Rozměry
    int planes;     // Počet rovin (obyčejně jedna nebo tři)
    Uint16 *pitches;    // Pole pitch
    Uint8 **pixels;     // Pole ukazatelů na data pro každou rovinu
    Uint32 hw_overlay:1;    // Hardwarově akcelerovaný?
} SDL_Overlay; 

Kromě pixelů jsou všechny položky pouze pro čtení, k těm se ale může přistupovat až po zamknutí struktury. Atribut format může nabývat následujících hodnot, více informací lze najít na webartz.com.

#define SDL_YV12_OVERLAY 0x32315659 // Planar mode: Y + V + U
#define SDL_IYUV_OVERLAY 0x56555949 // Planar mode: Y + U + V
#define SDL_YUY2_OVERLAY 0x32595559 // Packed mode: Y0+U0+Y1+V0
#define SDL_UYVY_OVERLAY 0x59565955 // Packed mode: U0+Y0+V0+Y1
#define SDL_YVYU_OVERLAY 0x55595659 // Packed mode: Y0+V0+Y1+U0 

Overlay se vytváří funkcí SDL_CreateYUVO­verlay(). Její parametry definují rozměry, formát a surface, na kterém bude zobrazen. Vzhledem k tomu, že je overlay vytvořen v hardwaru, bude při zobrazení oblast surfacu pod ním přepsána a její obsah není definován. Pro následné uvolnění slouží SDL_FreeYUVOver­lay().

SDL_Overlay *SDL_CreateYUVOverlay(int width, int height,
        Uint32 format, SDL_Surface *display);

void SDL_FreeYUVOverlay(SDL_Overlay *overlay); 

Při přímém přístupu k pixelům je vždy nutné overlay uzamknout.

int SDL_LockYUVOverlay(SDL_Overlay *overlay);
void SDL_UnlockYUVOverlay(SDL_Overlay *overlay); 

Overlay se zobrazuje funkcí SDL_DisplayYU­VOverlay(), pozice a velikost cílové oblasti se specifikuje obdélníkem dstrect. Pokud bude mít overlay jinou velikost než cílová oblast, bude automaticky roztáhnut (max. 2×). Funkce vrací v případě úspěchu nulu.

int SDL_DisplayYUVOverlay(SDL_Overlay *overlay,
            SDL_Rect *dstrect); 

Pozn.: Z mého výkladu bylo asi poznat, že toho o overlay moc nevím :-(. Něco málo informací včetně několika odkazů lze najít v diskusi k osmému dílu, kde se toto téma probíralo.

Little/big endian

Hlavičkový soubor SDL_endian.h deklaruje funkce pro práci s daty ve formátech little a big endian, tyto dva pojmy se vztahují k pořadí jednotlivých bytů ve vícebajtových proměnných. Na některých platformách se ukládají důležitější byty na nižší adresy a na jiných je tomu právě naopak. Vzhledem k tomu, že je SDL multiplatformní, a tedy dostupné na obou typech systémů, je přítomnost těchto funkcí naprosto zásadní.

Aby mohla aplikace jednoduše zjistit, na kterém typu systému běží, poskytuje SDL symbolickou konstantu SDL_BYTEORDER, která může být nastavena buď na SDL_LIL_ENDIAN, nebo na SDL_BIG_ENDIAN.

Používáte-li pro načítání obrázků, zvuků a ostatních dat standardní SDL funkce, nemusíte se teoreticky o podobné záležitosti vůbec starat. Problémy však mohou nastat, pokud si píšete vlastní loadery. API je relativně jednoduché, a proto odkazuji zájemce, vzhledem k místu, na výše zmíněný hlavičkový soubor.

Proměnné prostředí

SDL poskytuje dvojici funkcí SDL_putenv() a SDL_getenv(), které umožňují zápis a čtení hodnot do/z proměnných prostředí. Při zápisu se předává řetězec ve formátu „jméno=hodnota“, čtení by mělo být jasné.

int SDL_putenv(const char *variable);
#define putenv(X) SDL_putenv(X)

char *SDL_getenv(const char *name);
#define getenv(X) SDL_getenv(X) 

V shellu je možné definovat proměnné určitých názvů, kterými lze změnit standardní chování SDL. V tomto seriálu jsme se už setkali se SDL_VIDEODRIVER a SDL_AUDIODRIVER specifikující video a audio ovladače, je jich však mnohem více. Podrobný seznam je možné najít v první sekci SDL dokumentace pod pojmem SDL_envvars.

Dynamické knihovny

Většinou se služby z externích knihoven poskytují aplikaci při překladu, v SDL je však možné zpřístupňovat knihovny i za běhu programu. Dynamická knihovna se nahrává funkcí SDL_LoadObject(), v jediném parametru se jí předává řetězec se jménem a cestou. Pro uvolnění slouží funkce SDL_UnloadObject().

void *SDL_LoadObject(const char *sofile);
void SDL_UnloadObject(void *handle); 

Ukazatel na funkci nacházející se ve sdílené knihovně je možné získat pomocí SDL_LoadFunction(). Parametry definují handle knihovny, ve které se má hledat, a řetězec se jménem funkce. Knihovna musí zůstat zavedená do paměti po celou dobu používání, pointer by přestal být validní.

void *SDL_LoadFunction(void *handle, const char *name); 

Informace o procesoru

A ještě bonus na závěr: Hlavičkový soubor SDL_cpuinfo.h obsahuje několik funkcí, kterými lze zjistit vlastnosti procesoru v počítači. Co která dělá, si jistě domyslíte sami.

SDL_bool SDL_HasRDTSC();
SDL_bool SDL_HasMMX();
SDL_bool SDL_HasMMXExt();
SDL_bool SDL_Has3DNow();
SDL_bool SDL_Has3DNowExt();
SDL_bool SDL_HasSSE();
SDL_bool SDL_HasSSE2();
SDL_bool SDL_HasAltiVec(); 

Ukázkové programy

Obrázky ze ZIP archivu

Program je modifikací ukázkového příkladu ze 13. dílu, obrázky se teď načítají pomocí SDL_RWops ze ZIP archivu, jinak žádná větší změna. Aby šel program zkompilovat, musí být v systému nainstalovaná knihovna zziplib. Je šířena pod licencí GNU LGPL a pracuje pod několika operačními systémy včetně GNU/Linuxu a MS Windows. (Zdrojový kód se zvýrazněním syntaxe.)

SDL 23

Download

Pokračování

Jak jsem zmínil na začátku, toto je poslední díl našeho seriálu o knihovně SDL. Po pravdě zbyly ještě dvě témata, která jsem chtěl původně zařadit, ale už se jim věnovat nebudu.

Prvním z nich je rozšiřující knihovna SDL_net pro implementaci síťových her. Bohužel jediné, co o ní v současné době vím, je to, že existuje – na komplexní článek docela málo.

ict ve školství 24

Druhým tématem měla být tvorba GUI. Pro SDL existuje hned několik knihoven na tvorbu tlačítek, editboxů a podobných věcí, většinu z nich lze najít v menu libraries na libsdl.org. Další možností by mohlo být napojení SDL aplikace na GTK nebo QT, popř. minulý týden jsem objevil rychle se rozvíjející C++ knihovnu Guichan podporující SDL, Allegro a OpenGL (dohromady nebo zvlášť). I toto rozsáhlé téma ale nechávám na samostudium.

Příště vyjde ještě jeden, opravdu závěrečný ;-) díl s obsahem a rejstříkem a pak se se seriálem o SDL budeme moci rozloučit …

Autor článku

Backend programátor ve společnosti Avast, kde vyvíjí a spravuje BigData systém pro příjem a analýzu událostí odesílaných z klientských aplikací založený na Apache Kafka a Apache Hadoop.