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); }