The Subtleties of ColorDirectDraw 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:
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 } PALETTEENTRY; 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. 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 DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE 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 | DDPCAPS_INITIALIZE, palette, &lpddpal, NULL))) { // 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! |