#ifndef lint
static char sccsid[] = "@(#)tif_picio.c	1.20 5/22/90";
#endif

/*
 * Copyright (c) 1988, 1990 by Sam Leffler.
 * All rights reserved.
 *
 * This file is provided for unrestricted use provided that this
 * legend is included on all tape media and as a part of the
 * software program in whole or part.  Users may copy, modify or
 * distribute this file at will.
 */

/*
 * TIFF Library.
 *
 * Pixar Picio RLE Algorithm Support
 *
 * NB: This algorithm is not (currently) very effective
 *   because the state is reset at each scanline.  This
 *   was done to avoid having dangling pointers into the
 *   raw data buffer that are invalidated by the flushing
 *   that occurs in TIFFWriteScanline().  To fix this
 *   problem we need to add an encodestrip routine (to reset
 *   state) and maintain state during decoding so that
 *   runs and such can be restarted when they cross scanline
 *   boundaries.  However, since LZW already works on a
 *   strip basis and seems to be more effective, there's
 *   not much reason to go to the trouble.
 */
#include "file_formats/tiffio.h"

/*
 * Each image is composed of a sequence of dumps, runs,
 * partial dumps, and partial runs.  The first two
 * bytes of a sequence contain a flag indicator and
 * a count.  Flags are encoded in the upper 4 bits
 * of the first byte while the lower 12 bits contain
 * the count.
 */
/* tile flag indicators */
#define	PT_FILL		0		/* fill input buffer */
#define PT_FULLDUMP	1		/* full dump, use count */
#define PT_FULLRUN	2		/* full run, length precedes pixel */
#define PT_PARTDUMP	3		/* part dump, alpha constant */
#define PT_PARTRUN	4		/* part run, alpha constant */

/*
 * Picio encoding state.
 */
#define	MAXPACKETSIZE	8

struct	piciostate {
	u_char	*startptr;
	u_char	*lptr;		/* pointer to length byte in run */
	u_short	len;		/* length of run when in 16-bit mode */
	short	count;		/* repeat count of data in packet */
	short	amark;		/* index to alpha channel */
	int	type;		/* 4-bit packet type */
	int	fulldisklength;	/* size of data+alpha unit */
	int	infodisklength;	/* size of data unit */
	u_char	*fulleobuffer;	/* output buffer fencepost for full packet */
	u_char	*infoeobuffer;	/* output buffer fencepost for info packet */
/* temp buffers of data going to disk */
	u_char	disk0buffer[MAXPACKETSIZE];
	u_char	disk1buffer[MAXPACKETSIZE];
/* temp buffers of data in memory */
	u_short	core0buffer[MAXPACKETSIZE/2];
	u_short	core1buffer[MAXPACKETSIZE/2];
};

/* macros for unrolling loops */
#define REPEAT4(n, op) \
    switch (n) {case 4: op; case 3: op; case 2: op; case 1: op; }
#define REPEAT8(n, op) \
    switch (n) {case 8: op; case 7: op; case 6: op; case 5: op; \
		case 4: op; case 3: op; case 2: op; case 1: op; }

#if USE_PROTOTYPES
static	int PicioStripEncode(TIFF*);
static	int PicioEncode(TIFF*, u_char *, int);
static	int PicioDecodeStrip(TIFF*);
static	int PicioCleanup(TIFF*);
static	int pic8DecodeScanline(TIFF*, u_char *, int);
static	int pic16DecodeScanline(TIFF*, u_short *, int);
static	void endpacket(struct piciostate*);
#else
static	int PicioStripEncode(), PicioEncode();
static	int PicioDecodeStrip();
static	int pic8DecodeScanline(), pic16DecodeScanline();
static	int PicioCleanup();
static	void endpacket();
#endif

TIFFInitPicio(tif)
	TIFF *tif;
{
	tif->tif_stripdecode = PicioDecodeStrip;
	tif->tif_stripencode = PicioStripEncode;
	tif->tif_encoderow = PicioEncode;
	tif->tif_cleanup = PicioCleanup;
	return (1);
}

