GrafickΘ aplikace ve Visual C++ (6.)


V tΘto lekci se posuneme o velk² krok kup°edu, proto₧e vßm ukß₧u zdrojovΘ k≤dy, kterΘ vßm velmi usnadnφ prßci s Direct Draw objekty. VeÜker² k≤d je souΦßstφ SDK. Dßle se blφ₧e podφvßme na funkci Blt().

6.1.T°φdy CDisplay a CSurface

Bylo napsßno pßr jednoduch²ch funkcφ, kterΘ vßm vÜak velmi usnadnφ ₧ivot s DirectDraw. ZdrojovΘ k≤dy t∞chto funkcφ jsou souΦßstφ SDK, kterΘ jste instalovali na zaΦßtku naÜeho povφdßnφ. KonkrΘtn∞ byste je m∞li najφt v adresß°i SDK na disku. Pokud je tedy chcete pou₧φvat, je asi nejlepÜφ si do projektu zkopφrovat soubory ddutil.hddutil.cpp a dxutil.h. Jako celΘ DD i tyto soubory proÜly celkem radikßlnφm v²vojem. Na poΦßtku obsahovaly pouze globßlnφ funkce, nynφ obsahujφ dv∞ t°φdy, kterΘ se kompletn∞ starajφ o grafick² interface a povrchy.

Vlo₧te tedy oba soubory do vaÜeho projektu, nejlΘpe ve FileView p°es popup menu, kde vyberte polo₧ku Add files to project. V ClassView by se m∞li objevit dv∞ novΘ t°φdy: CDisplay a CSurface. Aby Üel projekt zkompilovat, musφte vypnout pro soubor ddutil.cpp p°edkompilovanΘ hlaviΦky, nebo do souboru vlo₧it stdafx.h vaÜeho projektu. DoporuΦuji ud∞lat to prvnφ. To ud∞lßte v menu Project Settings..., kde v levΘm okΘnku najdete po₧adovan² soubor a vpravo vyberete druhou kartu, kde zvolφte Precompiled header, kde zvolφte prvnφ volbu Not using precompiled header. Pro snazÜφ orientaci jsem vßm stßhl tento obrßzek:

Nynφ si zkuste, zdali vßm p∙jde projekt zkompilovat.

Cel² systΘm je velice jednoduch². StaΦφ do souboru control.h vlo₧it hlaviΦkov² soubor  ddutil.h a m∙₧ete zaΦφt. Vlo₧te tedy nßsledujφcφ °ßdek n∞kam na zaΦßtek souboru control.h:
#include "ddutil.h"

A nynφ ji₧ budete jen mazat k≤d, kter² jsme vytvo°ili v minul²ch lekcφch, proto₧e vÜe obstarß t°φda CDisplay a CSurface.

1. Vytvo°te objekt m_theDisplay (typu CDisplay) ve t°φd∞ CControl. PotΘ vlo₧te do funkce DDInit() volßnφ funkce CreateFullScreenDisplay(), jen₧ je Φlenskß funkce t°φdy CDisplay. Funkce p°ijφmß celkem 4 parametry. za prvΘ je to HANDLE naÜeho okna, kter² p°ijφmß i naÜe funkce DDInit(), dßle jsou to rozm∞ry obrazovky v pixelech, vy m∙₧ete zadat konstanty definovanΘ v²Üe. Jsou to RES_X, RES_Y a RES_BITDEPTH. To je vÜe, zbytek funkce a₧ na poslednφ °ßdek vyma₧te.

2. Vytvo°φme pozadφ. Ve t°φd∞ CControl deklarujte prom∞nnou typu ukazatel CSurface m_surBackground. Ve funkce DDInit() vytvo°te dynamicky objekt CSurface a zavolejte funkci CreateSurfaceFromFile()  objektu m_theDisplay. Funkce mß op∞t 4 parametry. Prvnφ je ukazatel na ukazatel na objekt povrchu, druh² je °et∞zec bitmapy, kterß se nahraje do povrchu a poslednφ dva parametry jsou po₧adovanΘ rozm∞ry povrchu, pokud zde zadßte 0, vytvo°φ se povrch o velikosti bitmapy. Dejte pozor aby na konci funkce DDInit() z∙stal °ßdek, kter² aktivuje smyΦku zprßv!

Funkce DDInit() vypadß po ·prav∞ takto:


HRESULT CControl::DDInit(HWND hWnd)
{
    HRESULT dwResult;
   
// 
    // Inicializace grafickeho rozhrani DD

    dwResult = m_theDisplay.CreateFullScreenDisplay(hWnd, RES_X, RES_Y, RES_BITDEPTH);
    if(dwResult != DD_OK) {
        TRACE("Cannot init direct draw system due %d\n", dwResult);
        return dwResult;
    }
  
 // 
    // Vytvorime povrch pozadi

    m_surBackground = new CSurface;
    dwResult = m_theDisplay.CreateSurfaceFromBitmap(&m_surBackground, "background.bmp", 0, 0);
    if(dwResult != DD_OK) {
        TRACE("Cannot create background due %d\n", dwResult);
        return dwResult;
    }
  
 //
    // Spustime smycku zprav

    m_bReady = TRUE;

    return dwResult;
}



3. P°epφÜeme funkci UpdateFrame(). Tuto funkci upravte nßsledujφcφm zp∙sobem:

void CControl::UpdateFrame()
{
    if(m_bReady) {
      
//...
       CRect rcBackground;
       rcBackground.SetRect(0,0,RES_X, RES_Y);

       m_theDisplay.Blt(0, 0, m_surBackground, rcBackground);
      
//...
       //...

       m_theDisplay.Present();
    }
}


Vidφte, ₧e se od p∙vodnφ funkce moc neliÜφ, ale vyu₧φvß novΘ t°φdy CDisplay.

6.2 DalÜφ mo₧nosti t°φdy CDisplay

6.2.1 Paleta

Nynφ si povφme n∞co o paletßch. T°φdy CDisplay samoz°ejm∞ podporuje i palety. Nejprve musφte deklarovat objekt LPDIRECTDRAWPALETTE, kter² m∙₧e b²t lokßlnφ. PotΘ zavolßte funkci CreatePaletteFromBitmap(), kterß mß pouze dva parametry. Prvnφ je objekt LPDIRECTDRAWPALETTE, kter² jsme ji₧ deklarovali a druh² je °et∞zec bitmapy, ze kterΘ se paleta vytvo°φ.

D∙le₧itΘ je, ₧e funkce vytvo°φ objekt palety podle palety bitmapy a ne podle barev bitmapy! ╚asto se toti₧ stßvß, ₧e si vytvo°φme bitmapu, kterß obsahuje vÜechny barvy, kterΘ pot°ebujeme, ale paleta tΘto bitmapy z∙stane standardnφ tzn. paleta zßkladnφch 256 barev (jako ve Windows). Tak₧e musφte vytvo°it bitmapu v n∞jakΘm externφm rastrovΘm editoru (nap°. Malovßnφ, Photoshop) a ulo₧it s 256 barevnou paletou. Tento externφ editor pak vytvo°φ adaptivnφ paletu, kterß p°esn∞ odpovφdß vaÜφ bitmap∞. Kdy₧ ho pak importujete do Visual C++, m∙₧ete klidn∞ bitmapu palety zmenÜit na 1x1 pixel s libovolnou barvou, aby nezabφral cennΘ kB v .exe souboru. Takovß bitmapa si ovÜem uchovß svojφ paletu (vytvo°enou externφm editorem) a ta se takΘ pou₧ije v naÜφ funkci.

