ftp.nice.ch/pub/next/tools/printer/djf_for_3.0.I.bs.tar.gz#/djf_for_3.0/printpage.c

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.