static
PicioDecodeStrip(tif)
	TIFF *tif;
{
	switch (tif->tif_dir.td_bitspersample) {
	case 8:	
		tif->tif_decoderow = pic8DecodeScanline;
		break;
	case 16:
		tif->tif_decoderow = pic16DecodeScanline;
		break;
	default:
		TIFFError("PicioDecode", "Cannot handle %d-bit pictures",
		    tif->tif_dir.td_bitspersample);
		return (0);
	}
	return (1);
}

#define	SPRAY4(nc, op) \
	switch (nc) { \
	case 4: op[3]; \
	case 3: op[2]; \
	case 2: op[1]; \
	case 1: op[0]; \
	}

static
pic8DecodeScanline(tif, op, cc)
	TIFF *tif;
	register u_char *op;
	int cc;
{
	static char module[] = "PicioDecode";
	register u_char *bp, *sp, *ep;
	register int npixels;
	short nc, flag, word, count, length;
	u_char spare[4];

	bp = (u_char *)tif->tif_rawcp;
	ep = bp + tif->tif_rawcc;
	npixels = tif->tif_dir.td_imagewidth;
	nc = (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG ?
	    tif->tif_dir.td_samplesperpixel : 1);
	while (bp < ep && npixels > 0) {
		word = (bp[1] << 8) | bp[0], bp += 2;
		count = (word & 0xfff) + 1;
		switch (flag = word >> 12) {
		case PT_FULLRUN:
			while (count-- > 0) {
				/*
				 * The first byte holds a count. 
				 * The next bytes hold data to be
				 * repeated.
				 */
				length = (*bp++) + 1;
				sp = spare+nc;
				REPEAT4(nc, *--sp = *bp++);
				npixels -= length;
				for (sp = spare; length-- > 0;)
					SPRAY4(nc, *op++ = sp);
			}
			break;
		case PT_FULLDUMP:
			npixels -= count;
			for (count *= nc; count-- > 0; *op++ = *bp++)
				;
			break;
		case PT_PARTRUN:
			spare[0] = *bp++;
			while (count-- > 0) {
				/*
				 * The first byte is a count. 
				 * The next bytes are data to
				 * be repeated.
				 */
				length = (*bp++) + 1;
				sp = spare+nc;
				REPEAT4(nc-1, *--sp = *bp++);
				npixels -= length;
				for (sp = spare; length-- > 0;)
					SPRAY4(nc, *op++ = sp);
			}
			break;
		case PT_PARTDUMP:
			/*
			 * The fixed value (normally alpha) is
			 * stored in the first byte.
			 */
			(sp = spare)[0] = *bp++;
			npixels -= count;
			while (count-- > 0) {
				REPEAT4(nc - 1, *op++ = *bp++);
				*op++ = sp[0];
			}
			break;
		default:
			TIFFError(module, 
			    "%s: Unknown flag 0x%x at scanline %d",
			    tif->tif_name, flag, tif->tif_row);
			return (0);
		}
	}
	tif->tif_rawcc -= bp - (u_char *)tif->tif_rawcp;
	tif->tif_rawcp = (char *)bp;
	if (npixels > 0) {
		TIFFError(module, "%s: Not enough data for scanline %d",
		    tif->tif_name, tif->tif_row);
		return (0);
	}
	return (1);
}

