home *** CD-ROM | disk | FTP | other *** search
- _GRAPHICS PROGRAMMING COLUMN_
- by Michael Abrash
-
-
- [LISTING ONE]
-
- /* Animates a line rotating about its center. 180 frames are constructed
- offscreen, one for each 1 degree rotation, and then copied to the screen to
- produce animation. Edsun CEG antialiasing or emulated CEG antialiasing may
- optionally be performed. To compile for Edsun CEG antialiasing, define
- USE_CEG on the compiler command line (/DUSE_CEG for MSC, -DUSE_CEG for Turbo C)
- and link this code with Listing 3. To compile for emulated CEG antialiasing,
- define EMULATE_CEG on the compiler command line and link this code with
- Listing 4. All C code tested with Microsoft C 5.0. Requires a large data model
- (the compact model was used for testing). */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <conio.h>
- #include <math.h>
- #include <dos.h>
- #ifdef __TURBOC__
- #include <mem.h>
- #else
- #include <memory.h>
- #endif
-
- /* Size of frames drawn to system memory, and size of the circle
- formed by the rotation of the line segment around its center */
- #define FRAME_WIDTH 49
- #define FRAME_HEIGHT 49
- #define FRAME_CENTER_X (FRAME_WIDTH/2)
- #define FRAME_CENTER_Y (FRAME_HEIGHT/2)
- #define X_RADIUS (FRAME_WIDTH/2-1)
- #define Y_RADIUS (FRAME_HEIGHT/2-1)
- #define SCREEN_WIDTH 320
- #define SCREEN_HEIGHT 200
- #define SCREEN_SEGMENT 0xA000
- #define X_OFFSET_ADJUST ((SCREEN_WIDTH/2) - FRAME_CENTER_X)
- #define Y_OFFSET_ADJUST ((SCREEN_HEIGHT/2) - FRAME_CENTER_Y)
- #define PI 3.141592
-
- int main(void);
- extern void DrawLine(unsigned char *,int,int,int,int,int,int,int);
- extern int SetCEGMode(int);
- extern void SetAAPalette(void);
-
- int main() {
- unsigned char *FramePtr[180], *ScreenPtr, *TempPtr;
- int angle, X1, Y1, X2, Y2, temp, i;
- union REGS regset;
-
- /* Generate 180 frames, one for each 1 degree rotation of the line
- segment about its center; store the results in system memory */
- printf("Precalculating frames. Please wait...");
- /* First, allocate space for the frames */
- for (angle = 0; angle < 180; angle++) {
- if ((FramePtr[angle] = (unsigned char *)
- malloc(FRAME_WIDTH * FRAME_HEIGHT)) == NULL) {
- printf("Out of memory\n");
- return(1);
- }
- /* Clear the frame to black */
- memset(FramePtr[angle], 0, FRAME_WIDTH * FRAME_HEIGHT);
- }
-
- /* Generate the frames, one for each 1 degree rotation of the line
- segment about its center */
- for (angle = 0; angle < 180; angle++) {
- /* Calculate upper end of line as a counterclockwise rotation from right
- end of a horizontal line and lower end of line as a counterclockwise
- rotation from left end of a horizontal line */
- temp = cos((double)angle * PI / 180.0) * X_RADIUS + 0.5;
- X1 = FRAME_CENTER_X + temp;
- X2 = FRAME_CENTER_X - temp;
- temp = sin((double)angle * PI / 180.0) * Y_RADIUS + 0.5;
- Y1 = FRAME_CENTER_Y - temp;
- Y2 = FRAME_CENTER_Y + temp;
- /* Draw the line in white */
- DrawLine(FramePtr[angle], FRAME_WIDTH, FRAME_HEIGHT, X1, Y1,
- X2, Y2, 15);
- }
-
- /* Set to the standard 256-color VGA mode, mode 0x13, 320x200 */
- regset.x.ax = 0x0013; int86(0x10, ®set, ®set);
-
- #ifdef USE_CEG
- /* Enable Advanced-8 CEG mode */
- if (!SetCEGMode(13)) {
- /* Restore text mode and we're done */
- regset.x.ax = 0x0003;
- int86(0x10, ®set, ®set);
- fprintf(stderr, "CEG/DAC not installed\n");
- return(1); /* no CEG/DAC installed */
- }
- #endif
-
- #ifdef EMULATE_CEG
- /* Set the palette up for antialiasing */
- SetAAPalette();
- #endif
-
- /* Draw the frames, at a rate of 1 frame per screen refresh
- interval, until a key is pressed */
- for (angle = 0;;) {
- do {
- /* Point to the destination area of display memory */
- #ifdef __TURBOC__
- ScreenPtr = MK_FP(SCREEN_SEGMENT, (Y_OFFSET_ADJUST*SCREEN_WIDTH)
- + X_OFFSET_ADJUST);
- #else
- FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
- FP_OFF(ScreenPtr) =
- (Y_OFFSET_ADJUST * SCREEN_WIDTH) + X_OFFSET_ADJUST;
- #endif
- /* Wait for the start of the vertical non-display portion of the
- frame */
- while (inp(0x3DA) & 0x08) ;
- while (!(inp(0x3DA) & 0x08)) ;
- /* Copy over the current frame, a scan line at a time */
- for (i = 0, TempPtr = FramePtr[angle]; i < FRAME_HEIGHT;
- i++, ScreenPtr += SCREEN_WIDTH, TempPtr += FRAME_WIDTH) {
- memcpy(ScreenPtr, TempPtr, FRAME_WIDTH);
- }
- angle = (angle + 1) % 180; /* wrap back every 180 frames */
- } while (!kbhit());
- if (getch() == 0x1B) /* pause if a key was pressed */
- break; /* exit if the key was Esc */
- getch(); /* wait for another key and resume */
- }
-
- /* Return the CEG/DAC to standard VGA operation by writing to
- palette location 223, restore text mode and we're done */
- #ifdef USE_CEG
- while (inp(0x3DA) & 0x08) ; /* wait for the start of */
- while (!(inp(0x3DA) & 0x08)) ; /* vertical non-display */
- outp(0x3C8, 223); outp(0x3C9, 0); outp(0x3C9, 0); outp(0x3C9, 0);
- #endif
- regset.x.ax = 0x0003; int86(0x10, ®set, ®set);
- return(0);
- }
-
-
-
- [LISTING TWO]
-
- /* Draws a non-antialiased line from (X1,Y1) to (X2,Y2) into the buffer
- pointed to by BufferPtr, of width BufferWidth and height BufferHeight. */
-
- #include <dos.h>
- #include <math.h>
- #define SWAP(a,b) {temp = a; a = b; b = temp;}
-
- void DrawLine(unsigned char *BufferPtr, int BufferWidth,
- int BufferHeight, int X1, int Y1, int X2, int Y2, int color)
- {
- int X, Y, DeltaX, DeltaY, temp;
- double Slope, InverseSlope;
-
- /* Calculate the X and Y lengths of the line */
- DeltaX = X2 - X1;
- DeltaY = Y2 - Y1;
-
- /* Determine the major axis */
- if (abs(DeltaY) > abs(DeltaX)) {
- /* Y is the major axis */
- if (DeltaY < 0) { /* make sure DeltaY is positive */
- SWAP(X1, X2);
- SWAP(Y1, Y2);
- DeltaX = -DeltaX;
- DeltaY = -DeltaY;
- }
- InverseSlope = (double)DeltaX / (double)DeltaY;
- /* Scan out the line, stepping along the Y axis one pixel at a
- time and calculating the corresponding X coordinates */
- for (Y = Y1; Y <= Y2; Y++) {
- X = X1 + (int)floor(((double)(Y - Y1) * InverseSlope) + 0.5);
- *(BufferPtr + BufferWidth * Y + X) = color;
- }
- } else {
- /* X is the major axis */
- if (DeltaX < 0) { /* make sure DeltaX is positive */
- SWAP(X1, X2);
- SWAP(Y1, Y2);
- DeltaX = -DeltaX;
- DeltaY = -DeltaY;
- }
- Slope = (double)DeltaY / (double)DeltaX;
- /* Scan out the line, stepping along the X axis one pixel at a
- time and calculating the corresponding Y coordinates */
- for (X = X1; X <= X2; X++) {
- Y = Y1 + (int)floor(((double)(X - X1) * Slope) + 0.5);
- *(BufferPtr + BufferWidth * Y + X) = color;
- }
- }
- }
-
-
-
-
- [LISTING THREE]
-
- /* Draws an Advanced-8 CEG-antialiased line from (X1,Y1) to (X2,Y2) into buffer
- pointed to by BufferPtr, of width BufferWidth and height BufferHeight. */
-
- #include <dos.h>
- #include <math.h>
- #define SWAP(a,b) {temp = a; a = b; b = temp;}
-
- void DrawLine(unsigned char *BufferPtr, int BufferWidth,
- int BufferHeight, int X1, int Y1, int X2, int Y2, int color)
- {
- int X, Y, DeltaX, DeltaY, temp, WeightingIndex, i, MixLength;
- double Slope, InverseSlope, XFloat, YFloat;
-
- /* Calculate X and Y lengths of the line */
- DeltaX = X2 - X1;
- DeltaY = Y2 - Y1;
-
- /* Determine the major axis */
- if (abs(DeltaY) > abs(DeltaX)) {
- /* Y is the major axis */
- if (DeltaY < 0) { /* make sure DeltaY is positive */
- SWAP(X1, X2);
- SWAP(Y1, Y2);
- DeltaX = -DeltaX;
- DeltaY = -DeltaY;
- }
- InverseSlope = (double)DeltaX / (double)DeltaY;
- /* Scan out line, stepping along the Y axis 1 pixel at a time and
- calculating corresponding pair of X coordinates, 1 on each side of
- ideal line, with the mix between background color and line color at
- each of the two X coordinates proportional to proximity of line to
- that pixel, and with line color intensities of the two X coordinates
- summing to 100% */
- for (Y = Y1; Y <= Y2; Y++) {
- /* Exact X coordinate at this Y coordinate */
- XFloat = (double)X1 + ((double)(Y - Y1) * InverseSlope);
- /* Nearest X coordinate on or to the left of the line */
- X = (int)floor(XFloat);
- /* Draw the color to the left pixel */
- *(BufferPtr + BufferWidth * Y + X) = color;
- /* Draw the weighting index for the desired color mix for the
- left pixel to the right pixel; the CEG/DAC uses this to
- mix the color we just wrote with the background color and
- draw that for the left pixel, then uses the complementary
- mix to draw the right pixel (this pixel). Confusing, but
- that's the way the CEG/DAC works! */
- *(BufferPtr + BufferWidth * Y + X + 1) =
- (int)((XFloat - X) * 32.0) + 192;
- }
- } else {
- /* X is the major axis */
- if (DeltaX < 0) { /* make sure DeltaX is positive */
- SWAP(X1, X2);
- SWAP(Y1, Y2);
- DeltaX = -DeltaX;
- DeltaY = -DeltaY;
- }
- Slope = (double)DeltaY / (double)DeltaX;
- /* Scan out the line, stepping along the X axis one pixel at a
- time and calculating the corresponding pair of Y coordinates,
- one on each side of the ideal line, with the mix between the
- background color and the line color at each of the two Y
- coordinates proportional to the proximity of the line to that
- pixel, and with the line color intensities of the two Y
- coordinates summing to 100% */
- for (X = X1; X <= X2; X++) {
- /* Exact Y coordinate at this X coordinate */
- YFloat = (double)Y1 + ((double)(X - X1) * Slope);
- /* Nearest Y coordinate on or above the line */
- Y = (int)floor(YFloat);
- /* Calculate the weighting index for the percentage of this
- pixel that's on the top scan line of the pair this pixel
- is split between */
- WeightingIndex = (int)((YFloat - Y) * 32.0);
- /* Set the weighting for the top pixel */
- *(BufferPtr + BufferWidth * Y + X) =
- WeightingIndex + 192;
- /* Set the weighting for the bottom pixel, with the top and
- bottom weightings summing to 100% */
- *(BufferPtr + BufferWidth * (Y + 1) + X) =
- 31 - WeightingIndex + 192;
- }
-
- /* Finally, post-process the buffer to put leading mix color
- bytes on mix sequences (so the weighting indexes have
- something to mix), and to turn one-pixel-wide sequences
- (artifacts of the above drawing approach, which will not
- display properly) into two-pixel-wide sequences by appending
- an additional mixed pixel 100% weighted to the background color */
- for (i = 0, MixLength = 0; i < (BufferWidth*BufferHeight); i++) {
- if (*(BufferPtr + i) != 0) {
- /* Part of a mix sequence; increment sequence length */
- MixLength++;
- } else {
- if (MixLength > 0) {
- /* Mix sequence just ended; set the line color to start
- the mix sequence */
- *(BufferPtr + i - MixLength - 1) = color;
- if (MixLength == 1)
- /* This is a 1-long mix sequence; pad it to a 2-long
- sequence with an all-background mixed pixel, so
- the mix sequence will display properly, rather
- than as a special 2-wide color/mix line case */
- *(BufferPtr + i) = 0xDF;
- MixLength = 0;
- }
- }
- }
- }
- }
-
- /* Sets the desired CEG/DAC mode, enabling CEG graphics. Returns 1 for
- success, 0 if no CEG/DAC is installed. */
- int SetCEGMode(int mode) {
- /* Wait for the start of the vertical non-display portion of the
- frame */
- while (inp(0x3DA) & 0x08) ;
- while (!(inp(0x3DA) & 0x08)) ;
-
- outp(0x3C7, 222); /* write the CEG enable sequence */
- outp(0x3C9, 'C'); outp(0x3C9, 'E'); outp(0x3C9, 'G');
- outp(0x3C7, 222);
- outp(0x3C9, 'E'); outp(0x3C9, 'D'); outp(0x3C9, 'S');
- outp(0x3C7, 222);
- outp(0x3C9, 'U'); outp(0x3C9, 'N');
- outp(0x3C9, mode); /* write the CEG mode */
-
- /* CEG mode should be enabled. Make sure this is a CEG/DAC */
- outp(0x3C6, 0xFF); /* enable all DAC mask bits */
- if ((inp(0x3C6) & 0x70) == 0x70)
- return(0); /* no version # bit is 0; this is not a CEG/DAC */
- else
- return(1); /* this is a CEG/DAC, and it's ready to go */
- }
-
-
-
- [LISTING FOUR]
-
- /* Draws an antialiased line from (X1,Y1) to (X2,Y2) into the buffer pointed
- to by BufferPtr, of width BufferWidth and height BufferHeight. White on black
- is the only supported color combination. */
-
- #include <dos.h>
- #include <math.h>
- #define SWAP(a,b) {temp = a; a = b; b = temp;}
-
- void DrawLine(unsigned char *BufferPtr, int BufferWidth,
- int BufferHeight, int X1, int Y1, int X2, int Y2, int color)
- {
- int X, Y, DeltaX, DeltaY, temp, WeightingIndex, i, MixLength;
- double Slope, InverseSlope, XFloat, YFloat;
-
- /* Calculate X and Y lengths of the line */
- DeltaX = X2 - X1;
- DeltaY = Y2 - Y1;
-
- /* Determine the major axis */
- if (abs(DeltaY) > abs(DeltaX)) {
- /* Y is the major axis */
- if (DeltaY < 0) { /* make sure DeltaY is positive */
- SWAP(X1, X2);
- SWAP(Y1, Y2);
- DeltaX = -DeltaX;
- DeltaY = -DeltaY;
- }
- InverseSlope = (double)DeltaX / (double)DeltaY;
- /* Scan out line, stepping along the Y axis one pixel at a time and
- calculating corresponding pair of X coordinates, 1 on each side of
- ideal line, with the mix between background color and line color at
- each of the two X coordinates proportional to proximity of line to
- that pixel, and with line color intensities of two X coordinates
- summing to 100% */
- for (Y = Y1; Y <= Y2; Y++) {
- /* Exact X coordinate at this Y coordinate */
- XFloat = (double)X1 + ((double)(Y - Y1) * InverseSlope);
- /* Nearest X coordinate on or to the left of the line */
- X = (int)floor(XFloat);
- /* Calculate the weighting index for the percentage of this
- pixel that's in the left column of the pair this pixel is
- split between */
- WeightingIndex = (int)((XFloat - X) * 32.0);
- /* Draw the left pixel with the desired color weighting */
- *(BufferPtr + BufferWidth * Y + X) = WeightingIndex + 16;
- /* Draw the right pixel with the complement of the left pixel
- color weighting, so that the line color intensities sum to 100% */
- *(BufferPtr + BufferWidth * Y + X + 1) =
- 31 - WeightingIndex + 16;
- }
- } else {
- /* X is the major axis */
- if (DeltaX < 0) { /* make sure DeltaX is positive */
- SWAP(X1, X2);
- SWAP(Y1, Y2);
- DeltaX = -DeltaX;
- DeltaY = -DeltaY;
- }
- /* Scan out the line, stepping along the X axis one pixel at a
- time and calculating the corresponding pair of Y coordinates,
- one on each side of the ideal line, with the mix between the
- background color and the line color at each of the two Y
- coordinates proportional to the proximity of the line to that
- pixel, and with the line color intensities of the two Y
- coordinates summing to 100% */
- Slope = (double)DeltaY / (double)DeltaX;
- for (X = X1; X <= X2; X++) {
- /* Exact Y coordinate at this X coordinate */
- YFloat = (double)Y1 + ((double)(X - X1) * Slope);
- /* Nearest Y coordinate on or above the line */
- Y = (int)floor(YFloat);
- /* Calculate the weighting index for the percentage of this
- pixel that's on the top scan line of the pair this pixel
- is split between */
- WeightingIndex = (int)((YFloat - Y) * 32.0);
- /* Draw the top pixel with the desired color weighting */
- *(BufferPtr + BufferWidth * Y + X) =
- WeightingIndex + 16;
- /* Draw the bottom pixel with the complement of the top pixel
- color weighting, so that the line color intensities sum to
- 100% */
- *(BufferPtr + BufferWidth * (Y + 1) + X) =
- 31 - WeightingIndex + 16;
- }
- }
- }
-
- /* Sets palette entries 16-47 for antialiasing, stepping from solid white to
- solid black (increments of 1/31st.) Steps are corrected for a gamma of 2.3. */
- void SetAAPalette() {
- union REGS regset;
- struct SREGS sregs;
- static unsigned char AASettings[32*3] = {
- 63,63,63, 62,62,62, 61,61,61, 60,60,60, 59,59,59, 58,58,58,
- 57,57,57, 56,56,56, 55,55,55, 54,54,54, 53,53,53, 52,52,52,
- 51,51,51, 50,50,50, 49,49,49, 47,47,47, 46,46,46, 45,45,45,
- 43,43,43, 42,42,42, 40,40,40, 39,39,39, 37,37,37, 35,35,35,
- 33,33,33, 31,31,31, 28,28,28, 26,26,26, 23,23,23, 19,19,19,
- 14,14,14, 0, 0, 0
- };
-
- regset.x.ax = 0x1012;
- regset.x.bx = 0x0010;
- regset.x.cx = 0x0020;
- regset.x.dx = (unsigned int) AASettings;
- segread(&sregs);
- sregs.es = sregs.ds; /* point ES:DX to AASettings */
- int86x(0x10, ®set, ®set, &sregs);
- }
-
-