Grafické aplikace ve Visual C++ (3.)
V této lekci si konečně povíme o základních pojmech a principech DirectDraw. V první části si
rozebereme důležité pojmy jako jsou například povrchy (surfaces) a v druhé části vám vysvětlím,
jak vlastně DirectDraw pracuje. Nakonec si ukážeme konkretní příklad, jak vytvořit objekt DirectDraw
a nastavíme další vlastnosti aplikace. Popisuju zde jen režim fullscreen (celoobrazovkový režim),
ale DirectDraw samozřejmě pracuje i v okně.
3.1. Základní pojmy
Základem každé aplikace založené na principu DirectDraw je jeden objekt typu IDirectDraw7
(je to vlastně ukazatel na rozhraní COM),
pomocí kterého vytvoříte všechny ostatní objekty potřebné pro kreslení apod. Takže první, co musíte
udělat je, že vytvoříte tento objekt.
Sprite je vlastně vámi vykreslovaný obrázek. Sprite je většinou
vytvořen z bitmapy, kterou načtete z externího souboru. Pokud budete mít
pohybující se letadlo, právě letadlo bude sprite.
Další velmi důležitou součástí je tzv. povrch (surface). Povrch je vlastně buffer tzn. kousek
paměti, buď ve video paměti nebo v systémové RAM. Je to něco podobného jako kontext zařízení (DC - viz minulá lekce).
Do tohoto bufferu zapisujete data, které posléze vidíte na monitoru v podobě obrázku.
Pokud budete používat 8-bitovou barevnou hloubku tzn. 256 barev, měli byste
vytvořit objekt palety. Pak je tento objekt velice důležitý, jinak se
bitmapy zobrazí chybně (mají proházené barvy a některé barvy dokonce chybí). Program, který pracuje
v 16-bitech (65 tisíc barev) žádnou paletu nepotřebuje, ale běží pomaleji, takže pokud
nevlastníte moc vykonný počítač doporučuji používat 8-bitů.
Dalším užitečným objektem je tzv. clipper. Abyste pochopili jak pracuje musíte znát
následující látku. Prozatím vám musí stačit, že zajišťuje tzv. clipping, což už jste
možná slyšeli. Jednoduše řečeno Clipper zabraňuje vykreslování spritů mimo monitor.
3.2. Základní principy DirectDraw
Jak jsem se již zmínil DirectDraw, dokáže vykreslovat pouze ve 2D tzn., že všechny
povrchy jsou ploché (mají dvě souřadnice x a y). Bitové kopírování mezi povrchy, což je
vlastně kopírování
jednotlivých bitů vašich bitmap, se v terminologii DirectDraw nazývá blitting
(bit block transfer - přenos bloku bitů).
Máme dva základní povrchy :
-
Front surface (FS)
-
Back surface (BS)
Front surface (přední povrch) je, jak napovídá název, ten povrch, který je právě
vidět na monitoru. Back surface (zadní povrch) je ukryt někde v paměti jakoby za
FS. Důležité je, že vždy zapisujete jen do BS nikdy do FS. Kdybyste totiž zapisovali
do FS přímo, docílili byste podobného efektu jako u GDI.
Takže vy zapíšete nějaké data do BS, ale co dál?
DirectDraw má jistou funkci, která dokáže oba povrchy prohodit. Takže FS je pak
vzadu a není vidět a BS je vpředu a normálně viditelný. Toto prohození je velmi
rychlé takže lidské oko ho samozřejmě nepostřehne. Funkce navíc vždy počká na tu
kratičkou dobu, kdy se paprsek monitoru přesouvá z pravého dolního rohu do levého horního,
takže vlastně nemáte žádnou šanci postřehnout nějaké problikávání ani kdybyste se rozkrájeli.
Tomuto systému prohazování povrchů se říká flipping. Flipping totiž prohazuje ukazatele na
oba povrchy a nepoužívá
běžný blitting, který se používá pro přenos bitů mezi povrchy a který je časově mnohem náročnější.
Velikost těchto dvou bufferů záleží na rozlišení, které právě používáte.
Tyto buffery se zásadně vytváří ve video paměti, která je rychlejší
než systémová RAM, zvláště u moderních grafických karet.
Dále si většinou vytváříte tzv. OffScreen surfaces, což jsou povrchy, které
jsou mimo flipovací smyčku. Tyto povrchy používáme pro bitmapy, které teprve
budeme chtít zobrazit tzn. jako pomocné buffery. Z těchto povrchů se pak
blitují data do BS. Tyto buffery se vytvářejí v RAM a tudíž mohou být větší
než FS a BS. Ve skutečnosti se vám nepodaří ve video paměti vytvořit buffer,
který je větší než FS. Ledaže máte grafickou kartu, která má hodně paměti a
podporuje funkci Wide surfaces (široké povrchy).
Celý systém vypadá následovně :
Pokud budete mít rozlišení 640x480 a 8-bitů barev (256 barev) FS vám zabere
přesně 640 x 480 (plus nějaké informace o povrchu), což je asi 300 kB paměti tzn.
že když vytvoříte FS a BS musíte
mít alespoň 600 kB video paměti na grafické kartě. Pokud tomu tak není, budete
muset vytvořit BS v RAM, ale to se silně nedoporučuje. Samozřejmě pokud zvýšíte
rozlišení nebo hloubku barev, paměťové nároky se rovněž zvýší, takže na to pozor.
Dnešní grafické karty mívají běžně alespoň 16 MB paměti a tam se vám to vejde s přehledem.
Dále je možno vytvořit více BS například dva, pak se systému říká Triple buffering
(máme celkem tři buffery : dva back + jeden front) a to už jste určitě slyšeli.
Každá aplikace DirectDraw pracuje tak, že se snaží prohazovat oba povrchy jak
nejrychleji to jde a přitom se občas něco zapíše do BS. Prohazovací funkce čeká
až se dokreslí všechny blittovací operace, takže se nemusíte bát, že by se něco
nedokreslilo nebo snad dokonce problikávalo! Čím více toho vykreslujete, tím
je prohazování pomalejší.
Jak ale zařídit toto rychlé prohazování?
Určitě nezkoušejte psát nekonečnou smyčku for(;;) , protože pak by se aplikace
zablokovala a nepřijímala by žádné zprávy Windows a ty budeme potřebovat.
Třída CWinApp má členskou funkci Run() , která
spouští smyčku zpráv. Vy tuto funkci
můžete přepsat a upravit tak, že aplikace normálně zpracovává zprávy Windows (windows
messages), ale navíc volá vaši funkci, která obnovuje obraz a prohazuje povrchy. Toto
provádí pouze pokud nemá, co na práci tzn., že nepřicházejí žádné zprávy od Windows.
3.3. Objekt DirectDraw
Takže teď již víme, že každá
aplikace založená na DirectDraw má jeden objekt
typu IDirectDraw7 .
Jak ale tento objekt vytvoříme?
Za prvé musíme
vložit hlavičkový soubor ddraw.h (nejlépe do souboru
StdAfx.h ) a za druhé přilinkovat dynamické knihovny
ddraw.dll a dxguid.dll .
Knihovny se vkládají v menu Project/Settings.
Na kartě Link se do políčka Object/library modules vloží
ddraw.lib a dxguid.lib .
To je vše. Teď nám kompilátor automaticky vloží
hlavičkový soubor ddraw.h a linker přilinkuje knihovnu
ddraw.dll a dxguid.dll .
Poznámka: Pro níže uvedenený kód musíte
mít nainstalované DirectX SDK 8.0, které bylo na řijnovém ChipCD.
Dále zkontrolujte v Options VC++, že na kartě Directories, je
cesta k hlavičkovým souborům naistalovaného SDK, pokud ne, musíte
položku přidat sami.
Nyní můžeme použít globální funkci DirectDrawCreateEx() k vytvoření objektu DirectDraw.
Funkce má následující deklaraci :
HRESULT DirectDrawCreateEx( GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter );
-
První parametr je globální unikátní identifikátor grafického ovladače.
Pokud zadáte NULL , program vybere současný grafický ovladač.
Navíc můžete zadat tyto hodnoty :
-
DDCREATE_EMULATIONONLY – využívá se pouze softwarová
emulace tzn. že není
podporováno žádné hardwarové urychlování
-
DDCREATE_HARDWAREONLY – využívá se pouze hardwarové
vybavení grafické karty,
pokud karta nepodporuje dané prvky, funkce vrací
DDERR_UNSUPPORTED .
-
Druhý parametr je ukazatel na ukazatel na objekt DirectDraw, který chceme vytvořit.
Ve třídě CControl si deklarujeme členskou proměnou typu
LPDIRECTDRAW7 m_lpDD , což je ukazatel
na DirectDraw. Tento ukazatel potom dosadíme jako druhý parametr funkce. Pozor! Je to
ukazatel na ukazatel!.
-
Tento parametr musí být nastaven na hodnotuIID_IDirectDraw7 .
Je identifikátor rozhraní COM.
-
Třetí parametr je pro integraci programování technologií COM (Component
Object Model).
Nyní funkce vrací chybu pokud zadáte něco jiného než NULL .
3.4. Nastavení dalších vlastností aplikace
Pomocí ukazatele na objekt DirectDraw nastavíme, jak se bude naše
aplikace chovat vůči jiným programům Windows a
jaké rozlišení a hloubku barev budeme používat.
Spoluprácí s ostatními programy zajišťuje tato funkce:
HRESULT SetCooperativeLevel(HWND hWnd, DWORD dwFlags);
Funkce má následující dva parametry :
-
hWnd - To jest handle na hlavní rámcové okno – ten se zjistí velmi jednoduše:
zavolejte funkci GetSafeHwnd() , která vrátí platný handle.
-
dwFlags - Příznaky, které popisují, jak se vaše
aplikace chová k ostatním. Mohou se samozřejmě
kombinovat. My použijeme následující :
-
DDSCL_EXCLUSIVE – zajistí, že aplikace má výhradní právo. Tento příznak
musí být použit s DDSCL_FULLSCREEN
-
DDSCL_FULLSCREEN – indikuje, že aplikace bude fullscreen (celoobrazovková).
Musí být samozřejmě použit s DDSCL_EXCLUSIVE .
-
DDSCL_ALLOWREBOOT – povoluje klávesovou zkratku Ctrl + Alt + Delete při
výhradním režimu tzn. že můžete z aplikace vyskočit přes Správce úloh. To je důležité,
když nevíte jestli vám aplikace náhodou neshodí Windows.
Příznaků je mnohem víc, ale my si vystačíme s těmito třemi.
Druhým velice důležitým nastavením je rozlišení a hloubka barev.
Dále tedy nastavíme grafický režim pomocí této funkce:
HRESULT SetDisplayMode(DWORD dwWidth,
DWORD dwHeight,
DWORD dwBPP,
DWORD dwRefreshRate,
DWORD dwFlags);
Tato funkce má pět parametrů:
-
dwWidth - Rozlišení v horizontálním směru v pixelech.
Můžete hodnotu načíst z registrů, z ini souboru nebo si definovat konstanty v souboru.
Možností je spoustu a zaleží na vás, jakou se vyberete. V příkladu definuji konstantu 640 pixelů.
-
dwHeight - Rozlišení ve vertikálním směru v pixelech. Nastavíme 480 pixelů.
-
dwBPP - Hloubka barev v bitech na pixel. Nastavíme 16 bitů.
-
dwRefreshRate - Obnovovací frekvence monitoru. Zde můžete nastavit obnovovací
frekvenci se kterou se bude váš monitor obnovovat. Problém je v tom, že pokud nastavíte natvrdo nějaké
číslo, nemusí (a taky nejspíš nebude) program běhat na jiném počítači, protože ne všechny monitory
zvládají vaše nastavení. Když zadáte 0, použije standardní nastavení DirectDraw (toto nastavení se dá
nastavit v DXDiag). Takže nastavte 0.
-
dwFlags - Příznaky pro budoucí použití:) Nastavte 0.
3.5. Příklad
Nyní dáme všechny nové poznatky dohromady.
Ve třetím kurzu o VC++ jste vytvořili třídu CControl, kde vytvořte členskou funkci třeba
DDInit(HWND hWnd) .
Budete ji volat vzápětí za funkcí WinInit() z
InitInstance() a předáte ji handle vašeho okna.
Ve třídě CControl také vytvořte členskou proměnnou
m_lpDD typu LPDIRECTDRAW7 a nadefinujte
konstanty RES_X = 640, RES_Y = 480 a RES_BITDEPTH = 16 .
HRESULT CControl::DDInit(HWND hWnd)
{
HRESULT dwResult;
// DirectDraw object creation
dwResult = DirectDrawCreateEx(NULL, (void**)&m_lpDD, IID_DirectDraw7, NULL);
if(dwResult != DD_OK) {
TRACE("Cannot create direct draw object due %d\n", dwResult);
return dwResult;
}
// Setting cooperative level
dwResult = m_lpDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE|
DDSCL_FULLSCREEN|
DDSCL_ALLOWREBOOT);
if(dwResult != DD_OK) {
TRACE("Cannot set cooperative level due %d\n", dwResult);
return dwResult;
}
// Setting display mode
dwResult = m_lpDD->SetDisplayMode(RES_X,RES_Y, RES_BITDEPTH, 0, 0);
if(dwResult != DD_OK) {
TRACE("Cannot set display mode due %d\n", dwResult);
return dwResult;
}
return dwResult;
}
4 . Závěr
Aplikace z dnešní lekce akorát přepne rozlišení, nic víc.
Přístě si vytvoříme front a back buffer a vytvoříme flipovací smyčku.
Těším se příště nashledanou.
|