Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

4. diel - SDĽ - Práca s obrázkami

V dnešnom diele si povieme niečo o obrázkoch a ako s nimi pracovať. Pretože sa obrázok nahráva najprv do SDL_Surface, budú podobné techniky použité pre všetky vykresľovanie, ktoré prebieha najprv na SDL_Surface, a až potom na obrazovku. Tiež si ukážeme funkcie pre kopírovanie blokov pamäte, ktoré budeme využívať najčastejšie.

Načítanie a vykreslenie .bmp obrázku

Najskôr si v zložke projektu vytvoríme novú zložku a pomenujeme ju Pictures. Túto zložku budeme tiež kopírovať do výstupnej zložky, preto pridáme príkaz pre jej skopírovanie do Pre-build eventu - xcopy "$(ProjectDir)Pictures" "$(TargetDir)Pictures"/e /i /f /y.

V aplikácii budeme potrebovať obrázok, ktorý vykreslíme. Využijeme zdroj z minulého dielu. V zazipované zložke sú rôzne PNG súbory, jeden z nich si vyberieme a vložíme ho do zložky Pictures. Najprv ale budeme potrebovať obrázok vo formáte BMP, preto tento obrázok skonvertuje - napríklad v obyčajnom maľovaní. Teraz máme v priečinku Pictures dva rovnaké obrázky, jeden vo formáte PNG a druhý vo formáte BMP.

Teraz si tento obrázok vykreslíme na obrazovku. Najprv budeme potrebovať obrázok načítať, k tomu slúži funkcia SDL_LoadBMP. Tá načíta obrázok zo súboru do SDL_Surface. Povedali sme si však, že SDL_Renderer dokáže pracovať iba s SDL_Texture. Musíme vytvoriť SDL_Texture z SDL_Surface, k tomu slúži funkcia SDL_CreateTex­tureFromSurfa­ce. Teraz máme uložený obrázok v pamäti grafickej karty a teda máme k dispozícii hardvérovo-akcelerované vykresľovanie.

SDL_Surface* SurfaceWithPicture = SDL_LoadBMP("Pictures/healer_f.bmp");
SDL_Texture* TextureWithPicture = SDL_CreateTextureFromSurface(renderer,SurfaceWithPicture);

Vykreslení obrázka

K samotnému vykreslenie na obrazovku budeme potrebovať iba jedinú funkciu - SDL_RenderCopy. Položky SDL_Renderer a SDL_Texture sú povinné. V našom prípade sa bude vykresľovať TextureWithPicture do SDL_Renderer okna. Ak jeden z posledných dvoch parametrov vynecháme, znamená to, že sa má použiť celá plocha. Podrobnejšie, keď vynecháme parameter zdroje, použije sa celá textúra, ak vynecháme parameter ciele, vykreslí sa obraz cez celú obrazovku. Ak budeme chcieť vykresliť len časť obrazu, musíme funkciu dodať tretí parameter (ukazovateľ na SDL_Rect). Naopak ak chceme, aby sa obrázok vykreslil na určité miesto, musíme dodať posledný parameter (opäť ukazovateľ na SDL_Rect). Najlepšie o efekte vypovedá nasledujúci obrázok.

Render copy demo - SDĽ

Všimnime si, že SDL automaticky zväčšuje obrázok tak, aby sa celý vošiel do pripraveného miesta. Vo výsledku teda môžeme byť veľkosť zdrojového obdĺžnika iná ako veľkosť obdĺžnika cieľového. Nemusí byť dokonca zachované ani proporcie (rozmery strán), SDL obrázok automaticky transformuje podľa potreby.

Tiež je potrebné spomenúť situáciu, kedy sa obdĺžnik dostane mimo rozsahu plochy. Ak sa to stane u ciele, nič zásadné sa nedeje. Jednoducho sa len obrázok oreže a vizuálny efekt vyzerá, ako by bol obrázok mimo okno (schovaný za rámom). Avšak ak táto situácia nastane pre zdrojový obraz, obdĺžnik sa oreže podľa okraja plochy. Ak bol obdĺžnik široký 50 bodov a 10 bodov presahuje pravý okraj obrázku, bude výsledok rovnaký, ako keby sme mali obdĺžnik na rovnakých súradniciach, ale o 10 bodov užší. Tento jav je ukázaný na nasledujúcom obrázku. Vidíme, že sa obrázok roztiahol do šírky, ale pritom stále zaberá celé okno.

Presahujúce zdrojový obdĺžnik - SDĽ

Načítanie iných formátov obrázkov

SDL v základe podporuje obrázky iba formáte BMP kvôli ich jednoduché kompresiu a formátu. Pokiaľ budeme chcieť použiť aj iné obrázky, budeme potrebovať rozšírenie k SDL, knižnicu SDL_image. Stiahneme si vývojársku verziu (Development Libraries) pre Visual Studio tu. Rovnako ako pre samotné SDL pridáme zložku include do "* Include Directories " a zložku lib / x64 do " Library Directories ". Přilinkujeme ďalšie knižnicu SDL2_image.lib v nastavení Linker - Input - Additional Dependencies. Ešte nezabudneme skopírovať všetky dynamické knižnice (.dll súbory) do zložky Output v projekte. Tentoraz okrem *SDL2_image.dll máme aj ďalšie. SDL2 musí vedieť pracovať rôznymi formátmi súborov a pre každý formát je pre lepšiu prehľadnosť a oddelenie kódu vytvorená samostatná dynamická knižnica.

Dokumentácia SDL2_image je pomerne jednoduchá, rovnako ako jej použitie. Tak ako u samotného SDL najskôr zavoláme metódu IMG_Init a IMG_Quit na konci programu.

Najčastejšie budeme nahrávať nový súbor. K tomu nám stačí jednoduchá funkcie IMG_Load. Funkcia sama rozozná, o aký formát súboru ide, a automaticky sa postará o práca s otvorením a zatvorením súboru. Nám sa potom vráti iba ukazovateľ na SDL_Surface, v ktorom je nahraný obrázok. Všetky ostatné funkcie prijímajú ako parameter ukazovateľ na SDL_RWops, ktorý v SDL slúži pre abstrakciu práce so súbormi. Dostaneme sa k nemu v jednom z ďalších dielov.

Posledná skupina funkcií, ktoré knižnica SDL_Image obsahuje, sú funkcie pre validáciu formátu. Pre každý typ existuje samostatná funkcia, ktorá vždy končí koncovkou súboru (IMG_isPNG, IMG_isJPG, IMG_isGIF). Všetky tieto funkcie prijímajú ako parameter SDL_RWops, preto sa nimi nebudeme zaoberať.

Teraz už by sme mali byť schopní načítať obrázok akéhokoľvek formátu a vykresliť ho na obrazovku. Najskôr knižnicu SDL_Image inicializujeme (IMG_Init), potom načítame obrázok do SDL_Surface (IMG_Load). Ďalej je postup rovnaký, ako sme použili pre BMP obrázok.

Vykresľovanie na vrstvy

V dnešnej ukážke spojíme všetky znalosti dohromady. Využijeme obrázku postavy, ktorý už máme, a pridáme k tomu obrázok pozadia - pre ukážku použijem tento. Stiahnem ho a pridám do zložky Pictures a aj do projektu (pre pohodlie).

Povedzme, že máme túto situáciu: Chceme vykresliť postavu (druhú zľava, tretí odhora) a pritom za túto postavu vykresliť pozadie, ktoré sme si stiahli. Najprv si obaja obrázky načítame do SDL_Surface funkcií IMG_Load. Pretože budeme chcieť zachovať proporcie pozadie, musíme zistiť veľkosť obrázka. Vieme, že obrázok je postavený na výšku, preto dlhšia hrana bude vertikálne. Tiež vieme, že sú štyri postavy nad sebou, teda výšku jednej postavy zistíme vydelením výšky celého obrázka štyrmi. To isté vykonáme pre šírku. Pretože už postava zostane tak, ako sme ju vybrali (nebude sa meniť postava ani pozadia), uložíme ju do SDL_Texture. Najskôr ale vytvoríme novú SDL_Surface s rozmermi dlhšej strany postavy. Potom využijeme funkcie SDL_BlitSurface, pomocou ktorej prekopíruje obsah z oboch SDL_Surface do novovytvorenej. Nesmieme zabudnúť, že chceme skopírovať iba jednu postavu. Zároveň teda budeme musieť vytvoriť obdĺžnik, z ktorého sa bude kopírovať. Po kopírovanie novú SDL_Surface prevedieme na SDL_Texture a vykreslíme.