static
pic16DecodeScanline(tif, op, cc)
	TIFF *tif;
	register u_short *op;
	int cc;
{
	static char module[] = "PicioDecode";
	register u_short *sp;
	register u_char *bp, *ep;
	register int npixels;
	short nc, flag, word, count, length;
	u_short spare[4];

	bp = (u_char *)tif->tif_rawcp;
	ep = bp + tif->tif_rawcc;
	npixels = tif->tif_dir.td_imagewidth;
	nc = (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG ?
	    tif->tif_dir.td_samplesperpixel : 1);
	while (bp < ep && npixels > 0) {
		word = (bp[1] << 8) | bp[0], bp += 2;
		count = (word & 0xfff) + 1;
		switch (flag = word >> 12) {
		case PT_FULLRUN:
			while (count-- > 0) {
				/*
				 * The first byte holds a count. 
				 * The next bytes hold data to be
				 * repeated.
				 */
				length = ((bp[1] << 8) | bp[0]) + 1; bp += 2;
				sp = spare+nc;
				REPEAT4(nc, *--sp = bp[0]|(bp[1]<<8); bp += 2);
				npixels -= length;
				for (sp = spare; length-- > 0;)
					SPRAY4(nc, *op++ = sp);
			}
			break;
		case PT_FULLDUMP:
			npixels -= count;
			while (count-- > 0)
				REPEAT4(nc, *op++ = bp[0]|(bp[1]<<8); bp += 2);
			break;
		case PT_PARTRUN:
			spare[0] = bp[0]|(bp[1]<<8); bp += 2;
			while (count-- > 0) {
				/*
				 * The first byte is a count. 
				 * The next bytes are data to
				 * be repeated.
				 */
				length = ((bp[1] << 8) | bp[0]) + 1; bp += 2;
				sp = spare+nc;
				REPEAT4(nc-1, *--sp = bp[0]|(bp[1]<<8); bp +=2);
				npixels -= length;
				for (sp = spare; length-- > 0;)
					SPRAY4(nc, *op++ = sp);
			}
			break;
		case PT_PARTDUMP:
			/*
			 * The fixed value (normally alpha) is
			 * stored in the first byte.
			 */
			(sp = spare)[0] = bp[0]|(bp[1]<<8); bp += 2;
			npixels -= count;
			while (count-- > 0) {
				REPEAT4(nc-1, *op++ = bp[0]|(bp[1]<<8); bp +=2);
				*op++ = sp[0];
			}
			break;
		default:
			TIFFError(module,
			    "%s: Unknown flag 0x%x at scanline %d",
			    tif->tif_name, flag, tif->tif_row);
			return (0);
		}
	}
	tif->tif_rawcc -= bp - (u_char *)tif->tif_rawcp;
	tif->tif_rawcp = (char *)bp;
	if (npixels > 0) {
		TIFFError(module, "%s: Not enough data for scanline %d",
		    tif->tif_name, tif->tif_row);
		return (0);
	}
	return (1);
}

/*
 * Data is encoded into packets that include
 * a flag, a count, and data; and which never
 * span scanlines.
 */
#if USE_PROTOTYPES
#define	DECLARE3(f,t1,a1,t2,a2,t3,a3)	f(t1 a1, t2 a2, t3 a3)
static u_char *startpacket(TIFF*, u_char *, short);
#else
#define	DECLARE3(f,t1,a1,t2,a2,t3,a3)	f(a1, a2, a3) t1 a1; t2 a2; t3 a3;
static u_char *startpacket();
#endif

/* yech -- this knows about bp & runstatus */
#define startnewbuffer(tif, pt) { \
	endpacket((struct piciostate *)tif->tif_data); \
	tif->tif_rawcc += bp - (u_char *)tif->tif_rawcp; \
	if (!TIFFFlushData(tif)) \
		return (-1); \
	bp = startpacket(tif, tif->tif_rawcp, runstatus = pt); \
	if (bp == (u_char *)0) \
		return (-1); \
}
#define	OVERRUN(ps,cp,len)	(cp > ps->CAT(len,eobuffer))

/* state indicators used in encoding */
#define PT_UNKNOWN	-1
#define PT_CFULLRUN	-2
#define PT_STARTUP	-3
#define PT_CPARTRUN	-4
/*
 * To encode tile type + same-pixel + same-alpha
 * into a single value, we use the following
 * magic constants.
 */
