/ * ** Pclcomp -- PCL compression filter. ** ** If you have any problems or errors to report, please send them to me: ** ** Tony Parkhurst ** ** Email address: tony@sdd.hp.com -or- hp-sdd!tony ** ** Please send a copy of the graphic file that is a problem, and the version ** of pclcomp you are using. ** ** All suggestions and requests are welcome. */ /* *************************************************************************** * * $Source: /disc/44/cgtriton/tony/filters/pclcomp/RCS/pclcomp.c,v $ * $Date: 91/05/30 15:18:51 $ * $Revision: 1.31 $ * * Description: Compresses pcl graphics files. * * Author: Tony Parkhurst * Created: 890427 * Language: C * * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved. * *************************************************************************** */ /* *************************************************************************** * * $Log: pclcomp.c,v $ * Revision 1.31 91/05/30 15:18:51 15:18:51 tony (Tony Parkhurst) * Oops, fixed it right this time. * * Revision 1.30 91/05/30 15:06:20 15:06:20 tony (Tony Parkhurst) * Added fix for negative value for <esc>*r#U. * * Revision 1.29 91/05/03 10:12:30 10:12:30 tony (Tony Parkhurst) * Small changes. * * Revision 1.28 91/04/30 09:41:24 09:41:24 tony (Tony Parkhurst) * Now puts stdin and stdout in binary mode for MSDOS. * Changes courtesy of Mike Slomin. * Changed usage message a bit. * * Revision 1.27 91/04/23 15:48:05 15:48:05 tony (Tony Parkhurst) * Added handling of plus_sign in value fields. * * Revision 1.26 91/04/23 09:47:11 09:47:11 tony (Tony Parkhurst) * Pass thru unknown modes. * * Revision 1.25 91/04/18 11:09:27 11:09:27 tony (Tony Parkhurst) * Added parse for fractions in values (i.e. <esc>(s16.67H) * * Revision 1.24 91/04/10 14:16:30 14:16:30 tony (Tony Parkhurst) * strips text and control codes between <esc>*rA and <esc>*rB w/ -s option * * Revision 1.23 91/04/05 14:53:25 14:53:25 tony (Tony Parkhurst) * Added fixed for deskjet * Also added a stripping feature. * * Revision 1.22 91/04/05 08:48:53 08:48:53 tony (Tony Parkhurst) * Added some error checkin on output for MS-DOS users. * * Revision 1.21 91/04/04 12:53:32 12:53:32 tony (Tony Parkhurst) * Replaced parser. * Now handles combined escape sequences. * Now handles downloads. * Now combines mode changes with data. * * Revision 1.20 91/04/04 08:02:12 08:02:12 tony (Tony Parkhurst) * Removed some test code. * * Revision 1.19 91/03/25 14:38:48 14:38:48 tony (Tony Parkhurst) * Changed defaults. * * Revision 1.18 91/03/25 14:31:22 14:31:22 tony (Tony Parkhurst) * Re-worked memory allocation stuff for funky input files. * * Revision 1.17 91/03/25 13:50:19 13:50:19 tony (Tony Parkhurst) * Use command line args for file w/o -i or -o. * * Revision 1.16 91/03/04 14:23:15 14:23:15 tony (Tony Parkhurst) * Fixed to allow ONLY mode 3 if the user really wants it. * * Revision 1.15 91/03/04 14:08:23 14:08:23 tony (Tony Parkhurst) * Added an exit(0) at the end of main. * fixed up some zerostrip stuff. * made mode 3 the highest priority mode. * * Revision 1.14 91/02/20 13:57:27 13:57:27 tony (Tony Parkhurst) * Changed priority a bit. * Added some zerostripping for mode 2. * * Revision 1.13 91/02/06 15:31:00 15:31:00 tony (Tony Parkhurst) * oops. * * Revision 1.12 91/02/06 14:41:28 14:41:28 tony (Tony Parkhurst) * fixed usage message * * Revision 1.11 91/02/06 14:38:10 14:38:10 tony (Tony Parkhurst) * Added file input and output for MS-DOS. * * Revision 1.10 91/02/05 17:49:23 17:49:23 tony (Tony Parkhurst) * Fixed problem with zero stripped input. * * Revision 1.9 91/02/05 16:11:39 16:11:39 tony (Tony Parkhurst) * Removed delay code and bitfield stuff. * * Revision 1.8 91/02/05 11:04:53 11:04:53 tony (Tony Parkhurst) * Added io delay stuff for testing. * * Revision 1.7 91/02/05 10:28:32 10:28:32 tony (Tony Parkhurst) * Fix for someone specifing ONLY mode 3. * * Revision 1.6 91/01/29 14:13:09 14:13:09 tony (Tony Parkhurst) * Updated some comments. * * Revision 1.5 91/01/29 13:26:24 13:26:24 tony (Tony Parkhurst) * Cleaned up, revamped a bit. * * Revision 1.4 89/11/09 15:59:16 15:59:16 tony (Tony Parkhurst) * Fix for esc * r U coming after esc * r A. * * Revision 1.3 89/10/24 11:31:12 11:31:12 tony (Tony Parkhurst) * Added parsing of <esc>*rC * * Revision 1.2 89/10/13 09:56:46 09:56:46 tony (Tony Parkhurst) * Completely revamped by Greg G. * * Revision 1.1 89/06/15 13:57:46 13:57:46 tony (Tony Parkhurst) * Initial revision * * *************************************************************************** */ static const char copyr[]= "Copyright (c) 1991, Hewlett-Packard Company, all rights reserved."; static const char author[]="Tony Parkhurst"; static const char rcs_id[]="$Header: pclcomp.c,v 1.31 91/05/30 15:18:51 tony Exp $"; static const char rev_id[]="$Revision: 1.31 $"; /* This program takes a PCL graphics file and will try and * optimize the compression. */ /* * This program was first a filter by Dean to compress pcl graphics. * * This program now will do optimal compression using modes 0,1,2 and 3 * * Also, this program will take compressed input. * * Input and output formats are standard pcl. * * Imaging files will be compressed too. * * pclcomp does not take advantage of Y-Offset for blank areas. * This is because Y-Offset creates white areas, but we don't do enough * parsing to determine what value "white" has. An application that * can assume white values could make use of this sequence. * * pclcomp does not do any of the block compression modes (4-8). * * There are a few obvious inefficiencies that I will fix later. * * Speaking of mode 3, there is a possible problem because each of the * output row storage areas are 2* size of what mode 0 would be. This * is clearly sufficient for modes 1 and 2, but it may not be for mode * 3. But I cannot think of a case in Mode 3 where this would be a problem. * * An additional enhancement would be to compare all the planes in a * multi-plane file (color) and if nothing changed, using mode 3, just * output a single <esc>*b0W. */ /* * Usage: pclcomp [-v] [-0] [-1] [-2] [-3] [-z] [-n###] < infile > outfile * * Pclcomp will do graphics compression based on compression modes 0, 1, 2 * and 3. (Mode 0 is uncompressed). Pclcomp will accept all modes, and * will attempt to optimize by selecting the best output mode for each * row (or plane) of data. By default, pclcomp will use all 4 modes, but * the user may restrict which output modes to use with the -0123 options. * For example, to use pclcomp for output to a PaintJet which only knows * modes 0 and 1 (the XL also understands modes 2 and 3), one would use: * * pclcomp -01 < infile > outfile * * Note: Mode 0 should always be allowed. None of the other modes is * guaranteed to be better than mode 0 in all cases. * * The 'v' option tells the number of rows (planes) of data input and output * in the different modes (to stderr). * * The 'z' option is useful for PaintJet files using only modes 0 and 1, it * does zero "stripping" as the PaintJet will do zero "filling". * NOTE: 'z' now means do NOT zerostrip. * * The 'n' option is to change the default number of pixels in a picture. * The proper way to set the pixel width is with the source raster width * sequence <esc*r#S>, but soo many applications just assume the default, * which is different on different printers, so I am providing this * command line option to set a new default. One could also change the * DEFAULT constant below (make sure it is a multiple of 8). Currently * it is set to 8" at 180 dpi (1440), but for 300 dpi, set it to 2400. * * default is now 2400 (for 300dpi ala LaserJet). */ #include <stdio.h> #include <string.h> #ifdef MSDOS #include <fcntl.h> #endif /* This flag is for code that uses bitfields for 68000 systems */ #define BITFIELDS 0 #define Get_Character() getchar() #define MIN(x,y) ( ((x) < (y)) ? (x) : (y) ) #define TRUE 1 #define FALSE 0 #define ESC 27 #define DEFAULT 2400 /* default width in pixels (multiple of 8) */ #define MAXMODES 4 #define MAXPLANES 8 #define MAXBYTES 60000 /* now mostly meaningless, just a big number */ unsigned char *seed_row[MAXPLANES]; unsigned char *new_row; unsigned char *out_row[MAXMODES]; unsigned int out_size[MAXMODES]; char memflag = FALSE; /* set when memory has been allocated */ char mode0=FALSE, mode1=FALSE, mode2=FALSE, mode3=FALSE; unsigned char num_planes=1; unsigned char curr_plane=0; char imaging = FALSE; /* not imaging, so no lockout */ char verbose = FALSE; unsigned char inmode = 0; /* input compression mode */ unsigned char outmode = 0; /* output compression mode */ unsigned int rasterwidth=DEFAULT/8; /* width of picture, in bytes */ unsigned int rpix = DEFAULT; /* width of picture, in pixels */ unsigned char invert=FALSE; /* invert the data (obsolete) */ unsigned char zerostrip= TRUE; /* strip trailing zeros */ unsigned int inuse[4]={0,0,0,0}, outuse[4] = {0,0,0,0}; char widthwarning = FALSE; /* for trucation warning */ char firstrow = TRUE; /* to prevent mode 3 from being first */ struct { /* this will hold the data for the */ unsigned char model; /* configuring of image processing */ unsigned char pix_mode; unsigned char inx_bits; unsigned char red; unsigned char green; unsigned char blue; short wr; short wg; short wb; short br; short bg; short bb; } imdata; extern unsigned char *malloc(); char *filein = NULL, *fileout = NULL; /* ** These variables are for the new parser. ** The new parser handles more sequences, and also deals with combined ** escape sequences better. */ int parameter; int group_char; int terminator; int old_terminator; int value; int frac; int scanf_count; char in_sequence = FALSE; char pass_seq; char plus_sign; /* for relative values */ /* dummy buffer */ char buf[BUFSIZ]; /* ** If the printer is a DeskJet, then we must handle <esc>*rB differently ** Option '-d' will turn on this mode. */ char deskjet = FALSE; /* ** Many drivers it seems put <esc>*rB<esc>*rA between each and every row ** of data. This defeats compression mode 3 on a DeskJet, and also ** makes the PaintJet (not XL) quite slow. This next flag "-s" on the ** command line, will attempt to do a reasonable job of stripping ** out the excess commands. ** ** The in_graphics flag will be used to strip unwanted control chars from ** the file. */ char strip_seq = FALSE; char in_graphics = FALSE; /* ** Just for certain special cases, it would be nice to append an <esc>E reset ** to the end of the job. Specify with "-r". */ char reset_seq = FALSE; char *progname; /* to hold the program name for verbose */ /* ** ** Main program. ** */ main(argc, argv) int argc; char *argv[]; { int c,j; extern char *optarg; extern int optind; progname = argv[0]; #ifdef MSDOS setmode(fileno(stdin), O_BINARY); /* Place stdin and stdout in */ setmode(fileno(stdout), O_BINARY); /* binary mode. (Mike Slomin)*/ #endif /* parse up the args here */ while ((c = getopt(argc, argv, "0123drsvzn:i:o:s")) != EOF ) switch(c){ case '0': mode0++; break; case '1': mode1++; break; case '2': mode2++; break; case '3': mode3++; break; case 'd': deskjet++; break; case 'r': reset_seq++; break; case 's': strip_seq++; break; case 'v': verbose++; break; case 'z': zerostrip = FALSE; break; case 'n': rpix = atoi(optarg); /* new default */ rasterwidth = (rpix + 7) / 8; /* round up */ break; case 'i': filein = optarg; break; case 'o': fileout = optarg; break; case '?': default: fprintf(stderr, "Usage: %s [-0123drsvz] [-n###] [infile [outfile]]\n", argv[0]); exit(-1); }; if ( verbose ) { fprintf(stderr, "%s: %s\n", argv[0], rev_id); } if ( ! ( mode0 || mode1 || mode2 || mode3) ) /* any modes on? */ mode0 = mode1 = mode2 = mode3 = TRUE; /* all modes by default */ /* ** Check to see if any file args were given on the command line. ** Ones that were not preceded by a "-i" or "-o". */ if ( filein == NULL && optind < argc && argv[optind] != NULL ) filein = argv[optind++]; if ( fileout == NULL && optind < argc && argv[optind] != NULL ) fileout = argv[optind++]; /* ** Now open files for stdin and stdout if provided by the user. */ if ( filein != NULL ) /* new input file */ if ( freopen( filein, "rb", stdin ) == NULL ) { fprintf(stderr,"Unable to open %s for input.\n",filein); exit(-42); } if ( fileout != NULL ) /* new output file */ if ( freopen( fileout, "wb", stdout ) == NULL ) { fprintf(stderr, "Unable to open %s for output.\n", fileout); exit(-43); } /* ** This is the pcl input parsing loop. */ while( ( c = getchar() ) != EOF ) { /* Ignore all chars until an escape char */ /* ** If we are in graphics, toss it if strip_seq is set. */ if ( c != ESC ) { if ( !strip_seq || !in_graphics ) putchar(c); /* pass it thru */ continue; /* pop to the top of the loop */ } /* ** Now we have an escape sequence, get the parameter char. */ parameter = getchar(); if ( parameter == EOF ) /* oops */ { putchar ( ESC ); fprintf(stderr, "Warning: File ended with <esc>.\n"); break; /* unexpected end of input */ } /* ** Now check to see if it is a two character sequence. */ if ( parameter >= '0' && parameter <= '~' ) { putchar ( ESC ); putchar ( parameter ); /* pass it thru */ /* ** If the second character is an E, then we ** and the printer do a reset. */ if ( parameter == 'E' ) { free_mem(); curr_plane = 0; num_planes = 1; imaging = FALSE; inmode = 0; outmode = 0; in_graphics = FALSE; /* can't do this if user gave value with -n. rasterwidth = DEFAULT/8; rpix = DEFAULT; */ } continue; /* return to the top */ } /* ** Now check to make sure that the parameter character is ** within range. */ if ( parameter < '!' || parameter > '/' ) { putchar ( ESC ); putchar ( parameter ); fprintf(stderr, "Warning: Invalid escape sequence.\n"); continue; } /* ** We are only interested in certain parameters, so pass ** the rest of the sequences. */ /* ** For the moment, we are only interested in '*' (graphics) ** '(' and ')' (downloads). Although we do not do anything ** with downloads, we need to pass the binary data thru ** untouched. ** Also, '&' is handled too. */ if ( parameter != '*' && parameter != '(' && parameter != ')' && parameter != '&' ) { putchar ( ESC ); putchar ( parameter ); Flush_To_Term(); /* flush rest of seq. */ continue; } /* ** Parameter character is in range, look for a valid group char */ group_char = getchar(); if ( group_char == EOF ) /* oops, ran out of input */ { putchar ( ESC ); putchar ( parameter ); fprintf(stderr, "Warning: Incomplete escape sequence.\n"); break; } /* ** See if in proper range. If it isn't, it is not an error ** because the group character is optional for some sequences. ** For the moment, we are not interested in those sequences, ** so pass them thru. */ if ( group_char < '`' || group_char > '~' ) { putchar ( ESC ); putchar ( parameter ); putchar ( group_char ); if ( group_char < '@' || group_char > '^' ) Flush_To_Term(); /* pass rest of seq. */ continue; } /* ** Now we have a valid group character, decide if we want ** to deal with this escape sequence. ** ** Sequences we want do deal with include: ** ** <esc>*r ** graphics ** <esc>*b ** graphics ** <esc>*v ** graphics ** ** Sequences we must pass thru binary data: ** ** <esc>*c ** pattern ** <esc>*t ** obsolete ** <esc>(f ** download char set ** <esc>(s ** download char ** <esc>)s ** download font ** <esc>&a ** logical page ** <esc>&l ** obsolete ** */ if ( ( parameter == '*' && group_char != 'r' && group_char != 'b' && group_char != 'v' && group_char != 'c' && group_char != 't' ) || ( parameter == '&' && group_char != 'a' && group_char != 'l' ) || ( parameter == '(' && group_char != 'f' && group_char != 's' ) || ( parameter == ')' && group_char != 's' ) ) { /* ** Definately not interested in the sequence. */ putchar ( ESC ); putchar ( parameter ); putchar ( group_char ); Flush_To_Term(); continue; } /* ** Now set up a pass thru flag so we can ignore the entire ** sequences of some of these. */ if ( parameter != '*' ) pass_seq = TRUE; else if ( group_char == 'c' || group_char == 't' ) pass_seq = TRUE; else pass_seq = FALSE; /* ** Now we have a sequence that we are definately interested in. ** ** Get the value field and terminator, and loop until final ** terminator is found. */ do { /* first see if the value has a plus sign */ scanf_count = scanf(" + %d", &value ); if ( scanf_count == 1 ) plus_sign = TRUE; else { plus_sign = FALSE; scanf_count = scanf(" %d", &value ); if ( scanf_count == 0 ) value = 0; /* by default */ } terminator = getchar(); /* ** check to see if a fractional parameter was passed. */ frac = 0; /* in case no fraction */ if ( terminator == '.' ) { /* ** Need to get fractional part. ** ** This will not work properly if the ** fraction is < .1 (i.e. a leading 0 is ** present). For example, the value ** 14.05 for point size, which would get ** rounded to 14.00 for scalable fonts, ** would get passed thru as 14.5 which is ** rounded to 14.5. This is unlikely to ** happen as the 14.05 case is rare, but ** it is valid PCL, so I will fix this ** in the future. */ if ( scanf("%d", &frac) != 1 ) { frac = 0; /* no frac? */ } /* ** Now get the real terminator. */ terminator = getchar(); } if ( terminator == EOF ) /* barf */ { fprintf(stderr, "Warning: Incomplete sequence at end of file.\n"); break; } /* ** If the pass_seq flag is set, then just pass ** it thru to stdout until a 'W' is found. */ if ( pass_seq ) { /* ** If not in sequence, then we output esc ** otherwise, output the saved terminator. */ if ( !in_sequence ) { in_sequence = TRUE; putchar ( ESC ); putchar ( parameter ); putchar ( group_char ); } else { putchar ( old_terminator ); } /* now pass the value */ if ( plus_sign ) putchar('+'); if ( scanf_count ) /* there was a value */ printf("%0d", value); /* need to output the fractional part */ if ( frac ) printf(".%0d", frac); /* ** We save the terminator, because we may ** need to change it to upper case. */ old_terminator = terminator; /* if binary data, pass it thru */ if ( terminator == 'W' ) /* aha */ { putchar ( terminator ); in_sequence = FALSE; /* terminates */ Flush_Bytes ( value ); /* pass data */ } continue; } /* ** Ok, this is a sequence we want to pay attention to. ** ** Do_Graphics returns TRUE if we need to pass seq. ** ** Note: Do_Graphics modifies the parser vars such ** as in_sequence. This is because it may ** have to output stuff directly. */ if ( Do_Graphics ( group_char, value, terminator ) ) { /* ** If not in sequence, then we output esc ** otherwise, output the saved terminator. */ if ( !in_sequence ) { in_sequence = TRUE; putchar ( ESC ); putchar ( parameter ); putchar ( group_char ); } else { putchar ( old_terminator ); } /* now pass the value */ if ( plus_sign ) putchar('+'); if ( scanf_count ) /* there was a value */ printf("%0d", value); /* need to output the fractional part */ if ( frac ) printf(".%0d", frac); /* ** We save the terminator, because we may ** need to change it to upper case. */ old_terminator = terminator; } } while ( terminator >= '`' && terminator <= '~' ); /* ** The oppsite test (above) may be more appropriate. That is, ** !(terminator >= '@' && terminator <= '^'). */ /* ** If we were in a sequence, then we must terminate it. ** If it was lower case, then it must be uppered. */ if ( in_sequence ) { putchar ( terminator & 0xdf ); /* a ==> A */ in_sequence = FALSE; } } /* ** If the user wants a reset, give him one. */ if ( reset_seq ) { putchar ( ESC ); putchar ( 'E' ); } /* ** Finished up, so print stats and close output file. */ fclose(stdout); if ( verbose ) { for(j = 0; j < 4; j++) fprintf(stderr,"Rows in mode %1d: %d\n", j, inuse[j]); for(j = 0; j < 4; j++) fprintf(stderr,"Rows out mode %1d: %d\n", j, outuse[j]); } exit(0); } /* ** Do_Graphics() takes the graphics escape sequence and performs the ** necessary functions. ** TRUE is returned if the escape sequence needs to be passed to the output. */ int Do_Graphics( group, num, terminator ) int group, num, terminator; { /* first look at vW */ if ( group == 'v' ) if ( terminator != 'W' ) return ( TRUE ); /* pass it thru */ else { if ( !in_sequence ) { putchar ( ESC ); putchar ( parameter ); putchar ( group ); } else putchar ( old_terminator ); in_sequence = FALSE; /* terminating */ printf("%0d", num); putchar ( terminator ); free_mem(); /* reset memory */ imaging++; fread(&imdata, MIN(num, 18), 1, stdin); fwrite(&imdata, MIN(num, 18), 1, stdout); num -= MIN(num, 18); /* copy rest of unknown data */ if ( num > 0 ) Flush_Bytes(num); switch(imdata.pix_mode){ case 0x00: rasterwidth = (rpix + 7)/8; num_planes = imdata.inx_bits; break; case 0x01: rasterwidth = rpix*imdata.inx_bits/8; break; case 0x02: rasterwidth = (rpix + 7)/8; num_planes =imdata.red + imdata.green + imdata.blue; break; case 0x03: rasterwidth = (imdata.red + imdata.green + imdata.blue)*rpix/8; break; } return ( FALSE ); } /* ** Now deal with <esc>*r stuff */ if ( group == 'r' ) { switch ( terminator ) { case 'A': case 'a': /* in graphics mode, may do stripping */ in_graphics = TRUE; /* if user wants to strip redundant seq */ if ( strip_seq && memflag ) return( FALSE ); curr_plane=0; zero_seeds(); /* may allocate mem */ break; case 'C': case 'c': /* not in graphics disable code strip */ in_graphics = FALSE; if ( strip_seq ) return( FALSE ); inmode = 0; outmode = 0; free_mem(); curr_plane=0; break; case 'B': case 'b': /* not in graphics disable code strip */ in_graphics = FALSE; if ( strip_seq ) return( FALSE ); if ( deskjet ) /* B resets modes on DJ */ { inmode = 0; outmode = 0; } free_mem(); curr_plane=0; break; case 'S': case 's': /* free mem in case widths changed */ free_mem(); rpix = num; if (imaging){ switch(imdata.pix_mode) { case 0x00: rasterwidth=(rpix+7)/8; break; case 0x01: rasterwidth = rpix*imdata.inx_bits/8; break; case 0x02: rasterwidth=(rpix+7)/8; break; case 0x03: rasterwidth = (imdata.red + imdata.green + imdata.blue)*rpix/8; break; } } else rasterwidth = (num + 7) / 8; break; case 'T': case 't': break; case 'U': case 'u': curr_plane=0; free_mem(); /* if ESC*rA came first */ /* num can be negative */ if ( num < 0 ) num_planes= -num; else num_planes = num; imaging = FALSE; /* goes off */ break; default: break; } return ( TRUE ); /* pass sequence on */ } /* group r */ /* ** Last and final group 'b'. All the graphics data comes thru here. */ switch ( terminator ) { case 'm': case 'M': inmode = num; return ( FALSE ); /* we do NOT pass this */ break; /* ** <esc>*b#X is obsolete, don't bother with it. ** If I did do something, then I would zero part of the ** seed rows. */ case 'x': case 'X': break; case 'y': case 'Y': /* zero only if allocated */ if ( memflag ) zero_seeds(); break; case 'W': if(!memflag) zero_seeds(); /* get memory */ /* fire up sequence */ if ( !in_sequence ) { putchar ( ESC ); putchar ( parameter ); putchar ( group ); } else putchar ( old_terminator ); in_sequence = FALSE; /* terminating */ if(curr_plane < num_planes) { Process(num, 'W'); if ( curr_plane + 1 < num_planes ) { /* now we have a problem */ zero_upper(curr_plane + 1); } } else Process_extra(num,'W'); curr_plane=0; return ( FALSE ); break; case 'V': if(!memflag) zero_seeds(); /* get memory */ /* ** If curr_plane is the last plane, this should ** be a 'W', not a 'V'. I could change it, ** then I would fix Process_extra() to not output ** anything as the 'W' was already sent. */ if( curr_plane < num_planes ) { /* fire up sequence */ if ( !in_sequence ) { putchar ( ESC ); putchar ( parameter ); putchar ( group ); } else putchar ( old_terminator ); in_sequence = FALSE; /* terminating */ Process(num, 'V'); curr_plane++; } else Process_extra(num,'V'); return ( FALSE ); break; default: break; } return ( TRUE ); /* pass sequence */ } /* ** Flush_To_Term() simply passes thru input until a valid terminator ** character is found. This is for unwanted escape sequences. */ Flush_To_Term() { int c; do { c = getchar(); if ( c == EOF ) /* this is a problem */ return; putchar ( c ); } while ( c < '@' || c > '^' ); } /* ** Flush_Bytes() simply transfers so many bytes directly from input to output. ** This is used to pass thru binary data that we are not interested in so that ** it will not confuse the parser. I.e. downloads. */ Flush_Bytes( num ) unsigned int num; { int bnum; while ( num > 0 ) { bnum = MIN ( BUFSIZ, num ); fread( buf, 1, bnum, stdin ); if ( fwrite( buf, 1, bnum, stdout ) < bnum ) /* check for error and exit */ if ( ferror(stdout) ) { perror("Output error"); exit(-2); } num -= bnum; } } /*----------------------------------------*/ /* ** Zero_seeds() will allocate and initialize memory. ** If memory has already been allocated, then it will just initialize it. */ zero_seeds() { int r; /* first allocate and init seed_rows for number of planes. */ for ( r = 0; r < num_planes ; r++) { if(!memflag) { seed_row[r] = (unsigned char *) malloc(rasterwidth); if ( seed_row[r] == NULL ) { fprintf(stderr, "Out of memory.\n"); exit(-3); } } /* zero seeds for mode 3 */ memset(seed_row[r], 0, rasterwidth); } if(!memflag) { new_row = (unsigned char *) malloc(rasterwidth); if ( new_row == NULL ) { fprintf(stderr, "Out of memory.\n"); exit(-3); } for(r=0; r<MAXMODES; r++) { /* 2 * width is needed for modes 1, 2 and 3 */ out_row[r] = (unsigned char *) malloc(2 * rasterwidth); if ( out_row[r] == NULL ) { fprintf(stderr, "Out of memory.\n"); exit(-3); } } } memset(new_row, 0, rasterwidth); memflag = TRUE; /* memory is in place */ } /* this routine if for incomplete transfers of data */ zero_upper(plane) int plane; { int i; /* assume memory already present */ for ( i = plane; i < num_planes; i++) memset(seed_row[i], 0, rasterwidth); } Process(inbytes, terminator) int inbytes, terminator; { int insize; int minmode = 0; inuse[inmode]++; switch ( inmode ) { case 0: if ( !widthwarning && inbytes > rasterwidth ) { /* This is likely to result in data truncation. */ widthwarning = TRUE; fprintf(stderr,"Warning: Input pixel width exceeds expected width.\n"); } insize = Mode_0_Graphics(inbytes,rasterwidth,new_row,invert); break; case 1: insize = Mode_1_Graphics(inbytes,rasterwidth,new_row,invert); break; case 2: insize = Mode_2_Graphics(inbytes,rasterwidth,new_row,invert); break; case 3: memcpy(new_row, seed_row[curr_plane], rasterwidth); insize = Mode_3_Graphics(inbytes,rasterwidth,new_row,invert); break; default: /* unknown mode? */ /* Don't know what to do about seed rows, pass stuff thru */ fprintf(stderr, "%s: Unsupported compression mode %d.\n", progname, inmode ); ChangeMode(inmode); /* go to that mode */ /* <esc>*b has already been output */ printf("%1d%c", inbytes, terminator); Flush_Bytes( inbytes ); firstrow = TRUE; /* pop it out of mode 3 */ /* Go ahead and clear the seed rows if present */ if ( memflag ) zero_seeds(); return; } /* ** */ if ( mode0 ) /* actually, this is redundant since new_row is mode 0 */ out_size[0] = Output_0( new_row, out_row[0], rasterwidth ); else out_size[0] = MAXBYTES+1; if ( mode1 ) out_size[1] = Output_1( new_row, out_row[1], rasterwidth ); else out_size[1] = MAXBYTES+1; if ( mode2 ) out_size[2] = Output_2( new_row, out_row[2], rasterwidth ); else out_size[2] = MAXBYTES+1; if ( mode3 ) out_size[3] = Output_3( seed_row[curr_plane], new_row, out_row[3], rasterwidth ); else out_size[3] = MAXBYTES+1; /* ** Now determine which mode will give the best output. Note that it ** takes 5 bytes to change modes, so we penalize all modes that are ** not the current output by 5 bytes. This is to discourage changing ** unless the benifit is worth it. The exception to this rule is ** mode 3. We want to encourage going to mode 3 because of the seed ** row behaviour. That is, if we have a simple picture that does ** not change much, and say each of the sizes for modes 1 and 2 always ** comes out to 4 bytes of data, then if we add 5 to mode 3 each time, ** it would never get selected. But, we remove the penalty, and if ** mode 3 is selected (0 bytes of data needed for mode 3), then each ** succesive row only needs 0 bytes of data. For a 300 dpi A size ** picture with 3 data planes, this could be a savings of 37k bytes. */ /* ** With the new parser, the output to change modes is now only ** 2 bytes, since it gets combined with the *b#W sequence. ** So, I decided to ignore the switching penalty. */ /* ** Due to a possible bug in PaintJet XL, don't allow mode 3 to be ** selected for the first row of output. But do allow it if the ** user has no other mode selected. */ if ( firstrow && (mode0 || mode1 || mode2) ) { out_size[3] = MAXBYTES+1; /* disable mode 3 for now */ if ( terminator == 'W' ) /* last plane? */ firstrow = FALSE; /* no longer first row */ } minmode = 3; if ( out_size[2] < out_size[minmode] ) minmode = 2; if ( out_size[1] < out_size[minmode] ) minmode = 1; if ( out_size[0] < out_size[minmode] ) minmode = 0; /* I may remove this sometime */ if ( minmode != outmode ) if ( out_size[minmode] == out_size[outmode] ) minmode = outmode; outuse[minmode]++; if ( outmode != minmode ) ChangeMode( minmode ); /* <esc>*b has already been output */ printf("%1d%c", out_size[minmode], terminator); if ( fwrite( out_row[minmode], 1, out_size[minmode], stdout) < out_size[minmode] ) /* check for error and exit */ if ( ferror(stdout) ) { perror("Output error"); exit(-2); } memcpy(seed_row[curr_plane], new_row, rasterwidth); } Process_extra(bytes, terminator) int bytes; char terminator; { int i; /* toss any excess data */ for(i = 0; i < bytes; i++) getchar(); /* last plane? force move down to next row */ if(terminator == 'W') { /* <esc>*b has already been output */ printf("0W"); firstrow = FALSE; /* not on first row anymore */ } } ChangeMode(newmode) int newmode; { /* ** <esc>*b have already been output. ** terminator is 'm' instead of 'M' since will be followed by 'W' */ printf("%1dm", newmode); outmode = newmode; } /* these decoders came from graphics.c in the gp parser */ /* $PAGE$ */ /*-----------------------------------------------------------------------*\ | | | Function Name: Mode_0_Graphics | | | | Description: | | | | This is the routine that handles a Mode 0 graphics block transfer | | to the Formatter Module. | | | \*-----------------------------------------------------------------------*/ /* FUNCTION */ Mode_0_Graphics(input_bytes, output_bytes, address, invert) unsigned int input_bytes, /* Count of bytes to be read. */ output_bytes; /* Count of bytes to be stored. */ unsigned char *address; /* Pointer to where to store bytes. */ unsigned char /* Boolean to request data inversion */ invert; { /* LOCAL VARIABLES */ unsigned char *store_ptr; /* Pointer to where to store the byte. */ unsigned int read_bytes, /* Local copy of input_bytes. */ write_bytes; /* Local copy of output_bytes. */ /* CODE */ /* Initialize the local variables. */ read_bytes = input_bytes; write_bytes = output_bytes; store_ptr = address; /* transfer the lesser of available bytes or available room */ if (invert) Inv_Transfer_Block( MIN(write_bytes,read_bytes), store_ptr); else Transfer_Block( MIN(write_bytes,read_bytes), store_ptr); /* now zero fill or throw excess data away */ if ( read_bytes > write_bytes ) Discard_Block(read_bytes - write_bytes); /* throw excess */ else { store_ptr += read_bytes; /* adjust pointer */ write_bytes -= read_bytes; /* zero fill count */ memset(store_ptr, 0, write_bytes); } return ( input_bytes ); } /* $PAGE$ */ /*-----------------------------------------------------------------------*\ | | | Function Name: Mode_1_Graphics | | | | Description: | | | | This is the routine that handles a Mode 1 graphics block transfer | | to the Formatter Module. Mode 1 graphics is a compacted mode. | | The data in Mode 1 is in pairs. The first byte is a replicate | | count and the second byte is the data. The data byte is stored | | then replicated the replicate count. Therefore a replicate count | | of 0 means the data byte is stored once. The input byte count | | must be an even amount for the data to be in byte pairs. | | | \*-----------------------------------------------------------------------*/ /* FUNCTION */ Mode_1_Graphics(input_bytes, output_bytes, address, invert) unsigned int input_bytes, /* Count of bytes to be read. */ output_bytes; /* Count of bytes to be stored. */ unsigned char *address; /* Pointer to where to store bytes. */ unsigned char /* Boolean to request data inversion */ invert; { /* LOCAL VARIABLES */ unsigned char *store_ptr, /* Pointer to where to store the byte. */ input_char; /* Byte to be replicated. */ unsigned int read_bytes, /* Local copy of input_bytes. */ write_bytes; /* Local copy of output_bytes. */ int replicate_count; /* Number of times to replicate data. */ /* CODE */ /* Initialize the local variables. */ read_bytes = input_bytes; write_bytes = output_bytes; store_ptr = address; /* Check for an even input count. */ if ((read_bytes % 2) == 0) { /* Even so input data is in pairs as required. So store the data. */ while ((read_bytes != 0) && (write_bytes != 0)) { /* First get the replicate count and the byte to store. */ replicate_count = (unsigned char) Get_Character(); input_char = (invert ? ~Get_Character() : Get_Character()); read_bytes -= 2; /* Since write_bytes was 0 there is room to store the byte. */ *store_ptr++ = input_char; write_bytes--; /* Now make sure there is room for the replicated data. */ if (replicate_count > write_bytes) { /* Too much so limit to the room available. */ replicate_count = write_bytes; } /* Update the amount to be written. */ write_bytes -= replicate_count; /* Then replicate it. */ while (replicate_count != 0) { /* Store the byte the decrement the count. */ *store_ptr++ = input_char; replicate_count--; } } } /* Discard any left over input. */ /* OR */ /* Discard all of the input data as odd byte count. */ Discard_Block(read_bytes); read_bytes = store_ptr - address; /* how much was done? */ /* zero fill if needed */ memset(store_ptr, 0, write_bytes); return(read_bytes); } /* $PAGE$ */ /*-----------------------------------------------------------------------*\ | | | Function Name: Mode_2_Graphics | | | | Description: | | | | This is the routine that handles a Mode 2 graphics block transfer | | to the Formatter Module. Mode 2 graphics is a compacted mode. | | The data in Mode 2 is of one of two types. The first type is a | | class type and the second type is a data type. The class type is | | a single byte which is a combination of replicate count and a sub | | mode. There are two sub modes within mode 2, sub mode 0 and sub | | mode 1. These sub modes are flagged by the MSB of the class type | | byte. If the MSB = 0 then the replicate count is the value of the | | class type byte. In sub mode 0 the replicate count ranges from 1 | | to 127. In sub mode 0 the next byte and then the replicate count | | of bytes are of the data type and stored. If the MSB = 1 then the | | sub mode is 1 and the replicate count is the negative value of the | | class type. In sub mode 1 the replicate count is stored in 2s | | compliment form and ranges from -1 to -127. In sub mode 1 the | | next byte is of the data type and is stored. That data byte is | | then replicated and stored the replicate count. If the class type | | byte is 128 then there is no data type byte. | | | \*-----------------------------------------------------------------------*/ /* FUNCTION */ Mode_2_Graphics(input_bytes, output_bytes, address, invert) unsigned int input_bytes, /* Count of bytes to be read. */ output_bytes; /* Count of bytes to be stored. */ unsigned char *address; /* Pointer to where to store bytes. */ unsigned char /* Boolean to request data inversion */ invert; { /* LOCAL VARIABLES */ unsigned char *store_ptr, /* Pointer to where to store the byte. */ input_char, /* Byte to be replicated. */ sub_mode; /* Flag if sub mode is 0 or 1. */ unsigned int read_bytes, /* Local copy of input_bytes. */ write_bytes; /* Local copy of output_bytes. */ int replicate_count; /* Number of times to replicate data. */ /* CODE */ /* Initialize the local variables. */ read_bytes = input_bytes; write_bytes = output_bytes; store_ptr = address; while ((read_bytes > 1) && (write_bytes != 0)) { /* First get the class type byte and the first data type byte. */ replicate_count = Get_Character(); /* First check that this not an ignore class type. */ if (replicate_count != 128) { /* Not ignore so get the data class byte. */ input_char = (invert ? ~Get_Character() : Get_Character()); read_bytes -= 2; /* Since write_bytes wasn't 0 there is room to store the byte. */ *store_ptr++ = input_char; write_bytes--; /* Determine the sub mode. */ if (replicate_count > 128) { /* Sub mode 1. */ sub_mode = 1; /* replicate count was unsigned char */ replicate_count = 256 - replicate_count; } else { /* Sub mode 0. */ sub_mode = 0; /* See if there is enoungh input left for the data byte count. */ if (replicate_count > read_bytes) { /* Too many data bytes so limit to the input left. */ replicate_count = read_bytes; } } /* Now make sure there is room for the replicated data. */ if (replicate_count > write_bytes) { /* Too much so limit to the room available. */ replicate_count = write_bytes; } /* Update the amount to be written. */ write_bytes -= replicate_count; /* Then replicate it. */ if (sub_mode == 0) { /* Sub mode 0 so get the replicate count of data bytes. */ if (invert) Inv_Transfer_Block(replicate_count, store_ptr); else Transfer_Block(replicate_count, store_ptr); read_bytes -= replicate_count; /* Find the last byte stored. */ store_ptr += replicate_count; } else { /* Sub mode 1 so just duplicate the original byte. */ while (replicate_count != 0) { /* Store the byte the decrement the count. */ *store_ptr++ = input_char; replicate_count--; } } } else { /* Ignore class so don't get the data class byte. */ read_bytes--; } } /* Now discard any left over input. */ Discard_Block(read_bytes); read_bytes = store_ptr - address; /* zero fill if needed */ memset(store_ptr, 0, write_bytes); return(read_bytes); } /* $PAGE$ */ /*-----------------------------------------------------------------------*\ | | | Function Name: Mode_3_Graphics | | | | Description: | | | | This is the routine that handles a Mode 3 graphics block transfer | | to the Formatter Module. Mode 3 graphics is a compacted mode. | | Mode 3 data is a difference from one row to the next. In order to | | work, each row must be saved to be a seed for the next. This | | mode is used in conjuction with other compaction modes when the | | data remains fairly constant between pairs of rows. | | The data is in the form: | | <command byte>[<optional offset bytes>]<1 to 8 replacement bytes> | | The command byte is in the form: | | Bits 5-7: Number of bytes to replace (1 - 8) | | Bits 0-4: Relative offset from last byte. | | (If the offset is 31, then add the following bytes for offset | | until an offset byte of less then 255 (but inclusive) | | | \*-----------------------------------------------------------------------*/ /* FUNCTION */ Mode_3_Graphics(input_bytes, output_bytes, address, invert) unsigned int input_bytes, /* Count of bytes to be read. */ output_bytes; /* Count of bytes to be stored. */ unsigned char *address; /* Pointer to where to store bytes. */ unsigned char /* Boolean to request data inversion */ invert; { /* LOCAL VARIABLES */ unsigned char *store_ptr, /* Pointer to where to store the byte. */ input_char; /* Byte to be changed. */ unsigned int read_bytes, /* Local copy of input_bytes. */ write_bytes; /* Local copy of output_bytes. */ unsigned int replace, /* number of bytes to replace, 1-8 */ offset; /* relative offset */ #if BITFIELDS union comtype { unsigned char comchar; /* command byte as char */ struct btype { unsigned repcount:3; /* replace count 1-8 */ unsigned roff:5; /* relative offset 0-30 */ } bitf; } command; #else unsigned char command; #endif /* CODE */ /* Initialize the local variables. */ read_bytes = input_bytes; write_bytes = output_bytes; store_ptr = address; /* read_bytes has to be at least 2 to be valid */ while ( read_bytes > 1 && write_bytes > 0 ){ /* start by getting the command byte */ read_bytes--; #if BITFIELDS command.comchar = Get_Character(); replace = command.bitf.repcount + 1; /* replace count 1-8 */ offset = command.bitf.roff; /* offset 0-30, 31= extend */ #else command = Get_Character(); replace = (command >> 5) + 1; offset = command & 0x1f; #endif store_ptr += offset; write_bytes -= offset; if ( offset == 31 ) /* get more offsets */ do{ offset = Get_Character(); read_bytes--; if ( read_bytes == 0 ) /* premature finish? */ return; /* no zero fill wih 3 */ store_ptr += offset; write_bytes -= offset; } while (offset == 255); /* 255 = keep going */ /* now do the byte replacement */ while ( replace-- && write_bytes > 0 && read_bytes > 0 ){ *store_ptr++ = (invert ? ~Get_Character() : Get_Character() ); read_bytes--; write_bytes--; } } /* don't do any zero fill with mode 3 */ /* discard any leftover input */ Discard_Block(read_bytes); return( store_ptr - address ); } Discard_Block(count) unsigned int count; { while ( count-- ) getchar(); } Transfer_Block( count, dest ) unsigned int count; unsigned char *dest; { fread(dest, 1, count, stdin); } /* this doesn't invert at the moment */ Inv_Transfer_Block( count, dest ) unsigned int count; unsigned char *dest; { fread(dest, 1, count, stdin); } Output_0(src, dest, count) unsigned char *src, *dest; int count; { memcpy(dest, src, count); if ( zerostrip ) while ( count && dest[count-1] == 0 ) count--; return(count); } Output_1(src, dest, count) unsigned char *src, *dest; register int count; { unsigned char *optr = dest, *iptr; int k,c; if ( zerostrip ) /* strip zeros */ { iptr = src + count - 1; /* point to end of data */ while ( count > 0 && *iptr-- == 0 ) /* hunt thru 0's */ count--; } iptr = src; while ( count ){ c = *iptr++; /* get value to work with */ count--; k = 0; while ( *iptr == c && k < 255 && count ){ k++; iptr++; count--; } *optr++ = k; /* output repeat count */ *optr++ = c; /* output value */ } count = optr - dest; /* for return value */ return ( count ); } Output_2(src, dest, count) unsigned char *src, *dest; register int count; { unsigned char *optr = dest, *iptr; int k,c; unsigned char *tptr, *tptr1, *tptr2; int tk,tc; if ( zerostrip ) /* strip zeros */ { iptr = src + count - 1; /* point to end of data */ while ( count > 0 && *iptr-- == 0 ) /* hunt thru 0's */ count--; } iptr = src; while ( count ){ c = *iptr++; /* get value to work with */ count--; k = 0; while ( *iptr == c && k < 127 && count ){ k++; iptr++; count--; } if ( k >= 1 ){ *optr++ = 256 - k; /* output repeat count */ *optr++ = c; /* output value */ } else { /* a two byte replicate run will * be sent as a repeated byte * unless it is preceeded and * and followed by a literal run, * in which case it is merged * into the run. */ tk = 0; tc = c; tptr = iptr; tptr1 = tptr; tptr1++; tptr2 = tptr1; tptr2++; while ( tk < 128 && (count - tk) > 0 && ((*tptr != tc) || (*tptr == tc && (count - tk - 1) > 0 && *tptr1 != *tptr && *tptr2 != *tptr1))){ tc = *tptr++; tk++; tptr1++; tptr2++; } if ( count && tk ) tk--; *optr++ = tk; /* output count */ *optr++ = c; /* output firstvalue */ while ( tk-- > 0){ *optr++ = *iptr++; count--; } } } count = optr - dest; /* for return value */ return ( count ); } Output_3(seed, new, dest, count) unsigned char *seed, *new, *dest; int count; { unsigned char *sptr=seed, *nptr=new, *dptr=dest; int i,j; #if BITFIELDS union comtype { unsigned char comchar; /* command byte as char */ struct btype { unsigned repcount:3; /* replace count 1-8 */ unsigned roff:5; /* relative offset 0-30 */ } bitf; } command; #else unsigned char command; #endif while ( count > 0 ){ i = 0; /* find first diff */ while ( *sptr == *nptr && i < count ){ i++; sptr++; nptr++; } if ( i >= count ) /* too far to find diff */ return(dptr - dest); /* bail */ count -= i; /* now count how many bytes to change */ for ( j = 1; j < 8; j++) /* j == 0 is already known */ if ( j > count || sptr[j] == nptr[j] ) break; j--; /* adjust */ #if BITFIELDS command.bitf.repcount = j; /* 0-7 ==> 1-8 */ command.bitf.roff = MIN ( i, 31 ); *dptr++ = command.comchar; /* output command */ #else command = (j << 5); command += MIN( i, 31 ); *dptr++ = command; #endif if ( i == 31 ) *dptr++ = 0; i -= MIN (i, 31); while( i ){ *dptr++ = MIN ( i, 255 ); if ( i == 255 ) *dptr++ = 0; i -= MIN ( i, 255 ); } while (j-- >= 0){ *dptr++ = *nptr++; sptr++; count--; } } return ( dptr - dest ); } /*----------------------------------------------------------------------*\ * This is here in case <ESC>*rU is sent after <ESC>*r#A, in which case * * we must deallocate the memory to provide for a different amount of * * planes when graphics are sent. * \*----------------------------------------------------------------------*/ free_mem() { int r; if ( !memflag ) return; /* no memory to free */ free(new_row); for(r = MAXMODES -1; r >= 0; r--) free(out_row[r]); for(r = num_planes - 1; r >= 0; r--) free(seed_row[r]); memflag = FALSE; }
file: /Techref/language/pcl/grphcomp/pclcomp.c, 54KB, , updated: 1991/5/30 16:19, local time: 2024/8/31 16:15,
3.15.21.48:LOG IN
|
©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://ecomorder.com/techref/language/pcl/grphcomp/pclcomp.c"> language pcl grphcomp pclcomp</A> |
Did you find what you needed? |
Welcome to ecomorder.com! |
Welcome to ecomorder.com! |
.