Dßle musφte zavolat Φlenskou funkci rozhranφ SetPalette(), kterß p°ijφmß p°esn∞ tento objekt. A to je vÜe - paleta je nastavena a pokud je sprßvn∞ vytvo°ena, vÜechny vaÜe bitmapy se zobrazφ ve sprßvn²ch barvßch. NejlepÜφ je ud∞lat jakousi kolß₧ ze vÜech pou₧it²ch bitmap a pak ji ulo₧it podle v²Üe vysv∞tlenΘho postupu v externφm editoru.

Nßsleduje jednoduch² p°φklad nastavenφ bitmapy:

// Musite mit predem zvolanou funkci CreateFullScreenDisplay()
LPDIRECTDRAWPALETTE lpDDPalette;

m_theDisplay.CreatePaletteFromFile(&lpDDPalette, "paleta.bmp");
m_theDisplay.SetPalette(lpDDPalette);

Pokud pracujete v 16-bitech (65 tis. barev), je nastavenφ palety zbyteΦnΘ. To znamenß, ₧e paletu mß smysl nastavovat jen v 8-bitech (256 barev) - ni₧Üφ barevnou hloubku ani neuvßdφm, kdo by cht∞l pracovat s 16 barvami. Naopak pokud pracujete v 8-bitech je paleta nutnß, proto₧e jinak se pou₧ijφ zßkladnφ barvy Windows a bitmapa se dost znehodnotφ (pokud se zrovna netrefφte).
 

6.2.2. Nastavenφ pr∙hlednΘ barvy neboli nastavenφ Color Key

Nastavenφ pr∙hlednΘ barvy je velmi d∙le₧itß vlastnost povrch∙ DirectDraw. Color key neboli barevn² klφΦ, oznaΦφ barvu v povrchu, kterou nechceme vykreslovat to znamenß, ₧e v²sledn² obrßzek bude v tomto mφst∞ pr∙hledn². Color key nastavujete pro ka₧d² vytvo°en² povrch zvlßÜ¥. My zatφm nemßme ₧ßdn² obrßzek, kter² bychom pot°ebovali vykreslovat transparentn∞. Princip color key spoΦφvß v oznaΦenφ pr∙hlednΘ barvy pomocφ makra RGB. Ve t°φd∞ CSurface objevφte funkci SetColorKey(), kterß p°ijφmß jeden parametr prßv∞ jako barvu.
 

// Musite mit predem zvolanou funkci CreateFullScreenDisplay()
//definice objektu povrchu

CSurface *lpMySurface = new CSurface;

//vytvoreni povrchu a prirazeni ukazatele, povrch je inicializovan bitmapou

m_theDisplay.CreateSurfaceFromFile(&lpMySurface, "pokus.bmp", 0, 0);

// Prirazeni barevne hodnoty CK // Takto priradime jako pruhlednou barvu cernou

