Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
You are on page 1of 42

VII PROGRAM CORRECTNESS

7.1 INTRODUCTION

We have made considerable headway in the mysteries of VGA graphics, but the location
of this chapter in the book indicates that there is plenty more to consider. Before we
introduce more new concepts we need to stop and reflect on what has been done at this
stage. More specifically, how well does it work? This may seem like an odd question
because all the programs presented in the previous chapters probably worked exactly as
specified.

It turns out that the previous graphics libraries have some potential problems. You might
remember that it was mentioned that void function FloodFill crashes when the area is too
large, and then there are problems when part of a graphics object is positioned outside the
normal graphics screen coordinate boundaries.

This boundary problem is going to be inspected first. The same system used with the
Optimize chapter will be repeated. First, you are shown a program with a problem, and
then, you will be shown one or more solutions to the problem.

First, we will look at so-called boundary problems: situations where the graphics object
does go outside the normal boundaries. For the purpose of our current graphics mode
0x13, that means the following boundaries.

┌──────────────────────────────┐
│0,0 319,0│
│ │
│ │
│ │
│0,199 319,199│
└──────────────────────────────┘

Chapter VII Program Correctness -7.1-


The routines that will be shown give protection against boundary problems for the 320 X
200 X 256 VGA mode. The methods shown will apply to other modes as well.

Pay close attention to these programs and learn to recognize situations that are sensitive
to boundary problems. Graphics games can become very weird when images "wrap-
around," and the graphics object shows up at two locations at the same time.

After the boundary issue, there will be other issues addressed as well. This chapter will
not try to cover every potential graphics problem, but it hopes to create an awareness
about potential problems. Awareness of a problem, or even a potential problem, is the first
step in solving the problem.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

7.2 BOUNDING THE PIXEL

It is possible that this boundary stuff does not make a lick of sense to you. Load program
CH07_01 and watch what happens. It is a small program that is meant to display four
pixels.
────────────────────────────────────────────────────────────────────────────────
/***********************************************************/
/*** CH07_01.CPP shows what happens when you try to put ***/
/*** pixels off the screen. ***/
/***********************************************************/

#include "GFXLIB5.HPP"

void main()
{
StartGfx();
Pixel( 50, 50, 15); // White pixel, on the screen.
Pixel(350, 50, 1); // Off the screen, to the right.
Pixel( 50, 250, 2); // Off the screen, below.
Pixel(350, 250, 4); // Below and to the right of the screen.
Stop();
EndGfx();
} // End main program CH07_01.CPP.
────────────────────────────────────────────────────────────────────────────────

Chapter VII Program Correctness -7.2-


The comments of program CH07_01 indicate that three pixels are outside the boundaries
of the VGA 320 X 200 display area. Yet, program execution shows that four pixels are
displayed. This is precisely why this chapter was created. Pixel parameters should be
followed accurately, and if parameters indicate coordinates outside the monitor, then, such
a pixel should not be seen. The problem is that values outside the boundaries "wrap
around" and show up in weird places. The behavior is similar to mod. A statement like
writeln(X mod 100) will not output a value greater than 99 regardless of the value of X.
Program CH07_02 solves the problem.
────────────────────────────────────────────────────────────────────────────────
/**************************************************************/
/*** CH07_02.CPP performs the same operation using the new ***/
/*** BoundedPixel void function. ***/
/**************************************************************/

#include "GFXLIB5.HPP"

void BoundedPixel(int X, int Y, Byte Color);

void main()
{
StartGfx();
Pixel( 50, 50, 15); // White pixel, on the screen.
Pixel(350, 50, 1); // Off the screen, to the right.
Pixel( 50, 250, 2); // Off the screen, below.
Pixel(350, 250, 4); // Below and to the right of the screen.
Stop();
EndGfx();
} // End main program CH07_02.CPP.

void BoundedPixel(int X, int Y, Byte Color)


{
if((X>0) && (Y>0) && (X<MAXX) && (Y<MAXY))
pokeb(VGA_RAM, X+Y*320, Color);
} // End void function BoundedPixel.
────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

Chapter VII Program Correctness -7.3-


7.3 CORRECTING THE LINE

Drawing very tiny pixels in the wrong location hardly is cause for excitement. A few pixels
in the wrong place are not that noticeable. Lines that are not properly checked cause
considerably more problems. Check out program CH07_03 and see what happens when
lines try to display in areas that do not exist.

────────────────────────────────────────────────────────────────────────────────
/*************************************************************/
/*** CH07_03.CPP shows what happens when you try to draw ***/
/*** lines that do not fit entirely on the screen. ***/
/*************************************************************/

#include "GFXLIB5.HPP"

void main()
{
StartGfx();
Line(0, 0, MAXX, MAXY, 15); // A normal diagonal white line.
Stop();
Line(20, 50, 350, 50, 14); // A horizontal yellow line that is too long.
Stop();
Line(40, 40, 40, 260, 4); // A vertical red line that is too long.
Stop();
Line(60, 60, 550, 510, 2); // A diagnonal green line that is too long.
Stop();
Line(200, 250, 400, 50, 3); // A corner "clip" blue line.
Stop();
EndGfx();
} // End main program CH07_03.CPP.
────────────────────────────────────────────────────────────────────────────────

Program CH07_03 should have convinced you that this "correctness" business is serious.
We cannot have lines wrapping around in all sorts of bizarre ways. The solution to the
problem can be quite simple. Use the same line routine that has been used before, but
call the bounded pixel function. This will cure any strange tendencies. Program CH07_04
may appear quite long, but it is basically your long, involved line routine with very minor
changes.

────────────────────────────────────────────────────────────────────────────────
/***************************************************************/
/*** CH07_04.CPP uses the bounded pixel routine inside the ***/
/*** slope line function to prevent any problems. ***/
/***************************************************************/

#include "GFXLIB5.HPP"

void BoundedPixel(int X, int Y, Byte Color);


void BoundedLine(int X1, int Y1, int X2, int Y2, Byte Color);

void main()
{

Chapter VII Program Correctness -7.4-


StartGfx();
BoundedLine(0, 0, MAXX, MAXY, 15); // A normal diagonal white line.
Stop();
BoundedLine(20, 50, 350, 50, 14); // A horizontal line that is too long.
Stop();
BoundedLine(40, 40, 40, 260, 4); // A vertical line that is too long.
Stop();
BoundedLine(60, 60, 550, 510, 2); // A diagnonal line that is too long.
Stop();
BoundedLine(200, 250, 400, 50, 3); // A corner "clip" line.
Stop();
EndGfx();
} // End main program CH07_04.CPP.

void BoundedPixel(int X, int Y, Byte Color)


{
if( (X>0) && (Y>0) && (X<MAXX) && (Y<MAXY) )
pokeb(VGA_RAM, X+Y*320, Color);
} // End void function BoundedPixel.

void BoundedLine(int X1, int Y1, int X2, int Y2, Byte Color)
{
int XDist, YDist; // X and Y distances.
int XDir, YDir; // X and Y directions.
int XCount, YCount; // X and Y counters.
int XYInc; // X and Y incrementer.

if (X1 == X2)
{
int X = X1;
if (Y1 > Y2)
{
int Temp = Y1;
Y1 = Y2;
Y2 = Temp;
} // End if.
for(int Y = Y1; Y <= Y2; Y++)
BoundedPixel(X, Y, Color);
} // End if.
else
{
if(Y1 == Y2)
{
int Y = Y1;
if(X1 > X2)
{
int Temp = X1;
X1 = X2;
X2 = Temp;
} // End if.
for (int X=X1; X<=X2; X++)
BoundedPixel(X, Y, Color);
} // End if.
} // End else.

if ( ! ((X1 == X2) || (Y1 == Y2)) )


{
XDist = X2 - X1;
YDist = Y2 - Y1;
XDir = Sign(XDist);
YDir = Sign(YDist);
XDist = abs(XDist);
YDist = abs(YDist);
XCount = X1;

Chapter VII Program Correctness -7.5-


YCount = Y1;
XYInc = 0;
while ((XCount != X2) || (YCount != Y2))
{
BoundedPixel(XCount, YCount, Color);
if (YDist < XDist)
{
XYInc += YDist;
XCount += XDir;
if (XYInc >= XDist)
{
XYInc -= XDist;
YCount += YDir;
} // End if.
} // End if.
else
{
XYInc += XDist;
YCount += YDir;
if (XYInc >= YDist)
{
XYInc -= YDist;
XCount += XDir;
} // End if.
} // End else.
} // End While.
BoundedPixel(XCount, YCount, Color);
} // End if.
} // End void function Line.
────────────────────────────────────────────────────────────────────────────────

