Nejprve tedy vložme zcela novou knihovnu. Z menu File vyberte volbu New. Na dialogu zvolte kartu Projects a označte MFC AppWizard (dll). Potřebujeme totiž vytvořit rozšířenou knihovnu MFC. Opět nezapomeňte zaškrtnout volbu Add to current project a do políčka Project name vepište Display. Na dialogu AppWizardu zvolte MFC Extension DLL a stiskněte tlačítko Finish. Do ClassView přibude projekt Display, který se nastaví jako aktivní - označte tedy jako aktivní projekt Game.
Nyní nastavme vzájemné vazby mezi projekty. Z menu Project vyberte volbu Dependencies. Na dialogu nastavte, aby projekt Game byl závislý na projektu Display takto:
Dále zeditujme nastavení nového projektu, abychom mohli použít knihovnu Common.dll a aby se soubor Display.dll vytvářel v Debug a Release celého projektu. Využijeme kontextového menu ClassView. Pravým tlačítkem klikněte na projekt Display a vyberte položku Settings. Na dialogu nastavte toto nastavení:
Nastavení samozřejmě proveďte i pro Release. Všimněte si, že nastavení je vlastně stejné jako v projektu Game (navíc jsou přidané knihovny potřebné pro DirectDraw, ale to už známe). Po kompilaci přibude do adresáře Debug či Release knihovna Display.dll.
Knihovna bude obsahovat několik tříd:
Protože CDisplay je závislá na CSurface, vytvořme nejprve třídu CSurface:
CSurface |
||
Členské proměnné |
|
|
Typ |
Název |
Popis |
BOOL |
m_bInit |
Proměnná je TRUE pokud je objekt povrchu řádně zinicializován. Pak lze volat další metody jako Blt() apod. |
BOOL |
m_bColorKey |
Proměnná je TRUE pokud se povrch má vykreslovat se zapnutým color key (zkrátka zda-li má či nemá CK). |
DWORD |
m_dwWidth |
Šířka povrchu v pixelech. Velikost povrchu je určena velikostí bitmapy, kterou do povrchu nahráváme (viz metody Create()) |
DWORD |
m_dwHeight |
Výška povrchu v pixelech. |
CString |
m_csBitmap |
Řetězec, do něhož uložíme cestu k bitmapě v datovém souboru. To se bude hodit, až bude potřeba povrch obnovit. |
LPDIRECTDRAWSURFACE7 |
m_lpDDSurface |
Ukazatel na vlastní objekt povrchu (rozhraní IDirectDrawSurface7) |
CDisplay* |
m_pDisplay |
Pomocný ukazatel na objekt CDisplay. |
Metody |
|
|
Návratová hodnota |
Název s parametry |
Popis |
HRESULT |
Create(DWORD, DWORD, UINT) |
Následující tři funkce inicializují objekt povrchu - liší se pouze parametry. První verze má jako první dva parametry požadovanou velikost povrchu. Třetí parametr je určuje další vlastnosti povrchu např. CK apod. |
HRESULT |
Create(CString, UINT) |
Druhá verze má první parametr řetězec s úplnou cestou bitmapy v datovém souboru. Povrch má velikost bitmapy a bitmapa je také nahrána do povrchu. Druhý parametr je stejný jako u předchozí funkce Create(). |
HRESULT |
Create(LPDIRECTDRAWSURFACE7) |
Třetí a poslední funkce Create() je vlastně kopírovací konstruktor. Vytvoří nový povrch pomocí již existujícího povrchu - udělá kompletní kopii. Jednu z těchto funkcí je třeba zavolat, aby bylo možné volat ostatní metody. |
HRESULT | Blt(CRect, CRect, CSurface*) | Následující funkce zajišťují vykreslování do aktuálního povrchu buď z jiného povrchu nebo jednolité výplně. První verze má dva parametry typu CRect, což jsou obdélníky, ze kterých a do kterých se kopíruje příslušná část povrchu určeného třetím parametrem. |
HRESULT | Blt(CRect, COLORREF) | Druhá verze vyplní obdélník zadaný prvním parametrem spojitou barvou definovanou druhým parametrem. |
HRESULT | CopyBitmap(CString) | Tato funkce slouží ke kopírování bitmapy ze souboru do předem vytvořeného povrchu. Využívá se při obnově povrchu apod. |
DWORD | Height() | Vrací výšku povrchu. |
DWORD | Width() | Vrací šířku povrchu. |
BOOL | IsColorKey() | Vrací TRUE pokud povrch je s CK, jinak FALSE. |
BOOL | IsInit() | Vrací stav proměnné m_bInit. |
BOOL | IsValid() | Vrací stav proměných m_bInit a zároveň ukazatele this (viz. dále). |
LPDIRECTDRAWSURFACE7* | GetSurface() | Vrací přímo ukazatel na povrch DirectDraw. |
void | SetColorKey() | Nastaví proměnnou m_bIsColorKey na TRUE (zapne CK) |
HRESULT | Restore(BOOL) | Obnoví povrch při ztrátě. Parametr určuje zda-li povrch bude obnoven a naplněn původní bitmapu nebo jen obnoven. |
HRESULT | Release() | Uvolní paměť povrchu a zruší objekt. Tato metoda není potřeba volat explicitně, protože se volá z destruktoru. |
LPDIRECTDRAWSURFACE7 | operator LPDIRECTDRAWSURFACE7() | Přetížený operátor. Objekt CSurface půjde přetypovat na LPDIRECTDRAWSURFACE7. |
Konstruktor a destruktor třídy neuvádím, protože to beru jako samozřejmost.
Deklarace třídy není složitá:
class AFX_EXT_CLASS CSurface
{
private:
LPDIRECTDRAWSURFACE7 m_lpDDSurface;
CDisplay *m_pDisplay;
BOOL m_bInit;
BOOL m_bColorKey;
DWORD m_dwWidth;
DWORD m_dwHeight;
CString m_csBitmap;
public:
//
// Create functions
HRESULT Create(DWORD dwWidth, DWORD
dwHeight, UINT nFlags = DDFLG_COLORKEY);
HRESULT Create(CString strBMP, UINT
nFlags = DDFLG_COLORKEY);
HRESULT Create(LPDIRECTDRAWSURFACE7
lpSurface);
//
HRESULT Release();
HRESULT Restore(BOOL bFill = false);
HRESULT SetColorKey();
//
// Copy bitmap to surface
HRESULT CopyBitmap(CString csBitmap);
//
// Inline functions
LPDIRECTDRAWSURFACE7 GetSurface();
BOOL IsInit() {return m_bInit;}
BOOL IsColorKey() {return m_bColorKey;}
DWORD Width() {return m_dwWidth;}
DWORD Height() {return m_dwHeight;}
BOOL IsValid();
//
// Blitting functions
HRESULT Blt(CRect rcDestin, CRect rcSource, CSurface* surSource);
HRESULT Blt(CRect rcDestin, COLORREF
Color);
//
// Overloaded operator to return
pointer to surface
operator LPDIRECTDRAWSURFACE7() {return m_lpDDSurface;};
public:
CSurface();
~CSurface();
};
Na deklaraci není nic zvláštního ani překvapivého snad kromě makra AFX_EXT_CLASS, o kterém již byla také řeč.
Nyní se podívejme na konstruktor a destruktor třídy:
CSurface::CSurface()
{
m_bInit = false;
m_bColorKey = false;
m_dwHeight = 0;
m_dwWidth = 0;
m_pDisplay = disGetDisplay();
SAFE_NULL(m_lpDDSurface);
}
CSurface::~CSurface()
{
if(m_bInit) { //if you do not call
release() before destruct object
Release(); //call
now
}
}
Opět zde není nic nového. Všimněte si volání uvolňovací funkce v destruktoru (viz. výše). Funkce disGetDisplay() vrací přímo objekt CDisplay. Je to globálně vytvořená funkce, o které bude řeč později.
Proberme implementace zbylých funkcí:
HRESULT CSurface::Create(DWORD dwWidth,
DWORD dwHeight, UINT nFlags)
{
HRESULT dwReturn = 1;// 1...ERROR_ALREADYINIT;
DDSURFACEDESC2 ddsd;
//
// Is surface already created? Is initialized display system?
if(m_bInit || !m_pDisplay) {
DXTRACE("Cannot create surface
because it is already created or the display system is not initialized.");
return dwReturn;
}
//
// Check if the input param are valid
ASSERT(dwWidth > 0 && dwHeight > 0);
//
// Create a DirectDrawSurface for this bitmap
ZeroMemory( &ddsd, sizeof(ddsd) );
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
m_dwWidth = ddsd.dwWidth = dwWidth;
m_dwHeight = ddsd.dwHeight = dwHeight;
//
// Creation of the new surface
dwReturn = m_pDisplay->GetDirectDraw()->CreateSurface(&ddsd,
&m_lpDDSurface, NULL);
//
// Try to create surface in RAM if video is full
if(dwReturn == DDERR_OUTOFVIDEOMEMORY) {
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN
| DDSCAPS_SYSTEMMEMORY;
dwReturn = m_pDisplay->GetDirectDraw()->CreateSurface(&ddsd,
&m_lpDDSurface, NULL);
}
if(DD_OK != dwReturn) {
DXERR("Cannot create the surface due",
dwReturn);
return dwReturn;
}
m_bInit = true;
return dwReturn;
}
Tuto funkci si probereme trochu podrobněji. Nejprve zjistíme platnost ukazatele m_pDisplay a to, zda-li povrch už nebyl jednou inicializován. Poté zkontrolujeme vstupní parametry. Dále plníme strukturu DDSURFACEDESC2, jak jsme byli zvyklí - nastavíme off-screen surface, šířku výšku a typ paměti v případě, že video paměť je plná. Vytvoříme vlastní povrch. Při neúspěchu zkusíme ještě povrch vytvořit v RAM místo video paměti. A to je vše. Detailní popis výše uvedených řádků najdete v několika předcházejících lekcích.
HRESULT CSurface::Create(CString strBMP,
UINT nFlags)
{
HRESULT dwRet = 1;// = ERROR_NOINIT;
DWORD dwSize;
DWORD dwWritten;
LPBYTE lpData;
HANDLE hFile = NULL;
BITMAP bm;
HBITMAP hBitmap;
CString strTempFile = _T("C:\\surface.tmp");
HDC hdcImage;
HDC hdc = NULL;
//
// Is surface initialized ?
// Is bitmap valid?
// Is display initialized?
if(m_bInit || strBMP.IsEmpty() || !m_pDisplay) {
DXTRACE("General error has been
occured while surface was created.");
return dwRet;
}
//
// Save internal copy of bitmap path
m_csBitmap = strBMP;
//
// Find file in storage
if((dwRet = stgGetFile3(strBMP, &lpData, dwSize)) != S_OK) {
DXERR("Cannot find specified bitmap
is storage due", dwRet);
return dwRet;
}
//create temporary file
hFile = (HANDLE)CreateFile(strTempFile,
GENERIC_WRITE ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY,
NULL);
if(hFile == INVALID_HANDLE_VALUE) {
dwRet = GetLastError();
DXERR("Cannot create temporary file
due", dwRet);
delete [] lpData;
return dwRet;
}
//
// Write bitmap data to file
WriteFile(hFile, lpData, dwSize, &dwWritten, NULL);
if(dwWritten != dwSize) {
dwRet = GetLastError();
DXERR("Cannot write data to temporary
file due", dwRet);
CloseHandle((HANDLE)hFile);
DeleteFile(strTempFile);
delete [] lpData;
return dwRet;
}
//
// Close temporary file
CloseHandle((HANDLE)hFile);
//
// Get bitmap handle
hBitmap = (HBITMAP)LoadImage(NULL, strTempFile, IMAGE_BITMAP,
0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
if (hBitmap == NULL) {
dwRet = GetLastError();
DXERR("Cannot create bitmap handle
due", dwRet);
DeleteFile(strTempFile);
delete [] lpData;
return dwRet;
}
//
// Get size of the bitmap
if(!GetObject(hBitmap, sizeof(BITMAP), &bm))
{
dwRet = GetLastError();
DXERR("Cannot get info about bitmap
due", dwRet);
DeleteFile(strTempFile);
delete [] lpData;
return dwRet;
}
//
// Create surface
if(ERROR_SUCCESS != (dwRet = Create(bm.bmWidth, bm.bmHeight,
nFlags))) {
DXERR("Cannot create surface due",
dwRet);
DeleteFile(strTempFile);
delete [] lpData;
return dwRet;
}
//
// Make sure this surface is restored.
Restore();
//
// Create DC for our bitmap
hdcImage = CreateCompatibleDC(NULL);
if (!hdcImage) {
dwRet = GetLastError();
DXERR("Cannot create compatible DC
due", dwRet);
DeleteFile(strTempFile);
delete [] lpData;
return dwRet;
}
//
// Select bitmap intoa a memoryDC so we can use it.
SelectObject(hdcImage, hBitmap);
//
// Get DC to surface
if ((dwRet = m_lpDDSurface->GetDC(&hdc)) != DD_OK)
{
DXERR("Cannot get surface DC due",
dwRet);
return dwRet;
}
//
// Blit bitmap to GDI surface
if(0 == StretchBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight,
hdcImage, 0, 0, bm.bmWidth , bm.bmHeight, SRCCOPY))
{
dwRet = GetLastError();
DXERR("Cannot blit bitmap to surface
due", dwRet);
return dwRet;
}
//
// Release DC
if(DD_OK != (dwRet = m_lpDDSurface->ReleaseDC(hdc))) {
DXERR("Cannot release surface DC due",
dwRet);
return dwRet;
}
//
// If we want to set color key, set it now
if(nFlags & DDFLG_COLORKEY) {
dwRet = SetColorKey();
}
//
//release handle to GDI object and handle to DC
DeleteDC(hdcImage);
DeleteObject(hBitmap);
DeleteFile(strTempFile);
delete [] lpData;
return dwRet;
}
Tato funkce konečně obsahuje nějaké novinky, takže si ji přiblížíme ještě více. Funkce opět obsahuje kontroly zda-li je povrch neinicializován a zda-li je inicializován objekt CDisplay. Idea funkce je taková, že nejprve nahrajeme bitmapu z datového souboru, poté vytvoříme dočasný soubor, do kterého bitmapu nahrajeme, dále získáme rozměry bitmapy, vytvoříme povrch podle těchto rozměrů a nakopírujeme bitmapu do povrchu. Je to celkem kostrbatý způsob, ale funguje to! Funkcí stgGetFile3() nahrajeme požadovanou bitmapu z datového souboru do paměťového bufferu.Funkce má tři parametry: cesta k bitmapě, ukazatel na ukazatel na buffer a velikost bufferu. Buffer je alokován až uvnitř funkce a parametr dwSize poté obsahuje velikost souboru. Poté vytvoříme nový soubor pomocí API funkce CreateFile() a zapíšeme obsah paměťového souboru do souboru na disku. Dále využíváme API funkce k získání rozměrů bitmapy. Když známe rozměry, můžeme konečně vytvořit povrch funkcí Create(DWORD,DWORD,UINT). Nakonec zbývá zkopírovat bitmapu do povrchu. To provedeme na úrovni DC s bitmapou a DC povrchu. Pokud si to uživatel přeje, nastavíme CK a na úplný závěr uvolníme všechnu alokovanou paměť.
A konečně je tu třetí verze funkce Create():
HRESULT CSurface::Create(LPDIRECTDRAWSURFACE7
lpSurface)
{
DDSURFACEDESC2 ddsd;
ddsd.dwSize = sizeof(ddsd);
HRESULT dwReturn = 1;//
= ERROR_INIT or INVALID SURFACE;
if(lpSurface && !m_bInit) {
dwReturn = lpSurface->GetSurfaceDesc(&ddsd);
if(dwReturn != DD_OK) {
DXERR("You
may pass invalid surface. Cannot get info about due", dwReturn);
return
dwReturn;
}
//
// Init member fuction according
existing surface
m_lpDDSurface = lpSurface;
m_dwHeight = ddsd.dwHeight;
m_dwWidth = ddsd.dwWidth;
m_bColorKey = ddsd.dwFlags & DDSD_CKSRCBLT;
m_bInit = true;
m_csBitmap = _S_EMPTY;
}
return dwReturn;
}
Vypadá přesně jako kopírovací konstruktor. Na začátku zjistíme informace o povrchu daném vstupním parametrem funkce. V druhé části funkce inicializujeme ostatní atributy třídy CSurface. Povrch ovšem zůstane prázdný (bez bitmapy).
Dále probereme možná nejsložitější funkci třídy, funkci CopyBitmap():
HRESULT CSurface::CopyBitmap(CString
csBitmap)
{
HRESULT dwReturn = 1;//
= ERROR_NOINIT or BITMAP NAME IS INVALID;
DWORD dwSize;
DWORD dwWritten;
LPBYTE lpData;
HANDLE hFile = NULL;
BITMAP bm;
HBITMAP hBitmap;
CString strTempFile = _T("C:\\surface.tmp");
HDC hdcImage;
HDC hdc = NULL;
HBITMAP hOld;
//
// If display initialized and storage opened
if(!csBitmap.IsEmpty()
&& m_bInit) {
//
// Try to find file in storage
dwReturn = stgGetFile3(csBitmap, &lpData, dwSize);
if(dwReturn != ERROR_SUCCESS) {
DXERR("Cannot
get file from storage due:", dwReturn);
return
dwReturn;
}
//
// Create temporary file
hFile = (HANDLE)CreateFile(strTempFile,
GENERIC_WRITE ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY,
NULL);
if(hFile == INVALID_HANDLE_VALUE) {
dwReturn =
GetLastError();
DXERR("Cannot
create temporary file due:", dwReturn);
delete []
lpData;
return
dwReturn;
}
//
// Write bitmap data to the temporary
file
WriteFile(hFile, lpData, dwSize, &dwWritten, NULL);
if(dwWritten != dwSize) {
dwReturn =
GetLastError();
DXERR("Cannot
write data due:", dwReturn);
CloseHandle((HANDLE)hFile);
delete []
lpData;
DeleteFile(strTempFile);
return
dwReturn;
}
//
// Close it
CloseHandle((HANDLE)hFile);
//
// Get bitmap handle
// Try to load bitmap from file
hBitmap = (HBITMAP)LoadImage(NULL, strTempFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
if (hBitmap == NULL) {
dwReturn =
GetLastError();
DXERR("Cannot
get bitmap handle due:", dwReturn);
delete []
lpData;
DeleteFile(strTempFile);
return
dwReturn;
}
//
// Get size of the bitmap
if(!GetObject(hBitmap, sizeof(BITMAP), &bm))
{
dwReturn =
GetLastError();
DXERR("Cannot
get bitmap information due:", dwReturn);
delete []
lpData;
DeleteObject(hBitmap);
DeleteFile(strTempFile);
return
dwReturn;
}
//
// Create DC for our bitmap
hdcImage = CreateCompatibleDC(NULL);
if (!hdcImage) {
dwReturn =
GetLastError();
DXERR("Cannot
create temporary DC due:", dwReturn);
delete []
lpData;
DeleteObject(hBitmap);
DeleteFile(strTempFile);
return
dwReturn;
}
//
// Select bitmap into a memoryDC so
we can use it.
hOld = (HBITMAP) SelectObject(hdcImage, hBitmap);
//
// Get DC of DD surface
if ((dwReturn = m_lpDDSurface->GetDC(&hdc)) == DD_OK)
{
//
// Blit
bitmap to GDI surface
StretchBlt(hdc, 0, 0, Width(), Height(), hdcImage, 0, 0, bm.bmWidth , bm.bmHeight,
SRCCOPY);
if(DD_OK != (dwReturn
= m_lpDDSurface->ReleaseDC(hdc)))
{
DXERR("Cannot release surface DC due:", dwReturn);
}
//
// If we want
to set color key, set it now else exit with OK
if(m_bColorKey) {
dwReturn = SetColorKey();
if(dwReturn != ERROR_SUCCESS) {
DXERR("Cannot set CK on surface due:", dwReturn);
}
}
//
//release
handle to GDI object and handle to DC
DeleteDC(hdcImage);
DeleteObject(hBitmap);
DeleteFile(strTempFile);
delete []
lpData;
}
if(dwReturn != DD_OK) {
DXERR("Cannot
get surface DC due:", dwReturn);
delete []
lpData;
DeleteObject(hBitmap);
DeleteDC(hdcImage);
DeleteFile(strTempFile);
return
dwReturn;
}
}
return dwReturn;
}
Funkce funguje podobně jako druhá verze Create().
Nejprve vytáhneme bitmapu z datového souboru, ale poté ji nakopírujeme do
povrchu, který byl předem vytvořen tzn., že nealokujeme další paměť pro povrch.
Protože kopírování bitmapy se provádí na úrovni DC (kontextů zařízení) povrchu a
DC s bitmapou, použijeme funkci StretchBlt(). Tato
funkce provede vykreslení (včetně roztáhnutí čí smrštění bitmapy) z jednoho DC
do jiného (parametry jsou dost podobné funkci Blt()
DirectDraw s tím rozdílem, že místo povrchů se používají DC). Na konci funkce je
důležité uvolnění paměti!
Povrch jsme vytvořili musíme ho ovšem i uvolnit. K tomu slouží funkce Release(), která se volá z destruktoru:
HRESULT CSurface::Release()
{
SAFE_RELEASE(m_lpDDSurface);
//safe release surface
m_bInit = false;
//and reset all member variable
m_bColorKey = false;
m_dwHeight = 0;
m_dwWidth = 0;
return ERROR_SUCCESS;
}
Tělo funkce je velice krátké a srozumitelné. Důležité je uvolnění rozhraní
povrchu DirectDraw.
Z minulého projektu si jistě vzpomínáte na obnovu povrchů po ztrátě focusu okna. Následující funkce Restore() zajistí kompletní obnovu povrchu včetně vyplnění povrchu původní bitmapou, pokud si to uživatel přeje:
HRESULT CSurface::Restore(BOOL
bFill)
{
HRESULT dwReturn = 1;//
1...ERROR_NOINIT;
if(m_bInit) {
//
// Try tu restore surface
dwReturn = m_lpDDSurface->Restore();
if(dwReturn != DD_OK) {
DXERR("Cannot
restore surface due", dwReturn);
return
dwReturn;
}
if(bFill) {
//
// Refill
surface by bitmap
dwReturn =
CopyBitmap(m_csBitmap);
}
}
return dwReturn;
}
Jediný vstupní parametr určuje, zda-li se povrch naplní či nenaplní původní
bitmapou. Jinak na funkce není nic zajímavého. Nejprve obnovíme (realokujeme)
povrch pomocí funkce Restore() (stejně jsme to
dělali i v minulém projektu). Poté využijeme funkce
CopyBitmap() k naplnění povrchu původní bitmapou. To je vše!
K nastavení Color Key používáme funkci SetColorKey():
HRESULT CSurface::SetColorKey()
{
HRESULT dwReturn = 1;
DWORD dwPixel;
DDSURFACEDESC2 ddsd;
DDCOLORKEY ddck;
if(m_bInit) {
//
// Now lock the surface so we can
read back the converted color
ddsd.dwSize = sizeof(ddsd);
dwReturn = GetSurface()->Lock( NULL,
&ddsd, DDLOCK_WAIT, NULL );
if(dwReturn != DD_OK) {
DXERR("Cannot
lock surface to set color key due", dwReturn);
return
dwReturn;
}
//
// Get first pixel
dwPixel = *(DWORD *) ddsd.lpSurface;
//
// Mask it to bpp
if(ddsd.ddpfPixelFormat.dwRGBBitCount
< 32) {
dwPixel &= (1
<< ddsd.ddpfPixelFormat.dwRGBBitCount) - 1;
}
//
// Unlock surface
GetSurface()->Unlock(NULL);
//
// Set color values
ddck.dwColorSpaceLowValue = ddck.dwColorSpaceHighValue
= dwPixel;
//
// Set color key on surface
dwReturn = m_lpDDSurface->SetColorKey(DDCKEY_SRCBLT, &ddck);
if(dwReturn != DD_OK) {
DXERR("Cannot
set color key on surface due", dwReturn);
}
if(dwReturn == DD_OK) {
m_bColorKey =
true;
}
}
return dwReturn;
}
Funkce vybere první pixel bitmapy a barvu tohoto pixelu nastaví jako transparentní. Abychom mohli přistoupit přímo k povrchu, musíme tento povrch "uzamknout" funkcí Lock(), která zároveň inicializuje ukazatel na pole pixelů. Poté přečteme barevnou informaci prvního pixelu a povrch odemkneme funkcí Unlock(). Zbytek metody je zcela zřejmý, nastavení CK jsme probrali v minulých lekcích.
Následující funkce vrací přímo ukazatel na povrch DirectDraw:
LPDIRECTDRAWSURFACE7
CSurface::GetSurface()
{
//
// Is surface initialized ?
if(m_bInit) {
return m_lpDDSurface;
}
return NULL;
}
Tuto funkci využijeme pokud potřebuje přímo pracovat s povrchem DirectDraw (například
jako parametr některých funkcí).
Funkce IsValid() testuje platnost povrchu a objektu CSurface:
BOOL CSurface::IsValid()
{
if(m_lpDDSurface && IsInit()) {
return TRUE;
}
else {
return FALSE;
}
}
Od IsInit() se liší právě v testování platnosti povrchu. Bezpečnější je tedy používat metodu IsValid().
Jako poslední si proberme pár funkcí Blt(). Má dvě verze. První kopíruje obsah povrchu do jiného povrchu a druhá vyplňuje aktuální povrch spojitou barvou:
HRESULT CSurface::Blt(CRect
rcDestin, CRect rcSource, CSurface* surSource)
{
HRESULT dwResult = 1; // 1 means: NO INIT
//
// Check if the destination surface is valid
if(IsValid() && surSource->IsValid()) {
//
// Blit with color key
if(surSource->IsColorKey()) {
while(1) {
dwResult = GetSurface()->Blt(&rcDestin, surSource->GetSurface(), &rcSource,
DDBLT_KEYSRC, NULL);
if(dwResult != DDERR_WASSTILLDRAWING) {
break;
}
}
}
//
// Blit without color key
else {
while(1) {
dwResult = GetSurface()->Blt(&rcDestin, surSource->GetSurface(), &rcSource, 0,
NULL);
if(dwResult != DDERR_WASSTILLDRAWING) {
break;
}
}
}
if(DD_OK != dwResult) {
DXERR("Cannot
blit solid rect due", dwResult);
}
}
return dwResult;
}
Na funkci není nic složitého. Rozlišujeme zda-li je použit CK či nikoliv. Všimněte si také použití funkce GetSurface(). Vykreslování zkoušíme tak dlouho dokud Blt() nevrací něco jiného než DDERR_WASSTILLDRAWING. Tento kód funkce vrací tehdy, když ještě nejsou dokončeny předchozí kreslící operace. Vše ostatní známe z minulých lekcí.
Druhá verze je ještě jednodušší:
HRESULT CSurface::Blt(CRect
rcDestin, COLORREF Color)
{
HRESULT dwResult = 1; // 1 means: NOT INIT
DDBLTFX fx;
//
// Check if the destination surface is valid
if(IsValid()) {
//
// Fill DDBLTFX structure valid date
fx.dwSize = sizeof(fx);
fx.dwFillColor = Color;
//
// Blitting...
while(1) {
dwResult =
GetSurface()->Blt(&rcDestin, NULL, NULL, DDBLT_COLORFILL, &fx);
if(dwResult
!= DDERR_WASSTILLDRAWING) {
break;
}
}
if(DD_OK != dwResult) {
DXERR("Cannot
blit solid rect due", dwResult);
}
}
return dwResult;
}
Nepotřebujeme zde žádný zdrojový povrch ani obdélník. Naplníme strukturu DDBLTFX tak, aby funkce Blt() vyplnila oblast určenou cílovým obdélníkem rcDestin spojitou barvou Color.
Dnešní lekce byla trochu delší, ale doufám, že jste všechno pochopili. Stihli jsme implementovat pouze třídu CSurface, ale v příkladu, který přikládám naleznete kompletní knihovnu Display.dll. Zbytek doděláme v příští lekci (aspoň doufám). Příklad si stáhněte ze sekce Downloads. Navíc v adresáři Release najdete program Storage3, který použijete při vytváření datových souborů. Zkuste si otevřít datový soubor použitý v projektu data.dat a uvidíte, jak program funguje.
Pokud by Vám přece jen něco uniklo, stačí napsat a já se k tomu vrátím.
Těším se příště nashledanou.