131 lines
5.2 KiB
C
131 lines
5.2 KiB
C
/* Function to draw an antialiased line from (X0,Y0) to (X1,Y1), using an
|
|
* antialiasing approach published by Xiaolin Wu in the July 1991 issue of
|
|
* Computer Graphics. Requires that the palette be set up so that there
|
|
* are NumLevels intensity levels of the desired drawing color, starting at
|
|
* color BaseColor (100% intensity) and followed by (NumLevels-1) levels of
|
|
* evenly decreasing intensity, with color (BaseColor+NumLevels-1) being 0%
|
|
* intensity of the desired drawing color (black). This code is suitable for
|
|
* use at screen resolutions, with lines typically no more than 1K long; for
|
|
* longer lines, 32-bit error arithmetic must be used to avoid problems with
|
|
* fixed-point inaccuracy. No clipping is performed in DrawWuLine; it must be
|
|
* performed either at a higher level or in the DrawPixel function.
|
|
* Tested with Borland C++ in C compilation mode and the small model.
|
|
*/
|
|
extern void DrawPixel(int, int, int);
|
|
|
|
/* Wu antialiased line drawer.
|
|
* (X0,Y0),(X1,Y1) = line to draw
|
|
* BaseColor = color # of first color in block used for antialiasing, the
|
|
* 100% intensity version of the drawing color
|
|
* NumLevels = size of color block, with BaseColor+NumLevels-1 being the
|
|
* 0% intensity version of the drawing color
|
|
* IntensityBits = log base 2 of NumLevels; the # of bits used to describe
|
|
* the intensity of the drawing color. 2**IntensityBits==NumLevels
|
|
*/
|
|
void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels, unsigned int IntensityBits)
|
|
{
|
|
unsigned int IntensityShift, ErrorAdj, ErrorAcc;
|
|
unsigned int ErrorAccTemp, Weighting, WeightingComplementMask;
|
|
int DeltaX, DeltaY, Temp, XDir;
|
|
|
|
/* Make sure the line runs top to bottom */
|
|
if (Y0 > Y1) {
|
|
Temp = Y0; Y0 = Y1; Y1 = Temp;
|
|
Temp = X0; X0 = X1; X1 = Temp;
|
|
}
|
|
/* Draw the initial pixel, which is always exactly intersected by
|
|
the line and so needs no weighting */
|
|
DrawPixel(X0, Y0, BaseColor);
|
|
|
|
if ((DeltaX = X1 - X0) >= 0) {
|
|
XDir = 1;
|
|
} else {
|
|
XDir = -1;
|
|
DeltaX = -DeltaX; /* make DeltaX positive */
|
|
}
|
|
/* Special-case horizontal, vertical, and diagonal lines, which
|
|
require no weighting because they go right through the center of
|
|
every pixel */
|
|
if ((DeltaY = Y1 - Y0) == 0) {
|
|
/* Horizontal line */
|
|
while (DeltaX-- != 0) {
|
|
X0 += XDir;
|
|
DrawPixel(X0, Y0, BaseColor);
|
|
}
|
|
return;
|
|
}
|
|
if (DeltaX == 0) {
|
|
/* Vertical line */
|
|
do {
|
|
Y0++;
|
|
DrawPixel(X0, Y0, BaseColor);
|
|
} while (--DeltaY != 0);
|
|
return;
|
|
}
|
|
if (DeltaX == DeltaY) {
|
|
/* Diagonal line */
|
|
do {
|
|
X0 += XDir;
|
|
Y0++;
|
|
DrawPixel(X0, Y0, BaseColor);
|
|
} while (--DeltaY != 0);
|
|
return;
|
|
}
|
|
/* Line is not horizontal, diagonal, or vertical */
|
|
ErrorAcc = 0; /* initialize the line error accumulator to 0 */
|
|
/* # of bits by which to shift ErrorAcc to get intensity level */
|
|
IntensityShift = 16 - IntensityBits;
|
|
/* Mask used to flip all bits in an intensity weighting, producing the
|
|
result (1 - intensity weighting) */
|
|
WeightingComplementMask = NumLevels - 1;
|
|
/* 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, truncating the
|
|
result so that we won't overrun the endpoint along the X axis */
|
|
ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY;
|
|
/* Draw all pixels other than the first and last */
|
|
while (--DeltaY) {
|
|
ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
|
|
ErrorAcc += ErrorAdj; /* calculate error for next pixel */
|
|
if (ErrorAcc <= ErrorAccTemp) {
|
|
/* The error accumulator turned over, so advance the X coord */
|
|
X0 += XDir;
|
|
}
|
|
Y0++; /* Y-major, so always advance Y */
|
|
/* The IntensityBits most significant bits of ErrorAcc give us the
|
|
intensity weighting for this pixel, and the complement of the
|
|
weighting for the paired pixel */
|
|
Weighting = ErrorAcc >> IntensityShift;
|
|
DrawPixel(X0, Y0, BaseColor + Weighting);
|
|
DrawPixel(X0 + XDir, Y0, BaseColor + (Weighting ^ WeightingComplementMask));
|
|
}
|
|
/* Draw the final pixel, which is always exactly intersected by the line
|
|
and so needs no weighting */
|
|
DrawPixel(X1, Y1, BaseColor);
|
|
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, truncating the
|
|
result to avoid overrunning the endpoint along the X axis */
|
|
ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX;
|
|
/* Draw all pixels other than the first and last */
|
|
while (--DeltaX) {
|
|
ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
|
|
ErrorAcc += ErrorAdj; /* calculate error for next pixel */
|
|
if (ErrorAcc <= ErrorAccTemp) {
|
|
/* The error accumulator turned over, so advance the Y coord */
|
|
Y0++;
|
|
}
|
|
X0 += XDir; /* X-major, so always advance X */
|
|
/* The IntensityBits most significant bits of ErrorAcc give us the
|
|
intensity weighting for this pixel, and the complement of the
|
|
weighting for the paired pixel */
|
|
Weighting = ErrorAcc >> IntensityShift;
|
|
DrawPixel(X0, Y0, BaseColor + Weighting);
|
|
DrawPixel(X0, Y0 + 1, BaseColor + (Weighting ^ WeightingComplementMask));
|
|
}
|
|
/* Draw the final pixel, which is always exactly intersected by the line
|
|
and so needs no weighting */
|
|
DrawPixel(X1, Y1, BaseColor);
|
|
}
|