The HALO Graphics Library

Victor Volkman


Victor R. Volkman received a BS in Computer Science from Michigan Technological University in 1986. Mr. Volkman is a frequent contributor to The C Users Journal and the C Gazette. He is currently employed as Software Engineer at Cimage Corporation of Ann Arbor, MI. He can be reached at the HAL 9000 BBS, (313) 663-4173, 1200/2400/9600 baud.

The HALO Graphics Library by Media Cybernetics, Inc. supports device-independent graphics programming with more than 200 functions. HALO provides device drivers for dozens of vector and bitmap graphics boards, dot matrix and laser printers, page scanners and video digitizers, graphics tablets, mice, and plotters. Some of the more popular graphics boards supported include CGA, EGA, VGA, Extended-VGA, MCGA, PGA, Hercules, and AT&T Targa. HALO for DOS lists at $395. HALO for OS/2 lists at $695 and is source-code compatible with the DOS version.

The BARGRAPH application in Listing 1 demonstrates the style and ease with which HALO can be integrated with C programs.

System Requirements

HALO makes only modest system hardware requirements. It will run on an IBM XT, AT, 3270 PC, AT&T 6300 or other true compatible computer with a base memory of 256k RAM. The computer must have at least one supported graphics device. Additionally, HALO requires MS-DOS v2.1 or later. For software development, you must have any one of the supported languages: Microsoft MASM v5.0, BASICA, QuickBASIC v4.0, Turbo BASIC v1.0, Lattice C v3.0+, Microsoft C v3.0+, Turbo C, Microsoft FORTRAN, Ryan-McFarland FORTRAN, Gold Hills Lisp, Microsoft Pascal, or Turbo Pascal v4.0. Development for the BARGRAPH application was completed on a 12.5 Mhz AT-compatible with 640K RAM and an Everex EV-640 graphics card (CGA and Hercules compatible). The BARGRAPH program was compiled with Microsoft C 5.1 and linked with the small model HALO library.

HALO is a graphics kernel system structured like a layercake (see Fig. 1) . Each layer may only talk to the layers directly above and below it. On the top layer is your source application program as written in any supported language (C, BASIC, Pascal, etc.). The application program layer contains many function call references to HALO . Since each language has its own parameter passing mechanism, a language binding layer is needed. The language binding presents the function arguments to the graphics kernel in a standard fashion.

The operations of the graphics functions themselves are split between the graphics kernel and device driver layers. The graphics kernel is linked into your application program. It performs the device independent functions such as polygon drawing, text manipulation, and viewport management. The components in the device driver layer speak directly to the hardware. Typical graphic device driver functions are vector drawing and bitmap panning. For maximum flexibility the device drivers are loaded dynamically.

HALO supports devices with four types of color palette management: devices which have a fixed set of colors that cannot be changed; devices which allow you to switch between several predefined palletes; devices which support more colors than can be displayed at one time (e.g. IBM EGA); and devices which support a programmable palette. (In the third case, colors are changed by specifying both index and bitmask for the palette.)

HALO supports devices in modes up to 16 bits per pixel (65,536 colors). In general, the bits of the same magnitude (i.e. power of 2) of each pixel in the display are referred to collectively as a bit plane. The graphics bitmap is defined as the sum of all the bit planes. The actual physical mapping of pixels in memory varies enormously between various graphics cards and their display modes. Fortunately, the HALO device drivers sufficiently hide this information so the programmer need never be concerned with such low-level details.

The HALO package supports three different types of coordinate systems: device coordinates, world coordinates, and normalized device coordinates. Which system you choose depends entirely on your requirements for device-independence. HALO provides functions to convert between any of the coordinate systems.

Dealing with aspect ratios is an important part of the graphic environment. Aspect ratio is used to convert from the perfect mathematical coordinate plane to the real-world imperfect graphics device. Specifically, the aspect ratio is the ratio of a pixel's width to its height. For example, the IBM EGA displays 640 x 350 pixels on a display 9.6" wide and 6.0" high. Each pixel is