The line business is not yet finished. We have an opportunity to be somewhat clever. It is
not difficult to check if the vertical and horizontal lines are outside the VGA screen
boundaries. An endpoint 200 pixels beyond the boundary will generate 200 function calls
to the bounded pixel routine and each call will be checked for correctness. The essence of
program CH07_05 is to compare the end points of the vertical and horizontal lines with 0,
MAXX and MAXY. If the endpoints exceed any of these values, then the line will only be
drawn to 0, or to MAXX or to MAXY. The result is that many unnecessary function calls
are avoided.

────────────────────────────────────────────────────────────────────────────────
/***************************************************************/
/*** CH07_05.CPP checks the end points of the vertical and ***/
/*** the horizontal lines to draw the line more efficiently. ***/
/***************************************************************/

#include "GFXLIB5.HPP"

void BoundedPixel(int X, int Y, Byte Color);


void BoundedLine(int X1, int Y1, int X2, int Y2, Byte Color);

void main()
{
StartGfx();
BoundedLine(0, 0, MAXX, MAXY, 15); // A normal diagonal white line.
Stop();
BoundedLine(20, 50, 350, 50, 14); // A horizontal line that is too long.

Chapter VII Program Correctness -7.6-


Stop();
BoundedLine(40, 40, 40, 260, 4); // A vertical line that is too long.
Stop();
BoundedLine(60, 60, 550, 510, 2); // A diagnonal line that is too long.
Stop();
BoundedLine(200, 250, 400, 50, 3); // A corner "clip" line.
Stop();
EndGfx();
} // End main program CH07_05.CPP.

void BoundedPixel(int X, int Y, Byte Color)


{
if( (X>0) && (Y>0) && (X<MAXX) && (Y<MAXY) )
pokeb(VGA_RAM, X+Y*320, Color);
} // End void function BoundedPixel.

void BoundedLine(int X1, int Y1, int X2, int Y2, Byte Color)
{
int XDist, YDist; // X and Y distances.
int XDir, YDir; // X and Y directions.
int XCount, YCount; // X and Y counters.
int XYInc; // X and Y incrementer.

void DoNothing(); // Does absolutely nothing.

if (X1 == X2)
{
if (Y1 > Y2)
{
int Temp = Y1;
Y1 = Y2;
Y2 = Temp;
} // End if.
if( (X1 < 0) || (Y1 < 0) || (X1 > MAXX) || (Y1 > MAXY) )
goto Exit;
if(Y2 > MAXY)
Y2 = MAXY;
if (Y1 < 0)
Y1 = 0;
int X = X1;
for(int Y = Y1; Y <= Y2; Y++)
pokeb(VGA_RAM, X+Y*320, Color);
} // End if.
else
{
if(Y1 == Y2)
{
if(X1 > X2)
{
int Temp = X1;
X1 = X2;
X2 = Temp;
} // End if.
if( (X1 < 0) || (Y2 < 0) || (X1 > MAXX) || (Y1 > MAXY) )
goto Exit;
if(X2 > MAXX)
X2 = MAXX;
if (X1 < 0)
X1 = 0;
int Y = Y1;
for(int X = X1; X <= X2; X++)
pokeb(VGA_RAM, X+Y*320, Color);
} // End if.
} // End else.

Chapter VII Program Correctness -7.7-


if ( ! ((X1 == X2) || (Y1 == Y2)) )
{
XDist = X2 - X1;
YDist = Y2 - Y1;
XDir = Sign(XDist);
YDir = Sign(YDist);
XDist = abs(XDist);
YDist = abs(YDist);
XCount = X1;
YCount = Y1;
XYInc = 0;
while ((XCount != X2) || (YCount != Y2))
{
BoundedPixel(XCount, YCount, Color);
if (YDist < XDist)
{
XYInc += YDist;
XCount += XDir;
if (XYInc >= XDist)
{
XYInc -= XDist;
YCount += YDir;
} // End if.
} // End if.
else
{
XYInc += XDist;
YCount += YDir;
if (XYInc >= YDist)
{
XYInc -= YDist;
XCount += XDir;
} // End if.
} // End else.
} // End While.
BoundedPixel(XCount, YCount, Color);
} // End if.
Exit: DoNothing(); // Label "exit" does absolutely nothing.
} // End void function Line.

void DoNothing()
{
} // End void function DoNothing.
────────────────────────────────────────────────────────────────────────────────

How about giving yourself a challenge. You can do still better than what we have shown.
Function BoundedLine is optimized by checking vertical and horizontal lines and altering
the end points. How about the diagonal lines? The routine works correctly for diagonal
lines, but it "cheats" by calling the BoundedPixel void function. And this can mean many
unnecessary calls. Can you figure out a way to alter the diagonal line program segment
so that it will avoid checking pixels outside the boundary? After all, why should we have all
the fun.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

Chapter VII Program Correctness -7.8-


7.4 MAKING THE RECTANGLE CORRECT

As graphics objects occupy more space, the effect of ignoring boundaries becomes more
pronounced. With this section we start looking at two-dimensional graphics objects and
the results are even more pronounced than the previous line demonstration. Program
CH07_06 shows a very strange pattern of broken-up open and closed rectangles.

Do keep in mind that many programmers are acutely aware of the display boundaries and
they do not view this "correctness" as any type of problem. Problems are more likely to
occur with random displays and video games that use moving backgrounds.

Chapter VII Program Correctness -7.9-


────────────────────────────────────────────────────────────────────────────────
/******************************************************************/
/*** CH07_06.CPP shows the problem with the rectangle routine. ***/
/******************************************************************/

#include "GFXLIB5.HPP"

void main()
{
StartGfx();
Rectangle(100, 75, 400, 125, 15, OPEN);
Rectangle(200, 150, 400, 250, 4, CLOSED);
Stop();
EndGfx();
} // End main program CH07_06.CPP.
────────────────────────────────────────────────────────────────────────────────

Program CH07_07 shows two incomplete rectangles. Only those graphics objects inside
the boundaries are shown. Nothing is wrapped around. This is precisely the desired
result for situations where the background is moving, and it is correct to only see part of a
graphics object within the background boundaries.

The techniques used in CH07_07 are similar to the checks used by program BLine3. End
coordinate values are compared to MAXX and MAXY, and if any values exceed maximum
X and Y values then adjustments are made to only draw objects up to the boundary
values.

────────────────────────────────────────────────────────────────────────────────
/********************************************************************/
/*** CH07_07.CPP fixes the problems with the Rectangle routine. ***/
/********************************************************************/

#include "GFXLIB5.HPP"

void BoundedPixel(int X, int Y, Byte Color);


void BoundedRectangle(int X1, int Y1, int X2, int Y2, Byte Color, Boolean Filled);

void main()
{
StartGfx();
BoundedRectangle(100, 75, 400, 125, 15, OPEN);
BoundedRectangle(200, 150, 400, 250, 4, CLOSED);
Stop();
EndGfx();
} // End main program CH07_07.CPP.

void BoundedPixel(int X, int Y, Byte Color)


{
if( (X>0) && (Y>0) && (X<MAXX) && (Y<MAXY) )
pokeb(VGA_RAM, X+Y*320, Color);
} // End void function BoundedPixel.

