Using an Identity Palette

The Windows Palette Manager, described in depth in Ron Gery's technical article "The Palette Manager: How and Why" (see the Further Reading section for details) arbitrates conflicts between Windows applications vying for color entries in a single hardware palette (known as the system palette). It gives each application it's own virtual 256-color palette, called a logical palette, and translates entries in the logical palette to entries in the system palette as they are needed for blting images to the screen.

An identity palette is a logical palette which exactly matches the current system palette. An identity palette does not require translation of palette entries, so using an identity palette can drastically improve the speed with which you can blt WinGDCs to the screen.

The WinG Halftone Palette is an identity palette. This article describes how to create your own identity palettes for maximum WinG blt speed.

Static Colors

The Palette Manager reserves a number of colors in the palette, called the static colors, which it uses to draw system elements such as window captions, menus, borders, and scroll bars. An identity palette must include the static colors in the appropriate palette entries.

The display driver defines the actual RGB values of the static colors, so they must always be determined at run time. The GetSystemPaletteEntries will retrieve the colors currently in the system palette, and you can isolate the static colors using the SIZEPALETTE and NUMCOLORS capability indices with GetDeviceCaps and a little knowledge of how the Palette Manager works.

The static colors are split in half and stored at either end of the system palette. If there are nColors possible entries in the system palette and there are nStaticColors static colors, then the static colors will be found in entries 0 through nStaticColors/2 - 1 and entries nColors - nStaticColors/2 through nColors-1 in the system palette. Typically, there are 20 static colors, found in indices 0-9 and 246-255 of a 256-color palette. The peFlags portion of these PALETTEENTRY structures must be set to zero.

The SetSystemPaletteUse API turns use of the static colors on and off for the system. Using SYSPAL_STATIC, 20 entries will be reserved in the palette. SYSPAL_NOSTATIC reserves only 2 entries, which must be mapped to black and white. See the Accessing a Full Palette Using SYSPAL_NOSTATIC article for more information.

Other Colors

The remaining non-static colors in the logical palette may be defined by the application, but they must be marked as PC_NOCOLLAPSE or PC_RESERVED (see the PALETTEENTRY documentation for a description) to ensure an identity palette.

A palette containing the static colors in the appropriate entries with the remaining entries marked PC_NOCOLLAPSE, selected and realized into a DC, becomes an identity palette (with the exception of a palette with duplicates of the high-intensity colors as mentioned below). Because no translation to the system palette is required, the Palette Manager can step aside gracefully and leave you to achieve maximum blt bandwidth.

If your palette contains duplicates of the high-intensity static colors (the colors at the top of the palette) you need to set the duplicate entries to PC_RESERVED so GDI doesn't map the upper colors in your logical palette into those slots. PC_RESERVED tells GDI to not only fail to map the current entry into another, but it also keeps other entries (the high-intensity colors in this case) from mapping into the current entry. PC_NOCOLLAPSE only stops the current entry from mapping into other entries, but other entries can still map into the current entry making the palette non-identity.

Creating an Identity Palette

The CreateIdentityPalette() function below shows how to create an identity palette from an array of RGBQUAD structures. Before you realize an identity palette for the first time, it is a good idea to clear the system palette by realizing a completely black palette, as the ClearSystemPalette() function below does. This will ensure that palette-managed applications executed before your application will not affect the identity mapping of your carefully constructed palette.

To make sure that you have successfully created and are using an identity palette, you can tell WinG to send debugging messages to the standard debug output, as described in the Debugging With WinG article.

The PALANIM sample (in the SAMPLES\PALANIM subdirectory of the WinG development kit) uses these routines to create a 256-entry identity palette filled with a wash of color.

Click Here to copy the CreateIdentityPalette() code sample to the clipboard.

Click Here to copy the ClearSystemPalette() code sample to the clipboard.

HPALETTE CreateIdentityPalette(RGBQUAD aRGB[], int nColors)

{

int i;

struct {

WORD Version;

WORD NumberOfEntries;

PALETTEENTRY aEntries[256];

} Palette =

{

0x300,

256

};

//*** Just use the screen DC where we need it

HDC hdc = GetDC(NULL);

//*** For SYSPAL_NOSTATIC, just copy the color table into

//*** a PALETTEENTRY array and replace the first and last entries

//*** with black and white

if (GetSystemPaletteUse(hdc) == SYSPAL_NOSTATIC)

{

//*** Fill in the palette with the given values, marking each

//*** as PC_NOCOLLAPSE

for(i = 0; i < nColors; i++)

{

Palette.aEntries[i].peRed = aRGB[i].rgbRed;

Palette.aEntries[i].peGreen = aRGB[i].rgbGreen;

Palette.aEntries[i].peBlue = aRGB[i].rgbBlue;

Palette.aEntries[i].peFlags = PC_NOCOLLAPSE;

}

//*** Mark any unused entries PC_NOCOLLAPSE

for (; i < 256; ++i)

{

Palette.aEntries[i].peFlags = PC_NOCOLLAPSE;

}

//*** Make sure the last entry is white

//*** This may replace an entry in the array!

Palette.aEntries[255].peRed = 255;

Palette.aEntries[255].peGreen = 255;

Palette.aEntries[255].peBlue = 255;

Palette.aEntries[255].peFlags = 0;

//*** And the first is black

//*** This may replace an entry in the array!

Palette.aEntries[0].peRed = 0;

Palette.aEntries[0].peGreen = 0;

Palette.aEntries[0].peBlue = 0;

Palette.aEntries[0].peFlags = 0;

}

else

//*** For SYSPAL_STATIC, get the twenty static colors into

//*** the array, then fill in the empty spaces with the

//*** given color table

{

int nStaticColors;

int nUsableColors;

//*** Get the static colors from the system palette

nStaticColors = GetDeviceCaps(hdc, NUMCOLORS);

GetSystemPaletteEntries(hdc, 0, 256, Palette.aEntries);

//*** Set the peFlags of the lower static colors to zero

nStaticColors = nStaticColors / 2;

for (i=0; i<nStaticColors; i++)

Palette.aEntries[i].peFlags = 0;

//*** Fill in the entries from the given color table

nUsableColors = nColors - nStaticColors;

for (; i<nUsableColors; i++)

{

Palette.aEntries[i].peRed = aRGB[i].rgbRed;

Palette.aEntries[i].peGreen = aRGB[i].rgbGreen;

Palette.aEntries[i].peBlue = aRGB[i].rgbBlue;

Palette.aEntries[i].peFlags = PC_NOCOLLAPSE;

}

//*** Mark any empty entries as PC_NOCOLLAPSE

for (; i<256 - nStaticColors; i++)

Palette.aEntries[i].peFlags = PC_NOCOLLAPSE;

//*** Set the peFlags of the upper static colors to zero

for (i = 256 - nStaticColors; i<256; i++)

Palette.aEntries[i].peFlags = 0;

}

//*** Remember to release the DC!

ReleaseDC(NULL, hdc);

//*** Return the palette

return CreatePalette((LOGPALETTE *)&Palette);

}

void ClearSystemPalette(void)

{

//*** A dummy palette setup

struct

{

WORD Version;

WORD NumberOfEntries;

PALETTEENTRY aEntries[256];

} Palette =

{

0x300,

256

};

HPALETTE ScreenPalette = 0;

HDC ScreenDC;

int Counter;

//*** Reset everything in the system palette to black

for(Counter = 0; Counter < 256; Counter++)

{

Palette.aEntries[Counter].peRed = 0;

Palette.aEntries[Counter].peGreen = 0;

Palette.aEntries[Counter].peBlue = 0;

Palette.aEntries[Counter].peFlags = PC_NOCOLLAPSE;

}

//*** Create, select, realize, deselect, and delete the palette

ScreenDC = GetDC(NULL);

ScreenPalette = CreatePalette((LOGPALETTE *)&Palette);

if (ScreenPalette)

{

ScreenPalette = SelectPalette(ScreenDC,ScreenPalette,FALSE);

RealizePalette(ScreenDC);

ScreenPalette = SelectPalette(ScreenDC,ScreenPalette,FALSE);

DeleteObject(ScreenPalette);

}

ReleaseDC(NULL, ScreenDC);

}