9.6 inches / 640 pixels = 0.015 inches / pixel (width)
7.2 inches / 350 pixels = 0.0205 inches / pixel (height)
Click Here for Equation

HALO automatically corrects circles, ellipses, and arcs for aspect ratio. If the correction was not applied, then a circle with a 100 pixel radius would appear to be 100 x 0.015 = 1.5 inches wide and 100 x 0.0205 = 2.05 inches tall. HALO always corrects in the vertical component so that it would actually produce a circle 100 x 0.015 = 1.5 inches wide and 100 x 0.73 x 0.0205 = 1.5 inches tall. However, it is strictly up to the programmer to include aspect ratio in his own calculations for boxes, lines, and other objects.

Graphics Objects

HALO offers the programmer all the necessary tools for drawing a variety of graphics objects including filled polygons and spline curves. All of the drawing operations make use of the graphics cursor. The graphics cursor is an invisible reference point on the display. The graphics cursor may be set at an absolute location or moved relative to its current position. The graphics cursor is used as the first point from which all line and polyline functions are drawn. It is also the center point for circle, arc, pie wedge, and ellipse drawing functions. Lastly, the graphics cursor is the starting point for fill functions.

Since lines are the most frequently used graphics objects, they require the most flexibility. HALO will draw a line from the current graphics cursor to a relative position (line relative) or to an absolute position (line absolute). You may specify both the width in pixels and the style of the line. HALO offers three basic line styles and seven user-defined line styles.

HALO has built-in functions for creating filled circle, pie wedge, box, and polygon shapes. Objects may be filled in the current color as solid or with a hatch style. Objects may be filled as they are drawn or filled later with a flood-fill function. HALO offers five basic hatching styles and five user-defined hatching styles.

In addition to geometric objects, HALO supports three types of graphics text: dot text, fast text, and stroke text. Dot text is a general purpose bitmapped font. HALO includes six dot text fonts, whose height and width may be scaled in integer multiples. Dot text may be drawn in any of the four compass directions. HALO maintains a special cursor called a text cursor for dot and stroke text.

Fast text is a special purpose bitmapped font whose data is taken from the graphics board's own ROMs. Additional font files are thus not used for fast text. Fast text may only be drawn at integer row and column text positions. Additionally, fast text may only be drawn from left to right.

Stroke text is HALO's most sophisticated text display. Stroke text is not defined as a bitmapped font but rather as a series of brush strokes or vectors. Since stroke text is displayed as vectors, it uses all the current line settings. Stroke text may be sized and rotated to any angle desired. When using stroke text drawn at an angle, the programmer must consider the aspect ratio of the display. The BARGRAPH program uses only the stroke text to achieve the highest quality image. Fig. 6 summarizes the tradeoffs between the various HALO text display schemes.

Advanced Features

HALO has a variety of features essential to the development of advanced graphics applications including area moves, rubberband functions, and the "Virtual Rasterizer Interface". Area moves involve copying from one part of the bitmap to another. The movefrom() and moveto() functions allow a rectangle of the display to be cut and pasted respectively. The moveto() function allows the buffer to be pasted in one of several modes including XOR, AND, OR, and complement.

Rubberband functions, like area moves, are designed to make interactive graphics programs easier to write. A rubberband object is one that can be stretched and dragged across the graphics screen without disturbing it. For example, you could write a simple function which polled the mouse to interactively position the endpoint of a vector. Each successive call of a rubberband function deletes (XORs) the previously displayed object and simultaneously writes it at a new position. HALO supports rubberband lines, boxes, and circles.

The "Virtual Rasterizer Interface" (VRI) allows you to create a virtual graphics display of any horizontal, vertical, and color resolution desired. VRI will use any combination of MS-DOS base memory, EMS memory, and disk space to store the image. The most common use of VRI is to assemble an image for a laser printer page. For example, an "A" size drawing (8.5" x 11") at 300 dpi is effectively a 2550 x 3300 pixel image, requiring just under one megabyte of storage. Once the VRI device is initialized, it accepts the same HALO calls as any other raster device. A VRI can be configured for up to a 16383 x 16383 resolution image or 32 megabytes, whichever is smaller.

BARGRAPH - A Small Application For HALO

The BARGRAPH demonstration application produces high-quality charts simply and efficiently. BARGRAPH takes a language-driven approach to specify the parameters of a chart. The PC-DOS usage of this program is "BARGRAPH datafile" where datafile is a plain ASCII file containing command strings. Each command string specifies a single detail of the chart such as the scale or legend. BARGRAPH input files include the HALO specific configuration data as well as the actual graph data. A typical BARGRAPH data file is shown in Fig. 7.

The operation of the BARGRAPH program is roughly divided into two phases. In the first phase, the datafile is parsed a line at a time and stored into the cmd_data[] static structure. The function process_graphics_cmd_line() is called once per input line. This function determines the command keyword and parses its arguments into the appropriate slot of cmd_data[]. The DATA, COLORS, MODE, and SCALE commands call parse_delimited_number_list() to store data in the numeric half of the udata union. Similarly, the LEGEND, FONT, TITLES, and DEVICE commands call parse_delimited_string_list() to place data in the string array half of the udata union. The COMMENT and END commands serve only documentation purposes and are thus ignored. The complete BARGRAPH syntax is diagrammed in Fig. 8.

The second phase uses data supplied in the static structures to setup the HALO environment and plot the graph on the screen. The HALO environment is established in two phases. First, the function setup_halo_globals() both inquires about the capabilities of, and sets the parameters for, the graphics device. A global structure called halo, devised expressly for this program, tracks HALO environment values throughout the program. The setdev() and initgraphics() functions must be the first two HALO calls in an application program. These load the device driver from disk and set the hardware graphics mode respectively. The remainder of the HALO calls in setup_halo_globals() set the degree mode, world coordinate rectangle, line width, line style, drawing color, and the stroke text font and color (see Fig. 9)

The function setup_graph_globals supervises the second phase of initialization. A global structure graph separates the BARGRAPH program data from the HALO data. The graph structure holds data in a form which will simplify calculations later. If the user does not supply SCALE Y-Axis upper and lower bounds, BARGRAPH will use the min and max data points as the scale range.

The bar graph is drawn by draw_bar_graph(). First, draw_axes() produces the axes in three steps. First, the legend string, horizontal X-Axis, and vertical Y-Axis are drawn at predefined coordinates. Secondly, tick marks and their labels are drawn along the Y-Axis. (The draw_axes() function makes a total of ten ticks above the Y-Axis.) Finally, the title for each bar is drawn below the X-Axis at a 45 degree angle — the angle keeps titles from overwriting each other. Since each stroke text character is a different size, the inqstsize() function must be called to determine the actual space required for each title string.

Once the axes are complete the bars are placed on the screen. If the graphics device is monochrome or the user has not specified any bar colors then a sequence of four hatch styles will be used. This ensures that default graphs are displayed similarly on monochrome and color graphics devices. The equations for determining the bar size are shown in Fig. 10.

Improving BARGRAPH

Some simple enhancements which might greatly increase the utility of BARGRAPH include the following:

(1) Read the HALO-dependent commands (DEVICE, PRINTER, MODE, etc.) from a default configuration file (e.g. BARGRAPH.CFG) so they need not be repeated in each data file.

(2) Add aspect-ratio calculations to standardize the look of the graph.

(3) Add line graph and pie-slice graph types to the program. Create a new command called CHART to specify the graph type.

(4) Allow the user to capture the graph display and save it with a file format which can be read by desktop publishing programs.

Conclusion

