
This is a fast framebuffer-level graphics library for linear 1, 2 and 3
byte-per-pixel modes (256-color, hicolor, truecolor). It uses a limited
number of functions from svgalib (libvga) for low-level hardware
communication (the library is included in the svgalib shared image).
In particular, svgalib maps the 64K VGA frame buffer window, and this library
directly addresses the buffer. For SVGA modes that use more than 64K of
screen memory, SVGA paging is required when writing to the physical screen;
this is done automatically for most functions (at a certain cost).
Alternatively, any number of virtual screens of any type in system memory can
be used, which can then be copied to the physical screen. There is also
support for 4 bytes per pixel framebuffers (and copying them to a 3 bytes per
pixel context), and limited planar 256 color mode support (copyscreen,
aligned putbox).

The planar 256 color modes (available on all VGA cards) can now be used
with a virtual screen, which is copied to the physical screen (with optional
page-flipping).

Bitmaps are raw, with one (or more) bytes per pixel (like pixmaps in X),
stored in row-major order. They are usually manipulated with the getbox
and putbox functions.

A graphics context is just a structure that holds the size of the associated
graphics screen, how it is organized, clipping status etc. You can define a
custom virtual (system memory) graphics context of any size with the
setcontextvirtual function. All operations work on the current context.

Any questions, bug-reports, additions, suggestions etc. are welcome.


Functions (all have a "gl_" prefix):

	- simple line drawing 

	setpixel( int x, int y, int c )
		Draw a single pixel at position (x, y) in color c. The lower
		8, 15, 16 or 24 bits of the color are significant, depending
		on the number of colors the current mode supports. 

	setpixelrgb( int x, int y, int r, int g, int b )
		Draw a single pixel at (x, y) with color components r, g,
		and b, ranging from 0 to 255. In 256 color mode, only
		meaningful if the RGB palette is set (setrgbpalette).

	int getpixel( int x, int y )
		Returns color of pixel at position (x, y).

	getpixelrgb( int x, int y, int *r, int *g, int *b )
		Store color components from pixel at (x, y) ranging from 0
		to 255, into integers pointed to by r, g and b.

	int rgbcolor( int r, int g, int b )
		Returns pixel value that corresponds with the given color
		components. Used by setpixelrgb.

	line( int x1, int y1, int x2, int y2, int c )
		Draw a line from (x1, y1) to (x2, y2) inclusive in 
		color c.

	hline( int x1, int y, int x2, int c )
		Draw a horizontal line from (x1, y) to (x2, y) in color c.

	circle( int x, int y, int r, int c )
		Draw a circle of radius r in color c.


	- box (bitmap) functions 

	fillbox( int x, int y, int w, int h, int c )
		Screen position (x, y), box size (w, h).
		Fill a rectangular area of the screen with a single color.

	getbox( int x, int y, int w, int h, void *dp )
		Bitmap data pointer dp. Bitmaps are in row-major order.
		Copy a rectangular bitmap from the screen to a buffer.

	putbox(	int x, int y, int w, int h, void *dp )
		Bitmap data pointer dp.
		Copy a bitmap to a rectangular area of the screen.

	putboxpart( int x, int y, int w, int h, int bw, int bh, void *bp,
	int xo, int yo )
		Copy a partial bitmap to the screen. (w, h) is the size
		of the partial bitmap, (bw, bh) that of the source bitmap,
		and (xo, yo) is the offset in pixels into the source bitmap.

	putboxmask( int x, int y, int w, int h, void *dp )
		As putbox, but do not write bitmap pixels of color zero.

	copybox( int x1, int y1, int w, int h, int x2, int y2 )
		Copy the rectangular area at (x1, y1) of size (w, h),
		to (x2, y2) (bitblit).

	copyboxtocontext( int x1, int y1, int w, int h, GraphicsContext *gc,
	int x2, int y2 )
		Copy the rectangular area at (x1, y1) of size (w, h), to
		position (x2, y2) in the context gc.


	- compiled bitmaps (linear 256 color mode only)

	compileboxmask( int w, int h, void *sdp, void *ddp )
		Convert rectangular masked bitmap of size (w, h) at sdp to
		a compressed format that allows faster drawing, which is
		stored at ddp. Allocating w * h bytes for the compiled
		version is usually enough; an upper limit should be 
		(w + 2) * h.

	int compiledboxmasksize( int w, int h, void *sdp )
		Returns the size of the compiled version of the masked
		bitmap of size (w, h) at sdp that will be generated by
		compileboxmask.  

	putboxmaskcompiled( int x, int y, int w, int h, void *dp )
		Write compiled bitmap to screen. This is significantly
		faster than non-compiled masked bitmaps, especially for
		sparse bitmaps.


	- clipping
	
	enableclipping()
		Enable automatic clipping in most functions.

	disableclipping()
		Disable clipping.

	setclippingwindow( int x1, int y1, int x2, int y2 )
		Set the clipping window to the rectangle with top-left
		corner (x1, y1) and bottom-right corner (x2, y2) (incl.).


	- graphics contexts and virtual screens

	setcontextvga( int mode )
		Set the graphics context to the physical screen of a
		vga mode (as defined in svgalib). The mode must be set
		first. The only thing you can do with a planar (mode X-like)
		256 color mode is aligned putbox, and as a target for
		copyscreen.

	setcontextvgavirtual( int mode )
		Allocate a virtual screen in system memory identical to
		the graphics mode, and make that the current graphics
		context.

	setcontextvirtual( int w, int h, int bpp, int bitspp, void *vbuf )
		Define the current graphics context to have a width of w
		pixels, height h, bpp bytes per pixel, bitspp significant
		color bits per pixel (8, 15, 16 or 24), with the framebuffer
		at vbuf. A 4 bytes per pixel context, with 24 significant
		color bits is also valid.

	getcontext( GraphicsContext *gc )
		Save the current context in a structure variable.

	setcontext( GraphicsContext *gc )
		Restore a previously saved context (make it the current
		context).

	freecontext( GraphicsContext *gc )
		Free the space allocated for the virtual screen in the
		given context.

	copyscreen( GraphicsContext *gc )
		Copy the current graphics context contents (screen data) to
		the specified graphics context (the physical screen, for
		example). Contexts are assumed to be identical in size.

	setscreenoffset( int o )
		Set the offset in pixels into video memory for copyscreen
		and copyboxtoscreen, allows for page-flipping. Must be a
		multiple of the scanline width in bytes. It is reset to
		zero after completion of copyscreen.

	int enablepageflipping( GraphicsContext *gc )
		Enable page flipping or triple buffering in copyscreen if
		the physical context gc can do it. Returns 3 if triple
		buffering will be used, 2 for page flipping, 0 if page
		flipping is not possible (due to video memory/mode
		limitations). When pageflipping is enabled, the screenoffset
		is ignored in copyscreen.


	- text writing, font handling

	setfont( int fw, int fh, void *fp )
		Use the font stored as character bitmaps at fp, with 
		characters of size (fw, fh), as the basic font for write
		operations. Note that the font included in the library must
		be expanded first, because it is stored bit-per-pixel.

	setwritemode( int m )
		Either WRITEMODE_OVERWRITE or WRITEMODE_MASKED. If the latter
		is selected, only foreground pixels of the font are used for
		write operations; the screen background is not erased.

	write( int x, int y, char *s )
		Write string s at (x, y) using currently selected font.

	writen( int x, int y, int n, char *s )
		Write n character string s at (x, y).

	expandfont( int fw, int fh, int c, void *sfp, void *dfp )
		Convert bit-per-pixel font at sfp, with characters of size
		(fw, fh), to an expanded font of character bitmaps, stored at
		dfp (size will be 256 * fw * fw * BYTESPERPIXEL). All
		non-zero pixels are set to color c.

	colorfont( int fw, int fh, int c, void *fp )
		Set all nonzero pixels in the font to color c.


	- VGA 256-color palette handling

	These functions are only valid in 256-color modes.

	getpalettecolor( int c, int *r, int *g, int *b )
		Get red, green and blue values (in the range 0-63) of 
		color c from the color-lookup-table, and store them as
		integers in the memory locations pointed to by r, g and b.  

	setpalettecolor( int c, int r, int g, int b )
		Set RGB values (0-63) for color c.

	getpalettecolors( int s, int n, void *dp )
		Get RGB values of n colors starting at s, which are stored
		as a table of groups of three bytes at dp. 

	setpalettecolors( int s, int n, void *dp )
		Set RGB values of n colors starting at color s.

	getpalette( void *p )
		Equivalent to getpalettecolors(0, 256, p).

	setpalette( void *p )
		Equivalent to setpalettecolors(0, 256, p).

	setrgbpalette()
		Set 256-color RGB palette (bits 0-2 blue, 3-5 green,
		6-7 red). Use with setpixelrgb.


	- miscellaneous

	clearscreen( int c )
		Fill the entire screen with color c.

	scalebox( int w1, int h1, void *sdp, int w2, int h2, void *ddp )
		Scale the bitmap of size (w1, h1) at sdp to size (w2, h2)
		and store it at ddp, which must be a large enough buffer.
		The pixel size of the current graphics context is used.

	setdisplaystart(int x, int y)
		Set the physical display start address to the pixel at (x,
		y). Can be used for hardware scrolling, or for page flipping
		(e.g. setdisplaystart(0, HEIGHT) displays from the second
		page). Make sure the scanline width (BYTEWIDTH) in bytes
		of the current context corresponds with the physical
		screen.


Macros defined in vgagl.h:

	WIDTH		The width in pixels of the current graphics context.
	HEIGHT 		Height in pixels.
	BYTESPERPIXEL	Number of bytes per pixel (1, 2, 3 or 4).
	BYTEWIDTH	Width of a scanline in bytes.
	COLORS		Number of colors.
	BITSPERPIXEL	Number of significant color bits.
	VBUF		Address of the framebuffer.
	__clip		Clipping flag.
	__clipx1	Top-left corner of clipping window.
	__clipy1
	__clipx2	Bottom-right corner of clipping window.
	__clipy2


