Transparent Clipping

Table of Contents:
What IS Transparent Clipping?
How do we accomplish this?
The Clipping Code
An example program


What IS Transparent Clipping?

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:

  1. The quick easy way
  2. The more thorough way that takes a little more work
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.

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...


How do we accomplish this?

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.

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:

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!


The Clipping Code

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!


Example Program

'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:

-----------------] Example Starts here [-----------------
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.
-----------------] Example Ends here [-----------------


  • Created by Chris Lattner