Borland Graphics Interface
The Borland Graphics Interface (BGI) is a graphics library developed by Borland in 1987 for MS-DOS operating systems, bundled with compilers such as Turbo C, Turbo Pascal, and Turbo C++ to enable developers to create portable graphics applications across diverse video hardware.[1][2] It provided a standardized API for drawing primitives like lines, circles, and text, supporting common modes such as CGA, EGA, and VGA through loadable drivers that abstracted hardware differences.[1][3] BGI's key features included support for 16-color palettes, filled shapes, and vector-based stroked fonts, making it accessible for educational and hobbyist programming in C and Pascal during the late 1980s and early 1990s.[1][4] Third-party extensions expanded compatibility to SVGA cards, printers like HP LaserJet, and plotters, while its simple function calls—such asinitgraph(), line(), and circle()—fostered widespread adoption in DOS-era games, utilities, and tutorials.[3][5]
Though officially supported by Borland until around 1992, BGI's legacy persists through open-source ports like WinBGIm (developed from 1998 onward for Windows) and SDL_bgi (for modern systems), which maintain its API for compatibility with legacy code and introductory computer graphics education.[2][5]
Overview
Introduction
The Borland Graphics Interface (BGI) is a device-independent graphics library developed by Borland International for DOS-based programming on IBM PCs and compatible systems. Introduced in 1987 with Turbo C version 1.5, it enables developers to implement 2D graphics in C, C++, and Pascal programs without requiring direct access to underlying hardware details.[6] BGI abstracts graphics operations, allowing applications to produce drawings, text, and images portably across diverse display adapters.[7] The library's key purpose is to streamline the creation of cross-hardware compatible 2D graphics applications. By providing a unified API, BGI reduces the complexity of supporting multiple graphics modes and drivers, fostering wider adoption in DOS-era development.[6] Its scope is confined to raster-based 2D operations, including shapes, fills, and bitmapped text output, with no built-in support for 3D graphics, audio, or interactive event handling.[7] At its core, BGI relies on modular components: thegraphics.h header file and graphics.lib library for C/C++ integration, alongside the Graph unit for Pascal programs. Hardware compatibility is achieved through dynamically loadable driver files (e.g., .bgi extensions like egavga.bgi), which are selected and initialized via functions such as initgraph to detect and configure the system's graphics mode.[6][7] This structure ensures straightforward setup while maintaining focus on essential 2D raster capabilities.
Design Principles and Goals
The Borland Graphics Interface (BGI) was fundamentally designed as a device-independent graphics library to enable developers to write portable code that could run consistently across the diverse and fragmented PC graphics hardware of the 1980s, such as CGA, EGA, and VGA adapters, without requiring hardware-specific modifications. This core goal addressed the era's challenge of incompatible graphics standards, which forced programmers to rewrite applications for each display type, thereby promoting a unified API that abstracted underlying hardware differences and ensured reliable output on various systems.[8][1] A key emphasis in BGI's design was simplicity, particularly for beginners and educators, by providing a high-level API focused on intuitive drawing primitives like lines, circles, and text output, rather than demanding low-level pixel manipulation or direct hardware register access. This approach reduced the learning curve for graphics programming in DOS environments, allowing novice users to create visual applications with minimal boilerplate code while fostering conceptual understanding over intricate technical details. The library's streamlined functions prioritized accessibility, making it suitable for introductory programming courses and rapid prototyping.[8][4] However, these design choices involved deliberate trade-offs, favoring ease of use and broad compatibility over high performance or cutting-edge capabilities; for instance, BGI relied on software rendering without hardware acceleration and supported only palette-based colors (up to 16 in base modes) rather than true color depth, limiting its suitability for demanding applications. This reflected the priorities of the time, where the need for a straightforward, cross-hardware solution outweighed the inclusion of advanced features that might compromise portability on lower-end systems.[8]History and Development
Origins and Initial Release
The Borland Graphics Interface (BGI) was developed by Borland International in 1987 to provide a device-independent graphics library for DOS-based programming on IBM PC compatibles, addressing the growing need for visual applications in an era when graphical user interfaces and data visualization were emerging in the personal computing landscape.[6] This development aligned with Borland's emphasis on creating fast, integrated development environments that simplified complex tasks for programmers, building directly on the Graph unit introduced in Turbo Pascal 4.0 earlier that year, which had already established a foundation for portable graphics routines.[6] By adapting these Pascal-based routines for C, Borland aimed to extend similar ease-of-use to C developers, complementing the language's rising prominence in systems and application programming. BGI made its first public release as part of Turbo C version 1.5 in late 1987, bundled with the compiler to enable immediate access to over 70 graphics functions for drawing, filling, and text output.[6] The library supported initial drivers for common PC graphics hardware, including the Color Graphics Adapter (CGA) for basic color modes, the Enhanced Graphics Adapter (EGA) for higher resolutions, and the Hercules monochrome adapter for text-based systems, allowing programs to automatically detect and adapt to installed hardware via functions likeinitgraph().[6] These drivers were distributed as loadable .BGI files, promoting portability without requiring recompilation for different video cards.
BGI rapidly gained traction among educational institutions and hobbyist programmers in the late 1980s, becoming a de facto standard for introductory graphics programming due to Borland's Turbo series dominance in the affordable compiler market, which democratized access to professional tools.[9] Its integration with Turbo C facilitated simple yet powerful visualizations in tutorials, games, and prototypes, filling a gap left by more cumbersome hardware-specific APIs prevalent at the time.[6]
Evolution and Integration with Borland Compilers
Following its initial release in 1987 with Turbo C, the Borland Graphics Interface (BGI) underwent enhancements starting with Turbo C++ in 1989, which expanded support for additional graphics drivers to accommodate emerging hardware like EGA and early VGA adapters.[10] These updates included improved driver loading mechanisms, such as dynamic registration via functions like registerbgidriver, allowing developers to integrate custom hardware support more seamlessly within Borland's C++ environment.[11] By the late 1980s, BGI also added printer and plotter drivers, such as those for Epson-compatible devices, enabling direct output of graphics to non-screen peripherals for applications requiring hard-copy rendering.[3] BGI's integration deepened with Borland's compiler suite, bundled as graphics.lib in Turbo C and subsequent Borland C++ IDEs for easy linking in DOS-based C/C++ programs.[11] In Turbo Pascal, it manifested as the Graph unit (GRAPH.TPU), providing over 50 routines for device-independent graphics, initialized via InitGraph and supporting drivers for adapters like CGA, EGA, VGA, Hercules, and IBM 8514.[7] This embedding extended to Borland's productivity tools, where BGI powered UI elements like charts and diagrams in Paradox and dBASE for enhanced visual data presentation.[12] Key evolutions in the 1990s included the bgiobj.exe utility, introduced around 1993, which converted .BGI driver and .CHR font files to .OBJ objects for static linking, reducing runtime dependencies and executable size.[11] BGI reached its peak integration in Borland C++ 4.52 (released in 1995), featuring comprehensive driver and font support, including full ASCII character sets and scalable stroked fonts for professional DOS applications. This version, along with the 1997 update to Borland C++ 5.02, represented the height of BGI's utility within Borland's ecosystem, with backward compatibility for DOS graphics in Windows-hosted environments.[13] However, the mid-1990s shift toward Windows APIs diminished BGI's relevance for new DOS-centric development, as Borland prioritized native Windows tools like OWL, leading to the phasing out of official BGI support by 1997.[13]Technical Architecture
Core Functions and API
The Borland Graphics Interface (BGI) provides a C-language application programming interface (API) defined in thegraphics.h header file, enabling developers to perform 2D graphics operations in DOS-based environments. This API abstracts hardware-specific details through a set of functions that handle initialization, drawing, text rendering, filling, and error management, primarily using integer-based parameters for coordinates and colors.[14]
Central to the API is the initialization function void initgraph(int *graphdriver, int *graphmode, char *pathtodriver), which sets up the graphics system by loading a driver, selecting a mode, and specifying the path to driver files; the DETECT constant (value -1) allows automatic hardware detection for the graphdriver parameter.[14] Drawing primitives include void line(int x1, int y1, int x2, int y2) for rendering straight lines between two points, void circle(int x, int y, int radius) for outlining circles centered at specified coordinates, and void rectangle(int left, int top, int right, int bottom) for drawing rectangular outlines, all using the current drawing color.[14] Color selection is managed via void setcolor(int color), where the color parameter is an integer from the adapter's palette, typically ranging from 0 (black) to 15 (white) in standard modes.[14]
Text output is facilitated by void outtextxy(int x, int y, char *textstring), which displays a null-terminated string at the given position using the current font and color.[14] Fill operations encompass void floodfill(int x, int y, int border), which fills an enclosed region starting from a seed point up to a specified border color using the current fill settings, and void setfillstyle(int pattern, int color), which configures the fill pattern (e.g., 1 for solid fill, or 2–11 for hatched patterns) and associated color for subsequent filled shapes.[14]
Error handling in the API relies on int graphresult(void), which returns an integer error code from the most recent graphics operation (0 indicating success), often paired with grapherrormsg(int errorcode) to retrieve a descriptive string.[14] The API enforces limitations such as exclusive use of integer coordinates, precluding sub-pixel precision, and a fixed color palette constrained by the graphics adapter (e.g., up to 16 colors in VGA modes).[14]
| Function | Prototype | Key Parameters | Purpose |
|---|---|---|---|
initgraph | void initgraph(int *graphdriver, int *graphmode, char *pathtodriver) | graphdriver (e.g., DETECT), graphmode, pathtodriver | Initializes graphics system and mode. |
line | void line(int x1, int y1, int x2, int y2) | x1, y1, x2, y2 (integers) | Draws line between points. |
circle | void circle(int x, int y, int radius) | x, y, radius (integers) | Draws circle outline. |
rectangle | void rectangle(int left, int top, int right, int bottom) | left, top, right, bottom (integers) | Draws rectangle outline. |
setcolor | void setcolor(int color) | color (0–15) | Sets drawing color. |
outtextxy | void outtextxy(int x, int y, char *textstring) | x, y (integers), textstring | Outputs text at position. |
floodfill | void floodfill(int x, int y, int border) | x, y (integers), border (color) | Fills enclosed area. |
setfillstyle | void setfillstyle(int pattern, int color) | pattern (1–12), color (0–15) | Sets fill pattern and color. |
graphresult | int graphresult(void) | None | Retrieves last error code. |
Drivers and Hardware Compatibility
The Borland Graphics Interface (BGI) employed a modular driver system to abstract hardware differences, allowing programs to output graphics without direct hardware-specific coding. Drivers were distributed as binary .bgi files, loaded dynamically at runtime via theinitgraph function, which handled initialization and selection based on available hardware. For instance, the egavga.bgi file supported both Enhanced Graphics Adapter (EGA) and Video Graphics Array (VGA) displays. Predefined constants in the graphics.h header, such as CGA, EGA, VGA, and HERC_MONO, corresponded to these drivers, enabling developers to specify hardware types explicitly or rely on automatic selection.[15][11]
Official BGI drivers provided compatibility with a range of period-appropriate hardware, focusing primarily on IBM PC-compatible displays, printers, and plotters. Display support included adapters like the Color Graphics Adapter (CGA) at 320x200 with 4 colors, Enhanced Graphics Adapter (EGA) at 640x350 with 16 colors, and Video Graphics Array (VGA) at 640x480 with 16 colors, alongside monochrome options such as Hercules and IBM 8514. Printer drivers accommodated devices like EPSON-compatible dot-matrix models and IBM line printers, while plotter support extended to HPGL standards for vector-based output. Borland bundled these drivers with compilers like Turbo C and Borland C++, ensuring broad compatibility within the DOS ecosystem of the late 1980s and early 1990s.[15][11]
Compatibility was enhanced through features like auto-detection, where the DETECT constant in initgraph invoked the detectgraph function to identify installed hardware and select the optimal driver and mode automatically. Developers could specify the path to .bgi files via the pathtodriver parameter in initgraph, defaulting to the current directory if unspecified, which facilitated distribution across varied system configurations. For standalone executables without external dependencies, drivers could be embedded by converting .bgi files to object files using the bgiobj.exe utility and registering them with registerbgidriver, allowing self-contained programs.[15][11]
Despite its flexibility, the BGI driver system had inherent limitations tied to the DOS environment. It lacked support for USB or other modern peripherals, restricting compatibility to parallel ports, serial interfaces, and legacy expansion cards prevalent in 1980s-1990s hardware. Memory constraints were particularly acute, with a maximum 64KB buffer for graphics operations in DOS, beyond which functions like imagesize would fail, necessitating careful resource management in applications. Up to 10 user drivers could be installed, but exceeding this or encountering memory shortages triggered errors reportable via graphresult.[15][11]
Supported Graphics Modes
The Borland Graphics Interface (BGI) allows developers to select graphics modes through thegraphmode parameter in the initgraph function, where predefined constants specify resolutions and color depths compatible with contemporary hardware such as CGA, EGA, VGA, and Hercules monochrome adapters.[16] These modes are hardware-dependent, with the library loading an appropriate driver to configure the display accordingly, ensuring portability across systems without direct BIOS mode manipulation. Note that mode constant values are specific to each driver.[17]
For CGA hardware, BGI supports low-resolution modes at 320×200 pixels with 4 colors (selected from palettes like cyan/magenta/lightgray for CGAC3) via constants such as CGAC0 (value 0), CGAC1 (1), CGAC2 (2), and CGAC3 (3), alongside a high-resolution monochrome option at 640×200 pixels with 2 colors using CGAHI (4).[16] EGA extends this to higher resolutions and palettes, offering 640×200 with 16 colors (EGALO, 0) or 640×350 with 16 colors (EGAHI, 1), drawing from a 64-color palette for richer visuals.[17] VGA modes build on EGA compatibility while adding finer detail, including 640×200 with 16 colors (VGALO, 0), 640×350 with 16 colors (VGAMED, 1), and 640×480 with 16 colors (VGAHI, 2).[16] Hercules monochrome graphics are handled via HERCMONOHI (0), providing 720×348 pixels in 2 colors for text-like high-resolution output.[17]
The following table summarizes representative modes across these hardware types:
| Hardware | Mode Constant | Value | Resolution | Colors |
|---|---|---|---|---|
| CGA | CGAC0 | 0 | 320×200 | 4 |
| CGA | CGAHI | 4 | 640×200 | 2 |
| EGA | EGALO | 0 | 640×200 | 16 |
| EGA | EGAHI | 1 | 640×350 | 16 |
| VGA | VGALO | 0 | 640×200 | 16 |
| VGA | VGAHI | 2 | 640×480 | 16 |
| Hercules | HERCMONOHI | 0 | 720×348 | 2 |
restorecrtmode after graphics operations, enabling mixed text-graphics applications, while runtime queries like getmaxx() and getmaxy() return the current mode's horizontal and vertical pixel extents for dynamic adaptation.[16] However, BGI modes impose constraints including fixed pixel aspect ratios (typically 1:1.2 for non-square pixels on CGA/EGA), no support for scalable vector graphics or anti-aliasing, and a base limitation to 16 colors, though extended modes via specific drivers can reach 256 colors on compatible hardware like VGA in non-standard configurations.[17]
Programming Usage
Initialization and Basic Drawing
To initialize the Borland Graphics Interface (BGI), programs must first include the<graphics.h> header file, which provides access to all BGI functions.[18][6] The core setup occurs via the initgraph function, which takes three parameters: an integer pointer for the graphics driver (e.g., DETECT for automatic hardware detection or a specific value like VGA for 9), an integer pointer for the graphics mode (e.g., VGAHI for 2, supporting 640x480 resolution with 16 colors), and a string specifying the path to BGI driver files (e.g., "c:\\tc\\bgi").[18][11] Upon calling initgraph(&gd, &gm, path), the function loads the appropriate driver file (e.g., EGAVGA.BGI for VGA hardware), sets the specified mode, and initializes defaults such as color palette and viewport; it may override the requested mode if hardware constraints apply.[6] After initialization, developers should invoke graphresult() to retrieve an error code (0 indicates success; non-zero values signal issues like -3 for file not found), ensuring robust setup before proceeding to drawing operations.[18][11] Cleanup is handled by closegraph(), which deallocates memory, restores text mode, and releases system resources.[6][18]
Basic drawing in BGI follows a straightforward sequence starting with color configuration. The setbkcolor(color) function establishes the screen background (e.g., setbkcolor([BLACK](/page/Black)) for color 0), clearing the viewport to that hue, while setcolor(color) defines the foreground for subsequent primitives (e.g., setcolor([WHITE](/page/White)) for color 15, with valid ranges from 0 to getmaxcolor(), typically 15 in 16-color modes).[18][6] Lines are rendered using line(x1, y1, x2, y2), connecting endpoints in pixel coordinates with the current color and default solid style.[11] Rectangles form via rectangle(left, top, right, bottom), outlining the shape between opposite corners without filling.[18] Text output integrates through settextstyle(font, direction, size) to configure appearance (e.g., settextstyle(DEFAULT_FONT, HORIZ_DIR, 1) for horizontal default font at size 1), followed by outtextxy(x, y, "text") to place the null-terminated string at the specified position.[6] These operations apply within the active viewport and respect the current color settings.
The BGI coordinate system employs a Cartesian layout with the origin at (0,0) in the upper-left corner of the screen or viewport, where the x-axis extends positively rightward and the y-axis downward, differing from standard mathematical conventions by inverting the y-direction for alignment with raster displays.[11][18] Maximum extents vary by mode and hardware (e.g., 639 for x and 479 for y in VGA 640x480), retrievable via getmaxx() and getmaxy() to bound drawings appropriately.[6] Viewport management restricts output to a rectangular region using setviewport(left, top, right, bottom, clip), where coordinates are absolute to the screen, and clip (non-zero) enables boundary truncation for primitives; the viewport's local origin resets to (0,0) at its top-left upon setting.[11] This setup allows modular drawing areas, with all coordinates interpreted relative to the current viewport unless specified otherwise.[18]
Common pitfalls in BGI usage often stem from initialization failures, such as incorrect paths to driver files (e.g., omitting the directory containing EGAVGA.BGI), triggering error code -3 (grFileNotFound) and preventing graphics mode entry, which may result in a blank or unresponsive screen.[18][11] Mode mismatches, like specifying an unsupported resolution for the hardware (error -10, grInvalidMode), can similarly cause initialization to fail silently or produce a black screen without error reporting if graphresult is unchecked.[6] DOS memory limitations pose another challenge, as the 640KB conventional memory cap may exhaust during driver loading or large viewport allocations (error -5, grNoLoadMem, or -7 for operations like flood fills), particularly on systems with resident TSR programs; allocating a custom buffer via setgraphbufsize beforehand can mitigate this by reserving space explicitly.[18] Always verifying graphresult post-initialization and ensuring driver compatibility with the detected hardware (via DETECT) helps avoid these issues.[11]
Advanced Features and Error Handling
BGI provides several advanced drawing functions for creating complex shapes beyond basic lines and rectangles. Thearc function draws an outline arc centered at coordinates (x, y) from a starting angle to an ending angle, both specified in degrees, with a given radius, using the current line style and color.[19] For filled variants, the sector function renders a pie-slice sector, filling it according to the current fill style and supporting elliptical shapes via separate x and y radii parameters. Additionally, drawpoly connects a series of points defined in an array to form a polyline, while fillpoly closes and fills the resulting polygon, enabling irregular shapes like custom polygons without manual boundary calculations.
Customization of visual styles extends to patterns, lines, and colors for more nuanced rendering. The setfillpattern function allows user-defined 8x8 bitmap patterns by passing an 8-byte array representing the upper-left corner of the pattern, combined with a fill color, overriding predefined fill styles for textured fills in shapes like sectors or polygons. Line appearance is controlled via setlinestyle, which sets solid, dotted, centered-dash, or user-defined patterns, along with thickness options from 1 to 3 pixels, applied to functions like arc and drawpoly.[20] Palette manipulation through setpalette remaps one of the 16 standard colors to a new value, with extensions like setrgbpalette for direct RGB specification, enabling dynamic color adjustments in VGA modes.
Input handling in base BGI remains rudimentary, focusing on keyboard events without native mouse support. Keyboard input integrates via standard functions like getch from conio.h, which retrieves a character without echoing or requiring Enter, and kbhit to check for pending input, allowing non-blocking event loops in graphics mode.[21][22] Mouse capabilities require third-party extensions, as the core library does not include them.[23]
Error handling in BGI relies on a simple diagnostic system to identify issues in graphics operations. The graphresult function returns an integer code for the most recent error, resetting it to grOk (0) afterward, with non-zero values indicating problems like invalid drivers or memory shortages.[24] Common codes include grInvalidDeviceNum (-15) for an invalid driver number, grNoInitGraph (-1) for uninitialized graphics, and grFileNotFound (-3) for missing driver files, among others defined in the graph_errors enumerated type.[24] For textual feedback, grapherrormsg converts an error code to a descriptive string, such as "Invalid device number" for grInvalidDeviceNum, facilitating debugging without hardcoded messages.[25] Developers typically check graphresult after critical calls like initgraph to ensure robust program flow.[24]
Extensions and Variants
Official Enhancements
Borland introduced the bgiobj.exe utility as a key enhancement to streamline the distribution of BGI-based applications by embedding graphics drivers and fonts directly into executable files. This tool converts binary .bgi driver files or .chr font files into relocatable .obj object modules that can be linked into the program during compilation, eliminating the need to ship separate files and ensuring the application remains self-contained. Usage involves invoking the command-line tool withbgiobj <inputfile.bgi> (or .chr), producing an output .obj file by default named after the input; the resulting module is then registered in the source code using functions like registerbgidriver() or registerbgifont() before calling initgraph(). Detailed command-line options and integration steps are outlined in the UTIL.DOC file distributed with Borland compilers.[11][26][27]
Palette management received add-ons for VGA modes, providing limited RGB customization beyond the default 16-color set through direct hardware access. While core BGI functions like setpalette() and setrgbpalette() handled standard adjustments, developers could employ the outportb() function for fine-grained tweaks to the VGA's digital-to-analog converter (DAC) registers, setting individual RGB values (0-63) for up to 256 colors. For instance, to define a custom color, code such as outportb(0x3C8, index); outportb(0x3C9, red_val); outportb(0x3C9, green_val); outportb(0x3C9, blue_val); directly programmed the palette hardware, enabling richer visual effects in 320x200x256 or similar modes.[11]
Comprehensive documentation and sample code formed another official enhancement, with Borland's Programmer's Guide and Library Reference manuals dedicating chapters to BGI architecture, function prototypes, and best practices. These resources covered initialization, driver selection, and advanced techniques like filled polygons and text styling, often with complete example programs. Compiler distributions included ready-to-compile demos in the EXAMPLES\BGI subdirectory, illustrating practical applications from basic line drawing to complex animations, aiding developers in rapid prototyping.[26][28]