abrash-black-book/55-03.md
2013-12-30 20:26:41 +11:00

126 lines
6.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

**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.
![](images/55-02.jpg)\
**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.