void BoundedRectangle(int X1, int Y1, int X2, int Y2, Byte Color, Boolean Filled)
{
void BHLine(int X1, int X2, int Y1, Byte Color);
void BVLine(int Y1, int Y2, int X1, Byte Color);
if(X1 < 0) X1 = 0;
if(Y1 < 0) Y1 = 0;

Chapter VII Program Correctness -7.10-


if(X2 < 0) X2 = 0;
if(Y2 < 0) Y2 = 0;
if(X1 > MAXX)
X1 = MAXX;
if(Y1 > MAXY)
Y1 = MAXY;
if(X2 > MAXX)
X2 = MAXX;
if(Y2 > MAXY)
Y2 = MAXY;
if(Filled)
for (int Y=Y1; Y<=Y2; Y++)
memset(VGAMEM + Y * 320 + X1, Color, X2 - X1 + 1);
else
{
BHLine(X1, X2, Y1, Color);
BHLine(X1, X2, Y2, Color);
BVLine(Y1, Y2, X1, Color);
BVLine(Y1, Y2, X2, Color);
}
} // End void function BoundedRectangle.

void BHLine(int X1, int X2, int Y1, Byte Color)


{
memset(VGAMEM + Y1 * 320 + X1, Color, X2 - X1 + 1);
} // End void function BHLine.

void BVLine(int Y1, int Y2, int X1, Byte Color)


{
for (int Y = Y1; Y <= Y2; Y++)
Pixel(X1, Y, Color);
} // End void function BVLine.
────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

7.5 MAKING CIRCLES, ELLIPSES AND ARCS CORRECT

Circles and arcs are two-dimensional graphics objects like rectangles, which display the
same odd behavior that we witnessed with unprotected rectangles. Program CH07_08 is
a classic example of a single, simple circle that executes in a graphics display at three
separate locations on the monitor. The center of the circle is located at [300,180], which
means that the circle should be drawn in the bottom-right corner of the display screen.
You will notice that the circle "wraps around" at two separate locations.

Chapter VII Program Correctness -7.11-


────────────────────────────────────────────────────────────────────────────────
/******************************************************************/
/*** CH07_08.CPP shows a circle that has points which lie ***/
/*** off of the screen. ***/
/******************************************************************/

#include "GFXLIB5.HPP"

void main()
{
StartGfx();
MakeTables();
Circle(300, 180, 50, 15);
Stop();
EndGfx();
} // End main program CH07_08.CPP.
────────────────────────────────────────────────────────────────────────────────

Program CH07_09 uses the not-so-clever approach of using the same circle routine and
calling the bounded pixel void function rather than the regular pixel routine. This approach
is fine, and the sample execution shows a correct, and desirable, output display.

────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_09.CPP shows the same circle as the previous program, ***/
/*** but this uses the BoundedPixel void function to prevent ***/
/*** erroneous points. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

void BoundedPixel(int X, int Y, Byte Color);


void BoundedCircle(int CX, int CY, int Radius, Byte Color);

void main()
{
StartGfx();
MakeTables();
BoundedCircle(300, 180, 50, 15);
Stop();
EndGfx();
} // End main program CH07_09.CPP.

void BoundedPixel(int X, int Y, Byte Color)


{
if( (X>0) && (Y>0) && (X<MAXX) && (Y<MAXY) )
pokeb(VGA_RAM, X+Y*320, Color);
} // End void function BoundedPixel.

void BoundedCircle(int CX, int CY, int Radius, Byte Color)


{
int X, Y; // X and Y Coordinates.

for (int K=1; K<=629; K++)


{
X = Radius * CosTab[K] + CX;

Chapter VII Program Correctness -7.12-


Y = Radius * SinTab[K] * AR + CY;
BoundedPixel(X, Y, Color);
}
} // End void function BoundedCircle.
────────────────────────────────────────────────────────────────────────────────

Chapter VII Program Correctness -7.13-


You have probably realized that program CH07_09 suffers from a problem that was
suggested earlier with bounded line routines. Calling the bounded pixel routine works, but
it can be very inefficient. Is there a way that you can prevent all those unnecessary pixel
calls when it is known that the graphics object is outside the boundary?

Keep in mind that our previous "clever" solution was done with lines and rectangles, and
each of these objects used only straight lines. A diagonal line was a little trickier, but still it
is not as complex as a curved object.

Program CH07_10 uses the so-called "quadrant" method. Load and run the program first,
check the execution and then read on for some explanation.

────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_10.CPP draws several circles using the new ***/
/*** quadrant method. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

Boolean InBounds(int X, int Y);


void BoundedCircle(int CX, int CY, int Radius, Byte Color);

void main()
{
StartGfx();
MakeTables();
BoundedCircle(MIDX, MIDY, 50, 15);
BoundedCircle( 40, 10, 50, 1);
BoundedCircle( 280, 10, 50, 1);
BoundedCircle( 280, 190, 50, 4);
BoundedCircle( 40, 190, 50, 14);
Stop();
EndGfx();
} // End main program CH07_10.CPP.

Boolean InBounds (int X, int Y)


{
if ( (X > 0) && (Y > 0) && (X < MAXX) && (Y < MAXY) )
return True;
else
return False;
} // End function InBounds.

void BoundedCircle(int CX, int CY, int Radius, Byte Color)


{
const QTR1 = 157; // First Quarter.
const QTR2 = 314; // Second Quarter.
const QTR3 = 471; // Third Quarter.
const QTR4 = 627; // Fourth Quarter ... not used in this program.

int X, Y; // X and Y Coordinates.


Boolean On; // Check if the pixel is on screen.
Boolean Tmp; // Check if the pixel is on screen.

On = InBounds(CosTab[0] * Radius + CX, SinTab[0] * Radius * AR + CY);

for (int K=1; K<=629; K++)

Chapter VII Program Correctness -7.14-


{
X = Radius * CosTab[K] + CX;
Y = Radius * SinTab[K] * AR + CY;
Tmp = InBounds(X, Y);
if ( !Tmp && On )
{
if (K < QTR1)
K = QTR1;
if (K > QTR3)
K = QTR4;
if ( (K > QTR1) && (K < QTR2) )
K = QTR2;
if ( (K > QTR2) && (K < QTR3) )
K = QTR3;
} // End if.
else if (Tmp)
pokeb(VGA_RAM, X + Y * 320, Color);
On = Tmp;
} // End for loop.
} // End void function BoundedCircle.
────────────────────────────────────────────────────────────────────────────────

Circles have a unique problem. There is no beginning and there is no end. The approach
used with lines and rectangles is to take "end coordinates" of the graphics object and
check them against MAXX and MAXY. Well if circles have no start or end then we will
have to force the issue. Circles can be cut up into four quadrants, and each quadrant has
a specific start and end. Since circles are represented by 2 * PI Radians, which means
roughly values from 0 to 6.28, we can divide 6.28 up and create values to be checked
against MAXX and MAXY.

The same quadrant logic will be used to correct the ellipse function. In this case, it will not
be necessary to draw an ellipse program that shows strange behavior. You are convinced
by now what will happen. A circle is a special ellipse and the logic is identical in nature for
program CH07_11.

────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_11.CPP draws several ellipses using the same ***/
/*** quadrant method introduced earlier. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

Boolean InBounds(int X, int Y);


void BoundedEllipse(int CX, int CY, int XR, int YR, Byte Color);

void main()
{
StartGfx();
MakeTables();
BoundedEllipse(MIDX, MIDY, 75, 50, 15);
BoundedEllipse( 40, 10, 40, 80, 1);
BoundedEllipse( 280, 10, 30, 90, 2);
BoundedEllipse( 280, 190, 20, 100, 4);
BoundedEllipse( 40, 190, 80, 50, 14);
Stop();

Chapter VII Program Correctness -7.15-


EndGfx();
} // End main program CH07_11.CPP.

Boolean InBounds (int X, int Y)


{
if ( (X > 0) && (Y > 0) && (X < MAXX) && (Y < MAXY) )
return True;
else
return False;
} // End function InBounds.

void BoundedEllipse(int CX, int CY, int XR, int YR, Byte Color)
{
const QTR1 = 157; // First Quarter.
const QTR2 = 314; // Second Quarter.
const QTR3 = 471; // Third Quarter.
const QTR4 = 627; // Fourth Quarter.

int X, Y; // X and Y Coordinates.


Boolean On; // Check if the pixel is on screen.
Boolean Tmp; // Check if the pixel is on screen.

On = InBounds(CosTab[0] * XR + CX, SinTab[0] * YR * AR + CY);

for (int K=1; K<=629; K++)


{
X = XR * CosTab[K] + CX;
Y = YR * SinTab[K] * AR + CY;
Tmp = InBounds(X, Y);
if ( !Tmp && On )
{
if (K < QTR1)
K = QTR1;
if (K > QTR3)
K = QTR4;
if ( (K > QTR1) && (K < QTR2) )
K = QTR2;
if ( (K > QTR2) && (K < QTR3) )
K = QTR3;
} // End if.
else if (Tmp)
pokeb(VGA_RAM, X + Y * 320, Color);
On = Tmp;
} // End for loop.
} // End void function BoundedEllipse.
────────────────────────────────────────────────────────────────────────────────

You might expect that a bounded arc function requires no additional explanation. After all,
aren't a circle and an ellipse special cases of an arc. They certainly are, but the arc
function has a very special personal problem. Circles and ellipses start and end very
nicely but arcs use parameters for starts and ends. This means that some clever person
can easily specify some ending value that exceeds 6.28. The same logic can be used
when we improve Arc. We need protection against values that are outside the radian
value limit and then also use the previous quadrant method.

────────────────────────────────────────────────────────────────────────────────

Chapter VII Program Correctness -7.16-


/*******************************************************************/
/*** CH07_12.CPP draws several arcs using the "quadrant" ***/
/*** method. This also solves the "wrap-around" problem which ***/
/*** occurs with end values greater than 2 * PI. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

Boolean InBounds(int X, int Y);


void BoundedArc(int CX, int CY, int XR, int YR,
float Start, float Finish, Byte Color);

void main()
{
StartGfx();
MakeTables();
BoundedArc(MIDX, MIDY, 75, 50, 1.0, 4.0, 15);
BoundedArc( 40, 10, 40, 80, 1.0, 7.0, 1);
BoundedArc( 280, 10, 30, 90, 3.0, 8.0, 2);
BoundedArc( 280, 190, 20, 100, 0.0, 6.0, 4);
BoundedArc( 40, 190, 80, 50, 0.0, 5.0, 14);
Stop();
EndGfx();
} // End main program CH07_12.CPP.

Boolean InBounds (int X, int Y)


{
if ( (X > 0) && (Y > 0) && (X < MAXX) && (Y < MAXY) )
return True;
else
return False;
} // End function InBounds.

void BoundedArc(int CX, int CY, int XR, int YR,


float Start, float Finish, Byte Color)
{
const QTR1 = 157; // First Quarter.
const QTR2 = 314; // Second Quarter.
const QTR3 = 471; // Third Quarter.
const QTR4 = 627; // Fourth Quarter.

int X, Y; // X and Y Coordinates.


Boolean On; // Check if the pixel is on screen.
Boolean Tmp; // Check if the pixel is on screen.
int RealK; // Check K for wrap-around.

int Start2 = Start * 100;


int Finish2 = Finish * 100;
int Direction = (Finish > Start) ? 1 : -1;

On = InBounds(CosTab[0] * XR + CX, SinTab[0] * YR * AR + CY);


for (int K=Start2; K<=Finish2; K+=Direction)
{
RealK = (K > 629) ? K - 629 : K;
X = XR * CosTab[RealK] + CX;
Y = YR * SinTab[RealK] * AR + CY;
Tmp = InBounds(X, Y);
if ( !Tmp && On )
{
if (K < QTR1)
K = QTR1;
if (K > QTR3)
K = QTR4;
if ( (K > QTR1) && (K < QTR2) )

Chapter VII Program Correctness -7.17-


K = QTR2;
if ( (K > QTR2) && (K < QTR3) )
K = QTR3;
} // End if.
else if (Tmp)
pokeb(VGA_RAM, X + Y * 320, Color);
On = Tmp;
} // End for loop.
} // End void function BoundedArc.
────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

Chapter VII Program Correctness -7.18-


7.6 FLOOD FILLING LARGE AREAS

It has been some time since we discussed FloodFill. Back in the early chapters where
FloodFill was first introduced it was mentioned to be careful. Floodfilling an area that is
too large results in "Stack overflow" problems. The nature of the FloodFill routine is
recursive, and recursion uses a stack to keep track of function calls and local variable
values. Too many recursive calls result in the stack throwing in the towel and the program
crashing. It is not exactly acceptable to either crash a program or to state that FloodFill is
only permitted for small areas.

This type of correctness problem is very different from boundary problems. Here, we
really have an example of a routine that did not exactly work properly to start with. Why
introduce incorrect functions? The reason is simple, it makes learning easier. Pretty weird
you think. Learning is easier when we learn something wrong first. Well the point is that
you did not learn something wrong. The flood fill concepts were correct, and it helped for
you to see the logic in a small, pretty-easy-to-explain, FloodFill routine. Now that you
have more graphics sophistication under your belt the time has come to take a second
look and fix this problem.

Our main concern is to avoid program crashes. They are annoying and consumers do not
like purchasing software that does not work properly. Our first solution is to implement a
user-defined stack and control the pushing and popping business. When the stack
becomes full we can avoid a program crash. Program CH07_13 satisfies that
requirement. We have solved the overflow problem. There are no more crashes, but we
still have not filled in the area completely.

────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_13.CPP repairs the recursive flood fill problem ***/
/*** by implementing an artificial stack to fill a larger, but ***/
/*** still incomplete area. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

const FLOODSIZE=20000; // Maximum flood fill size.

struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.

typedef Coord huge StackType[FLOODSIZE]; // Stack type.


int Top; // Top of stack.

Byte OldColor; // Original color.


StackType Stack; // Artificial Stack

void Push(Coord N);

Chapter VII Program Correctness -7.19-


void Pop(Coord &N);
void Flood(int X, int Y, Byte NewColor);

void main()
{
StartGfx();
MakeTables();
Circle(MIDX, MIDY, 100, 15);
Flood(MIDX, MIDY, 14);
Stop();
EndGfx();
} // End main program CH07_13.CPP.

void Flood(int X, int Y, Byte NewColor)


{
Coord Temp; // Temporary coordinate.

void MoveToNext(Coord& N);

Top = 0;
Temp.X = X;
Temp.Y = Y;
OldColor = GetPixel(X, Y);
do
{
if (GetPixel(Temp.X, Temp.Y) == OldColor)
{
Pixel(Temp.X, Temp.Y, NewColor);
Push(Temp);
MoveToNext(Temp);
} // End if.
else
{
Pop(Temp);
MoveToNext(Temp);
} // End else.
} // End do loop.
while(Top > 0);
} // End void function Flood.

void MoveToNext(Coord& N)
{
if ( ( GetPixel(N.X+1, N.Y) == OldColor ) )
N.X++;
else if ( ( GetPixel(N.X, N.Y -1) == OldColor ) )
N.Y--;
else if ( ( GetPixel(N.X-1, N.Y) == OldColor ) )
N.X--;
else if ( ( GetPixel(N.X, N.Y+1) == OldColor ) )
N.Y++;
} // End void function MoveToNext.

void Push(Coord N)
{
if (Top < FLOODSIZE)
{
Top++;
Stack[Top] = N;
} // End if.
} // End void function Push.

void Pop(Coord& N)
{
if (Top > 0)

Chapter VII Program Correctness -7.20-


{
N = Stack[Top];
Top--;
} // End if.
} // End void function Pop.
────────────────────────────────────────────────────────────────────────────────

Chapter VII Program Correctness -7.21-


Now we are going to become a little weird. You have probably heard of a stack, and you
have probably heard of a queue. Well, how about a stack that behaves like a queue?
Pretty strange? What we are doing is switching to a queue structure when the stack is
filled up. Compare the Push routine in program CH07_14 with that in program CH07_13,
and you will notice the difference. The result is that we can now display more pixels
before running into stack size limitations.

────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_14.CPP shows an alternate method of using an ***/
/*** artificial queue in a stack-like manner for more pixels. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

const FLOODSIZE=20000; // Maximum flood fill size.

struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.

typedef Coord huge StackType[FLOODSIZE]; // Stack type.


int Top; // Top of stack.

Byte OldColor; // Original color.


StackType Stack; // Artificial Stack

void Push(Coord N);


void Pop(Coord& N);
void Flood(int X, int Y, Byte NewColor);

void main()
{
StartGfx();
MakeTables();
Circle(MIDX, MIDY, 100, 15);
Flood(MIDX, MIDY, 14);
Stop();
EndGfx();
} // End main program CH07_14.CPP.

void Flood(int X, int Y, Byte NewColor)


{
Coord Temp; // Temporary coordinate.

void MoveToNext(Coord& N);

Top = 0;
Temp.X = X;
Temp.Y = Y;
OldColor = GetPixel(X, Y);
do
{
if (GetPixel(Temp.X, Temp.Y) == OldColor)
{
Pixel(Temp.X, Temp.Y, NewColor);
Push(Temp);
MoveToNext(Temp);

Chapter VII Program Correctness -7.22-


} // End if.
else
{
Pop(Temp);
MoveToNext(Temp);
} // End else.
} // End do loop.
while(Top > 0);
} // End void function Flood.

void MoveToNext(Coord& N)
{
if ( ( GetPixel(N.X+1, N.Y) == OldColor ) )
N.X++;
else if ( ( GetPixel(N.X, N.Y -1) == OldColor ) )
N.Y--;
else if ( ( GetPixel(N.X-1, N.Y) == OldColor ) )
N.X--;
else if ( ( GetPixel(N.X, N.Y+1) == OldColor ) )
N.Y++;
} // End void function MoveToNext.

void Push(Coord N)
{
if (Top >= FLOODSIZE)
Top -= FLOODSIZE;
Top++;
Stack[Top] = N;
} // End void function Push.

void Pop(Coord& N)
{
if (Top > 0)
{
N = Stack[Top];
Top--;
} // End if.
} // End void function Pop.
────────────────────────────────────────────────────────────────────────────────

Notice the huge that is placed before the identifier name of StackType. This is called a
type modifier, and it is necessary whenever you wish to declare a data structure larger
than 64000 bytes. In the case of our StackType, we have an array of 20000 structures,
and each structure is 4 bytes in size. The resulting data type is 20000 X 4, or 80000,
bytes in size. In order to use the huge type modifier, you also need to make some
changes to the compiler configuration. Under Options / Compiler / Code Generation,
the memory Model needs to be set to either Large or Huge. Any other setting will cause
problems and/or crash the computer.

Life is good (provided a good life is defined by improved floodfill functions). You can fill up
larger areas. There appears to be no more nasty stack overflow problems, and the entire
area fills up nicely. So just when you thought you could relax, we will throw a monkey
wrench into the works. Check out program CH07_15. It is the same logic as CH07_14
but it tries to floodfill around some line that has been introduced. All of a sudden this
obstacle makes our nifty floodfill void function behave in a very unsatisfactory manner.

Chapter VII Program Correctness -7.23-


────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_15.CPP shows an alternate method of using an ***/
/*** artificial queue in a stack-like manner for more pixels. ***/
/*** This example shows potential problems with the stack-queue ***/
/*** method when an obstacle, like a line, is introduced. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

const FLOODSIZE=20000; // Maximum flood fill size.

struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.

typedef Coord huge StackType[FLOODSIZE]; // Stack type.


int Top; // Top of stack.

Byte OldColor; // Original color.


StackType Stack; // Artificial Stack

void Push(Coord N);


void Pop(Coord& N);
void Flood(int X, int Y, Byte NewColor);

void main()
{
StartGfx();
MakeTables();
Circle(MIDX, MIDY, 100, 15);
Flood(MIDX, MIDY, 14);
Stop();
EndGfx();
} // End main program CH07_15.CPP.

void Flood(int X, int Y, Byte NewColor)


{
Coord Temp; // Temporary coordinate.

void MoveToNext(Coord& N);

Top = 0;
Temp.X = X;
Temp.Y = Y;
OldColor = GetPixel(X, Y);
do
{
if (GetPixel(Temp.X, Temp.Y) == OldColor)
{
Pixel(Temp.X, Temp.Y, NewColor);
Push(Temp);
MoveToNext(Temp);
} // End if.
else
{
Pop(Temp);
MoveToNext(Temp);
} // End else.
} // End do loop.
while(Top > 0);

Chapter VII Program Correctness -7.24-


} // End void function Flood.

void MoveToNext(Coord& N)
{
if ( ( GetPixel(N.X+1, N.Y) == OldColor ) )
N.X++;
else if ( ( GetPixel(N.X, N.Y -1) == OldColor ) )
N.Y--;
else if ( ( GetPixel(N.X-1, N.Y) == OldColor ) )
N.X--;
else if ( ( GetPixel(N.X, N.Y+1) == OldColor ) )
N.Y++;
} // End void function MoveToNext.

void Push(Coord N)
{
if (Top >= FLOODSIZE)
Top -= FLOODSIZE;
Top++;
Stack[Top] = N;
} // End void function Push.

void Pop(Coord& N)
{
if (Top > 0)
{
N = Stack[Top];
Top--;
} // End if.
} // End void function Pop.
────────────────────────────────────────────────────────────────────────────────

So how do we deal with this problem? We can use some of the tactics, like a stack-queue
approach, and combine this with some other new ideas. We can use a "quad fill"
approach that divides the area up into four quadrants. This approach improves the
problem considerably.

────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_16.CPP shows a new solution . . . the QuadFlood ***/
/*** method, which solves the obstacle problem in most cases. ***/
/*******************************************************************/

#include "GFXLIB5.HPP"

const FLOODSIZE=20000; // Maximum flood fill size.

struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.

typedef Coord huge StackType[FLOODSIZE]; // Stack type.


int Top; // Top of stack.

Byte OldColor; // Original color.


StackType Stack; // Artificial Stack

Chapter VII Program Correctness -7.25-


void Push(Coord N);
void Pop(Coord& N);
void Flood(int X, int Y, Byte NewColor);

void main()
{
StartGfx();
MakeTables();
Circle(MIDX, MIDY, 100, 15);
Line(190, 101, 310, 150, 15);
Line(191, 104, 310, 153, 15);
Line( 60, 102, 125, 102, 15);
Flood(MIDX, MIDY, 2);
Stop();
EndGfx();
} // End main program CH07_16.CPP.

void Flood(int X, int Y, Byte NewColor)


{
int XTR, YTR; // Temporary x and y values.

void QuadFlood(int X, int Y, Byte NewColor);

OldColor = GetPixel(X, Y);

// Draw horizontal line to left.


XTR = X;
YTR = Y;
do
{
pokeb(VGA_RAM, XTR+YTR*320, NewColor);
XTR--;
} // End do loop.
while( (GetPixel(XTR, YTR) == OldColor) && (XTR > 0) );

// Draw horizontal line to right.


XTR = X;
YTR = Y;
do
{
pokeb(VGA_RAM, XTR+YTR*320, NewColor);
XTR++;
} // End do loop.
while( (GetPixel(XTR, YTR) == OldColor) && (XTR < MAXX) );

// Draw vertical line to bottom.


XTR = X;
YTR = Y;
do
{
pokeb(VGA_RAM, XTR+YTR*320, NewColor);
YTR++;
} // End do loop.
while( (GetPixel(XTR, YTR) == OldColor) && (YTR < MAXY) );

// Draw vertical line to top.


XTR = X;
YTR = Y;
do
{
pokeb(VGA_RAM, XTR+YTR*320, NewColor);
YTR--;
} // End do loop.

Chapter VII Program Correctness -7.26-


while( (GetPixel(XTR, YTR) == OldColor) && (YTR > 0) );

QuadFlood(X-1, Y-1, NewColor);


QuadFlood(X+1, Y-1, NewColor);
QuadFlood(X+1, Y+1, NewColor);
QuadFlood(X-1, Y+1, NewColor);
} // End void function Flood.

