[unix-pc.general] Graphics

egray@fthood.UUCP (01/06/89)

How about a "readmac" clone?  It has some good simple examples of how the
bit mapped stuff works.

Emmet P. Gray				US Army, HQ III Corps & Fort Hood
...!uunet!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);
}