A gamma-corrected uniform palette (for Microsoft Windows)
This paper proposes a general purpose, uniform, 256-colour palette to use in applications for Microsoft Windows. Several "common" or uniform palettes (for Windows applications) have already been proposed, notably the "Web Safe palette". The palette presented here improves on these earlier proposals on the following points:
- The palette is based on the 6×6×6 RGB colour cube, but unlike most alternatives, each of the primary colour axes of the cube has been gamma-corrected before subdivision.
- The palette includes the Microsoft Windows "static colours" at the appropriate locations.
- The palette includes a (gamma-corrected) gray scale ramp of 28 gray levels. The ramp excludes black and white (which are already in the 6×6×6 colour cube). The gray levels in this ramp overlap neither with the two additional gray levels among the "static colours", nor with the six grays in the 6×6×6 colour cube. The total number of grays (including black and white) is therefore 38.
Gray levels are important to better represent drawings and pencil sketches. Gray levels are also used to reduce the saturation of a colour by making a (dither) pattern of the colour and a gray level of the same brightness. (Dithering is not demonstrated at this page.)
There are two gamma values used in the uniform palette. Theoretically, the most correct value for gamma is 2.5. However, taking the bad adjustment of a typical computer monitor into account (most users set their monitor too bright, resulting in a serious black-level error), a gamma-correction factor of 2.0 is more appropriate. This is the value used for the gray scale ramp.
Initially, I also used a gamma of 2.0 for the 6×6×6 colour cube. When evaluating the first version of the proposed uniform palette, it turned out that it had too few saturated colours. Given the course subdivision of the colour cube, it turned out that it is better to optimize (or exaggerate) chromatic differences, at the cost of being less accurate in reproducing brightness. The new proposal uses a gamma factor of 1.5 for the colour cube.
Implementation
A code snippet in C that populates a LOGPAL structure with the colours for the uniform palette appears below. The function finishes by creating a HPALETTE (palette handle) from the palette.
HPALETTE MakeUniformPalette(void) { /* the Windows' "static" colours */ PALETTEENTRY sys_palette[20] = { { 0, 0, 0, 0}, {0x80, 0, 0, 0}, /* top 10 colours */ { 0,0x80, 0, 0}, {0x80,0x80, 0, 0}, { 0, 0,0x80, 0}, {0x80, 0,0x80, 0}, { 0,0x80,0x80, 0}, {0xc0,0xc0,0xc0, 0}, { 192, 220, 192, 0}, { 166, 202, 240, 0}, { 255, 251, 240, 0}, { 160, 160, 164, 0}, /* bottom 10 colours */ {0x80,0x80,0x80, 0}, {0xff, 0, 0, 0}, { 0,0xff, 0, 0}, {0xff,0xff, 0, 0}, { 0, 0,0xff, 0}, {0xff, 0,0xff, 0}, { 0,0xff,0xff, 0}, {0xff,0xff,0xff, 0} }; /* gamma-corrected values for Red/Green/Blue in the colour cube */ BYTE colorlevel[6] = { 0, 87, 138, 181, 220, 255 }; /* values for the gray scale ramp */ BYTE graylevel[28] = { 47, 67, 82, 95, 106, 116, 125, 134, 142, 150, 157, 164, 171, 177, 183, 189, 195, 201, 206, 212, 217, 222, 227, 232, 237, 241, 246, 251 }; struct { WORD palVersion; WORD palNumEntries; PALETTEENTRY palEntry[256]; } logpal = { 0x300, 256 }; int i; int r, g, b, entry; /* Basically, we construct a 6x6x6 colour cube (216 colours) and * add the Windows' static colours and a gray scale ramp. Eight of * the 20 static colours overlap with the 6x6x6 colour cube, so the * gray scale must fit 256 - (216+20-8) = 28 entries, without * overlapping any of the static colours or the colour cube. */ /* the static colours */ memcpy(logpal.palEntry, sys_palette, 10*sizeof(PALETTEENTRY)); memcpy(logpal.palEntry+246, sys_palette+10, 10*sizeof(PALETTEENTRY)); /* the 6x6x6 colour cube (but skip the 8 corners of the cube) */ for (r = 0, entry = 10; r < 6; r++) for (g = 0; g < 6; g++) for (b = 0; b < 6; b++) if (r != 0 && r != 5 || g != 0 && g != 5 || b != 0 && b != 5) { logpal.palEntry[entry].peRed = colorlevel[r]; logpal.palEntry[entry].peGreen = colorlevel[g]; logpal.palEntry[entry].peBlue = colorlevel[b]; entry++; } /* if */ ASSERT(entry == 10+216-8); /* 10 static colours, 216 iterations, * 8 colours skipped */ /* the gray scale */ for (g = 0; g < 28; g++, entry++) { logpal.palEntry[entry].peRed = graylevel[g]; logpal.palEntry[entry].peGreen = graylevel[g]; logpal.palEntry[entry].peBlue = graylevel[g]; } /* for */ ASSERT(entry == 246); /* should now have filled up to second * section of static colours */ /* set the "no collapse" flag to create an identity palette */ for (i = 10; i < 246; i++) logpal.palEntry[i].peFlags = (BYTE)PC_NOCOLLAPSE; return CreatePalette((LOGPALETTE *)&logpal); }
Some notes on the code presented above:
- The reference for the various Windows structures and function is, of course, the Microsoft Win32 SDK documentation.
- See the article "The Microsoft Windows Palette Manager" for details on the "identity palettes" that the code snippet refers to.
- The static colours are also covered in detail in the article "The Microsoft Windows Palette Manager".
Examples
Below is the famous Lena picture in RGB format and mapped to the uniform palette as created by the MakeUniformPalette() function presented above. To compare it to alternatives, the Lena picture is also mapped to the "Web Safe" palette and to an optimized palette. The RGB picture is in JPEG format, the 256 colour pictures are in CompuServe GIF format. None of the pictures has been dithered.
Original picture (RGB) |
Gamma-corrected uniform palette |
Web Safe palette |
Optimized palette |
Note that you should be running in 24-bit RGB mode to properly view the differences in palette mappings. If you are running in 256 colour mode, the browser maps all palettes to a common palette, resulting in double loss.
The improvement of the proposed uniform palette over the "Web Safe" palette is subtle —or even disputable. In my opinion, the proposed uniform palette contains more subtle tints, especially in the lighter areas, and more pastel (low saturation) tints. While the (chromatic) contrast may be less, the source picture is reproduced more accurately (again, in my opinion). When using error diffusion dithering, less contrast between the palette entries makes the dither appear less coarse.
The easiest way to get the uniform palette proposed in this paper in your graphic editor is to save the appropriate picture to disk and to load it in your paint application of choice. Then, you can inspect the palette and, mostly, save it to disk in the native palette file format of the paint application.
Other considerations
Using uniform (256-colour) palette is a last resort: if at all possible, use an optimized palette. Note how the optimized palette results in a significantly better appearance of Lena. Even slightly optimized palettes are markedly better than no optimization at all.
Linear uniform palettes (as opposed to a gamma corrected uniform palette) are trivially dithered with a (dispersed dot) ordered dither and matrices for such ordered dithers have been well covered in literature. Gamma corrected uniform palettes require matrices that take the non-linearity of the axes' subdivision into account. Optimized palettes require quite different dithering methods, such as the (Floyd-Steinberg) error diffusion dithers and the Riemersma dither. These dithers are more complex than ordered dithers and, hence, do not lend themselves for real-time dithering.