kc@rna.UUCP (Kaare Christian) (01/27/85)
Macpainters, Here is an enhancement of the macqms program that was originally posted by Van Jacobson. The antecedent authors are Winkler and Patterman. Macqms transforms a macpaint document into a format that can be printed on a QMS laser printer. This enhancement is a filter that smoothes the images before printing. This increases the apparent contrast, reduces (but does not eliminate) the jaggies, and makes most lettering look less sparkly. It also slows things down considerably and it expands the data by a factor of ten or so. Some documents are improved markedly, others suffer little improvement. The major deficiency is sluggishness. Be prepared to wait, possibly a long time. This program is a cpu-hog. You may want to restrict its use, or have it auto-nice itself nearly out of existence. This filter has been designed semi-modularly so that it can be attached to the other mac->some_printer programs. Since our only hi-res, bitwise printer is a QMS I've left this chore to others. I am distributing this filter along with the slightly modified macqms program, and the updated manual page. Kaare Christian, uucp:cmcl2!rna!kc bell:212-570-7672 (Cut below and process with sh) -------------------------------------------------------------------- echo filter.c sed 's/^X//' > filter.c << 'All work and no play makes Jack a dull boy' X/* X * A simple convolver that converts macpaint images into 4x resolution X * with smoothing. This reduces the jagginess of a macpaint image, so it X * looks nicer on the qms laser printer. This filter assumes a 4x X * resolution increase, but it may be adaptable to other increments. X * This code is separate from the macqms source so that it can be X * adapted, with minimal change, to the other macintosh->whatever filters. X * Adaptation of this filter to the imagen macpaint routine should be X * trivial. X * X * There are two connections between this filter and the macqms code: X * 1. The putdata routine. It accepts data in mac format, puts it X * in local buffers, de-length codes as necessary, and calls do_filter at X * the end of each line. X * 2. The sendline routine uses the stdout, which must be connected X * properly by macqms routines. The macqms routines must send the proper X * qms cmds before and after the data generated by this filter. X * X * There is one dependency upon the qms device: X * 1. The sendline routine (and the two routines it calls, squeeze X * and booltobin) are qms specific. These will need to be replaced for other X * output devices. X * X * This filter is attached to the macqms filter by intercepting output X * calls at the lowest level. Macqms was modified slightly to funnel all X * output through one low level routine, which either does the output or X * calls putdata to output the data via this filter. X * X * I have done some performance tuning. More than 3x of speed improvement X * has been realized since the first working version. More tuning is X * possible. Hint, hint. X * X * My thanks go to Van Jacobson for the macqms.c program. X * X * This filter may be distributed, only without charge, to anyone. This X * notice must be attached, and all modifications must be duly noted X * by their author in the revision history. This filter may be used for X * any purpose, except that it may not be sold, or incorporated into X * any software that is sold. X * X * Kaare Christian, The Rockefeller Univ. 1230 York Ave. NY NY 10021 X * uucp:cmcl2!rna!kc 212-570-7672 X * X * Revision History: X * Created Jan 22, 1985 kc X * X */ X X#include <stdio.h> X#define MAC_SCANLINE_BITS 576 X#define MB MAC_SCANLINE_BITS X#define QB MB*4 X Xstatic char macbuf[MB]; Xstatic char qbuf[5][QB+1]; /* the +1 simplifies makeline */ Xstatic char outbuf[MB]; /* MB just happens to be the right size */ Xstatic int i_macbuf = 0; Xextern int smoothval; X X/* X * the input side connection to the macintosh data X */ Xputdata(flag,byte) Xint flag,byte; X{ Xint i; X Xif (flag == -1) X do_filter(); Xelse if (flag == 0) X bintobool(byte); Xelse for(i=0;i<flag;i++) X bintobool(byte); X} X X/* X * make four scan lines from one X */ Xdo_filter() X{ Xint line; X Xi_macbuf = 0; X Xcenters(); Xfor(line=0;line<4;line++) { X makeline(line); X clipline(line); X sendline(line); X } Xrollbuff(); X} X Xrollbuff() /* place centers from qbuf[4] into qbuf[0] */ X{ Xregister int i; Xfor(i=0;i<QB;i+=4) X qbuf[0][i] = qbuf[4][i]; X} X Xcenters() X{ X/* X place data from macbuf into every fourth position of qbuf[4] X Only the points inbetween the centers are candidates for filtering X */ Xregister int i, j; Xfor(i=0,j=0;i<QB;i+=4,j++) X qbuf[4][i] = macbuf[j]; X} X X#define LWT0 16 X#define LWT1 12 X#define LWT2 8 X#define LWT3 4 Xstatic int xyweights[4][4] = { X { 16, 12, 8, 4 }, X { 12, 9, 6, 3 }, X { 8, 6, 4, 2 }, X { 4, 3, 2, 1}}; X Xmakeline(N) X{ Xregister int n = N, loc, x; Xregister char *ptr, *p0, *p4; Xp0 = qbuf[0]; Xp4 = qbuf[4]; Xptr = qbuf[n]; Xfor(loc=0;loc<QB;loc++) { X x = loc & 03; /* distance past a center */ X if (n == 0) { /* fill in line 0 tween the centers */ X if (x==0) X ptr[loc] = ptr[loc] ? LWT0 : 0; X else if (x==1) X ptr[loc] = (ptr[loc-1] ? LWT1 : 0) X + (ptr[loc+3] ? LWT3 : 0); X else if (x==2) X ptr[loc] = (ptr[loc-2] ? LWT2 : 0) X + (ptr[loc+2] ? LWT2 : 0); X else if (x==3) X ptr[loc] = (ptr[loc-3] ? LWT3 : 0) X + (ptr[loc+1] ? LWT1 : 0); X } X else if (x == 0) { /* fill in lines 1..3 between centers */ X if (n==1) X ptr[loc] = (p0[loc] ? LWT1 : 0) X + (p4[loc] ? LWT3 : 0); X else if (n==2) X ptr[loc] = (p0[loc] ? LWT2 : 0) X + (p4[loc] ? LWT2 : 0); X else if (n==3) X ptr[loc] = (p0[loc] ? LWT3 : 0) X + (p4[loc] ? LWT1 : 0); X } X else { /* the full xy scene */ X int l; X l = loc & (~03); X ptr[loc] = (p0[l] ? xyweights[n][x] : 0) X + (p0[l+4] ? xyweights[n][4-x] : 0) X + (p4[l] ? xyweights[4-n][x] : 0) X + (p4[l+4] ? xyweights[4-n][4-x] : 0); X } X } X} X Xclipline(n) Xint n; X{ Xregister char *p, *q; Xp = qbuf[n]; Xq = p + QB; Xwhile(p<q) X if (*p > smoothval) X *p++ = 1; X else X *p++ = 0; X} X X/* X * Send an line of data to the qms, compressed heavily X * (ideally this would be less qms specific) X */ Xsendline(n) Xint n; X{ Xregister int i; Xchar *cptr; X Xcptr = qbuf[n]; Xfor(i=0;i<MB;i++) X outbuf[i] = "0123456789ABCDEF"[booltobin(&cptr)]; Xi=0; Xwhile(i<MB) X i += squeeze(i); Xputchar('\n'); X} X X/* X * qms compression (note - efficiency counts here) X */ Xsqueeze(from) X{ Xint loc = from; Xint n; Xchar ch; Xch = outbuf[loc]; Xif ((ch == '0') || (ch == 'F')) X while((loc < MB) && (outbuf[loc] == ch)) X loc++; Xn = loc-from; Xif (n > 3) { /* squeeze it */ X printf("^%c%03d",(ch == '0') ? 'D' : 'B', n); X return(n); X } X/* oh well, just send one */ Xputchar(ch); Xreturn(1); X} X Xstatic char masks[8] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; Xbintobool(byte) Xint byte; X{ Xint i; Xfor(i=0;i<8;i++) X macbuf[i_macbuf++] = (byte & masks[i]) ? 1 : 0; X} X Xbooltobin(ptr) Xchar **ptr; X{ Xregister int val = 0; Xregister int i; X Xfor(i=4;i<8;i++) X if (*(*ptr)++) X val |= masks[i]; Xreturn(val); X} All work and no play makes Jack a dull boy echo macqms.c sed 's/^X//' > macqms.c << 'All work and no play makes Jack a dull boy' X/* X This filter converts MacPaint files into Quic files for printing X on an QMS-1200 or -800 laser printer. It was written by Van X Jacobson of Lawrence Berkeley Laboratory (van@lbl-csam.arpa, X ...!ucbvax!lbl-csam!van) and is in the public domain. It was X loosely based on macimp.c by Winkler@harvard which was in turn X based on MACimp for Tops-20 by Ed Pattermann, Stanford. X X Usage: X macqms [-l] [-s] [-f[n]] [-x<xpos>] [-y<ypos>] [-m<mag>] X [-n<ipp>[,<ipr>]] [file ...] X X '-l' prints in "landscape" orientation (default is "portrait" X orientation). <xpos> and <ypos> are the position of the upper X left corner of the bitmap. They are given in inches and default X to 0.41 and 0.70 (which will center a 576x720 image at mag 4). X <mag> is a magnification for the bitmap and can be any integer X >0. The default is 4. Magnifications larger than 4 may result in X bizarre images. `-n<ipp>' prints <ipp> images-per-page. The files X are printed `m' across by `n/m' down. "m" can be specified (e.g., X "-n4,2") or it can be defaulted. The default is the largest number X of images that will fit the current magnification & orientation. X X Send enhancements and bug reports to van@lbl-csam. X X History: X 21 August 1984 VJ - created. X 1 Jan 1985 Hacked to pipe output to lpr. X -s option writes standard out. X (Note site dependent LPR define and the qms_init proc) kc X 22 jan 1985 Added output filtering. Controlled by the -f option. kc X*/ X X#include <stdio.h> Xdouble atof(); Xextern char **environ; X X#define MAC_HEADER_LENGTH 512 X#define MAC_SCANLINES 720 /* number of scanlines in image */ X#define MAC_SCANLINE_BITS 576 /* number of bits in a scanline */ X#define MAC_SCANLINE_BYTES (MAC_SCANLINE_BITS/8) X#define LPR "/usr/local/bin/lpr" X Xint mag=4; /* horiz & vert magnification in dots */ Xint window=MAC_SCANLINE_BITS; /* horiz width of image in dots */ Xint initial_xpos = 410; /* left margin position in inches*1000 */ Xint initial_ypos = 700; /* top margin position in inches*1000 */ Xchar orientation = 'P'; /* "portrait" orientation */ Xint qms_pagewidth = 8500; /* in inches*1000 */ Xint ipp = 1; /* 1 image per page */ Xint ipr = 0; /* default images per row */ Xint images = 0; /* number of images on current page */ Xint xpos; Xint ypos; Xint xpos_incr; Xint ypos_incr; Xint stdoutput; /* set means ship to std, not to lpr */ Xint filter; /* set means invoke the smoothing filter */ Xint smoothval = 6; Xchar usage[] = X"usage: macqms [-l] [-f] [-s[val]] [-m<mag>]\n\t\t[-x<xpos>] [-y<ypos>] [-n<ipp>] [file ..]\n"; X Xmain(argc,argv) Xint argc; Xchar **argv; X{ X register int i; X X argv++; argc--; X for ( i=0; i < argc; i++ ) X { X if ( argv[i][0] != '-' ) X break; X X switch ( argv[i][1] ) X { X case 'm': X mag = atoi( &argv[i][2] ); X if ( mag <= 0 ) X mag = 1; X break; X case 'l': X orientation = 'L'; X qms_pagewidth = 11000; X break; X case 'x': X xpos = atof( &argv[i][2] )*1000.; X break; X case 'y': X ypos = atof( &argv[i][2] )*1000.; X break; X case 'n': X sscanf( &argv[i][2], "%d,%d", &ipp, &ipr ); X break; X case 's': X stdoutput++; X break; X case 'f': X filter++; X if (argv[i][2] != 0) X smoothval = atoi(&argv[i][2]); X if ((smoothval < 3) || (smoothval > 16)) X smoothval = 6; X break; X default: X fprintf( stderr, usage ); X exit(1); X } X } X if ( i >= argc ) { X /* no file arguments - send contents of stdin */ X qms_init("macqms"); X send_one_bitmap(); X } X else { X qms_init(argv[i]); X for ( ; i < argc; i++ ) X { X if ( freopen( argv[i], "r", stdin ) == NULL ) X { X perror( argv[i] ); X exit(2); X } X send_one_bitmap(); X } X } X X /* if we have a partial page of images left over, eject it. */ X if ( images > 0 ) X qms_eject(); X X qms_end(); X fflush(stdout) ; X} X X Xsend_one_bitmap() X{ X /* set initial position & switch to plot mode */ X printf( "^IT%05d\n^IJ%05d\n^P%04d\n", xpos, ypos, window ); X throw_away_mac_header() ; X send_bitmap_data() ; X /* exit plot mode & update the postion for the next picture, if any */ X printf( "^G\n" ); X if ( ++images >= ipp ) X qms_eject(); X else X { X if ( (images % ipr) != 0 ) X xpos += xpos_incr; X else X { X xpos = initial_xpos; X ypos += ypos_incr; X } X } X} X Xqms_eject() X{ X printf("^,\n"); X xpos = initial_xpos; X ypos = initial_ypos; X images = 0; X} X Xint obytes; Xint scanline; X Xsend_bitmap_data() X{ X register int repeat_count, i; X X obytes=0; X scanline=0; X X while ( (repeat_count = getchar()) != EOF ) X { X repeat_count &= 0xff; X if ( repeat_count >= 128 ) X { X if ( repeat_count == 128 ) X continue; X X /* this is a <repeat count><data to repeat> pair. This X * is almost identical to the qms compression scheme so X * we just reformat slightly & write it out. X */ X repeat_count = 257 - repeat_count; X check_for_error( repeat_count ); X printhex( repeat_count, nextbyte() ); X } X else X { X /* this is a <count><count+1 bytes of data> sequence. X * We just ship out the data. X */ X check_for_error( ++repeat_count ); X while ( repeat_count-- > 0 ) X printhex( 0, nextbyte() ); X } X X if ( obytes >= MAC_SCANLINE_BYTES ) X { X if (filter) X putdata(-1,0); X else X putchar( '\n' ); X if ( ++scanline >= MAC_SCANLINES ) X break; X obytes = 0; X } X } X} X Xcheck_for_error( bytes ) X{ X /* check that the result of putting "bytes" data bytes is <= 1 X * scan lines worth of data. X */ X if ( (obytes += bytes) > MAC_SCANLINE_BYTES ) X { X /* too many bytes - assume that we dropped some & output X * filler for the previous scanline. X */ X printf( "\n^C%03d00\n", MAC_SCANLINE_BYTES + bytes - obytes ); X X fprintf( stderr, X "macqms: %d bytes on scanline %d (input byte %D)\n", X obytes, scanline, ftell( stdin ) ); X X obytes = bytes; X scanline++; X } X} X Xthrow_away_mac_header() X{ X short bytenum ; X X for ( bytenum = 0 ; bytenum < MAC_HEADER_LENGTH ; bytenum ++ ) X ( void ) nextbyte() ; X} X Xnextbyte() X{ X int byte; X X if ( (byte = getchar()) == EOF ) X { X fprintf( stderr, X "macqms: unexpected EOF reading macpaint file.\n" ); X exit(1); X } X return (byte); X} X Xstatic char hex_tab[] = { '0', '1', '2', '3', '4', '5', '6', '7', X '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; X Xprinthex( repeat, byte ) Xint repeat, byte; X{ X if (filter) X putdata(repeat,byte); X else { /* the old style */ X /* print a byte as 2 hex digits.N.B.- the qms currently requires X * that the digits be in *upper case* so we can't use printf. X */ X if (repeat) X printf( "^C%03d", repeat ); X putchar( hex_tab[(byte>>4) & 0xf]); X putchar( hex_tab[byte & 0xf]); X } X} X Xqms_init(iname) Xchar *iname; X{ X if (!stdoutput) { X int pf[2]; X int kidpid; X if (pipe(pf)<0) { X perror("macqms piping problem"); X exit(-1); X } X kidpid = fork(); X if (kidpid == 0) { X /* kid */ X /* here at rna the -Q-q arg does what the documented X * -l arg is supposed to do. X * It sends raw stuff to the qms. You may have to X * tweak the lpr args so that raw data X * (hat commands) are passed unmolested X * to the qms. */ X static char *args[] = { X "lpr", "-Q-q", "-J", 0, "-", 0 }; X args[3] = iname; X /* child */ X close(pf[1]); X dup2(pf[0],0); X close(pf[0]); X execv(LPR,args,environ); X perror("macqms exec error"); X } X else if (kidpid > 0) { X close(pf[0]); X dup2(pf[1],1); X close(pf[1]); X } X else { X perror("macqms fork error"); X exit(-1); X } X } X /* switch to quic mode & make sure the qms X * is set up the way we expect. X */ X printf( "\n^PY^-\n^ISYNTAX00000\n^F^-\n" ); X /* set the portrait/landscape orientation */ X printf( "^IO%c\n", orientation ); X /* set the magnification */ X if (!filter) X printf( "^IP%02d%02d\n", mag, mag ); X else { X if (mag == 4) { X printf( "^IP%02d%02d\n", 1, 1 ); X window *= 4; X } X else filter = 0; /* can't filter except at mag == 4 */ X } X /* set the postioning stuff needed for multiple images per page */ X xpos = initial_xpos; X ypos = initial_ypos; X ypos_incr = (mag * MAC_SCANLINES * 10) / 3; /* inches per image */ X xpos_incr = (mag * MAC_SCANLINE_BITS * 10) / 3; X if ( ipr <= 0 ) X ipr = (qms_pagewidth - xpos) / xpos_incr; X} X Xqms_end() X{ X /* exit quic mode */ X printf( "^-^PN^-\n" ); X} All work and no play makes Jack a dull boy echo macqms.l sed 's/^X//' > macqms.l << 'All work and no play makes Jack a dull boy' X.TH MACQMS local "12 August 1984" X.UC 4 X.SH NAME Xmacqms \- Convert Macintosh MacPaint file(s) to QMS Laser Printer format X.SH SYNOPSIS X.B macqms X[ X.B \-l X] X[ X.B \-s X] X[ X.B \-f[n] X] X[ X.B \-m \fImag\fP X] X[ X.B \-x \fIxpos\fP X] X[ X.B \-y \fIypos\fP X] X[ X.B \-n \fIipp[,ipr]\fP X] X[ file ... ] X.SH DESCRIPTION X.B Macqms Xconverts MacPaint document(s) into a file that can be printed on a XQMS-1200 or QMS-800 laser printer. If no file names are given, standard Xinput is read. The program Xworks for files created by MacPaint and for Mac screen dumps created Xwith OPTION-COMMAND-3. It does X.I not Xwork for MacWrite documents. X.PP XCommand line options are: X.TP X.B \-l Xspecifies X.I landscape Xorientation for the output. The default is X.I portrait Xorientation. X.TP X.B -s Xplaces the output on the standard output. The default is to pipe the Xoutput to lpr. X.TP X.B \-f Xruns the macpaint document through a smoothing filter so that it Xappears less jaggy. The magnification must be the default (4), the Xorientation must be portrait, and the multiple images per page feature Xmust be turned off. XThe optional numeric parameter X.I n Xcontrols the sensitivity of the filter. XEight is the theoretically correct value, although six (the default) Xseems to produce ``smoother'' looking pictures. Reasonable values are Xfrom about four to twelve. XThis filter is very slow. Typical times (per image) Xare six minutes on a lightly loaded VAX 780. Much longer times (up to Xthirty minutes of cpu time on a 750) have been observed. Typically the document increases in size by a factor of ten. X.TP X.B \-m \fIn\fP Xspecifies a magnification factor. By default, the MacPaint image is Xmagnified by a factor of 4. This just about fills the output page and Xis about 30% larger than the screen image. XThe magnification, X.IR n , Xcan be any integer > 0. However, at ``-m5'' and above the image might not fit Xon the page and the QMS will do strange things to it. X.TP X.B \-x \fIpos\fP Xpositions the left edge of the image. The position, X.IR pos , Xis given in \fIinches\fP. The default is ``-x0.41''. X.TP X.B \-y \fIpos\fP Xpositions the top edge of the image. The default is ``-y0.70''. X.sp XDefault positions were chosen to center a Xfull image on an 8.5 x 11 page at the default magnification. X.TP X.B \-n \fIipp[,ipr]\fP Xprints multiple images on one page. By default a page eject Xis done between each MacPaint file printed. This flag results in X.I ipp Ximages being printed on each page. The number of images per row, X.I ipr , Xcan be specified or will default the the maximum number of images that Xwill fit across the page. No space is inserted between multiple images Xon a page so a large `composite' can be built up from smaller Ximages. Images are printed `row-wise'. E.g., X.ce Xmacqms -m2 -n4 f1 f2 f3 f4 Xwill print as X.TS Xcenter,allbox; Xc c. Xf1 f2 Xf3 f4 X.TE X.SH AUTHOR XVan Jacobson, Real Time Systems Group, Lawrence Berkeley Laboratory X.SH SEE ALSO Xmacget(local) X.SH BUGS XProbably. XThe author obviously got hit by a bad case of ``creeping featurism''. X XThe filter is too slow. XDoes anybody know how to speed up an eight million point convolver? All work and no play makes Jack a dull boy exit Newsgroups: net.sources.mac Subject: A Smoother Mac->qms converter Macpainters, Here is an enhancement of the macqms program that was originally posted by Van Jacobson. The antecedent authors are Winkler and Patterman. Macqms transforms a macpaint document into a format that can be printed on a QMS laser printer. This enhancement is a filter that smoothes the images before printing. This increases the apparent contrast, reduces (but does not eliminate) the jaggies, and makes most lettering look less sparkly. It also slows things down considerably and it expands the data by a factor of ten or so. Some documents are improved markedly, others suffer little improvement. The major deficiency is sluggishness. Be prepared to wait, possibly a long time. This program is a cpu-hog. You may want to restrict its use, or have it auto-nice itself nearly out of existence. This filter has been designed semi-modularly so that it can be attached to the other mac->some_printer programs. Since our only hi-res, bitwise printer is a QMS I've left this chore to others. I am distributing this filter along with the slightly modified macqms program, and the updated manual page. Kaare Christian, uucp:cmcl2!rna!kc bell:212-570-7672 (Cut below and process with sh) -------------------------------------------------------------------- echo filter.c sed 's/^X//' > filter.c << 'All work and no play makes Jack a dull boy' X/* X * A simple convolver that converts macpaint images into 4x resolution X * with smoothing. This reduces the jagginess of a macpaint image, so it X * looks nicer on the qms laser printer. This filter assumes a 4x X * resolution increase, but it may be adaptable to other increments. X * This code is separate from the macqms source so that it can be X * adapted, with minimal change, to the other macintosh->whatever filters. X * Adaptation of this filter to the imagen macpaint routine should be X * trivial. X * X * There are two connections between this filter and the macqms code: X * 1. The putdata routine. It accepts data in mac format, puts it X * in local buffers, de-length codes as necessary, and calls do_filter at X * the end of each line. X * 2. The sendline routine uses the stdout, which must be connected X * properly by macqms routines. The macqms routines must send the proper X * qms cmds before and after the data generated by this filter. X * X * There is one dependency upon the qms device: X * 1. The sendline routine (and the two routines it calls, squeeze X * and booltobin) are qms specific. These will need to be replaced for other X * output devices. X * X * This filter is attached to the macqms filter by intercepting output X * calls at the lowest level. Macqms was modified slightly to funnel all X * output through one low level routine, which either does the output or X * calls putdata to output the data via this filter. X * X * I have done some performance tuning. More than 3x of speed improvement X * has been realized since the first working version. More tuning is X * possible. Hint, hint. X * X * My thanks go to Van Jacobson for the macqms.c program. X * X * This filter may be distributed, only without charge, to anyone. This X * notice must be attached, and all modifications must be duly noted X * by their author in the revision history. This filter may be used for X * any purpose, except that it may not be sold, or incorporated into X * any software that is sold. X * X * Kaare Christian, The Rockefeller Univ. 1230 York Ave. NY NY 10021 X * uucp:cmcl2!rna!kc 212-570-7672 X * X * Revision History: X * Created Jan 22, 1985 kc X * X */ X X#include <stdio.h> X#define MAC_SCANLINE_BITS 576 X#define MB MAC_SCANLINE_BITS X#define QB MB*4 X Xstatic char macbuf[MB]; Xstatic char qbuf[5][QB+1]; /* the +1 simplifies makeline */ Xstatic char outbuf[MB]; /* MB just happens to be the right size */ Xstatic int i_macbuf = 0; Xextern int smoothval; X X/* X * the input side connection to the macintosh data X */ Xputdata(flag,byte) Xint flag,byte; X{ Xint i; X Xif (flag == -1) X do_filter(); Xelse if (flag == 0) X bintobool(byte); Xelse for(i=0;i<flag;i++) X bintobool(byte); X} X X/* X * make four scan lines from one X */ Xdo_filter() X{ Xint line; X Xi_macbuf = 0; X Xcenters(); Xfor(line=0;line<4;line++) { X makeline(line); X clipline(line); X sendline(line); X } Xrollbuff(); X} X Xrollbuff() /* place centers from qbuf[4] into qbuf[0] */ X{ Xregister int i; Xfor(i=0;i<QB;i+=4) X qbuf[0][i] = qbuf[4][i]; X} X Xcenters() X{ X/* X place data from macbuf into every fourth position of qbuf[4] X Only the points inbetween the centers are candidates for filtering X */ Xregister int i, j; Xfor(i=0,j=0;i<QB;i+=4,j++) X qbuf[4][i] = macbuf[j]; X} X X#define LWT0 16 X#define LWT1 12 X#define LWT2 8 X#define LWT3 4 Xstatic int xyweights[4][4] = { X { 16, 12, 8, 4 }, X { 12, 9, 6, 3 }, X { 8, 6, 4, 2 }, X { 4, 3, 2, 1}}; X Xmakeline(N) X{ Xregister int n = N, loc, x; Xregister char *ptr, *p0, *p4; Xp0 = qbuf[0]; Xp4 = qbuf[4]; Xptr = qbuf[n]; Xfor(loc=0;loc<QB;loc++) { X x = loc & 03; /* distance past a center */ X if (n == 0) { /* fill in line 0 tween the centers */ X if (x==0) X ptr[loc] = ptr[loc] ? LWT0 : 0; X else if (x==1) X ptr[loc] = (ptr[loc-1] ? LWT1 : 0) X + (ptr[loc+3] ? LWT3 : 0); X else if (x==2) X ptr[loc] = (ptr[loc-2] ? LWT2 : 0) X + (ptr[loc+2] ? LWT2 : 0); X else if (x==3) X ptr[loc] = (ptr[loc-3] ? LWT3 : 0) X + (ptr[loc+1] ? LWT1 : 0); X } X else if (x == 0) { /* fill in lines 1..3 between centers */ X if (n==1) X ptr[loc] = (p0[loc] ? LWT1 : 0) X + (p4[loc] ? LWT3 : 0); X else if (n==2) X ptr[loc] = (p0[loc] ? LWT2 : 0) X + (p4[loc] ? LWT2 : 0); X else if (n==3) X ptr[loc] = (p0[loc] ? LWT3 : 0) X + (p4[loc] ? LWT1 : 0); X } X else { /* the full xy scene */ X int l; X l = loc & (~03); X ptr[loc] = (p0[l] ? xyweights[n][x] : 0) X + (p0[l+4] ? xyweights[n][4-x] : 0) X + (p4[l] ? xyweights[4-n][x] : 0) X + (p4[l+4] ? xyweights[4-n][4-x] : 0); X } X } X} X Xclipline(n) Xint n; X{ Xregister char *p, *q; Xp = qbuf[n]; Xq = p + QB; Xwhile(p<q) X if (*p > smoothval) X *p++ = 1; X else X *p++ = 0; X} X X/* X * Send an line of data to the qms, compressed heavily X * (ideally this would be less qms specific) X */ Xsendline(n) Xint n; X{ Xregister int i; Xchar *cptr; X Xcptr = qbuf[n]; Xfor(i=0;i<MB;i++) X outbuf[i] = "0123456789ABCDEF"[booltobin(&cptr)]; Xi=0; Xwhile(i<MB) X i += squeeze(i); Xputchar('\n'); X} X X/* X * qms compression (note - efficiency counts here) X */ Xsqueeze(from) X{ Xint loc = from; Xint n; Xchar ch; Xch = outbuf[loc]; Xif ((ch == '0') || (ch == 'F')) X while((loc < MB) && (outbuf[loc] == ch)) X loc++; Xn = loc-from; Xif (n > 3) { /* squeeze it */ X printf("^%c%03d",(ch == '0') ? 'D' : 'B', n); X return(n); X } X/* oh well, just send one */ Xputchar(ch); Xreturn(1); X} X Xstatic char masks[8] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 }; Xbintobool(byte) Xint byte; X{ Xint i; Xfor(i=0;i<8;i++) X macbuf[i_macbuf++] = (byte & masks[i]) ? 1 : 0; X} X Xbooltobin(ptr) Xchar **ptr; X{ Xregister int val = 0; Xregister int i; X Xfor(i=4;i<8;i++) X if (*(*ptr)++) X val |= masks[i]; Xreturn(val); X} All work and no play makes Jack a dull boy echo macqms.c sed 's/^X//' > macqms.c << 'All work and no play makes Jack a dull boy' X/* X This filter converts MacPaint files into Quic files for printing X on an QMS-1200 or -800 laser printer. It was written by Van X Jacobson of Lawrence Berkeley Laboratory (van@lbl-csam.arpa, X ...!ucbvax!lbl-csam!van) and is in the public domain. It was X loosely based on macimp.c by Winkler@harvard which was in turn X based on MACimp for Tops-20 by Ed Pattermann, Stanford. X X Usage: X macqms [-l] [-s] [-f[n]] [-x<xpos>] [-y<ypos>] [-m<mag>] X [-n<ipp>[,<ipr>]] [file ...] X X '-l' prints in "landscape" orientation (default is "portrait" X orientation). <xpos> and <ypos> are the position of the upper X left corner of the bitmap. They are given in inches and default X to 0.41 and 0.70 (which will center a 576x720 image at mag 4). X <mag> is a magnification for the bitmap and can be any integer X >0. The default is 4. Magnifications larger than 4 may result in X bizarre images. `-n<ipp>' prints <ipp> images-per-page. The files X are printed `m' across by `n/m' down. "m" can be specified (e.g., X "-n4,2") or it can be defaulted. The default is the largest number X of images that will fit the current magnification & orientation. X X Send enhancements and bug reports to van@lbl-csam. X X History: X 21 August 1984 VJ - created. X 1 Jan 1985 Hacked to pipe output to lpr. X -s option writes standard out. X (Note site dependent LPR define and the qms_init proc) kc X 22 jan 1985 Added output filtering. Controlled by the -f option. kc X*/ X X#include <stdio.h> Xdouble atof(); Xextern char **environ; X X#define MAC_HEADER_LENGTH 512 X#define MAC_SCANLINES 720 /* number of scanlines in image */ X#define MAC_SCANLINE_BITS 576 /* number of bits in a scanline */ X#define MAC_SCANLINE_BYTES (MAC_SCANLINE_BITS/8) X#define LPR "/usr/local/bin/lpr" X Xint mag=4; /* horiz & vert magnification in dots */ Xint window=MAC_SCANLINE_BITS; /* horiz width of image in dots */ Xint initial_xpos = 410; /* left margin position in inches*1000 */ Xint initial_ypos = 700; /* top margin position in inches*1000 */ Xchar orientation = 'P'; /* "portrait" orientation */ Xint qms_pagewidth = 8500; /* in inches*1000 */ Xint ipp = 1; /* 1 image per page */ Xint ipr = 0; /* default images per row */ Xint images = 0; /* number of images on current page */ Xint xpos; Xint ypos; Xint xpos_incr; Xint ypos_incr; Xint stdoutput; /* set means ship to std, not to lpr */ Xint filter; /* set means invoke the smoothing filter */ Xint smoothval = 6; Xchar usage[] = X"usage: macqms [-l] [-f] [-s[val]] [-m<mag>]\n\t\t[-x<xpos>] [-y<ypos>] [-n<ipp>] [file ..]\n"; X Xmain(argc,argv) Xint argc; Xchar **argv; X{ X register int i; X X argv++; argc--; X for ( i=0; i < argc; i++ ) X { X if ( argv[i][0] != '-' ) X break; X X switch ( argv[i][1] ) X { X case 'm': X mag = atoi( &argv[i][2] ); X if ( mag <= 0 ) X mag = 1; X break; X case 'l': X orientation = 'L'; X qms_pagewidth = 11000; X break; X case 'x': X xpos = atof( &argv[i][2] )*1000.; X break; X case 'y': X ypos = atof( &argv[i][2] )*1000.; X break; X case 'n': X sscanf( &argv[i][2], "%d,%d", &ipp, &ipr ); X break; X case 's': X stdoutput++; X break; X case 'f': X filter++; X if (argv[i][2] != 0) X smoothval = atoi(&argv[i][2]); X if ((smoothval < 3) || (smoothval > 16)) X smoothval = 6; X break; X default: X fprintf( stderr, usage ); X exit(1); X } X } X if ( i >= argc ) { X /* no file arguments - send contents of stdin */ X qms_init("macqms"); X send_one_bitmap(); X } X else { X qms_init(argv[i]); X for ( ; i < argc; i++ ) X { X if ( freopen( argv[i], "r", stdin ) == NULL ) X { X perror( argv[i] ); X exit(2); X } X send_one_bitmap(); X } X } X X /* if we have a partial page of images left over, eject it. */ X if ( images > 0 ) X qms_eject(); X X qms_end(); X fflush(stdout) ; X} X X Xsend_one_bitmap() X{ X /* set initial position & switch to plot mode */ X printf( "^IT%05d\n^IJ%05d\n^P%04d\n", xpos, ypos, window ); X throw_away_mac_header() ; X send_bitmap_data() ; X /* exit plot mode & update the postion for the next picture, if any */ X printf( "^G\n" ); X if ( ++images >= ipp ) X qms_eject(); X else X { X if ( (images % ipr) != 0 ) X xpos += xpos_incr; X else X { X xpos = initial_xpos; X ypos += ypos_incr; X } X } X} X Xqms_eject() X{ X printf("^,\n"); X xpos = initial_xpos; X ypos = initial_ypos; X images = 0; X} X Xint obytes; Xint scanline; X Xsend_bitmap_data() X{ X register int repeat_count, i; X X obytes=0; X scanline=0; X X while ( (repeat_count = getchar()) != EOF ) X { X repeat_count &= 0xff; X if ( repeat_count >= 128 ) X { X if ( repeat_count == 128 ) X continue; X X /* this is a <repeat count><data to repeat> pair. This X * is almost identical to the qms compression scheme so X * we just reformat slightly & write it out. X */ X repeat_count = 257 - repeat_count; X check_for_error( repeat_count ); X printhex( repeat_count, nextbyte() ); X } X else X { X /* this is a <count><count+1 bytes of data> sequence. X * We just ship out the data. X */ X check_for_error( ++repeat_count ); X while ( repeat_count-- > 0 ) X printhex( 0, nextbyte() ); X } X X if ( obytes >= MAC_SCANLINE_BYTES ) X { X if (filter) X putdata(-1,0); X else X putchar( '\n' ); X if ( ++scanline >= MAC_SCANLINES ) X break; X obytes = 0; X } X } X} X Xcheck_for_error( bytes ) X{ X /* check that the result of putting "bytes" data bytes is <= 1 X * scan lines worth of data. X */ X if ( (obytes += bytes) > MAC_SCANLINE_BYTES ) X { X /* too many bytes - assume that we dropped some & output X * filler for the previous scanline. X */ X printf( "\n^C%03d00\n", MAC_SCANLINE_BYTES + bytes - obytes ); X X fprintf( stderr, X "macqms: %d bytes on scanline %d (input byte %D)\n", X obytes, scanline, ftell( stdin ) ); X X obytes = bytes; X scanline++; X } X} X Xthrow_away_mac_header() X{ X short bytenum ; X X for ( bytenum = 0 ; bytenum < MAC_HEADER_LENGTH ; bytenum ++ ) X ( void ) nextbyte() ; X} X Xnextbyte() X{ X int byte; X X if ( (byte = getchar()) == EOF ) X { X fprintf( stderr, X "macqms: unexpected EOF reading macpaint file.\n" ); X exit(1); X } X return (byte); X} X Xstatic char hex_tab[] = { '0', '1', '2', '3', '4', '5', '6', '7', X '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; X Xprinthex( repeat, byte ) Xint repeat, byte; X{ X if (filter) X putdata(repeat,byte); X else { /* the old style */ X /* print a byte as 2 hex digits.N.B.- the qms currently requires X * that the digits be in *upper case* so we can't use printf. X */ X if (repeat) X printf( "^C%03d", repeat ); X putchar( hex_tab[(byte>>4) & 0xf]); X putchar( hex_tab[byte & 0xf]); X } X} X Xqms_init(iname) Xchar *iname; X{ X if (!stdoutput) { X int pf[2]; X int kidpid; X if (pipe(pf)<0) { X perror("macqms piping problem"); X exit(-1); X } X kidpid = fork(); X if (kidpid == 0) { X /* kid */ X /* here at rna the -Q-q arg does what the documented X * -l arg is supposed to do. X * It sends raw stuff to the qms. You may have to X * tweak the lpr args so that raw data X * (hat commands) are passed unmolested X * to the qms. */ X static char *args[] = { X "lpr", "-Q-q", "-J", 0, "-", 0 }; X args[3] = iname; X /* child */ X close(pf[1]); X dup2(pf[0],0); X close(pf[0]); X execv(LPR,args,environ); X perror("macqms exec error"); X } X else if (kidpid > 0) { X close(pf[0]); X dup2(pf[1],1); X close(pf[1]); X } X else { X perror("macqms fork error"); X exit(-1); X } X } X /* switch to quic mode & make sure the qms X * is set up the way we expect. X */ X printf( "\n^PY^-\n^ISYNTAX00000\n^F^-\n" ); X /* set the portrait/landscape orientation */ X printf( "^IO%c\n", orientation ); X /* set the magnification */ X if (!filter) X printf( "^IP%02d%02d\n", mag, mag ); X else { X if (mag == 4) { X printf( "^IP%02d%02d\n", 1, 1 ); X window *= 4; X } X else filter = 0; /* can't filter except at mag == 4 */ X } X /* set the postioning stuff needed for multiple images per page */ X xpos = initial_xpos; X ypos = initial_ypos; X ypos_incr = (mag * MAC_SCANLINES * 10) / 3; /* inches per image */ X xpos_incr = (mag * MAC_SCANLINE_BITS * 10) / 3; X if ( ipr <= 0 ) X ipr = (qms_pagewidth - xpos) / xpos_incr; X} X Xqms_end() X{ X /* exit quic mode */ X printf( "^-^PN^-\n" ); X} All work and no play makes Jack a dull boy echo macqms.l sed 's/^X//' > macqms.l << 'All work and no play makes Jack a dull boy' X.TH MACQMS local "12 August 1984" X.UC 4 X.SH NAME Xmacqms \- Convert Macintosh MacPaint file(s) to QMS Laser Printer format X.SH SYNOPSIS X.B macqms X[ X.B \-l X] X[ X.B \-s X] X[ X.B \-f[n] X] X[ X.B \-m \fImag\fP X] X[ X.B \-x \fIxpos\fP X] X[ X.B \-y \fIypos\fP X] X[ X.B \-n \fIipp[,ipr]\fP X] X[ file ... ] X.SH DESCRIPTION X.B Macqms Xconverts MacPaint document(s) into a file that can be printed on a XQMS-1200 or QMS-800 laser printer. If no file names are given, standard Xinput is read. The program Xworks for files created by MacPaint and for Mac screen dumps created Xwith OPTION-COMMAND-3. It does X.I not Xwork for MacWrite documents. X.PP XCommand line options are: X.TP X.B \-l Xspecifies X.I landscape Xorientation for the output. The default is X.I portrait Xorientation. X.TP X.B -s Xplaces the output on the standard output. The default is to pipe the Xoutput to lpr. X.TP X.B \-f Xruns the macpaint document through a smoothing filter so that it Xappears less jaggy. The magnification must be the default (4), the Xorientation must be portrait, and the multiple images per page feature Xmust be turned off. XThe optional numeric parameter X.I n Xcontrols the sensitivity of the filter. XEight is the theoretically correct value, although six (the default) Xseems to produce ``smoother'' looking pictures. Reasonable values are Xfrom about four to twelve. XThis filter is very slow. Typical times (per image) Xare six minutes on a lightly loaded VAX 780. Much longer times (up to Xthirty minutes of cpu time on a 750) have been observed. Typically the document increases in size by a factor of ten. X.TP X.B \-m \fIn\fP Xspecifies a magnification factor. By default, the MacPaint image is Xmagnified by a factor of 4. This just about fills the output page and Xis about 30% larger than the screen image. XThe magnification, X.IR n , Xcan be any integer > 0. However, at ``-m5'' and above the image might not fit Xon the page and the QMS will do strange things to it. X.TP X.B \-x \fIpos\fP Xpositions the left edge of the image. The position, X.IR pos , Xis given in \fIinches\fP. The default is ``-x0.41''. X.TP X.B \-y \fIpos\fP Xpositions the top edge of the image. The default is ``-y0.70''. X.sp XDefault positions were chosen to center a Xfull image on an 8.5 x 11 page at the default magnification. X.TP X.B \-n \fIipp[,ipr]\fP Xprints multiple images on one page. By default a page eject Xis done between each MacPaint file printed. This flag results in X.I ipp Ximages being printed on each page. The number of images per row, X.I ipr , Xcan be specified or will default the the maximum number of images that Xwill fit across the page. No space is inserted between multiple images Xon a page so a large `composite' can be built up from smaller Ximages. Images are printed `row-wise'. E.g., X.ce Xmacqms -m2 -n4 f1 f2 f3 f4 Xwill print as X.TS Xcenter,allbox; Xc c. Xf1 f2 Xf3 f4 X.TE X.SH AUTHOR XVan Jacobson, Real Time Systems Group, Lawrence Berkeley Laboratory X.SH SEE ALSO Xmacget(local) X.SH BUGS XProbably. XThe author obviously got hit by a bad case of ``creeping featurism''. X XThe filter is too slow. XDoes anybody know how to speed up an eight million point convolver? All work and no play makes Jack a dull boy exit