User Reports


GRAD Graphics Library

Ron Burk and Helen Custer


Ron Burk has a BSEE from the University of Kansas and has been a programmer for the past 10 years. He is currently president of Burk Labs, a small software consulting firm.

Helen Custer holds degrees in Computer Science, English, and Psychology from the University of Kansas and is currently a Senior Software Technical Wrter for a Fortune 500 company. She has coauthored books on C, GW-BASIC, and Z-BASIC.

Both may be contacted at Burk Labs, P.O. Box 3082; Redmond, WA 98073-3082.

The GRAD Graphics Library, written by Conrad Kwok, is a shareware package for drawing simple graphics images, including circles, lines, ellipses, arcs, and rectangles. It can also fill regions, display characters, and dump screen graphics to your printer. The 50-odd graphics functions are carefully documented in a 100-page users manual.

The functions are written for PC/XT/AT clones using Microsoft C v4.0. The package is written in Microsoft C and 8088 assembly language. GRAD can also be compiled with Turbo C, and directions for doing so are included with the disks; a fewer minor changes are required.

GRAD supports both CGA (640 x 200) and HGA (720 x 348) graphics cards, but unfortunately, it only supports one device at a time. You link with the library that corresponds to the device you want to use; there is no auto-detect of the graphics card adaptor. The routines are modularized in such a way that it may be possible to make them work with other graphics devices by changing one or two files of source code. However, a graphics device is not absolutely necessary, as GRAD allows you to define up to nine virtual graphics screens at run time.

GRAD also supports several printers, including the Epson FX-80, the Okidata ML192, and compatibles, or laser printers using the JLASER card. You can also configure other printers to work with GRAD.

The GRAD user's manual and assorted documentation files thoroughly document the functions that are available in GRAD. The writing is friendly and, in addition to GRAD, the files document a number of concepts relating to graphics libraries in general. It describes how fonts are viewed by a graphics package, how to use a graphics coordinate system, and what a virtual graphics screen is, among other things. Example code is provided for functions that are difficult to describe.

Pixels Vs. Lines

The graphics screen on most personal computers is pixel-oriented; it is made up of dots that you can turn on or off. A pen plotter, on the other hand, is line-oriented; everything it draws is made up of line segments. The GRAD library is oriented towards pixel graphics. For example, it supports the ability to "grab" a rectangular portion of the screen and transfer it to another part of the screen. That sort of operation could not be implemented with a pen plotter.

You could, however, use GRAD as a PC device driver for a more general set of line-oriented library functions. GRAD supplies almost all of the primitives you would need for such a project. Also, most printers support pixel graphics, so they can serve as hard-copy devices for programs that use pixel graphics. If your printer is similar to the Epson FX-80 or the Okidata ML192, you can adapt the software to work with your printer. An appendix at the back of the manual documents that process. In the general case, however, you may have to buy the source code from the author to make GRAD work with your printer.

Standard Transformations

In some kinds of graphics, you find yourself drawing the same basic symbol in slightly different ways (different proportions, different locations on the screen, and so on). Three types of transformations of graphics are commonly supported by high-level graphics libraries:

GRAD supports graphics translation by allowing you to change the value for the upper left corner, or "origin", of your frame. For example, if you wish a graphic element to appear multiple times in your final drawing, you can create a subroutine that draws the element, then call that subroutine multiple times. Between calls to the subroutine, you simply change the origin for the element.

GRAD does not support graphics scaling or rotation. If you want to draw the same symbol with different heights or widths, you must implement the scaling with your own code. Likewise, if you want to rotate a graphic image so that it appears sideways or upside-down, you'll have to write your own code to do this.

One reason you might want to do a transformation such as scaling is to solve the problem of aspect ratio. Aspect ratio is the ratio of a pixel's height to its width. GRAD assumes that each pixel is square, the same height as width. However, on a typical CGA monitor, each pixel is rectangular instead of square, that is, its aspect ratio is not 1:1.

The aspect ratio problem becomes very clear when you ask GRAD to draw a circle on a CGA monitor. It draws a true circle, but because the pixels are not square, the result on the screen is a "stretched" circle (an ellipse). In a line-based graphics library, this problem can be solved by applying the appropriate scaling transformation just before translating the line into pixels. In GRAD, however, there isn't much you can do except take the problem into account in your code and draw a rectangle to get a square, an ellipse to get a circle, and so on.

