home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / source / chaptere / le-1.c next >
Encoding:
C/C++ Source or Header  |  1997-06-18  |  10.3 KB  |  266 lines

  1. /* *** Listing 20.1 ***
  2.  *
  3.  * Draws an ellipse of the specified X and Y axis radii and color,
  4.  * using a fast integer-only & square-root-free approach, and
  5.  * generating the arc for one octant into a buffer, drawing four
  6.  * symmetries from that buffer, then doing the same for the other
  7.  * axis.
  8.  * VGA or EGA.
  9.  * Compiled with Borland C++ 4.02.  Link with L20-2.C.
  10.  * Checked by Jim Mischel 11/30/94
  11.  */
  12. #include <dos.h>
  13.  
  14. /* Handle differences between Turbo C & MSC. Note that Turbo C accepts
  15.    outp as a synonym for outportb, but not outpw for outport */
  16. #ifdef __TURBOC__
  17. #define outpw  outport
  18. #endif
  19.  
  20. #define SCREEN_WIDTH_IN_BYTES 80       /* # of bytes across one scan
  21.                                    line in mode 12h */
  22. #define SCREEN_SEGMENT        0xA000      /* mode 12h display memory seg */
  23. #define GC_INDEX              0x3CE    /* Graphics Controller port */
  24. #define SET_RESET_INDEX       0        /* Set/Reset reg index in GC */
  25. #define SET_RESET_ENABLE_INDEX 1        /* Set/Reset Enable reg index
  26.                                              in GC */
  27. #define BIT_MASK_INDEX        8         /* Bit Mask reg index in GC */
  28.  
  29. unsigned char PixList[SCREEN_WIDTH_IN_BYTES*8/2];
  30.                                     /* maximum major axis length is
  31.                                        1/2 screen width, because we're
  32.                                        assuming no clipping is needed */
  33.  
  34. /* Draws the arc for an octant in which Y is the major axis. (X,Y) is the
  35.    starting point of the arc. HorizontalMoveDirection selects whether the
  36.    arc advances to the left or right horizontally (0=left, 1=right).
  37.    RowOffset contains the offset in bytes from one scan line to the next,
  38.    controlling whether the arc is drawn up or down. Length is the
  39.    vertical length in pixels of the arc, and DrawList is a list
  40.    containing 0 for each point if the next point is vertically aligned,
  41.    and 1 if the next point is 1 pixel diagonally to the left or right */
  42. void DrawVOctant(int X, int Y, int Length, int RowOffset,
  43.    int HorizontalMoveDirection, unsigned char *DrawList)
  44. {
  45.    unsigned char far *ScreenPtr, BitMask;
  46.  
  47.    /* Point to the byte the initial pixel is in */
  48. #ifdef __TURBOC__
  49.    ScreenPtr = MK_FP(SCREEN_SEGMENT,
  50.       (Y * SCREEN_WIDTH_IN_BYTES) + (X / 8));
  51. #else
  52.    FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
  53.    FP_OFF(ScreenPtr) =(Y * SCREEN_WIDTH_IN_BYTES) + (X / 8);
  54. #endif
  55.    /* Set the initial bit mask */
  56.    BitMask = 0x80 >> (X & 0x07);
  57.  
  58.    /* Draw all points in DrawList */
  59.    while ( Length-- ) {
  60.       /* Set the bit mask for the pixel */
  61.       outp(GC_INDEX + 1, BitMask);
  62.       /* Draw the pixel. ORed to force read/write to load latches.
  63.          Data written doesn't matter, because set/reset is enabled
  64.          for all planes. Note: don't OR with 0; MSC optimizes that
  65.          statement to no operation */
  66.       *ScreenPtr |= 0xFE;
  67.       /* Now advance to the next pixel based on DrawList */
  68.       if ( *DrawList++ ) {
  69.          /* Advance horizontally to produce a diagonal move. Rotate
  70.             the bit mask, advancing one byte horizontally if the bit
  71.             mask wraps */
  72.          if ( HorizontalMoveDirection == 1 ) {
  73.             /* Move right */
  74.             if ( (BitMask >>= 1) == 0 ) {
  75.                BitMask = 0x80;   /* wrap the mask */
  76.                ScreenPtr++;      /* advance 1 byte to the right */
  77.             }
  78.          } else {
  79.             /* Move left */
  80.             if ( (BitMask <<= 1) == 0 ) {
  81.                BitMask = 0x01;   /* wrap the mask */
  82.                ScreenPtr--;      /* advance 1 byte to the left */
  83.             }
  84.          }
  85.       }
  86.       ScreenPtr += RowOffset; /* advance to the next scan line */
  87.    }
  88. }
  89.  
  90. /* Draws the arc for an octant in which X is the major axis. (X,Y) is the
  91.    starting point of the arc. HorizontalMoveDirection selects whether the
  92.    arc advances to the left or right horizontally (0=left, 1=right).
  93.    RowOffset contains the offset in bytes from one scan line to the next,
  94.    controlling whether the arc is drawn up or down. Length is the
  95.    horizontal length in pixels of the arc, and DrawList is a list
  96.    containing 0 for each point if the next point is horizontally aligned,
  97.    and 1 if the next point is 1 pixel above or below diagonally */
  98. void DrawHOctant(int X, int Y, int Length, int RowOffset,
  99.    int HorizontalMoveDirection, unsigned char *DrawList)
  100. {
  101.    unsigned char far *ScreenPtr, BitMask;
  102.  
  103.    /* Point to the byte the initial pixel is in */
  104. #ifdef __TURBOC__
  105.    ScreenPtr = MK_FP(SCREEN_SEGMENT,
  106.       (Y * SCREEN_WIDTH_IN_BYTES) + (X / 8));
  107. #else
  108.    FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
  109.    FP_OFF(ScreenPtr) =(Y * SCREEN_WIDTH_IN_BYTES) + (X / 8);
  110. #endif
  111.    /* Set the initial bit mask */
  112.    BitMask = 0x80 >> (X & 0x07);
  113.  
  114.    /* Draw all points in DrawList */
  115.    while ( Length-- ) {
  116.       /* Set the bit mask for the pixel */
  117.       outp(GC_INDEX + 1, BitMask);
  118.       /* Draw the pixel (see comments above for details) */
  119.       *ScreenPtr |= 0xFE;
  120.       /* Now advance to the next pixel based on DrawList */
  121.       if ( *DrawList++ ) {
  122.          /* Advance vertically to produce a diagonal move */
  123.          ScreenPtr += RowOffset; /* advance to the next scan line */
  124.       }
  125.       /* Advance horizontally. Rotate the bit mask, advancing one
  126.          byte horizontally if the bit mask wraps */
  127.       if ( HorizontalMoveDirection == 1 ) {
  128.          /* Move right */
  129.          if ( (BitMask >>= 1) == 0 ) {
  130.             BitMask = 0x80;   /* wrap the mask */
  131.             ScreenPtr++;      /* advance 1 byte to the right */
  132.          }
  133.       } else {
  134.          /* Move left */
  135.          if ( (BitMask <<= 1) == 0 ) {
  136.             BitMask = 0x01;   /* wrap the mask */
  137.             ScreenPtr--;      /* advance 1 byte to the left */
  138.          }
  139.       }
  140.    }
  141. }
  142.  
  143. /* Draws an ellipse of X axis radius A and Y axis radius B in
  144.  * color Color centered at screen coordinate (X,Y). Radii must
  145.  * both be non-zero */
  146. void DrawEllipse(int X, int Y, int A, int B, int Color) {
  147.    int WorkingX, WorkingY;
  148.    long Threshold;
  149.    long ASquared = (long) A * A;
  150.    long BSquared = (long) B * B;
  151.    long XAdjust, YAdjust;
  152.    unsigned char *PixListPtr;
  153.  
  154.    /* Set drawing color via set/reset */
  155.    outpw(GC_INDEX, (0x0F << 8) | SET_RESET_ENABLE_INDEX);
  156.                                  /* enable set/reset for all planes */
  157.    outpw(GC_INDEX, (Color << 8) | SET_RESET_INDEX);
  158.                                  /* set set/reset (drawing) color */
  159.    outp(GC_INDEX, BIT_MASK_INDEX); /* leave the GC Index reg pointing
  160.                                        to the Bit Mask reg */
  161.  
  162.    /* Draw the four symmetric arcs for which X advances faster (that is,
  163.       for which X is the major axis) */
  164.  
  165.    /* Draw the four arcs; set draw parameters for initial point (0,B) */
  166.    /* Calculate all points along an arc of 1/8th of the ellipse and
  167.       store that info in PixList for later drawing */
  168.    PixListPtr = PixList;
  169.    WorkingX = 0;
  170.    XAdjust = 0;
  171.    YAdjust = ASquared * 2 * B;
  172.    Threshold = ASquared / 4 - ASquared * B;
  173.  
  174.    for (;;) {
  175.       /* Advance the threshold to the value for the next X point
  176.          to be drawn */
  177.       Threshold += XAdjust + BSquared;
  178.  
  179.       /* If the threshold has passed 0, then the Y coordinate has
  180.          advanced more than halfway to the next pixel and it's time
  181.          to advance the Y coordinate by 1 and set the next threshold
  182.          accordingly */
  183.       if ( Threshold >= 0 ) {
  184.          YAdjust -= ASquared * 2;
  185.          Threshold -= YAdjust;
  186.          *PixListPtr++ = 1;   /* advance along both axes */
  187.       } else {
  188.          *PixListPtr++ = 0;   /* advance only along the X axis */
  189.       }
  190.  
  191.       /* Advance the X coordinate by 1 */
  192.       XAdjust += BSquared * 2;
  193.       WorkingX++;
  194.  
  195.       /* Stop if X is no longer the major axis (the arc has passed the
  196.          45-degree point) */
  197.       if ( XAdjust >= YAdjust )
  198.          break;
  199.    }
  200.  
  201.    /* Now draw each of 4 symmetries of the octant in turn, the
  202.       octants for which X is the major axis. Adjust every other arc
  203.       so that there's no overlap */
  204.    DrawHOctant(X,Y-B,WorkingX,SCREEN_WIDTH_IN_BYTES,0,PixList);
  205.    DrawHOctant(X+1,Y-B+(*PixList),WorkingX-1,SCREEN_WIDTH_IN_BYTES,1,
  206.       PixList+1);
  207.    DrawHOctant(X,Y+B,WorkingX,-SCREEN_WIDTH_IN_BYTES,0,PixList);
  208.    DrawHOctant(X+1,Y+B-(*PixList),WorkingX-1,-SCREEN_WIDTH_IN_BYTES,1,
  209.       PixList+1);
  210.  
  211.    /* Draw the four symmetric arcs for which X advances faster (that is,
  212.       for which Y is the major axis) */
  213.  
  214.    /* Draw the four arcs; set draw parameters for initial point (A,0) */
  215.    /* Calculate all points along an arc of 1/8th of the ellipse and
  216.       store that info in PixList for later drawing */
  217.    PixListPtr = PixList;
  218.    WorkingY = 0;
  219.    XAdjust = BSquared * 2 * A;
  220.    YAdjust = 0;
  221.    Threshold = BSquared / 4 - BSquared * A;
  222.  
  223.    for (;;) {
  224.       /* Advance the threshold to the value for the next Y point
  225.          to be drawn */
  226.       Threshold += YAdjust + ASquared;
  227.  
  228.       /* If the threshold has passed 0, then the X coordinate has
  229.          advanced more than halfway to the next pixel and it's time
  230.          to advance the X coordinate by 1 and set the next threhold
  231.          accordingly */
  232.       if ( Threshold >= 0 ) {
  233.          XAdjust -= BSquared * 2;
  234.          Threshold = Threshold - XAdjust;
  235.          *PixListPtr++ = 1;   /* advance along both axes */
  236.       } else {
  237.          *PixListPtr++ = 0;   /* advance only along the X axis */
  238.       }
  239.  
  240.       /* Advance the Y coordinate by 1 */
  241.       YAdjust += ASquared * 2;
  242.       WorkingY++;
  243.  
  244.       /* Stop if Y is no longer the major axis (the arc has passed the
  245.          45-degree point) */
  246.       if ( YAdjust > XAdjust )
  247.          break;
  248.    }
  249.  
  250.    /* Now draw each of 4 symmetries of the octant in turn, the
  251.       octants for which Y is the major axis. Adjust every other arc
  252.       so that there's no overlap */
  253.    DrawVOctant(X-A,Y,WorkingY,-SCREEN_WIDTH_IN_BYTES,1,PixList);
  254.    DrawVOctant(X-A+(*PixList),Y+1,WorkingY-1,SCREEN_WIDTH_IN_BYTES,1,
  255.       PixList+1);
  256.    DrawVOctant(X+A,Y,WorkingY,-SCREEN_WIDTH_IN_BYTES,0,PixList);
  257.    DrawVOctant(X+A-(*PixList),Y+1,WorkingY-1,SCREEN_WIDTH_IN_BYTES,0,
  258.       PixList+1);
  259.  
  260.    /* Reset the Bit Mask register to normal */
  261.    outp(GC_INDEX + 1, 0xFF);
  262.    /* Turn off set/reset enable */
  263.    outpw(GC_INDEX, (0x00 << 8) | SET_RESET_ENABLE_INDEX);
  264. }
  265.  
  266.