Use the environment variable GSVGAMODE to select the graphics mode used by
the testgl program (e.g. export GSVGAMODE=G640x480x256).

It may be interesting to compare the framebuffer video speeds for certain
modes, especially among Vesa Local Bus cards (even if only 320x200x256
works). There is considerable variation in performance among VLB motherboard
implementations and cards. If you want, you can mail them to me
(hhanemaa@cs.ruu.nl) and I'll collect the results at this place. Please state
CPU, card type/chipset, and bus type (of the card)/(ISA) bus speed.

The speedtest program is using aligned REP STOSL (memset), which is
relatively slow on a 486 (4 cycles); unrolled MOV's may be faster. It does
show that many VLB cards have in fact only a 16-bit bus interface (like my
Cirrus 5426 based card). The maximum throughput using STOSL on a 386 (5 
cycles) is about 15M/s at 16-bit VLB 40MHz, 30M/s with 32 bits. Anyone with
an ET4000/W32(i) to show off?

The 'fun' program displays a frame rate that also very dependent on general
memory architecture (it uses two virtual screens). Notice that the frame rate
has a significant variation between runs (up to 15% on my machine), but it
doesn't change within a run. I believe this is because of pages mapping to
the same external cache slot, which happens quite often with a direct mapped
cache (a bad thing for a multi-tasking VM OS). This means that if you were
to run a small but memory-intensive program taking say a day to calculate
something, it would be a good idea to display timed progress information at
the start. Restarting until the speed is good would possibly save several
hours of computation time.

Note: there seem to be VLB motherboards around that have problems with
(sound) DMA when writing video data to the local bus (possibly related to
running the VLB in 'slave' mode).

				  bus	256-color		hicolor	truec.
card			system	 speed	320x200	640x480	800x600 640x480 640x480
AVGA3, CLGD5426, VLB	386DX/40  40.0	12.5	13.7	12.8	 7.6	 4.6
--same, high MCLK	386DX/40  40.0	15.5	15.5	15.5	11.7	 8.4
--same  		486SX/40  40.0	15.6	15.6	15.6	11.7	 8.4
--same, low write delay	486SX/40  40.0	   -	19.5	   -	   -	   -
Trident 9000, 512K ISA  386DX/40   8.0	 2.1	 2.2	 2.2	   -	   -
Trident 8900C, ISA	386DX/33	 2.6	 3.1	 2.1	   -	   -
Tseng MegaVGA/1024 ISA	486DX/33  13.3	 8.5	 8.5	 8.5	 8.5	 6.0
--ET4000, AT&T491/2 DAC	386SX/40  10.0	 4.8	 4.8	 4.8	 4.8	 4.8
Trident 9000, 512K ISA  486DX/33  13.3	 2.9	 2.3	 2.5	   -	   -
S3-805 VLB (stosl)	486DX2/66 33	10.8	   -	   -	   -	   -
--same (stosw)		486DX2/66 33    16.0	   -       -       -       -
ATI Ultra Pro EISA	486DX/50   8	 6.2     6.2     6.2      6.2    6.2


Note

	For three bytes per pixel (true color) modes, it is possible that
	pixels cross a SVGA segment boundary. This should be correctly
	handled	by most functions. It can be avoided by using a logical
	scanline length that is a divisor of 65536 (a power of 2), like 1024
	(as opposed to 960) for 320x200 and 2048 (1920) for 640x480. For
	800x600, this is impractical (4096 as opposed to 2400 doesn't fit in
	2MB). Alternatively, avoid those functions by using a virtual screen.


Question: How do I poll the keyboard without waiting?

One way to do it is to mess with termio parameters like this:
(This is what the vga_getkey function does.)

#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>

int getkey() {
	struct termio zap, original;
	char c;
	int e;

	ioctl(fileno(stdin), TCGETA, &original);	/* Get termio */
	zap = original;
	zap.c_cc[VMIN] = 0;				/* Modify termio  */
	zap.c_cc[VTIME] = 0;
	zap.c_lflag = 0;
	ioctl(fileno(stdin), TCSETA, &zap);		/* Set new termio */
	e = read(fileno(stdin), &c, 1);			/* Read one char */
	ioctl(fileno(stdin), TCSETA, &original);	/* Restore termio */
	if (e == 0 || e == -1)
		return 0;				/* No key pressed. */
	return c;					/* Return key. */
}

You can have complete keyboard control by using RAW mode. An example should
be in the kbd-08? package (which is hard to find; I think it is on
nic.funet.fi, /pub/Linux/PEOPLE/Linus or similar).


Q: What's the fuss about a DLL library?
A: The building of a DLL library includes a conversion, at the assembler
   level, of all references in the library code of exported global variables
   into indirect references with some overhead added to preserve register
   values. This is not very efficient if the global variables are used all
   over the place (e.g. currentcontext and its fields). The testgl program
   ran 20% slower because of this. Therefore, the library internally uses a
   copy of currentcontext that is not exported. This means that the current
   context may only be changed with vgagl functions.
