How Windows 3.11 Managed 256-Color Palettes

In the era of Windows 3.11, 256-color (8-bit) video adapters relied on indexed hardware palettes rather than true color values per pixel. To prevent competing applications from corrupting each other’s visual output, Windows used its Graphics Device Interface (GDI) to mediate between hardware constraints and application needs. This was achieved by maintaining a single master system palette, allowing applications to request custom colors via logical palettes, and arbitrating display priorities between foreground and background windows through dedicated window messages.

The Hardware Constraint: Indexed Color

An 8-bit display adapter stores pixel data not as red, green, and blue values, but as index pointers (from 0 to 255) referencing a hardware Look-Up Table (LUT) in the video Digital-to-Analog Converter (DAC). While each of the 256 entries in the DAC could be set to any color from a 24-bit or 18-bit master range, the screen could only show 256 distinct shades at any single moment across all running applications.

System Palette vs. Logical Palettes

To maintain visual stability across the operating system, Windows 3.11 divided palette responsibility into two layers:

  • The System Palette: This was the physical representation of the video card's 256-entry DAC. Windows reserved 20 fixed "static colors" (10 at indices 0–9, and 10 at indices 246–255) for core user interface elements like window borders, title bars, button faces, and system text. These reserved entries guaranteed that Windows controls remained legible regardless of what an application did. The remaining 236 slots (indices 10 to 245) were open for applications to allocate dynamically.
  • Logical Palettes: Applications defined their own color requirements using a LOGPALETTE structure. An application did not directly write to the hardware registers; instead, it asked GDI to match its logical palette to the system palette.

Realization and Foreground Priority

When an application wanted to draw using custom colors, it performed a two-step process: selecting its logical palette into a Device Context via SelectPalette(), and then calling RealizePalette().

During palette realization, GDI compared the requested colors to the current system palette:

  1. Direct Mapping: If an empty slot existed in the 236 dynamic entries, GDI programmed the hardware DAC with the requested RGB value.
  2. Nearest Matching: If all 236 dynamic slots were occupied, GDI mapped the application's logical color to the closest existing entry in the system palette using an internal Euclidean color-distance algorithm.

Because multiple applications could not simultaneously claim all 236 dynamic slots with conflicting colors, Windows prioritized the active application.

Palette Arbitration Messages

Windows 3.11 managed shared palette access dynamically using two cooperative messages:

  • WM_QUERYNEWPALETTE: Sent to a top-level window when it gained focus. This gave the foreground application the first right to realize its colors. Because it was evaluated first, the active window usually obtained exact matches for all or most of its required colors.
  • WM_PALETTECHANGED: Broadcast to all top-level windows whenever the active application modified the system palette. Upon receiving this message, background windows were forced to re-realize their palettes and repaint their client areas. Since the foreground window had already claimed the available hardware slots, background windows had to accept nearest-match compromises.

This mechanism often produced a visible artifact known as "palette thrashing" or "palette flashes," where switching focus between two color-heavy applications (such as graphics viewers or multimedia titles) caused the background application to abruptly distort into incorrect, often garish colors.

Identity Palettes for Fast Bitmaps

For performance-critical operations, such as drawing Device-Independent Bitmaps (DIBs), the overhead of GDI translating logical indices to system indices per pixel was prohibitively slow. Developers used a technique called an "identity palette." By querying the 20 reserved system colors and placing them in the exact same index positions within the application's logical palette—along with matching the remaining 236 colors sequentially—the logical palette matched the system palette 1:1. This allowed GDI to use high-speed memory copies (BitBlt) directly to video memory without performing index-translation lookups.