JavaScript EditorFree JavaScript Editor     Ajax Editor 

Main Page
  Previous Section Next Section

The Subtleties of Color

DirectDraw supports a number of different color depths, including 1, 2, 4, 8, 16, 24, and 32 bpp. Obviously, 1, 2, and 4 bits per pixel are a little outdated, so don't concern yourself with these color depths. On the other hand, the 8-, 16-, 24-, and 32-bit modes are of utmost interest. Most games you write, you'll write to run in either 8-bit palletized mode for speed reasons (also it's a good mode to learn with), or 16- or 24-bit mode for full RGB color utilization. The RGB modes work by writing similar-sized WORDs into the frame buffer, as shown in Figure 6.6. The palletized mode works by using a look-up table that is indexed by each individual pixel value in the frame buffer, which is always a single byte. Thus, there are 256 different values—you have seen all this before, so it should look familiar.

Figure 6.6. Comparison of various color depths.


What you need to learn to do is create a 256-color palette and then tell DirectDraw that you want to use it. So let's see the steps involved:

  1. Create one or more palette data structures as arrays of 256 PALETTENTRY's.

  2. Create a DirectDraw palette interface IDirectDrawPalette object from the DirectDraw object itself. In many cases, this will be directly mapped to the hardware VGA palette registers.

  3. Attach the palette object to a drawing surface, such as the primary surface, so all data rendered to it is displayed in the appropriate colors.

  4. (Optional) If you desire, you can change the palette entries or the entire palette. You will need to take this step if you sent a NULL palette during step 2 and opted to omit step 1. Basically, what I'm trying to say is that when you create a palette interface, you can send it a palette of color also. But if you don't, you can always do it later. Therefore, step 2 can be step 1 if you remember to fill up the palette entries at a later time.

Let's begin by creating the palette data structure. It's nothing more than an array of 256 palette entries based on the PALETTENTRY Win32 structure, shown here:

typedef struct tagPALETTEENTRY
        BYTE peRed;     // red component 8-bits
        BYTE peGreen;   // green component 8-bits
        BYTE peBlue;    // blue component 8-bits
        BYTE peFlags;   // control flags: set to PC_NOCOLLAPSE

Look familiar? It better! Anyway, to create a palette, you simply create an array of these structures, like this:

PALETTEENTRY palette[256];

And then you fill them up in any way you desire. However, there is one rule: You must set the peFlags field to PC_NOCOLLAPSE. This is necessary because you don't want Win32/DirectX optimizing your palette for you. With that in mind, here's an example of creating a random palette with black in position 0 and white in position 255:

PALETTEENTRY palette[256]; // palette storage

// fill em up with color!
for (int color=1; color < 255; color++)
    // fill with random RGB values
    palette[color].peRed   = rand()%256;
    palette[color].peGreen = rand()%256;
    palette[color].peBlue  = rand()%256;

    // set flags field to PC_NOCOLLAPSE
    palette[color].peFlags = PC_NOCOLLAPSE;
    } // end for color

// now fill in entry 0 and 255 with black and white
palette[0].peRed   = 0;
palette[0].peGreen = 0;
palette[0].peBlue  = 0;
palette[0].peFlags = PC_NOCOLLAPSE;

palette[255].peRed   = 255;
palette[255].peGreen = 255;
palette[255].peBlue  = 255;
palette[255].peFlags = PC_NOCOLLAPSE;

That's all there is to it! Of course, you can create multiple palettes and fill them with whatever you want; it's up to you.

Moving on, the next step is to create the actual IDirectDrawPalette interface. Luckily for you, the interface hasn't changed as of DirectX 6.0, so you don't need to use QueryInterface() or anything. Here's the prototype for IDirectDraw7:: CreatePalette(), which creates a palette object:

HRESULT CreatePalette(DWORD dwFlags,   // control flags
LPPALETTEENTRY lpColorTable,  // palette data or NULL
LPDIRECTDRAWPALETTE FAR *lplpDDPalette,  // received palette interface
       IUnknown FAR *pUnkOuter);   // advanced, make NULL

The function returns DD_OK if successful.

Let's take a look at the parameters. The first parameter is dwFlags, which controls the various properties of the palette—more on this in a minute. The next parameter is a pointer to the initial palette, or NULL if you don't want to send one. Next you have the actual IDirectDrawPalette interface storage pointer that receives the interface if the function is successful. Finally, pUnkOuter is for advanced COM stuff, so simply send NULL.

The only interesting parameter of the bunch is, of course, the flags parameter dwFlags. Let's take a more in-depth look at what your options are. Refer to Table 6.4 for the possible values you can logically OR to create the flags WORD.

Table 6.4. Control Flags for CreatePalette()
Value Description
DDPCAPS_1BIT 1-bit color. There are two entries in the color table.
DDPCAPS_2BIT 2-bit color. There are four entries in the color table.
DDPCAPS_4BIT 4-bit color. There are 16 entries in the color table.
DDPCAPS_8BIT 8-bit color. The most common. There are 256 entries in the color table.
DDPCAPS_8BITENTRIES This is for an advanced feature referred to as indexed palettes and is used for 1-, 2-, and 4-bit palettes. Just say no.
DDPCAPS_ALPHA Indicates that the peFlags member of the associated PALETTEENTRY structure is to be interpreted as a single _8-bit alpha value controlling the transparency. A palette created with this flag can only be attached to a D3D texture surface created with the DDSCAPS_TEXTURE capability flag. Again, this is advanced and for big G's.
DDPCAPS_ALLOW256 Indicates that this palette can have all 256 entries defined. Normally, entries 0 and 255 are reserved for black and white, respectively, and on some systems like NT you can't write to these entries under any circumstances. However, in most cases you don't need this flag because 0 is usually black anyway, and most palettes can live with entry 255 being white. It's up to you.
DDPCAPS_INITIALIZE Initialize this palette with the colors in the color array passed at lpDDColorArray. This is used to enable the palette data sent to be downloaded into the hardware palette.
DDPCAPS_PRIMARYSURFACE This palette is attached to the primary surface. Changing this palette's color table immediately affects the display unless DDPSETPAL_VSYNC is specified and supported.
DDPCAPS_VSYNC Forces palette updates to be performed only during the vertical blank period. This minimizes color anomalies and sparkling. Not fully supported yet, though.

A lot of confusing control words, if you ask me. Basically, you only need to work with 8-bit palettes, so the control flags you need to OR together are


And if you don't care about setting color entries 0 and 256, you can omit DDPCAPS_ALLOW256. Furthermore, if you're not sending a palette during the CreatePalette() call, you can omit DDPCAPS_INITIALIZE.

Sucking all that down into your brain, here's how you would create a palette object with your random palette:

LPDIRECTDRAWPALETTE lpddpal = NULL; // palette interface

if (FAILED(lpdd->CreatePalette(DDPCAPS_8BIT |
                                DDPCAPS_ALLOW256 |
   // error
   } // end if

If the function call is successful, lpddpal will return with a valid IDirectDrawPalette interface. Also, the hardware color palette will instantly be updated with the sent palette, which in this case is a collection of 256 random colors.

Normally, at this point I would drop a demo on you, but unfortunately we're at one of those "chicken and the egg" points in DirectDraw. That is, you can't see the colors until you can draw on the screen. So that's what's next!

      Previous Section Next Section

    JavaScript EditorAjax Editor     JavaScript Editor