Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
79 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Creating a
 DirectDraw Palette

 Pixel Formats
 Locking Surfaces
 Plotting Pixels
 Notes on Speed
 Fading Out
 Basic Transparency

 Printable version
 Discuss this article
 in the forums



The Series
 Beginning Windows
 Programming

 Using Resources
 in Win32 Programs

 Tracking Your
 Window/Using GDI

 Introduction
 to DirectX

 Palettes and Pixels
 in DirectDraw

 Bitmapped Graphics
 in DirectDraw

 Developing the
 Game Structure

 Basic Tile Engines
 Adding Characters
 Tips and Tricks

Introduction

Today we're going to go over basic DirectDraw graphics using both palettized and RGB modes. What's the difference? Well, a palettized screen mode is probably what you're used to if you've been programming in DOS. To plot pixels, you write a single byte to the video memory. That byte is an index into a lookup table full of colors, which is called a palette. RGB modes are different, because there's no lookup table. To plot a pixel in an RGB display mode, you write the values for red, green, and blue directly into the video memory. Any color depth higher than 8-bit is RGB instead of palettized. But now we're getting ahead of ourselves!

In writing this article, I assume you have either read my previous entries in the series, or are otherwise familiar with setting up DirectDraw and creating surfaces. We'll be using version 7 of DirectX, which contains the most recent DirectDraw interfaces. Actually, on a side note, the DirectDraw interfaces in DirectX 7 will be the last ones released! Don't worry, future versions of DirectX will still be backwards-compatible, but future implementations will have DirectDraw and Direct3D integrated into one component. But let's not worry about that now. :)

One last thing I will mention before we get started: the information on palettes is not necessary for anything I'll be covering later in the series, so if you're not interested in palettized modes, feel free to skip over the first part of this article and pick up when we get to the Pixel Formats section. Now that my disclaimers are at an end, let's get going!

Creating a DirectDraw Palette

Before working with graphics in a color depth of 8-bit or less, you must create a palette, which is simply a table full of colors. More specifically, in DirectX, a palette is a table full of PALETTEENTRY structures. To create one, there are three steps we need to take:

  1. Create the color lookup table.
  2. Get a pointer to an IDirectDrawPalette interface.
  3. Attach the palette to a DirectDraw surface.

I'll assume that we're using 8-bit color for the rest of this section; if you wanted to write a game with 16 colors (or less), you wouldn't be reading up on all this crazy Windows stuff, right? Anyway, in 8-bit color depth, you have a palette with 256 entries. So to create our lookup table, we'll need 256 of these:

typedef struct tagPALETTEENTRY { // pe
  BYTE peRed;
  BYTE peGreen; 
  BYTE peBlue;
  BYTE peFlags;
} PALETTEENTRY;

The first three members are fairly obvious; they are the intensities of red, green, and blue in the given color. Each one can range from 0 to 255, as BYTE is an unsigned data type. The last member of the structure is a control flag and should be set to PC_NOCOLLAPSE. Basically what this does is guarantees that your colors will be preserved as they are, rather than being matched to an existing system palette. We've all seen what MS Paint does when you try to save a picture as an 8-bit image. You want that happening to your game? I didn't think so. :)

Now, as I said, we're going to need a bunch of these structures, so the logical thing to do is to declare an array that we'll use for the lookup table. It would look like this:

PALETTEENTRY palette[256];

So now that we've got our array, you can load in your colors. When I'm working in a palettized mode, I'll usually have my colors saved in an external file, and use something like this to load the colors in:

FILE* file_ptr;
int x;

if ((file_ptr = fopen("palette.dat", "rb")) != NULL)
{
  fread(palette, sizeof(PALETTEENTRY), 256, file_ptr);
  fclose(file_ptr);
}

All right, that does it for step one. Now we need to get the palette interface. This is accomplished by a call to IDirectDraw7::CreatePalette(), which looks like this:

HRESULT CreatePalette(
  DWORD dwFlags,                           
  LPPALETTEENTRY lpColorTable,             
  LPDIRECTDRAWPALETTE FAR *lplpDDPalette,  
  IUnknown FAR *pUnkOuter
);

The return type is the standard HRESULT we're used to, so you can use the FAILED() and SUCCEEDED() macros to test whether or not the call succeeds. The parameters are as follows:

DWORD dwFlags: Any number of a series of flags describing the palette object. As usual, you can combine the values with the | operator. The valid flags are:

DDPCAPS_1BIT1-bit color; this corresponds to a 2-color palette.
DDPCAPS_2BIT2-bit color; this corresponds to a 4-color palette.
DDPCAPS_4BIT4-bit color; this corresponds to a 16-color palette.
DDPCAPS_8BIT8-bit color; this corresponds to a 256-color palette.
DDPCAPS_8BITENTRIESIndicates that the index refers to an 8-bit color index. That is, each color entry is itself an index to a destination surface's 8-bit palette. It must be combined with DDPCAPS_1BIT, DDPCAPS_2BIT, or DDPCAPS_4BIT. This is called an indexed palette. Weird, hey?
DDPCAPS_ALPHAIndicates that the peFlags member of each PALETTEENTRY should be interpreted as an alpha value. Palettes created with this flag can only be attached to a Direct3D texture surface, since DirectDraw itself doesn't support alpha-blending.
DDPCAPS_ALLOW256Allows all 256 entries of an 8-bit palette to be used. Normally, index 0 is reserved for black, and index 255 is reserved for white.
DDPCAPS_INITIALIZEIndicates that the palette should be initialized with the values of an array of PALETTEENTRYs that is passed in another parameter of CreatePalette().
DDPCAPS_PRIMARYSURFACEIndicates that the palette will be attached to the primary surface, so that altering it immediately changes the colors of the display.
DDPCAPS_VSYNCForces palette changes to take place only during the vertical blank, minimizing the odd effects normally associated with palette-changing during a drawing cycle. This is not fully supported.

For the most part, you'll want to use DDPCAPS_8BIT | DDPCAPS_INITIALIZE, though you can take out the latter if you just want to create an empty palette and set it up later. Or you may want to add DDPCAPS_ALLOW256 if you really want to change the two normally reserved entries.

LPPALETTEENTRY lpColorTable: This is a pointer to the lookup table we created, so just pass the name of the array.

LPDIRECTDRAWPALETTE FAR *lplpDDPalette: This is the address of a pointer to an IDirectDrawPalette interface, which will be initialized if the call succeeds.

IUnkown FAR *pUnkOuter: As always, this is for advanced COM stuff and should be set to NULL.

That's not too bad, so now we can go ahead and create our palette object. The last step is to attach that palette to a surface, which requires only a simple function call to IDirectDrawSurface7::SetPalette(). The function is shown below:

HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette);

Pretty straightforward, isn't it? All you do is pass the interface pointer we got in the last step. So, to put a quick example together, let's assume we already have a lookup table created in an array called palette, like we did above. To create a DirectDraw palette to hold the colors, and attach it to our primary surface (which has also been created already), we do the following:

LPDIRECTDRAWPALETTE lpddpal;

// create the palette object
if (FAILED(lpdd7->CreatePalette(DDPCAPS_8BIT | DDPCAPS_INITIALIZE,
                                palette, &lpddpal, NULL)))
{
  // error-handling code here
}

// attach to primary surface
if (FAILED(lpddsPrimary->SetPalette(lpddpal)))
{
  // error-handling code here
}

And that's all there is to it. Once your palette is set up, plotting pixels is not too different from the way it's done in RGB modes. From this point on, I'll be covering both RGB and palettized modes. Before we get into actually displaying graphics, though, I need to show you what RGB pixel formats are like.




Next : Pixel Formats