#define	SP_FALSE	(0 * (PT_PARTRUN - PT_CPARTRUN + 1))
#define	SP_TRUE		(1 * (PT_PARTRUN - PT_CPARTRUN + 1))
#define	SA_FALSE	(2 * SP_FALSE)
#define	SA_TRUE		(4 * SP_TRUE)

static	u_char *newdisk;
static	u_char *olddisk;
static	u_short *newcore;
static	u_short *oldcore;

/*
 * Setup encoding state.
 */
static
PicioStripEncode(tif)
	register TIFF *tif;
{
	register struct piciostate *ps;
	int bytespersample;

	if (tif->tif_data != NULL)
		return (1);
	if (tif->tif_dir.td_bitspersample != 8 &&
	    tif->tif_dir.td_bitspersample != 16) {
		TIFFError("PicioEncode", "Cannot handle %d-bit pictures",
		    tif->tif_dir.td_bitspersample);
		return (0);
	}
	tif->tif_data = malloc(sizeof (struct piciostate));
	if (tif->tif_data == NULL) {
		TIFFError("PicioEncode", "No space for picio state block");
		return (0);
	}
	bzero(tif->tif_data, sizeof (struct piciostate));
	ps = (struct piciostate *)tif->tif_data;
	if (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG) {
		ps->fulldisklength = tif->tif_dir.td_samplesperpixel;
		if ((ps->infodisklength = tif->tif_dir.td_samplesperpixel) > 3)
			ps->infodisklength = 3;
		ps->amark = tif->tif_dir.td_matteing ?
		    tif->tif_dir.td_samplesperpixel-1 : 3;
	} else {
		ps->fulldisklength = ps->infodisklength = 1;
		ps->amark = 0;
	}
	bytespersample = tif->tif_dir.td_bitspersample <= 8 ? 1 :
			 tif->tif_dir.td_bitspersample <= 16 ? 2 : 4;
	ps->fulldisklength *= bytespersample;
	ps->infodisklength *= bytespersample;

	/*
	 * eofullbuffer & eoinfobuffer mark the last spot
	 * in the buffer at which a packet can be placed
	 * (either a fulldisklength or infodisklength packet).
	 * The 2+ is for the 2 byte packet type and length.
	 */
	ps->fulleobuffer = (u_char *)tif->tif_rawdata +
	    tif->tif_rawdatasize - (2+ps->fulldisklength) - 1;
	ps->infoeobuffer = (u_char *)tif->tif_rawdata +
	    tif->tif_rawdatasize - (2+ps->infodisklength) - 1;
	return (1);
}

/*
 * Encode a scanline of pixels.
 */
static
PicioEncode(tif, ip, cc)
	TIFF *tif;
	register u_char *ip;
	int cc;
{
	register u_char *bp;
	register struct piciostate *ps;
	int cmpstatus, runstatus, nc, hasalphachannel;
	u_short *cp1, *cp2, *wp;
	u_char *dp, *ep;

	ps = (struct piciostate *)tif->tif_data;
	ep = ip + cc;
	bp = (u_char *)tif->tif_rawcp;
	if (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG) {
		nc = tif->tif_dir.td_samplesperpixel;
		hasalphachannel = tif->tif_dir.td_matteing;
	} else {
		nc = 1;
		hasalphachannel = 0;
	}
	oldcore = ps->core0buffer;
	newcore = ps->core1buffer;
	olddisk = ps->disk0buffer;
	newdisk = ps->disk1buffer;
	/* setup initial alpha to be different */
	oldcore[ps->amark] =  0;
	newcore[ps->amark] = -1;

	/*
	 * Process each pixel with a finite state machine with
	 * one-pixel look-ahead.  The look-ahead is used to compress
	 * identical pixels into ``pixel runs''.  The states of
	 * encoding process include all the packet types, filled
	 * out with a startup state (used only at startup) and
	 * an unknown state (used when we have no packet open and
	 * do not know yet what kind of packet this last pixel
	 * will start.  Also needed are temporary runlength states
	 * which are needed, for example, when we have just finished 
	 * one run in a runlength packet and do not know whether this
	 * last pixel is the start of a new run or the start of a
	 * whole new (dump) packet.
	 */
	runstatus = PT_STARTUP;
	while (ip < ep) {
		/*
		 * Swap pointers to new and old data buffers.
		 */
#define	SWAP(t, a, b)	{ t tmp; tmp = a; a = b; b = tmp; }
		SWAP(u_short *, oldcore, newcore);
		SWAP(u_char *, olddisk, newdisk);

		/* 
		 * Next move the new pixel from the
		 * user buffer into the local array.
		 * and set up the cmpstatus flag to
		 * indicate if the new and old pixels
		 * agree fully, or just in the flat
		 * field (alpha) component.
		 */
		cmpstatus = SP_TRUE;
		cp1 = newcore; cp2 = oldcore; dp = newdisk;
		if (tif->tif_dir.td_bitspersample == 8) {
			REPEAT4(nc, *cp1 = *ip++;
			    if (*cp2++ != *cp1)
				cmpstatus = SP_FALSE;
			    *dp++ = *cp1++);
		} else {
			wp = (u_short *)ip;	/* XXX for non-pcc compilers */
			REPEAT4(nc, *cp1 = *wp++;
			    if (*cp2++ != *cp1)
				cmpstatus = SP_FALSE;
			    *dp++ = *cp1;
			    *dp++ = *cp1++ >> 8);
			ip = (u_char *)wp;
		}
		cmpstatus += oldcore[ps->amark] == newcore[ps->amark] ?
		    SA_TRUE : SA_FALSE;

		/*
		 * Here is the main loop that decides how to
		 * handle the new pixel based on the current
		 * state.
		 */
		switch (runstatus + cmpstatus) {
		/*
		 * Continue the full run because
		 * the new pixel matches the old.
		 */
		case PT_FULLRUN + SP_TRUE + SA_FALSE:
		case PT_FULLRUN + SP_TRUE + SA_TRUE:
			/*
			 * The run count just overflowed.
			 * Go to the CFULLRUN state, because
			 * this run is done, and we do not
			 * know whether to start a new packet
			 * or just a new run.
			 */
			if (tif->tif_dir.td_bitspersample == 16) {
				ps->lptr[0] = ++(ps->len);
				ps->lptr[1] = ps->len >> 8;
				if (ps->len != 0)
					break;
				ps->lptr[0] = --(ps->len);
				ps->lptr[1] = ps->len >> 8;
			} else {
				if (++(*ps->lptr) != 0)
					break;
				(*ps->lptr)--;
			}
			/* fall thru... */
		case PT_FULLRUN + SP_FALSE + SA_FALSE:
		case PT_FULLRUN + SP_FALSE + SA_TRUE:
			/*
			 * Terminate the current run and
			 * go to CFULLRUN to decide whether
			 * to start a new run with this
			 * pixel or to start a whole new
			 * packet.
			 */
			runstatus = PT_CFULLRUN;
			break;
		/*
		 * In the middle of a fullrun packet; wait
		 * to see whether to start a new run, or
		 * whether to end this packet and start
		 * a new packet.
		 */
		case PT_CFULLRUN + SP_TRUE + SA_FALSE:
		case PT_CFULLRUN + SP_TRUE + SA_TRUE:
			/*
			 * Start a new run.  Increment the
			 * packet count which holds the number of
			 * runs in this packet.  Put out the
			 * run length (1, meaning 1 repetition or
			 * 2 instances) and the full component
			 * information.
			 */
			if (bp > ps->fulleobuffer) {
				startnewbuffer(tif, PT_FULLRUN);
			} else {
				ps->count++;
				ps->lptr = bp;
				*bp++ = 1;
				if (tif->tif_dir.td_bitspersample == 16) {
					ps->len = 1;
					*bp++ = 0;
				}
				dp = newdisk;
				REPEAT8(ps->fulldisklength, *bp++ = *dp++);
			}
			runstatus = PT_FULLRUN;
			break;
		case PT_CFULLRUN + SP_FALSE + SA_TRUE:
			/*
			 * End this packet and start a partial
			 * dump packet, because at least the alpha
			 * stayed constant.
			 */
			endpacket(ps);
			bp = startpacket(tif, bp, runstatus = PT_PARTDUMP);
			if (bp == 0)
				return (-1);
			break;
		case PT_CFULLRUN + SP_FALSE + SA_FALSE:
			endpacket(ps);
			bp = startpacket(tif, bp, runstatus = PT_FULLDUMP);
			if (bp == 0)
				return (-1);
			break;
		case PT_FULLDUMP + SP_TRUE + SA_TRUE:
		case PT_FULLDUMP + SP_TRUE + SA_FALSE:
			/*
			 * End this full dump packet and start
			 * a full run because of the match.
			 */
			endpacket(ps);
			bp = startpacket(tif, bp, runstatus = PT_FULLRUN);
			if (bp == 0)
				return (-1);
			break;
		case PT_FULLDUMP + SP_FALSE + SA_TRUE:
			endpacket(ps);
			bp = startpacket(tif, bp, runstatus = PT_PARTDUMP);
			if (bp == 0)
				return (-1);
			break;
		case PT_FULLDUMP + SP_FALSE + SA_FALSE:
			/*
			 * Continue the dump packet.  Write
			 * the full component information into
			 * the buffer.
			 */
			if (bp > ps->fulleobuffer) {
				startnewbuffer(tif, PT_FULLDUMP);
			} else {
				ps->count++;
				dp = olddisk;
				REPEAT8(ps->fulldisklength, *bp++ = *dp++);
			}
			break;
		case PT_PARTRUN + SP_TRUE + SA_FALSE:
		case PT_PARTRUN + SP_TRUE + SA_TRUE:
			/*
			 * Continue the part run packet.
			 */
			if (tif->tif_dir.td_bitspersample == 16) {
				ps->lptr[0] = ++(ps->len);
				ps->lptr[1] = ps->len >> 8;
				if (ps->len != 0)
					break;
				ps->lptr[0] = --(ps->len);
				ps->lptr[1] = ps->len >> 8;
			} else {
				if (++(*ps->lptr) != 0)
					break;
				(*ps->lptr)--;
			}
			/* fall thru... */
		case PT_PARTRUN + SP_FALSE + SA_TRUE:
			/*
			 * Go to CPARTRUN because this may
			 * just be the start of a new run for
			 * this packet.
			 */
			runstatus = PT_CPARTRUN;
			break;
		case PT_PARTRUN + SP_FALSE + SA_FALSE:
			/*
			 * With no match at all, we have to end
			 * this part run packet.  With only this
			 * last pixel to start with, we do not
			 * know what packet to start next.  Thus
			 * we go to the UNKNOWN state.
			 */
			endpacket(ps);
			runstatus = PT_UNKNOWN;
			break;
		case PT_CPARTRUN + SP_TRUE + SA_FALSE:
		case PT_CPARTRUN + SP_TRUE + SA_TRUE:
			if (bp > ps->infoeobuffer) {
				startnewbuffer(tif, PT_PARTRUN);
			} else {
				ps->count++;
				ps->lptr = bp;
				*bp++ = 1;
				if (tif->tif_dir.td_bitspersample == 16) {
					ps->len = 1;
					*bp++ = 0;
				}
				dp = newdisk;
				REPEAT8(ps->infodisklength, *bp++ = *dp++);
			}
			runstatus = PT_PARTRUN;
			break;
		case PT_CPARTRUN + SP_FALSE + SA_TRUE:
			endpacket(ps);
			bp = startpacket(tif, bp, runstatus = PT_PARTDUMP);
			if (bp == 0)
				return (-1);
			break;
		case PT_CPARTRUN + SP_FALSE + SA_FALSE:
			endpacket(ps);
			bp = startpacket(tif, bp, runstatus = PT_FULLDUMP);
			if (bp == 0)
				return (-1);
			break;
		case PT_PARTDUMP + SP_TRUE + SA_FALSE:
		case PT_PARTDUMP + SP_TRUE + SA_TRUE:
			/*
			 * This dump packet ends and a partial
			 * run packet begins with these last two
			 * pixels.
			 */
			endpacket(ps);
			bp = startpacket(tif, bp, runstatus = PT_PARTRUN);
			if (bp == 0)
				return (-1);
			break;
		case PT_PARTDUMP + SP_FALSE + SA_TRUE:
			/*
			 * The partial dump packet continues.
			 */
			if (bp > ps->infoeobuffer) {
				startnewbuffer(tif, PT_PARTDUMP);
			} else {
				ps->count++;
				dp = olddisk;
				REPEAT8(ps->infodisklength, *bp++ = *dp++);
			}
			break;
		case PT_PARTDUMP + SP_FALSE + SA_FALSE:
			/*
			 * The partial dump ends because alpha
			 * is no longer constant.  Finish off
			 * this packet and go to the unknown
			 * state with this new pixel.
			 */
			if (bp > ps->infoeobuffer) {
				startnewbuffer(tif, PT_PARTDUMP);
			} else {
				ps->count++;
				dp = olddisk;
				REPEAT8(ps->infodisklength, *bp++ = *dp++);
			}
			endpacket(ps);
			runstatus = PT_UNKNOWN;
			break;
		
		case PT_STARTUP + SP_FALSE + SA_FALSE:
		case PT_STARTUP + SP_FALSE + SA_TRUE:
		case PT_STARTUP + SP_TRUE + SA_FALSE:
		case PT_STARTUP + SP_TRUE + SA_TRUE:
			/*
			 * The only thing to do when starting is to
			 * start a packet.  However, we do not know
			 * which to start until we have two pixels,
			 * so we go to the unknown state.
			 */
			runstatus = PT_UNKNOWN;
			break;
		case PT_UNKNOWN + SP_TRUE + SA_FALSE:
		case PT_UNKNOWN + SP_TRUE + SA_TRUE:
			runstatus = hasalphachannel ? PT_PARTRUN : PT_FULLRUN;
			bp = startpacket(tif, bp, runstatus);
			if (bp == 0)
				return (-1);
			break;
		case PT_UNKNOWN + SP_FALSE + SA_TRUE:
			bp = startpacket(tif, bp, runstatus = PT_PARTDUMP);
			if (bp == 0)
				return (-1);
			break;
		case PT_UNKNOWN + SP_FALSE + SA_FALSE:
			bp = startpacket(tif, bp, runstatus = PT_FULLDUMP);
			if (bp == 0)
				return (-1);
			break;
		}
	}

	/*
	 * The following code handles the cleanup at the 
	 * end of the scanline -- we must flush the current
	 * packet.
	 */
	switch (runstatus) {
	case PT_FULLRUN:
		/*
		 * Everything is up to date.
		 */
		break;
	case PT_CFULLRUN:
		/*
		 * Add the last new pixel as a run 
		 * with repeat count 0.
		 */
		if (bp > ps->fulleobuffer) {
			startnewbuffer(tif, 0);
		} else {
			ps->count++;
			*bp++ = 0;
			if (tif->tif_dir.td_bitspersample == 16)
				*bp++ = 0;
			dp = newdisk;
			REPEAT8(ps->fulldisklength, *bp++ = *dp++);
		}
		break;
	case PT_FULLDUMP:
		/*
		 * Add this new pixel as the last pixel in a dump.
		 */
		if (bp > ps->fulleobuffer) {
			startnewbuffer(tif, 0);
		} else {
			ps->count++;
			dp = newdisk;	
			REPEAT8(ps->fulldisklength, *bp++ = *dp++);
		}
		break;
	case PT_PARTRUN:
		/*
		 * We are up to date.
		 */
		break;
	case PT_CPARTRUN:
		/*
		 * See CFULLRUN above.
		 */
		if (bp > ps->infoeobuffer) {
			startnewbuffer(tif, 0);
		} else {
			ps->count++;
			*bp++ = 0;
			if (tif->tif_dir.td_bitspersample == 16)
				*bp++ = 0;
			dp = newdisk;
			REPEAT8(ps->infodisklength, *bp++ = *dp++);
		}
		break;
	case PT_PARTDUMP:
		/*
		 * See FULLDUMP above.
		 */
		if (bp > ps->infoeobuffer) {
			startnewbuffer(tif, 0);
		} else {
			ps->count++;
			dp = newdisk;
			REPEAT8(ps->infodisklength, *bp++ = *dp++);
		}
		break;
	case PT_STARTUP:
		break;
	case PT_UNKNOWN:
		/*
		 * We are caught with one pixel in the hand and no packet
		 * to add it to.  We start a simple fulldump packet.
		 */
		bp = startpacket(tif, bp, 0);
		if (bp == 0)
			return (-1);
		break;
	}
	endpacket(ps);
	tif->tif_rawcc += bp - (u_char *)tif->tif_rawcp;
	tif->tif_rawcp = (char *)bp;
	return (1);
}

