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

  1. /*
  2.  * C implementation of Bresenham's line drawing algorithm
  3.  * for the EGA and VGA. Works in modes 0xE, 0xF, 0x10, and 0x12.
  4.  *
  5.  * Compiled with Borland C++ 4.02
  6.  * Checked by Jim Mischel 11/21/94 
  7.  *
  8.  * By Michael Abrash.
  9.  */
  10.  
  11. #include <dos.h>             /* contains MK_FP macro */
  12.  
  13. #define EVGA_SCREEN_WIDTH_IN_BYTES     80
  14.                                        /* memory offset from start of
  15.                                           one row to start of next */
  16. #define EVGA_SCREEN_SEGMENT            0xA000
  17.                                        /* display memory segment */
  18. #define GC_INDEX                       0x3CE
  19.                                        /* Graphics Controller
  20.                                           Index register port */
  21. #define GC_DATA                        0x3CF
  22.                                        /* Graphics Controller
  23.                                           Data register port */
  24. #define SET_RESET_INDEX                0  /* indexes of needed */
  25. #define ENABLE_SET_RESET_INDEX         1  /* Graphics Controller */
  26. #define BIT_MASK_INDEX                 8  /* registers */
  27.  
  28. /*
  29.  * Draws a dot at (X0,Y0) in whatever color the EGA/VGA hardware is
  30.  * set up for. Leaves the bit mask set to whatever value the
  31.  * dot required.
  32.  */
  33. void EVGADot(X0, Y0)
  34. unsigned int X0;     /* coordinates at which to draw dot, with */
  35. unsigned int Y0;     /* (0,0) at the upper left of the screen */
  36. {
  37.    unsigned char far *PixelBytePtr;
  38.    unsigned char PixelMask;
  39.  
  40.    /* Calculate the offset in the screen segment of the byte in
  41.       which the pixel lies */
  42.    PixelBytePtr = MK_FP(EVGA_SCREEN_SEGMENT,
  43.       ( Y0 * EVGA_SCREEN_WIDTH_IN_BYTES ) + ( X0 / 8 ));
  44.  
  45.    /* Generate a mask with a 1 bit in the pixel's position within the
  46.       screen byte */
  47.    PixelMask = 0x80 >> ( X0 & 0x07 );
  48.  
  49.    /* Set up the Graphics Controller's Bit Mask register to allow
  50.       only the bit corresponding to the pixel being drawn to
  51.       be modified */
  52.    outportb(GC_INDEX, BIT_MASK_INDEX);
  53.    outportb(GC_DATA, PixelMask);
  54.  
  55.    /* Draw the pixel. Because of the operation of the set/reset
  56.       feature of the EGA/VGA, the value written doesn't matter.
  57.       The screen byte is ORed in order to perform a read to latch the
  58.       display memory, then perform a write in order to modify it. */
  59.    *PixelBytePtr |= 0xFE;
  60. }
  61.  
  62. /*
  63.  * Draws a line in octant 0 or 3 ( |DeltaX| >= DeltaY ).
  64.  */
  65. void Octant0(X0, Y0, DeltaX, DeltaY, XDirection)
  66. unsigned int X0, Y0;          /* coordinates of start of the line */
  67. unsigned int DeltaX, DeltaY;  /* length of the line (both > 0) */
  68. int XDirection;               /* 1 if line is drawn left to right,
  69.                                  -1 if drawn right to left */
  70. {
  71.    int DeltaYx2;
  72.    int DeltaYx2MinusDeltaXx2;
  73.    int ErrorTerm;
  74.  
  75.    /* Set up initial error term and values used inside drawing loop */
  76.    DeltaYx2 = DeltaY * 2;
  77.    DeltaYx2MinusDeltaXx2 = DeltaYx2 - (int) ( DeltaX * 2 );
  78.    ErrorTerm = DeltaYx2 - (int) DeltaX;
  79.  
  80.    /* Draw the line */
  81.    EVGADot(X0, Y0);              /* draw the first pixel */
  82.    while ( DeltaX-- ) {
  83.       /* See if it's time to advance the Y coordinate */
  84.       if ( ErrorTerm >= 0 ) {
  85.          /* Advance the Y coordinate & adjust the error term
  86.             back down */
  87.          Y0++;
  88.          ErrorTerm += DeltaYx2MinusDeltaXx2;
  89.       } else {
  90.          /* Add to the error term */
  91.          ErrorTerm += DeltaYx2;
  92.       }
  93.       X0 += XDirection;          /* advance the X coordinate */
  94.       EVGADot(X0, Y0);           /* draw a pixel */
  95.    }
  96. }
  97.  
  98. /*
  99.  * Draws a line in octant 1 or 2 ( |DeltaX| < DeltaY ).
  100.  */
  101. void Octant1(X0, Y0, DeltaX, DeltaY, XDirection)
  102. unsigned int X0, Y0;          /* coordinates of start of the line */
  103. unsigned int DeltaX, DeltaY;  /* length of the line (both > 0) */
  104. int XDirection;               /* 1 if line is drawn left to right,
  105.                                  -1 if drawn right to left */
  106. {
  107.    int DeltaXx2;
  108.    int DeltaXx2MinusDeltaYx2;
  109.    int ErrorTerm;
  110.  
  111.    /* Set up initial error term and values used inside drawing loop */
  112.    DeltaXx2 = DeltaX * 2;
  113.    DeltaXx2MinusDeltaYx2 = DeltaXx2 - (int) ( DeltaY * 2 );
  114.    ErrorTerm = DeltaXx2 - (int) DeltaY;
  115.  
  116.    EVGADot(X0, Y0);           /* draw the first pixel */
  117.    while ( DeltaY-- ) {
  118.       /* See if it's time to advance the X coordinate */
  119.       if ( ErrorTerm >= 0 ) {
  120.          /* Advance the X coordinate & adjust the error term
  121.             back down */
  122.          X0 += XDirection;
  123.          ErrorTerm += DeltaXx2MinusDeltaYx2;
  124.       } else {
  125.          /* Add to the error term */
  126.          ErrorTerm += DeltaXx2;
  127.       }
  128.       Y0++;                   /* advance the Y coordinate */
  129.       EVGADot(X0, Y0);        /* draw a pixel */
  130.    }
  131. }
  132.  
  133. /*
  134.  * Draws a line on the EGA or VGA.
  135.  */
  136. void EVGALine(X0, Y0, X1, Y1, Color)
  137. int X0, Y0;    /* coordinates of one end of the line */
  138. int X1, Y1;    /* coordinates of the other end of the line */
  139. char Color;    /* color to draw line in */
  140. {
  141.    int DeltaX, DeltaY;
  142.    int Temp;
  143.  
  144.    /* Set the drawing color */
  145.  
  146.    /* Put the drawing color in the Set/Reset register */
  147.    outportb(GC_INDEX, SET_RESET_INDEX);
  148.    outportb(GC_DATA, Color);
  149.    /* Cause all planes to be forced to the Set/Reset color */
  150.    outportb(GC_INDEX, ENABLE_SET_RESET_INDEX);
  151.    outportb(GC_DATA, 0xF);
  152.  
  153.    /* Save half the line-drawing cases by swapping Y0 with Y1
  154.       and X0 with X1 if Y0 is greater than Y1. As a result, DeltaY
  155.       is always > 0, and only the octant 0-3 cases need to be
  156.       handled. */
  157.    if ( Y0 > Y1 ) {
  158.       Temp = Y0;
  159.       Y0 = Y1;
  160.       Y1 = Temp;
  161.       Temp = X0;
  162.       X0 = X1;
  163.       X1 = Temp;
  164.    }
  165.  
  166.    /* Handle as four separate cases, for the four octants in which
  167.       Y1 is greater than Y0 */
  168.    DeltaX = X1 - X0;    /* calculate the length of the line
  169.                            in each coordinate */
  170.    DeltaY = Y1 - Y0;
  171.    if ( DeltaX > 0 ) {
  172.       if ( DeltaX > DeltaY ) {
  173.          Octant0(X0, Y0, DeltaX, DeltaY, 1);
  174.       } else {
  175.          Octant1(X0, Y0, DeltaX, DeltaY, 1);
  176.       }
  177.    } else {
  178.       DeltaX = -DeltaX;             /* absolute value of DeltaX */
  179.       if ( DeltaX > DeltaY ) {
  180.          Octant0(X0, Y0, DeltaX, DeltaY, -1);
  181.       } else {
  182.          Octant1(X0, Y0, DeltaX, DeltaY, -1);
  183.       }
  184.    }
  185.  
  186.    /* Return the state of the EGA/VGA to normal */
  187.    outportb(GC_INDEX, ENABLE_SET_RESET_INDEX);
  188.    outportb(GC_DATA, 0);
  189.    outportb(GC_INDEX, BIT_MASK_INDEX);
  190.    outportb(GC_DATA, 0xFF);
  191. }
  192.  
  193.