home *** CD-ROM | disk | FTP | other *** search
/ Graphics Programming Black Book (Special Edition) / BlackBook.bin / disk1 / source / chapterc / lc-1.c next >
C/C++ Source or Header  |  1997-06-18  |  9KB  |  212 lines

  1. /* *** Listing 18.1 ***
  2.  *
  3.  * Draws a circle of the specified radius and color, using a fast
  4.  * integer-only & square-root-free approach, and generating the
  5.  * arc for one octant into a buffer, then drawing all 8 symmetries
  6.  * from that buffer.
  7.  * Will work on VGA or EGA, but will draw what appears to be an
  8.  * ellipse in non-square-pixel modes.
  9.  * Compiled with Borland C++ 4.02.  Link with L18-2.C.
  10.  * Checked by Jim Mischel 11/30/94
  11.  */
  12.  
  13. #include <dos.h>
  14.  
  15. /* Handle differences between Turbo C & MSC. Note that Turbo C accepts
  16.    outp as a synonym for outportb, but not outpw for outport */
  17. #ifdef __TURBOC__
  18. #define outpw  outport
  19. #endif
  20.  
  21. #define SCREEN_WIDTH_IN_BYTES 80        /* # of bytes across one scan
  22.                                              line in mode 12h */
  23. #define SCREEN_SEGMENT        0xA000     /* mode 12h display memory seg */
  24. #define GC_INDEX              0x3CE     /* Graphics Controller port */
  25. #define SET_RESET_INDEX       0         /* Set/Reset reg index in GC */
  26. #define SET_RESET_ENABLE_INDEX 1        /* Set/Reset Enable reg index
  27.                                              in GC */
  28. #define BIT_MASK_INDEX        8         /* Bit Mask reg index in GC */
  29.  
  30. unsigned char PixList[SCREEN_WIDTH_IN_BYTES*8/2];
  31.                                     /* maximum major axis length is
  32.                                        1/2 screen width, because we're
  33.                                        assuming no clipping is needed */
  34.  
  35. /* Draws the arc for an octant in which Y is the major axis. (X,Y) is the
  36.    starting point of the arc. HorizontalMoveDirection selects whether the
  37.    arc advances to the left or right horizontally (0=left, 1=right).
  38.    RowOffset contains the offset in bytes from one scan line to the next,
  39.    controlling whether the arc is drawn up or down. Length is the
  40.    vertical length in pixels of the arc, and DrawList is a list
  41.    containing 0 for each point if the next point is vertically aligned,
  42.    and 1 if the next point is 1 pixel diagonally to the left or right */
  43.  
  44. void DrawVOctant(int X, int Y, int Length, int RowOffset,
  45.    int HorizontalMoveDirection, unsigned char *DrawList)
  46. {
  47.    unsigned char far *ScreenPtr, BitMask;
  48.  
  49.    /* Point to the byte the initial pixel is in */
  50. #ifdef __TURBOC__
  51.    ScreenPtr = MK_FP(SCREEN_SEGMENT,
  52.       (Y * SCREEN_WIDTH_IN_BYTES) + (X / 8));
  53. #else
  54.    FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
  55.    FP_OFF(ScreenPtr) =(Y * SCREEN_WIDTH_IN_BYTES) + (X / 8);
  56. #endif
  57.    /* Set the initial bit mask */
  58.    BitMask = 0x80 >> (X & 0x07);
  59.  
  60.    /* Draw all points in DrawList */
  61.    while ( Length-- ) {
  62.       /* Set the bit mask for the pixel */
  63.       outp(GC_INDEX + 1, BitMask);
  64.       /* Draw the pixel. ORed to force read/write to load latches.
  65.          Data written doesn't matter, because set/reset is enabled
  66.          for all planes. Note: don't OR with 0; MSC optimizes that
  67.          statement to no operation */
  68.       *ScreenPtr |= 0xFE;
  69.       /* Now advance to the next pixel based on DrawList */
  70.       if ( *DrawList++ ) {
  71.          /* Advance horizontally to produce a diagonal move. Rotate
  72.             the bit mask, advancing one byte horizontally if the bit
  73.             mask wraps */
  74.          if ( HorizontalMoveDirection == 1 ) {
  75.             /* Move right */
  76.             if ( (BitMask >>= 1) == 0 ) {
  77.                BitMask = 0x80;   /* wrap the mask */
  78.                ScreenPtr++;      /* advance 1 byte to the right */
  79.             }
  80.          } else {
  81.             /* Move left */
  82.             if ( (BitMask <<= 1) == 0 ) {
  83.                BitMask = 0x01;   /* wrap the mask */
  84.                ScreenPtr--;      /* advance 1 byte to the left */
  85.             }
  86.          }
  87.       }
  88.       ScreenPtr += RowOffset; /* advance to the next scan line */
  89.    }
  90. }
  91.  
  92. /* Draws the arc for an octant in which X is the major axis. (X,Y) is the
  93.    starting point of the arc. HorizontalMoveDirection selects whether the
  94.    arc advances to the left or right horizontally (0=left, 1=right).
  95.    RowOffset contains the offset in bytes from one scan line to the next,
  96.    controlling whether the arc is drawn up or down. Length is the
  97.    horizontal length in pixels of the arc, and DrawList is a list
  98.    containing 0 for each point if the next point is horizontally aligned,
  99.    and 1 if the next point is 1 pixel above or below diagonally */
  100.  
  101. void DrawHOctant(int X, int Y, int Length, int RowOffset,
  102.    int HorizontalMoveDirection, unsigned char *DrawList)
  103. {
  104.    unsigned char far *ScreenPtr, BitMask;
  105.  
  106.    /* Point to the byte the initial pixel is in */
  107. #ifdef __TURBOC__
  108.    ScreenPtr = MK_FP(SCREEN_SEGMENT,
  109.       (Y * SCREEN_WIDTH_IN_BYTES) + (X / 8));
  110. #else
  111.    FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
  112.    FP_OFF(ScreenPtr) =(Y * SCREEN_WIDTH_IN_BYTES) + (X / 8);
  113. #endif
  114.    /* Set the initial bit mask */
  115.    BitMask = 0x80 >> (X & 0x07);
  116.  
  117.    /* Draw all points in DrawList */
  118.    while ( Length-- ) {
  119.       /* Set the bit mask for the pixel */
  120.       outp(GC_INDEX + 1, BitMask);
  121.       /* Draw the pixel (see comments above for details) */
  122.       *ScreenPtr |= 0xFE;
  123.       /* Now advance to the next pixel based on DrawList */
  124.       if ( *DrawList++ ) {
  125.          /* Advance vertically to produce a diagonal move */
  126.          ScreenPtr += RowOffset; /* advance to the next scan line */
  127.       }
  128.       /* Advance horizontally. Rotate the bit mask, advancing one
  129.          byte horizontally if the bit mask wraps */
  130.       if ( HorizontalMoveDirection == 1 ) {
  131.          /* Move right */
  132.          if ( (BitMask >>= 1) == 0 ) {
  133.             BitMask = 0x80;   /* wrap the mask */
  134.             ScreenPtr++;      /* advance 1 byte to the right */
  135.          }
  136.       } else {
  137.          /* Move left */
  138.          if ( (BitMask <<= 1) == 0 ) {
  139.             BitMask = 0x01;   /* wrap the mask */
  140.             ScreenPtr--;      /* advance 1 byte to the left */
  141.          }
  142.       }
  143.    }
  144. }
  145.  
  146. /* Draws a circle of radius Radius in color Color centered at
  147.  * screen coordinate (X,Y) */
  148.  
  149. void DrawCircle(int X, int Y, int Radius, int Color) {
  150.    int MajorAxis, MinorAxis;
  151.    unsigned long RadiusSqMinusMajorAxisSq, MinorAxisSquaredThreshold;
  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.    /* Set up to draw the circle by setting the initial point to one
  163.       end of the 1/8th of a circle arc we'll draw */
  164.    MajorAxis = 0;
  165.    MinorAxis = Radius;
  166.    /* Set initial Radius**2 - MajorAxis**2 (MajorAxis is initially 0) */
  167.    RadiusSqMinusMajorAxisSq = (unsigned long) Radius * Radius;
  168.    /* Set threshold for minor axis movement at (MinorAxis - 0.5)**2 */
  169.    MinorAxisSquaredThreshold = (unsigned long) MinorAxis * MinorAxis -
  170.       MinorAxis;
  171.  
  172.    /* Calculate all points along an arc of 1/8th of the circle and
  173.       store that info in PixList for later drawing */
  174.    PixListPtr = PixList;
  175.    do {
  176.       /* Advance (Radius**2 - MajorAxis**2); if it equals or passes
  177.          the MinorAxis**2 threshold, advance one pixel along both the
  178.          major and minor axes and set the next MinorAxis**2 threshold;
  179.          otherwise, advance one pixel only along the major axis */
  180.       RadiusSqMinusMajorAxisSq -=
  181.          MajorAxis + MajorAxis + 1;
  182.       if ( RadiusSqMinusMajorAxisSq <= MinorAxisSquaredThreshold ) {
  183.          /* Advance 1 pixel along both the major and minor axes */
  184.          MinorAxis--;
  185.          MinorAxisSquaredThreshold -= MinorAxis + MinorAxis;
  186.          *PixListPtr++ = 1;   /* advance along both axes */
  187.       } else {
  188.          *PixListPtr++ = 0;   /* advance only along the major axis */
  189.       }
  190.       MajorAxis++;   /* always advance one pixel along the major axis */
  191.    } while ( MajorAxis <= MinorAxis );
  192.  
  193.    /* Now draw each of the 8 symmetries of the octant in turn */
  194.    /* Draw the octants for which Y is the major axis */
  195.    DrawVOctant(X-Radius,Y,MajorAxis,-SCREEN_WIDTH_IN_BYTES,1,PixList);
  196.    DrawVOctant(X-Radius,Y,MajorAxis,SCREEN_WIDTH_IN_BYTES,1,PixList);
  197.    DrawVOctant(X+Radius,Y,MajorAxis,-SCREEN_WIDTH_IN_BYTES,0,PixList);
  198.    DrawVOctant(X+Radius,Y,MajorAxis,SCREEN_WIDTH_IN_BYTES,0,PixList);
  199.  
  200.    /* Draw the octants for which X is the major axis */
  201.    DrawHOctant(X,Y-Radius,MajorAxis,SCREEN_WIDTH_IN_BYTES,0,PixList);
  202.    DrawHOctant(X,Y-Radius,MajorAxis,SCREEN_WIDTH_IN_BYTES,1,PixList);
  203.    DrawHOctant(X,Y+Radius,MajorAxis,-SCREEN_WIDTH_IN_BYTES,0,PixList);
  204.    DrawHOctant(X,Y+Radius,MajorAxis,-SCREEN_WIDTH_IN_BYTES,1,PixList);
  205.  
  206.    /* Reset the Bit Mask register to normal */
  207.    outp(GC_INDEX + 1, 0xFF);
  208.    /* Turn off set/reset enable */
  209.    outpw(GC_INDEX, (0x00 << 8) | SET_RESET_ENABLE_INDEX);
  210. }
  211.  
  212.