home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1992
/
06
/
grphprog.asc
< prev
next >
Wrap
Text File
|
1992-05-18
|
29KB
|
722 lines
_GRAPHICS PROGRAMMING COLUMN_
by Michael Abrash
[LISTING ONE]
/* 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++ 3.0 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);
}
[LISTING TWO]
/* Sample line-drawing program to demonstrate Wu antialiasing. Also draws
* non-antialiased lines for comparison.
* Tested with Borland C++ 3.0 in C compilation mode and the small model.
*/
#include <dos.h>
#include <conio.h>
void SetPalette(struct WuColor *);
extern void DrawWuLine(int, int, int, int, int, int, unsigned int);
extern void DrawLine(int, int, int, int, int);
extern void SetMode(void);
extern int ScreenWidthInPixels; /* screen dimension globals */
extern int ScreenHeightInPixels;
#define NUM_WU_COLORS 2 /* # of colors we'll do antialiased drawing with */
struct WuColor { /* describes one color used for antialiasing */
int BaseColor; /* # of start of palette intensity block in DAC */
int NumLevels; /* # of intensity levels */
int IntensityBits; /* IntensityBits == log2 NumLevels */
int MaxRed; /* red component of color at full intensity */
int MaxGreen; /* green component of color at full intensity */
int MaxBlue; /* blue component of color at full intensity */
};
enum {WU_BLUE=0, WU_WHITE=1}; /* drawing colors */
struct WuColor WuColors[NUM_WU_COLORS] = /* blue and white */
{{192, 32, 5, 0, 0, 0x3F}, {224, 32, 5, 0x3F, 0x3F, 0x3F}};
void main()
{
int CurrentColor, i;
union REGS regset;
/* Draw Wu-antialiased lines in all directions */
SetMode();
SetPalette(WuColors);
for (i=5; i<ScreenWidthInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
WuColors[WU_BLUE].IntensityBits);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
WuColors[WU_BLUE].IntensityBits);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor,
WuColors[WU_BLUE].NumLevels, WuColors[WU_BLUE].IntensityBits);
}
for (i=0; i<ScreenWidthInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor,
WuColors[WU_WHITE].NumLevels,
WuColors[WU_WHITE].IntensityBits);
}
getch(); /* wait for a key press */
/* Now clear the screen and draw non-antialiased lines */
SetMode();
SetPalette(WuColors);
for (i=0; i<ScreenWidthInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
WuColors[WU_BLUE].BaseColor);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
WuColors[WU_BLUE].BaseColor);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor);
}
for (i=0; i<ScreenWidthInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor);
}
getch(); /* wait for a key press */
regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */
int86(0x10, ®set, ®set); /* return to text mode */
}
/* Sets up the palette for antialiasing with the specified colors.
* Intensity steps for each color are scaled from the full desired intensity
* of the red, green, and blue components for that color down to 0%
* intensity; each step is rounded to the nearest integer. Colors are
* corrected for a gamma of 2.3. The values that the palette is programmed
* with are hardwired for the VGA's 6 bit per color DAC.
*/
void SetPalette(struct WuColor * WColors)
{
int i, j;
union REGS regset;
struct SREGS sregset;
static unsigned char PaletteBlock[256][3]; /* 256 RGB entries */
/* Gamma-corrected DAC color components for 64 linear levels from 0% to
100% intensity */
static unsigned char GammaTable[] = {
0, 10, 14, 17, 19, 21, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34,
35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46,
47, 48, 48, 49, 49, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55,
56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63};
for (i=0; i<NUM_WU_COLORS; i++) {
for (j=0; j<WColors[i].NumLevels; j++) {
PaletteBlock[j][0] = GammaTable[((double)WColors[i].MaxRed * (1.0 -
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
PaletteBlock[j][1] = GammaTable[((double)WColors[i].MaxGreen * (1.0 -
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
PaletteBlock[j][2] = GammaTable[((double)WColors[i].MaxBlue * (1.0 -
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
}
/* Now set up the palette to do Wu antialiasing for this color */
regset.x.ax = 0x1012; /* set block of DAC registers function */
regset.x.bx = WColors[i].BaseColor; /* first DAC location to load */
regset.x.cx = WColors[i].NumLevels; /* # of DAC locations to load */
regset.x.dx = (unsigned int)PaletteBlock; /* offset of array from which
to load RGB settings */
sregset.es = _DS; /* segment of array from which to load settings */
int86x(0x10, ®set, ®set, &sregset); /* load the palette block */
}
}
[LISTING THREE]
/* VGA mode 13h pixel-drawing and mode set functions.
* Tested with Borland C++ 3.0 in C compilation mode and the small model.
*/
#include <dos.h>
/* Screen dimension globals, used in main program to scale. */
int ScreenWidthInPixels = 320;
int ScreenHeightInPixels = 200;
/* Mode 13h draw pixel function. */
void DrawPixel(int X, int Y, int Color)
{
#define SCREEN_SEGMENT 0xA000
unsigned char far *ScreenPtr;
FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
FP_OFF(ScreenPtr) = (unsigned int) Y * ScreenWidthInPixels + X;
*ScreenPtr = Color;
}
/* Mode 13h mode-set function. */
void SetMode()
{
union REGS regset;
/* Set to 320x200 256-color graphics mode */
regset.x.ax = 0x0013;
int86(0x10, ®set, ®set);
}
[LISTING FOUR]
/* 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++ 3.0 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);
}
[LISTING FIVE]
/* Mode set and pixel-drawing functions for the 640x480 256-color mode of
* Tseng Labs ET4000-based SuperVGAs.
* Tested with Borland C++ 3.0 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, ®set, ®set);
}
[LISTING SIX]
; Inner loop for drawing Y-major lines from Wu-antialiased line drawer.
YMajorLoop:
add dx,bp ;calculate error for next pixel
jnc NoXAdvance ;not time to step in X yet
;the error accumulator turned over,
; so advance the X coord
add si,bx ;add XDir to the pixel pointer
NoXAdvance:
add si,SCREEN_WIDTH_IN_BYTES ;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.
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
add ah,al ;BaseColor + Weighting
mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
xor ah,ch ;Weighting ^ WeightingComplementMask
add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
mov [si+bx],ah ;DrawPixel(X+XDir, Y,
; BaseColor + (Weighting ^ WeightingComplementMask));
dec di ;--DeltaY
jnz YMajorLoop
[WU ANTIALIASING]
; C near-callable function to draw an antialiased line from
; (X0,Y0) to (X1,Y1), in mode 13h, the VGA's standard 320x200 256-color
; mode. Uses 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). No clipping is
; performed in DrawWuLine. Handles a maximum of 256 intensity levels per
; antialiased color. 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.
; Tested with TASM 3.0.
;
; C near-callable as:
; void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor,
; int NumLevels, unsigned int IntensityBits);
SCREEN_WIDTH_IN_BYTES equ 320 ;# of bytes from the start of one scan line
; to the start of the next
SCREEN_SEGMENT equ 0a000h ;segment in which screen memory resides
; Parameters passed in stack frame.
parms struc
dw 2 dup (?) ;pushed BP and return address
X0 dw ? ;X coordinate of line start point
Y0 dw ? ;Y coordinate of line start point
X1 dw ? ;X coordinate of line end point
Y1 dw ? ;Y coordinate of line end point
BaseColor dw ? ;color # of first color in block used for
; antialiasing, the 100% intensity version of the
; drawing color
NumLevels dw ? ;size of color block, with BaseColor+NumLevels-1
; being the 0% intensity version of the drawing color
; (maximum NumLevels = 256)
IntensityBits dw ? ;log base 2 of NumLevels; the # of bits used to
; describe the intensity of the drawing color.
; 2**IntensityBits==NumLevels
; (maximum IntensityBits = 8)
parms ends
.model small
.code
; Screen dimension globals, used in main program to scale.
_ScreenWidthInPixels dw 320
_ScreenHeightInPixels dw 200
.code
public _DrawWuLine
_DrawWuLine proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
push si ;preserve C's register variables
push di
push ds ;preserve C's default data segment
cld ;make string instructions increment their pointers
; Make sure the line runs top to bottom.
mov si,[bp].X0
mov ax,[bp].Y0
cmp ax,[bp].Y1 ;swap endpoints if necessary to ensure that
jna NoSwap ; Y0 <= Y1
xchg [bp].Y1,ax
mov [bp].Y0,ax
xchg [bp].X1,si
mov [bp].X0,si
NoSwap:
; Draw the initial pixel, which is always exactly intersected by the line
; and so needs no weighting.
mov dx,SCREEN_SEGMENT
mov ds,dx ;point DS to the screen segment
mov dx,SCREEN_WIDTH_IN_BYTES
mul dx ;Y0 * SCREEN_WIDTH_IN_BYTES yields the offset
; of the start of the row start the initial
; pixel is on
add si,ax ;point DS:SI to the initial pixel
mov al,byte ptr [bp].BaseColor ;color with which to draw
mov [si],al ;draw the initial pixel
mov bx,1 ;XDir = 1; assume DeltaX >= 0
mov cx,[bp].X1
sub cx,[bp].X0 ;DeltaX; is it >= 1?
jns DeltaXSet ;yes, move left->right, all set
;no, move right->left
neg cx ;make DeltaX positive
neg bx ;XDir = -1
DeltaXSet:
; Special-case horizontal, vertical, and diagonal lines, which require no
; weighting because they go right through the center of every pixel.
mov dx,[bp].Y1
sub dx,[bp].Y0 ;DeltaY; is it 0?
jnz NotHorz ;no, not horizontal
;yes, is horizontal, special case
and bx,bx ;draw from left->right?
jns DoHorz ;yes, all set
std ;no, draw right->left
DoHorz:
lea di,[bx+si] ;point DI to next pixel to draw
mov ax,ds
mov es,ax ;point ES:DI to next pixel to draw
mov al,byte ptr [bp].BaseColor ;color with which to draw
;CX = DeltaX at this point
rep stosb ;draw the rest of the horizontal line
cld ;restore default direction flag
jmp Done ;and we're done
align 2
NotHorz:
and cx,cx ;is DeltaX 0?
jnz NotVert ;no, not a vertical line
;yes, is vertical, special case
mov al,byte ptr [bp].BaseColor ;color with which to draw
VertLoop:
add si,SCREEN_WIDTH_IN_BYTES ;point to next pixel to draw
mov [si],al ;draw the next pixel
dec dx ;--DeltaY
jnz VertLoop
jmp Done ;and we're done
align 2
NotVert:
cmp cx,dx ;DeltaX == DeltaY?
jnz NotDiag ;no, not diagonal
;yes, is diagonal, special case
mov al,byte ptr [bp].BaseColor ;color with which to draw
DiagLoop:
lea si,[si+SCREEN_WIDTH_IN_BYTES+bx]
;advance to next pixel to draw by
; incrementing Y and adding XDir to X
mov [si],al ;draw the next pixel
dec dx ;--DeltaY
jnz DiagLoop
jmp Done ;and we're done
; Line is not horizontal, diagonal, or vertical.
align 2
NotDiag:
; Is this an X-major or Y-major line?
cmp dx,cx
jb XMajor ;it's X-major
; It's a Y-major line. Calculate the 16-bit fixed-point fractional part of a
; pixel that X advances each time Y advances 1 pixel, truncating the result
; to avoid overrunning the endpoint along the X axis.
xchg dx,cx ;DX = DeltaX, CX = DeltaY
sub ax,ax ;make DeltaX 16.16 fixed-point value in DX:AX
div cx ;AX = (DeltaX << 16) / DeltaY. Won't overflow
; because DeltaX < DeltaY
mov di,cx ;DI = DeltaY (loop count)
sub si,bx ;back up the start X by 1, as explained below
mov dx,-1 ;initialize the line error accumulator to -1,
; so that it will turn over immediately and
; advance X to the start X. This is necessary
; properly to bias error sums of 0 to mean
; "advance next time" rather than "advance
; this time," so that the final error sum can
; never cause drawing to overrun the final X
; coordinate (works in conjunction with
; truncating ErrorAdj, to make sure X can't
; overrun)
mov cx,8 ;CL = # of bits by which to shift
sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
; instead of 16 because we work only
; with the high byte of ErrorAcc)
mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
dec ch ; intensity weighting, producing
; result (1 - intensity weighting)
mov bp,BaseColor[bp] ;***stack frame not available***
;***from now on ***
xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
; AH = scratch register
; Draw all remaining pixels.
YMajorLoop:
add dx,bp ;calculate error for next pixel
jnc NoXAdvance ;not time to step in X yet
;the error accumulator turned over,
; so advance the X coord
add si,bx ;add XDir to the pixel pointer
NoXAdvance:
add si,SCREEN_WIDTH_IN_BYTES ;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.
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
add ah,al ;BaseColor + Weighting
mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
xor ah,ch ;Weighting ^ WeightingComplementMask
add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
mov [si+bx],ah ;DrawPixel(X+XDir, Y,
; BaseColor + (Weighting ^ WeightingComplementMask));
dec di ;--DeltaY
jnz YMajorLoop
jmp Done ;we're done with this line
; It's an X-major line.
align 2
XMajor:
; Calculate the 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.
sub ax,ax ;make DeltaY 16.16 fixed-point value in DX:AX
div cx ;AX = (DeltaY << 16) / Deltax. Won't overflow
; because DeltaY < DeltaX
mov di,cx ;DI = DeltaX (loop count)
sub si,SCREEN_WIDTH_IN_BYTES ;back up the start X by 1, as
; explained below
mov dx,-1 ;initialize the line error accumulator to -1,
; so that it will turn over immediately and
; advance Y to the start Y. This is necessary
; properly to bias error sums of 0 to mean
; "advance next time" rather than "advance
; this time," so that the final error sum can
; never cause drawing to overrun the final Y
; coordinate (works in conjunction with
; truncating ErrorAdj, to make sure Y can't
; overrun)
mov cx,8 ;CL = # of bits by which to shift
sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
; instead of 16 because we work only
; with the high byte of ErrorAcc)
mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
dec ch ; intensity weighting, producing
; result (1 - intensity weighting)
mov bp,BaseColor[bp] ;***stack frame not available***
;***from now on ***
xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
; AH = scratch register
; Draw all remaining pixels.
XMajorLoop:
add dx,bp ;calculate error for next pixel
jnc NoYAdvance ;not time to step in Y yet
;the error accumulator turned over,
; so advance the Y coord
add si,SCREEN_WIDTH_IN_BYTES ;advance Y
NoYAdvance:
add si,bx ;X-major, so add XDir to the pixel pointer
; 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.
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
add ah,al ;BaseColor + Weighting
mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
xor ah,ch ;Weighting ^ WeightingComplementMask
add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
mov [si+SCREEN_WIDTH_IN_BYTES],ah
;DrawPixel(X, Y+SCREEN_WIDTH_IN_BYTES,
; BaseColor + (Weighting ^ WeightingComplementMask));
dec di ;--DeltaX
jnz XMajorLoop
Done: ;we're done with this line
pop ds ;restore C's default data segment
pop di ;restore C's register variables
pop si
pop bp ;restore caller's stack frame
ret ;done
_DrawWuLine endp
end