//do kterých míst obrazovky se bude vykreslovat
SDL_Rect* TargetRectangle = new SDL_Rect;
TargetRectangle->x = TargetRectangle->y = 0;

//načtení obrázků
SDL_Surface* SurfaceWithPicture = SDL_LoadBMP("Pictures/healer_f.bmp");
SDL_Surface* SurfaceWithBackground = IMG_Load("Pictures/GreenBlackBG_0.png");

//zjištění rozměrů postavy
int CharacterHeight = SurfaceWithPicture->h / 4;
int CharacterWidth = SurfaceWithPicture->w / 3;

//nastavení rozměrů obdélníku podle velikosti postavy, pro lepší viditelnost bude 5x větší.

//nastavení obdelníku, ze kterého se bude vykreslovat
SDL_Rect* CharacterSourceRect = new SDL_Rect;
CharacterSourceRect->x = 1 * CharacterWidth;
CharacterSourceRect->y = 2 * CharacterHeight;
CharacterSourceRect->w = CharacterWidth;
CharacterSourceRect->h = CharacterHeight;

//vytvoření konečné Surface, která bdue převedena na SDL_Texture
SDL_Surface* FinalSurface=SDL_CreateRGBSurface(NULL,
                             CharacterHeight,CharacterHeight,
                 32,
                 0,0,0,0);

//zkopírování obrazů do finální surface
SDL_BlitSurface(SurfaceWithBackground, NULL, FinalSurface, NULL);
SDL_BlitSurface(SurfaceWithPicture,CharacterSourceRect,FinalSurface,NULL);

//vytvoření textury, která se bude vykreslovat na obrazovku
SDL_Texture* TextureWithCharacterAndBackground = SDL_CreateTextureFromSurface(renderer,FinalSurface);
prvý pokus - SDĽ

Vidíme, že sa nám zároveň s obrázkom vykreslilo i biele pozadie. Keby sme nahrali obrázok vo formáte PNG s priehľadným pozadím, tak bude aj vykreslené pozadie priehľadné. Prečo sme teda nahrali BMP obrázok? Ukážeme si dve funkcie, už spomínanou SDL_SetColorKey, ktoré nastaví určitú farbu ako priehľadnú. K tomu budeme potrebovať túto farbu definovať. O to sa nám postará funkcia SDL_MapRGB. Nesmieme zabúdať, že obrázok môže byť uložený v niekoľkých formátoch, preto ako parameter odovzdáme formát SDL_Surface. Nasledujúci kód pridáme pred časť kódu, kde sa kopíruje SDL_Surface do finálnej SDL_Surface.

Uint32 WhiteColor = SDL_MapRGB(SurfaceWithPicture->format, 255, 255, 255);
SDL_SetColorKey(SurfaceWithPicture, SDL_ENABLE, WhiteColor);
druhy pokus - SDĽ

Teraz opravíme ešte posledné dva detaily. Jednak vidíme, že pozadie postavy je čierne, ale my sme chceli použiť celý obrázok. Príčina je vo funkcii SDL_BlitSurface, ktorá zachováva proporcie - skopírovala teda len ľavý horný roh obrázku. Ak budeme chcieť obrázok zmenšiť, musíme použiť funkciu SDL_BlitScaled, ktorá obrázok zmenší do požadovanej veľkosti. Chová sa vlastne ako SDL_RenderCopy. Prečo teda existujú dve metódy? Ako som už spomenul, ak sa obrázok transformuje na novú veľkosť, vyžaduje to nejaký výkon. Z toho vyplýva, že funkcia SDL_BlitSurface bude rýchlejší než SDL_BlitScaled, pretože nevykonáva toľko operácií.