void QuadFlood(int X, int Y, Byte NewColor)


{
Coord Temp; // Temporary coordinate.

Boolean RangeOK(Coord Temp);


void MoveToNext(Coord& N);

Top = 1;
Temp.X = X;
Temp.Y = Y;
do
{
if( (GetPixel(Temp.X, Temp.Y) == OldColor) && RangeOK(Temp) )
{
pokeb(VGA_RAM, Temp.X + Temp.Y * 320, NewColor);
Push(Temp);
MoveToNext(Temp);
} // End if.
else
{
Pop(Temp);
MoveToNext(Temp);
} // End else.
} // End do loop.
while(Top > 0);
} // End void function Flood::QuadFlood.

Boolean RangeOK(Coord N)
{
return Boolean( (N.X >= 0) && (N.X <= MAXX) &&
(N.Y >= 0) && (N.Y <= MAXY) );
} // End function Flood::RangeOK.

void MoveToNext(Coord& N)
{
if ( ( GetPixel(N.X+1, N.Y) == OldColor ) )
N.X++;
else if ( ( GetPixel(N.X, N.Y -1) == OldColor ) )
N.Y--;
else if ( ( GetPixel(N.X-1, N.Y) == OldColor ) )
N.X--;
else if ( ( GetPixel(N.X, N.Y+1) == OldColor ) )
N.Y++;
} // End void function Flood::MoveToNext.

void Push(Coord N)
{
if (Top >= FLOODSIZE)
Top -= FLOODSIZE;
Top++;
Stack[Top] = N;
} // End void function Push.

void Pop(Coord& N)
{
if (Top > 0)

Chapter VII Program Correctness -7.27-


{
N = Stack[Top];
Top--;
} // End if.
} // End void function Pop.
────────────────────────────────────────────────────────────────────────────────

Please be advised that the QuadFill function is not yet perfect. It is possible to design
some rather obscure configurations that might miss some areas. A routine that covers
every possible coordinate, no matter how obscurely hidden, is very complex and very
lengthy. The provided void function will work in most cases.

It is also advisable to fill up large areas first and then draw other graphics on top of the
flooded area. In other words, floodfill from larger to smaller, rather than filling around many
objects.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

Chapter VII Program Correctness -7.28-


7.7 SUMMARY

Correctness is not a topic to be taken lightly in computer science. Many companies and
individuals have been severely handicapped by programs that did not work correctly. In
the area of graphics you will probably not encounter any disastrous results if your graphics
do not work properly, but the output can become pretty bizarre. Graphics is no different
from other areas of computer science. You will always be subject to the efficiency
dilemma. It is not possible to improve in one area without taking away elsewhere. The
process of making routines foolproof so that no graphics objects are drawn in wrong areas
means that extra code has to be written to check for many situations. These extra checks
take time, and your execution efficiency decreases. Putting code in-line optimizes
execution efficiency, but it decreases readability, and the program code becomes more
difficult to understand and update at some future date. It is now time to create
GRAFIX07.LIB, which will combine the routines of GRAFIX05.LIB with the new bounded
routines. Void functions like Pixel and Line will also have BoundedPixel and
BoundedLine if a more accurate - but slower - routine is desired. We also have a better
FloodFill routine capable of filling far greater areas without runtime errors.

────────────────────────────────────────────────────────────────────────────────
/************************************************************************/
/*** NOTE: THIS LIBRARY MUST BE COMPILED USING A "LARGE" OR GREATER ***/
/*** SIZED MEMORY MODEL. IF IT IS NOT, IT WILL CRASH ON ***/
/*** EXECUTION. THE MEMORY MODEL CAN BE SPECIFIED IN "OPTIONS / ***/
/*** COMPILER / CODE GENERATION." ***/
/************************************************************************/
/*** GFXLIB6 combimes all the graphics routines in one library of ***/
/*** optimized and altered routines and functions. This library ***/
/*** represents the first library that is suitable for actual ***/
/*** graphics programming. It is still not the last library. ***/
/*** There are many more routines to be introduced and there will ***/
/*** also be additional optimization. ***/
/************************************************************************/
#include <dos.h>
#include <conio.h>
#include <math.h>
/*********************************************************************/
/*** USER DEFINED AND ENUMERATED DATA TYPES ***/
/*********************************************************************/
typedef unsigned short Byte; // Byte type definition.
typedef unsigned int Word; // Word type definition.
enum Boolean {False,True}; // Boolean type definition.
/*********************************************************************/
/*** CONSTANT VARIABLE IDENTIFIERS ***/
/*********************************************************************/
const int MINX = 0; // Minimum x coordinate value.
const int MIDX = 320; // Midpoint x coordinate value.
const int MAXX = 639; // Maximum x coordinate value.
const int MINY = 0; // Minimum y coordinate value.
const int MIDY = 240; // Midpoint y coordinate value.
const int MAXY = 479; // Maximum y coordinate value.
const int MAXC = 16; // Maximum number of colors.
const float AR = 1.05; // Aspect ratio to make circles symetrical.
const float TWOPI = 6.28; // Approximation of 2 * PI.
const Boolean CLOSED = True; // Constant to draw filled-in rectangle.
const Boolean OPEN = False; // Constant to draw open rectangle.

Chapter VII Program Correctness -7.29-


