179 lines
7.3 KiB
HTML
179 lines
7.3 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
<head>
|
|
<meta name="vsisbn" content="1576101746" />
|
|
<meta name="vstitle" content="Michael Abrash's Graphics Programming Black Book, Special Edition" />
|
|
<meta name="vsauthor" content="Michael Abrash" />
|
|
<meta name="vspublisher" content="The Coriolis Group" />
|
|
<meta name="vspubdate" content="07/01/97" />
|
|
<meta name="vscategory" content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development" />
|
|
|
|
<title>Michael Abrash's Graphics Programming Black Book Special Edition: Wu'ed in Haste; Fried, Stewed at Leisure</title>
|
|
<meta name="chapter" content="42" />
|
|
<meta name="pages" content="784-787" />
|
|
</head>
|
|
|
|
<body>
|
|
<center>
|
|
<table border="1">
|
|
<tr>
|
|
<td>
|
|
<a href="42-03.html">Previous</a>
|
|
</td>
|
|
|
|
<td>
|
|
<a href="index.html">Table of Contents</a>
|
|
</td>
|
|
|
|
<td>
|
|
<a href="42-05.html">Next</a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p><b>LISTING 42.4 L42-4.C</b></p>
|
|
<pre>
|
|
/* Function to draw a non-antialiased line from (X0,Y0) to (X1,Y1), using a
|
|
* simple fixed-point error accumulation approach.
|
|
* Tested with Borland C++ in C compilation mode and the small model.
|
|
*/
|
|
extern void DrawPixel(int, int, int);
|
|
|
|
/* Non-antialiased line drawer.
|
|
* (X0,Y0),(X1,Y1) = line to draw, Color = color in which to draw
|
|
*/
|
|
void DrawLine(int X0, int Y0, int X1, int Y1, int Color)
|
|
{
|
|
unsigned long ErrorAcc, ErrorAdj;
|
|
int DeltaX, DeltaY, XDir, Temp;
|
|
|
|
/* Make sure the line runs top to bottom */
|
|
if (Y0 > Y1) {
|
|
Temp = Y0; Y0 = Y1; Y1 = Temp;
|
|
Temp = X0; X0 = X1; X1 = Temp;
|
|
}
|
|
DrawPixel(X0, Y0, Color); /* draw the initial pixel */
|
|
if ((DeltaX = X1 - X0) >= 0) {
|
|
XDir = 1;
|
|
} else {
|
|
XDir = -1;
|
|
DeltaX = -DeltaX; /* make DeltaX positive */
|
|
}
|
|
if ((DeltaY = Y1 - Y0) == 0) /* done if only one point in the line */
|
|
if (DeltaX == 0) return;
|
|
|
|
ErrorAcc = 0x8000; /* initialize line error accumulator to .5, so we can
|
|
advance when we get halfway to the next pixel */
|
|
/* Is this an X-major or Y-major line? */
|
|
if (DeltaY > DeltaX) {
|
|
/* Y-major line; calculate 16-bit fixed-point fractional part of a
|
|
pixel that X advances each time Y advances 1 pixel */
|
|
ErrorAdj = ((((unsigned long)DeltaX << 17) / (unsigned long)DeltaY) +
|
|
1) >> 1;
|
|
/* Draw all pixels between the first and last */
|
|
do {
|
|
ErrorAcc += ErrorAdj; /* calculate error for this pixel */
|
|
if (ErrorAcc & ~0xFFFFL) {
|
|
/* The error accumulator turned over, so advance the X coord */
|
|
X0 += XDir;
|
|
ErrorAcc &= 0xFFFFL; /* clear integer part of result */
|
|
}
|
|
Y0++; /* Y-major, so always advance Y */
|
|
DrawPixel(X0, Y0, Color);
|
|
} while (--DeltaY);
|
|
return;
|
|
}
|
|
/* It's an X-major line; calculate 16-bit fixed-point fractional part of a
|
|
pixel that Y advances each time X advances 1 pixel */
|
|
ErrorAdj = ((((unsigned long)DeltaY << 17) / (unsigned long)DeltaX) +
|
|
1) >> 1;
|
|
/* Draw all remaining pixels */
|
|
do {
|
|
ErrorAcc += ErrorAdj; /* calculate error for this pixel */
|
|
if (ErrorAcc & ~0xFFFFL) {
|
|
/* The error accumulator turned over, so advance the Y coord */
|
|
Y0++;
|
|
ErrorAcc &= 0xFFFFL; /* clear integer part of result */
|
|
}
|
|
X0 += XDir; /* X-major, so always advance X */
|
|
DrawPixel(X0, Y0, Color);
|
|
} while (--DeltaX);
|
|
}
|
|
</pre>
|
|
|
|
<p>Listing 42.1 isn’t particularly fast, because it calls <b>DrawPixel()</b> for each pixel. On the other hand, <b>DrawPixel()</b> makes it easy to try out Wu antialiasing in a variety of modes; just adapt the code in Listing 42.3 for the 256-color mode you want to support. For example, Listing 42.5 shows code to draw Wu-antialiased lines in 640x480 256-color mode on SuperVGAs built around the Tseng Labs ET4000 chip with at least 512K of display memory installed. It’s well worth checking out Wu antialiasing at 640x480. Although antialiased lines look much smoother than normal lines at 320x200 resolution, they’re far from perfect, because the pixels are so big that the eye can’t blend them properly. At 640x480, however, Wu-antialiased lines look fabulous; from a couple of feet away, they look as straight and smooth as if they were drawn with a ruler.</p>
|
|
|
|
<p><b>LISTING 42.5 L42-5.C</b></p>
|
|
<pre>
|
|
/* Mode set and pixel-drawing functions for the 640x480 256-color mode of
|
|
* Tseng Labs ET4000-based SuperVGAs.
|
|
* Tested with Borland C++ in C compilation mode and the small model.
|
|
*/
|
|
#include <dos.h>
|
|
|
|
/* Screen dimension globals, used in main program to scale */
|
|
int ScreenWidthInPixels = 640;
|
|
int ScreenHeightInPixels = 480;
|
|
|
|
/* ET4000 640x480 256-color draw pixel function. */
|
|
void DrawPixel(int X, int Y, int Color)
|
|
{
|
|
#define SCREEN_SEGMENT 0xA000
|
|
#define GC_SEGMENT_SELECT 0x3CD /* ET4000 segment (bank) select reg */
|
|
unsigned char far *ScreenPtr;
|
|
unsigned int Bank;
|
|
unsigned long BitmapAddress;
|
|
|
|
/* full bitmap address of pixel, as measured from address 0 to 0xFFFFF */
|
|
BitmapAddress = (unsigned long) Y * ScreenWidthInPixels + X;
|
|
/* Bank # is upper word of bitmap addr */
|
|
Bank = BitmapAddress >> 16;
|
|
/* Upper nibble is read bank #, lower nibble is write bank # */
|
|
outp(GC_SEGMENT_SELECT, (Bank << 4) | Bank);
|
|
/* Draw into the bank */
|
|
FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
|
|
FP_OFF(ScreenPtr) = (unsigned int) BitmapAddress;
|
|
*ScreenPtr = Color;
|
|
}
|
|
|
|
/* ET4000 640x480 256-color mode-set function. */
|
|
void SetMode()
|
|
{
|
|
union REGS regset;
|
|
|
|
/* Set to 640x480 256-color graphics mode */
|
|
regset.x.ax = 0x002E;
|
|
int86(0x10, &regset, &regset);
|
|
}
|
|
</pre>
|
|
|
|
<p>Listing 42.1 requires that the DAC palette be set up so that a <b>NumLevel</b>-long block of palette entries contains linearly decreasing intensities of the drawing color. The size of the block is programmable, but must be a power of two. The more intensity levels, the better. Wu says that 32 intensities are enough; on my system, eight and even four levels looked pretty good. I found that gamma correction, which gives linearly spaced intensity steps, improved antialiasing quality significantly. Fortunately, we can program the palette with gamma-corrected values, so our drawing code doesn’t have to do any extra work.</p>
|
|
|
|
<p>Listing 42.1 isn’t very fast, so I implemented Wu antialiasing in assembly, hard-coded for mode 13H. The implementation is shown in full in Listing 42.6. High-speed graphics code and fast VGAs go together like peanut butter and jelly, which is to say very well indeed; the assembly implementation ran more than twice as fast as the C code on my 486. Enough said!</p>
|
|
|
|
<center>
|
|
<table border="1">
|
|
<tr>
|
|
<td>
|
|
<a href="42-03.html">Previous</a>
|
|
</td>
|
|
|
|
<td>
|
|
<a href="index.html">Table of Contents</a>
|
|
</td>
|
|
|
|
<td>
|
|
<a href="42-05.html">Next</a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
<hr width="90%" size="1" noshade="noshade" />
|
|
|
|
<div align="center">
|
|
Graphics Programming Black Book © 2001 Michael Abrash
|
|
</div>
|
|
</body>
|
|
</html>
|