FOURCC.org - Video Codecs and Pixel Formats

DirectDraw Overlay Surfaces

There appears to be a great deal of confusion over how to create and display an overlay surface under DirectDraw so here's some sample code showing (very simply) how to create an overlay and show it stretched into a window. This code is not intended to be directly compiled (I cut and pasted it from various testcases) but to show the basic flow of events. Also, to aid clarity, it is rather oversimplified. It assumes the FOURCC when, in fact, it should query the supported formats from the driver and it doesn't consider multipage overlays and flipping (these are left as an exercise for the reader :-) ).

Disclaimer

You use this code at your own risk. I make no guarantee that it will work and I will not be held responsible for anything nasty that happens as a result of you using it. Having said that, it's worked fine for me and may be of use to you too. Also, if you find any blatant errors, please let me know.

Assumptions

The sample code assumes the following variables :

Type Name Description
DDPIXELFORMAT ddPixelFormat Pixel format description for the overlay surface
HRESULT ddrval Return value from DirectDraw API calls
DDSURFACEDESC ddsd Surface description used in creation of primary and overlay surfaces
LPDIRECTDRAW lpDD Pointer to the global DirectDraw object
LPDIRECTDRAWSURFACE lpPrimary Pointer to the primary surface
LPDIRECTDRAWSURFACE lpOverlay Pointer to the overlay surface
LPDIRECTDRAWCLIPPER lpClipper Pointer to the clipper
RECT rectClient Window size and position
RECT rectOverlay Overlay source rectangle
DDOVERLAYFX ddofx Effects used in displaying the overlay
DWORD dwFlags Work variable used in the move/size code
DWORD dwKeyColour The colour to be used as the key for the overlay. This value will change depending upon the primary surface mode.
HWND hwnd Handle of the window inside which the overlay is to be displayed.

Object Creation

/*************************/
/* Initialise DirectDraw */
/*************************/
ddrval = DirectDrawCreate(NULL, &lpDD, NULL);
if(ddrval != DD_OK)
{
  TRACE("Can't initialise DirectDraw ! 0x%08lx\n", ddrval);
  return(FALSE);
}

ddrval = IDirectDraw_SetCooperativeLevel(lpDD, hwnd, DDSCL_NORMAL);
if(ddrval != DD_OK)
{
  TRACE("Can't set DirectDraw cooperative level ! 0x%08lx\n", ddrval);
  IDirectDraw_Release(lpDD);
  return(FALSE);
}

/******************************/
/* Create the primary surface */
/******************************/
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

ddrval = IDirectDraw_CreateSurface( lpDD, &ddsd, &lpPrimary, NULL );

if( ddrval != DD_OK )
{
  TRACE("Can't create primary surface ! 0x%08lx\n", ddrval);
  IDirectDraw_Release(lpDD);
  return(FALSE);
}

/******************************/
/* Create the overlay surface */
/******************************/
ddPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddPixelFormat.dwFlags = DDPF_FOURCC;
ddPixelFormat.dwFourCC = mmioFOURCC('U','Y','V','Y');
ddPixelFormat.dwYUVBitCount = 16;

ddsd.dwSize = sizeof(DDSURFACEDESC);
ddsd.dwFlags = DDSD_CAPS |
               DDSD_HEIGHT |
               DDSD_WIDTH |
               DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY;
ddsd.dwHeight = 240; /* Change these values as appropriate (obviously !) */
ddsd.dwWidth = 320;

memcpy(&(ddsd.ddpfPixelFormat), &ddPixelFormat, sizeof(DDPIXELFORMAT));

ddrval = IDirectDraw_CreateSurface( lpDD, &ddsd, &lpOverlay, NULL );

if( ddrval != DD_OK )
{
  TRACE("Can't create overlay surface ! 0x%08lx\n", ddrval);
  IDirectDrawSurface_Release(lpPrimary);
  IDirectDraw_Release(lpDD);
  return(FALSE);
}
/***********************************/
/* Create a clipper for our window */
/***********************************/
ddrval = IDirectDraw_CreateClipper(lpDD, 0, &lpClipper, NULL);
if( ddrval != DD_OK )
{
  TRACE("Can't create DirectDraw clipper ! 0x%08lx\n", ddrval);
}
else
  ddrval = IDirectDrawClipper_SetHWnd(lpClipper, 0, hwnd);

Window Move/Size Processing

The following code should be put in a function called when processing WM_MOVE and WM_SIZE messages. Note that it is vitally important that your window receives WM_MOVE messages even if the size has not changed. If the window is a child of the main application window (as in most cases), these messages are not posted to it by default and you have to relay them from the parent's window handler function.

/***************************************/
/* Show the overlay with colour keying */
/***************************************/

/*************************/
/* Where is the window ? */
/*************************/
GetWindowRect(hwnd, &rectClient);

/*********************************/
/* Position and show the overlay */
/*********************************/
SetRect(&rectOverlay, 0, 0, 320, 240);

ddofx.dwSize = sizeof(DDOVERLAYFX);
ddofx.dckDestColorkey.dwColorSpaceLowValue = dwKeyColour;
ddofx.dckDestColorkey.dwColorSpaceHighValue = dwKeyColour;

dwFlags = DDOVER_KEYDESTOVERRIDE | DDOVER_SHOW;

ddrval = IDirectDrawSurface_UpdateOverlay(lpOverlay,
                                          &rectOverlay,
                                          lpPrimary,
                                          &rectClient,
                                          dwFlags,
                                          &ddofx);
if(ddrval != DD_OK)
{
  // Oops - can't update overlay. Put your error handling code here.
}

Window Paint Processing

This is the basic code required inside WM_PAINT processing to fill the client area with a colour key value above which the overlay will be displayed. Note the use of a clipper to prevent the DirectDraw Blt call from corrupting any overlying windows, menus, etc.

Many people use normal GDI calls to draw the colour key but this requires a great deal of care when using a palettised desktop since the palette index of a particular RGB colour can change if the palette is altered by another application resulting in your user seeing a lovely (for example) magenta rectangle instead of your nice overlay surface. Using the DirectDraw Blt, however, you can guarantee the value written to the primary surface so your colour key will work regardless of the state of the system palette.

/****************************************************************/
/* Attach the clipper to the primary surface for this operation */
/****************************************************************/
ddrval = IDirectDrawSurface_SetClipper(lpPrimary, lpClipper);

hdc = BeginPaint(hwnd, &ps);

/* Fill the client area with colour key */
ptClient.x = ps.rcPaint.left;
ptClient.y = ps.rcPaint.top;
ClientToScreen(hwnd, &ptClient);
rectBlt.left = ptClient.x;
rectBlt.top = ptClient.y;

ptClient.x = ps.rcPaint.right;
ptClient.y = ps.rcPaint.bottom;
ClientToScreen(hwnd, &ptClient);
rectBlt.right = ptClient.x;
rectBlt.bottom = ptClient.y;

ddbfx.dwSize = sizeof(DDBLTFX);
ddbfx.dwFillColor = dwKeyColour;

TRACE("Filling rectangle (%d, %d), (%d, %d) with colour 0x%08lx\n",
      rectBlt.left,
      rectBlt.top,
      rectBlt.right,
      rectBlt.bottom,
      ddbfx.dwFillColor);

IDirectDrawSurface_Blt(lpPrimary,
                       &rectBlt,
                       NULL,
                       &rectBlt,
                       DDBLT_COLORFILL | DDBLT_WAIT,
                       &ddbfx);

EndPaint(hwnd, &ps);

ddrval = IDirectDrawSurface_SetClipper(lpPrimary, NULL);
fourcc.org ©2011 Silicon.dk ApS