The Palette Manager: How and Why

Ron Gery
Microsoft Developer Network Technology Group

Created: March 23, 1992

There are two sample applications associated with this technical article.

Click to open or copy the files for the MULTIPAL sample application.

Click to open or copy the files for the DIBIT sample application.

Abstract

This article is intended as a full introduction to the Microsoft® Windows™ Palette Manager. Beyond simply describing the use of the palette interface, this article attempts to explain its specific internal workings and gives reasons for its chosen implementation. Due to the complexity of the Palette Manager, this article gives some topics an initial explanation and then goes into them in greater detail later in the article. Because the result is rather long, it is recommended that you read it start to finish before using it as a reference. For a less in-depth discussion on using palettes, check out the "Palette Awareness" technical article on the Microsoft Developer Network CD (Technical Articles, Windows Articles, GDI Articles).

Disclaimer   Any and all internal details discussed in this article are subject to change without notice in future versions of Windows. Any reliance on these details must be made version-dependent on Windows versions 3.0 and 3.1.

Overview

The first part of this two-part article describes the design of the Microsoft® Windows™ Palette Manager and the basic process of palette realization.

Terminology

Before starting on the meat of this article, some commonly used terms need to be introduced. They are covered in much greater detail below, but establishing a working definition early on should help to keep them easily identifiable throughout.

Introduction

The Windows Palette Manager, an integral part of GDI, was written so that applications can make use of a class of display adapter hardware that possesses the ability to display a vast number of colors but is restricted to only a certain number of colors at any one time. The design was targeted specifically to 8-bit display adapters with a hardware palette (or color lookup table) of 256 entries. Every pixel on the screen is stored as an 8-bit index into the hardware palette. Each entry in the hardware palette defines a color, usually 24 bits (8 each of red, green, and blue), although often only 18 bits of color information are actually used. These displays are known as palette devices.

The palette solution for display adapters is not a clean one. While providing the enticement of high color resolution, it does not really deliver. The future of display hardware, it is hoped, will continue in the direction of full-color devices, supporting 24 bits of "pure" color per pixel without the limitation of a color table. In the current time frame, though, palette devices are a fact of life that are becoming widely available even on entry-level PC systems.

The fundamental problem posed by a palette device is how do you allow every application to make use of all those available 256 high-resolution colors? Obviously that is not possible under Windows because Windows is intended as an environment for running multiple applications at once. Some priority system needs to be created to ensure that at least some applications are happy some of the time. That is the role the Windows Palette Manager plays. It controls the use of the system palette, the basic premise being that the current active application has priority over all other applications in setting the system palette. It also provides an interface for setting and accessing entries in the system palette.

The Palette Manager interface is also intended to be device independent in nature. On nonpalette devices, some of the functions may not actually perform any work "behind the scenes," but the Palette Manager interface makes it possible for an application to attain access to as much color support as the device can provide. Devices that do support palettes are identified by having the RC_PALETTE bit set in the RASTERCAPS value returned from the GetDeviceCaps function.

Should every application make use of the Palette Manager? The answer is no. Applications that are not especially concerned with color do not need to deal with palettes. On palette devices, the application simply gets a color selection similar to that of a VGA device. If that is a sufficient rainbow for the purposes of your application, ignore this article.

Note on VGAs and Palettes

While the standard VGA hardware does have a palette-based design, the number of available colors (64) and the number of hardware palette entries (16) are too limiting for use by the Palette Manager. As a result, the Windows standard VGA driver does not support palettes; instead, it has a fixed color table of 16 colors. The Windows Palette Manager cannot be used to alter the colors of this table.

Basic Design

Before going into the basics of the Palette Manager setup, this article looks at some alternatives to the Palette Manager. One possibility is to have the hardware palette preset to a color wash that roughly covers the color spectrum. This is roughly akin to expanding the VGA hardware palette from 16 colors to 256 colors. Presetting the hardware palette can achieve very effective dithering and greater color accuracy for color fills and such. The advantage of a scheme like this is that it eliminates conflict over the hardware palette. Although no application can actually get super-accurate colors (unless they happen to coincide with the predefined 256), all applications receive a reasonable palette to choose from. The disadvantage—a big one—is that there is no color accuracy. Even with the larger rainbow, an application cannot hope to effectively display a scanned image, especially not one with complex colors like flesh tones. Although images can be dithered with acceptable results, color information is lost, editing capabilities disappear, and the dithering process is complex and time consuming. Any output primitives that are not dithered by the system, including lines and text, cannot be displayed accurately. The 8514 driver shipped with Windows version 2.x uses this approach.

The other possibility lies at the other end of the flexibility scale: Allow the foreground application to control the whole system palette. In this scheme, the rest of the system needs either to adjust every time the system palette is changed or simply to make do with inaccurate colors. In this scenario, the foreground application has access to 256 distinct colors and can therefore perfectly display an 8-bit image. The rest of the applications and systemwide features like window borders and menu highlights suffer the consequences.

The Palette Manager provides a compromise between these two extremes. The system palette is broken up into two sections, one with fixed colors and one with colors that can be changed by applications. The system palette predefines 20 entries; these colors are known as the static or reserved colors and consist of the 16 colors found in the Windows version 3.0 VGA driver and 4 additional colors chosen for their visual appeal. The DEFAULT_PALETTE stock object is, as the name implies, the default palette selected into a device context (DC) and consists of these static colors. Applications can set the remaining 236 colors using the Palette Manager.

The 20 static colors have several uses. They provide VGA color functionality for any application that does not explicitly use palettes; a color not based on a palette is mapped to these colors just as it would be on a VGA device driver, so there are no color concerns. The window manager component of Windows falls into this category of applications, and the static colors are used to draw window borders and other standard pieces. The static colors also provide a fallback set of colors for palette-using applications that are not in the foreground and that must match existing system palette colors. The use of static colors is discussed in greater detail below.

An application can set the 236 nonstatic colors by requesting colors from the Palette Manager. The active window has the highest priority in setting these colors. The remaining windows, known as background windows, receive priority based on the Z-order. Any system entries not used by a window with a higher priority can be set by these background windows. Once the system palette has been filled with requested colors, all subsequent color requests are mapped to the closest color available in the system palette. Yes, this sounds complicated. It is discussed in much greater depth below.

The Palette Manager provides VGA-level color to applications that are not color intensive, while simultaneously providing access to the system palette for those applications that are concerned with color accuracy.

Layout of the System Palette

The placement of the static colors in the system palette is of some concern. The most common raster operation (ROP) used under Windows is the XOR, which is usually used for inverting colors and then restoring them. To provide VGA-like behavior to the most basic of operations, the XOR must be made to operate in a consistent fashion. To accomplish this, the static colors are placed at either end of the system palette, 10 at the start and 10 at the end. This results in the inverse index of any one of the static colors being another static color (0 inverts to 255 and so on). The colors are ordered so that they match the index order on the VGA and result in the same inverse order. The 236 settable colors are between the two groups of static colors.

The diagram in Figure 1 shows the basic layout of the system palette.

Figure 1. The system palette layout

Table 1 shows the static colors, their logical color values, and the system palette indexes where they are located. Note that the specific values (especially for the four non-VGA colors) are specific to Windows versions 3.0 and 3.1 and may change in future releases.

Table 1. The Static Colors

Index Red Green Blue Color
0 0 0 0 black
1 0x80 0 0 dark red
2 0 0x80 0 dark green
3 0x80 0x80 0 dark yellow
4 0 0 0x80 dark blue
5 0x80 0 0x80 dark magenta
6 0 0x80 0x80 dark cyan
7 0xC0 0xC0 0xC0 light gray
8 0xC0 0xDC 0xC0 money green
9 0xA6 0xCA 0xF0 sky blue
246 0xFF 0xFB 0xF0 cream
247 0xA0 0xA0 0xA4 light gray
248 0x80 0x80 0x80 medium gray
249 0xFF 0 0 red
250 0 0xFF 0 green
251 0xFF 0xFF 0 yellow
252 0 0 0xFF blue
253 0xFF 0 0xFF magenta
254 0 0xFF 0xFF cyan
255 0xFF 0xFF 0xFF white

When a palette driver is first enabled and initialized, the Palette Manager sets the system palette for that device. The Palette Manager initializes the nonstatic 236 colors to a rainbow wash that is not maintained beyond the initialization phase. The rainbow allows colors placed in the lower nonstatic indexes to XOR better, but the Palette Manager never uses them for matching purposes.

Simple Use of a Logical Palette

Now that the Palette Manager has the system palette ready for use, it is up to the application to ask for the colors that it wants to display. An application requests colors from the Palette Manager by using a logical palette object. This object is created using the CreatePalette function. It consists of an array of PALETTEENTRY structures that define the colors that are being requested. A palette object is selected into a DC using the SelectPalette function. (The SelectObject function does not work with palettes.) Unlike other logical objects, a palette object must be explicitly realized by the application using the RealizePalette function. The actual color-matching work is performed during the realization step.

The following snippet of code creates a simple palette with 64 shades of blue:

HPALETTE CreateBluePalette()
{
    PLOGPALETTE pPal;
    HPALETTE hLogPal;
    unsigned char blue;

    pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPAL) +
            63 * sizeof(PALETTEENTRY));
                  // (Extra entry in LOGPAL struct)
    if (!pPal)
        return(NULL);

    pPal->palVersion = 0x300;
    pPal->palNumEntries = 64;

    // Brighter shades are placed first for higher priority.
    for (blue = 255, i = 0; i < 64; blue -= 4, i++)
    {
        pPal->palPalEntry[i].peRed = 
        pPal->palPalEntry[i].peGreen = 
        pPal->palPalEntry[i].peFlags = 0;
        pPal->palPalEntry[i].peBlue = blue;
    }
    hLogPal = CreatePalette(pPal);
    LocalFree(pPal);
    return(hLogPal);
}

The following code performs the work necessary to use the palette created above. Once it is executed, subsequent palette-based drawing will be relative to this logical palette. These two function calls are essentially always found as a pair:

SelectPalette(hDC, hBluePal, FALSE);
RealizePalette(hDC);

The selected and realized palette is the palette used for operations to the screen until a different palette is realized. Output to the screen using another DC without first realizing a different palette is affected by the current palette realization without regard to the palette actually selected into that DC. Inadvertent palette use can be eliminated by always selecting and realizing the appropriate palette before drawing and then restoring the previous palette when the drawing is complete.

Two distinct calls exist instead of a single select call that implicitly performs the realization for a simple reason. The reasoning is that it is valid to request a re-realization of a palette without having to reselect it. This is plausible when realizing a background palette in response to a system palette change. It is a good idea, though, to always pair the calls. Reselecting a palette that is already selected causes no harm, nor does re-realizing it.

Palette objects follow most of the same rules as other logical objects. They should be deleted when no longer needed by using the DeleteObject function, and they must be deselected from all DCs before being deleted (by using SelectPalette to select a different palette into the DC). One notable difference is that an application can select a palette object into more than one DC (belonging to a single device) at a time, but the palette's realization remains constant for all of the DCs.

Default behavior

When an application does not explicitly select a palette (and before a palette is initially selected into a DC), a default palette is selected into the DC. This palette is the DEFAULT_PALETTE stock object. The default palette contains 20 entries corresponding to the static colors, and the Palette Manager trivially maps it to the system palette. All palette management is performed automatically for an application that does not explicitly use palettes.

Restoring the previous palette

The big question with restoring the previous palette is "Should the previous palette be realized?" The answer is "It depends." The danger is that realizing the previous palette can, in the foreground case, change the system palette and destroy any work performed with the new palette. If the previous palette is the default palette, the correct behavior is to realize the previous palette—realizing the default palette does not affect the system palette. If the previous palette is a potentially harmful palette to realize, the application can select the palette back into the DC and force it into the background (bForceBackground parameter set to TRUE) before realizing it to ensure that it does not affect the new palette's realization work. Simply not realizing the previous palette is a valid option, but depending on the expectation of the application that selected that palette in the first place, this approach could leave the palette hanging without a valid realization for subsequent drawing.

Basic Palette Realization

Once RealizePalette is called, the real work begins. Unfortunately, there are many little quirks involved in the realization process, so this section deals with the vanilla version only. This is the gist of the process. During selection, the logical palette is marked as being either a foreground or a background palette. In the simplest case, SelectPalette is called with FALSE as the last parameter. This indicates that the foreground status of the palette is determined by the state of the window that owns the given DC. If the window is the current active window or a descendent of the currently active window, the palette is a foreground palette. Otherwise the palette is a background palette.

Each entry in the system palette can have one state: static, used, or unused. Static entries cannot be overwritten by a realization and are, in effect, permanently used. Unused entries can be set in the realization process. Once set, these entries become used—subsequent background realizations cannot overwrite the entry. What separates used entries from static entries is that a foreground realization changes all used entries to unused entries.

The critical property of a foreground palette is that, when realized, it can overwrite all entries other than static in the system palette. The Palette Manager accomplishes this by marking all of the entries that are not static in the system palette as unused before the realization of a foreground palette, thereby eliminating all of the used entries. No preprocessing is done on the system palette for a background palette realization. The net effect is that the system palette is cleared for a foreground palette, but it is left in its current use state for a background palette.

Now it's time for the realization itself. Each color in the logical palette is color-matched to every color in the system palette. The closest match is tracked. If an exact match is found, the color is matched and the entry in the system palette is marked as used. If no exact match exists, the Palette Manager looks for an entry that is unused. If one is found, the logical color is set in the system palette at that entry, and the entry is marked as used. A color in the system palette has thus been changed. If there are no unused entries left in the system palette (the palette is full), the logical color is mapped to the system palette entry with the closest color match. Notice that the color could be mapped to a color in the system palette that was set by the same logical palette.

In nonalgorithmic terms, the palette being realized takes up any free entries that remain in the system palette for its own use. When there are no more entries to take, the remaining colors in the logical palette are mapped to the nearest color that is already in the system palette. The foreground palette gets to set all of the possible nonstatic colors. Background palettes can only set what remains open and are prioritized in a first come, first served manner. The priority queue starts up again the next time a foreground palette is realized.

Using symbolically small palettes, Figure 2 shows the realization process in diagrammatic terms.

Figure 2. The realization process

Pretty simple, actually. The end result is that every color in the logical palette ends up with a mapping to one of the entries in the system palette. This mapping index remains current until the next time the palette is realized. An application using the logical color to draw uses this system palette mapping for actual drawing to the screen.

If any entries in the system palette were changed during the realization, the driver is notified of the change and updates the hardware palette with the new colors. Also, if the palette was a foreground palette, the WM_PALETTECHANGED message is broadcast to the system so that other applications are aware of the systemwide change. More on the messaging process below.

There are some interesting things that should be noted again. If a background palette is being realized and there are still entries in the system palette that are marked as unused, the background palette can set them. The foreground palette only has priority, not exclusivity. Also, if the foreground palette wants more entries than are actually available for setting in the system palette, too bad. When all of the unused system entries are used up, subsequent logical palette entries get mapped to the nearest color. As soon as a new foreground palette is realized, the system palette is effectively flushed of old priorities, and all of the nonstatic entries are once again open for use. Finally, identical colors are matched to existing entries in the system palette, eliminating color redundancy.

Bitmap Storage and Foreground Mapping

Device-dependent color bitmaps are stored as a collection of indexes (not colors) on palette devices. To achieve the desired color on the screen, the indexes must refer to a correct color in the hardware palette. The desired colors, though, are based on the colors of a logical palette, and the trick is to have the indexes work properly no matter how this palette is realized and mapped to the system palette. Clearly the indexes have to be relative to something in the logical palette.

One possible solution is to have the bitmap indexes be indexes into the logical palette. When the palette is realized, a translation table is established to correspond to the way the palette was mapped to the system palette. All subsequent blting operations going from the bitmap to the screen involve translating each pixel from a logical index to a physical index. This translation greatly slows down the blts.

To avoid this slowdown in many cases, the Palette Manager uses a slightly modified indexing system using foreground indexes. The first time a palette is realized, the Palette Manager builds up a foreground mapping for that palette. If the palette is being realized as a background palette, the foreground mapping is done without actually updating the system palette. This foreground mapping is the way the palette maps into the system palette when it is realized as a foreground palette. This mapping is a constant for the palette. (If any palette entries are changed, the mapping must be regenerated.) In terms of implementation, each entry in the logical palette has a foreground index associated with it.

Any subsequent time that the palette is realized as a foreground palette, the realization work is already done, and there is no need for any color-matching. The foreground indexes set the necessary colors in the system palette, and every color in the logical palette is automatically mapped to the expected physical color. For this reason, realizing a foreground palette is faster if the palette has already been realized in the past.

Bitmaps are stored using these foreground indexes. What does this mean for blting from memory? If the palette that corresponds to the bitmap is the currently realized foreground palette, the indexes in the bitmap refer directly to the current system palette. There is no need for translation! This makes for quite a speed improvement over constantly translating. Of course, if the palette is a background palette, translating every index in the bitmap may still be necessary. The translation in this case is from the foreground index to the current mapping index.

As a bonus, a bitmap that is based on the default palette also requires no translation because the default palette's mapping never changes (it maps directly to the static colors). The result is that applications that do not explicitly use palettes get "fast" blting regardless of their foreground/background state.

There are limitations to this scheme. Most critical to note is that the bitmap is inextricably tied to the palette. If a different palette is used during the blting operation, the bitmap is essentially meaningless because the indexes no longer have a valid reference point. The other limitation is a bit more subtle. Because only foreground indexes are used, and they are restricted by the presence of the static colors, an 8-bit image that does not contain all of the static colors in its original color table effectively loses whatever colors could not be set directly in the system palette. The device-dependent bitmap has lost color information.

The application does not need to concern itself with any of the index translation work; it is all automatically handled by GDI and the driver. The application does need to worry about using the correct palette with the bitmap before blting. A more detailed discussion of blting from memory bitmaps is found below.

Palette devices handle monochrome bitmaps the way other devices do: There is no dependence on any palette information, and monochrome-to-color blts are performed with the usual foreground/background mapping.

Messaging

