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