Xref: utzoo comp.unix.microport:942 comp.unix.xenix:2597
Path: utzoo!attcan!uunet!husc6!mailrus!umix!metavax!oxtrap!b-tech!m2-net!lewis
From: lewis@m2-net.UUCP (Dave Lewis)
Newsgroups: comp.unix.microport,comp.unix.xenix
Subject: Graphics Library (part 1 of 5)
Summary: Graphics primitives for AT class computers running uPort (Xenix?)
Keywords: Graphics EGA CGA Hercules Printers Microport Xenix 286 386
Message-ID: <1881@m2-net.UUCP>
Date: 1 Jul 88 03:56:58 GMT
Reply-To: lewis@m-net.UUCP (Dave Lewis)
Organization: M-NET, Ann Arbor, MI
Lines: 730

> 
> A library of graphics primitives for System V/AT (and probably any
> other Intel unix, with a bit of hacking) is now available, supporting
> common video cards and printers.  Demonstration programs are included
> with the code.
> 
> Please contact me by e-mail if you would like a copy.  If there is 
> sufficient interest, I will post source (about 130K bytes).
> 
> 	-Dave Lewis
> 

I have received enough requests to justify posting the code for this.
The code is pretty well tested on various uPort 286 configurations
(with CGA, EGA, Hercules, and dot matrix printers).  I would particularly
appreciate it if any users of Xenix 286, Venix, and the various 386 unices 
could try this out and send me the needed changes.  As soon as we have
this running on several flavors of Intel Unix, it may be appropriate
to post to the archives (yes? no? comments?).  Thanks.

			Dave Lewis
			Ann Arbor, MI
			...![ itivax umix ]!m-net!dtlewis!lewis

------------------------------  cut here  ------------------------------
# To recover, type "sh archive"
echo restoring Readme
sed 's/^X//' > Readme < to  along with any other output formatting that 
Xmay be required for text.  An alternative method, based on an undocumented
Xfeature of the lp driver, is to use minor numbers 128 and 129 of the lp
Xdriver, rather than 0 and 1.  This device will correspond to the transparent
Xmode of the lp device driver.  The commands "mknod /dev/lpt0 c 0 128",
X"link /dev/lpt0 /dev/lpt", "lpadmin -plpt -v/dev/lpt -elp", "enable lpt"
Xand "/usr/lib/accept lpt" may be used to set this up (you may need to
Xuse "mknod /dev/lpt1 c 0 129" and "link /dev/lpt0 /dev/lpt", depending on
Xwhich printer port you are using.  The lp command then becomes "lp -dlp"
X(set PRINTPROG in graphics.h accordingly).  If the environment variable
X"PLOTDEV" is defined, it will be used as the name of the output printer
X(e.g. "PLOTDEV=lpt; export PLOTDEV" to use the transparent printer device).
X
X
XHow it works:
X-------------
X
XInit() attaches the shared memory segment for your video board to your
Xprocess address space.  The pointer to this memory segment is used for
Xdirect read and write access to bit mapped graphics.  In the case of
Xa printer, init() simply allocates some memory buffers which it 
Xtreats like the bit map for the printer, then adds some escape codes
Xand spools the whole mess off to the lp program.
X
XThe screen (or printer page) is represented by a normalized address
Xspace of 32768 by 32768.  This gives a reasonable amount of resolution,
Xand allows valid addresses to be represented by non-negative short integers,
Xgiving efficient calculations and easy checking for addresses that would
Xbe "off the edge of the screen."  All of the output routines, except
Xwrite_pix(), use this normalized address space, regardless of the pixel
Xaspect ratio or the aspect ratio of the physical screen.  
X
XThe write_pix() routine uses physical pixel coordinates (row,col)
Xto access one pixel on the graphics device.  All output, whether lines,
Xdots or text, use the write_pix() routine at the lowest level.  In
Xthis way, all knowledge of the display bit map, interleaving and 
Xsegmentation are localized to the write_pix() routine.  To add support
Xfor a new device type, only write_pix() is changed, along with the
Xappropriate parameters in bitmaps.h and the init() and finish() routines
X(for initializing and releasing graphics resources, respectively).
X
XPositioning on the screen is done with a "graphics cursor," which
Xis used to position all graphics and text output.  The cursor is
Xpositioned in normalized coordinates using the movepen() routine.
XRoutines such as draw() cause the cursor to be moved to new positions.
X
XThe box() and linedraw() routines, as well as the stroke font text,
Xcall movepen() and draw() to position the cursor and draw a vector, 
Xrespectively.  This is the basis of all the graphical output, with
Xthe low level output handled by write_pix().
X
XTwo types of text output are supported.  A stroke font is invoked by
Xgrafstr(), and a bit cell font is invoked by cellstr().  Grafchar()
Xand cellchar() are single character versions which should normally
Xnot be directly called (just use the grafstr() and cellstr() versions).
XCursor() is used to position the graphics cursor in terms of character
Xrows and columns, as if it were a text cursor.
X
XThe finish() routine detaches shared memory, frees resources, and 
Xsends any output to the print queue.
X
XAll routines are written in C (no assembler at all) and use short integer
Xarithmetic wherever possible.  Long integers are used when more accuracy
Xis required, and floats and doubles are avoided wherever possible.  The
Xoverall performance is quite acceptable, even for text output, and is 
Xin fact considerably faster than similar code under MS-DOS using BIOS
Xrather than write_pix().  The development machine for this code is a
X6 MHz Zenith 241 with no 80287 coprocessor, hence the avoidance of floating
Xpoint math.
X
XBugs:
X-----
X
XPix_mode() does not set XOR mode for EGA high res graphics.  
XPix_color() does not do anything for EGA high res graphics.
X
XLimitations:
X------------
X
XNo checking is done to control ownership of the console output.  The
Xuser of the program is assumed to be at the console.  Any checks for
Xthis would have to be done by an application program using these
Xroutines.
X
XThe EGA and VGA adapters control color and writing modes (OR or XOR)
Xwith output to port addresses on the EGA/VGA card.  On System V/AT,
Xthis requires use of the outb() subroutine with write access to /dev/mem.
XSince this would require the application program to be suid to root,
Xsupport for color and writing modes has not yet been implemented.
X
XThese routines have been tested by the author only on an Everex Edge
Xvideo card, emulating CGA modes 4 and 6, and Hercules graphics.  Printer
Xoutput has been tested only on a Tandy DMP 130 (IBM printer mode).  
XOther users have verified EGA mode and various printers.
X
XThe graphics.h file has declarations for parameters that will be used
Xfor various coordinate transformations.  However, none of this is
Ximplemented at the present time.  The only coordinate system which the
Xapplication program should refer to is the normalized 2-D space used
Xto represent the screen.  It is important to realize that even an object
Xas simple as a circle will require additional coordinate transformations
Xto handle aspect ratio.
X
XCredits:
X--------
X
XThanks to John Antypas, Denis Fortin and Bill Rankin for assistance in
Xtesting and developing the code.  
X
XThe font.h and cellfont.h files contain data for stroke fonts and bit cell
Xfonts, respectively.  The copyright status of this data is uncertain.
XThe stroke font is taken from _Advanced_Graphics_in_C_, by Nelson
XJohnson (McGraw-Hill, 1987).  The copyright notice for this book specifically
Xallows copying of the code, but does not say if you need to have purchased
Xthe book to be so entitled.  I recommend the book, in any case, so you may
Xwish to buy a copy.  The bit cell font was received third or fourth hand,
Xand is presumed public domain unless I hear otherwise.  The name at the
Xhead of the data file is:
X[
X	/* RAM-Loadable Character Sets for the IBM PC
X	 Richard Wilton
X	 July 1986 */
X
XIn any case, if you can locate the font data in your machine's BIOS, you
Xcan use that instead, and you are presumably licenced to do so by virtue
Xhaving bought the machine.
X
XThe setmode.c code comes from Jeff Turner, and was posted to the net
Xa while back.  It has been modified by me for Everex Edge and Hercules
Xgraphics, and is presumed in the public domain.  
X
XThe rest of the material is copyrighted by me, but may be used and
Xcopied by anybody, as long as it is not used for commercial profit.
XI would very much appreciate hearing about any bug fixes and new 
Xdevice support that may be forthcoming.  Thanks. 
X
X
X				David T. Lewis  
X				umix!m-net!dtlewis!lewis
X				122 S. Seventh
X				Ann Arbor MI  48103
X
X				system5 dtlewis 2 2.3.0-U AT
X				Wed Jun 29 23:48:47 EDT 1988
X
X
X
XxX--EOF--XxX
echo restoring Things_to_do
sed 's/^X//' > Things_to_do < graphics.1 < xdemo.c <
X#include 
X#include 
X#include "graphics.h"
X#define MAXINT 32767
X#define NUMLINES 32
X
X#define MSG_COUNT 20
X
Xint init();
Xint linedraw();
Xint finish();
Xenum PIX_MODE pix_mode();
Xint pix_color();
Xint clear();
Xint grafstr();
X
Xvoid sig_catch() {
X	/* Clean up and exit. */
X	finish();
X	system("mode 2");
X	exit(0);
X}
X
Xstatic int msg_count = 0;
Xvoid message()  {
X	/* Write a cute message every time we get a time signal. */
X	int save_color;
X	enum PIX_MODE save_pix_mode;
X	save_color = pix_color(3);
X	save_pix_mode = pix_mode(XOR);
X	cursor(2,2);
X	cellstr("Press  to quit...");
X	pix_color(save_color);
X	pix_mode(save_pix_mode);
X	if (msg_count < MSG_COUNT)  {
X		/* Do it MSG_COUNT times, then stop. */
X		signal(SIGALRM,message);
X		alarm(1);
X		msg_count++;
X	}
X}
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
Xchar buffer[80];
Xint mode = 8;
Xlong x1;
Xlong y1;
Xlong x2;
Xlong y2;
Xint x1rate;
Xint y1rate;
Xint x2rate;
Xint y2rate;
Xlong timeval;
Xlong time();
Xstruct tm *timestruct;
Xstruct tm *gmtime();
Xvoid srand();
Xint rand();
Xint (*signal ())();
X
Xstruct endpoints {
X	int x1;
X	int x2;
X	int y1;
X	int y2;
X} buff[NUMLINES]; 
X
Xint idx_to_buff, idx;
X
X/* Initialize the line buffer to invalid values (so they don't get plotted). */
X
Xfor (idx_to_buff=0; idx_to_buff < NUMLINES; idx_to_buff++)  {
X	buff[idx_to_buff].x1 = -1;
X	buff[idx_to_buff].y1 = -1;
X	buff[idx_to_buff].x2 = -1;
X	buff[idx_to_buff].y2 = -1;
X	}
Xidx_to_buff = 0;
X
X/* Get the mode to use. */
Xif (argc == 2)  {
X	mode = 0;
X	mode = *(argv[1]) - '0';
X	init(mode);
X	strcpy(buffer,"mode ");
X	strcat(buffer,argv[1]);
X	system(buffer);
X	}
Xelse {
X	printf("usage:  xdemo mode\n\n	mode = {4,6,8,16}\n\nAssuming CGA hi-res graphics mode...",0);
X	init(6);
X	system("mode 6");
X	}
X
X/* Clear the video display. */
Xclear();
X
X/* Get the current time, and use it as a seed value for the rand() function. */
Xtimeval = time((long *) 0);
Xtimestruct = gmtime(&timeval);
Xsrand((unsigned)timestruct->tm_sec);
X
X/* Modify the slopes and starting location of the endpoint motion, to give */
X/* different patterns every time we run the program.	*/
Xx1 = rand();
Xy1 = rand();
Xx2 = rand();
Xy2 = rand();
Xx1rate = rand() & 0x0FF + 256;
Xy1rate = rand() & 0x0FF + 256;
Xx2rate = rand() & 0x0FF + 256;
Xy2rate = rand() & 0x0FF + 256;
Xif (rand() & 0x01) x1rate *= -1;
Xif (rand() & 0x01) y1rate *= -1;
Xif (rand() & 0x01) x2rate *= -1;
Xif (rand() & 0x01) y2rate *= -1;
X
X/* Draw a border and a text message. */
Xpix_color(3);
Xbox(0,0,32767,32767);
Xgrafstr("XOR Write Mode",10000,31000,220,120,800);
Xpix_color(1);
Xlinedraw(16383,6000,16383,26767);
Xlinedraw(6000,16383,26767,16383);
Xpix_color(2);
X
X/* Set for XOR mode. */
Xpix_mode(XOR);
X
X/* Fork and start flashing a cute message. */
Xif (fork())  {
X	/* Child process here. */
X	/* Set a timer to post message. */
X	signal(SIGALRM,message);
X	alarm(3);
X	for(;;) pause();
X}
X
X/* Parent process continues here. */
X
X/* Catch interrupt signals. */
Xsignal (SIGINT, sig_catch);
X
X/* Loop for a while. */
Xwhile (msg_count < MSG_COUNT)  {
X
X	/* Increment buffer pointer, wrapping around buffer. */
X	++idx_to_buff;
X	idx_to_buff %= NUMLINES;
X
X	/* Erase (redraw in XOR mode) old line. */
X	linedraw( buff[idx_to_buff].x1, 
X		buff[idx_to_buff].y1, 
X		buff[idx_to_buff].x2, 
X		buff[idx_to_buff].y2
X		);
X
X	/* Get coordinates of new line. */
X	if (((x1 += x1rate) < 0) || (x1 > MAXINT))  { 
X		x1rate *= -1;
X		x1 += (2 * x1rate);
X		}
X	if (((y1 += y1rate) < 0) || (y1 > MAXINT))  {
X		y1rate *= -1;
X		y1 += (2 * y1rate);
X		}
X	if (((x2 += x2rate) < 0) || (x2 > MAXINT))  {
X		x2rate *= -1;
X		x2 += (2 * x2rate);
X		}
X	if (((y2 += y2rate) < 0) || (y2 > MAXINT))  {
X		y2rate *= -1;
X		y2 += (2 * y2rate);
X		}
X
X	/* Store it in buffer. */
X	buff[idx_to_buff].x1 = x1;
X	buff[idx_to_buff].y1 = y1;
X	buff[idx_to_buff].x2 = x2;
X	buff[idx_to_buff].y2 = y2;
X
X	/* Draw new line. */
X	linedraw( buff[idx_to_buff].x1, 
X		buff[idx_to_buff].y1, 
X		buff[idx_to_buff].x2, 
X		buff[idx_to_buff].y2
X		);
X	}
X
X/* Restart the pattern. */
X
Xfor (idx_to_buff=0; idx_to_buff < NUMLINES; idx_to_buff++)  {
X	buff[idx_to_buff].x1 = -1;
X	buff[idx_to_buff].y1 = -1;
X	buff[idx_to_buff].x2 = -1;
X	buff[idx_to_buff].y2 = -1;
X	}
X
Xidx_to_buff = 0;
X
Xclear();
X
X/* Loop until  key is pressed. */
Xfor (;;)  {
X
X	/* Increment buffer pointer, wrapping around buffer. */
X	++idx_to_buff;
X	idx_to_buff %= NUMLINES;
X
X	/* Erase (redraw in XOR mode) old line. */
X	linedraw( buff[idx_to_buff].x1, 
X		buff[idx_to_buff].y1, 
X		buff[idx_to_buff].x2, 
X		buff[idx_to_buff].y2
X		);
X
X	/* Get coordinates of new line. */
X	if (((x1 += x1rate) < 0) || (x1 > MAXINT))  { 
X		x1rate *= -1;
X		x1 += (2 * x1rate);
X		}
X	if (((y1 += y1rate) < 0) || (y1 > MAXINT))  {
X		y1rate *= -1;
X		y1 += (2 * y1rate);
X		}
X	if (((x2 += x2rate) < 0) || (x2 > MAXINT))  {
X		x2rate *= -1;
X		x2 += (2 * x2rate);
X		}
X	if (((y2 += y2rate) < 0) || (y2 > MAXINT))  {
X		y2rate *= -1;
X		y2 += (2 * y2rate);
X		}
X
X	/* Store it in buffer. */
X	buff[idx_to_buff].x1 = x1;
X	buff[idx_to_buff].y1 = y1;
X	buff[idx_to_buff].x2 = x2;
X	buff[idx_to_buff].y2 = y2;
X
X	/* Draw new line. */
X	linedraw( buff[idx_to_buff].x1, 
X		buff[idx_to_buff].y1, 
X		buff[idx_to_buff].x2, 
X		buff[idx_to_buff].y2
X		);
X	}
X}
XxX--EOF--XxX
-- 
Dave Lewis
Ann Arbor, MI
...![ itivax umix ]!m-net!dtlewis!lewis