Windows defines three palette-related messages: WM_QUERYNEWPALETTE, WM_PALETTECHANGED, and WM_PALETTEISCHANGING. The first two are useful for application implementation, but the third is a holdover from an earlier design and should be ignored.

The WM_QUERYNEWPALETTE message indicates window activation and provides an application the opportunity to realize its palette. This is the first and ideal opportunity to realize the drawing palette in the foreground and is usually a good time to get all the palette management organized. Windows does not actually use the return value.

The WM_PALETTECHANGED message informs all applications that the system palette has changed, changing the screen color of pixels mapped to the previous setting of the hardware palette. This message is sent when a foreground palette realization has caused at least one entry in the system palette to change. This is a purely informational message. Because this message is broadcast to the system, currently running applications receive it in Z-order, and this order determines palette priority. The usual response to this message is to re-realize any palettes currently used for display and to repaint using the new palette realization. Applications not using any palettes for the display are not affected by the change in the system palette.

The wParam parameter of the message identifies the window that caused the system palette to change. To avoid excess processing and a potentially recursive situation, the application causing the WM_PALETTECHANGED message should avoid realizing another foreground palette in response to this message.

If, in response to the WM_PALETTECHANGED message, an application simply forces a repaint without realizing its palette, the application effectively loses its place in the Z-order of realization because the resulting WM_PAINT message is processed only after all other applications have had an opportunity to realize their palettes. This can be detrimental to color accuracy.

Below is a good starting point for palette messaging:

case WM_PALETTECHANGED:
    if (wParam == hWnd)            // Responding to own message.
                                   // Nothing to do.
        break;
case WM_QUERYNEWPALETTE:
    hDC = GetDC(hWnd);
    hOldPal = SelectPalette(hDC, hPalCurrent, FALSE);
    i = RealizePalette(hDC);       // Realize drawing palette.

    if (i)                         // Did the realization change?
        InvalidateRect(hWnd, NULL, TRUE);    // Yes, so force a 
                                             // repaint.
    SelectPalette(hDC, hOldPal, TRUE);
    RealizePalette(hDC);
    ReleaseDC(hWnd, hDC);
    return(i);

More intricate responses to these messages are discussed below.

Notice that no painting is actually performed directly in response to these messages. The messages simply provide a mechanism for realizing a palette and establishing its relative priority. Once the palette is realized, no other palette can nullify the results of that realization (until the next foreground palette is realized). For the foreground applications, the WM_QUERYNEWPALETTE message indicates that it is time to realize the foreground palette, the one that controls the system palette; its realization does not change until that application realizes a different foreground palette or the application is deactivated and loses its foreground status, allowing another application to realize a foreground palette.

When the application does actually repaint and realizes the palette again, the mapping is already set, and the system palette does not change. Realizing a palette in response to these messages gets the palette "into" the current system palette picture so that later drawing can simply reference this earlier work.

Using Colors

The next step in understanding the Palette Manager is to see how it interprets the colors used by palette-using applications. The simplest colors are the ones found in the color table of a device-independent bitmap (DIB); they are always relative to the currently selected palette (based on the hDC parameter to SetDIBits, StretchDIBits, or SetDIBitsToDevice) and are color-matched to that palette on a palette device. If DIB_PAL_COLORS is used in the DIB function, the color table has indexes into the palette, so the matching is already done. Because the colors are used to define a single pixel in the image, dithering is not an option.

Colors passed directly to GDI (for example, using the CreatePen, SetTextColor, or FloodFill function) are of three possible types. The easiest type uses palette indexes (the color is built using the PALETTEINDEX macro), and it always maps to the color referenced in the palette. Palette-relative colors (built using the PALETTERGB macro) specify that the color should be color-matched to the current logical palette and that the nearest color in that palette should be used; on a nonpalette device, the original RGB color is used as is. The last color type, the pure RGB color (built using the RGB macro), is not mapped to the current logical palette. It is handled by the device driver as though that driver were a VGA driver. To be precise, the color is mapped to the nearest static color, and if a brush is being created, the color is dithered using the static colors. No dithering is done for any color based on a logical palette.

Palettes on Nonpalette Devices

The Palette Manager also functions on devices that do not support palettes. Palette selection works the same, although the foreground/background status is not maintained. Realization is simply ignored. Explicit references to the logical palette (PALETTEINDEX colors, DIB_PAL_COLORS color tables) are dereferenced, and the actual RGB color in the logical palette is used for the operation. Colors defined with the PALETTERGB macro or the RGB macro are used as RGB values. No palette-related messages are sent. The goal is to allow applications to use palettes in a device-independent fashion and to not worry about the actual palette capabilities of the device driver.

More Details

The information above is a broad overview of the design of the Palette Manager. The Palette Manager has many nuances and side effects that a developer of a palette-using application should be aware of. The rest of this article describes these subtleties.

Device Capabilities

Palette devices have several entries in their capability table that are of interest. All are accessible using the GetDeviceCaps function. First, the RC_PALETTE bit of the RASTERCAPS word is set to indicate that this device is indeed a palette device. The number of entries in the device's hardware palette is available with the SIZEPALETTE index; this value is set to 256 on a standard palette driver. The NUMRESERVED index specifies how many static colors the device is maintaining. (This could be a number other than 20, but because there are some problems in the Palette Manager in dealing with values smaller than 20, they are not common.) The COLORRES index identifies the actual color resolution of the device, that is, the number of bits of color resolution that the device actually maintains. On 256-color devices, this value is often 18, with 6 bits each for red, green, and blue. (As a result, a color component value of 0x11111111 is equivalent to a value of 0x11111100.) The Palette Manager operates with full 8-bit color resolution, so it is up to the application to handle any decreased resolution. Seeing as how the device can support 256 colors, one might expect the NUMCOLORS index to specify 256 as the number of supported colors. This, however, is not the case. Because the driver does not control most of those colors, the appropriate value for NUMCOLORS is 20, which is the number of static colors (although this may change in future versions). Similarly, when the driver enumerates pens and brushes and defines values for NUMPENS and NUMBRUSHES, it only duplicates the functionality of a VGA driver, enumerating pen and brush variations based on the VGA colors.

Palette Creation

When a palette is created, the application has the ability to control how each color is mapped to the system palette by using the peFlags field in each color's PALETTEENTRY structure. The most common setting is 0, which means this is a normal color request and follows the standard rules of realization.

The PC_EXPLICIT setting allows an application to map a palette entry directly to a specific entry in the system palette. The color in the system entry palette is not affected, so the logical palette entry is mapped to reflect the current contents of the system palette. This mapping is not affected by the foreground versus background status of the palette.

Things get more interesting with the PC_RESERVED and PC_NOCOLLAPSE flags. The PC_RESERVED flag is used in conjunction with the AnimatePalette function for palette animation (more details below). A color with this flag is not color-matched to any entry in the system palette; it can only occupy an unused entry. If an unused entry is available, the Palette Manager sets the color and marks the entry as used and reserved, which means that no other logical color can be color-matched to the entry. The entry is reserved for exclusive use by this logical palette entry. If there are no unused entries available, the logical palette entry is mapped to black (index 0) in the system palette and cannot be animated. PC_NOCOLLAPSE mapping is very similar to PC_RESERVED mapping in that an unused entry is always chosen over color-matching, but the color is matched if there are no unused entries. Also, once the color is set in the system palette, it is an ordinary used entry, and other color requests can be matched to it. The PC_NOCOLLAPSE flag is useful in situations where the application needs to guarantee that its palette entries are realized as a block in the system palette. Entries marked with PC_NOCOLLAPSE cannot be animated.

Color-matching Algorithm

Color-matching is performed using a closest-Pythagorean-distance algorithm. The number being minimized is (deltaR2 + deltaG2 + deltaB2). While this may seem a simplistic approach, the results are actually quite good.

Methods for Displaying Multiple Palettes

If an application needs to display more than one palette-based image at a time, it has several options for managing the multiple palettes:

A single palette that contains a rainbow of colors allows all the images to be displayed with approximate colors. None will look accurate, with the exception of very simplistic ones made up of basic colors that are found in the rainbow. This approach has the same effect as using a 256-color driver with a fixed hardware palette and has all of the related limitations.

Simply combining all of the palettes into a giant, conglomerate palette that is used for all drawing accomplishes the basic task. Any colors that cannot be set in the system palette are matched to existing colors. While simple concatenation of the palettes works, a better color mix can usually be encouraged with a more thorough merging (entry 0 comes from palette A, entry 1 from palette B, and so forth). If the original palettes are already sorted so that the more significant colors appear earlier in the palette, the resulting merged palette has the most significant colors from all the images at the front of the palette. Notice that the realization process will automatically collapse duplicate entries.

The giant palette approach preserves some image quality across all images, but it may not always be convenient in coding terms. For example, adding another image for display necessitates building and realizing a new palette, which also means that all bitmaps based on the original palette must be rebuilt. Also, DIBs maintained with DIB_PAL_COLORS tables need to have the tables updated with every change to the merged palette.

Another approach is for the application to manage the individual palettes as separate entities (this often makes managing separate images easier) and prioritize them for color-matching by forcing all but one of the palettes to be realized in the background. This is accomplished by using SelectPalette as follows:

// Select the chosen dominant palette.
SelectPalette(hDC, hDominantPalette, FALSE);
RealizePalette(hDC);

// Select a lower priority palette.
SelectPalette(hDC, hLowlyPalette, TRUE);
RealizePalette(hDC);

When the application is in the foreground, the dominant palette establishes itself in the system palette. The lower priority palettes used by the application are then matched to the first one and, if any room remains, could change the system palette as well. The dominant image looks the best because it was the first to set colors, and the subsequent images look as good as they can given the status of the system palette. It is important to note that one palette must be realized without being forced into the background; this ensures that the application gets access to the system palette.

For the background palette approach to work, the application must ensure that the secondary palettes are realized before any other application can realize another palette. When the application realizes its dominant palette in response to the WM_QUERYNEWPALETTE message, the application needs to realize its secondary palettes in response to the resulting WM_PALETTECHANGED message (wParam parameter equal to hWnd). Otherwise, all of the other applications have a chance to realize their palettes first, and the foreground application's background images lose out.

case WM_QUERYNEWPALETTE:
    hDC = GetDC(hWnd);
    hOldPal = SelectPalette(hDC, hDominantPal, FALSE);
    i = RealizePalette(hDC);     // Realize dominant palette.
    SelectPalette(hDC, hOldPal, TRUE);
    RealizePalette(hDC);
    ReleaseDC(hWnd, hDC);

    if (!i)          // No realization change, but let 
                     // secondaries realize.
        SendMessage(hWnd, WM_PALETTECHANGED, hWnd, NULL);
    else             // Palette did change. Repaint dominant image.
        InvalidateRect(hWnd, lpDominantRect, 1);
    return(i);

// This message is received for 3 reasons:
//  - Result of dominant palette realization (wParam == hWnd)
//  - Dominant palette did not actually change realization 
//    (wParam == hWnd)
//  - Some other application changed system palette (wParam != hWnd)
case WM_PALETTECHANGED:
    if (wParam == hWnd)  // Dominant palette realization caused 
                         // this.
        // Realize the secondary palettes, forcing them into the
        // background.
        // If the realization changes the system palette mapping,
        // then force an appropriate repaint.
    {
        hDC = GetDC(hWnd);
        hOldPal = SelectPalette(hDC, hLowlyPalette, TRUE);
        if (RealizePalette(hDC))
            InvalidateRect(hWnd, lpLowlyRect, 1);
        SelectPalette(hDC, hOldPal, TRUE);
        RealizePalette(hDC);
        ReleaseDC(hWnd, hDC);
    } else {
        // Normal palette adjustment/repaint. Dominant palette
        // needs to be realized here as well.
        hDC = GetDC(hWnd);
        hOldPal = SelectPalette(hDC, hDominantPalette, TRUE);
        if (RealizePalette(hDC))
            InvalidateRect(hWnd, lpDominantRect, 1);
        SelectPalette(hDC, hLowlyPalette, TRUE);
        if (RealizePalette(hDC))
            InvalidateRect(hWnd, lpLowlyRect, 1);
        SelectPalette(hDC, hOldPal, TRUE);
        RealizePalette(hDC);
        ReleaseDC(hWnd, hDC);
    }
    break;

The background palette approach to multiple palettes has the advantage of being very simple to code and to manage. The main limitation is that for images with many colors, only one image is displayed optimally; the other images suffer because their colors are matched to existing colors instead of being exact. The effect is identical to the Palette Manager's mechanism for handling multiple applications, each with a single palette, but in this case a single application is determining the priorities of multiple palettes. The MULTIPAL sample application on the Microsoft Developer Network CD uses this approach to manage multiple palettes.

Color analysis can provide the optimal solution. The application can analyze the color use in the images that are to be displayed and come up with the ultimate palette that contains a mix of colors sufficient to display all of the images in the best possible manner. Of course, this can be done in varying levels of complexity and exactness and is not recommended for most applications. Once a good color-use analysis is written, it can also be used nicely for creating idealized palettes for images with 24-bit color information and for other similar color-tracking purposes. This type of approach, though potentially very pleasing visually, tends to be on the slow side and is not trivial to code.

Contending with Palette Changes

When the system palette is changed by the foreground application, pixels on the screen that were mapped to entries that have changed are no longer correctly colored. The rest of the system needs to adjust to this change to maintain visual integrity.

Some applications and situations are not affected by this type of change, and they require no adjustments. Because the static colors do not change, applications that use only static colors (including all applications that do not use palettes explicitly) are not affected by the system palette change; this is one of the advantages of the static colors. Similarly, any pieces of a palette-using application that are drawn using only static colors do not change. The RealizePalette function returns a value for the number of entries in the logical palette that were remapped during the realization; this is the number of entries that were mapped to the system palette differently from the previous mapping. Do not confuse this with the number of entries in the system palette that changed. When the return value is 0 and the logical palette was used to draw the image currently on the screen, no pixels in the drawing were affected by the system palette change.

It is important to remember that palette-based memory bitmaps are not affected by changes in the system palette. To display the bitmap after a system palette change, simply select and realize the associated palette and blt the bitmap to the screen. The re-realized palette ensures that the bitmap's color-mapping is based on the current system palette. The bitmap may not look as good as it did when its palette was the foreground palette, but it looks as good as it can using the available colors in the system palette.

Adjusted repaints

The simplest, although not usually the quickest, method of handling a system palette change is to repaint the entire client area. This approach also produces the most accurate results. To map properly to the new system palette, the logical palette being used for the drawing must be re-realized before the drawing begins. The repainting can be optimized if it is known that some parts are not affected by the palette change. Because the repainting is based on a new mapping of the logical palette, the physical colors used are based on the current system palette.

UpdateColors

The UpdateColors function allows the application to simply update all of the visible pixels in its screen DC to match to the current system palette. The Palette Manager maintains three mappings for each entry in a logical palette. Two of these, the foreground index and the current index, have already been discussed. The third is the previous index; it is simply the current index from the previous realization. After an application re-realizes its palette in response to a change in the system palette but before it repaints itself, the image displayed by the application is drawn with this previous mapping. When the UpdateColors function is called, the Palette Manager creates a translation table from the previous mapping to the current mapping and passes it to the device driver. The driver then translates each pixel in the application's client area from the old mapping to the current mapping. The result is that the image is now drawn using the current mapping and therefore is based on the current system palette. Note that the logical palette must be re-realized after the system palette change for the updating to work properly.

The advantage of using this function is that it is simple to use and usually faster than repainting. The big disadvantage is that information is lost with every subsequent update, degrading the color quality of whatever is being updated. An application can compensate for this disadvantage by limiting the number of consecutive updates that it performs. For example, it can allow only two UpdateColors calls before a complete repaint:

case WM_QUERYNEWPALETTE:
    hDC = GetDC(hWnd);
    hOldPal = SelectPalette(hDC, hPalCurrent, FALSE);
    i = RealizePalette(hDC);     // Realize drawing palette.
    SelectPalette(hDC, hOldPal, TRUE);
    RealizePalette(hDC);
    ReleaseDC(hWnd, hDC);
    if (i)                       // Did the realization change?
    {
        InvalidateRect(hWnd, NULL, TRUE);    // Yes, so force a 
                                             // repaint.
        gUpdateCount = 0;        // Starting update tracking 
                                 // from scratch.
    }
    return(i);

case WM_PALETTECHANGED:
    if (wParam != hMyWnd)
    {
        hDC = GetDC(hWnd);
        hOldPal = SelectPalette(hDC, hPalCurrent, TRUE);

        // Only need to repaint if logical palette is remapped.
        if (RealizePalette(hDC))     // Realize drawing palette.
        {
            // If fewer than two updates have been done, we can 
            // update.
            if (++gUpdateCount < 2)
                UpdateColors(hDC);
            // Otherwise, it's time to repaint from scratch.
            else
            {
                gUpdateCount = 0;
                InvalidateRect(hWnd, NULL, TRUE);
            }
        }
        SelectPalette(hDC, hOldPal, TRUE);
        RealizePalette(hDC);
        ReleaseDC(hWnd, hDC);
    }
    break;

case WM_PAINT:
    if (gUpdateCount > 0)        // If painting after some 
                                 // updating...
    {
        BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        gUpdateCount = 0;        // Starting update tracking from 
                                 // scratch.
        InvalidateRect(hWnd, (LPRECT) NULL, TRUE);
        break;
    }
    else
        // Usual paint stuff.
    break;

The update count is also reinitialized to 0 when the application repaints as a foreground application or when it needs to repaint after any part of the window has been updated with UpdateColors. In both cases, the entire window is invalidated to guarantee a clean and optimal repaint.

Moving into the foreground

When a palette-using application becomes the foreground application (that is, when the application is activated), it should re-realize its palette in the foreground and repaint as appropriate. The message to key on is the WM_QUERYNEWPALETTE message. This situation is very similar to a system palette change in that if the application doesn't have anything to repaint, it doesn't actually need to repaint. The UpdateColors function is of little use in this case; a full repaint is the way to go. The key operation of being activated is the realization of the palette in the foreground.

Life after application termination

When a palette-using application is terminated and its top-level window is destroyed, the system needs to adjust to allow other applications access to the palette. If the next application to be activated is a palette-using application, there isn't much confusion because this application realizes its palette in the foreground in response to the WM_QUERYNEWPALETTE message, and palette normalcy is restored. If, on the other hand, the next application to be activated does not use palettes, who gets to control the palette? The system behavior differs slightly between Windows version 3.0 and version 3.1.

In Windows version 3.0, when an application's top-level window is destroyed and that window used a palette, the Palette Manager flushes out the system palette by marking all nonstatic entries as unused. This allows other applications access to the nonstatic entries. Next, the WM_PALETTECHANGED message is broadcast to the remaining applications and the desktop window.

The Palette Manager in Windows version 3.1 behaves the same way except, in addition, it sends the WM_QUERYNEWPALETTE message to the next palette-using application in the Z-order if the new active application does not use palettes. If no palette-using applications are currently running and the desktop is drawn with a bitmap, the desktop is given palette priority. A palette-using application in this case is defined as an application that has at some point explicitly called SelectPalette.

Before terminating, the terminating application only needs to worry about deleting any palette objects it created. The system handles the rest.

Memory Bitmaps

Bitmaps on palette devices do not contain color information. Without an accompanying palette to provide the color information, a bitmap is like a paint-by-numbers picture without the paint.

Memory bitmaps on palette devices are mostly like bitmaps on other devices. The one glaring difference is that each pixel is an index instead of an actual color. The color information is located in the palette that was used for drawing onto the bitmap. Because of this, using the bitmaps involves also setting up the correct palette before any operation is performed. The connection between memory bitmaps and the correct palette cannot be stressed enough. A color bitmap is only meaningful when used with the palette on which it is based; the palette is the one that actually defines the color. Of course, the application must first realize the palette to make it useful.

Monochrome bitmaps are treated the same across all devices. Because they contain no color information, there is no attached palette or a need for extra processing on a palette device.

Interesting blt behaviors

Blting from the screen to the screen involves no translation because all pixels are based on the same system palette, while blting from memory to the screen is done with a simple table translation that is set up by the Palette Manager during realization. On the other hand, blting from a memory bitmap to another memory bitmap is not trivial if the two bitmaps are not based on the same palette. The correct way to blt from one memory bitmap to another is to use DIBs, as follows:

SelectPalette(hDC, hPalette1, TRUE);
RealizePalette(hDC);
GetDIBits(hDC, hBitmap1, 0, height, lpBits, lpInfo, DIB_RGB_COLORS);

SelectObject(hMemDC, hBitmap2);
SelectPalette(hMemDC, hPalette2, TRUE);
RealizePalette(hMemDC);
StretchDIBits(hMemDC, DestX, DestY, Xext, Yext, SrcX, SrcY, Xext, 
              Yext, lpBits, lpInfo, DIB_RGB_COLORS, SRCCCOPY);

By using the DIB format, an application can disassociate the bitmap information from the palette and then establish a new relationship between the image and the second palette. Windows does not automatically perform this translation.

If the two bitmaps share a single palette, a simple blt between the two works without modification because the indexes in both bitmaps represent the same colors. Also, if the bitmaps use only static colors (meaning that effectively they are not based on a palette), the blting needs no translation.

Stretched blts

The term stretched blt refers to a blt operation that involves the stretching or shrinking of the source rectangle to fit the destination rectangle (technically speaking, when the source and destination extents do not match). A stretched blt is not necessarily or uniquely a result of calling the StretchBlt function. The BitBlt function can result in a stretched blt if the source and destination DCs have different mapping modes and, when the coordinates are converted to device units, the extents are no longer the same. Likewise, the StretchBlt function can result in a blt that is not a stretched blt if the extents are equal in device units.

The above DIB-based translation is performed automatically by GDI's stretched blt simulations. As a result, stretched blts between two DCs with different base palettes are performed with a palette translation, while a same-size blt operation is not translated. The exception to this rule is drivers that perform their own stretched blt operation (RC_STRETCHBLT bit in the RASTERCAPS capability word). These drivers circumvent GDI simulations and do not do the palette translation.

GDI simulations for a stretched blt also possess some interesting behaviors when the source and destination are based on the same palette. Because the stretching simulation is performed with DIBs, the speed of getting and setting of the DIB data is a definite concern. In cases where the two palettes are the same, the DIB conversions are sped up using DIB_PAL_COLORS for the color table. The problem is that the source image can contain colors that are not actually in the logical palette on which it is based (for example, if a static color is used to draw in a bitmap with a palette that does not explicitly have the color). Pixels drawn with these colors cannot be color-matched to the destination palette because only indexes are used, and these pixels end up being mapped to black. This behavior is not limited to memory-to-memory stretched blts; it applies to all classes of palette-based stretched blts. A simple application workaround for this situation is to place the static colors in the logical palette being used—they don't take up any room in the system palette (they map to the existing static colors), and they allow the DIB_PAL_COLORS color table to reference the static colors. Keeping the source bitmap "clean" of nonpalette colors is another safe approach.

ROPs on palette devices

Raster operations do not mean much on palette devices. The driver views every pixel as an 8-bit value and carries out the raster operation on these values one bit at a time. The resulting 8-bit value is then an index into the hardware palette. When this value happens to be in the range of the static colors, there is consistency, but values outside this range map to whatever color happens to be there. Predicting the resulting index is further complicated by the application's not knowing how its logical palette is mapped to the system palette; finding the hardware index that is being used for a particular entry in the palette is not straightforward. On top of that, the system palette can change once the application is no longer in the foreground, and repainting based on the new colors needs to be recalculated from scratch.

Some simple ROPs do work in a predictable manner. BLACKNESS and WHITENESS are preserved in meaning on palette devices. Also, inverting a static color results in another static color (consistent with the VGA color scheme), so simple highlighting can be accomplished.

DIB Handling

Converting DIBs into a device-dependent form naturally lends itself to palette use because the color table is essentially a palette. The whole format, in fact, greatly resembles the setup of a palette device with pixels represented as indexes into a color table. With 1-bit and 4-bit DIBs, if the colors in the table are boring (that is, black and white for 1-bit and the 16 VGA colors for 4-bit), using a palette is not required for color accuracy because the static colors and the default palette suffice. For 8-bit DIBs, though, a palette is useful for preserving color information on the screen.

An application creates a palette to go along with a DIB by simply using the colors in the color table to define the palette. Note that the RGB triples in the DIB's color table are stored in reverse order. This palette is then used either to convert the DIB into a bitmap using the SetDIBits function (the palette then defines the bitmap) or to display the DIB directly on the device.

If DIB_RGB_COLORS is used for the setting operation, the color table is defined as RGB values, and the Palette Manager matches each color to the current logical palette before the DIB is actually translated. The matching process is eliminated when the application uses the DIB_PAL_COLORS format for the DIB, using a table of indexes into the logical palette as the color table (this does not apply to 24-bit DIBs). Because the palette is created directly from the color table, the indexing is a one-to-one mapping. Notice that using an index table still works on nonpalette devices because every index is dereferenced back to the original color that is found in the palette. See the "Using DIBs with Palettes" technical article and the DIBIT sample application on the Microsoft Developer Network CD for more detailed information.

DIBs stored in the 24-bits-per-pixel format do not usually come with a palette; to create a palette to enhance the image, the application may be granted a suggested palette (indicated by a nonzero biClrUsed field), use a generic rainbow palette, or analyze the colors actually used and build a palette to match. The process of converting a 24-bit DIB into a device-dependent bitmap on a palette device is a slow one because each color in the DIB is color-matched to the logical palette. This makes the 24-bit format not exactly ideal for palette devices. Yes, we are talking really slow.

GetDIBits on palette devices

Using the GetDIBits function on a palette device can result in loss of color information. When the destination DIB has a 1-bit format, the Palette Manager builds a color table with black and white as the colors and maps each color in the bitmap to these two colors. Similarly, it builds a color table made up of the 16 VGA colors for converting device-dependent bitmaps to 4-bit DIBs, and the colors in the bitmap are mapped down to those colors. There is no attempt at determining an optimal 16-color scheme, and if an application wants to perform that kind of work, it needs to build the 4-bit version of the DIB on its own (probably based on an 8-bit DIB). A 24-bit DIB is built by the device driver using the colors found in the accompanying palette; obviously no new color information can be generated.

More interesting is the 8-bit DIB case. The Palette Manager builds a color table composed of the 20 static colors (10 at either end of the range) and 236 colors based on the logical palette being used. The colors are chosen by looking at the palette's foreground mapping and working backward; the resulting table is identical to the system palette when the palette is realized in the foreground. Entries that are not used by the logical palette are set to black. Any colors in the bitmap that are not in the logical palette or among the static colors are mapped to black. Because of the introduction of the static colors into the color table, color information from the original source is most likely lost. For example, calling SetDIBits with an 8-bit DIB containing no static colors and then calling GetDIBits with the same bitmap and palette does not return the same DIB. In fact, the only way colors are not lost is if the logical palette contains fewer than 237 colors that differ from the static colors. Remember, however, that "excess" colors were already effectively lost when the DIB's palette was realized; any colors not set in the system palette were color-matched to those that were.

If the application chooses the DIB_PAL_COLORS option in the GetDIBits function, the Palette Manager matches the color table calculated above to the current logical palette to arrive at the indexes that are placed in the table.

Intricacies of the Realization Process

Here are some thoughts about a few peculiarities of the realization process that may or may not be useful information for an application writer.

The default palette is always forced to be realized in the background. More than that, the mapping is unchanging and is never recalculated. The translation table set up for blting purposes is NULL (that is, there is no translation).

When a palette is realized for the first time, its foreground mapping is calculated. So if the palette is being realized in the background for its first realization, two different realizations take place.

The next time the palette is realized in the foreground, no color-matching takes place, and the previously computed foreground mapping is used to realize the palette. To force a palette to re-realize, use the UnrealizeObject function.

The Palette Manager color-matches a logical color against the physical palette by starting at index 0 and scanning each entry, in order, to the end. This means that if a palette has colors that already exist in the system palette, the realization may not result in a "block" setting of colors. Colors will be mapped to existing entries instead of taking up unused spaces.

This also leads to an interesting loophole. If an application realizes a palette that contains one of the upper 10 static colors in such a way that the color does not get color-matched (that is, with PC_NOCOLLAPSE or PC_RESERVED), that color is placed in the system palette. The next time a palette containing a high static color is realized, that color is matched to the copy of the static color that is present. At this point, that color has lost all of the usual features of a static color: it doesn't invert consistently, it takes up room in the nonstatic zone, and its index entry does not match the real static color. One way to work around this is to use colors that have almost the same values as the upper static colors, that is, use RGB(0,0,0xFE) instead of RGB(0,0,0xFF) for blue. Visually, the difference is indistinguishable, but to the Palette Manager the two colors are different.

The Palette Manager tracks which of the entries in the system palette have ever been used. This is not the eternal ever of time but the ever of a Windows-based session—once the entry is set by an application, that entry is no longer special. Initially, only the static colors fit into this category. When a palette realization starts replacing entries in the system palette, entries that have never been used are set before entries that have been used in the past. This means that if the first foreground palette that is realized is small, the second foreground palette that is realized starts using up system palette entries at the end of the first palette's section as shown in Figure 3. The "never used" status of an entry goes away once the entry is used, whether it be by a different foreground palette or a background palette.

Figure 3. First and second realizations of the foreground palette

The result of this peculiarity is that a palette is not guaranteed to be set into the system palette starting at index 10 (first index after static color section). This feature of the Palette Manager allows two applications with small palettes to share the system palette without causing the system palette to change after the initial realization. One way for an application to ensure that its realization begins at the first index of the nonstatic section is to prefill the system palette. This can be done easily with a sufficiently large PC_NOCOLLAPSE palette of all black entries.

Writing a Device-Independent Palette Application

One of the goals of the Palette Manager is to allow applications to use palettes in a device-independent manner. As noted above, an application can still reference the colors in a logical palette on a nonpalette device; this capability is the key to device independence.

DIBs can be handled using palettes on any type of device. A palette created from the color table is used on a palette device to get the proper colors into the system palette and is essentially ignored on a nonpalette device. Using RGB colors in the DIB table (DIB_RGB_COLORS) is cleaner and a bit quicker on nonpalette devices than on palette devices, but prematching the colors and using indexes in the color table (DIB_PAL_COLORS) is faster than using RGB colors on palette devices. When indexes are used on a nonpalette device, the Palette Manager pulls out the indexed colors from the logical palette so that the original RGB values are actually used for the DIB conversion. A 24-bit DIB also works with a logical palette in a device-independent fashion: the palette is not used on nonpalette device drivers and is used by the Palette Manager for color-matching on palette devices.

Using colors outside of the DIB framework depends to a large extent on what the application wants to accomplish, although there are some general guidelines. If the application is outputting based on some working palette (for example, drawing into a bitmap that is already based on a palette), the PALETTEINDEX-type of color definition provides easy access to the palette. On nonpalette devices, the color in the entry being referenced is used.

When the application is working with a more generalized palette and is looking for the best color mapping on any type of device, the PALETTERGB color definition is more useful than the PALETTEINDEX color definition. On palette devices, the RGB color is matched to the nearest entry in the logical palette. On nonpalette devices, the RGB value is used as is. A good spectrum of colors can be achieved on palette devices, as well as on full-color devices, with a carefully chosen palette.

Here is an example of code for a device-independent color wash with blues:

hBluePal = CreateBluePalette();
if (hBluePal)
{
    SelectPalette(hDC, hBluePal, TRUE);
    RealizePalette(hDC);
    SelectObject(hDC, GetStockObject(NULL_PEN));

    for (blue = 255, i = 0; i < 64; blue -= 4, i++)
    {
        // Create a brush with desired shade of blue; use 
        // palette relative to get good color on palette devices.
        if (hBrush = CreateSolidBrush(PALETTERGB(0, 0, blue)))
        {
            hBrush = SelectObject(hDC, hBrush);
            // A color block in the wash.
            Rectangle(hDC, i*5, 0, i*5+4, 100);
            hBrush = SelectObject(hDC, hBrush);
            DeleteObject(hBrush);
        }
    }
    SelectPalette(hDC, GetStockObject(DEFAULT_PALETTE), TRUE);
    RealizePalette(hDC);
    DeleteObject(hBluePal);
}

The one problem with the code above is that the wash only consists of the 64 shades found in the palette, so the reliance on the palette is not completely removed. Representing an intermediate color is not trivial. If the color is requested using the above code (by increasing the iterations through the color loop) and PALETTERGB, it will be mapped to the nearest logical palette color, and the wash will have repeated strokes of the same color on a palette device. Using a PALETTERGB intermediate color works nicely for nonpalette devices because the intermediate color can be represented either as a pure color or as a dither.

With a 16-bit full-color device driver, colors will most likely repeat due to the resolution constraint of only 5 bits per color component, unless the driver has been written to support dithering. Similarly, palette devices with only 18 bits of color resolution per entry also lose color resolution. A possible remedy for loss of color resolution is for the application to perform its own dithering by using pattern brushes and the colors from the palette. Although this solution limits output to the logical palette's colors for all devices, the output can be improved. Of course, full device independence can be forsaken, and the application can create the dithers only on palette devices.

Palette Animation

The term palette animation refers to the process of directly changing a color in the hardware palette in order to change the color of a pixel on the screen. This technique creates the effect of motion or simply recolors an image. Using the Palette Manager for palette animation is a two-step process. First, the application creates an animating palette by marking animating entries with the PC_RESERVED flag. During realization, this entry does not match any existing colors in the system palette but instead only maps to an unused slot (when available). This system palette entry is then marked as an animating entry and is not available for mapping by any other color. Once the animating palette is created, selected, and realized, the application calls AnimatePalette to accomplish the animating itself.

The AnimatePalette function is very similar to the SetPaletteEntries function in that it specifies new settings for a subsection of a logical palette. The difference is that AnimatePalette sets new values only for those entries that are marked with the PC_RESERVED flag, and if such an entry is mapped to an animating entry in the system palette, the entry in the system palette is set with the new color as well. The Palette Manager sends the changed entries to the device driver one entry at a time, not as a block. The driver, in turn, sets the new colors in the hardware palette. No window messaging takes place because any system palette entry that is changing is off limits to all other applications. Any pixels on the screen that are mapped to the changed entries now are displayed with the new color. Depending on how the device driver synchronizes the setting of the hardware palette with its screen refresh, some video snow may appear during the process.

One thing to remember about animating palettes is that once the system palette has no unused entries, subsequent colors marked with the PC_RESERVED flag map to index 0 (black) and are not animated. As a result, an application realizing an animating palette in the background may end up with all the colors mapping to black, and this makes for less than optimal repainting. One possible mechanism for handling this is to create a parallel palette without the PC_RESERVED flag for painting in the background. This, of course, requires that the application refrains from animating when in the background.

On nonpalette devices, calling AnimatePalette accomplishes nothing more than setting new colors in the logical palette. This palette function becomes really useful only on a palette device.

SetSystemPaletteUse

The SetSystemPaletteUse function allows an application to free up the entries used by the static colors for general realization purposes. Black and white remain static at index 0 and index 255, respectively. Although this may sound like a great thing, the usefulness of this process is restricted in many ways, not the least of which are the parts of the system and the Palette Manager that do not properly handle the lack of static colors. The term system colors used below refers to the colors used by Windows to draw window components; the colors are accessible via the GetSysColor and SetSysColors functions.

To properly use the SYSPAL_NOSTATIC option, an application should follow these guidelines:

When the SYSPAL_NOSTATIC option is set, all that happens internally is that the Palette Manager updates a few data structures to reflect the fact that all but two of the static colors are no longer static. The colors themselves remain in place until a realization needs those entries.

After normal system palette use is established when the application calls SetSystemPaletteUse with the SYSPAL_STATIC option, the application should do the following to restore the system:

  1. Restore the original system colors.

  2. Send the WM_SYSCOLORCHANGE message to inform the rest of the system of this change.

  3. Any palette realized with the SYSPAL_NOSTATIC setting must be unrealized using UnrealizeObject before its next realization.

The system palette should be restored to its normal state as soon as the application is no longer the active application. When the system palette is restored to normal behavior using the SYSPAL_STATIC option, the Palette Manager simply restores the static colors and informs the device driver of this change.

Of course, if the application can ensure that no drawing is done with the system colors (for example, a maximized window with no title bar or menus), there is no need for saving and restoring the system color information.

The basic problem with using the SetSystemPaletteUse function is that the Palette Manager does not remap the default palette to use only black and white, and the device driver is not made aware of the lack of the static colors. As a result, any operation that involves using the static colors (for example, using RGB colors and drawing window borders) maps to the system palette entries that used to contain the static colors. The results on the screen are unpredictable. Why isn't the support better? Mostly because the SetSystemPaletteUse function is a stopgap measure to deal with the problem of insufficient palette access. The Palette Manager is designed for managing the shared use of the system palette, and once the static colors are gone, palette sharing is no longer a viable possibility; ensuring that all other applications are still color-matched in an acceptable manner is not easily possible. If you want your application to use all 256 colors, you should follow the rules above regarding the SYSPAL_NOSTATIC option so that the application does not interfere with the operation of other applications running in the system.

The GetSystemPaletteUse function determines the current state of the system palette.

Other Functions

The Palette Manager provides a few other support functions for manipulating logical palettes and getting information about the system palette. These functions are relatively simple and straightforward.

SetPaletteEntries and GetPaletteEntries

The SetPaletteEntries function lets an application alter the color information of entries in a logical palette. To become effective, the newly changed palette needs to be re-realized. Because the palette has changed, the Palette Manager treats it as a completely new palette, and the foreground mapping is built up from scratch. Any bitmaps that were attached to the original version of the palette are no longer necessarily valid.

The GetPaletteEntries function gets the contents of a logical palette. To extract all the information about the palette, use the GetPaletteEntries function in conjunction with the GetObject function, which returns the number of entries in the palette.

ResizePalette

The ResizePalette function allows for the resizing of a logical palette. Similar to SetPaletteEntries, the ResizePalette function results in a palette that must be re-realized by the application to become useful and is treated by the Palette Manager as a new palette that needs a new foreground mapping.

UnrealizeObject

When a logical palette is explicitly unrealized using the UnrealizeObject function, the Palette Manager throws out any previous realization information that was maintained for the palette. The next time the palette is realized by an application, the Palette Manager has to realize it from scratch, rebuilding the foreground mapping in the process. This function is useful in cases where you expect the palette to have a different realization, possibly matching better to the current system palette.

GetNearestPaletteIndex

The GetNearestPaletteIndex function matches a given color to a logical palette. The function returns the index of the logical palette entry whose color is chosen by the Palette Manager to be the closest. This same functionality takes place inside the Palette Manager when dealing with a PALETTERGB color on a palette device.

GetSystemPaletteEntries

As the name implies, the GetSystemPaletteEntries function returns information about the color values in the system palette. Only the color values are returned; internal information such as which entries are used by the foreground palette and which entries are PC_RESERVED is not accessible.

Clipboard Use with Bitmaps

The Clipboard data format, CF_PALETTE, is used for passing logical palettes. Its main purpose is to permit an application to properly paste a CF_BITMAP object on a palette device. Unless the bitmap is built entirely of static colors (which could be the case with applications that are not palette-aware), the palette is necessary for displaying the bitmap. Creating a new palette with the same colors does not necessarily guarantee that the bitmap color information remains valid. A palette-using application placing a palette-based bitmap in the Clipboard must also include the defining palette.

If the source application does not provide a palette with the bitmap, the application is indicating that the default palette should be used to display the bitmap. This is the case with applications that do not use palettes.

The best way to place an image in the Clipboard is to use a DIB. A device-independent version of the bitmap can be used by palette-savvy applications, as well as those that lack palette expertise. The DIB method further promotes device independence by being useful on nonpalette devices.

Another way to place a bitmap in the Clipboard is to use a metafile that contains palette-based records. The following sequence of records does the job nicely:

  1. Create a matching palette.

  2. Select the palette.

  3. Realize the palette.

  4. Use StretchDIBits on a DIB version of the bitmap with equal source and destination extents. (Or use BitBlt on a device-dependent bitmap, which is recorded using a DIB.)

Applications not aware of palettes can play this metafile and display a palette-based bitmap without even knowing it. Meanwhile, palette-using applications probably benefit more from using the CF_DIB format because the relevant color information is more readily accessible.

The Trouble with Metafiles

Placing palettes into metafiles is tricky business. First, the metafile needs to be as undamaging as possible to the visuals of the application performing the playback. Second, once palette information is placed in a metafile, it is very difficult for an application to extract that information.

To illustrate the first issue, imagine an application that is about to play two metafiles to the screen. If both metafiles contain a SelectPalette/RealizePalette pair that results in a foreground palette realization, the second metafile to be played could blow away the first one's system palette settings and thereby destroy the image. Such carnivorous behavior could be avoided by recording the metafile's palette selection so that the palette is always forced to be a background palette. In this way, when the metafile is played back, it always has a lower palette priority and does not degrade any palette-based image already displayed by the application. Unfortunately, this is not possible with Windows versions 3.0 and 3.1 due to faulty implementation that loses the bForceBackground parameter information during recording. When played, the SelectPalette call always has the parameter set to FALSE. The way to work around this problem during playback is for the application to enumerate the metafile's records, trap META_SELECTPALETTE records, and play them manually with bForceBackground set to TRUE:

// In application's EnumFunc for metafile enumeration...

case META_SELECTPALETTE:
    SelectPalette(hDC, lpHTable->objectHandle[(lpMFR->rdParm[0])], 
        TRUE);
    break;

The second limitation is not a concern for most applications, but those applications that want to optimize palette use or to merge palette information need to know what colors are being used inside a metafile. Currently, the only way to do this is to use the EnumMetaFile function to look at each record and extract the needed color information. Color information is found in records that create and alter palettes and in DIB records that have RGB color tables.

Palette-Insensitive Applications

Applications that use palettes explicitly can have parts that do not have any knowledge of the Palette Manager. Palette insensitivity exists when no application-created palette is used for display purposes and only the implicitly selected default palette is in use.

Applications that are not palette-aware continue to work on a palette device by relying on the static color scheme. These applications never request colors that are based on a palette; only RGB values are used. As mentioned above, the device driver provides VGA-level support for RGB colors by using the static colors. Applications do, however, lose the ability to use all of the available ROPs in a consistent manner on palette devices, although XOR still works. Using ROPs, it is possible for an application that is not palette-aware to end up with pixel values that do not reference a static color. This type of behavior, hopefully, is rare.

A palette-insensitive application does not need to worry about maintaining a palette in conjunction with color bitmaps because the default palette provides all of the needed color information. Color bitmaps created by this application are based on the default palette and can represent all of the static colors, which cover the entire color range accessible by the application. Because the default palette has a fixed mapping to the system palette, no bitmap translation is needed for blting.

It is possible for a palette-insensitive application to use a palette without knowing it by playing a metafile that contains palette-based information. The application is able to simply play the metafile and display a palette-based image. The problem that the application now faces is that it has no support for dealing with system palette changes, so the image may not be ideally displayed at all times. The Palette Manager has no support for handling this sort of situation; the application would have to be made palette-aware to permit consistent display of palette-based metafiles.

Pasting a bitmap from the Clipboard is another potential pitfall. Because memory bitmaps on palette devices are based on a logical palette (with the exception of those made entirely of static colors), the palette that accompanies the bitmap in the Clipboard is needed for proper display. The bitmap alone is useless. Looking for an alternative Clipboard format, such as a DIB or a metafile with a built-in palette, is probably the best solution. Palette applications should be aware of this possible scenario and provide an alternative format whenever possible.

MYPAL: Tool for Tracking the System Palette

The MYPAL application in the Microsoft Windows version 3.1 (and version 3.0) Software Development Kit (SDK) is a useful tool for tracking the system palette. This application uses PC_EXPLICIT entries in its logical palette to mirror the contents of the system palette. With MYPAL visible, it is possible to watch the system palette as it changes. Note that the application does not repaint with the new colors; the rectangles displayed are mapped to the corresponding entries in the hardware palette and simply reflect its current status.

Another feature of the MYPAL application is its ability to identify the hardware mapping of pixels on the screen. When you press the right mouse button in MYPAL, the application identifies the current mapping of the pixel pointed to by the cursor. By dragging the cursor to the pixel in question, it is possible to identify the color of an individual pixel. Using the ZOOMIN application, also found in the SDK, helps you get accurate pixel identification.

The mechanism for determining the hardware mapping of a single index on the screen is rather unclean. When a pixel is chosen for identification, GetPixel is used to get its color value. To arrive at a hardware mapping, this color is then compared to each entry in the current system palette. When a match is found, the proper entry is identified. This algorithm depends on nonrepetition of colors in the system palette, which is not necessarily guaranteed (repeats can be set using PC_RESERVED and PC_NOCOLLAPSE entries in a logical palette). Also, the MYPAL application in the Windows version 3.0 SDK has another problem in that its internal tracking of the system palette is only updated in response to the WM_PAINT message; this is not sufficient because a change in the system palette does not cause the application to repaint. A better approach, found in MYPAL in the Windows version 3.1 SDK, is to update that information on a need-to-know basis, when the right mouse button is first pressed. Neither version competently deals with animating entries.

What the Device Driver Sees

All of the actual palette managing is at the GDI level. A device driver that provides palette support is simply told how to set the hardware palette and how to interpret indexes in bitmaps. For all palette-based operations, the driver does not even deal with actual colors, only indexes.

Setting the hardware palette is fairly easy. If any change is necessitated either during realization or as a result of palette animation, the Palette Manager passes the new palette information to the driver. The driver then sets the new values into the hardware color lookup table. Poof! The colors are changed.

With every new palette realization, the Palette Manager also provides the device driver with a corresponding bitmap translation table. This table, one entry for every possible index, defines how memory bitmaps are to be interpreted. The driver uses this table every time a memory-to-screen blt takes place; every pixel value in the memory bitmap is translated by this table to the corresponding screen pixel value. The table is built by the Palette Manager as a mapping between the logical palette's foreground indexes and its current realization indexes. In cases where the translation table is an identity table, there is no need for translation, and GDI sends the driver NULL instead of an actual table. The driver maintains a current translation table at all times, but it is not used for screen-to-screen or memory-to-memory blts.

The device driver builds an inverse of the translation table for performing screen-to-memory blts. The inverse table is by no means guaranteed to be one-to-one when the palette is in the background, so this type of blt can very easily lose color information. To ensure a valid screen-to-memory blt, the application must be the foreground application and use the foreground palette.

When GDI passes a color to the driver (for object realization, drawing modes, and so forth), the color can be either an RGB color or an index. If the application passed in a pure RGB value, it is passed as is to the driver, and the driver proceeds to simulate VGA behavior with the color by using the static colors in the system palette. The VGA simulation means that the color is matched to the 16 VGA colors and dithered using those colors when a solid brush is being realized. The Palette Manager converts colors that are based on a logical palette into indexes and passes these indexes to the driver. If the DC involved in the operation is a screen DC, the index is the current mapping of that color; if the DC is a memory DC, the index is the foreground mapping of the color.

DIB operations require the Palette Manager to massage the input so that the driver can interpret the colors correctly. A palette driver never actually sees any colors in conjunction with a DIB; the communication for both getting and setting is accomplished using indexes. For converting DIBs into a device-dependent bitmap (via SetDIBits or StretchDIBits), the Palette Manager maps each entry in the DIB's color table to the current palette and then uses the palette's corresponding foreground indexes to build a new index table that is passed to the driver. The driver sees only the foreground indexes. When the DIB is to be set directly to the device (via SetDIBitsToDevice or StretchDIBits), the Palette Manager builds the driver's table using the palette's current mapping instead of the foreground mapping. Palette-based device drivers do not touch the color table when performing a GetDIBits operation; the Palette Manager itself fills in the color information and provides the driver with a translation table (not necessarily one-to-one) specifying the mapping from memory bitmap pixel values to DIB index values.

For 24-bit DIBs, the translation process gets a bit uglier. Because the DIB data consists of only 24-bit RGB values, each one must be mapped to the current palette individually during a "set" operation. The driver calls a Palette Manager function for each color to get this mapping. Yes, this is slow. Many drivers implement a color caching scheme of some sort to minimize calls back to the Palette Manager. Handling a GetDIBits operation with a 24-bit DIB as the destination is easier than the "set" operation because the source bitmap has a limited number of colors. The Palette Manager provides the driver with a lookup table of RGB values, each corresponding to an index in the source bitmap.

Informational Limitations

The Palette Manager buffers applications from the actual details of the system palette. Palette realization from an application's perspective is just a black box that shields the system palette from direct outside interference. As a result, some applications do not have as much control over the realization process as they would like.

An application cannot choose the system palette index to which a given color maps. (The PC_EXPLICIT option lets you attach an entry to a system palette entry, but it does not actually change the system palette.) Controlling exactly how a palette maps to the system palette is useful for an application that wants to use raster operations in a meaningful fashion. The PC_NOCOLLAPSE flag gives an application some control. By knowing how many static colors are in the system palette and how the Palette Manager realizes PC_NOCOLLAPSE palettes (a less polite method is to use a PC_RESERVED palette), an application can get its colors spread, in order, in the nonstatic entries of the system palette. Remember that flushing the system palette is a precaution worth taking to guarantee that the first entry used is at index 10, assuming a standard system palette. Although the application can't specifically dictate the placing of a single color, it can control the ordering and placement of an entire logical palette. Of course, this mechanism only works when the logical palette has first choice in an empty system palette (to be precise, the application needs to have the foreground palette).

Conversely, once the Palette Manager has realized a palette, it is not trivial for an application to determine exactly where the Palette Manager has mapped each logical entry. One way to make this determination is to use the GetSystemPaletteEntries function to get a copy of the system palette and then search for each logical color to see where it is in the system palette. This works for colors that are actually set in the system palette, but it does not accomplish the task if the color is color-matched to the system palette. A workable, but not perfect, solution is for the application to create a logical palette from the system palette entries and use the GetNearestPaletteIndex function to get a mapping. The limitation that remains is that colors in the system palette may be reserved (and not used for color-matching) or have multiple copies (introduced by an application using PC_RESERVED or PC_NOCOLLAPSE).

Static Color Gamma Correction

A small wrench is thrown into the workings of the Palette Manager by the digital-to-analog converters (DACs) that most devices have. While the DACs may generate a linear relationship between the value of a color and the visual appearance of that color, the human eye does not work in the same manner. Without some sort of correction, the color RGB(128,0,0) does not appear to the eye to be half the intensity of the color RGB(255,0,0); in fact, it looks much darker. The process of manipulating the meanings of colors to get visual linearity is called gamma correction. An RGB color value is passed to the device driver, and the driver converts the value (usually by means of a lookup table for each component) into a corrected RGB value that is then passed to the hardware. The result is that, for example, the color RGB(128,0,0) is actually displayed using the hardware value RGB(192,0,0).

To perform truly effective gamma correction, the hardware setup needs to be calibrated and setup-specific numbers must be generated. This level of color calibration is also needed for device-independent color, which is in the works for future products. Computing a generalized correction per device driver is another possibility, but at the time the Palette Manager was designed, this approach seemed a cop-out and was not implemented. Another consideration is that gamma correction inevitably causes loss of actual color resolution because the color mapping is clearly not one-to-one. Some of the dim hardware colors are not accessible, and multiple bright colors may map to the same hardware color.

The solution used by Windows versions 3.0 and 3.1 is not entirely elegant. The driver uses values of 191 instead of 128 in the hardware palette for the low-intensity static colors. This tactic achieves a brightness comparable to a standard VGA. When the driver performs VGA simulations for RGB color-matching, it treats the low-intensity colors as though they have values of 128, so that, for example, a solid brush with the color RGB(128,0,0) maps to a solid low-intensity red and is not dithered. The Palette Manager is not aware of the gamma correction and maintains the system palette with the 191-based values for palette matching; the default palette is mapped as a special case so that its 128-based low-intensity colors map to the proper static entries in the system palette.

The results for applications are mostly innocuous, but they do change some expectations. The color table for 8-bit DIBs returned by the GetDIBits function has the low-intensity static colors specified with 191-based values. These values have the very convenient property that when mapped down to VGA-resolution colors (a choice of 0, 128, or 255), the chosen mapping is 128, so the low-intensity colors are mapped correctly on VGA-type devices. A palette created from this color table has the 191-based values in it, and when it is realized, the colors map to the static colors because palette realization is based on the real values in the hardware palette.

Confusing? Well, it gets a bit worse. The one loophole in the scheme becomes apparent when a palette is created with a 128-based color such as a DIB created on a VGA. The palette color, when realized, is matched to a true 128-based color in the hardware, getting its own entry in the system palette when appropriate. When used for drawing, the color is quite a bit darker than on a VGA. Things are even uglier if the application decides to also do some drawing that is not based on the palette by simply using an RGB color, because the device driver maps that color to the matching static color. When this color is used for drawing, the resulting color is much brighter than the palette-based version. This type of visual inconsistency can be avoided by not mixing palette-based and RGB-based drawing in the same application.

One less-than-elegant workaround to the problem of DIBs stored with VGA colors is to make a special case for the color table. If the color table has the exact logical colors found on the VGA driver (specifically, colors with low intensity are based on 128 and colors with high intensity are based on 255), then the application can use the default palette (GetStockObject(DEFAULT_PALETTE)) for displaying the DIB instead of creating a palette based on the color table. The default palette's special mapping ensures that it is mapped to the proper indexes in the system palette and that its low-intensity colors are not dim. Note that making a copy of the default palette is not the same as using the actual default palette stock object because the copy does not maintain the stock object's special-case mapping.

Concluding Thoughts

As a conclusion to this rather lengthy discussion, here is a quick summary of the important concepts of the Palette Manager: