home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1992 / 06 / grphprog.asc < prev    next >
Text File  |  1992-05-18  |  29KB  |  722 lines

  1. _GRAPHICS PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. /* Function to draw an antialiased line from (X0,Y0) to (X1,Y1), using an
  8.  * antialiasing approach published by Xiaolin Wu in the July 1991 issue of
  9.  * Computer Graphics. Requires that the palette be set up so that there
  10.  * are NumLevels intensity levels of the desired drawing color, starting at
  11.  * color BaseColor (100% intensity) and followed by (NumLevels-1) levels of
  12.  * evenly decreasing intensity, with color (BaseColor+NumLevels-1) being 0%
  13.  * intensity of the desired drawing color (black). This code is suitable for
  14.  * use at screen resolutions, with lines typically no more than 1K long; for
  15.  * longer lines, 32-bit error arithmetic must be used to avoid problems with
  16.  * fixed-point inaccuracy. No clipping is performed in DrawWuLine; it must be
  17.  * performed either at a higher level or in the DrawPixel function.
  18.  * Tested with Borland C++ 3.0 in C compilation mode and the small model.
  19.  */
  20. extern void DrawPixel(int, int, int);
  21.  
  22. /* Wu antialiased line drawer.
  23.  * (X0,Y0),(X1,Y1) = line to draw
  24.  * BaseColor = color # of first color in block used for antialiasing, the
  25.  *          100% intensity version of the drawing color
  26.  * NumLevels = size of color block, with BaseColor+NumLevels-1 being the
  27.  *          0% intensity version of the drawing color
  28.  * IntensityBits = log base 2 of NumLevels; the # of bits used to describe
  29.  *          the intensity of the drawing color. 2**IntensityBits==NumLevels
  30.  */
  31. void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels,
  32.    unsigned int IntensityBits)
  33. {
  34.    unsigned int IntensityShift, ErrorAdj, ErrorAcc;
  35.    unsigned int ErrorAccTemp, Weighting, WeightingComplementMask;
  36.    int DeltaX, DeltaY, Temp, XDir;
  37.  
  38.    /* Make sure the line runs top to bottom */
  39.    if (Y0 > Y1) {
  40.       Temp = Y0; Y0 = Y1; Y1 = Temp;
  41.       Temp = X0; X0 = X1; X1 = Temp;
  42.    }
  43.    /* Draw the initial pixel, which is always exactly intersected by
  44.       the line and so needs no weighting */
  45.    DrawPixel(X0, Y0, BaseColor);
  46.  
  47.    if ((DeltaX = X1 - X0) >= 0) {
  48.       XDir = 1;
  49.    } else {
  50.       XDir = -1;
  51.       DeltaX = -DeltaX; /* make DeltaX positive */
  52.    }
  53.    /* Special-case horizontal, vertical, and diagonal lines, which
  54.       require no weighting because they go right through the center of
  55.       every pixel */
  56.    if ((DeltaY = Y1 - Y0) == 0) {
  57.       /* Horizontal line */
  58.       while (DeltaX-- != 0) {
  59.          X0 += XDir;
  60.          DrawPixel(X0, Y0, BaseColor);
  61.       }
  62.       return;
  63.    }
  64.    if (DeltaX == 0) {
  65.       /* Vertical line */
  66.       do {
  67.          Y0++;
  68.          DrawPixel(X0, Y0, BaseColor);
  69.       } while (--DeltaY != 0);
  70.       return;
  71.    }
  72.    if (DeltaX == DeltaY) {
  73.       /* Diagonal line */
  74.       do {
  75.          X0 += XDir;
  76.          Y0++;
  77.          DrawPixel(X0, Y0, BaseColor);
  78.       } while (--DeltaY != 0);
  79.       return;
  80.    }
  81.    /* Line is not horizontal, diagonal, or vertical */
  82.    ErrorAcc = 0;  /* initialize the line error accumulator to 0 */
  83.    /* # of bits by which to shift ErrorAcc to get intensity level */
  84.    IntensityShift = 16 - IntensityBits;
  85.    /* Mask used to flip all bits in an intensity weighting, producing the
  86.       result (1 - intensity weighting) */
  87.    WeightingComplementMask = NumLevels - 1;
  88.    /* Is this an X-major or Y-major line? */
  89.    if (DeltaY > DeltaX) {
  90.       /* Y-major line; calculate 16-bit fixed-point fractional part of a
  91.          pixel that X advances each time Y advances 1 pixel, truncating the
  92.          result so that we won't overrun the endpoint along the X axis */
  93.       ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY;
  94.       /* Draw all pixels other than the first and last */
  95.       while (--DeltaY) {
  96.          ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
  97.          ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
  98.          if (ErrorAcc <= ErrorAccTemp) {
  99.             /* The error accumulator turned over, so advance the X coord */
  100.             X0 += XDir;
  101.          }
  102.          Y0++; /* Y-major, so always advance Y */
  103.          /* The IntensityBits most significant bits of ErrorAcc give us the
  104.             intensity weighting for this pixel, and the complement of the
  105.             weighting for the paired pixel */
  106.          Weighting = ErrorAcc >> IntensityShift;
  107.          DrawPixel(X0, Y0, BaseColor + Weighting);
  108.          DrawPixel(X0 + XDir, Y0,
  109.                BaseColor + (Weighting ^ WeightingComplementMask));
  110.       }
  111.       /* Draw the final pixel, which is always exactly intersected by the line
  112.          and so needs no weighting */
  113.       DrawPixel(X1, Y1, BaseColor);
  114.       return;
  115.    }
  116.    /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
  117.       pixel that Y advances each time X advances 1 pixel, truncating the
  118.       result to avoid overrunning the endpoint along the X axis */
  119.    ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX;
  120.    /* Draw all pixels other than the first and last */
  121.    while (--DeltaX) {
  122.       ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
  123.       ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
  124.       if (ErrorAcc <= ErrorAccTemp) {
  125.          /* The error accumulator turned over, so advance the Y coord */
  126.          Y0++;
  127.       }
  128.       X0 += XDir; /* X-major, so always advance X */
  129.       /* The IntensityBits most significant bits of ErrorAcc give us the
  130.          intensity weighting for this pixel, and the complement of the
  131.          weighting for the paired pixel */
  132.       Weighting = ErrorAcc >> IntensityShift;
  133.       DrawPixel(X0, Y0, BaseColor + Weighting);
  134.       DrawPixel(X0, Y0 + 1,
  135.             BaseColor + (Weighting ^ WeightingComplementMask));
  136.    }
  137.    /* Draw the final pixel, which is always exactly intersected by the line
  138.       and so needs no weighting */
  139.    DrawPixel(X1, Y1, BaseColor);
  140. }
  141.  
  142.  
  143. [LISTING TWO]
  144. /* Sample line-drawing program to demonstrate Wu antialiasing. Also draws
  145.  * non-antialiased lines for comparison.
  146.  * Tested with Borland C++ 3.0 in C compilation mode and the small model.
  147.  */
  148. #include <dos.h>
  149. #include <conio.h>
  150.  
  151. void SetPalette(struct WuColor *);
  152. extern void DrawWuLine(int, int, int, int, int, int, unsigned int);
  153. extern void DrawLine(int, int, int, int, int);
  154. extern void SetMode(void);
  155. extern int ScreenWidthInPixels;  /* screen dimension globals */
  156. extern int ScreenHeightInPixels;
  157.  
  158. #define NUM_WU_COLORS 2 /* # of colors we'll do antialiased drawing with */
  159. struct WuColor {        /* describes one color used for antialiasing */
  160.    int BaseColor;       /* # of start of palette intensity block in DAC */
  161.    int NumLevels;       /* # of intensity levels */
  162.    int IntensityBits;   /* IntensityBits == log2 NumLevels */
  163.    int MaxRed;          /* red component of color at full intensity */
  164.    int MaxGreen;        /* green component of color at full intensity */
  165.    int MaxBlue;         /* blue component of color at full intensity */
  166. };
  167. enum {WU_BLUE=0, WU_WHITE=1};             /* drawing colors */
  168. struct WuColor WuColors[NUM_WU_COLORS] =  /* blue and white */
  169.     {{192, 32, 5, 0, 0, 0x3F}, {224, 32, 5, 0x3F, 0x3F, 0x3F}};
  170.  
  171. void main()
  172. {
  173.    int CurrentColor, i;
  174.    union REGS regset;
  175.  
  176.    /* Draw Wu-antialiased lines in all directions */
  177.    SetMode();
  178.    SetPalette(WuColors);
  179.    for (i=5; i<ScreenWidthInPixels; i += 10) {
  180.       DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
  181.             ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
  182.             WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
  183.             WuColors[WU_BLUE].IntensityBits);
  184.    }
  185.    for (i=0; i<ScreenHeightInPixels; i += 10) {
  186.       DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
  187.             WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
  188.             WuColors[WU_BLUE].IntensityBits);
  189.    }
  190.    for (i=0; i<ScreenHeightInPixels; i += 10) {
  191.       DrawWuLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
  192.             ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor,
  193.             WuColors[WU_BLUE].NumLevels, WuColors[WU_BLUE].IntensityBits);
  194.    }
  195.    for (i=0; i<ScreenWidthInPixels; i += 10) {
  196.       DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
  197.             ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor,
  198.             WuColors[WU_WHITE].NumLevels,
  199.             WuColors[WU_WHITE].IntensityBits);
  200.    }
  201.    getch();                /* wait for a key press */
  202.  
  203.    /* Now clear the screen and draw non-antialiased lines */
  204.    SetMode();
  205.    SetPalette(WuColors);
  206.    for (i=0; i<ScreenWidthInPixels; i += 10) {
  207.       DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
  208.             ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
  209.             WuColors[WU_BLUE].BaseColor);
  210.    }
  211.    for (i=0; i<ScreenHeightInPixels; i += 10) {
  212.       DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
  213.             WuColors[WU_BLUE].BaseColor);
  214.    }
  215.    for (i=0; i<ScreenHeightInPixels; i += 10) {
  216.       DrawLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
  217.             ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor);
  218.    }
  219.    for (i=0; i<ScreenWidthInPixels; i += 10) {
  220.       DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
  221.             ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor);
  222.    }
  223.    getch();                /* wait for a key press */
  224.  
  225.    regset.x.ax = 0x0003;   /* AL = 3 selects 80x25 text mode */
  226.    int86(0x10, ®set, ®set);   /* return to text mode */
  227. }
  228.  
  229. /* Sets up the palette for antialiasing with the specified colors.
  230.  * Intensity steps for each color are scaled from the full desired intensity
  231.  * of the red, green, and blue components for that color down to 0%
  232.  * intensity; each step is rounded to the nearest integer. Colors are
  233.  * corrected for a gamma of 2.3. The values that the palette is programmed
  234.  * with are hardwired for the VGA's 6 bit per color DAC.
  235.  */
  236. void SetPalette(struct WuColor * WColors)
  237. {
  238.    int i, j;
  239.    union REGS regset;
  240.    struct SREGS sregset;
  241.    static unsigned char PaletteBlock[256][3];   /* 256 RGB entries */
  242.    /* Gamma-corrected DAC color components for 64 linear levels from 0% to
  243.       100% intensity */
  244.    static unsigned char GammaTable[] = {
  245.       0, 10, 14, 17, 19, 21, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34,
  246.       35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46,
  247.       47, 48, 48, 49, 49, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55,
  248.       56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63};
  249.  
  250.    for (i=0; i<NUM_WU_COLORS; i++) {
  251.       for (j=0; j<WColors[i].NumLevels; j++) {
  252.          PaletteBlock[j][0] = GammaTable[((double)WColors[i].MaxRed * (1.0 -
  253.                (double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
  254.          PaletteBlock[j][1] = GammaTable[((double)WColors[i].MaxGreen * (1.0 -
  255.                (double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
  256.          PaletteBlock[j][2] = GammaTable[((double)WColors[i].MaxBlue * (1.0 -
  257.                (double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
  258.       }
  259.       /* Now set up the palette to do Wu antialiasing for this color */
  260.       regset.x.ax = 0x1012;   /* set block of DAC registers function */
  261.       regset.x.bx = WColors[i].BaseColor;   /* first DAC location to load */
  262.       regset.x.cx = WColors[i].NumLevels;   /* # of DAC locations to load */
  263.       regset.x.dx = (unsigned int)PaletteBlock; /* offset of array from which
  264.                                                    to load RGB settings */
  265.       sregset.es = _DS; /* segment of array from which to load settings */
  266.       int86x(0x10, ®set, ®set, &sregset); /* load the palette block */
  267.    }
  268. }
  269.  
  270.  
  271. [LISTING THREE]
  272.  
  273.  
  274. /* VGA mode 13h pixel-drawing and mode set functions.
  275.  * Tested with Borland C++ 3.0 in C compilation mode and the small model.
  276.  */
  277. #include <dos.h>
  278.  
  279. /* Screen dimension globals, used in main program to scale. */
  280. int ScreenWidthInPixels = 320;
  281. int ScreenHeightInPixels = 200;
  282.  
  283. /* Mode 13h draw pixel function. */
  284. void DrawPixel(int X, int Y, int Color)
  285. {
  286. #define SCREEN_SEGMENT  0xA000
  287.    unsigned char far *ScreenPtr;
  288.  
  289.    FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
  290.    FP_OFF(ScreenPtr) = (unsigned int) Y * ScreenWidthInPixels + X;
  291.    *ScreenPtr = Color;
  292. }
  293.  
  294. /* Mode 13h mode-set function. */
  295. void SetMode()
  296. {
  297.    union REGS regset;
  298.  
  299.    /* Set to 320x200 256-color graphics mode */
  300.    regset.x.ax = 0x0013;
  301.    int86(0x10, ®set, ®set);
  302. }
  303.  
  304.  
  305. [LISTING FOUR]
  306.  
  307. /* Function to draw a non-antialiased line from (X0,Y0) to (X1,Y1), using a
  308.  * simple fixed-point error accumulation approach.
  309.  * Tested with Borland C++ 3.0 in C compilation mode and the small model.
  310.  */
  311. extern void DrawPixel(int, int, int);
  312.  
  313. /* Non-antialiased line drawer.
  314.  * (X0,Y0),(X1,Y1) = line to draw, Color = color in which to draw
  315.  */
  316. void DrawLine(int X0, int Y0, int X1, int Y1, int Color)
  317. {
  318.    unsigned long ErrorAcc, ErrorAdj;
  319.    int DeltaX, DeltaY, XDir, Temp;
  320.  
  321.    /* Make sure the line runs top to bottom */
  322.    if (Y0 > Y1) {
  323.       Temp = Y0; Y0 = Y1; Y1 = Temp;
  324.       Temp = X0; X0 = X1; X1 = Temp;
  325.    }
  326.    DrawPixel(X0, Y0, Color);  /* draw the initial pixel */
  327.    if ((DeltaX = X1 - X0) >= 0) {
  328.       XDir = 1;
  329.    } else {
  330.       XDir = -1;
  331.       DeltaX = -DeltaX; /* make DeltaX positive */
  332.    }
  333.    if ((DeltaY = Y1 - Y0) == 0)  /* done if only one point in the line */
  334.       if (DeltaX == 0) return;
  335.  
  336.    ErrorAcc = 0x8000;   /* initialize line error accumulator to .5, so we can
  337.                            advance when we get halfway to the next pixel */
  338.    /* Is this an X-major or Y-major line? */
  339.    if (DeltaY > DeltaX) {
  340.       /* Y-major line; calculate 16-bit fixed-point fractional part of a
  341.          pixel that X advances each time Y advances 1 pixel */
  342.       ErrorAdj = ((((unsigned long)DeltaX << 17) / (unsigned long)DeltaY) +
  343.             1) >> 1;
  344.       /* Draw all pixels between the first and last */
  345.       do {
  346.          ErrorAcc += ErrorAdj;      /* calculate error for this pixel */
  347.          if (ErrorAcc & ~0xFFFFL) {
  348.             /* The error accumulator turned over, so advance the X coord */
  349.             X0 += XDir;
  350.             ErrorAcc &= 0xFFFFL;    /* clear integer part of result */
  351.          }
  352.          Y0++;                      /* Y-major, so always advance Y */
  353.          DrawPixel(X0, Y0, Color);
  354.       } while (--DeltaY);
  355.       return;
  356.    }
  357.    /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
  358.       pixel that Y advances each time X advances 1 pixel */
  359.    ErrorAdj = ((((unsigned long)DeltaY << 17) / (unsigned long)DeltaX) +
  360.          1) >> 1;
  361.    /* Draw all remaining pixels */
  362.    do {
  363.       ErrorAcc += ErrorAdj;      /* calculate error for this pixel */
  364.       if (ErrorAcc & ~0xFFFFL) {
  365.          /* The error accumulator turned over, so advance the Y coord */
  366.          Y0++;
  367.          ErrorAcc &= 0xFFFFL;    /* clear integer part of result */
  368.       }
  369.       X0 += XDir;                /* X-major, so always advance X */
  370.       DrawPixel(X0, Y0, Color);
  371.    } while (--DeltaX);
  372. }
  373.  
  374.  
  375. [LISTING FIVE]
  376.  
  377. /* Mode set and pixel-drawing functions for the 640x480 256-color mode of
  378.  * Tseng Labs ET4000-based SuperVGAs.
  379.  * Tested with Borland C++ 3.0 in C compilation mode and the small model.
  380.  */
  381. #include <dos.h>
  382.  
  383. /* Screen dimension globals, used in main program to scale */
  384. int ScreenWidthInPixels = 640;
  385. int ScreenHeightInPixels = 480;
  386.  
  387. /* ET4000 640x480 256-color draw pixel function. */
  388. void DrawPixel(int X, int Y, int Color)
  389. {
  390. #define SCREEN_SEGMENT        0xA000
  391. #define GC_SEGMENT_SELECT     0x3CD /* ET4000 segment (bank) select reg */
  392.    unsigned char far *ScreenPtr;
  393.    unsigned int Bank;
  394.    unsigned long BitmapAddress;
  395.  
  396.    /* Full bitmap address of pixel, as measured from address 0 to 0xFFFFF */
  397.    BitmapAddress = (unsigned long) Y * ScreenWidthInPixels + X;
  398.    /* Bank # is upper word of bitmap addr */
  399.    Bank = BitmapAddress >> 16;
  400.    /* Upper nibble is read bank #, lower nibble is write bank # */
  401.    outp(GC_SEGMENT_SELECT, (Bank << 4) | Bank);
  402.    /* Draw into the bank */
  403.    FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
  404.    FP_OFF(ScreenPtr) = (unsigned int) BitmapAddress;
  405.    *ScreenPtr = Color;
  406. }
  407.  
  408. /* ET4000 640x480 256-color mode-set function. */
  409. void SetMode()
  410. {
  411.    union REGS regset;
  412.  
  413.    /* Set to 640x480 256-color graphics mode */
  414.    regset.x.ax = 0x002E;
  415.    int86(0x10, ®set, ®set);
  416. }
  417.  
  418.  
  419.  
  420. [LISTING SIX]
  421.  
  422. ; Inner loop for drawing Y-major lines from Wu-antialiased line drawer.
  423. YMajorLoop:
  424.         add     dx,bp                   ;calculate error for next pixel
  425.         jnc     NoXAdvance              ;not time to step in X yet
  426.                                         ;the error accumulator turned over,
  427.                                         ; so advance the X coord
  428.         add     si,bx                   ;add XDir to the pixel pointer
  429. NoXAdvance:
  430.         add     si,SCREEN_WIDTH_IN_BYTES ;Y-major, so always advance Y
  431. ; The IntensityBits most significant bits of ErrorAcc give us the intensity
  432. ; weighting for this pixel, and the complement of the weighting for the
  433. ; paired pixel.
  434.         mov     ah,dh   ;msb of ErrorAcc
  435.         shr     ah,cl   ;Weighting = ErrorAcc >> IntensityShift;
  436.         add     ah,al   ;BaseColor + Weighting
  437.         mov     [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
  438.         mov     ah,dh   ;msb of ErrorAcc
  439.         shr     ah,cl   ;Weighting = ErrorAcc >> IntensityShift;
  440.         xor     ah,ch   ;Weighting ^ WeightingComplementMask
  441.         add     ah,al   ;BaseColor + (Weighting ^ WeightingComplementMask)
  442.         mov     [si+bx],ah ;DrawPixel(X+XDir, Y,
  443.                         ; BaseColor + (Weighting ^ WeightingComplementMask));
  444.         dec     di      ;--DeltaY
  445.         jnz     YMajorLoop
  446.  
  447.  
  448. [WU ANTIALIASING]
  449.  
  450. ; C near-callable function to draw an antialiased line from
  451. ; (X0,Y0) to (X1,Y1), in mode 13h, the VGA's standard 320x200 256-color
  452. ; mode. Uses an antialiasing approach published by Xiaolin Wu in the July
  453. ; 1991 issue of Computer Graphics. Requires that the palette be set up so
  454. ; that there are NumLevels intensity levels of the desired drawing color,
  455. ; starting at color BaseColor (100% intensity) and followed by (NumLevels-1)
  456. ; levels of evenly decreasing intensity, with color (BaseColor+NumLevels-1)
  457. ; being 0% intensity of the desired drawing color (black). No clipping is
  458. ; performed in DrawWuLine. Handles a maximum of 256 intensity levels per
  459. ; antialiased color. This code is suitable for use at screen resolutions,
  460. ; with lines typically no more than 1K long; for longer lines, 32-bit error
  461. ; arithmetic must be used to avoid problems with fixed-point inaccuracy.
  462. ; Tested with TASM 3.0.
  463. ;
  464. ; C near-callable as:
  465. ;    void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor,
  466. ;        int NumLevels, unsigned int IntensityBits);
  467.  
  468. SCREEN_WIDTH_IN_BYTES equ 320    ;# of bytes from the start of one scan line
  469.                 ; to the start of the next
  470. SCREEN_SEGMENT    equ    0a000h    ;segment in which screen memory resides
  471.  
  472. ; Parameters passed in stack frame.
  473. parms    struc
  474.     dw    2 dup (?) ;pushed BP and return address
  475. X0    dw    ?    ;X coordinate of line start point
  476. Y0    dw    ?    ;Y coordinate of line start point
  477. X1    dw    ?    ;X coordinate of line end point
  478. Y1    dw    ?    ;Y coordinate of line end point
  479. BaseColor dw    ?    ;color # of first color in block used for
  480.             ; antialiasing, the 100% intensity version of the
  481.             ; drawing color
  482. NumLevels dw    ?    ;size of color block, with BaseColor+NumLevels-1
  483.             ; being the 0% intensity version of the drawing color
  484.             ; (maximum NumLevels = 256)
  485. IntensityBits dw ?    ;log base 2 of NumLevels; the # of bits used to
  486.             ; describe the intensity of the drawing color.
  487.             ; 2**IntensityBits==NumLevels
  488.             ; (maximum IntensityBits = 8)
  489. parms    ends
  490.  
  491.     .model    small
  492.         .code
  493. ; Screen dimension globals, used in main program to scale.
  494. _ScreenWidthInPixels    dw      320
  495. _ScreenHeightInPixels   dw      200
  496.  
  497.     .code
  498.     public    _DrawWuLine
  499. _DrawWuLine proc near
  500.     push    bp    ;preserve caller's stack frame
  501.     mov    bp,sp    ;point to local stack frame
  502.     push    si    ;preserve C's register variables
  503.     push    di
  504.     push    ds    ;preserve C's default data segment
  505.     cld        ;make string instructions increment their pointers
  506.  
  507. ; Make sure the line runs top to bottom.
  508.     mov    si,[bp].X0
  509.     mov    ax,[bp].Y0
  510.     cmp    ax,[bp].Y1    ;swap endpoints if necessary to ensure that
  511.     jna    NoSwap        ; Y0 <= Y1
  512.     xchg    [bp].Y1,ax
  513.     mov    [bp].Y0,ax
  514.     xchg    [bp].X1,si
  515.     mov    [bp].X0,si
  516. NoSwap:
  517.  
  518. ; Draw the initial pixel, which is always exactly intersected by the line
  519. ; and so needs no weighting.
  520.     mov    dx,SCREEN_SEGMENT
  521.     mov    ds,dx        ;point DS to the screen segment
  522.     mov    dx,SCREEN_WIDTH_IN_BYTES
  523.     mul    dx        ;Y0 * SCREEN_WIDTH_IN_BYTES yields the offset
  524.                 ; of the start of the row start the initial
  525.                 ; pixel is on
  526.     add    si,ax        ;point DS:SI to the initial pixel
  527.     mov    al,byte ptr [bp].BaseColor ;color with which to draw
  528.     mov    [si],al        ;draw the initial pixel
  529.  
  530.     mov    bx,1        ;XDir = 1; assume DeltaX >= 0
  531.     mov    cx,[bp].X1
  532.     sub    cx,[bp].X0    ;DeltaX; is it >= 1?
  533.     jns    DeltaXSet    ;yes, move left->right, all set
  534.                 ;no, move right->left
  535.     neg    cx        ;make DeltaX positive
  536.     neg    bx        ;XDir = -1
  537. DeltaXSet:
  538.  
  539. ; Special-case horizontal, vertical, and diagonal lines, which require no
  540. ; weighting because they go right through the center of every pixel.
  541.     mov    dx,[bp].Y1
  542.     sub    dx,[bp].Y0    ;DeltaY; is it 0?
  543.     jnz    NotHorz        ;no, not horizontal
  544.                 ;yes, is horizontal, special case
  545.     and    bx,bx        ;draw from left->right?
  546.     jns    DoHorz        ;yes, all set
  547.     std            ;no, draw right->left
  548. DoHorz:
  549.     lea    di,[bx+si]    ;point DI to next pixel to draw
  550.     mov    ax,ds
  551.     mov    es,ax        ;point ES:DI to next pixel to draw
  552.     mov    al,byte ptr [bp].BaseColor ;color with which to draw
  553.                 ;CX = DeltaX at this point
  554.     rep    stosb        ;draw the rest of the horizontal line
  555.     cld            ;restore default direction flag
  556.     jmp    Done        ;and we're done
  557.  
  558.     align    2
  559. NotHorz:
  560.     and    cx,cx        ;is DeltaX 0?
  561.     jnz    NotVert        ;no, not a vertical line
  562.                 ;yes, is vertical, special case
  563.     mov    al,byte ptr [bp].BaseColor ;color with which to draw
  564. VertLoop:
  565.     add    si,SCREEN_WIDTH_IN_BYTES ;point to next pixel to draw
  566.     mov    [si],al        ;draw the next pixel
  567.     dec    dx        ;--DeltaY
  568.     jnz    VertLoop
  569.     jmp    Done        ;and we're done
  570.  
  571.     align    2
  572. NotVert:
  573.     cmp    cx,dx        ;DeltaX == DeltaY?
  574.     jnz    NotDiag        ;no, not diagonal
  575.                 ;yes, is diagonal, special case
  576.     mov    al,byte ptr [bp].BaseColor ;color with which to draw
  577. DiagLoop:
  578.     lea    si,[si+SCREEN_WIDTH_IN_BYTES+bx]
  579.                 ;advance to next pixel to draw by
  580.                 ; incrementing Y and adding XDir to X
  581.     mov    [si],al        ;draw the next pixel
  582.     dec    dx        ;--DeltaY
  583.     jnz    DiagLoop
  584.     jmp    Done        ;and we're done
  585.  
  586. ; Line is not horizontal, diagonal, or vertical.
  587.     align    2
  588. NotDiag:
  589. ; Is this an X-major or Y-major line?
  590.     cmp    dx,cx
  591.     jb    XMajor            ;it's X-major
  592.  
  593. ; It's a Y-major line. Calculate the 16-bit fixed-point fractional part of a
  594. ; pixel that X advances each time Y advances 1 pixel, truncating the result
  595. ; to avoid overrunning the endpoint along the X axis.
  596.     xchg    dx,cx        ;DX = DeltaX, CX = DeltaY
  597.     sub    ax,ax        ;make DeltaX 16.16 fixed-point value in DX:AX
  598.     div    cx        ;AX = (DeltaX << 16) / DeltaY. Won't overflow
  599.                 ; because DeltaX < DeltaY
  600.     mov    di,cx        ;DI = DeltaY (loop count)
  601.     sub    si,bx        ;back up the start X by 1, as explained below
  602.     mov    dx,-1        ;initialize the line error accumulator to -1,
  603.                 ; so that it will turn over immediately and
  604.                 ; advance X to the start X. This is necessary
  605.                 ; properly to bias error sums of 0 to mean
  606.                 ; "advance next time" rather than "advance
  607.                 ; this time," so that the final error sum can
  608.                 ; never cause drawing to overrun the final X
  609.                 ; coordinate (works in conjunction with
  610.                 ; truncating ErrorAdj, to make sure X can't
  611.                 ; overrun)
  612.     mov    cx,8            ;CL = # of bits by which to shift
  613.     sub    cx,[bp].IntensityBits    ; ErrorAcc to get intensity level (8
  614.                     ; instead of 16 because we work only
  615.                     ; with the high byte of ErrorAcc)
  616.     mov    ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
  617.     dec    ch               ; intensity weighting, producing
  618.                        ; result (1 - intensity weighting)
  619.     mov    bp,BaseColor[bp]    ;***stack frame not available***
  620.                     ;***from now on              ***
  621.     xchg    bp,ax            ;BP = ErrorAdj, AL = BaseColor,
  622.                     ; AH = scratch register
  623.  
  624. ; Draw all remaining pixels.
  625. YMajorLoop:
  626.     add    dx,bp            ;calculate error for next pixel
  627.         jnc     NoXAdvance              ;not time to step in X yet
  628.                                         ;the error accumulator turned over,
  629.                                         ; so advance the X coord
  630.         add     si,bx                   ;add XDir to the pixel pointer
  631. NoXAdvance:
  632.         add    si,SCREEN_WIDTH_IN_BYTES ;Y-major, so always advance Y
  633.  
  634. ; The IntensityBits most significant bits of ErrorAcc give us the intensity
  635. ; weighting for this pixel, and the complement of the weighting for the
  636. ; paired pixel.
  637.     mov    ah,dh    ;msb of ErrorAcc
  638.     shr    ah,cl    ;Weighting = ErrorAcc >> IntensityShift;
  639.     add    ah,al    ;BaseColor + Weighting
  640.     mov    [si],ah    ;DrawPixel(X, Y, BaseColor + Weighting);
  641.     mov    ah,dh    ;msb of ErrorAcc
  642.     shr    ah,cl    ;Weighting = ErrorAcc >> IntensityShift;
  643.     xor    ah,ch    ;Weighting ^ WeightingComplementMask
  644.     add    ah,al    ;BaseColor + (Weighting ^ WeightingComplementMask)
  645.     mov    [si+bx],ah ;DrawPixel(X+XDir, Y,
  646.             ; BaseColor + (Weighting ^ WeightingComplementMask));
  647.     dec    di    ;--DeltaY
  648.     jnz    YMajorLoop
  649.     jmp    Done    ;we're done with this line
  650.  
  651. ; It's an X-major line.
  652.     align    2
  653. XMajor:
  654. ; Calculate the 16-bit fixed-point fractional part of a pixel that Y advances
  655. ; each time X advances 1 pixel, truncating the result to avoid overrunning
  656. ; the endpoint along the X axis.
  657.     sub    ax,ax        ;make DeltaY 16.16 fixed-point value in DX:AX
  658.     div    cx        ;AX = (DeltaY << 16) / Deltax. Won't overflow
  659.                 ; because DeltaY < DeltaX
  660.     mov    di,cx        ;DI = DeltaX (loop count)
  661.     sub    si,SCREEN_WIDTH_IN_BYTES ;back up the start X by 1, as
  662.                 ; explained below
  663.     mov    dx,-1        ;initialize the line error accumulator to -1,
  664.                 ; so that it will turn over immediately and
  665.                 ; advance Y to the start Y. This is necessary
  666.                 ; properly to bias error sums of 0 to mean
  667.                 ; "advance next time" rather than "advance
  668.                 ; this time," so that the final error sum can
  669.                 ; never cause drawing to overrun the final Y
  670.                 ; coordinate (works in conjunction with
  671.                 ; truncating ErrorAdj, to make sure Y can't
  672.                 ; overrun)
  673.     mov    cx,8            ;CL = # of bits by which to shift
  674.     sub    cx,[bp].IntensityBits    ; ErrorAcc to get intensity level (8
  675.                     ; instead of 16 because we work only
  676.                     ; with the high byte of ErrorAcc)
  677.     mov    ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
  678.     dec    ch               ; intensity weighting, producing
  679.                        ; result (1 - intensity weighting)
  680.     mov    bp,BaseColor[bp]    ;***stack frame not available***
  681.                     ;***from now on              ***
  682.     xchg    bp,ax            ;BP = ErrorAdj, AL = BaseColor,
  683.                     ; AH = scratch register
  684. ; Draw all remaining pixels.
  685. XMajorLoop:
  686.     add    dx,bp            ;calculate error for next pixel
  687.         jnc     NoYAdvance              ;not time to step in Y yet
  688.                                         ;the error accumulator turned over,
  689.                                         ; so advance the Y coord
  690.         add     si,SCREEN_WIDTH_IN_BYTES ;advance Y
  691. NoYAdvance:
  692.         add    si,bx        ;X-major, so add XDir to the pixel pointer
  693.  
  694. ; The IntensityBits most significant bits of ErrorAcc give us the intensity
  695. ; weighting for this pixel, and the complement of the weighting for the
  696. ; paired pixel.
  697.     mov    ah,dh    ;msb of ErrorAcc
  698.     shr    ah,cl    ;Weighting = ErrorAcc >> IntensityShift;
  699.     add    ah,al    ;BaseColor + Weighting
  700.     mov    [si],ah    ;DrawPixel(X, Y, BaseColor + Weighting);
  701.     mov    ah,dh    ;msb of ErrorAcc
  702.     shr    ah,cl    ;Weighting = ErrorAcc >> IntensityShift;
  703.     xor    ah,ch    ;Weighting ^ WeightingComplementMask
  704.     add    ah,al    ;BaseColor + (Weighting ^ WeightingComplementMask)
  705.     mov    [si+SCREEN_WIDTH_IN_BYTES],ah
  706.             ;DrawPixel(X, Y+SCREEN_WIDTH_IN_BYTES,
  707.             ; BaseColor + (Weighting ^ WeightingComplementMask));
  708.     dec    di    ;--DeltaX
  709.     jnz    XMajorLoop
  710.  
  711. Done:                ;we're done with this line
  712.     pop    ds    ;restore C's default data segment
  713.     pop    di    ;restore C's register variables
  714.     pop    si
  715.     pop    bp    ;restore caller's stack frame
  716.     ret        ;done
  717. _DrawWuLine endp
  718.     end
  719.  
  720.  
  721.  
  722.