Previous | Table of Contents | Next

LISTING 42.4 L42-4.C

    /* 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);
    }

Listing 42.1 isn’t particularly fast, because it calls DrawPixel() for each pixel. On the other hand, DrawPixel() 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 640×480 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 640×480. Although antialiased lines look much smoother than normal lines at 320×200 resolution, they’re far from perfect, because the pixels are so big that the eye can’t blend them properly. At 640×480, 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.

LISTING 42.5 L42-5.C

    /* 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);
    }

Listing 42.1 requires that the DAC palette be set up so that a NumLevel-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.

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!

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash