Topic : DirectX Programming
Author : Ian Fleeton
Page : << Previous 5  Next >>
Go to page :


to them by way of an index.

Note: True colour is 24 bits giving a massive 16 million different colours. Supposedly our eyes cannot distinguish between similar colours at this level, hence the name. 32 bits is hardly ever used as a way of having more colours. Instead the remaining 8 bits are either used for transparency purposes or they are used as padding.


Palette Problems and Benefits


The main reason for using a palette is for the speed, allowing for a higher frame rate. The second benefit is that you can change the palette's colours without altering the image, and the colours in the image will be changed automatically by the video card. This is great for effects such as fading, rotating through a set of colours and blinking lights.

256 colours is not actually as restrictive as you might believe. For example, Age of Empires II uses 256 colours and looks great. However, you will need a very good artist to produce quality graphics under these circumstances. Part of the skill is choosing a good set of colours to begin with.


Right, I get the picture! Can I program one now?


Sure. First off you need to know what a PALETTEENTRY is. It's a Microsoft structure for defining a colour to go into a palette. There are four fields - one for red, green and blue and a field for flags. Flags? Well there's one you need to know about and that is PC_NOCOLLAPSE. This prevents DirectDraw from trying to optimise (and therefore change) your palette. So you must initialise 256 of these PALETTEENTRY structures. Lets make a random palette:

PALETTENTRY paletteEntry[256];
for(int i = 0; i < 256; i ++)
{
    paletteEntry[i].peRed = rand() % 256;
    paletteEntry[i].peGreen = rand() % 256;
    paletteEntry[i].peBlue = rand() % 256;
    paletteEntry[i].peFlags = PC_NOCOLLAPSE;
}


At the moment this palette only exists as data floating around in some RAM. Now we've got to get it into the picture. This involves another DirectDraw COM interface - IDirectDrawPalette. What we must do is create the IDirectDrawPalette with our palette entries and then attach it to the primary surface because that's the one that the user will see. Later you can use this interface to change the palette.


IDirectDrawPalette pal;
PALETTEENTRY pe[256];
// Set up palette entries
// dd is your DirectDraw interface pointer
HRESULT r;
r = dd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &pal, 0);
if(FAILED(r)) { /* Error */ }



The first parameter to CreatePalette are flags that describe the palette. DDPCAPS_8BIT says that we're using an 8 bit palette, not one of those really old 4, 2 or 1 bit ones. DDPCAPS_ALLOW256 means that all 256 colours are used and that none are kept in place by Windows. DDPCAPS_INITIALIZE isn't used here because the latest DirectDraw version always initializes the palette with entries so you must specify a PALETTEENTRY array. This is the second parameter. I've tried setting it to 0 (or NULL) but it doesn't work. So if you don't care about the palette's colours when you're creating the IDirectDrawPalette interface just use an uninitialised array.

Lets now attach the palette.

r = primary->SetPalette(pal);
if(FAILED(r)) { /* Error */ }


Now, whatever gets displayed on the screen will have its colours determined by the palette.

If you later want to change the colours in the palette, you don't have to create another IDirectDrawPalette interface. Instead, fill up a PALETTEENTRY array with the new colours and use IDirectDrawPalette::SetEntries():


r = pal->SetPaletteEntries(0, 0, 256, peNewColours);
if(FAILED(r)) { /* Error */ }



The first parameter must be zero because there aren't any flags supported yet. The next parameter is the starting colour, typically zero, and the one following that is the number of colours you want to update, typically all 256. The final parameter is the pointer to the PALETTEENTRY array.

That's about it for palettes then. The rest of the tutorial is a collection of hints on creating and using them. The demo project is called ddraw2 and displays a palette of colours and then makes them pulse by altering the brightness of the palette.


Paint Shop Pro Palette Loading


PSP saves its palette files in plain text which is very useful for loading into your own programs. Here's a typical JASC palette but with 250 entries missing:

