DirectX (12.)

Ve 12. lekci o DirectX budeme pokraΦovat v budovßnφ projektu z minulΘ lekce. KonkrΘtn∞ dnes vytvo°φme knihovnu Display.dll a pokusφme se implementovat t°φdu CSurface. Funkce nebudu rozbφrat do ·pln²ch detail∙, proto₧e v∞tÜina k≤du Vßm ji₧ bude znßmß.

12.1. Knihovna Display.dll

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.

12.2. NovΘ t°φdy

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.

12.2. Zßv∞r

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.

Ji°φ Formßnek