A read-only colormap is intialized with immutable color values when
the colormap is created. StaticGray, StaticColor and TrueColor
visuals all have read-only colormaps. On the plus side, XAllocColor()
(or XAllocNamedColor()
) always succeeds returning a pixel which will be the closest match the server finds in the colormap.
Additionaly, colorcells are implicitely shared -- no client can change any
of the colorcell values, so all clients can use all colors in a colormap. On
the down side, since the colormap entries cannot be changed, visuals with a
small number of planes have less of a chance of returning a pixel
whose value exactly matches a client's requested color.
Since any call to XAllocColor()
returns a valid pixel, programs
should be able to run under any of these 3 visuals without much effort.
The problem remaining is limiting calls to XAllocColor()
for each color change, each call which requires a roundtrip to the server
needlessly slowing down your application.
For GrayScale
, PseudoColor
and DirectColor
visuals (with read-write colormaps) colors can be allocated read-only
or read-write. Allocation continues until all the available
entries in the colormap are used up, after which, any new
attempts will fail. Read-only allocation with XAllocColor()
allows color entries to be shared among clients
requesting the exact same color.
For clients to share a readonly color, the red, green and blue
components must be the same. If your colormap has run out of free
entries, XAllocColor()
will not return successfully
if there does not already exist an entry with the same color
you are requesting. For read-write colormaps, any entries
allocated (using XAllocColorCells()
or XAllocColorPlanes()
)
are exclusively for the use of the requesting client, and that
pixel will not be returned to another client.
The main problem with read-write colormaps is that they can become filled quickly when multiple clients are running since each client has its own idea of what its best colors are. Once this happens your client must deal with color allocation failures. Some applications avoid this problem by creating a private colormap allowing the program to use all the entries without the worry that other clients may have allocated some unknown number of color cells.
Remember, when a program uses a private colormap, it is up to the window manager to install that colormap, if necessary, when the application gets the input focus. (Some high end servers allow multiple colormaps to be installed at once, though most midrange and lowend servers only can have one colormap in hardware at a time.) When this happens you will experience the "techno-flash" effect. Applications will show false colors when their colormap becomes uninstalled and a different colormap is swapped in.
To help alleviate this problem X has "standard colormaps".
Some client allocates a contiguous range of entries in the colormap
and stores a ramp of colors representing an RGB cube into these entries.
The client uses XSetCloseDownMode()
with RetainPermanent
so when the client exits these cells are still marked as allocated
by the server, leaving the colors permanently stored.
A property is then stored on the root window describing the
RGB cube. A standard colormap is defined by the structure:
typedef struct { Colormap colormap; unsigned long red_max; unsigned long red_mult; unsigned long green_max; unsigned long green_mult; unsigned long blue_max; unsigned long blue_mult; unsigned long base_pixel; VisualID visualid; XID killid; } XStandardColormap;
colormap: colormap (ID) used by the standard colormap (it does not have to be the default colormap.) red_max, green_max, blue_max: maximum value for red/green/blue red_mult, green_mult, blue_mult: multipler for red/green/blue values base_pixel: base value to add to resulting calculated pixel visualid: ID of the visual used to create the colormap killid: ID used to free the allocated cells in colormap. (May be an ID to use with XKillClient() or ReleaseByFreeingColormap indicating XFreeColormap() should be called on the colormap ID)
Instead of calling a color allocation function, the property
is retrieved into an XStandardColormap
structure.
All pixel calculations are done using the XStandardColormap
structure directly. For example, to calculate a pixel value from an RGB
triplet (each component in the range 0-65535):
pixel = stdCmap.base_pixel + (((wantRed * stdCmap.red_max)/65535) * stdCmap.red_mult) + (((wantGreen * stdCmap.green_max)/65535) * stdCmap.green_mult) + (((wantBlue * stdCmap.blue_max)/65535) * stdCmap.blue_mult);
This does not always return a pixel with the exact wantRed, wantGreen
and wantBlue
values, but instead gives the closest color within our RGB cube. Now, like with read-only colormaps, given an RGB triplet, we can always get back a valid pixel. As the size of our cube grows, so
does the accuracy of our "closest" pixel. Plus, since standard colormaps
are described in a property on the root window, multiple clients can share
the same read-write color cells without doing any explicit allocation and
no server trips need to be made.
Standard colormaps are a good thing. A common setup for midrange hardware has an X server running with a default eight bit PseudoColor visual. Most programs allocate colors, and with each allocation we lose an entry in our colormap. With only 256 entries available this quickly becomes a problem if you have more than one color intensive application running. For resource specifications this is not bad due to the color name database "rgb.txt". "rgb.txt" defines a mapping from names to rgb color triplets of the form:
255 248 220 cornsilk
This way client colors can be specified by "cornsilk" instead of an RGB triplet, and any other client requesting "cornsilk" will be given a pixel to the same color entry (sharing).
However, once a program needs to mathematically allocate colors "rgb.txt" no longer is of any use. For these midrange servers, standard colormaps are a great solution. Allocating a range of entries for the standard colormap, the user has control over how large the cube should be, and can even leave entries for other programs that are oblivious to standard colormaps.
StaticGray
contains grayscale values ranging from black to white.
Unfortunately, no order, such as black in pixel 0 and white in the last
colormap entry, is defined to exist.
GrayScale
also contains grayscale values, but these values must
first be allocated by clients. Since GrayScale has a read-write colormap,
there is the possibility of allocation failure. StaticColor
is initialized with some set of colors, though what colors and what
entries they are in is undefined.
PseudoColor
has a read-write colormap that clients can fill by their color
requests. PseudoColor
has the possibility of allocation failure.
TrueColor
has well defined colormap entries according to the bits defined
by its visual. The client must manipulate these bits to obtain the
pixel with the color they require.
DirectColor
is like TrueColor
, but with an initially undefined colormap.
Clients allocate colors as needed, using the bits specified by the visual to calculate
pixel values. DirectColor
has the possibility of allocation failure, and
is generally the most difficult of all the visuals to correctly use.
Our color management problem only seems to be getting worse, but there is hope!
First, if a standard colormap property exists, we would like to use it.
If the visualid
of the standard colormap matches the id of our
client's visual, we can use the previous calculation to get our pixel value,
and all is well.
StaticGray
and GrayScale
colormaps, except for one being uninitialzed, could be
treated the same. If we could somehow order the entries in both types of
colormap, we could mathematically calculate pixel values. This is possible
if a client side lookup table is used. Since the values are grayscale
we can allocate a ramp of grays from white to black, then for any RGB triplet
we can calculate the intensity, scale it to our table, and get a pixel.
StaticColor
and PseudoColor
colormaps could be the same except PseudoColor
colormaps
are unitialized. We cannot just ramp colors int the same way we could
with grays. Instead, consider the standard colormap structure. If colors
are allocated to fit a standard colormap specification we can then use
our standard colormap calculation to get pixel values.
DirectColor
and TrueColor
. TrueColor
requires shifting the bits of an RGB triplet appropriately and
bitwise OR'ing them together, the result being our pixel.
DirectColor
requires a little more thought.
For DirectColor
a pixel is decomposed into three values used to index into
three separate tables, one for red, one for green and one
for blue, the RGB color component being the value of the entry
at that index. Unlike TrueColor
, there is no explicit relationship
between the bits of an RGB triplet and a pixel (since DirectColor
is read-write, the entries can be allocated in any order).
The trick to DirectColor
is in creating such a relationship.
Notice for TrueColor
pixels, the more bits that are set in
a component, the more intense that component (all bits
set in each component yields the color white, all bits unset
in each component yields black). If we allocate our DirectColor
entries such that the pixel with the least bits available is
black and the most bits is white (ramping each table in between)
we effectively create a TrueColor
colormap. We can then use
the same code (bit shifts and bitwise OR'ing) for both classes.
With appropriate initialization, the number of cases we need to worry about drops from six to three. Since visuals that have read-write colormaps can now be treated as their read-only equivalents, we can get a "closest" pixel for any RGB triplet.