126 lines
6.8 KiB
Markdown
126 lines
6.8 KiB
Markdown
**LISTING 55.3 L55-3.C**
|
||
|
||
/* Converts a model color (a color in the RGB color cube, in the current
|
||
color model) to a color index for mode X. Pure primary colors are
|
||
special-cased, and everything else is handled by a 2-2-2 model. */
|
||
int ModelColorToColorIndex(ModelColor * Color)
|
||
{
|
||
if (Color->Red == 0) {
|
||
if (Color->Green == 0) {
|
||
/* Pure blue */
|
||
return(192+(Color->Blue >> 2));
|
||
} else if (Color->Blue == 0) {
|
||
/* Pure green */
|
||
return(128+(Color->Green >> 2));
|
||
}
|
||
} else if ((Color->Green == 0) && (Color->Blue == 0)) {
|
||
/* Pure red */
|
||
return(64+(Color->Red >> 2));
|
||
}
|
||
/* Multi-color mix; look up the index with the two most significant bits
|
||
of each color component */
|
||
return(((Color->Red & 0xC0) >> 2) | ((Color->Green & 0xC0) >> 4) |
|
||
((Color->Blue & 0xC0) >> 6));
|
||
}
|
||
|
||
In DEMO1, three-quarters of the palette is set up with 64 intensity
|
||
levels of each of the three pure primary colors (red, green, and blue),
|
||
and then most drawing is done with only pure primary colors. The
|
||
resulting rendering quality is very good because there are so many
|
||
levels of each primary.
|
||
|
||
The downside is that this excellent quality is available for only three
|
||
colors: red, green, and blue. What about all the other colors that are
|
||
mixes of the primaries, like cyan or yellow, to say nothing of gray? In
|
||
the DEMO1 color model, any RGB color that is not a pure primary is
|
||
mapped into a 2-2-2 RGB space that the remaining quarter of the VGA's
|
||
palette is set up to display; that is, there are exactly two bits of
|
||
precision for each color component, or 64 general RGB colors in all.
|
||
This is genuinely lousy color resolution, being only 1/64th of the
|
||
resolution we really need for each color component. In this model, a
|
||
staggering 262,144 colors from the 24-bit RGB cube map to *each* color
|
||
in the 2-2-2 VGA palette. The results are not impressive; the colors of
|
||
mixed-primary surfaces jump abruptly, badly damaging the illusion of
|
||
real illumination. To see how poor a 2-2-2 RGB selection can look, run
|
||
DEMO1, and press the ‘2' key to turn on spotlight 2, the blue spotlight.
|
||
Because the ambient lighting is green, turning on the blue spotlight
|
||
causes mixed-primary colors to be displayed—and the result looks
|
||
terrible, because there just isn't enough color resolution.
|
||
Unfortunately, 2-2-2 RGB is close to the best general color resolution
|
||
the VGA can display; 3-3-2 is as good as it gets.
|
||
|
||
Another approach would be to set up the palette with reasonably good
|
||
mixes of two primaries but no mixes of three primaries, then use only
|
||
two-primary colors in your applications (no grays or whites or other
|
||
three-primary mixes). Or you could choose to shade only selected
|
||
objects, using part of the palette for a good range of the colors of
|
||
those objects, and reserving the rest of the palette for the fixed
|
||
colors of the other, nonshaded objects. Jim Kent, author of Autodesk
|
||
Animator, suggests dynamically adjusting the palette to the needs of
|
||
each frame, for example by allocating the colors for each frame on a
|
||
first-come, first-served basis. That wouldn't be trivial to do in real
|
||
time, but it would make for extremely efficient use of the palette.
|
||
|
||
Another widely used solution is to set up a 2-2-2, 3-3-2, or 2.6-2.6-2.6
|
||
(6 levels per primary) palette, and dither colors. Dithering is an
|
||
excellent solution, but outside the scope of this book. Take a look at
|
||
Chapter 13 of Foley and Van Dam (cited in "Further Readings") for an
|
||
introduction to color perception and approximation.
|
||
|
||
The sad truth is that the VGA's 256-color palette is an inadequate
|
||
resource for general RGB shading. The good news is that clever
|
||
workarounds can make VGA graphics look nearly as good as 24-bpp
|
||
graphics; but the burden falls on you, the programmer, to design your
|
||
applications and color mapping to compensate for the VGA's limitations.
|
||
To experiment with a different 256-color model in X-Sharp, just change
|
||
**InitializePalette()** to set up the desired palette and
|
||
**ModelColorToColorIndex()** to map 24-bit RGB triplets into the palette
|
||
you've set up. It's that simple, and the results can be striking indeed.
|
||
|
||
#### A Bonus from the BitMan {#Heading4}
|
||
|
||
Finally, a note on fast VGA text, which came in from a correspondent who
|
||
asked to be referred to simply as the BitMan. The BitMan passed along a
|
||
nifty application of the VGA's under-appreciated write mode 3 that is,
|
||
under the proper circumstances, the fastest possible way to draw text in
|
||
any 16-color VGA mode.
|
||
|
||
The task at hand is illustrated by Figure 55.2. We want to draw what's
|
||
known as solid text, in which the effect is the same as if the cell
|
||
around each character was drawn in the background color, and then each
|
||
character was drawn on top of the background box. (This is in contrast
|
||
to transparent text, where each character is drawn in the foreground
|
||
color without disturbing the background.) Assume that each character
|
||
fits in an eight-wide cell (as is the case with the standard VGA fonts),
|
||
and that we're drawing text at byte-aligned locations in display memory.
|
||
|
||
Solid text is useful for drawing menus, text areas, and the like;
|
||
basically, it can be used whenever you want to display text on a
|
||
solid-color background. The obvious way to implement solid text is to
|
||
fill the rectangle representing the background box, then draw
|
||
transparent text on top of the background box. However, there are two
|
||
problems with doing solid text this way. First, there's some flicker,
|
||
because for a little while the box is there but the text hasn't yet
|
||
arrived. More important is that the background-followed-by-foreground
|
||
approach accesses display memory three times for each byte of font data:
|
||
once to draw the background box, once to read display memory to load the
|
||
latches, and once to actually draw the font pattern. Display memory is
|
||
incredibly slow, so we'd like to reduce the number of accesses as much
|
||
as possible. With the BitMan's approach, we can reduce the number of
|
||
accesses to just one per font byte, and eliminate flicker, too.
|
||
|
||
\
|
||
**Figure 55.2** *Drawing solid text.*
|
||
|
||
The keys to fast solid text are the latches and write mode 3. The
|
||
latches, as you may recall from earlier discussions in this book, are
|
||
four internal VGA registers that hold the last bytes read from the VGA's
|
||
four planes; every read from VGA memory loads the latches with the
|
||
values stored at that display memory address across the four planes.
|
||
Whenever a write is performed to VGA memory, the latches can provide
|
||
some, none, or all of the bits written to memory, depending on the bit
|
||
mask, which selects between the latched data and the drawing data on a
|
||
bit-by-bit basis. The latches solve half our problem; we can fill the
|
||
latches with the background color, then use them to draw the background
|
||
box. The trick now is drawing the text pixels in the foreground color at
|
||
the same time.
|