Journal: Dr. Dobb's Journal Nov 1991 v16 n11 p123(7) ----------------------------------------------------------------------------- Title: Antialiasing with the Sierra Hicolor DAC. (Sierra Semiconductor Hicolor digital to analog converter)(Graphics Programming)(includes listing) (tutorial) Author: Abrash, Michael. AttFile: Program: GP-NOV91.ASC Source code listing. Abstract: Sierra Semiconductor's Hicolor digital to analog converter provides classic, high quality antialiasing. Pixels in the Hicolor are stored in one word of display memory; each pixel on the screen corresponds to a group of sub-pixels in the memory buffer. Sub-pixels are averaged and the screen pixel is set to that level. In antialiasing, the image draws to the memory buffer at a multiple of the resolution that the screen supports. A sub-pixel resolution of twice normal along each axe has visible jagged edges but is better than nonantialiased images. Blending colors into a smooth gradient across a boundary allows the human eye to identify the boundary with better precision than the resolution of the screen. ----------------------------------------------------------------------------- Descriptors.. Company: Sierra Semiconductor Corp. (Products). Ticker: SERA. Product: Sierra Semiconductor HiColor (Digital-to-analog converter) (Usage). Topic: Digital to Analog Converters Tutorial Programming Instruction Computer Graphics. Feature: illustration chart. ----------------------------------------------------------------------------- Full Text: There's an Italian saying, the gist of which is, "It need not be true, so long as it's well said." This strikes close to the essential truth of antialiasing: The image need not be accurate, so long as it looks like it is. You don't go to the trouble of antialiasing in order to get a mathematically precise representation of an image; you do it so the amazing eye/brain integrating/pattern matching system will see what you want it to see. This is a particularly relevant thought at the moment, for we're smack in the middle of discussing the Sierra Hicolor DAC, which makes classic, high-quality antialiasing, of the sort that Targa boards have offered for years, available at mass-market prices. To recap, the Hicolor DAC extends SuperVGA to provide selection among enough colors for serious rendering and antialiasing; 32,768 simultaneous colors, to be exact. Although the Hicolor DAC falls short of the 24-bpp true color standard, you aren't likely to find a 24-bpp adapter priced in the $200-300 range. Last month, we looked at simple, unweighted antialiasing in the context of the VGA's standard 256-color mode, performing antialiasing between exactly four colors--red, green, blue, and black--with five semi-independent levels of each of the three primary colors available. This month, we'll start off by discussing the basic Hicolor programming model, then we'll do the same sort of antialiasing as last month--but this time with 32 fully independent levels of each primary color and resolutions up to 800x600, which makes quite a difference indeed. A Brief Primer on the Sierra Hicolor DAC The operation of the Hicolor DAC in 32K-color mode is remarkably simple. First, the VGA must be set to a 256-color mode with twice the desired horizontal resolution; for example, a 1600x 600 256-color mode would be selected if 800x600 32K-color mode were desired. Then, the Hicolor DAC is set to high-color mode via the command register; in high-color mode, the Hicolor DAC takes each pair of 256-color pixels, joins them together into one 16-bit pixel, converts the red, green, and blue components (described shortly) directly to proportional analog values (no palette involved), and sends them to the monitor. Ther is a serious problem here, however: There is no standard way for an application to select a high-color mode. It's not enough to set up the Hicolor DAC; the VGA must also be set to the appropriate double-resolution 256-color mode, and the sequence for doing that--especially selecting high-speed clocks-varies from VGA to VGA. There is no VESA mode number for high-color modes; there is a VESA programming guideline for high-color modes, and I'm looking into it, but it's certainly not as simple as a mode number. In any case, the VESA interface isn't always available. Consequently, high-color mode selection is adapter-dependent. Fortunately, most of the Hicolor-based boards are built around the Tseng Labs ET4000 VGA chip (ATI is the only exception I know of, which is why I'm focusing on ET4000-based Hicolor boards in this column), and Tseng provides a BIOS interface for high-color modes. (There's no guarantee that manufacturers using the ET4000 will follow the Tseng interface, but I suspect they will, as it's the closest thing to a standard at the mooment.) Unfortunately, when I run the ET4000 BIOS function that reports whether a Hicolor DAC is present on my Toshiba portable without a Hicolor board installed, it hangs my system, so it's not a good idea to rely on the BIOS functions alone. My solution, shown in Listing One (page 146), is to first check for a Hicolor DAC and an ET4000 at the hardware level; if both are present, I call the BIOS (which is presumably at least not hostile to the Tseng BIOS high-color extensions at this point) to check for the availability of Hicolor modes, and finally, if all has gone well, to set the desired high-color mode. This is probably overkill, but at least this way you get three kinds of chip ID code to mix and match as you wish. Programming the Hicolor DAC The pixel format of the Hicolor DAC is straightforward: Each pixel is stored in one word of display memory, with the lowest 5 bits forming the blue component, the next 5 bits forming the green component, the next 5 bits forming the red component, and bit 15 ignored, as shown in Figure 1. Pixels start at even addresses. The bits within a word are organized Intel style, with the byte at the even address containing bits 7-0 (blue and part of green), and the byte at the odd address containing bits 158 (red and the rest of green). Pixels proceed linearly for the length of the bitmap; the organization is the same as 256-color mode, except that each pixel takes up one word, rather than one byte. As in SuperVGA 256-color modes, the bitmap is too long to be addressed in the 64K video memory window, so banking must be used; again, the banking is just like 256-color or banking, except that each bank contains only half as many pixels; 32,768, to be exact. On the ET4000, the Segment Select register at 3CDh controls banking, as shown in Figure 2. There are 16 banks, each spanning 64 Kbytes of the total 1-Mbyte bitmap. Banks can be selected separately for read and write to facilitate scrolling and screen-to-screen copies, although we won't need that today. Simple enough, but there's a catch: broken rasters. Banks are 64K in length. If each Hicolor scan line is 1600 bytes long, then 65,536/1600=40 raster lines (lines 0-39) fit in bank 0--with 1536 bytes of the next line (line 40), also in the first bank. The last 64 bytes of line 40 are in bank 1, as shown in Figure 3, so the line is split by the bank boundary; hence the term "broken raster." Broken rasters crop up for other bank crossings as well, and make Hicolor programming somewhat slower (the extent depends heavily on the quality of the code) and considerably more complicated. Broken rasters are not unique to Hicolor modes; 800x600 and 640x480 256-color modes also normally have broken rasters. However, there's a clever workaround for broken rasters in 256-color modes: Stretch the bitmap width to 1K pixels, via the Row Offset register, so that banks split between raster lines (although this works for 800x600 only if the VGA has 1 Mbyte of memory). Sad to say, stretching the bitmap width to 1K pixels doesn't work in Hicolor mode. There's not enough memory to do it at 800x600 (at least until 2-Mbyte VGAs appear), but that's not the problem at 640x480. The problem is that a Row Offset register setting of 256 would be required to stretch the bitmap width to 1K pixels--and the Row Offset register only goes up to 255. I'm sure that when the VGA was being designed, 255 seemed like plenty, but then, 640K once seemed like pie in the sky. The upshot is that Hicolor programming unavoidably requires handling broken rasters. That's a nuisance, but a manageable one; next, we'll see polygon fill code that deals with broken rasters. Non-antialiased Hicolor Drawing Listing Two (page 146) draws a perspective cube in 640x480 32K color mode, with help from the initialization code in Listing One, the DrawPixel-based low-level polygon fill code in Listing Three (page 146), and the header file in Listing Seven, page 149. (FILCNVXD.C from last month and Listing Four from the March column are also required.) Not surprisingly, the cube drawn by Listing Two looks a lot like the non-antialiased cube drawn last month, but isn't as jagged because the resolution is now much higher. Nonetheless, jaggies are still quite prominent, and they remain clearly visible at 800x600. (I've used 640x480 mode so that the code will work on fixed-frequency monitors, but Listing Two can be altered for 800x600 mode simply by changing the parameter passed to SetHCMode and the value of Bitmap WidthInBytes.) Listing Two doesn't run very fast when linked to Listing Three; I suspect you'll like the low-level polygon fill code in Listing Four (page 146) much better. This code handles broken rasters reasonably efficiently, by checking for them at the beginning of each scan line, then splitting up the fill and banking appropriately whenever a bank crossing is detected. Simple Unweighted Antialiasing As the saying goes, you can never be too rich, too thin, or have too many colors available. Personally, I only buy one of those three assertions: You really can't have too many colors. Listings Five (page 148) and Six (page 149), together with Listings One, Three, and Seven, FILCNVXD.C from last month, and Listing Four from March, show why. This program draws the same cube, but this time employing the simple, unweighted antialiasing we used last month--and taking advantage of the full color range of the Hicolor DAC. The results are excellent: On my venerable NEC MultiSync, at a viewing distance of one foot, all but two of the edges look absolutely smooth, with not the slightest hint of jaggies, and the two imperfect edges show only slight ripples. At two feet, the cube looks perfect. The difference between the non-antialiased and antialiased cubes is astounding, considering that we're working with the same resolution in both cases. A quick review of the simple antialiasing used this month and last: The image is drawn to a memory buffer at a multiple of the resolution that the actual screen supports. Each pixel on the screen maps to a group of hi-res pixels (subpixels), arranged in a square, in the memory buffer. The colors of the subpixels in each square are averaged, and the corresponding screen pixel is set to the average subpixel color. There's not enough memory to scan out the entire image at high resolution (about 50K is required just to scan out one raster line at 4X resolution!), so Listing Six scans out just those pixels that lie in a specified band. (Each band corresponds to a single raster line in Listing Five.) Note that Listing Six draws 32-bit pixels to the memory buffer; this is true color, plus an extra byte for flexibility. Consequently, Listing Six is a general-purpose tool, and can be used with any sort of adapter, so long as the main program knows how to convert from true color to adapter-specific pixels. Listing Five does this by calculating the average intensity in each subpixel group of each of the three primary colors, in the range 0-255, then looking up the gamma corrected equivalent color value for the Hicolor DAC, mapped into the range 0-31. A quick look at the gamma-corrected color mapping table in Listing Five shows why hardware gamma correction is sorely missed in the Hicolor DAC. The brightest half of the color range--from half intensity to full intensity--is spanned by only 9 of the Hicolor DAC's 32 color values. That means that for brighter colors, the Hicolor DAC effectively has only half the color resolution that you'd expect from 5 bits per color gun, and the resolution is even worse at the highest intensities. I'd like to take a moment to emphasize that although Listing Five works with only the three primary colors, it could just as easily work with the thousands of colors that can be produced as mixes of the three primaries; there are none of the limitations of 256-color mode, and no special tricks (such as biasing the palette according to color frequency) need be used. Inevitably, though, proportionately fewer intermediate blends are available and hence antialiasing becomes less precise when there is less contrast between colors; you're not going to be able to do much antialiasing between a pixel with a green true color value of 250 and another with a value of 255. This is where the lack of gamma correction and the difference between 15-bpp and true color become apparent. Notes on the Antialiasing Implementation Listing Five features user-selectable subpixel resolution (the multiple of the screen resolution at which the image should be drawn into the memory buffer). A subpixel resolution of two times normal along both axes (2X) looks much better than nonantialiased drawing, but still has visible jaggies. Subpixel resolution of 4X looks terrific, as mentioned earlier. Higher subpixel resolutions are, practically speaking, reserved for 386 protected mode, because they would require a buffer larger than 64K to hold the high-res equivalent of a single scan line. On the downside, Listing Five is very slow, even though the conversion process from true color pixels to Hicolor pixels is limited to the bounding rectangle for the cube being drawn, thereby saving the time that was wasted last month drawing the empty space around the cube. It could easily be sped up by, say, an order of magnitude, in a number of ways. First, you could implement an ASM function that's the equivalent of memset, but stores longs (dwords) rather than chars (bytes). In the absence of any such C library function, Listing Six uses a loop with a pointer to a long, hardly a recipe for high performance. Listing Five could also be sped up by doing the screen pixel construction from each square of subpixels in assembly language using pointers rather than array look-ups. It would also help to organize the screen pixel drawing more as a variant rectangle fill, instead of going through DrawPixel every time, so that the screen pointer doesn't have to be recalculated from scratch and the bank doesn't need to be calculated and set for every pixel. Clipping each polygon to the band before rather than after scanning it out would speed things up, as would building an edge list for the polygons once, ahead of time, then advancing it incrementally to scan out each band, rather than doing one complete scan of each polygon for each band. Bigger bands would help; drawing the whole image to the memory buffer in one burst, then converting the entire image to Hicolor pixels in a single operation, would be ideal, but would require a ridiculous amount of memory. (Would you believe, 31 megs for one full 800x600 Hicolor screen at 4X resolution?) Finally, to alter Listing Five for 800x 600 Hicolor mode, change the parameter passed to SetHCMode, the value of BitmapWidthInBytes, and the value of SCREEN WIDTH. Further Thoughts on Antialiasing The banded true color approach of Listings Five and Six is easily extended to other antialiasing approaches. For example, you could, if you wished, average together all the subpixels not within a square, but rather within a circle of radius sqrt(2.0)*ResolutionMultiplier/2 around each pixel center. This approach is a little more complicated, but it has one great virtue: An image will be antialiased identically, regardless of its rotation. Why is the shape of the subpixed area that's collected into a screen pixel important, when the maximum resolution we can actually draw with is the resolution of the screen? I'll quote William vanRyper, from the graphics.disp/vga conference on BIX: If you anti-alias an edge on the screen, and let the eye-brain pick the edge somewhere the gradien between the object color and the background, you can adjust the placement of that perceptual edge by altering the ramp of the gradient. If the number of intermediate values you can choose among is greater then the number of gradient pixels you set (across the edge), you can adjust the position of the perceptucal edge in increments of less than a pixel. This means you can locate the antialiased object to sub-pixel precesion. In other words, by using blends of color in a smooth, consistent gradient across a boundary, you can get the eye to pick out the boundary location with a precision that's greater than the resolution of the screen. This is, of course, part and parcel of the wonderful eye/brain magic that allows color to substitute for resolution and makes antialiasing worthwhile. Given that we can draw images with perceived resolution higher than the screen, consistency in subpixel placement is very important. Unfortunately, our simple square antialiasing does not produce the same results (a consistent color gradient) for an image rotated 45 degrees as it does for an unrotated image--but antialiasing based on a circular subpixel area does. So the shape of the subpixel area used for antialiasing matters because if it's not symmetric in all directions, boundaries will appear to wiggle as images roate, destroying the image of reality that antialiased animation strives to create. On the other hand, if you're drawing only static images, use a square subpixel area for antialiasing; it's fast, easy, and looks just fine in that context. As I said at the outset, we're not seeking mathematical perfection here, just a good-looking display for the purpose at hand. If it looks good, it is good