The HALO Graphics Library by Media Cybernetics is a highly useful programming tool for developing your own graphically oriented programs. The versatility, efficiency, and functionality of HALO are easily demonstrated by BARGRAPH. The BARGRAPH applications program as presented required less than two dozen different functions out of the 200 offered in HALO. The executable file amounts to just under 100K plus about 12K for device drivers, a fairly modest memory requirement. The most important contribution to BARGRAPH is the ability to operate with any combination of the dozens of screen and printer drivers that HALO offers.

Rasters, Pixels, Vectors, Palletes — Elements Of The Graphic Environment

Graphics objects may be constructed from pixels or vectors. A pixel (or Picture Element) is the smallest resolvable discrete point on a graphics device. Graphics devices addressable only by pixels are known as raster devices. The resolution of a raster-type graphics card or mode is expressed in pixels. For example, the minimum resolution of the IBM EGA card is 640 columns x 350 rows of pixels. In the special case of a monochrome display, a pixel directly corresponds to a single bit in display memory. Color displays require more than one bit per pixel to describe the color of the pixel. For example, the IBM EGA card uses four bits per pixel to produce a total of 24 = 16 colors.

In contrast, vectors are line segments defined by a starting point, direction, and length. Although every raster device can display vectors, vector devices do not have bitmaps and cannot display pixels, as such. For example, a pen plotter typically has no knowledge of vectors it has already drawn. Certain hybrid graphics devices, such as the Control Systems Artist, accept both raster and vector data.

Every graphics device, raster and vector, has a finite set of discrete displayable colors called the palette. On color devices, each pixel is displayed in the color corresponding to its index in the palette. For example, the IBM EGA has a palette of 16 colors out of 64 available. Fig. 2 shows a portion of an example IBM EGA palette: a pixel with index of 15 would be bright white (all bits set) whereas a pixel with index of 3 would be dull red (only 1 red bit set).

The most flexible graphics devices support a programmable palette. Programmable palette devices allow you to specify integer values for the amount of Red, Green, and Blue (RGB) components of each color. For example, the Number Nine Revolution in 832 x 624 resolution has a palette of 16 colors. Each index of the palette has 256 possible values for each RGB color component.

Coordinate Systems

The HALO package supports three different types of coordinate systems: device coordinates, world coordinates, and normalized device coordinates. Which system you choose depends entirely on your requirements for device-independence. A summary of the coordinate systems is presented in Fig. 10. HALO provides functions to convert between any of the coordinate systems.

The device coordinate system maps each logical coordinate directly to its physical coordinate or pixel. In the device coordinate system, the upper-left corner of the screen is at (0,0) and the lower-right corner is at the maximum coordinate. For example, on the Hercules Monographics card with a resolution of 720 x 350 the upper-left corner is (0,0) and the lower-right hand corner is (719,349) (see Figure 3) . In HALO, device coordinates have the advantage that they can be expressed in integers rather than floats.

Since device coordinates are dependent on the resolution of the output device you use, they are a poor choice for writing portable applications. The world coordinate system allows you to specify your own resolution independently of the hardware. This coordinate translation means that even though the Hercules and IBM CGA cards have different heights and widths, your program can operate exactly the same for both of them.

When enabled, HALO will translate from world coordinates to device coordinates automatically. For example, if you were to define the world coordinates from (-100.0,-100.0) to (100.0,100.0) then a reference to (0.0,0.0) would map to the center of the display. World coordinates assume a Cartesian orientation. In HALO, world coordinates are expressed as floats rather than integers. The BARGRAPH program uses a world coordinate system from (0.0,0.0) to (1.0,1.0).

