Professional Documents
Culture Documents
CH07
CH07
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│
└──────────────────────────────┘
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.
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
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.
────────────────────────────────────────────────────────────────────────────────
#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_02.CPP.
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
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 main()
{
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.
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 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.
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)
{
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.
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.
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
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.
#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 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 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;
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
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.
#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 main()
{
StartGfx();
MakeTables();
BoundedCircle(300, 180, 50, 15);
Stop();
EndGfx();
} // End main program CH07_09.CPP.
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"
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.
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"
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();
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.
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.
────────────────────────────────────────────────────────────────────────────────
#include "GFXLIB5.HPP"
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.
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
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"
struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.
void main()
{
StartGfx();
MakeTables();
Circle(MIDX, MIDY, 100, 15);
Flood(MIDX, MIDY, 14);
Stop();
EndGfx();
} // End main program CH07_13.CPP.
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)
────────────────────────────────────────────────────────────────────────────────
/*******************************************************************/
/*** CH07_14.CPP shows an alternate method of using an ***/
/*** artificial queue in a stack-like manner for more pixels. ***/
/*******************************************************************/
#include "GFXLIB5.HPP"
struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.
void main()
{
StartGfx();
MakeTables();
Circle(MIDX, MIDY, 100, 15);
Flood(MIDX, MIDY, 14);
Stop();
EndGfx();
} // End main program CH07_14.CPP.
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);
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.
#include "GFXLIB5.HPP"
struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.
void main()
{
StartGfx();
MakeTables();
Circle(MIDX, MIDY, 100, 15);
Flood(MIDX, MIDY, 14);
Stop();
EndGfx();
} // End main program CH07_15.CPP.
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);
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"
struct Coord
{
unsigned int X; // X coordinate.
unsigned int Y; // Y coordinate.
}; // End struct Coord.
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.
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)
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.
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
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.
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 (GetPixel(X,Y) != OldColor)
Stuck = True;
if ((X < MINX) || (X > MAXX))
Stuck = True;
if ((Y < MINY) || (Y > MAXY))
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"
/**********************************************************************/
/*** 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 TestPixel()
{
int X, Y;
Byte Color;
Testing ("PIXEL");
randomize();
for (int K = 1; K <= 1000; K++)
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++)
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()
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();
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();
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()
{
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