Predposledný vec je pozícia postavy. Nie je úplne vycentrovaná doprostred. Tu si vystačíme s SDL_Rect a jednoduchými matematickými počty. Podľa šírky postavy a celkovej šírky nové SDL_Surface určíme nové súradnice postavy.

Posledná vec nie je vidieť, ale je tiež veľmi dôležitá. Do doby, než získame SDL_Texture s obrázkom, sme vytvorili dva SDL_Rect a tri SDL_Surface. Tie už ďalej v programe potrebovať nebudeme, preto ich musíme uvoľniť funkcií SDL_FreeSurface. Teraz už máme všetko hotové. Prikladám finálna časť kódu a výstup.

//načtení obrázků
SDL_Surface* SurfaceWithPicture = SDL_LoadBMP("Pictures/healer_f.bmp");
SDL_Surface* SurfaceWithBackground = IMG_Load("Pictures/GreenBlackBG_0.png");

//nastavení barvy, která bude průhledná
Uint32 WhiteColor = SDL_MapRGB(SurfaceWithPicture->format, 255, 255, 255);
SDL_SetColorKey(SurfaceWithPicture, SDL_ENABLE, WhiteColor);

//zjištění rozměrů postavy
int CharacterHeight = SurfaceWithPicture->h / 4;
int CharacterWidth = SurfaceWithPicture->w / 3;

//vytvoření obdelníka, na který se bude vykreslovat na obrazovku
SDL_Rect* TargetRectangle = new SDL_Rect;
TargetRectangle->x = TargetRectangle->y = 0;
TargetRectangle->w = TargetRectangle->h = CharacterHeight * 5;

//obdelník, ve kterém je naše postavave na zdrojovém obrázku
SDL_Rect* CharacterSourceRect = new SDL_Rect;
CharacterSourceRect->x = 1 * CharacterWidth;
CharacterSourceRect->y = 2 * CharacterHeight;
CharacterSourceRect->w = CharacterWidth;
CharacterSourceRect->h = CharacterHeight;

//vytvoření SDL_Surface, která se poté změní na SDL_Texture
SDL_Surface* FinalSurface=SDL_CreateRGBSurface(NULL, CharacterHeight,CharacterHeight, 32, 0,0,0,0);

//Vytvoření obdelníku, na který se bude malovat tak, aby byla postava uprostřed
SDL_Rect* CharacterTargetRect = new SDL_Rect;
CharacterTargetRect->h = CharacterHeight;
CharacterTargetRect->w = CharacterWidth;
CharacterTargetRect->y = 0;
CharacterTargetRect->x = (FinalSurface->w – CharacterWidth) / 2;

//překreslení - nejprve pozadí, potom postava
SDL_BlitScaled(SurfaceWithBackground, NULL, FinalSurface, NULL);
SDL_BlitSurface(SurfaceWithPicture,CharacterSourceRect,FinalSurface,CharacterTargetRect);

//vytvoření SDL_Texture z SDL_Surface
SDL_Texture* TextureWithCharacterAndBackground = SDL_CreateTextureFromSurface(renderer,FinalSurface);

//Uvolnění prostředků
SDL_FreeSurface(SurfaceWithPicture);
SDL_FreeSurface(SurfaceWithBackground);
SDL_FreeSurface(FinalSurface);
delete CharacterSourceRect;
delete CharacterTargetRect;
finalni pokus - SDĽ

Teraz už sme schopní vykresliť ľubovoľný obrázok na obrazovku. V budúcom dieli sa pozrieme, akým spôsobom budeme vykresľovať text. Využijeme to k vypísanie FPS (Frames per second - počet snímok za sekundu) na obrazovku.


 

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 718x (5.75 MB)

 

Predchádzajúci článok
SDĽ - Základy vykresľovanie
Všetky články v sekcii
SDĽ
Preskočiť článok
(neodporúčame)
SDĽ - Vykreslenie textu
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity