--------------------------------- ZipGraph: The Low-Level Routines! --------------------------------- version 1.20 1 November, 1989 written by: ----------- Scott Robert Ladd 705 West Virginia Gunnison CO 81230 BBS (303)641-5125 FidoNet 1:104/708 Legal Disclaimer (Yuck) ======================= The software presented in this package is public domain. You may do anything you please with this software. As such, there are no warranties or guarantees whatsoever as to the quality, suitability, or general functionality of this software. If you use this software, you accept full responsibility for what it does or doesn't do. Personally, I hate these disclaimers; however, in the litigious world we live in, you can't even give something away for free without covering yourself. Frankly, I suspect most people are generally appreciative that I've made this stuff available to them. This software was developed using Zortech's C 2.01 on a 20Mhz 80386-based PC running MS-DOS 3.30. It's also been tested with Mcirosft C 5.10 and QuickC 2.01, Borland Turbo C 2.0, and Lattice C 6.01. I suspect other compilers will work, too, but I haven't had time to try them. One last request: if you do use this module, I'd like to hear from you. I'm interested in any changes you might make. If you find any bugs (!), drop me a line, and I'll see what I can do. Again, since this is free, I can't make any guarantees about how quickly any bug will get fixed. But I will do my best! Introduction ============ This document should be part of an archived package, which contains a complete C-language module for the low-level portion of the ZipGraph graphics library for PCs using MS-DOS. Included in the archive should be: ZG_LWLVL.H -- the required header file ZG_LWLVL.C -- C language cource code ZGTEST.C -- a program to test the above ZG_LWLVL.DOC -- This document! If you do redistribute this code, please do so with all of the files together in an archive. While I use LHARC to distribute this package, you can use any archiver you want. Just keep these three files together, since that's how they belong. General Documentation ===================== The following is an excerpt reprint from my Januray-February 1990 Cing Clearly column in Micro Cornucopia Magazine, where this module first appeared. The files in this archive are a part of a library called ZipGraph, which provides several levels of functionality. These are the low-level routines, which handle basic tasks such as the determination of the installed adapter type and the plotting of pixels. Future segments will cover drawing primitives, clipping, region filling, and other basic tasks. In the highest level of this library, I will present a series of C++ classes, built on the lower-level C code, which will handle advanced routines to handle ray tracing, 3D modelling, and animation. An obvious question might be: why write a graphics module? Not only are there dozens of commercial graphics libraries for C, but almost every compiler vendor now includes their own graphics routines. What does ZipGraph offer that others don't? I had several goal in mind when building ZipGraph. To begin with, I wanted it to be fast. The current version is more than twice as fast as any other graphics library I have tried. I was surprised to find that my C-language version of the low-level graphics routines was nearly as fast as the one I had built in assembler language. The advantages of having easily maintainable C source far outweighed the few percentage points loss in speed when compared to my assembler language implementation. My second goal was to make ZipGraph portable. As time passes, more and more of my article involve programs which do graphics. Alas, C compiler vendors do not have any interest in making their proprietary graphics libraries compatible. If I write a program using the Borland Graphic Interface (BGI) included with Turbo C, that program won't compile with any other C compiler. Rather than shut people out, I decided to build a library which compiled with all of the popular compilers. The version of ZipGraph presented here compiles with Borland Turbo C 2.0, Lattice C 6.01, Microsoft C 5.10 and QuickC 2.01, and Zortech C/C++ 2.01. In addition, the resulting object modules can be linked to Microsoft Fortran 5.0 and Stony Brook Modula-2 2.01. My final reason for writing ZipGraph is that I find commercial libraries limited. For example, most do not include printer routines, and some do not support certain graphics adapters. On top of that they lack fundamental capabilities, such as a function to generate non-orthagonal ellipses. No graphics library I know of completely supports ray tracing, animation, object rotation, and 3D plotting. Additionally, commercial libraries are written using C and assembler language only, without utilizing the object- oriented features of C++. As you'll see in future columns, C++ provides the ability to do some fantastic things. The ZG_LWLVL module, presented here, is the basis of all the other modules in the ZipGraph system. It uses some of the more powerful features of C, and provides the basic functions for detecting the type of graphics adapter installed in your machine, and supports the plotting and reading pixels on all common IBM adapters. As mentioned earlier, this module is almost 850 lines long, making it the longest piece of code ever published in this column. Subsequently, I'll have to forego a long detailed discussion of ZG_LWLVL. Instead, I'll be focusing on the techniques used to make it work. IBM PCs and their compatibles were originally designed to be modular; in spite of IBM's recent move toward building video hardware into the computer's motherboard, most vendors have maintained the ability to install whatever kind of video adapter you want. Currently, there are six standard video adapters in common use in PCs: the Monochrome Display Adapter (MDA), the Color Graphics Adapter (CGA), the Hercules Graphics Card (HGC), the Enhanced Graphics Adapter (EGA), the Multi-Colored Graphics Array (MCGA), and the Video Graphics Array (VGA). One problem PC software has always faced is that any one of these video adapters can be installed in a PC. While most of the adapters are supported by the PC's BIOS, the Hercules card is not. I'm sure most of you have had the experience of finding a pieces of graphics software which will not run on your computer. Some graphics libraries allow you to "detect" which kind of video adapter is installed. Borland's Graphic Interface (BGI) has this capability. However, the BGI uses external drivers, which are loaded at run time. While you can convert the BGI drivers to object modules, you then have to go through a clumsy procedure to link them in and make the program aware of their resident status. Other "auto-sensing packages" fail to support certain adapters, or are just simply clumsy. I've always been a believer that computers should do work for you. ZipGraph not only detects the presence of all of the above adapters, it also automatically sets up its routines so that you can write one program with which works with all of those adapters. The type of adapter you're working with is as transparent as possible. Listing 1 shows ZG_LWLVL.H, the header file which must be included in any source program which uses the LG_LWLVL module. Listing 2 is ZG_LWLVL.C, the implementation of the module. Finally, Listing 3 shows one of my test programs, ZGTEST.C, which will give you an example of how to use the ZG_LWLVL module. We'll start with a quick synopsis of how to use the ZG_LWLVL functions. First, you need to call ZG_Init to initialize the module. ZG_Init detects the adapter installed in the PC, and saves its initial status. Information on the adapter will be returned in the public ZG_VideoInfo structure. Then you need to set a graphics mode using the ZG_SetMode function. It sets the video mode you request, assigns the proper pixel plotting and reading function to the function pointers ZG_PlotPixel and ZG_ReadPixel, and returns information on the new graphics mode in ZG_VideoInfo. You can then plot pixels by calling the function pointed to by ZG_PlotPixel, a read pixels via the function pointer ZG_ReadPixel. When your program is done, it should call ZG_Done to restored the video adapter to its pre-program state. Examining ZGTEST.C will show you the details of how this all fits together. No let's examine the above process in detail. When ZG_Init is called, it attempts to identify the video adapter installed in your computer. There isn't an built-in way to determine the adapter type, but we can use the process of elimination. ZG_Init() begins be calling an MCGA and VGA BIOS function which returns the adapter type. If an MCGA or VGA BIOS is installed, this call will tell us which one of those it is. If the adapter installed is not a VGA, the call to this BIOS function will fail. Once we've eliminated the VGA, we call an EGA BIOS function. Again, if the EGA BIOS is not present, the function will not return expected values, and we know that an EGA is not present. If no EGA is found, we ask the BIOS for the hardware information word. We check the appropriate bits to see if a color or monochrome adapter is installed. A color adapter will be a CGA at this point (since we have eliminated the other color adapters). If a monochrome adapter is installed, our final task is to differentiate between an MDA and a HGC. This is done by monitoring the vertical synch bit of the monochrome card's status register; if the bit changes, we have a Hercules card. Information on the adapter's type and its installed monitor are placed into the ZG_VideoInfo structure. Constants for these values are defined in the ZG_LWLVL.H files can be used to make you code a bit clearer. At this point, all ZipGraph has done is determine what kind of video adapter you have. I've tested the detection routine on several computers with a variety of adapters installed. So far, it has worked flawlessly with VGA, EGA, MDA, and Hercules adapters. I do not have a CGA or MCGA adapter available to me, but I was able to test those routines on my Paradise 16- bit VGA card, which emulates the CGA and MCGA. I'd appreciate hearing from you if you have problems. In order to display graphics, you now need to set a graphics mode. You do this by calling the ZG_SetMode function, passing it as its first parameter one of the ZG_MOD constants defined in ZG_LWLVL.H. ZG_SetMode will return 1 if the requested mode is not valid for the adapter detected. Information on the mode will again be returned in the public global variable ZG_VideoInfo. That information includes the x and y dimensions of the new graphics mode, and the number of colors available. If all goes well, ZG_SetMode will return 0 to indicate that success. ZG_SetMode also does some automagical work for you. Two special graphics modes are ZG_MOD_BESTRES and ZG_MOD_MOSTCOLOR. Respectively, they represent -- for the detected adapter -- the best possible resolution, and the resolution providing the greatest selection of colors. The global static array VideoTable contains the actual modes for both of these modes, for each adapter. In addition, VideoTable also contains a bit mask which indicates the valid modes for a given adapter. The requested graphics mode is compared against the ModeList value for a given adapter type, to be sure it is valid. If it isn't, ZG_SetMode returns an error. Once it has determined the validity of the requested graphics mode, ZG_SetMode uses the information stored in the global static array ModeData to do the actual mode set-up. ModeData contains the equivalent BIOS mode for the mode requested, the addresses of the appropriate pixel plotting and reading functions, and the dimensions and color counts for each mode. When the mode is set, ZG_SetMode assigns the appropriate values in the ModeData table to the function pointers ZG_PlotPixel and ZG_ReadPixel. We are ensured that for each graphics mode, the correct pixel plotting and reading routines are set -- all automatically. Once that is done, ZG_SetMode assigns values from the ModeData table to the width, height, and color count values of the ZG_VideoInfo structure. It should be noted here that the Hercules card is handled in a special manner by ZG_LWLVL.C. Since the HGC is not supported by the PC BIOS in any way, it's graphics and text modes must be set via special functions. All the ZipGraph functions do is make exceptions to their normal set-up by calling the special Hercules functions rather than the BIOS (as used for the other graphics cards). And we're ready to plot pixels! Different video modes require different pixel plotting and reading routines, which is why I use pointers to the functions for these tasks. ZG_PlotPixel and ZG_ReadPixel can be used exactly as if they were regular functions. However, they do different things based on the specific type of graphics adapter installed. Their value is set based on the graphics mode you requested via ZG_SetMode. In a way, you can look upon this as a form of polymorphism (an object-oriented programming concept) for C. This document has already run far longer than it should, so I won't go into the specifics of how the individual pixel plotting and reading functions work. If you're dying to know, pick up a copy of Richard Wilton's Programmer's Guide to PC & PS/2 Video Systems (ISBN 1-55615-103-9), by far the best reference ever published about the nuts and bolts of PC graphics programming. I doubt ZG_LWLVL will remain static. The most obvious enhancement is to include support for the "Super VGA modes, such as 800 x 600 graphics. Additionally, it would be nice to add the capability to use some of the non-documented VGA modes, such as 320 by 400 pixels with 256 colors. But all in good time. I suspect this issue's source code is more than enough for most of you to "chew" on for a while. VERSION HISTORY! ---------------- Version 1.00 was built and released quickly because of a magazine article deadline. Later, as I looked at it, I saw that the performance could be increased dramatically. So, that's what I did, generating version 1.10. 1.20 corrected some minor bugs in the handling of modes which are only rarely used, such as 320 by 200 by 16 color and EGA monochrome mode.