[comp.sys.att] graphics question on the 3B1

walker@edge.UUCP (Dan Walker) (08/07/87)

I just set up my 3B1 and I have a question. I would like to write some
educational type games for my daughter to play on the system, and I need to
know where I can find terminal type information on how to send grahics
characters to the screen. I would like to draw some animation type stuff.
If anyone can point me to some documentation, I would sure appreciate it, and
I would be happy to make available anything I develope. Thanx all.

I would also be interested in anything like this that may already be written.
===============================================================================

                                                    Dan Walker (N7GLK)
	Pay no attention to that man behind         Edge Computer
	the curtain.                                Scottsdale, Arizona
						 ...!ihnp4!mot!edge!walker
					      ...!seismo!ism780c!edge!walker
						       and now at
					  ...!ihnp4!mot!edge!gtx!ariel!daddy

	When the odds are 50/50, chances are 10 to 1 against you.

egray@fthood.UUCP (08/11/87)

I found very little documentation on the bit mapped graphics routines and
found a few errors in what documentation there was.  (I don't rememeber
exactly what the errors were...)

I've written a program to read Macintosh "MacPaint" files and display them
on the bit mapped screen.  This source code might be useful as an 
example to others...

Emmet P. Gray				US Army, HQ III Corps & Fort Hood
...!ihnp4!uiucuxc!fthood!egray		Attn: AFZF-DE-ENV
					Directorate of Engineering & Housing
					Environmental Management Office
					Fort Hood, TX 76544-5057

------------------------------------------------------------------------------
/*
 * Program to display and print Macintosh MacPaint images
 *
 * Emmet P. Gray			US Army, HQ III Corps & Fort Hood
 * ...!ethos!gizzmo!fthood!egray	Attn: AFZF-DE-ENV
 * ...!ihnp4!uiucuxc!fthood!egray	Directorate of Engineering & Housing
 *					Environmental Management Office
 *					Fort Hood, TX 76544-5057
 */

#include <stdio.h>
#include <tam.h>

#define INVERSE

unsigned short pic[720][36];
int row, col, next_row, hi_byte = 1;
					/* table for transposing bits */
unsigned char xp_table[256] = {0, 128, 64, 192, 32, 160, 96, 224, 16, 144,
	80, 208, 48, 176, 112, 240, 8, 136, 72, 200, 40, 168, 104, 232, 24,
	152, 88, 216, 56, 184, 120, 248, 4, 132, 68, 196, 36, 164, 100, 228,
	20, 148, 84, 212, 52, 180, 116, 244, 12, 140, 76, 204, 44, 172, 108,
	236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 130, 66, 194, 34, 162,
	98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10, 138, 74, 202, 42,
	170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134, 70, 198,
	38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 142, 78,
	206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1, 129,
	65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9,
	137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
	5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117,
	245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189,
	125, 253, 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51,
	179, 115, 243, 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219,
	59, 187, 123, 251, 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87,
	215, 55, 183, 119, 247, 15, 143, 79, 207, 47, 175, 111, 239, 31, 159,
	95, 223, 63, 191, 127, 255};

main(argc, argv)
int argc;
char *argv[];
{
	FILE *fp;
	int choice, next;
	short win;
	char name[80];

	winit();
	if (!iswind()) {
		fprintf(stderr, "Sorry, readmac requires a bit-mapped screen\n");
		wexit(1);
	}
					/* create the window */
	win = wcreate(1, 0, 24, 80, BORDVSCROLL); 
	clear();
	wgoto(win, 0, 0);

	if (argc != 2) {
		wprintf(win, "Readmac - program to display and print Macintosh MacPaint images\n\n");
		wprintf(win, "up	move the image up 20 pixel rows\n");
		wprintf(win, "down	move the image down 20 pixel rows\n");
		wprintf(win, "P	Print the image\n");
		wprintf(win, "L	Load another image\n");
		wprintf(win, "Q	Quit\n\n");
		wprintf(win, "Enter the file name : ");
		wgets(win, name);
	}
	else
		strcpy(name, argv[1]);

	start:
	row = 0;
	col = 0;
	next_row = 0;
	if (!(fp = fopen(name, "r"))) {
		wprintf(win, "Can't open '%s' for read\n", name);
		wprintf(win, "Enter the file name [or control-c to quit] : ");
		wgets(win, name);
		goto start;
	}

	wprintf(win, "Reading the data file...\n");
	if (read_data(fp)) {
		wprintf(win, "Bad file or not in MacPaint format\n", name);
		sleep(3);
		clean_up(win);
		wexit(1);
	}
	fclose(fp);
	clear();
					/* show first 240 rows */
	wrastop(win, pic, 72, 0, 0, 0, 0, 0, 0, 576, 240, SRCSRC, DSTSRC, 0);

	wprintf(win, "\033[=1C");	/* turn cursor off */

	while (1) {
		choice = wgetc(win);
		switch (choice) {
			case 033:	/* escape character '^[' */
				next = wgetc(win);
				if (next != '[') {
					wputc(win, 07);
					break;
				}
				next = wgetc(win);
				switch (next) {
					case 'B':	/* roll down key */
					case 'S':	/* arrow on border */
						roll(1, win);
						break;
					case 'A':	/* roll up key */
					case 'T':	/* arrow on border */
						roll(0, win);
						break;
					default:
						wputc(win, 07);
						break;
				}
				break;
			case 'P':
			case 'p':	/* print the file */
				wgoto(win, 0, 0);
				print_it(win);
				clear();
					/* repaint the image */
				wrastop(win, pic, 72, 0, 0, 0, 0, 0, 0, 576, 240, SRCSRC, DSTSRC, 0);
				break;
			case 'L':
			case 'l':	/* load another image */
				wgoto(win, 0, 0);
				wprintf(win, "\033[=0C");
				wprintf(win, "Enter the new file name : ");
				wgets(win, name);
				wprintf(win, "\033[=1C");
				goto start;
			case 3:
			case 'Q':
			case 'q':	/* quit */
				clean_up(win);
				wexit(0);
				break;
			default:
				wputc(win, 07);
				break;
		}
	}
}

