home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1991 / 05 / graph_pr.asc < prev    next >
Text File  |  1991-03-15  |  18KB  |  453 lines

  1. _GRAPHICS PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. /* Animates a line rotating about its center. 180 frames are constructed 
  8. offscreen, one for each 1 degree rotation, and then copied to the screen to 
  9. produce animation. Edsun CEG antialiasing or emulated CEG antialiasing may 
  10. optionally be performed. To compile for Edsun CEG antialiasing, define 
  11. USE_CEG on the compiler command line (/DUSE_CEG for MSC, -DUSE_CEG for Turbo C)
  12. and link this code with Listing 3. To compile for emulated CEG antialiasing, 
  13. define EMULATE_CEG on the compiler command line and link this code with 
  14. Listing 4. All C code tested with Microsoft C 5.0. Requires a large data model
  15. (the compact model was used for testing). */
  16.  
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <conio.h>
  20. #include <math.h>
  21. #include <dos.h>
  22. #ifdef __TURBOC__
  23. #include <mem.h>
  24. #else
  25. #include <memory.h>
  26. #endif
  27.  
  28. /* Size of frames drawn to system memory, and size of the circle
  29.    formed by the rotation of the line segment around its center */
  30. #define FRAME_WIDTH     49
  31. #define FRAME_HEIGHT    49
  32. #define FRAME_CENTER_X  (FRAME_WIDTH/2)
  33. #define FRAME_CENTER_Y  (FRAME_HEIGHT/2)
  34. #define X_RADIUS        (FRAME_WIDTH/2-1)
  35. #define Y_RADIUS        (FRAME_HEIGHT/2-1)
  36. #define SCREEN_WIDTH    320
  37. #define SCREEN_HEIGHT   200
  38. #define SCREEN_SEGMENT  0xA000
  39. #define X_OFFSET_ADJUST ((SCREEN_WIDTH/2) - FRAME_CENTER_X)
  40. #define Y_OFFSET_ADJUST ((SCREEN_HEIGHT/2) - FRAME_CENTER_Y)
  41. #define PI              3.141592
  42.  
  43. int main(void);
  44. extern void DrawLine(unsigned char *,int,int,int,int,int,int,int);
  45. extern int SetCEGMode(int);
  46. extern void SetAAPalette(void);
  47.  
  48. int main() {
  49.    unsigned char *FramePtr[180], *ScreenPtr, *TempPtr;
  50.    int angle, X1, Y1, X2, Y2, temp, i;
  51.    union REGS regset;
  52.  
  53.    /* Generate 180 frames, one for each 1 degree rotation of the line
  54.       segment about its center; store the results in system memory */
  55.    printf("Precalculating frames. Please wait...");
  56.    /* First, allocate space for the frames */
  57.    for (angle = 0; angle < 180; angle++) {
  58.       if ((FramePtr[angle] = (unsigned char *)
  59.             malloc(FRAME_WIDTH * FRAME_HEIGHT)) == NULL) {
  60.          printf("Out of memory\n");
  61.          return(1);
  62.       }
  63.       /* Clear the frame to black */
  64.       memset(FramePtr[angle], 0, FRAME_WIDTH * FRAME_HEIGHT);
  65.    }
  66.  
  67.    /* Generate the frames, one for each 1 degree rotation of the line
  68.       segment about its center */
  69.    for (angle = 0; angle < 180; angle++) {
  70.       /* Calculate upper end of line as a counterclockwise rotation from right
  71.          end of a horizontal line and lower end of line as a counterclockwise 
  72.          rotation from left end of a horizontal line */
  73.       temp = cos((double)angle * PI / 180.0) * X_RADIUS + 0.5;
  74.       X1 = FRAME_CENTER_X + temp;
  75.       X2 = FRAME_CENTER_X - temp;
  76.       temp = sin((double)angle * PI / 180.0) * Y_RADIUS + 0.5;
  77.       Y1 = FRAME_CENTER_Y - temp;
  78.       Y2 = FRAME_CENTER_Y + temp;
  79.       /* Draw the line in white */
  80.       DrawLine(FramePtr[angle], FRAME_WIDTH, FRAME_HEIGHT, X1, Y1,
  81.             X2, Y2, 15);
  82.    }
  83.  
  84.    /* Set to the standard 256-color VGA mode, mode 0x13, 320x200 */
  85.    regset.x.ax = 0x0013; int86(0x10, ®set, ®set);
  86.  
  87. #ifdef USE_CEG
  88.    /* Enable Advanced-8 CEG mode */
  89.    if (!SetCEGMode(13)) {
  90.    /* Restore text mode and we're done */
  91.       regset.x.ax = 0x0003;
  92.       int86(0x10, ®set, ®set);
  93.       fprintf(stderr, "CEG/DAC not installed\n");
  94.       return(1);     /* no CEG/DAC installed */
  95.    }
  96. #endif
  97.  
  98. #ifdef EMULATE_CEG
  99.    /* Set the palette up for antialiasing */
  100.    SetAAPalette();
  101. #endif
  102.  
  103.    /* Draw the frames, at a rate of 1 frame per screen refresh
  104.       interval, until a key is pressed */
  105.    for (angle = 0;;) {
  106.     do {
  107.       /* Point to the destination area of display memory */
  108. #ifdef __TURBOC__
  109.       ScreenPtr = MK_FP(SCREEN_SEGMENT, (Y_OFFSET_ADJUST*SCREEN_WIDTH)
  110.             + X_OFFSET_ADJUST);
  111. #else
  112.       FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
  113.       FP_OFF(ScreenPtr) =
  114.             (Y_OFFSET_ADJUST * SCREEN_WIDTH) + X_OFFSET_ADJUST;
  115. #endif
  116.       /* Wait for the start of the vertical non-display portion of the
  117.          frame */
  118.       while (inp(0x3DA) & 0x08) ;
  119.       while (!(inp(0x3DA) & 0x08)) ;
  120.       /* Copy over the current frame, a scan line at a time */
  121.       for (i = 0, TempPtr = FramePtr[angle]; i < FRAME_HEIGHT;
  122.             i++, ScreenPtr += SCREEN_WIDTH, TempPtr += FRAME_WIDTH) {
  123.          memcpy(ScreenPtr, TempPtr, FRAME_WIDTH);
  124.       }
  125.       angle = (angle + 1) % 180; /* wrap back every 180 frames */
  126.     } while (!kbhit());
  127.     if (getch() == 0x1B)   /* pause if a key was pressed */
  128.       break;               /* exit if the key was Esc */
  129.     getch();               /* wait for another key and resume */
  130.    }
  131.  
  132.    /* Return the CEG/DAC to standard VGA operation by writing to
  133.       palette location 223, restore text mode and we're done */
  134. #ifdef USE_CEG
  135.    while (inp(0x3DA) & 0x08) ;      /* wait for the start of */
  136.    while (!(inp(0x3DA) & 0x08)) ;   /* vertical non-display */
  137.    outp(0x3C8, 223); outp(0x3C9, 0); outp(0x3C9, 0); outp(0x3C9, 0);
  138. #endif
  139.    regset.x.ax = 0x0003; int86(0x10, ®set, ®set);
  140.    return(0);
  141. }
  142.  
  143.  
  144.  
  145. [LISTING TWO]
  146.  
  147. /* Draws a non-antialiased line from (X1,Y1) to (X2,Y2) into the buffer 
  148. pointed to by BufferPtr, of width BufferWidth and height BufferHeight. */
  149.  
  150. #include <dos.h>
  151. #include <math.h>
  152. #define SWAP(a,b) {temp = a; a = b; b = temp;}
  153.  
  154. void DrawLine(unsigned char *BufferPtr, int BufferWidth,
  155.       int BufferHeight, int X1, int Y1, int X2, int Y2, int color)
  156. {
  157.    int X, Y, DeltaX, DeltaY, temp;
  158.    double Slope, InverseSlope;
  159.  
  160.    /* Calculate the X and Y lengths of the line */
  161.    DeltaX = X2 - X1;
  162.    DeltaY = Y2 - Y1;
  163.  
  164.    /* Determine the major axis */
  165.    if (abs(DeltaY) > abs(DeltaX)) {
  166.       /* Y is the major axis */
  167.       if (DeltaY < 0) {    /* make sure DeltaY is positive */
  168.          SWAP(X1, X2);
  169.          SWAP(Y1, Y2);
  170.          DeltaX = -DeltaX;
  171.          DeltaY = -DeltaY;
  172.       }
  173.       InverseSlope = (double)DeltaX / (double)DeltaY;
  174.       /* Scan out the line, stepping along the Y axis one pixel at a
  175.          time and calculating the corresponding X coordinates */
  176.       for (Y = Y1; Y <= Y2; Y++) {
  177.          X = X1 + (int)floor(((double)(Y - Y1) * InverseSlope) + 0.5);
  178.          *(BufferPtr + BufferWidth * Y + X) = color;
  179.       }
  180.    } else {
  181.       /* X is the major axis */
  182.       if (DeltaX < 0) {    /* make sure DeltaX is positive */
  183.          SWAP(X1, X2);
  184.          SWAP(Y1, Y2);
  185.          DeltaX = -DeltaX;
  186.          DeltaY = -DeltaY;
  187.       }
  188.       Slope = (double)DeltaY / (double)DeltaX;
  189.       /* Scan out the line, stepping along the X axis one pixel at a
  190.          time and calculating the corresponding Y coordinates */
  191.       for (X = X1; X <= X2; X++) {
  192.          Y = Y1 + (int)floor(((double)(X - X1) * Slope) + 0.5);
  193.          *(BufferPtr + BufferWidth * Y + X) = color;
  194.       }
  195.    }
  196. }
  197.  
  198.  
  199.  
  200.  
  201. [LISTING THREE]
  202.  
  203. /* Draws an Advanced-8 CEG-antialiased line from (X1,Y1) to (X2,Y2) into buffer
  204. pointed to by BufferPtr, of width BufferWidth and height BufferHeight. */
  205.  
  206. #include <dos.h>
  207. #include <math.h>
  208. #define SWAP(a,b) {temp = a; a = b; b = temp;}
  209.  
  210. void DrawLine(unsigned char *BufferPtr, int BufferWidth,
  211.       int BufferHeight, int X1, int Y1, int X2, int Y2, int color)
  212. {
  213.    int X, Y, DeltaX, DeltaY, temp, WeightingIndex, i, MixLength;
  214.    double Slope, InverseSlope, XFloat, YFloat;
  215.  
  216.    /* Calculate X and Y lengths of the line */
  217.    DeltaX = X2 - X1;
  218.    DeltaY = Y2 - Y1;
  219.  
  220.    /* Determine the major axis */
  221.    if (abs(DeltaY) > abs(DeltaX)) {
  222.       /* Y is the major axis */
  223.       if (DeltaY < 0) {    /* make sure DeltaY is positive */
  224.          SWAP(X1, X2);
  225.          SWAP(Y1, Y2);
  226.          DeltaX = -DeltaX;
  227.          DeltaY = -DeltaY;
  228.       }
  229.       InverseSlope = (double)DeltaX / (double)DeltaY;
  230.       /* Scan out line, stepping along the Y axis 1 pixel at a time and 
  231.          calculating corresponding pair of X coordinates, 1 on each side of 
  232.          ideal line, with the mix between background color and line color at 
  233.          each of the two X coordinates proportional to proximity of line to 
  234.          that pixel, and with line color intensities of the two X coordinates 
  235.          summing to 100% */
  236.       for (Y = Y1; Y <= Y2; Y++) {
  237.          /* Exact X coordinate at this Y coordinate */
  238.          XFloat = (double)X1 + ((double)(Y - Y1) * InverseSlope);
  239.          /* Nearest X coordinate on or to the left of the line */
  240.          X = (int)floor(XFloat);
  241.          /* Draw the color to the left pixel */
  242.          *(BufferPtr + BufferWidth * Y + X) = color;
  243.          /* Draw the weighting index for the desired color mix for the
  244.             left pixel to the right pixel; the CEG/DAC uses this to
  245.             mix the color we just wrote with the background color and
  246.             draw that for the left pixel, then uses the complementary
  247.             mix to draw the right pixel (this pixel). Confusing, but
  248.             that's the way the CEG/DAC works! */
  249.          *(BufferPtr + BufferWidth * Y + X + 1) =
  250.                (int)((XFloat - X) * 32.0) + 192;
  251.       }
  252.    } else {
  253.       /* X is the major axis */
  254.       if (DeltaX < 0) {    /* make sure DeltaX is positive */
  255.          SWAP(X1, X2);
  256.          SWAP(Y1, Y2);
  257.          DeltaX = -DeltaX;
  258.          DeltaY = -DeltaY;
  259.       }
  260.       Slope = (double)DeltaY / (double)DeltaX;
  261.       /* Scan out the line, stepping along the X axis one pixel at a
  262.          time and calculating the corresponding pair of Y coordinates,
  263.          one on each side of the ideal line, with the mix between the
  264.          background color and the line color at each of the two Y
  265.          coordinates proportional to the proximity of the line to that
  266.          pixel, and with the line color intensities of the two Y
  267.          coordinates summing to 100% */
  268.       for (X = X1; X <= X2; X++) {
  269.          /* Exact Y coordinate at this X coordinate */
  270.          YFloat = (double)Y1 + ((double)(X - X1) * Slope);
  271.          /* Nearest Y coordinate on or above the line */
  272.          Y = (int)floor(YFloat);
  273.          /* Calculate the weighting index for the percentage of this
  274.             pixel that's on the top scan line of the pair this pixel
  275.             is split between */
  276.          WeightingIndex = (int)((YFloat - Y) * 32.0);
  277.          /* Set the weighting for the top pixel */
  278.          *(BufferPtr + BufferWidth * Y + X) =
  279.                WeightingIndex + 192;
  280.          /* Set the weighting for the bottom pixel, with the top and
  281.             bottom weightings summing to 100% */
  282.          *(BufferPtr + BufferWidth * (Y + 1) + X) =
  283.                31 - WeightingIndex + 192;
  284.       }
  285.  
  286.       /* Finally, post-process the buffer to put leading mix color
  287.          bytes on mix sequences (so the weighting indexes have
  288.          something to mix), and to turn one-pixel-wide sequences
  289.          (artifacts of the above drawing approach, which will not
  290.          display properly) into two-pixel-wide sequences by appending
  291.          an additional mixed pixel 100% weighted to the background color */
  292.       for (i = 0, MixLength = 0; i < (BufferWidth*BufferHeight); i++) {
  293.          if (*(BufferPtr + i) != 0) {
  294.             /* Part of a mix sequence; increment sequence length */
  295.             MixLength++;
  296.          } else {
  297.             if (MixLength > 0) {
  298.                /* Mix sequence just ended; set the line color to start
  299.                   the mix sequence */
  300.                   *(BufferPtr + i - MixLength - 1) = color;
  301.                if (MixLength == 1)
  302.                   /* This is a 1-long mix sequence; pad it to a 2-long
  303.                      sequence with an all-background mixed pixel, so
  304.                      the mix sequence will display properly, rather
  305.                      than as a special 2-wide color/mix line case */
  306.                   *(BufferPtr + i) = 0xDF;
  307.                MixLength = 0;
  308.             }
  309.          }
  310.       }
  311.    }
  312. }
  313.  
  314. /* Sets the desired CEG/DAC mode, enabling CEG graphics. Returns 1 for
  315.    success, 0 if no CEG/DAC is installed. */
  316. int SetCEGMode(int mode) {
  317.    /* Wait for the start of the vertical non-display portion of the
  318.       frame */
  319.    while (inp(0x3DA) & 0x08) ;
  320.    while (!(inp(0x3DA) & 0x08)) ;
  321.  
  322.    outp(0x3C7, 222);    /* write the CEG enable sequence */
  323.    outp(0x3C9, 'C'); outp(0x3C9, 'E'); outp(0x3C9, 'G');
  324.    outp(0x3C7, 222);
  325.    outp(0x3C9, 'E'); outp(0x3C9, 'D'); outp(0x3C9, 'S');
  326.    outp(0x3C7, 222);
  327.    outp(0x3C9, 'U'); outp(0x3C9, 'N');
  328.    outp(0x3C9, mode);   /* write the CEG mode */
  329.  
  330.    /* CEG mode should be enabled. Make sure this is a CEG/DAC */
  331.    outp(0x3C6, 0xFF);   /* enable all DAC mask bits */
  332.    if ((inp(0x3C6) & 0x70) == 0x70)
  333.       return(0);  /* no version # bit is 0; this is not a CEG/DAC */
  334.    else
  335.       return(1);  /* this is a CEG/DAC, and it's ready to go */
  336. }
  337.  
  338.  
  339.  
  340. [LISTING FOUR]
  341.  
  342. /* Draws an antialiased line from (X1,Y1) to (X2,Y2) into the buffer pointed 
  343. to by BufferPtr, of width BufferWidth and height BufferHeight. White on black 
  344. is the only supported color combination. */
  345.  
  346. #include <dos.h>
  347. #include <math.h>
  348. #define SWAP(a,b) {temp = a; a = b; b = temp;}
  349.  
  350. void DrawLine(unsigned char *BufferPtr, int BufferWidth,
  351.       int BufferHeight, int X1, int Y1, int X2, int Y2, int color)
  352. {
  353.    int X, Y, DeltaX, DeltaY, temp, WeightingIndex, i, MixLength;
  354.    double Slope, InverseSlope, XFloat, YFloat;
  355.  
  356.    /* Calculate X and Y lengths of the line */
  357.    DeltaX = X2 - X1;
  358.    DeltaY = Y2 - Y1;
  359.  
  360.    /* Determine the major axis */
  361.    if (abs(DeltaY) > abs(DeltaX)) {
  362.       /* Y is the major axis */
  363.       if (DeltaY < 0) {    /* make sure DeltaY is positive */
  364.          SWAP(X1, X2);
  365.          SWAP(Y1, Y2);
  366.          DeltaX = -DeltaX;
  367.          DeltaY = -DeltaY;
  368.       }
  369.       InverseSlope = (double)DeltaX / (double)DeltaY;
  370.       /* Scan out line, stepping along the Y axis one pixel at a time and 
  371.          calculating corresponding pair of X coordinates, 1 on each side of 
  372.          ideal line, with the mix between background color and line color at 
  373.          each of the two X coordinates proportional to proximity of line to 
  374.          that pixel, and with line color intensities of two X coordinates 
  375.          summing to 100% */
  376.       for (Y = Y1; Y <= Y2; Y++) {
  377.          /* Exact X coordinate at this Y coordinate */
  378.          XFloat = (double)X1 + ((double)(Y - Y1) * InverseSlope);
  379.          /* Nearest X coordinate on or to the left of the line */
  380.          X = (int)floor(XFloat);
  381.          /* Calculate the weighting index for the percentage of this
  382.             pixel that's in the left column of the pair this pixel is
  383.             split between */
  384.          WeightingIndex = (int)((XFloat - X) * 32.0);
  385.          /* Draw the left pixel with the desired color weighting */
  386.          *(BufferPtr + BufferWidth * Y + X) = WeightingIndex + 16;
  387.          /* Draw the right pixel with the complement of the left pixel
  388.             color weighting, so that the line color intensities sum to 100% */
  389.          *(BufferPtr + BufferWidth * Y + X + 1) =
  390.                31 - WeightingIndex + 16;
  391.       }
  392.    } else {
  393.       /* X is the major axis */
  394.       if (DeltaX < 0) {    /* make sure DeltaX is positive */
  395.          SWAP(X1, X2);
  396.          SWAP(Y1, Y2);
  397.          DeltaX = -DeltaX;
  398.          DeltaY = -DeltaY;
  399.       }
  400.       /* Scan out the line, stepping along the X axis one pixel at a
  401.          time and calculating the corresponding pair of Y coordinates,
  402.          one on each side of the ideal line, with the mix between the
  403.          background color and the line color at each of the two Y
  404.          coordinates proportional to the proximity of the line to that
  405.          pixel, and with the line color intensities of the two Y
  406.          coordinates summing to 100% */
  407.       Slope = (double)DeltaY / (double)DeltaX;
  408.       for (X = X1; X <= X2; X++) {
  409.          /* Exact Y coordinate at this X coordinate */
  410.          YFloat = (double)Y1 + ((double)(X - X1) * Slope);
  411.          /* Nearest Y coordinate on or above the line */
  412.          Y = (int)floor(YFloat);
  413.          /* Calculate the weighting index for the percentage of this
  414.             pixel that's on the top scan line of the pair this pixel
  415.             is split between */
  416.          WeightingIndex = (int)((YFloat - Y) * 32.0);
  417.          /* Draw the top pixel with the desired color weighting */
  418.          *(BufferPtr + BufferWidth * Y + X) =
  419.                WeightingIndex + 16;
  420.          /* Draw the bottom pixel with the complement of the top pixel
  421.             color weighting, so that the line color intensities sum to
  422.             100% */
  423.          *(BufferPtr + BufferWidth * (Y + 1) + X) =
  424.                31 - WeightingIndex + 16;
  425.       }
  426.    }
  427. }
  428.  
  429. /* Sets palette entries 16-47 for antialiasing, stepping from solid white to 
  430. solid black (increments of 1/31st.) Steps are corrected for a gamma of 2.3. */
  431. void SetAAPalette() {
  432.    union REGS regset;
  433.    struct SREGS sregs;
  434.    static unsigned char AASettings[32*3] = {
  435.       63,63,63, 62,62,62, 61,61,61, 60,60,60, 59,59,59, 58,58,58,
  436.       57,57,57, 56,56,56, 55,55,55, 54,54,54, 53,53,53, 52,52,52,
  437.       51,51,51, 50,50,50, 49,49,49, 47,47,47, 46,46,46, 45,45,45,
  438.       43,43,43, 42,42,42, 40,40,40, 39,39,39, 37,37,37, 35,35,35,
  439.       33,33,33, 31,31,31, 28,28,28, 26,26,26, 23,23,23, 19,19,19,
  440.       14,14,14,  0, 0, 0
  441.    };
  442.  
  443.    regset.x.ax = 0x1012;
  444.    regset.x.bx = 0x0010;
  445.    regset.x.cx = 0x0020;
  446.    regset.x.dx = (unsigned int) AASettings;
  447.    segread(&sregs);
  448.    sregs.es = sregs.ds;       /* point ES:DX to AASettings */
  449.    int86x(0x10, ®set, ®set, &sregs);
  450. }
  451.  
  452.  
  453.