Virtual Screens

A virtual screen is just like the real screen in every way—you just can't see it. Suppose you want your graphics program to have the ability to undo the last drawing request the user made. One way to accomplish the visual part of this task is to use a virtual screen. For each user request that is not an undo request, you first perform the previous request on the virtual screen, then perform the new request on the real screen. If the request is an undo, you could simply copy the virtual screen to the real screen.

GRAD provides virtual screens which it calls "frames". A frame is a rectangular memory area where a graphic image is stored. If the memory area corresponds to video memory, then the graphic is visible on the screen. If the memory area is regular memory, the frame is a virtual graphics screen. A graphic image created in this area can only be seen by dumping it to the printer or by copying it to the video memory. Frames are especially useful for windowing operations, as described in the following section.

Drawing Attributes

GRAD allows you to specify line styles and writing modes. Normally, when you draw a line across the screen, you get a solid line. A line style, however, allows you to specify that all lines are dotted lines, or dashed lines, or almost any pattern of dots and dashes you like.

Another drawing attribute that GRAD lets you specify is the writing mode. On a pen plotter, a line is a line—you can never erase an existing line. On a graphics screen, however, there are several interesting possibilities. Usually, you want the screen to look like it would on a pen plotter. This is called OR mode, since it is accomplished by bitwise ORing the pixels to be drawn with the screen pixels' existing value. GRAD also supports an XOR mode and an AND mode.

The XOR mode can be used to "erase" lines, because if you draw a line in OR mode and then redraw the line in XOR mode, the line disappears. This isn't perfect, however; if there is a second line on the screen that intersects the first one, it will have a "hole" in it, because the pixel where the two lines intersected is turned off. You can also use XOR mode to achieve a kind of reverse-video effect, by turning on a block of pixels, switching to XOR mode, then drawing on the block.

Drawing lines in AND mode doesn't make much sense, because the only pixels that will get turned on are those that were already on. In other words, it will look as though nothing got drawn. AND mode is useful for Bit-Block Transfers, however.

Bit-Block Transfers, or bitblts (pronounced "bitblits"), are at the heart of windowing systems that operate in graphics mode. For example, moving a window from one place to another is a bitblt operation; so is removing a window (copying a block of background pattern to it). GRAD provides basic bitblt operations that allow you to transfer blocks between virtual screens and to and from files. GRAD's bitblt operations obey the current writing mode, so you can combine the block transfers with the bit-wise modes to do things like erase a window or cause a window to appear in reverse-video.

Clipping

Clipping is the ability to restrict graphics output to a specific (usually rectangular) region of the screen. For example, if you are using an inch-high strip along the bottom of the screen to display status information about your program, you want to ensure that no other part of your output strays into that area. If your graphics library supports clipping, you can define a clipping rectangle. Your program can then continue to draw anywhere it likes, but only that portion of the drawing that lies within the clipping region appears.

GRAD allows you to specify a single, rectangular clipping region called a "window". There is no on/off function to disable or enable the defined clipping region. Instead, GRAD supplies a ResetWin() function that redefines the clipping region to be the entire virtual screen (which effectively turns clipping off).

Drawing Text

Whether you are drawing business charts or flowcharts, you inevitably need to display text along with your graphics. There are two general ways to draw text in graphics mode on a pixel-oriented device. Bitmapped fonts are the kind you normally see in text mode on a PC screen. As the name implies, they are defined in terms of a set of bits that are on or off, each bit corresponding to a pixel of the overall character. Bitmapped fonts are easy to define and fast to display, but difficult to scale up and down in size, and difficult to clip except on character boundaries. Stroke fonts, on the other hand, are stored as line segments and, therefore, can usually be scaled up and down in size, stretched in any direction (to form slanted text, for example), and even rotated to arbitrary angles.

GRAD has no stroke fonts but supports bitmapped fonts. These fonts can be stored on files and loaded into memory dynamically, as needed. This is useful when you want to use many fonts, but don't want to consume a lot of memory. You can get the effect of rotated fonts (you can get one of four, 90-degree rotations) by using a specially rotated font file. There are 18 font files on the GRAD disk. Although most of these are variations on a couple of fonts, they provide good examples of what you can do. You can also make bitmapped fonts that have variable width. This looks more professional, especially with larger fonts.

