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


stored BGR not RGB
      // so flip them around.
      //
        for(i=0; i<n; i++ )
        {
            BYTE r = ape[i].peRed;
            ape[i].peRed = ape[i].peBlue;
            ape[i].peBlue = r;
        }
    }
    if (pdd->CreatePalette(DDPCAPS_8BIT, ape, &ddpal, NULL) != DD_OK)
    {
        return NULL;
    } else {
        return ddpal;
    }
}

Next we set this palette to the primary surface like this:

    lpDDPal = DDLoadPalette(lpDD, szBitmap); // Call the function above to load the palette
        if (lpDDPal)

    lpDDSPrimary->SetPalette(lpDDPal); // this sets the palette for the primary surface


6. Color Keying

DirectDraw uses the concept of color keying to allow for using transparent colors during blits. That is, you can make part of your bitmap appear to be transparent, which is a useful technique to use when blitting sprites onto a background surface. The color key specifies a range of palette entries in the source or the destination bitmap that will not be copied during a blit. Typically you set a the 0th or the 255th palette entry to be the transparent color on the source bitmaps, and also construct our bitmaps such that the areas that need to be transparent use this color of the palette.

   // Set the color key for this bitmap.
    //
    // Whatever color is at entry 255 in the palette will be the
    // transparent color for blits. This color is black unless
    // you used the DDPCAPS_ALLOW256 flag when the palette was
    // created

    DDCOLORKEY ddck;
    ddck.dwColorSpaceLowValue = 0xff;
    ddck.dwColorSpaceHighValue = 0xff;

    // lpDDSSomeBitmapSurface is a DirectDraw surface with a
    // bitmap loaded into it. This needs to be done for each
    // surface containing a bitmap.
    lpDDSSomeBitmapSurface->SetColorKey( DDCKEY_SRCBLT, &ddck );



7. Composing the Scene

At this point we are ready to start creating a game, simulation or other graphics based program. The simple approach is to blit a background bitmap onto the back buffer, then blit some sprites (also just bitmaps) onto the back buffer at their appropriate locations, and flip. Then repeat the above steps, blitting the sprites into new locations.

// This function should be called repeatedly during the idle time of your
// PeekMessage()loop.

void updateFrame( void )
{
    RECT rcRect;
    HRESULT ddrval;
    int xpos, ypos;

    // Blit the stuff for the next frame
    SetRect(&rcRect, 0, 0, 640, 480);

    // blit the background bitmap. This is a 640x480 bitmap,
    // so it will fill the screen (remember, we set the video
    // mode to 640x480x8.
    // The parameter lpDDSOne is a hypothetical surface with this
    // background bitmap loaded. LpDDSBack is our backbuffer.
    ddrval = lpDDSBack->BltFast( 0, 0, lpDDSOne, &rcRect,
    DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT);

    if( ddrval != DD_OK )
    {
        return;
    }
    SetRect(&rcRect, 0, 0, 32, 32); // our fictitious sprite bitmap is 32x32 in size

    // xpos and ypos represent some appropriate location for the sprite.
    // lpDDSMySprite is just a surface with the bitmap for our sprite.
    ddrval = lpDDSBack->BltFast( xpos, ypos, lpDDSMySprite,
    &rcRect, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT );
    if( ddrval != DD_OK )
    {
        return;
    }

    // Flip the surfaces
    ddrval = lpDDSPrimary->Flip( NULL, DDFLIP_WAIT );
} /* updateFrame */


A couple of things to note here. In the calls to BltFast(), and Flip(), the last parameter included a DDXX_WAIT flag. This is necessary because the each of these functions is asynchronous. That is, they will return when the operation was successfully started (but not yet finished), or when an error caused the function to not be able to start successfully. The most common error returned is an error indicating that the surface is busy completing a previous operation on the surface. You use the DDXX_WAIT flag to tell DirectDraw to keep trying the operation as long as the surface is busy, or until some other error occurs.

Notice that the first call to BltFast(), the one that is blitting the background, uses the flag DDBLTFAST_NOCOLORKEY. This tells BltFast to ignore the color key, and can improve the performance of the blit on some cards. This means that any transparent areas on this bitmap will be copied and therefore not be transparent, but this is OK since this is the background bitmap and not a sprite like image.

When the Flip() function is called, the surface objects associated with the video memory underneath are swapped. This means that what was once the primary buffer is now the backbuffer and vice versa. The result is that you can just keep using your backbuffer pointer to compose the scenes, and call Flip(), without having to worry about keeping track of where the buffers are.

8. Cleaning up

To cleanup in DirectX, the main thing is to call the Release() member function on any DirectX object you created. You would do something like this:

*
* Call Release on all objects we created to clean up
*/
void finiObjects( void )
{
    if( lpDD != NULL )
    {
        if( lpDDSPrimary != NULL )
        {
            lpDDSPrimary->Release();
            lpDDSPrimary = NULL;
        }

        if( lpDDSOne != NULL )
        {
            lpDDSOne->Release();
            lpDDSOne = NULL;
        }

        if( lpDDPal != NULL )
        {
            lpDDPal->Release();
            lpDDPal = NULL;
    }
    lpDD->Release();
    lpDD = NULL;
} /* finiObjects */

When you call release on the actual DirectDraw object (the instance of LPDIRECTDRAW) the reference count will go to zero, and the object will be destroyed. When this happens, the original screen mode is restored.





A Few Details
1. Debugging DirectDraw


When a DirectDraw app is running full screen, GDI is not available. This means that you cannot set breakpoints in your app and expect to see the debugger when they are hit. There are some fancy solutions involving debuggers that support dual monitors, or remote debugging, but the simplest in terms of hardware requirements is to simply write your app such that it can run windowed as well as full screen.

2. Running Windowed

When you want to run windowed you must initialize DirectDraw differently. Since you have to coexist with GDI, you are not allowed to change the display mode. The main thing to keep in mind is that when you create your primary surface, you are getting a pointer to the display memory currently visible and in use by GDI. You can, if you want, scribble anywhere you want on the screen. So you have to be careful to keep your drawing inside of your window. You can use a DirectDraw clipper to help with this if you like.

The following function initializes DirectDraw for running in a window, and doesn't use page flipping.

Bool IsWindowed = false;

// function to initialize DirectDraw in windowed mode
bool DDGameInitWindowed(THIS_ HWND hwnd)
{
    DDSURFACEDESC ddsd;
    DDSCAPS ddscaps;
    HRESULT ddrval;

    IsWindowed = true;

   /*
    * create the main DirectDraw object
    */
    ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
    if(


Page : << Previous 3  Next >>