/*
 * Startpacket takes the old and new pixels
 * and starts the requested packet using them.
 */
static u_char *
DECLARE3(startpacket, TIFF*, tif, u_char*, ptr, short, type)
{
	register struct piciostate *ps;
	register u_char *dp;

	/*
	 * Set up packet type and count.  The packet count
	 * is incremented as the data is repeated.  These
	 * two values are merged and written when ending
	 * the packet.
	 */
	ps = (struct piciostate *)tif->tif_data;
	if (ptr > ps->fulleobuffer) {
		tif->tif_rawcc += ptr - (u_char *)tif->tif_rawcp;
		if (!TIFFFlushData(tif))
			return ((u_char *)0);
		ptr = (u_char *)tif->tif_rawcp;
	}
	ps->type = type;
	ps->count = 0;
	ps->startptr = ptr;
	ptr += 2;		/* skip packet header */
	switch (type) {
	case 0:
		ps->type = PT_FULLDUMP;
		dp = newdisk;
		REPEAT8(ps->fulldisklength, *ptr++ = *dp++);
		break;
	case PT_FULLDUMP:
	case PT_FULLRUN:
		if (type == PT_FULLRUN) {
			ps->lptr = ptr;
			*ptr++ = 1;
			if (tif->tif_dir.td_bitspersample == 16) {
				ps->len = 1;
				*ptr++ = 0;
			}
		}
		dp = olddisk;
		REPEAT8(ps->fulldisklength, *ptr++ = *dp++);
		break;
	case PT_PARTRUN:
	case PT_PARTDUMP:
		dp = olddisk + ps->infodisklength;
		REPEAT8(ps->fulldisklength - ps->infodisklength,
		    *ptr++ = *dp++);
		if (type == PT_PARTRUN) {
			ps->lptr = ptr;
			*ptr++ = 1;
			if (tif->tif_dir.td_bitspersample == 16) {
				ps->len = 1;
				*ptr++ = 0;
			}
		}
		dp = olddisk;
		REPEAT8(ps->infodisklength, *ptr++ = *dp++);
		break;
	}
	return (ptr);
}

/*
 * Set the packet header from the
 * accumulated state information.
 */
static void
endpacket(ps)
	register struct piciostate *ps;
{
	ps->count |= ps->type << 12;
	ps->startptr[1] = ps->count >> 8;
	ps->startptr[0] = ps->count;
}

static
PicioCleanup(tif)
	TIFF *tif;
{
	if (tif->tif_data != NULL) {
		free(tif->tif_data);
		tif->tif_data = NULL;
	}
	return (1);
}