Chapter VII Program Correctness -7.30-
/*********************************************************************/
/*** GLOBAL VARIABLE IDENTIFIERS ***/
/*********************************************************************/
float SinTab[630]; // Sin lookup table.
float CosTab[630]; // Cos loopup table.
/*********************************************************************/
/*** FUNCTION PROTOTYPES ***/
/*********************************************************************/
void SetMode(Byte Mode);
void StartGfx();
void EndGfx();
void Stop();
void Pixel(int X, int Y, Byte Color);
Byte GetPixel(int X, int Y);
void Line(int X1, int Y1, int X2, int Y2, Byte Color);
void Rectangle(int X1, int Y1, int X2, int Y2, Byte Color, Boolean Filled);
void MakeTables();
void Circle(int CX, int CY, int Radius, Byte Color);
void Ellipse(int CX, int CY, int XR, int YR, Byte Color);
void Arc(int CX, int CY, int XR, int YR, float Start, float Finish, Byte Color);
void FloodFill(int X, int Y, Byte NewColor);
void ClearGfx(Byte Color);
int Round(float X);
/*********************************************************************/
/*** FUNCTION IMPLEMENTATIONS ***/
/*********************************************************************/
/*** Set Mode ***/
/*********************/
void SetMode(Byte Mode)
{
_AH = 0x00; // Select "set video mode" function (00h)
_AL = Mode; // Set mode selected.
geninterrupt(0x10); // Call video interrupt (10h).
}
/*********************/
/*** Start GFX ***/
/*********************/
void StartGfx()
{
SetMode(0x12); // Set MCGA 320x200x256 mode (13h).
MakeTables();
}
/*********************/
/*** End GFX ***/
/*********************/
void EndGfx()
{
SetMode(0x03); // Set standard text mode (03h).
}
/*********************/
/*** Stop ***/
/*********************/
void Stop()
{
getch(); // Provides a pause .
}
/*********************/
/*** Pixel ***/
/*********************/
void Pixel(int X, int Y, Byte Color)
{
_AH = 0x0C;
_AL = Color;
_BH = 0;

Chapter VII Program Correctness -7.31-


_CX = X;
_DX = Y;
geninterrupt(0x10);
}
/*********************/
/*** GetPixel ***/
/*********************/
Byte GetPixel(int X, int Y)
{
_AH = 0x0D;
_CX = X;
_DX = Y;
geninterrupt(0x10);
return _AL;
}
/*********************/
/*** Line ***/
/*********************/
void Line(int X1, int Y1, int X2, int Y2, Byte Color)
{
int XDist, YDist; // X and Y distances.
int XDir, YDir; // X and Y directions.
int XCount, YCount; // X and Y counters.
int XYInc; // X and Y incrementer.

int Sign(int Number);

if (X1 == X2)
{
int X = X1;
if (Y1 > Y2)
{
int Temp = Y1;
Y1 = Y2;
Y2 = Temp;
} // End if.
for(int Y = Y1; Y <= Y2; Y++)
Pixel(X,Y,Color);
} // End if.
else
{
if(Y1 == Y2)
{
int Y = Y1;
if(X1 > X2)
{
int Temp = X1;
X1 = X2;
X2 = Temp;
} // End if.
for (int X=X1; X<=X2; X++)
Pixel(X,Y,Color);
} // End if.
} // End else.

if ( ! ((X1 == X2) || (Y1 == Y2)) )


{
XDist = X2 - X1;
YDist = Y2 - Y1;
XDir = Sign(XDist);
YDir = Sign(YDist);
XDist = abs(XDist);
YDist = abs(YDist);
XCount = X1;

Chapter VII Program Correctness -7.32-


YCount = Y1;
XYInc = 0;
while ((XCount != X2) || (YCount != Y2))
{
Pixel(XCount, YCount, Color);
if (YDist < XDist)
{
XYInc += YDist;
XCount += XDir;
if (XYInc >= XDist)
{
XYInc -= XDist;
YCount += YDir;
} // End if.
} // End if.
else
{
XYInc += XDist;
YCount += YDir;
if (XYInc >= YDist)
{
XYInc -= YDist;
XCount += XDir;
} // End if.
} // End else.
} // End While.
Pixel(XCount, YCount, Color);
} // End if.
} // End void function Line.

int Sign(int Number)


{
if (Number < 0)
return -1;
else
return +1;
} // End function Line::Sign.
/*********************/
/*** Rectangle ***/
/*********************/
void Rectangle(int X1, int Y1, int X2, int Y2, Byte Color, Boolean Filled)
{
if (Filled)
for (int Y = Y1; Y <= Y2; Y++)
Line(X1, Y, X2, Y, Color);
else
{
Line(X1, Y1, X2, Y1, Color);
Line(X1, Y2, X2, Y2, Color);
Line(X1, Y1, X1, Y2, Color);
Line(X2, Y1, X2, Y2, Color);
} // End else.
}
/*********************/
/*** MakeTables ***/
/*********************/
void MakeTables()
{
float Radian; // Radian measure.

for (int X=0; X<=629; X++)


{
Radian = float(X) / 100;
SinTab[X] = sin(Radian);

Chapter VII Program Correctness -7.33-


CosTab[X] = cos(Radian);
} // End for loop.
}
/*********************/
/*** Circle ***/
/*********************/
void Circle(int CX, int CY, int Radius, Byte Color)
{
int X, Y; // X and Y coordinate.
for (int K = 1; K <= 629; K++)
{
X = Radius * CosTab[K] + CX;
Y = Radius * SinTab[K] * AR + CY;
Pixel(X,Y,Color);
} // End for loop.
}
/*********************/
/*** Ellipse ***/
/*********************/
void Ellipse(int CX, int CY, int XR, int YR, Byte Color)
{
int X, Y; // X and Y coordinate.
for (int K = 1; K <= 629; K++)
{
X = XR * CosTab[K] + CX;
Y = YR * SinTab[K] * AR + CY;
Pixel(X,Y,Color);
} // End for loop.
}
/*********************/
/*** Arc ***/
/*********************/
void Arc(int CX, int CY, int XR, int YR, float Start, float Finish, Byte Color)
{
int X, Y; // X and Y coordinate.

int Start2 = Start * 100;


int Finish2 = ((Finish * 100) < 629) ? (Finish * 100) : 629;
for (int K = Start2; K <= Finish2; K++)
{
X = XR * CosTab[K] + CX;
Y = YR * SinTab[K] * AR + CY;
Pixel(X,Y,Color);
} // End for loop.
}
/*********************/
/*** FloodFill ***/
/*********************/
void FloodFill(int X, int Y, Byte NewColor)
{
void RecursiveFill(int X, int Y, Byte OldColor, Byte NewColor);

Byte OldColor = GetPixel(X,Y);


RecursiveFill(X,Y,OldColor, NewColor);
}
void RecursiveFill(int X, int Y, Byte OldColor, Byte NewColor)
{
Boolean Stuck = False;

if (GetPixel(X,Y) != OldColor)
Stuck = True;
if ((X < MINX) || (X > MAXX))
Stuck = True;
if ((Y < MINY) || (Y > MAXY))

Chapter VII Program Correctness -7.34-


Stuck = True;
if (!Stuck)
{
Pixel(X, Y, NewColor);
RecursiveFill(X+1, Y, OldColor, NewColor);
RecursiveFill(X-1, Y, OldColor, NewColor);
RecursiveFill( X, Y+1, OldColor, NewColor);
RecursiveFill( X, Y-1, OldColor, NewColor);
} // End if.
}
/*********************/
/*** ClearGfx ***/
/*********************/
void ClearGfx(Byte Color)
{
Rectangle(MINX, MINY, MAXX, MAXY, Color, CLOSED);
}
/*********************/
/*** Round ***/
/*********************/
int Round(float X)
{
return ( floor(X+0.5) );
}
────────────────────────────────────────────────────────────────────────────────

Yes you guessed it, there has to be a program to make sure that all this good stuff works
correctly. So load program CH07_18 and make sure everything is in order. It takes extra
space on your disk, and it takes extra space in the book, but it is always a good habit to
know that your library works correctly.

────────────────────────────────────────────────────────────────────────────────
/**********************************************************************/
/*** CH07_17.CPP is specifically designed to call each one ***/
/*** of the voids/functions in the GFXLIB7.HPP file. This ***/
/*** program gives assurance that the routines in the GFXLIB7.HPP ***/
/*** library are ready to be used in programming. ***/
/*** GFXLIB7 contains routines that are specifically designed to ***/
/*** be used in the 0x13 (320 X 200) graphics mode. ***/
/*** The GFXLIB7 library also contains routines that check for ***/
/*** boundary violations. ***/
/**********************************************************************/
#include <iostream.h>
#include <stdlib.h>
#include "GFXLIB7.HPP"

typedef char Str40[41];

void Testing(Str40 ProcName);


void TestPixel();
void TestBdPixel();
void TestLine();
void TestBdLine();
void TestRectangle();
void TestBdRectangle();
void TestCircle();
void TestBdCircle();

Chapter VII Program Correctness -7.35-


void TestEllipse();
void TestBdEllipse();
void TestArc();
void TestBdArc();
void TestGetPixel();
void TestFloodFill();
void TestClearGfx();

/**********************************************************************/
/*** This is the program main's body. You will need to press ***/
/*** <ENTER> at the conclusion of each test to clear the screen ***/
/*** and start the next test. The FloodFill test requires that ***/
/*** you press <ENTER> for every stage inside the program. When ***/
/*** in doubt, press <ENTER> to continue with program execution. ***/
/**********************************************************************/
void main()
{
TestPixel();
TestBdPixel();

TestLine();
TestBdLine();

TestRectangle();
TestBdRectangle();

TestCircle();
TestBdCircle();

TestEllipse();
TestBdEllipse();

TestArc();
TestBdArc();

TestGetPixel();
TestFloodFill();
TestClearGfx();
} // End main program CH07_17.CPP.

