This is printpage.c in view mode; [Download] [Up]
/* * Beginn der Deskjet 500 Codierung; * Hier erstmal alle statischen Variablen zur Compression * werden nur von PrintPage() und PrintScanLine() benutzt */ #include <limits.h> #include <stdio.h> #ifdef __ARCHITECTURE__ #include <mach/cthreads.h> #else #include <cthreads.h> #endif #include <windowserver/printmessage.h> #define MIN(a,b) ((a)<(b)?(a):(b)) #define sendsequence(s) (void) fwrite(s, 1, sizeof(s), stdout) #define zerostrip 1 static int in_mode; /* current mode */ static unsigned char *seed_row; static unsigned char obuf1[1024], obuf2[1024]; static unsigned char *testrow = obuf1, *bestrow = obuf2; static int testsize, bestsize; static char page_init[] = { 27, '*', 'p', '0', 'x', 'p', '1', 'Y', 27, '*', 'r', '1', 'A' }; /* <e>*p0x1Y<e>*r1A gives yet another 1/8" 'headroom' */ static char page_exit[] = { 27, '*', 'r', 'B', 27, '&', 'l', '0', 'H' }; /* * Compression Routines taken from pclcomp by Tony Parkhurst * Email address: tony@sdd.hp.com -or- hp-sdd!tony */ /* Compression for mode 9 taken from ghostscript 2.5.2 */ /* ** Compress_0() does mode 0 compression (which is a no compression). ** always usable !! */ static int Compress_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); } #ifdef USE_1 /* ****************************************************************************** ** ** Compress_1() does PCL compression mode 1 on the data. ** This mode is run length encoding. ** ** Given an input byte count of n, then the worst case output size ** would be 2 * n. ** ****************************************************************************** */ static int Compress_1(src, dest, count) unsigned char *src, *dest; register int count; { unsigned char *outptr = dest, *inptr = src; unsigned char data; /* data values to match */ unsigned char temp; int repcount; /* repeat count */ /* ** "count" is the number of bytes in "src" to compress ** into "dest". */ while ( count ) { data = *inptr++; /* get value to work with */ count--; repcount = 0; /* no repeats yet */ /* ** Look for successive bytes that match "data". */ while ( count && *inptr == data ) { repcount++; inptr++; count--; } /* ** Now if we are out of data (count == 0), then ** if the repeated byte was zero, then we can ignore it ** unless the user disabled zero stripping. */ if ( count == 0 && data == 0 && zerostrip ) break; /* done */ /* ** Now output the repeat count and the value. ** ** If the repeat count exceeds 255, then we must send ** more repeat-value pairs. ** ** Each time thru the loop, repcount needs to be decremented ** to keep in sync. That is, if repcount == 256, then the ** first time thru the loop, <255> <data> are output, then ** repcount is now 1. So the next time thru the loop, ** repcount needs to be adjusted to 0 so that the next pair ** is <0> <data>. Thus, 1 data plus 255 repeats of data ** plus 1 data + 0 repeats of data ==> 257 total bytes ** of "data", which is what a repcount of 256 means. */ do { temp = MIN(repcount, 255); *outptr++ = temp; repcount -= temp; *outptr++ = data; } while ( repcount-- ); } return ( outptr - dest ); /* size of compressed data */ } #endif #ifdef USE_2 /* ****************************************************************************** ** ** Compress_2() does PCL compression mode 2 on the data. ** This mode is a combination of modes 0 and 1. ** ** Given an input byte count of n, then the worst case output size ** would be n + (n + 127)/128. ** ****************************************************************************** */ static int Compress_2(src, dest, count) unsigned char *src, *dest; register int count; { unsigned char *outptr, *inptr; unsigned char *saveptr; unsigned char data; /* data byte */ unsigned char lastbyte; /* last byte */ int repcount; /* repeat count */ int litcount; /* literal count */ /* ** src points to the input data. ** dest points to the output buffer. ** count is the number of input bytes. */ inptr = src; outptr = dest; /* ** Start loop thru data. Check for possible repeat at beginning. */ while ( count ) { data = *inptr++; /* get value to work with */ count--; repcount = 0; /* no repeat count yet */ /* ** Check for repeat, since we are not in the middle ** of a literal run, it does not have to be more than ** two bytes of similar data. */ while ( count && *inptr == data ) { repcount++; inptr++; count--; } /* ** Now, if we are out of data (count == 0), then ** if the repeated byte was zero, then ignore it ** completely (don't bother outputing the trailing zeros). ** ** To always strip zero's, simply remove the "zerostrip" ** from the test. */ if ( count == 0 && data == 0 && zerostrip) break; /* done */ /* ** If there was a repeat (repcount > 0), then we ** can output the command here, otherwise, we ** need to go into literal run mode. ** ** Note: This is a while loop because the repeat count ** may actually be greater than 127. */ if ( repcount >= 1 ) /* repeat mode */ { while (repcount > 127) { *outptr++ = 129; /* count 127 */ *outptr++ = data; /* value */ repcount-= 128; /* offset */ } if (repcount > 0) { *outptr++ = 256 - repcount; /* count */ *outptr++ = data; /* value */ /* ** Now pop to the top of the loop ** looking for more repeat counts. */ continue; /* top of loop */ } /* ** Special case: If we have arrived at this point, ** then repcount is now equal to 0. This means ** that when we entered this section, repcount ** was a multiple of 128 (i.e. 128 :-). ** ** This means that there were 129 identical bytes, ** so the output does a replicate of 127 which ** gives 128 bytes, and we now have one byte left ** over which should NOT be output as a repeat ** run, rather it should be merged into the following ** literal run (if it exists). ** ** So, we will simply fall thru to the next section ** of code which assumes that we are working on ** a literal run. */ } /* ** Literal run: At this point, the current data byte ** does NOT match the following byte. We will transfer ** these non-identical bytes until: ** ** 1) we run out of input data (count == 0). ** 2) we run out of room in this output block (128) ** 3) we come across a value which occurs at least ** three times in a row. A value occuring only ** twice in a row does NOT justify dropping ** out of a literal run. ** ** Special case: If we run out of room in the output block ** (which is 128 bytes), the last two values are the same, ** AND there is more input, it makes sense to restart ** the repeat detector in case the following bytes are ** repeats of the two. A simple check of the following ** byte will determine this. ** (This case falls out with the test for triples below). ** ** Special case: If we run out of room in the output block ** (which is 128 bytes), the last value is the same as ** the next one on the input, then it is better to let ** that byte be used in a possible replicate run following ** the literal run. If the last byte matches ONLY the ** following byte, (and not the one after that,) it is ** a wash, but for best results, we will test the ** following two bytes. ** */ litcount = 0; saveptr = outptr++; /* save location of the command byte */ *outptr++ = data; /* save the first byte. */ lastbyte = data; /* remember for testing */ while ( count && litcount < 127 ) { data = *inptr++; count--; litcount++; *outptr++ = data; /* ** Test to see if this byte matched the last one. ** If so, check the next one for a triple. */ if ( lastbyte == data && count && *inptr == data ) { /* ** We have a triple, adjust accordingly. ** ** Add two bytes back onto the input. */ count += 2; inptr -= 2; outptr -= 2; litcount -= 2; break; /* out of loop */ } lastbyte = data; /* save data byte */ } /* ** Check the special case number 2 above. */ if ( litcount == 127 && count > 1 && data == *inptr && data == inptr[1] ) { /* Restore the last byte to the input stream */ count += 1; inptr -= 1; outptr -= 1; litcount -= 1; } /* ** Save the literal run count. */ *saveptr = litcount; /* ** Now go back to the top and look for repeats. */ } count = outptr - dest; /* for return value */ return ( count ); } #endif #ifdef USE_3 /* ****************************************************************************** ** ** Compress_3() does PCL compression mode 3 on the data. ** This mode is delta row encoding. ** ** Given an input byte count of n, then the worst case output size ** would be n + (n + 7)/8 ** ****************************************************************************** */ static int Compress_3(seed, new, dest, count) unsigned char *seed, *new, *dest; register int count; { unsigned char *sptr=seed, *nptr=new, *dptr=dest; unsigned char *saveptr; int delta, xfer; unsigned char command; /* ** "count" is the number of bytes of data in "new" that need to ** be compressed into "dest" using "seed" as the basis for diffs. */ while ( count > 0 ) { delta = 0; /* position counter */ /* ** Hunt through the data until the new data is different ** from the seed data. */ while ( *sptr == *nptr && delta < count ) { delta++; sptr++; nptr++; } /* ** Either a difference has been found, or we ran out of data. */ if ( delta >= count ) /* too far */ break; /* done */ count -= delta; /* ** Now set up the output with the command byte[s]. ** (leaving the actual byte copy count till last.) */ /* ** The first command byte can only hold up to 31. ** If the delta is larger, then following bytes will ** be needed. */ saveptr = dptr++; /* save the address for command byte */ command = MIN(delta, 31); /* ** Any remaining count follows. ** ** If count is 31, then a following byte must be given, ** even if 0. Same holds if 255 is given in succesive bytes. */ if ( command == 31 ) { delta -= command; /* adjust for first count */ do { xfer = MIN(delta,255); delta -= xfer; *dptr++ = xfer; } while ( xfer == 255 ); } /* ** Now transfer up to 8 bytes, stopping when the new byte ** matches the seed byte. One could make a case for ** transfering a matching byte too (if stuck in the middle ** of the 8 bytes), but it does not impact the worst case, ** and in the long run, the compression will not be as good. ** Also, we need to make sure that we don't overrun count. ** ("count" is checked first so we don't reference past the ** end of the memory block). */ for ( xfer = 0; count && *sptr != *nptr && xfer < 8; xfer++) { *dptr++ = *nptr++; /* transfer byte */ sptr++; /* bump seed pointer too */ count--; } /* ** Now xfer is the number of bytes transfered, but the ** number range is 3 bits (0-7), so decrement and merge ** it into the command byte and put it in the data. */ command += ((xfer - 1) << 5); *saveptr = command; } return ( dptr - dest ); } #endif #ifdef USE_9 /* ****************************************************************************** ** ** Compress_9() does PCL compression mode 9 on the data. ** This mode is delta row encoding. ** ** Given an input byte count of n, then the worst case output size ** would be ??? ** ****************************************************************************** */ static int Compress_9(previous, current, compressed, bytecount) unsigned const char *previous, *current; unsigned char *compressed; register int bytecount; /* * Mode 9 2D compression for the HP DeskJet 500C. This mode can give * very good compression ratios, especially if there are areas of flat * colour (or blank areas), and so is 'highly recommended' for colour * printing in particular because of the very large amounts of data which * can be generated */ { register const unsigned char *cur = current; register const unsigned char *prev = previous; register unsigned char *out = compressed; const unsigned char *end = current + bytecount; while (cur < end) { /* Detect a run of unchanged bytes. */ const unsigned char *run = cur; register const unsigned char *diff; int offset; while (cur < end && *cur == *prev) { cur++, prev++; } if (cur == end) break; /* rest of row is unchanged */ /* Detect a run of changed bytes. */ /* We know that *cur != *prev. */ diff = cur; do { prev++; cur++; } while (cur < end && *cur != *prev); /* Now [run..diff) are unchanged, and */ /* [diff..cur) are changed. */ offset = diff - run; { const unsigned char *stop_test = cur - 4; int dissimilar, similar; while (diff < cur) { const unsigned char *compr = diff; const unsigned char *next; /* end of run */ unsigned char value=0; while (diff <= stop_test && ((value = *diff) != diff[1] || value != diff[2] || value != diff[3])) diff++; /* Find out how long the run is */ if (diff > stop_test) /* no run */ next = diff = cur; else { next = diff + 4; while (next < cur && *next == value) next++; } #define MAXOFFSETU 15 #define MAXCOUNTU 7 /* output 'dissimilar' bytes, uncompressed */ if ((dissimilar = diff - compr)) { int temp, i; if ((temp = --dissimilar) > MAXCOUNTU) temp = MAXCOUNTU; if (offset < MAXOFFSETU) *out++ = (offset << 3) | (unsigned char) temp; else { *out++ = (MAXOFFSETU << 3) | (unsigned char) temp; offset -= MAXOFFSETU; while (offset >= 255) { *out++ = 255; offset -= 255; } *out++ = offset; } if (temp == MAXCOUNTU) { temp = dissimilar - MAXCOUNTU; while (temp >= 255) { *out++ = 255; temp -= 255; } *out++ = (unsigned char) temp; } for (i = 0; i <= dissimilar; i++) *out++ = *compr++; offset = 0; } /* end uncompressed */ #define MAXOFFSETC 3 #define MAXCOUNTC 31 /* output 'similar' bytes, run-length encoded */ if ((similar = next - diff)) { int temp; if ((temp = (similar -= 2)) > MAXCOUNTC) temp = MAXCOUNTC; if (offset < MAXOFFSETC) *out++ = 0x80 | (offset << 5) | (unsigned char) temp; else { *out++ = 0x80 | (MAXOFFSETC << 5) | (unsigned char) temp; offset -= MAXOFFSETC; while (offset >= 255) { *out++ = 255; offset -= 255; } *out++ = offset; } if (temp == MAXCOUNTC) { temp = similar - MAXCOUNTC; while (temp >= 255) { *out++ = 255; temp -= 255; } *out++ = (unsigned char) temp; } *out++ = value; offset = 0; } /* end compressed */ diff = next; } } } return out - compressed; } #endif /* * print a scanline and select the modes */ void PrintScanLine(unsigned char *buf, int anz) { int minmode = 0; unsigned char *temprow; /* Anything is better than this */ bestsize = INT_MAX; #ifdef USE_9 testsize = seed_row ? Compress_9(seed_row, buf, testrow, anz) : INT_MAX; if (testsize + (in_mode != 9 ? 2 : 0) < bestsize) { temprow = testrow; testrow = bestrow; bestrow = temprow; bestsize = testsize; minmode = 9; } #endif #ifdef USE_3 testsize = seed_row ? Compress_3(seed_row, buf, testrow, anz) : INT_MAX; if (testsize + (in_mode != 3 ? 2 : 0) < bestsize) { temprow = testrow; testrow = bestrow; bestrow = temprow; bestsize = testsize; minmode = 3; } #endif #ifdef USE_2 /* mode 2 is always tested */ testsize = Compress_2( buf, testrow, anz); if (testsize + (in_mode != 2 ? 2 : 0) < bestsize) { temprow = testrow; testrow = bestrow; bestrow = temprow; bestsize = testsize; minmode = 2; } #endif #ifdef USE_1 /* Usually don't check mode 1 (it is rarely better than mode 2) and */ /* mode 0 (mode 2 is only about 1% longer in worst case) */ testsize = Compress_1( buf, testrow, anz); if (testsize + (in_mode != 1 ? 2 : 0) < bestsize) { temprow = testrow; testrow = bestrow; bestrow = temprow; bestsize = testsize; minmode = 1; } #endif /* Mode 0 is cheap and always there */ testsize = Compress_0(buf, testrow, anz); if (testsize + (in_mode != 0 ? 1 : 0) < bestsize) { bestrow = buf; bestsize = testsize; minmode = 0; } if ( in_mode != minmode ) { printf(minmode ? "%dm" : "m", minmode); in_mode = minmode; } /* <esc>*b has already been output */ printf(bestsize ? "%dw" : "w", bestsize); if ( fwrite( bestrow, 1, bestsize, stdout) < bestsize ) { /* check for error and exit */ if ( ferror(stdout) ) { perror("Output error"); exit(-2); } } } /* * small improvement for speed: * if we know that we are printing (one of the pins 0-49 is active) it is * faster to print a null line, than skipping it via *b%dY. This way the * printing head doesn't need to print, move vertically and restart printing. */ #define PINS 50 void PrintPage(void) { /* output the whole Page to the printer */ register unsigned char *sp, *p; register int i; extern NXPrintPageMessage ppm; extern mutex_t mutex1; int in_seq = -1, empty_lines = 0; mutex_lock(mutex1); sendsequence(page_init); /* start plot at current position */ mutex_unlock(mutex1); in_mode = -1; /* invalidate some vars */ seed_row = NULL; printf("\033*b"); /* prepare */ for (i = ppm.pixelsHigh, sp = (unsigned char *) ppm.printerData; i > 0; i--, sp += ppm.bytesPerRow) { /* #ifdef DEBUG fprintf(stderr, "Line: %d/%d\n", ppm.pixelsHigh - i, ppm.pixelsHigh); #endif */ /* Im Druck einer 50er Zeile? */ if (in_seq >= 0) { mutex_lock(mutex1); PrintScanLine(sp, ppm.bytesPerRow); mutex_unlock(mutex1); /* Ende der ganzen Zeile? */ if (++in_seq == PINS) in_seq = -1; } else { for (p = sp + ppm.bytesPerRow - 1; p >= sp && *p == 0; p--) ; /* Leere Zeile? */ if (p < sp) empty_lines++; else { /* Volle Zeile nach leeren Zeilen */ if (empty_lines > 0) { (void) fprintf(stdout, "%dy", empty_lines); } mutex_lock(mutex1); PrintScanLine(sp, ppm.bytesPerRow); mutex_unlock(mutex1); empty_lines = 0; /* Erster Pin */ in_seq = 0; } } seed_row = sp; } printf("M"); /* Must have one capital letter */ mutex_lock(mutex1); sendsequence(page_exit); /* Eject page */ mutex_unlock(mutex1); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.