Documentation
[SDK Documentation] [GapiDraw from a DirectDraw developers perspective]

 

GapiDraw from a DirectDraw developers perspective

The Direct Draw examples on this page was taken from the excellent Direct Draw Programming tutorial by Lan Mader, available online at gamedev.net.

This document has been updated for use with GapiDraw 3.0 or later.
Last updated on Wednesday, January 14, 2004.

 

Introduction

GapiDraw is designed to be as similar to DirectDraw as possible, but at the same time being as easy to use and as optimized as possible for handheld devices. This page presents some common tasks in DirectDraw, and how they are implemented in GapiDraw.

 

Opening the display device

A display is to a computer nothing more than a memory area containing bytes representing pixels. To write directly to this area, both DirectDraw and GapiDraw requires you to create a specialized surface called a primary surface. Drawing to this primary surface directly affects what is visible on screen.

The first step to create a primary surface is to open the display and set a display mode. The following steps are the minimum required number of steps to do so using DirectDraw.

DirectDraw

LPDIRECTDRAW lpDD;

HRESULT ddrval;

// Create the main Direct Draw object
ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
if(ddrval != DD_OK)
{
	return(false);
}

// Set Cooperative level to allow Direct Draw to run full screen
ddrval = lpDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
if(ddrval != DD_OK)
{
	lpDD->Release();
	return(false);
}

// Set display mode to 320x240x16
ddrval = lpDD->SetDisplayMode(320, 240, 16);
if(ddrval != DD_OK)
{
	lpDD->Release();
	return(false);
}

With GapiDraw things are much easier. Since surfaces are objects instead of COM interfaces, they never have to be manually released. The following GapiDraw example opens the display device and sets the default display mode with just one command.

GapiDraw

CGapiDisplay display;

HRESULT gdrval;

// Open fullscreen display in standard Pocket PC 320x240x16 mode
gdrval = display.OpenDisplay(GDDISPLAY_FULLSCREEN, hWnd, 320, 240, 0, 0, 0, 0);
if(gdrval != GD_OK)
{
	return(false);
}

 

Retreiving the primary surface and back buffer

Using Direct Draw, you manually have to request the main Direct Draw object to create a special surface for you, the primary surface, which can be used to draw directly to the display. The reason only one surface interface is used for both memory surfaces and displays in Direct Draw can easily be explained with the lack of subclassing in the old COM model being used. The following example creates a primary surface and retrieves its back buffer using Direct Draw.

DirectDraw

LPDIRECTDRAWSURFACE lpDDSPrimary; // DirectDraw primary surface
LPDIRECTDRAWSURFACE lpDDSBack;    // DirectDraw back surface

DDSURFACEDESC ddsd;
DDSCAPS ddscaps;
HRESULT ddrval;

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;

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

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

Again, using GapiDraw things are much easier. The CGapiDisplay object automatically becomes the primary surface once CGapiSurface::OpenDisplay has been called. Since CGapiDisplay is a subclass of CGapiSurface, all blit and drawing operations are already available. To get the backbuffer from the CGapiDisplay, this is done as in the following code sample.

GapiDraw

CGapiSurface* pBackbuffer; // GapiDraw back surface

// Get the back buffer
pBackbuffer = display.GetBackBuffer();
if(NULL == pBackbuffer)
{
	return(false);
}

 

Flipping and losing surfaces

Surfaces in DirectDraw are usually stored in video memory and may actually be overwritten at any time (in case the user switched applications or started another one using GDI). This means that each operation to a surface can fail at any time as well, simply because the surface data was overwritten. So all operations using Direct Draw must check every time if the surface was lost, and then manually restore and re-create the surface from scratch. The following example illustrates this.

DirectDraw

ddrval = lpDDSBack->Blt(&rcRectDest, lpDDSMySurf, &rcRectSrc, DDBLT_WAIT, NULL);
if(ddrval == DDERR_SURFACELOST)
{
	// The surface was overwritten, and you must now restore
	// and re-create all your surfaces manually.
}

ddrval = lpDDSPrimary->Flip(NULL, DDFLIP_WAIT);
if(ddrval == DDERR_SURFACELOST)
{
	// The surface was overwritten, and you must now restore
	// and re-create all your surfaces manually.
}

Since Pocket PCs do not use video memory, all surface data is stored in physical RAM memory and is copied to the display area only when CGapiDisplay::Flip is called. We therefore do not have to check for lost video surfaces.

GapiDraw

// Surfaces in normal operations cannot be lost
gdrval = backbuffer.Blt(&rcRectDest, &mysurf, &rcRectSrc, 0, NULL);
gdrval = display.Flip();

 

Summing up

What has been mentioned above are the major differences between GapiDraw and Direct Draw. Other features such as blits, color keys, rectangle coordinates etc. are identical. GapiDraw also contains a huge amount of extra features not available in Direct Draw, such as advanced blit effects, zooming while rotating, loading bitmap images from file or memory, drawing tools, collision masks, surface intersections, thread timers, bitmapped font support, and much more.