The problem with this though is that it really is the cheap way out. What we really want is to be able to call the Line procedure and have the GraphPro library automatically (transparently) pick which one we want on the fly. No, this isn't some kind of artificial intelligence, it is merely good programming...
Instead, we will rename the Line routine to be LineNC (Not Clipped), and the SetPixel routine to be SetPixelNC, etc. Okay, so now NOTHING gets called when you call Line or SetPixel. In fact, you get an error. Well to fix this, we will define the following types:
One of the biggest problems that we have when we clip things is the interface to the programmer. Instead of simply being able to call the Line procedure, we have to remember to call the LineC procedure, or else things won't work right. This is easy to fix though. There are two ways to fix this problem:
The quick easy way is just to rename all of the clipping procedures so that old Line is now called the LineNC procedure, and the old LineC procedure is now called the Line procedure. This ensures that if we forget what we are doing, we will automatically call the safer of the two procedures.
What we want to do is to make a new procedure, Clipping. What this will do is change the default state of the clipping routines. For example, the following code will draw unclipped lines:
Clipping(False);
Line(...);
Line(...);
Line(...);
To make these so that they all are clipped, we merely change the first line, and it will make all of the line routines start clipping:
Clipping(True);
Line(...);
Line(...);
Line(...);
You can even change the clipping state on the fly:
Clipping(False);
Line(...);
Line(...);
Clipping(True);
Line(...);
Line(...);
Okay, to accomplish this, there is also an easy way and a hard way. The easy way would be to make a silly little flag that gets set or cleared by the Clipping routine, and checked for in the Line routine. Unfortunately though, this would slow it down because it would have to check the state of that flag and possibly make a second call. In a routine like the SetPixel routine, an extra cmp, jmp and call would slow it down tremendously.
TYPE
IIBProcedure = PROCEDURE(X, Y : INTEGER; C : BYTE);
IIIIBProcedure = PROCEDURE(X1, Y1, X2, Y2 : INTEGER; C : BYTE);
These two types are in the form that SetPixel and Line use. IIB stands for "Integer, Integer, Byte". You can probably guess what IIIIB stands for. Now, we define two variables like this:
VAR
SetPixel : IIBProcedure; { Define SetPixel interface }
Line : IIIIBProcedure; { Define Line interface }
Whoa! What is this? What we just did was define two variables (Named SetPixel and Line) that hold a pointer to a procedure. SetPixel uses the IIB format and Line uses the IIIIB format. Now if we want to use clipped lines, we simply do this:
Line := LineC; { Tell it to use LineC }
Line(0, 0, 319, 199, 15);
Conversely, if we want to draw unclipped lines, then we do the following:
Line := LineNC; { Tell it to use LineNC }
Line(0, 0, 319, 199, 15);
Okay, now we just have to write the code for Clipping and we're done!
With the framework that we just developed, the "Clipping" routine becomes a simple little routine. All that it has to do is assign the variables Line and SetPixel to their respective routines. It looks like this:
PROCEDURE Clipping(State : BOOLEAN);
BEGIN
IF State THEN { Assign the pointers to the routines that clip }
BEGIN
SetPixel := SetPixelC;
Line := LineC;
END
ELSE { Assign the pointers to the routines that don't clip }
BEGIN
SetPixel := SetPixelNC;
Line := LineNC;
END;
END;
In the future, as we develop more routines that clip, we will add to this routine so that it is current with all of our clipping routines. If we add a call to this in the initialization of the GraphPro unit, we will be complete:
BEGIN
Screen.DBuffer := FALSE; { The screen buffer defaults to the real thing }
Screen.Buffer := PTR($A000,0);
Clipping(False); { Clipping defaults to off }
END.
In our Sprite unit, we will add routines that are similar to this, so that sprite clipping can be controlled just as easily. Anyway, on with the example!
'kay! Here is the example for the last section. It has been modified though for our new purposes. Notice how I use the LineNC routine to draw lines that aren't clipped. This just shows that you don't have to switch the clipping state every time that you want to draw a line. Anyways, here it is:
PROGRAM LineClippingExample;
USES CRT, GraphPro;
VAR
X1, Y1, X2, Y2 : INTEGER;
Font : FontType;
BEGIN
Randomize;
InitGraph; { Initialize the graphics }
ClrScr(7);
LoadBIOSFont(Font, 16); { Get a 16 point font }
WriteG1(Font, 1, 1, 'Press any key to continue or ESC to quit', 8);
WriteG1(Font, 0, 0, 'Press any key to continue or ESC to quit', 15);
SetClipBoundary(80, 50, 240, 150); { Set the clipping boundary }
Clipping(FALSE); { Turn clipping off to draw }
{ the clipping boundary }
{ Notice that half of these lines are inside of the clipping area,
and half are outside... }
Line( 80, 50, 240, 50, 7); Line( 79, 49, 241, 49, 15);
Line( 80, 50, 80, 150, 7); Line( 79, 49, 79, 151, 15);
Line(240, 50, 240, 150, 0); Line(241, 49, 241, 151, 8);
Line( 80, 150, 240, 150, 0); Line( 79, 151, 241, 151, 8);
Clipping(True);
REPEAT
X1 := Random(320); Y1 := Random(200); { Generate random lines }
X2 := Random(320); Y2 := Random(200);
LineNC(X1, Y1, X2, Y2, 9); { Draw the line without clipping }
LineC(X1, Y1, X2, Y2, 1); { Draw the line with clipping }
UNTIL ReadKey = #27;
CloseGraph; { Deinit graphics }
END.