/*
 * The data file uses a straight forward compression technique.  If the
 * 'index' byte is greater than 127, then repeat the next character 
 * (257-index) times.  If the 'index' byte is less than 127, then read
 * the next (index+1) bytes.
 */

read_data(fp)
FILE *fp;
{
	int count, repeat, c;
	unsigned char byte;
					/* skip the 640 byte header */
	if (fseek(fp, 640L, 0))
		return(1);

	count = 0;
	repeat = 0;
	while ((c = fgetc(fp)) != EOF) {
					/* ignore trash after row 720 */
		if (row == 720)
			return(0);

		byte = c & 0xff;
		if (count) {		/* read the next 'count' bytes */
			count--;
			store_video(byte);
			continue;
		}
		if (repeat) {		/* repeat the byte */
			while (repeat--)
				store_video(byte);
			repeat = 0;
			continue;
		}
		if (byte > 127) {	/* repeat factor */
			repeat = 257 - byte;
			if (repeat > 72)
				return(1);
			continue;
		}
		count = byte + 1;	/* count factor */
		if (count > 72)
			return(1);
	}
					/* short file ? */
	if (row < 720)
		return(1);
	return(0);
}

/*
 * The image is stored in a two dimensional array of unsigned short 
 * integers 36 wide by 720 long.  The 8 bit char to 16 bit short integer
 * conversion is reversed (typical of Intel vs. Motorola chips).  In
 * addition, the byte order of the bit-mapped screen on the Macintosh is
 * right to left (meaning the most significant bit appears on the screen
 * to the left) and ours is left to right.  The byte transposition
 * table 'xp_table[]' is used to transpose the bit patterns (much faster
 * than rotating bits 180 degrees).
 */

store_video(byte)
unsigned char byte;
{
	static int previous;

	if (hi_byte) {
					/* xp_table[] rotates it 180 degrees */
#ifdef INVERSE
		previous = 0xff - xp_table[byte];
#else  INVERSE
		previous = xp_table[byte];
#endif INVERSE
		hi_byte = 0;
		return;
	}
	hi_byte = 1;
					/* reverse the byte order */
#ifdef INVERSE
	pic[row][col] = ((0xff - xp_table[byte]) << 8) + previous;
#else  INVERSE
	pic[row][col] = (xp_table[byte] << 8) + previous;
#endif INVERSE
	col++;
	if (col == 36) {
		row++;
		col = 0;
	}
	return;
}

/*
 * roll up or down 20 pixels
 */

roll(direction, win)
int direction;
short win;
{
	unsigned short *npic;

	if (direction) {		/* down 20 */
		next_row += 20;
		if (next_row > 480) {
			next_row = 480;
			wputc(win, 07);
		}
	}
	else {				/* up 20 */
		next_row -= 20;
		if (next_row < 0) {
			next_row = 0;
			wputc(win, 07);
		}
	}
					/* new pointer to pic[] array */
	npic = &pic[next_row][0];

	wrastop(win, npic, 72, 0, 0, 0, 0, 0, 0, 576, 240, SRCSRC, DSTSRC, 0);
	return;
}

/*
 * set up a borderless window
 */

clean_up(win)
short win;
{
	WSTAT wbuf;
	
	wprintf(win, "\033[=0C");	/* turn cursor back on */
	clear();
	wbuf.begy = 1;
	wbuf.begx = 0;
	wbuf.height = 24;
	wbuf.width = 80;
	wbuf.uflags = NBORDER;
	wsetstat(win, &wbuf);
	wgoto(win, 0, 0);
	wdelete(win);
	return;
}

/*
 * get a string from a window, analogous to gets() except we do the
 * echoing, editing, and control-c handling
 */

wgets(win, string)
short win;
char *string;
{
	int c;
	char *first;

	first = string;
	while ((c = wgetc(win)) != '\r') {
					/* if control c */
		if (c == 3) {
			clean_up(win);
			wexit(0);
		}
					/* if backspace */
		if (c == 8) {
			string--;
			if (string < first)
				string = first;
			else
				wputc(win, c);
			continue;
		}
					/* provide the echo ourselves */
		wputc(win, c);
		*string++ = (c & 0177);
	}
	wprintf(win, "\r\n");
	*string = '\0';
	return;
}

/*
 * Print the image on a dot matrix printer.  The 'start' string sets the
 * vertical line spacing to 8/72 inch, the 'middle' strings sends the
 * code to print the next 576 characters as graphics, and the 'end' string
 * returns the vertical line spacing to normal.  On some printers, bit 0
 * is at the top, others it's on the bottom.
 */

print_it(win)
short win;
{
	FILE *lp;
	int ptr_row, up_side_down;
	char *start, *middle, *end, printer[10];
	unsigned char *ptr_str, *get_str();

	wprintf(win, "Choice of printers\n\n");
	wprintf(win, "1) Epson FX\n");
	wprintf(win, "2) Apple DMP/NEC 8023\n");
	wprintf(win, "3) Gemini 10\n");
	wprintf(win, "4) IBM Graphics Printer\n");
	wprintf(win, "5) other\n\n");
	again:
	wprintf(win, "Enter choice [1-5] : ");
	wgets(win, printer);

	switch (*printer) {
		case 0:			/* carriage return alone */
			return;
		case '1':		/* Epson */
			start =  "\033A\010";
			middle = "\033*\004@\002";
			end    = "\0332";
			up_side_down = 0;
			break;
		case '2':		/* Apple DMP/NEC 8023 */
			start =  "\033T16";
			middle = "\033G0576";
			end    = "\033B";
			up_side_down = 1;
			break;
		case '3':		/* Gemini 10 */
			start =  "\0333\020";
			middle = "\033L@\002";
			end    = "\0332";
			up_side_down = 0;
			break;
		case '4':		/* IBM Graphics Printer */
			start =  "\033A\010";
			middle = "\033L@\002";
			end    = "\0332";
			up_side_down = 0;
			break;
		case '5':		/* Other */
			start =  "";
			middle = "";
			end    = "";
			up_side_down = 0;
			break;
		default:		/* oops! */
			goto again;
	}
/*
 * Please note, a 'popen()' to the '/usr/bin/lp' command WILL NOT WORK
 * since the device driver for the '/dev/lp' will do post-processing and
 * CR-LF translations. 
 */
	if (!(lp = fopen("/dev/rawlp", "w"))) {
		wprintf(win, "Can't open /dev/rawlp\n");
		sleep(3);
		clean_up(win);
		wexit(1);
	}
	fputs(start, lp);

	for (ptr_row = 0; ptr_row < 720; ptr_row += 8) {
					/* get a 576 char raster */
		ptr_str = get_str(ptr_row, up_side_down);
		fputs(middle, lp);
					/* spit out the raster */
		fwrite(ptr_str, sizeof *ptr_str, 576, lp);
		fputc('\n', lp);
	}

	fputs(end, lp);
	fputc(014, lp);
	pclose(lp);
	return;
}

/* 
 * Convert that mess in the pic[] array into a printer raster of 576 chars.
 * A byte sent to a printer makes a 8 pixel vertical line, however the same
 * byte sent to the screen makes a 8 pixel horizontal line.  Therefore we 
 * must 'rotate' the bit pattern of 8 rows of integers by 90 degrees to form
 * a printer raster of 16 characters.  (Can't use a translation table now,
 * since the rotation actualy involves combining bits from different bytes)
 */

unsigned char *
get_str(ptr_row, up_side_down)
int ptr_row;
int up_side_down;
{
	static unsigned char raster[576];
	int ras_col, offset, ras_row, bit, mult;
	unsigned char pat;

	for (ras_col = 0; ras_col < 36; ras_col++) {
					/* clear previous pattern */
		for (bit = 0; bit < 16; bit++) 
			raster[(ras_col * 16) + bit] = 0;
					/* for each stack of 8 */
		for (offset = 0; offset < 8; offset++) {
			ras_row = ptr_row + offset;
					/* rotate bit pattern 90 degrees */
			for (bit = 0; bit < 16; bit++) {
				mult = 1 << bit;
#ifdef INVERSE
				if ((0xffff - pic[ras_row][ras_col]) & mult) {
#else  INVERSE
				if (pic[ras_row][ras_col] & mult) {
#endif INVERSE
					if (up_side_down)
						pat = 1 << offset;
					else
						pat = 1 << (7 - offset);
					raster[(ras_col * 16) + bit] |= pat;
				}
			}
		}
	}
	return(raster);
}