GRAD also supplies a graphics input function that reads from the keyboard. This is very handy when you need to query the user while you are drawing graphics, since you will want the keyboard input to be echoed on the screen with graphic text. Remember that just calling gets() probably won't produce the desired result when the screen is in graphics mode.

Picture Segments

If you are writing a program that allows the user to manipulate the graphics drawn on the screen, you may want to provide a way for them to control units of the picture more complicated than individual pixels or lines. For example, the user of an architectural program may want to move an entire wall (including windows and doors) as a unit. Picture segments support this type of operation. A picture segment is just a sequence of drawing commands that you can store, retrieve, and use to draw the same object in a variety of places on the screen. GRAD does not provide picture segments as such, but it defines a draw() function which is a step in that direction.

draw() takes three arguments: a C string containing drawing commands and two integer arguments that can be used to parameterize the commands in the string. The key feature of the graphics commands that you store in the string is that they are relative to the current drawing coordinate. For example, here is a command that draws a rectangle at the current location.

Draw("RT10 DN5 LF10 UP5", 0, 0);
It always draws the same size rectangle; however, it could be parameterized like this:

Draw("RT%OX DN%OY, LF%OX, UP%OY", 3, 10);
In this case, the arguments to draw() alter the symbol that is specified in the command string.

Notice that you could build up command strings, save them to files, and bring them back later — just as you would use a symbol library. GRAD just supplies the basic command string ability, though. You would have to design your own functions to manage a symbol library.

Graphics Environment

If you were going to implement a symbol library, you would want the drawing of symbols to be modular. The symbol might draw in a different line style, graphics mode, or font, or use a different clipping window than the calling routine. A modular library would ensure that each symbol routine resets all these attributes back to their original values after the symbol is drawn.

Fortunately, there is an easier solution. GRAD groups attributes like the current origin, clip region, line style, font, and so on, into a bundle which it calls an environment. The modular symbol or graphics routine can simply save the current environment before it begins drawing, and restore it after all its graphics operations are complete.

Utility Programs

The GRAD disk contains several utility programs, which Conrad Kwok wrote as sample programs for the library. The first program, Interp, is an interpreter for GRAD library functions. You can place a series of GRAD function calls in a file, then give that file name as an argument to Interp. Interp interprets the graphics commands and draws the resulting graphic on the screen. This is a fast way to experiment with the library, since you don't have to recompile anything to make changes to what you're drawing.

The input to the interpreter mimics the analagous C functions. Whenever a particular function returns a value, you can simply write:

var1 = function(val1, val2, ...)
The variable that you name (in this case, var1) is created and initialized by the value returned by the function. Similarly, for functions that return values through pointers, you can type something like this:

function (&var1, &var2, ...)
The variable names available for use are hard-coded in the program, but the source to the interpreter is supplied, so you could easily extend it. Listing 1 shows a sample input file which draws an ellipse around the text "The C User's Journal".

MPrint (Merge Print) is a variation of Interp. MPrint allows you to specify a file containing lines of text that are merged with the graphics drawn by the interpreter. In other words, you can print graphics in graphics mode on the printer and print the text portions in text mode, which is much faster than printing text in graphics mode.

Distribution and Licensing

The GRAD graphics library is a good, basic, integer graphics system. It contains a complete set of primitives which could be used as a base for a more sophisticated graphics package, a floating-point package, for example. The main disadvantage of the library is that it is not written for multiple graphics adaptors. You must compile the library for a specific adaptor.

Conrad Kwok, GRAD's author, is distributing this graphics package as shareware. If you find his program useful, he requests that you send a contribution of $20 to him. If you send $20 or more, you will receive updates to the library. If you send a contribution of $60 or more, you will get the source for the latest version of GRAD, as well as a programmer reference manual which documents the internal data structures and algorithms used in the library. The source is copyrighted.

The licensing terms for GRAD are as follows:

If your program does not meet the above requirements, you must get written permission from Conrad Kwok before distributing it.