lpMySurface->SetColorKey(RGB(0, 0, 0);

N
ynφ kdy₧ zavolßte funkci
Blt() s tφm to povrchem, funkce poznß, ₧e povrch mß definovan² CK a pou₧ije blit s CK a Φernß barva bude pr∙hlednß.

 

6.3. Funkce Blt()

Jist∞ jste  si vÜimli, ₧e funkce Blt() je i ve t°φd∞ CDisplay, to je ale funkce, kterß je zjednoduÜenß a jen mßlo vyu₧φvß mo₧nosti pravΘ funkce Blt(). O tΘto funkci jsem se ji₧ n∞kolikrßt zmi≥oval, ale nikdy jsem po°ßdn∞ ne°ekl, co vÜechno vlastn∞ umφ. U₧ je vßm asi jasnΘ, ₧e slou₧φ ke kopφrovßnφ - blittovßnφ - dat (bit∙) mezi povrchy. V souboru "ddraw.h" mß nßsledujφcφ deklaraci:

HRESULT Blt(
  LPRECT lpDestRect,
  LPDIRECTDRAWSURFACE7 lpDDSrcSurface, 
  LPRECT lpSrcRect,
  DWORD dwFlags,
  LPDDBLTFX lpDDBltFx
);

Jak vidφte mß spoustu parametr∙ a my si je te∩ koneΦn∞ vÜechny probereme.
 

  1. LPRECT lpDestRect - ukazatel na objekt obdΘlnφku (m∙₧e b²t i CRect), kter² slou₧φ jako cφlov² obdΘlnφk.

  2. LPDIRECTDRAWSURFACE7 lpDDSrcSurface - ukazatel na zdrojov² povrch

  3. LPRECT lpSrcRect - ukazatel na objekt obdΘlnφku (m∙₧e b²t i CRect), kter² slou₧φ jako zdrojov² obdΘlnφk.

  4. DWORD dwFlags - dalÜφ parametry viz. nφ₧e

  5. LPDDBLTFX lpDDBltFx - dalÜφ parametry parametr∙ viz. nφ₧e

Funkci vßm osv∞tlφ nßsledujφcφ obrßzek:

Tak u₧ je vßm to aspo≥ troÜku jasnΘ? Doufßm, ₧e ano. Nynφ si podrobn∞ vysv∞tlφme poslednφ dva zßhadnΘ parametry.

1. dwFlags

Tento parametr m∙₧e nab²vat mnoha hodnot, kterΘ se navφc dajφ kombinovat. Nßsledujφcφ tabulka vysv∞tlφ, co kterß hodnota d∞lß:
 

Hodnota

Funkce

DDBLT_COLORFILL

Tento parametr pou₧ijeme, pokud chceme vyplnit obdΘlnφkovou oblast jednou barvou. Pak dosadφme mφsto druhΘho a t°etφho parametru NULL a barvu definujeme v poslednφm parametru viz nφ₧e.

DDBLT_DDFX

Tφmto parametrem °ekneme funkci, ₧e chceme pou₧φt specißlnφ efekty definovanΘ v poslednφm parametru.

DDBLT_KEYDEST

Tφmto parametrem °φkßme, ₧e chceme pou₧φt color key, kter² je ovÜem nastaven² na cφlovΘm povrchu.

DDBLT_KEYSRC

StejnΘ jako p°edchozφ jen pro zdrojov² povrch. ╚ast∞ji pou₧φvßno ne₧ DDBLT_KEYDEST.

DDBLT_WAIT

Tφmto parametrem °φkßme, ₧e funkce Blt() mß Φekat, a₧ se dokonΦφ vÜechny p°edchozφ blittovacφ akce a teprve potom se provede a skonΦφ. Pou₧φvßme ho velmi Φasto.

Hodnot je samoz°ejm∞ jeÜt∞ vφc, ale nßm budou tyto bohat∞ staΦit. Hodnoty se mohou mezi sebou kombinovat pomocφ operßtoru | (nap°. DDBLT_WAIT | DDBLT_COLORFILL). Na konci tΘto Φßsti bude n∞kolik p°φklad∙, jak je mo₧no pou₧φt uvedenΘ hodnoty. Druhß struktura ji₧ nenφ tak d∙le₧itß, ale p°esto s jejφ pomocφ m∙₧eme nastavit zajφmavΘ efekty.

 

2. lpDDBltFx

Princip pou₧itφ je troÜku odliÜn², proto₧e musφte nejd°φve vytvo°it strukturu typu DDBLTFX, kterou pak p°edßte funkci Blt() p°i vlastnφm blittovßnφ. Jako prvnφ musφte inicializovat velikost struktury, abyste s nφ mohli pracovat (viz p°φklad). Nßsledujφcφ tabulka ukazuje n∞kterΘ vlastnosti tΘto struktury:
 

Prom∞nnß

Pou₧itφ

dwSize

Velikost struktury v bytech. Tento parametr musφ b²t bezpodmφneΦn∞ nastaven d°φve ne₧ zaΦnete se strukturou pracovat (sizeof(DDBLTFX);).

dwDDFX

Tady m∙₧ete nastavit specißlnφ efekty p°i blitovßnφ (rotace: DDBLTFX_ROTATE180, p°evracenφ: DDBLTFX_MIRRORLEFTRIGHT, problikßvßnφ: DDBLTFX_NOTEARING ....etc.).

dwFillColor

Pamatujete si na hodnotu DDBLT_COLORFILL? Prßv∞ zde se nastavuje barva v²pln∞, op∞t pomocφ makra RGB.

Pokud pou₧φvßme parametr dwDDFX, musφme funkci Blt() °φct, ₧e ho opravdu chceme pou₧φt nastavenφm hodnoty DDBLT_DDFX. To samΘ platφ i pro dwFillColor a DDBLT_COLORFILL. Nynφ si ukß₧eme n∞kolik p°φklad∙, jak pou₧φt funkci Blt() v praxi:

 //1. priklad
 //normalni kopirovani bitmapy mezi dvema povrchy
 //oba povrchy jsou predem vytvorene

  CRect dest, src;

  //nastaveni dvou ctvercu 200x200
  //cilovy ctverec
 
  dest.left = 0;  
  dest.top = 0;  
  dest.right = 200;  
  dest.bottom = 200;

  //zdrojovy ctverec  
  src.left = 0;  
  srct.top = 0;  
  src.right = 200;  
  src.bottom = 200;

  //pouzivame zdrojovy color key
  DDSetColorKey(srcSurface, CLR_INVALID);

  //vlastni kopirovani - blitting
  destSurface->Blt(&dest, srcSurface, &src, DDBLT_WAIT | DDBLT_KEYSRC, NULL);

  //-------------------------------------------------

  //2. priklad
  //kopirovani jednobarevne plochy
  //oba povrchy jsou predem vytvorene

  CRect dest;

  //struktura DDBLTFX je potreba kvuli definici barvy
  DDBLTFX fx;

  fx.dwSize = sizeof(DDBLTFX); //nutne
  fx.dwFillColor = RGB(150, 100, 200); //nejaka pekna barva

  //nastaveni jen ciloveho ctverce 200x200
  //cilovy ctverec

  dest.left = 0;
  dest.top = 0;
  dest.right = 200;
  dest.bottom = 200;
  //nastaveni color key je zbytecne, protoze se jedna o jednolitou plochu
  //vlastni kopirovani - blitting

  destSurface->Blt(&dest, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &fx);

Pou₧itφ ostatnφch efekt∙ je analogickΘ, p°esto pokud byste m∞li n∞jakΘ specißlnφ po₧adavky, napiÜte mi.
 

Poznßmka: Pokud nenastavφte color key pro povrch, kter² pak kopφrujete s parametrem DDBLT_KEYSRC, funkce Blt() vracφ chybnou hodnotu, tak₧e nezapome≥te d∙sledn∞ hlφdat, kter² povrch mß a kter² ne nastaven² color key.

Poznßmka: Abyste mohli pou₧φt p°φmo tuto funkci s pou₧itφm t°φdy CSurface a CDisplay musφte vyu₧φt funkcφ CSurface::GetDDrawSurface() a CDisplay::GetBackSurface(), kterΘ ob∞ vracφ ukazatele na pravΘ povrchy DirectDraw.

6.3. Zßv∞r

A je tu zase konec. Doufßm, ₧e se vßm dneÜnφ lekce lφbila a ₧e vßm p°inesla zase n∞co novΘho. U₧ jsme se urΦit∞ dostali za p∙lku celΘ teorie DirectDraw. P°φÜt∞ si vytvo°φme t°φdy CSprite, kterß bude p°edstavovat jak²koliv kreslen² obrßzek.

T∞Üφm se p°φÜt∞ nashledanou.
                                                                                                                                                                                                                                                                                                               Ji°φ Formßnek