Nynφ je ten sprßvn² Φas, abychom si vytvo°ily tzv. Off-screen surfaces neboli pomocnΘ povrchy, do kter²ch si ulo₧φme naÜe bitmapy. Tyto povrchy poslΘze vykreslujeme do zadnφho povrchu, kter² jsme vytvo°ili v minulΘ lekci. V dneÜnφ lekci vytvo°φme dalÜφ dv∞ ΦlenskΘ funkce t°φdy CControl. Prvnφ z nich vytvo°φ Off-screen surface a vrßtφ ukazatel na n∞j a druhß do tohoto povrchu nahraje bitmapu ze souboru. PotΘ tento povrch m∙₧ete blittovat do zadnφho bufferu, jeho₧ obsah se zobrazφ na monitoru.
Off-screen surfaces jsou op∞t buffery, kterΘ ji6 nemusφ b²t v grafickΘ pam∞ti (zßle₧φ na velikosti grafickΘ pam∞ti). Pokud vlastnφte grafickou kartu, kter² na sob∞ nese mΘn∞ ne₧ 8MB, je vhodnΘ vÜechny pomocnΘ povrchy vytvß°et v systΘmovΘ pam∞ti.
Zpravidla vytvß°φme povrchy, kterΘ jsou velkΘ jako bitmapa, kterou chceme do povrchu nahrßt. Dßle zßle₧φ na hloubce barev, se kterou prßv∞ pracujeme. Pochopiteln∞ pam∞¥ovΘ nßroky se zv²Üφ, pokud pracujete v 16-bitech (65 tis. barev) oproti 8-bit∙m (256 barev) (o 32-bitech nemluv∞).
Povrchy vytvß°φme stejn∞ jako v minul²ch lekcφch, s tφm rozdφlem, ₧e
strukturu DDSURFACEDESC
naplnφme jin²mi daty a navφc p°esn∞ urΦφme
rozm∞ry vytvß°enΘho povrchu. P°idejte funkci
CreateSurface() do t°φdy CControl. HlaviΦku funkce vidφte nφ₧e. Tato funkce vßm
pouze alokuje pam∞¥ a vrßtφ ukazatel na prßzdn² povrch. VÜimn∞te si, ₧e funkce
mß jako prvnφ parametr ukazatel na ukazatel, to proto, ₧e m∞nφ hodnotu
ukazatele.
Jednoduch² k≤d jak by mohla vypadat vaÜe funkce pro vytvo°enφ povrchu :
HRESULT
CControl::CreateSurface(DWORD dwWidth, DWORD dwHeight, LPDIRECTDRAWSURFACE7 * lpSurface) {
DDSURFACEDESC2 ddsd; // 1 ZeroMemory( &ddsd, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); // 2 DWORD dwRet; ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; // 3 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; // 4 ddsd.dwHeight = dwWidth; // 5 ddsd.dwWidth = dwHeight; dwRet = m_lpDD->CreateSurface(&ddsd, lpSurface, NULL); // 6 if(dwRet != DD_OK) { TRACE("Cannot create surface due %d\n", dwRet); } return dwRet; }
Nynφ si rozebereme ka₧d² °ßdek zvlßÜ¥:
DDSURFACEDESC2
. dwSize
. Tento atribut musφ
b²t v₧dy inicializovßn d°φve ne₧ zaΦneme se strukturou pracovat (viz minulß
lekce). dwCaps
urΦuje typ povrchu. V naÜem
p°φpad∞ je to Off-screen surface. dwWidth
(Üφ°ka)
a dwHeight
(v²Üka). Jednotky jsou pochopiteln∞ pixely.
CreateSurface()
,
kterou ji₧ znßme z minulΘ lekce, kdy₧ jsme vytvß°eli p°ednφ buffer. Funkce vracφ hodnotu DD_OK pokud je ·sp∞Ünß.
Nynφ mßme v pam∞ti alokovanou oblast pam∞ti, do kterΘ si m∙₧ete nahrßt bitmapu. Tuto bitmapu m∙₧ete nahrßt ze zdroj∙ p°φmo ve VC++, ale i z naprosto nezßvislΘho souboru bitmapy. Osobn∞ doporuΦuji druh² zp∙sob, proto₧e je bitmapa uchovßna mimo spustitelnß soubor, kter² pak nenar∙stß do obrovsk²ch velikostφ. Nßsledujφcφ funkce nahrßvß bitmapu mimo soubor aplikace. Jako prvnφ parametr jφ p°edßvßte cestu (absolutnφ nebo relativnφ) na bitmap. Druh² parametr urΦuje povrch, do kterΘho se bitmapa bude nahrßvat.
HRESULT
CControl::CopyBitmap(CString csFile, LPDIRECTDRAWSURFACE7 Surface) { HDC hdcImage, hdc = NULL; BITMAP bm; DDSURFACEDESC2 ddsd; HBITMAP hbm; DWORD dwRet; // // Get handle to bitmap hbm = (HBITMAP)LoadImage(NULL, csFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);//1 if(!hbm) { dwRet = GetLastError(); TRACE("Cannot load image due %d\n", dwRet); return dwRet; } // // Get size of the bitmap GetObject(hbm, sizeof(BITMAP), &bm); //2 // // Make sure this surface is restored. Surface->Restore();//3 // // Create DC for our bitmap hdcImage = CreateCompatibleDC(NULL);//4 ASSERT(hdcImage); // // Select bitmap intoa a memoryDC so we can use it. SelectObject(hdcImage, hbm);//5 // // get size of surface. //6 ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH; Surface->GetSurfaceDesc(&ddsd); //blit bitmap to GDI surface dwRet = Surface->GetDC(&hdc);//7 if (dwRet != DD_OK) { TRACE("Cannot get DC of surface due %d\n", dwRet); return dwRet; } dwRet = StretchBlt( hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);//8 if(dwRet == 0) { dwRet = GetLastError(); TRACE("Cannot blit bitmap to surface due %d\n", dwRet); } else { dwRet = ERROR_SUCCESS; } Surface->ReleaseDC(hdc);//9 // // Release handle to GDI object and handle to DC DeleteObject(hbm);//10 DeleteDC(hdcImage); return dwRet;
}
V²Üe uvedenß funkce je troÜku slo₧it∞jÜφ, tak₧e si ji p∞kn∞ vysv∞tlφme. Vyu₧φvßme zde GDI, kterΘ je popsßno v lekci Φ.2.
LoadImage()
, ve kterΘ urΦujeme jmΘno bitmapy (strBMPFile
)
a dalÜφ parametry, kterΘ nßm urΦφ, ₧e bitmapu nahrßvßme ze souboru atd.
Detailn∞ je tato funkce popsßna v MSDN. GetObject()
naplnφ strukturu BITMAP
, kterß mimo jinΘ obsahuje Üφ°ku a v²Üku
bitmapy. Restore()
realokuje pam∞¥ urΦenou pro
povrch. M∙₧e se stßt, ₧e se povrch ztratφ, nap°φklad p°φ p°epnutφ rozliÜenφ,
pak je t°eba zavolat tuto funkci, aby nßm realokovala pam∞¥. My pak musφme
nahrßt znovu bitmapu do povrchu !!! Problematika ztrßcenφ povrch∙ bude
vysv∞tlena nejspφÜe v p°φÜtφ lekci.GetSurfaceDesc()
p°ijφmß jako parametr
ukazatel ne strukturu DDSURFACEDESC2
. StretchBlt()
kopφruje kontext za°φzenφ
bitmapy do kontextu povrchu. ProblΘm se zφskßnφm DC naÜeho povrchu je v tom, ₧e operace je Φasov∞ nßroΦnß tzn. ₧e pokud budete ve vaÜem programu stßle p°istupovat k DC, program se citeln∞ zpomalφ.
Poznßmka:
Pokud pou₧φvßte 8-bitovou barevnou hloubku, pravd∞podobn∞ bitmapa nebude
vypadat tak jak mß, jeliko₧ nenφ nastavena ₧ßdnß paleta. Pokud nepracujete na
·pln∞ pomalΘm poΦφtaΦi, m∙₧ete nastavit 16-bit∙ a vÜe bude v po°ßdku.
Nejprve vytvo°φme povrch pozadφ, do kterΘho nahrajeme po₧adovanou bitmapu ve formßtu .bmp. NejlepÜφ je, kdy₧ bitmapa je stejn∞ velkß jako je nastavenΘ rozliÜenφ (viz minulß lekce), pak nedochßzφ k deformaci bitmapy (i kdy₧ teoreticky funkce CopyBitmap() umφ bitmapu roztßhnout, zkuste si to a uvidφte, ₧e bitmapa bude zkreslenß). Ve t°φd∞ CControl deklarujte novou prom∞nnou typu ukazatel na povrch, kter² bude ukazovat na povrch naÜeho pozadφ. Typ je stejn² jako u zadnφho a p°ednφho bufferu tj. LPDIRECTDRAWSURFACE7 a prom∞nnou nazv∞te t°eba m_lpDDSBackground. Dßle na konec funkce DDInit() p°idejte nßsledujφcφ k≤d:
//
// Jako prvni vytvorime povrch pro nase pozadi, velikost je stejna jako
rozliseni protoze pozadi pokryva celou obrazovku
dwResult = CreateSurface(RES_X, RES_Y, &m_lpDDSBackground);
if(dwResult != DD_OK) {
TRACE("Cannot create surface for
background due %d\n", dwResult);
return dwResult;
}
//
// Za druhe do vytvoreneho povrchu nakopirujeme zvolenou bitmapu
dwResult = CopyBitmap("background.bmp", m_lpDDSBackground);
if(dwResult != DD_OK) {
TRACE("Cannot copy bitmap to surface
due %d\n", dwResult);
return dwResult;
}
Nynφ u₧ m∙₧eme vykreslit naÜφ bitmapu do zadnφho bufferu. To provedeme ve funkci UpdateFrame(). T∞sn∞ p°ed prohozenφm p°ipiÜte tento k≤d :
CRect rcBackground;
rcBackground.SetRect(0,0,RES_X, RES_Y);
m_lpDDSBack->Blt(&
rcBackground,
m_lpDDSBackground, &
rcBackground, DDBLT_WAIT ,
NULL);
Funkce Blt()
provede blitting do zadnφho bufferu.
Funkce p°ijφmß dva obdΘlnφky odkud a kam se bude kopφrovat. V naÜem p°φpad∞ jsou
oba obdΘlnφky stejnΘ. Druh²
parametr je ukazatel na povrch, ze kterΘho data kopφrujeme tzn. ukazatel na
nßÜ pomocn² povrch pozadφ. Parametr DDBLT_WAIT
zajistφ, ₧e funkce poΦkß a₧
se dokonΦφ vÜechny p°edchozφ blittovacφ operace. Funkci Blt()
budeme jeÜt∞ dßle rozebφrat v dalÜφ lekci.
A u₧ je tu zase konec. Po dneÜnφ lekci uvidφte na monitoru vaÜφ bitmapu, kterΘ se po°ßd dokola p°ekresluje z naÜeho pomocnΘho povrchu. Prohazovßnφ nenφ vid∞t, jeliko₧ vykreslujeme po°ßd tu samou bitmapu. Kdybyste si nahrßli dv∞ bitmapy a po tΘ je st°φdali, vid∞li byste jak DirectDraw nedovolφ ₧ßdnΘ "ÜerednΘ" problikßvßnφ. P°φklad z dneÜnφ lekce si samoz°ejm∞ m∙₧ete stßhnou v sekci Downloads.
T∞Üφm se p°φÜt∞ nashledanou.
© 2001 Vogel Publishing, design by ET NETERA