Topic : DirectDraw Programming
Author : Lar Mader
Page : << Previous 2  Next >>
Go to page :


*   2) Setting the Cooperative level
*   3) Setting the Display mode
*
*/
bool DirectDrawInit(HWND hwnd)
{
    HRESULT ddrval;

   /*
    * Create the main DirectDraw object.
    *
    * This function takes care of initializing COM and Constructing
    * the DirectDraw object.
    */
    ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
    if( ddrval != DD_OK )
    {
        return(false);
    }

   /*
    * The cooperative level determines how much control we have over the
    * screen. This must at least be either DDSCL_EXCLUSIVE or DDSCL_NORMAL
    *
    * DDSCL_EXCLUSIVE allows us to change video modes, and requires
    * the DDSCL_FULLSCREEN flag, which will cause the window to take over
    * the fullscreen. This is the preferred DirectDraw mode because it allows
    * us to have control of the whole screen without regard for GDI.
    *
    * DDSCL_NORMAL is used to allow the DirectDraw app to run windowed.
    */
    ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
    if( ddrval != DD_OK )
    {
        lpDD->Release();
        return(false);
    }

   /*
    * Set the video mode to 640x480x8
    * This is allowed because we have set exclusive mode above
    */
    ddrval = lpDD->SetDisplayMode( 640, 480, 8);
    if( ddrval != DD_OK )
    {
        lpDD->Release();
        return(false);
    }

    return(true);
}

We have now initialized DirectDraw and set the display mode.

3. Creating the primary surface and setting up for page flipping

Next we create the primary surface with one back buffer. This is called a complex surface in DirectDraw. The BackBuffer is "attached" to the primary surface. When the cleaning up at the end of the program, you only need to call Release on the primary surface pointer, and the backbuffer is freed for you.

// globals (ugh)
LPDIRECTDRAWSURFACE lpDDSPrimary; // DirectDraw primary surface
LPDIRECTDRAWSURFACE lpDDSBack; // DirectDraw back surface

/*
* Create a Primary surface that is flippable, with an
* attached backbuffer
*/
bool CreatePrimarySurface()
{
    DDSURFACEDESC ddsd;
    DDSCAPS ddscaps;
    HRESULT ddrval;

    // Create the primary surface with 1 back buffer
    memset( &ddsd, 0, sizeof(ddsd) );
    ddsd.dwSize = sizeof( ddsd );

    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
    ddsd.dwBackBufferCount = 1;

    ddrval = lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );
    if( ddrval != DD_OK )
    {
        lpDD->Release();
        return(false);
    }

    // Get the pointer to the back buffer
    ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
    ddrval = lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);
    if( ddrval != DD_OK )
    {
        lpDDSPrimary->Release();
        lpDD->Release();
        return(false);
    }

    return true;
}

We have now created our double buffered page flipping environment.

4. Loading a bitmap into video memory

The next step is to load the bitmaps for the game. Ideally there is enough video memory so that all the bitmaps live in video memory. If not, CreateSurface will create the surfaces in system memory for you. Everything will still work, the blits from these surfaces will simply be a bit slower.

/*
* Function to create an offscreen surface and load a bitmap from
* disk into it. The szBitmap field is the filename of the Bitmap.
*/
IDirectDrawSurface * DDLoadBitmap(IDirectDraw *pdd, LPCSTR szBitmap)
{
    HBITMAP hbm;
    BITMAP bm;
    IDirectDrawSurface *pdds;

    // LoadImage has some added functionality in Windows 95 that allows
    // you to load a bitmap from a file on disk.
    hbm = (HBITMAP)LoadImage(NULL, szBitmap, IMAGE_BITMAP, 0, 0,
    LR_LOADFROMFILE|LR_CREATEDIBSECTION);

    if (hbm == NULL)
        return NULL;

    GetObject(hbm, sizeof(bm), &bm); // get size of bitmap

   /*
    * create a DirectDrawSurface for this bitmap
    * source to function CreateOffScreenSurface() follows immediately
    */

    pdds = CreateOffScreenSurface(pdd, bm.bmWidth, bm.bmHeight);
    if (pdds) {
        DDCopyBitmap(pdds, hbm, bm.bmWidth, bm.bmHeight);
    }

    DeleteObject(hbm);

    return pdds;
}

/*
* Creates a DirectDraw Surface of a specified size
* This surface will be in video memory if enough is
* available, otherwise it will be created in system memory
*/
IDirectDrawSurface * CreateOffScreenSurface(IDirectDraw *pdd, int dx, int dy)
{
    DDSURFACEDESC ddsd;
    IDirectDrawSurface *pdds;

    //
    // 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;
    ddsd.dwWidth = dx;
    ddsd.dwHeight = dy;

    if (pdd->CreateSurface(&ddsd, &pdds, NULL) != DD_OK)
    {
        return NULL;
    } else {
        return pdds;
    }
}

/*
* This function copies a previously loaded bitmap into a DirectDraw surface.
* Notice that we can obtain a GDI style device context for a
* DirectDraw surface, with which to BitBlt the bitmap into the
* surface.
*/
HRESULT DDCopyBitmap(IDirectDrawSurface *pdds, HBITMAP hbm, int dx, int dy)
{
    HDC hdcImage;
    HDC hdc;
    HRESULT hr;
    HBITMAP hbmOld;

    //
    // select bitmap into a memoryDC so we can use it.
    //
    hdcImage = CreateCompatibleDC(NULL);
    hbmOld = (HBITMAP)SelectObject(hdcImage, hbm);

    if ((hr = pdds->GetDC(&hdc)) == DD_OK)
    {
        BitBlt(hdc, 0, 0, dx, dy, hdcImage, 0, 0, SRCCOPY);
        pdds->ReleaseDC(hdc);
    }

    SelectObject(hdcImage, hbmOld);
    DeleteDC(hdcImage);

    return hr;
}

At this point we have a fully functioning DirectDraw surface with a bitmap loaded into it. We will use this surface later to blit it onto the scene we are composing.

5. Loading the palette

We need to set the palette for the primary surface. This implies that all of the bitmaps that are created for the game should all have been created with the same palette. Here is a function to create a DirectDraw palette for a given bitmap.

/*
* Creates a DirectDraw palette for a given bitmap on disk.
* The parameter szBitmap is the file name of the bitmap.
*
*/
IDirectDrawPalette * DDLoadPalette(IDirectDraw *pdd, LPCSTR szBitmap)
{
    IDirectDrawPalette* ddpal;
    int i;
    int n;
    int fh;
    PALETTEENTRY ape[256];

    if (szBitmap && (fh = _lopen(szBitmap, OF_READ)) != -1)
    {
        BITMAPFILEHEADER bf;
        BITMAPINFOHEADER bi;

        _lread(fh, &bf, sizeof(bf));
        _lread(fh, &bi, sizeof(bi));
        _lread(fh, ape, sizeof(ape));
        _lclose(fh);

        if (bi.biSize != sizeof(BITMAPINFOHEADER))
            n = 0;
        else if (bi.biBitCount > 8)
            n = 0;
        else if (bi.biClrUsed == 0)
            n = 1 << bi.biBitCount;
        else
            n = bi.biClrUsed;

      //
      // a DIB color table has its colors


Page : << Previous 2  Next >>