Normalized Device Coordinates (NDCs) are another way of mapping from logical coordinates to physical coordinates. NDCs are like device coordinates because the upper-left corner is always the origin of the screen (see Figure 4B) . NDCs differ from device coordinates in that the location of lower-right corner of the screen is always the same regardless of the actual output device being used. The only difference between NDCs and world coordinates is that the upper-left corner and lower-right corners are fixed at (0.0,0.0) and (1.0,1.0) respectively in the NDC system. NDCs are used in the HALO function set-viewport() to allow viewports (i.e. windows) to be nested in a device-independent way.

A viewport is a region of the display into which graphics are mapped. By default, the viewport includes the entire screen from (0.0,0.0) to (1.0,1.0) in NDCs. After setting a viewport, all graphics calls in world coordinates will map into the new viewport. Only one viewport can be in effect at any time. for example, to put a viewport in the upper-right hand quadrant of the screen you would specify (0.5,0.5) and (1.0,1.0). Figure 5 shows a bargraph mapped into the upper-right quadrant specified.

Figure 1

Figure 2 Example Palette for IBM EGA

Figure 3 Example Device Coordinates

Figure 4a Example World Coordinates

Figure 4b Normalized Device Coordinates

Figure 5

Figure 6 HALO '88 Graphics Text Summary

              Display   Drawing  Display
Text Type     Quality   Speed    Flexibility
Stroke text  High      Slow      High
Dot text     Medium    Medium    Medium
Fast text    Low       Fast      Low

Figure 7 Example BARGRAPH data file

COMMENT this is a test of the bargraph application
DEVICE HALOHERC.DEV
MODE 0
PRINTER HALOEPSN.PRN
ATTRIBUTES -1,-1,0,0,0,0,0,0,-1,0,0,-1,1,-1,-1,-1,0
FONT HALO104.FNT
COLORS 1,2,3
LEGEND 1989 Projected Sales
TITLES Jan,Feb,Mar,Apr,May,Jun,
TITLES Jul,Aug,Sep,Oct,Nov,Dec
SCALE 0.0,200.0
DATA 10.0,42.0,130.0,80.0,54.3,140.0
DATA 180.0,135.0,300.0,69.0,94.7,101.0
END

Figure 8 Complete BARGRAPH Command Syntax

COMMAND                MEANING                               DEFAULT
-------                -------                               -------
COMMENT                Documentation only                     N/A
DEVICE     s1          Name of HALO screen device             HALOIBMG.DEV
PRINTER    s1          Name of HALO printer device            none
FONT       s1          Name of HALO stroke font to use        HALO104.FNT
LEGEND     s1          Legend is centered over top of graph   none
TITLES     s1,s2...sn  Titles are displayed underneath BARs   none
MODE       v1          Graphics mode (device dependent)       0
ATTRIBUTES v1,v2...vn  Printer attributes (device dependent)  none
SCALE      v1,v2       Set extent of Y-Axis from v1...v2      autoscaling
DATA       v1,v2...vn  Input n data values (may be repeated)  none
COLORS     c1,c2...cn  Color pattern to use for bars          monochrome hatch
END                    Signifies end of a graph               N/A

Figure 9

Initialization of HALO '88 in setup_halo_globals()

setdev(halo.device);              /* Initialize the graphics device  */
setdegree(&halo.degree_mode);           /* Use degrees, not radians  */
setworld(&halo.x1,&halo.y1,&halo.x2,&halo.y2); /* World rectangle  */
setlnwidth(&halo.lnwidth) ;                /* Line width is 1 pixel  */
setlnstyle(&halo.lnstyle);                 /* Line style is solid  */
setcolor(&halo.maxcolor);      /* Max screen color is usually white  */
setfont(halo.font);                    /* Load font from disk file  */
setstclr(&halo.maxcolor,&halo.maxcolor) ;  /* Set stroke text color  */

Figure 10

                    Upper-left   Lower-right     Hardware
Coordinate System     Corner        Corner       Independence
Norm. Device Coord  0,0,0,0       1,0,1,0             Yes
World Coordinates   User-defined  User-defined        Yes
Device Coordinates      0,0       Hardw. depend.      No
Figure 11