void Testing(Str40 ProcName)


{
clrscr();
cout << "TESTING ROUTINE: " << ProcName << endl << endl << endl;
cout << "PRESS <ENTER> TO START TEST";
cin.ignore(80,'\n');
StartGfx();
} // End void function Testing.

void TestPixel()
{
int X, Y;
Byte Color;

Testing ("PIXEL");
randomize();
for (int K = 1; K <= 1000; K++)

Chapter VII Program Correctness -7.36-


{
X = random(MAXX);
Y = random(MAXY);
Color = random(MAXC);
Pixel(X,Y,Color);
} // End for loop.
Stop();
EndGfx();
} // End void function TestPixel.

void TestBdPixel()
{
int X, Y;
Byte Color;

Testing("BOUNDED PIXEL");
randomize();
for (int K = 1; K <= 1000; K++)
{
X = random(2*MAXX);
Y = random(2*MAXY);
Color = random(MAXC);
BoundedPixel(X,Y,Color);
} // End for loop.
Stop();
EndGfx();
} // End void function Test BdPixel.

void TestLine()
{
int X1, Y1, X2, Y2;
Byte Color;

Testing("LINE");
randomize();
for (int K = 1; K <= 100; K++)
{
X1 = random(MAXX);
Y1 = random(MAXY);
X2 = random(MAXX);
Y2 = random(MAXY);
Color = random(MAXC);
Line(X1, Y1, X2, Y2, Color);
} // End for loop.

Stop();
EndGfx();
} // End void function TestLine.

void TestBdLine()
{
int X1, Y1, X2, Y2;
Byte Color;

Testing("BOUNDED LINE");
randomize();
for (int K = 1; K <= 100; K++)

Chapter VII Program Correctness -7.37-


{
X1 = random(MAXX * 2);
Y1 = random(MAXY * 2);
X2 = random(MAXX * 2);
Y2 = random(MAXY * 2);
Color = random(MAXC);
BoundedLine(X1, Y1, X2, Y2, Color);
} // End for loop.
Stop();
EndGfx();
} // End void function TestBdLine.

void TestRectangle()
{
int X1, Y1, X2, Y2;
Byte Color;
Boolean Filled;

Testing("RECTANGLE");
randomize();
for (int K = 1; K <= 100; K++)
{
X1 = random(MIDX);
Y1 = random(MIDY);
X2 = random(MIDX) + MIDX;
Y2 = random(MIDY) + MIDY;
Color = random(MAXC);
Filled = Boolean(random(2) == 1);
Rectangle(X1, Y1, X2, Y2, Color, Filled);
} // End for loop.
Stop();
EndGfx();
} // End void function TestRectangle.

void TestBdRectangle()
{
int X1, Y1, X2, Y2;
Byte Color;
Boolean Filled;

Testing("BOUNDED RECTANGLE");
randomize();
for (int K = 1; K <= 100; K++)
{
X1 = random(MIDX * 2);
Y1 = random(MIDY * 2);
X2 = random(MIDX * 2) + MIDX * 2;
Y2 = random(MIDY * 2) + MIDY * 2;
Color = random(MAXC);
Filled = Boolean(random(2) == 1);
BoundedRectangle(X1, Y1, X2, Y2, Color, Filled);
}
Stop();
EndGfx();
} // End void function TestBdRectangle.

void TestCircle()

Chapter VII Program Correctness -7.38-


{
int CX, CY;
int Radius;
int Color;

Testing("CIRCLE");
randomize();
for (int K = 1; K <= 100; K++)
{
CX = random(MAXX);
CY = random(MAXY);
Radius = random(MIDY);
Color = random(MAXC);
Circle(CX, CY, Radius, Color);
} // End for loop.
Stop();
EndGfx();
} // End void function TestCircle.

void TestBdCircle()
{
int CX, CY;
int Radius;
int Color;

Testing("BOUNDED CIRCLE");
randomize();
for (int K = 1; K <= 100; K++)
{
CX = random(MAXX);
CY = random(MAXY);
Radius = random(MIDY * 2);
Color = random(MAXC);
BoundedCircle(CX, CY, Radius, Color);
}
Stop();
EndGfx();
} // End void function TestBdCircle.

void TestEllipse()
{
int CX, CY;
int XR, YR;
int Color;

Testing("ELLIPSE");
randomize();
for (int K = 1; K <= 100; K++)
{
CX = random(MAXX);
CY = random(MAXY);
XR = random(MIDX);
YR = random(MIDY);
Color = random(MAXC);
Ellipse(CX, CY, XR, YR, Color);
} // End for loop.
Stop();

Chapter VII Program Correctness -7.39-


EndGfx();
} // End void function Test Ellipse.

void TestBdEllipse()
{
int CX, CY;
int XR, YR;
int Color;

Testing("BOUNDED ELLIPSE");
randomize();
for (int K = 1; K <= 100; K++)
{
CX = random(MAXX);
CY = random(MAXY);
XR = random(MIDX * 2);
YR = random(MIDY * 2);
Color = random(MAXC);
BoundedEllipse(CX, CY, XR, YR, Color);
} // End for loop.
Stop();
EndGfx();
} // End void function TestBdEllipse.

void TestArc()
{
int CX, CY;
int XR, YR;
int Color;
float Start, Finish;

Testing("ARC");
randomize();
for (int K = 1; K <= 100; K++)
{
CX = random(MAXX);
CY = random(MAXY);
XR = random(MIDX);
YR = random(MIDY);
Start = random(1000)/1000.0 * TWOPI;
Finish = random(1000)/1000.0 * TWOPI;
Color = random(MAXC);
Arc(CX, CY, XR, YR, Start, Finish, Color);
} // End for loop.
Stop();
EndGfx();
} // End void function TestArc().

void TestBdArc()
{
int CX, CY;
int XR, YR;
int Color;
float Start, Finish;

Testing("BOUNDED ARC");
randomize();

Chapter VII Program Correctness -7.40-


for (int K = 1; K <= 100; K++)
{
CX = random(MAXX);
CY = random(MAXY);
XR = random(MIDX * 2);
YR = random(MIDY * 2);
Start = random(1000)/1000.0 * TWOPI;
Finish = random(1000)/1000.0 * TWOPI;
Color = random(MAXC);
BoundedArc(CX, CY, XR, YR, Start, Finish, Color);
} // End for loop.
Stop();
EndGfx();
} // End void function TestBdArc().

void TestGetPixel()
{
int X, Y;
Byte Color;

Testing("GETPIXEL");
randomize();
for (int K = 1; K <= 10000; K++)
{
X = random(MAXX);
Y = random(MAXY);
Color = random(2) + 1;
Pixel(X, Y, Color);
} // End for loop.
Stop();
for (int R = 0; R <= MAXX; R++)
for (int C = 0; C <= MAXY; C++)
if (GetPixel(R, C) == 1)
Pixel(R, C, 4);
Stop();
EndGfx();
} // End void function TestGetPixel.

void TestFloodFill()
{
Testing("FLOODFILL");
Circle(MIDX, MIDY, 50, 15);
FloodFill(MIDX, MIDY, 15);
Circle(MIDX, MIDY, 40, 0);
FloodFill(MIDX, MIDY, 0);
Circle(MIDX, MIDY, 30, 1);
FloodFill(MIDX, MIDY, 1);
Circle(MIDX, MIDY, 20, 4);
FloodFill(MIDX, MIDY, 4);
Circle(MIDX, MIDY, 10, 10);
FloodFill(MIDX, MIDY, 10);
Stop();
EndGfx();
} // End void function TestFloodFill.

void TestClearGfx()
{

Chapter VII Program Correctness -7.41-


Testing("CLEARGFX");
for (int Color = 1; Color <=128; Color++)
ClearGfx(Color);
Stop();
EndGfx();
} // End void function TestClearGfx.
────────────────────────────────────────────────────────────────────────────────

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄

Chapter VII Program Correctness -7.42-

You might also like