JASC-PAL
0100
256
41 0 0
41 15 0
44 15 0
...
255 250 0
255 254 0
0 0 0

You can probably see how to do this yourself now. First, skip over the rubbish at the top of the file and then read the entries into a PALETTEENTRY array. What do those thing at the top mean? The first line is just an identifier so PSP knows it's handling one of its own palettes, the second line is probably a version identifier and the third line is the number of colours. You can check all that if you want but this is how I approach it:

std::ifstream file("dark.pal");
if(!file)
{
#ifdef SHAREWARE
ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE);
#else
// Handle error
#endif
}
std::string tmp; // Used for reading garbage safely
file >> tmp >> tmp >> tmp;
PALETTEENTRY pe[256];
for(int i = 0; i < 256; i ++)
{
    int r, g, b;
    file >> r >> g >> b;
    if(r < 0 || g < 0 || b < 0 || r > 255 || g > 255 || b > 255)
    { /* Error - palette is corrupt */ }
    pe[i].peRed = r;
    pe[i].peGreen = g;
    pe[i].peBlue = b;
    pe[i].peFlags = PC_NOCOLLAPSE;
}



Grey-Scale Palettes


For a grey-scale palette you need to have all colour components the same within an entry. To create a palette of 256 greys, starting with black and ending at white, use the following code:

PALETTEENTRY pe[256];
for(BYTE b = 0; b <= 255; b ++)
{
    pe[i].peRed = pe[i].peGreen = pe[i].peBlue = b;
    pe[i].peFlags = PC_NOCOLLAPSE;
}



16-bit Colour


This section of the tutorial isn't as easy as the rest. Instead it is a collection of notes on 16-bit colour under DirectX put together to help a couple of my readers.

The code presented here hasn't been tested but should work. Also, you may want to optimise here and there because my functions are written so you can understand them easily.

16-bit is used often because you get a lot more colour than with 8-bit but it's not as bandwidth hungry as true colour.

24-bit's nice and easy because you have one byte per colour element (red, green and blue). There are, unfortunately for you, a few different ways of encoding 16-bit colours. Only two are of importance.

There is 555 colour and 565 colour, named because of the way the bits are arranged. The bits for these are as follows:

-rrrrrgggggbbbbb and rrrrrggggggbbbbb

In the first case the first bit is either ignored or used for alpha blending - something you won't need writing 2D DirectDraw games. In the second there is an extra bit for green. Why green I hear you ask. People who know a lot about our eyes say we're more perceptive to it. I'll take their word on it.

You do not have a choice which one to use! It depends on the user's video card and the two encodings are common enough that you better consider both. If you write your game to use only one of those encodings then the game may look okay on your computer but the colours will be messed up on somebody elses. Sometimes you will make a mistake in manipulating the bits for one of them. Be sure to test.

When you get onto sprites, most of this is unimportant if you use Windows' LoadBitmap() because it will do all the detection automatically and blitting will be fine. If you intend to load your own graphic file format or decide to write pixels to the screen yourself then you need to pay attention.

You will need to write two pieces of code, or use a variable for the number of positions you need to bitshift the red and green components. (Blue is always unaffected.) Which you use depends on the how efficient it will be for the situation.

// Strategy 1 - Two optimised functions

void renderWholeScreen555();
void renderWholeScreen565();

void loop()
{
  if(isDisplay555())
    renderWholeScreen555();
  else
    renderWholeScreen565();
}

// Strategy 2 - Single functing using variables

void renderWholeScreen16()
{
  int rShift;
  int gShift;
  if(isDisplay555())
  {
      rShift = 10;
      gShift = 5;
  }
  else
  {
      rShift = 11;
      gShift = 6;
  }

  // Plot a pixel
  setPixel(x, y, (red << redShift) + (green << greenShift) + blue);
}


Note: In Windows Game Programming for Dummies - this is not covered.

How do you know which it is? Well, set up the display mode and co-op level and then do a GetDisplayMode on the

Page : << Previous 5  Next >>