Steve Brailsford has eight years experience working with computer graphics systems. He is currently working as a software engineer for the Vitro Corporation on their Man-Machine Interface project. Responsibilities include writing graphics device drivers, C2 prototypes, and researching usability and training of C2 systems. Contact the author at Vitro Corporation, Dept AT 4-1417A, 14000 Georgia Ave., Silver Spring, MD 20906 (email: fsb@sparc.vitro.com).
An interesting way to learn about computer graphics is to write your own paint program. I needed a paint program that could store images with some information encoded on some of the graphics objects. I looked at using one of the popular CAD packages to produce drawings. I planned to read in a popular format, like DXF, and then embed the needed information. The platform was specified as a PC running MS-DOS. The display had to be at least 640x480 with 256 colors. There were going to be lots of drawing files and each file needed to be loaded and displayed within three seconds. This time constraint was the driving factor for much of the program design.
User Interface
The first decision was the user interface of the program. A paint program feel, with windows and pull-down menus at the top of the screen, was what I was looking for. Windows and menus should be easy to use, and pulldown menus at the top take up only a small part of the screen so there is as much drawing room as possible.PC graphics packages on the market handle graphics object drawing, windows, and menu functions. I evaluated a few of these packages and chose the Halo graphics and window library. Window libraries typically have a simple main routine. You initialize some structures and then call the window-initialization function. Then the window manager takes over the program and you get control back on an event, such as a mouse-button press. There are many events, so you can control much of what goes on in the program. When a new event occurs, you can perform some action or ignore it.
File Format
The key to fast picture loads is the file format. I chose a display-list format over a bitmap format because the objects can be stored by specifying a description instead of storing every pixel on the screen. I started with a header in the file, which consists of six bytes for a format code, 80 bytes for a description string, and a long word to store a pointer into the file. The format bytes are used to encode a version number for this file format (e.g., ver001.) The description stores a quick summary of the drawing. The pointer was used because some tables are stored after the header and the display-list data. The program begins with a pointer to the start of the display list. The pointer to the end of the display list initially points to the start of the display list.The display list is a list of graphics objects, stored with a byte holding a function code. The function code specifies the kind of object to draw (see Listing 1) . The next byte holds the color for the object. Two bytes store line width and line type, or text width and height. After these are two integers that hold the x and y coordinates of the insertion point for the object. The insertion point is used to make object editing easier, as I'll show later. These bytes make up a static portion of the graphics object.
The next data bytes depend on the function code. A polygon needs several points specified and a text code needs a string of text characters. The coordinates for the other vertices of a polynomial are stored as relative coordinates. Relative coordinates are calculated as the difference in x and y coordinates between the current vertex and the previous vertex. The insertion point is also the first point of the object.
With this display list format, most files were only a few thousand bytes instead of 640x480 bytes (307,200 bytes). Fewer bytes mean faster loading and, as long as there are not too many graphic objects, the drawing speed will be very fast too. The drawings I expected to display were not large enough to raise issues of drawing speed.
Drawing Objects
The first function needed is one to draw the graphics objects as they are read from the display list (see Listing 2) . The static part of the object is read and the function code determines subsequent reads. This routine also is used when the display needs to be redrawn, such as when artifacts are left by editing functions. For example, when a line is deleted, the system may not draw the line's pixels the same way every time. Deletions are performed by drawing the object with the background color. This works fine except when the object crosses another object. In this situation a redraw is done to restore the image.The most complex task is handling editing functions. In bit-map graphics, editing is done on a pixel by pixel basis. With display-list graphics, more functionality can be gained. The major functions of editing graphic objects are move, delete, and copy.
A move can be accomplished by changing the insertion x, y point, redrawing the object to reflect its new position. A more interactive method is to use a move mode selected from a pull-down menu. You replace the cursor with a box and click the box over one of the object's vertex points. The display list can be searched for coordinates within the box quickly. When one is found, you redraw the object with a special highlight color. Once the object is highlighted, the cursor is changed back into cross hairs and the selected object moves when the cursor is moved. You move the object using the XOR function. Most graphics libraries have a function to set XOR mode when drawing graphics. When XOR mode is active the color drawn is XORed with the current pixel color. When the cursor moves to a new position, the object is XOR drawn again at the old position and XOR drawn in the new position. The second XOR draw will replace the original pixel color at the old position. This method provides a simple and fast way to move objects around the screen. Once the mouse button is clicked, you draw the object at its new position in its true color and draw the object at its original position in the background color. This causes problems only when the original position crossed another object. Then the redraw can be used to clean things up.
A copy is handled in the same way as a move, except you don't draw over the original object, and you must add a copy of the object's display-list description at the end of the display list and change the insertion point to that of the new object. To delete an object, select the object with the pick box, as before, and then draw over it in the background color, redrawing any other objects affected.
There are many implementation decisions in a graphics program. The solutions I chose suited my application well, but exploring the alternatives was entertaining and educational.
Listing 3