home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1991
/
05
/
graph_pr.asc
< prev
next >
Wrap
Text File
|
1991-03-15
|
18KB
|
453 lines
_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);
}