/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information.       */
/* Contact email: mac@aarongiles.com                     */
/*********************************************************/

//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================

#if THINK_C
#include "THINK.Header"
#include <stddef.h>
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif

//=====================================================================================
// Includes specific to this module
//=====================================================================================

#include "TIFF.h"
#include "TIFFTables.c"

//=====================================================================================
// TIFFPrivates: private data structure for TIFF images, maintained for each image.
//=====================================================================================

typedef struct TIFFPrivates TIFFPrivates, *TIFFPrivatesPtr, **TIFFPrivatesHandle;
typedef (*ExtractProcPtr)(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);

struct TIFFPrivates
{
	Boolean offsetsAreShort;	// flag: true if the strip offsets are shorts
	ushort photoInt;			// photometric interpretation
	ushort compression;			// compression method used
	ushort bitsPerSample;		// number of bits per sample (must be same for all)
	ushort samplesPerPixel;		// number of samples per pixel (only first 3 count)
	ushort depth;				// calculated depth of the image
	ulong height, width;		// size of the image
	ulong rowsPerStrip;			// number of rows per strip
	ulong stripOffsetsList;		// offset in data of list of strip offsets
	ulong colorTableOffset;		// offset of the color table in the file
	CTabHandle decompColors;	// color table to be used in decompression
	Boolean invert;				// flag: true to invert all outgoing bytes
	Boolean prediction;			// flag: true if we are decoding with prediction
	ExtractProcPtr extract;		// pointer to extract function
};

//=====================================================================================
// Constants specific to this module
//=====================================================================================

enum {
	tagBitsPerSample = 0x102,			// [short], default = 1
	tagColorMap = 0x140,				// [short], required for palette images
	tagCompression = 0x103,				// short, default = 1
		compNone = 1,
		compModCCITT3 = 2,
		compCCITT3 = 3,
		compLZW = 5,
		compPackBits = 32773,
	tagFillOrder = 0x10a,				// fill order, must be 1
	tagImageDescription = 0x13c,		// [ASCII], optional
	tagImageLength = 0x101,				// short or long, required
	tagImageWidth = 0x100,				// short or long, required
	tagPhotometricInterpretation = 0x106,// short, required
		photoWhiteIsZero = 0,
		photoBlackIsZero = 1,
		photoRGB = 2,
		photoPalette = 3,
	tagOrientation = 0x112,				// orientation, must be 1
	tagPlanarConfiguration = 0x11c,		// planar configuration, must be 1
	tagPredictor = 0x13d,				// predictor, must be 1
	tagRowsPerStrip = 0x116,			// short or long, default = 16777215
	tagSamplesPerPixel = 0x115,			// short, default = 1
	tagStripOffsets = 0x111				// [short or long], required
};

enum {
	typeByte = 1,
	typeASCII = 2,
	typeShort = 3,
	typeLong = 4,
	typeRational = 5
};

enum {
	errAllDone = 1
};

//=====================================================================================
// Global variables local to this module
//=====================================================================================

static TIFFPrivates gTIFFDefaults = {
	false,						// offsets are long
	0,							// no photometric interpretation default
	compNone,					// no compression
	1,							// 1 bit per sample
	1,							// 1 sample per pixel
	1,							// depth is 1 bit
	0, 0,						// height and width are 0
	0,							// 0 rows per strip (fixed at runtime for def.)
	0,							// a nil strip offsets list
	0,							// and a nil color table offset
	nil,						// nil handle for the image color table
	false,						// don't invert
	false,						// no prediction
	nil							// no default decoding function
};

static long gTotalLines, gCurrentLines;
static uchar *gStack, *gTree;

//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================

static OSErr ProcessTag(uchar *dataStart, ulong offset, ImageHandle theImage);
static CTabHandle MakeBlackAndWhiteCTable(ulong pi, short depth);
static CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth);
static OSErr SetUpCompression(TIFFPrivatesHandle privates, ImageHandle theImage);
static OSErr DecompressTIFFImage(uchar *dataStart, TIFFPrivatesHandle privates,
			PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length);
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow);
static OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
			TIFFPrivatesHandle privates);
static OSErr ExtractCCITT3(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
static OSErr ExtractPackBits(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
static OSErr ExtractLZW(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
static OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
			TIFFPrivatesHandle privates);
static OSErr ExtractPackBitsRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
static OSErr ExtractLZWRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates);
static ulong GetOneUnsigned(uchar *data, long offset);
static ulong GetByte(uchar *data, long offset);
static ulong GetShort(uchar *data, long offset);
static ulong GetLong(uchar *data, long offset);
static Fixed GetRational(uchar *data, long offset);

//=====================================================================================
// Boolean idTIFF(uchar *theData, long theSize)
//=====================================================================================
// Examines the given data and attempts to identify it as a TIFF image.
//=====================================================================================

extern Boolean idTIFF(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
{
#if applec
#pragma unused(refNum, theSpec)
#endif
	return (theSize > 4 && 
			(theData[0] == 'I' && theData[1] == 'I' && theData[2] == 42 && theData[3] == 0) ||
			(theData[0] == 'M' && theData[1] == 'M' && theData[2] == 0 && theData[3] == 42));
}

//=====================================================================================
// OSErr OpenTIFF(ImageHandle theImage)
//=====================================================================================
// Initializes the image record for a TIFF image; note that since we are a simple
// baseline TIFF reader, we only read the first set of tags.
//=====================================================================================

extern OSErr OpenTIFF(ImageHandle theImage)
{
	long theLen = GetHandleSize((*theImage)->data);
	char hState = HGetState((*theImage)->data);
	TIFFPrivatesHandle privates;
	OSErr theErr = noErr;
	ulong offset, count;
	uchar *dataStart;

	theErr = PtrToHand(&gTIFFDefaults, (Handle *)&privates, sizeof(TIFFPrivates));
	if (theErr != noErr) return theErr;
	(*theImage)->privateData = (Handle)privates;
	HLock((*theImage)->data);
	dataStart = (uchar *)*(*theImage)->data;
	offset = GetLong(dataStart, 4);
	count = GetShort(dataStart, offset), offset += 2;
	while (count-- && theErr == noErr) {
		theErr = ProcessTag(dataStart, offset, theImage);
		offset += 12;
		if (offset > theLen) theErr = codecBadDataErr;
	}
	if (theErr == noErr) {
		if ((*privates)->photoInt == photoWhiteIsZero || (*privates)->photoInt == photoBlackIsZero) {
			(*privates)->depth = (*theImage)->depth = (*privates)->bitsPerSample;
			if ((*theImage)->depth != 1 && (*theImage)->depth != 4 && (*theImage)->depth != 8)
				gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
			else if ((*theImage)->depth == 1)
				(*privates)->invert = ((*privates)->photoInt == photoBlackIsZero);
			else {
				(*privates)->decompColors = MakeBlackAndWhiteCTable((*privates)->photoInt, 
							(*theImage)->depth);
				if (!(*privates)->decompColors) theErr = memFullErr;
			}
		} else if ((*privates)->photoInt == photoRGB) {
			if ((*privates)->bitsPerSample != 8)
				gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
			(*privates)->depth = (*theImage)->depth = 32;
		} else if ((*privates)->photoInt == photoPalette) {
			(*privates)->depth = (*theImage)->depth = (*privates)->bitsPerSample;
			if ((*theImage)->depth != 4 && (*theImage)->depth != 8)
				gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
			if (!(*privates)->colorTableOffset) theErr = codecBadDataErr;
			(*privates)->decompColors = ExtractColorTable(dataStart, 
						(*privates)->colorTableOffset, (*theImage)->depth);
			if (!(*privates)->decompColors) theErr = memFullErr;
		} else gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
		if (!(*theImage)->depth) theErr = codecBadDataErr;
		if (!(*privates)->stripOffsetsList)
			gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
		if ((*privates)->compression == compModCCITT3 && (*theImage)->depth != 1)
			theErr = codecBadDataErr;
		if (!Width(&(*theImage)->grect) || !Height(&(*theImage)->grect))
			theErr = codecBadDataErr;
		if (theErr == noErr) theErr = SetUpCompression(privates, theImage);
		if (!(*privates)->rowsPerStrip || (*privates)->rowsPerStrip > (*privates)->height)
			(*privates)->rowsPerStrip = (*privates)->height;
		if ((*privates)->decompColors)
			(*theImage)->ipalette = NewPalette(256, (*privates)->decompColors, pmTolerant, 0);
		(*theImage)->qrect = (*theImage)->crect = (*theImage)->grect;
	}
	HSetState((*theImage)->data, hState);
	return theErr;
}

//=====================================================================================
// OSErr DrawTIFF(Handle theHandle, JVDrawParamsHandle theParams)
//=====================================================================================
// Draws a TIFF image into the specified GWorld, from the srcRect into the dstRect,
// clipping as dstRgn specifies.
//=====================================================================================

extern OSErr DrawTIFF(Handle theHandle, JVDrawParamsHandle theParams)
{
	TIFFPrivatesHandle privates = (TIFFPrivatesHandle)(*theParams)->privateData;
	NestedProgress theProgress = (*theParams)->progress;
	char hState = HGetState(theHandle);
	Handle lzwDataHandle = nil;
	OSErr theErr = noErr;
	GWorldPtr theGWorld;
	uchar *dataStart;
	
	if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc, 
				codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
	HLockHi(theHandle);
	if ((*privates)->compression == compLZW) {
		lzwDataHandle = NewHandleClear(4096 + 4096 * 4);
		if (!lzwDataHandle) lzwDataHandle = TempNewHandle(4096 + 4096 * 4, &theErr);
		if (lzwDataHandle && theErr == noErr) {
			HLockHi(lzwDataHandle);
			gStack = (uchar *)StripAddress(*lzwDataHandle);
			gTree = gStack + 4096;
		}
	}
	if (theErr == noErr) {
		dataStart = (uchar *)StripAddress(*theHandle);
		theGWorld = NewTempGWorld((*privates)->width, (*privates)->height, (*privates)->depth,
					(*privates)->decompColors);
		if (theGWorld) {
			theErr = DecompressTIFFImage(dataStart, privates, GetGWorldPixMap(theGWorld), 
						(ICMProgressProcRecordPtr)&theProgress, GetHandleSize(theHandle));
			if ((*theParams)->progress.aborted) theErr = codecAbortErr;
			DisposeGWorld(theGWorld);
		} else gIntError = errNoDrawMemory, theErr = memFullErr;
	}
	if (lzwDataHandle) DisposeHandle(lzwDataHandle);
	HSetState(theHandle, hState);
	if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc, 
				codecProgressUpdatePercent, 0x00010000L, theProgress.prog.progressRefCon);
	return theErr;
}

//=====================================================================================
// OSErr CloneTIFF(ImageHandle origImage, ImageHandle newImage)
//=====================================================================================
// Called when an image is cloned, to give us a chance to duplicate any secondary
// storage.
//=====================================================================================

extern OSErr CloneTIFF(ImageHandle origImage, ImageHandle newImage)
{
	TIFFPrivatesHandle origPrivates = (TIFFPrivatesHandle)(*origImage)->privateData;
	TIFFPrivatesHandle newPrivates = (TIFFPrivatesHandle)(*newImage)->privateData;
	CTabHandle theColors;
	OSErr theErr;
	
	if (origPrivates && newPrivates) {
		if (theColors = (*origPrivates)->decompColors) {
			theErr = HandToHand((Handle *)&theColors);
			(*newPrivates)->decompColors = theColors;
		}
	}
	return theErr;
}

//=====================================================================================
// OSErr CloseTIFF(ImageHandle theImage)
//=====================================================================================
// Called before the image is disposed, to get rid of any secondary storage we
// allocated on open.
//=====================================================================================

extern OSErr CloseTIFF(ImageHandle theImage)
{
	TIFFPrivatesHandle privates = (TIFFPrivatesHandle)(*theImage)->privateData;
	
	if (privates)
		if ((*privates)->decompColors) DisposeHandle((Handle)(*privates)->decompColors);
	return noErr;
}

//=====================================================================================
// OSErr ProcessTag(uchar *dataStart, ulong offset, ImageHandle theImage)
//=====================================================================================
// Process the TIFF tag pointed to by offset.  If it's something we recognize, we
// attempt to fill in the appropriate data structures in the image record, or in the
// attached TIFF private data.
//=====================================================================================

static OSErr ProcessTag(uchar *dataStart, ulong offset, ImageHandle theImage)
{
	TIFFPrivatesHandle privates = (TIFFPrivatesHandle)(*theImage)->privateData;
	ushort tag = GetShort(dataStart, offset);
	OSErr theErr = noErr;
	ulong data;
	
	switch (tag) {
		case tagImageWidth:
			(*privates)->width = (*theImage)->grect.right = 
						GetOneUnsigned(dataStart, offset + 2);
			break;

		case tagImageLength:
			(*privates)->height = (*theImage)->grect.bottom = 
						GetOneUnsigned(dataStart, offset + 2);
			break;

		case tagBitsPerSample:
			(*privates)->bitsPerSample = GetOneUnsigned(dataStart, offset + 2);
			break;

		case tagCompression:
			(*privates)->compression = GetOneUnsigned(dataStart, offset + 2);
			break;

		case tagPhotometricInterpretation:
			(*privates)->photoInt = GetOneUnsigned(dataStart, offset + 2);
			break;

		case tagStripOffsets:
			data = GetShort(dataStart, offset + 2);
			if (data == typeShort) (*privates)->offsetsAreShort = true;
			else if (data == typeLong) (*privates)->offsetsAreShort = false;
			else return codecBadDataErr;
			data = GetLong(dataStart, offset + 4);
			if (data == 1 || (data == 2 && (*privates)->offsetsAreShort))
				(*privates)->stripOffsetsList = offset + 8;
			else (*privates)->stripOffsetsList = GetLong(dataStart, offset + 8);
			break;

		case tagSamplesPerPixel:
			(*privates)->samplesPerPixel = GetOneUnsigned(dataStart, offset + 2);
			break;

		case tagRowsPerStrip:
			(*privates)->rowsPerStrip = GetOneUnsigned(dataStart, offset + 2);
			break;

		case tagImageDescription:
			if (GetShort(dataStart, offset + 2) != typeASCII) return codecBadDataErr;
			data = GetLong(dataStart, offset + 4);
			if (data <= 4) offset += 8;
			else offset = GetLong(dataStart, offset + 8);
			if (!(*theImage)->comments) 
				theErr = PtrToHand(dataStart + offset, &(*theImage)->comments, data - 1);
			break;

		case tagColorMap:
			if (GetShort(dataStart, offset + 2) != typeShort) return codecBadDataErr;
			(*privates)->colorTableOffset = offset + 4;
			break;
		
		case tagPredictor:
			data = GetOneUnsigned(dataStart, offset + 2);
			if (data == 1) (*privates)->prediction = false;
			else if (data == 2) (*privates)->prediction = true;
			else gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
			break;

		case tagFillOrder:
		case tagOrientation:
		case tagPlanarConfiguration:
			if (GetOneUnsigned(dataStart, offset + 2) != 1)
				gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
	}
	return theErr;
}

//=====================================================================================
// CTabHandle MakeBlackAndWhiteCTable(ulong pi, short depth)
//=====================================================================================
// Creates a black & white/grayscale color table, according to the given
// photometric interpretation.  If pi says that white is zero, we go from brightest to
// darkest in even steps for the entire color table size; otherwise, we go from
// darkest to brightest.
//=====================================================================================

static CTabHandle MakeBlackAndWhiteCTable(ulong pi, short depth)
{
	ushort color, count = (1L << depth), i;
	CTabHandle theColors;
	
	theColors = (CTabHandle)NewHandleClear(sizeof(ColorTable) + (count - 1) * sizeof(ColorSpec));
	if (theColors) {
		(*theColors)->ctSeed = GetCTSeed();
		(*theColors)->ctFlags = 0x8000;
		(*theColors)->ctSize = count - 1;
		for (i = 0; i < count; i++) {
			color = (ulong)i * 65535L / (long)(count - 1);
			if (pi == photoWhiteIsZero) color = 65535L - color;
			(*theColors)->ctTable[i].rgb.red = (*theColors)->ctTable[i].rgb.green =
					(*theColors)->ctTable[i].rgb.blue = color;
		}
	}
	return theColors;
}

//=====================================================================================
// CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth)
//=====================================================================================
// Extracts the included TIFF color table from the data at the given offset.
//=====================================================================================

static CTabHandle ExtractColorTable(uchar *dataStart, ulong offset, short depth)
{
	ulong count = GetLong(dataStart, offset) / 3, size = (1L << depth), i;
	CTabHandle theColors;

	if ((depth != 4 && depth != 8) || count > size) return nil;
	theColors = (CTabHandle)NewHandleClear(sizeof(ColorTable) + (size - 1) * sizeof(ColorSpec));
	offset = GetLong(dataStart, offset + 4);
	if (theColors) {
		(*theColors)->ctSeed = GetCTSeed();
		(*theColors)->ctFlags = 0x8000;
		(*theColors)->ctSize = size;
		for (i = 0; i < count; i++)
			(*theColors)->ctTable[i].rgb.red = GetShort(dataStart, offset), offset += 2;
		for (i = 0; i < count; i++)
			(*theColors)->ctTable[i].rgb.green = GetShort(dataStart, offset), offset += 2;
		for (i = 0; i < count; i++)
			(*theColors)->ctTable[i].rgb.blue = GetShort(dataStart, offset), offset += 2;
	}
	return theColors;
}

//=====================================================================================
// OSErr SetUpCompression(TIFFPrivatesHandle privates, ImageHandle theImage)
//=====================================================================================
// Sets up the compression parameters for the TIFF image.
//=====================================================================================

static OSErr SetUpCompression(TIFFPrivatesHandle privates, ImageHandle theImage)
{
	OSErr theErr = noErr;
	
	switch ((*privates)->compression) {
		case compNone:
			(*theImage)->compression = 0;
			BlockMove(gString[strUncompressed], (*theImage)->compressionDesc, *gString[strUncompressed] + 1);
			(*privates)->extract = ((*privates)->samplesPerPixel == 1) ?
				(ExtractProcPtr)ExtractUncompressed : (ExtractProcPtr)ExtractUncompressedRGB;
			break;
			
//		case compCCITT3:
		case compModCCITT3:
			(*theImage)->compression = kCCITTCompression;
			BlockMove(gString[strCCITT3], (*theImage)->compressionDesc, *gString[strCCITT3] + 1);
			(*privates)->extract = (ExtractProcPtr)ExtractCCITT3;
			break;
			
		case compPackBits:
			(*theImage)->compression = kPackBitsCompression;
			BlockMove(gString[strPackBits], (*theImage)->compressionDesc, *gString[strPackBits] + 1);
			(*privates)->extract = ((*privates)->samplesPerPixel == 1) ?
				(ExtractProcPtr)ExtractPackBits : (ExtractProcPtr)ExtractPackBitsRGB;
			break;
			
		case compLZW:
			(*theImage)->compression = kLZWCompression;
			if ((*privates)->prediction)
				BlockMove(gString[strLZWPredict], (*theImage)->compressionDesc, *gString[strLZWPredict] + 1);
			else BlockMove(gString[strLZW], (*theImage)->compressionDesc, *gString[strLZW] + 1);
			(*privates)->extract = ((*privates)->samplesPerPixel == 1) ?
				(ExtractProcPtr)ExtractLZW : (ExtractProcPtr)ExtractLZWRGB;
			break;
			
		default:
			gIntError = errBadTIFFExtension, theErr = codecBadDataErr;
			break;
	}
	return theErr;
}

//=====================================================================================
// OSErr DecompressTIFFImage(uchar *dataStart, TIFFPrivatesHandle privates,
//			PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
//=====================================================================================
// Oversees the decompression of a TIFF image, handling strip decoding and banding, and
// calling the appropriate function to perform the actual decompression.
//=====================================================================================

static OSErr DecompressTIFFImage(uchar *dataStart, TIFFPrivatesHandle privates,
			PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
{
	ulong numStrips = (((*privates)->height + (*privates)->rowsPerStrip - 1) / 
				(*privates)->rowsPerStrip);
	ulong row, strip, offset, offsetOffset = (*privates)->stripOffsetsList;
	uchar *ptr, *rowStart, *dataEnd = dataStart + length;
	char pState = GetPixelsState(dstPixMap);
	RgnHandle dstRgn = qd.thePort->visRgn;
	ulong totalRows = 0, bandRow = 0;
	char mmuMode = true32b;
	OSErr theErr = noErr;
	
	LockPixels(dstPixMap);
	rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
	for (strip = 0; strip < numStrips && totalRows < (*privates)->height; strip++) {
		if ((*privates)->offsetsAreShort)
			offset = GetShort(dataStart, offsetOffset), offsetOffset += 2;
		else offset = GetLong(dataStart, offsetOffset), offsetOffset += 4;
		ptr = dataStart + offset;
		for (row = 0; row < (*privates)->rowsPerStrip && totalRows < (*privates)->height; row++) {
			if (ptr < dataStart || ptr > dataEnd) {
				theErr = codecBadDataErr;
				break;
			}
			SwapMMUMode(&mmuMode);
			if (row == 0) (*privates)->extract(nil, nil, privates);
			theErr = (*privates)->extract(&ptr, rowStart, privates);
			SwapMMUMode(&mmuMode);
			rowStart += ((*dstPixMap)->rowBytes & 0x3fff);
			if (theErr == noErr && !(++totalRows & 63) && prog->progressProc)
				theErr = (OSErr)CallICMProgressProc(prog->progressProc, 
							codecProgressUpdatePercent,
							FixRatio(totalRows, (*privates)->height), prog->progressRefCon);
			if (++bandRow == Height(&(*dstPixMap)->bounds)) {
				CopyPixMapToDestination(dstPixMap, totalRows - bandRow);
				bandRow = 0;
				rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
			}
			if (theErr != noErr) break;
		}
		if (theErr != noErr) break;
	}
	if (theErr == errAllDone) theErr = noErr;
	if (theErr == noErr && bandRow) 
		CopyPixMapToDestination(dstPixMap, totalRows - bandRow);
	SetPixelsState(dstPixMap, pState);
	return theErr;
}

//=====================================================================================
// void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
//=====================================================================================
// Copies the band contained in srcPixMap to the destination, offset from the top by
// startRow.
//=====================================================================================

static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
{
	Rect srcRect = (*srcPixMap)->bounds, dstRect = (*srcPixMap)->bounds;
	PixMapHandle dstPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
	char srcState = HGetState((Handle)srcPixMap);
	char dstState = HGetState((Handle)dstPixMap);
	
	OffsetRect(&dstRect, 0, startRow);
	HLock((Handle)srcPixMap);
	HLock((Handle)dstPixMap);
	CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, &srcRect, &dstRect,
				srcCopy + ditherCopy, qd.thePort->visRgn);
	HSetState((Handle)dstPixMap, dstState);
	HSetState((Handle)srcPixMap, srcState);
}

//=====================================================================================
// OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
//			TIFFPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of uncompressed 1, 4, or 8 bit data.
//=====================================================================================

static OSErr ExtractUncompressed(uchar **srcData, uchar *dstData,
			TIFFPrivatesHandle privates)
{
	ulong bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
	uchar *src = *srcData;

	if (srcData == nil && dstData == nil) return noErr;
	if ((*privates)->invert) while (bytes--) *dstData++ = ~(*src++);
	else while (bytes--) *dstData++ = *src++;
	*srcData = src;
	return noErr;
}

//=====================================================================================
// OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
//			TIFFPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of uncompressed RGB data, skipping the alpha channel in the
// destination and ignoring anything other than the first 3 samples for each pixel.
//=====================================================================================

static OSErr ExtractUncompressedRGB(uchar **srcData, uchar *dstData,
			TIFFPrivatesHandle privates)
{
	ushort extra = (*privates)->samplesPerPixel - 3;
	ulong col = (*privates)->width;
	uchar *src = *srcData;

	if (srcData == nil && dstData == nil) return noErr;
	while (col--) {
		dstData++, *dstData++ = *src++, *dstData++ = *src++, *dstData++ = *src++;
		src += extra;
	}
	*srcData = src;
	return noErr;
}

//=====================================================================================
// OSErr ExtractCCITT3(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of modified Huffman CCITT-compressed 1-bit data.
//=====================================================================================

static OSErr ExtractCCITT3(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
{
	static ushort gBitBuffer, gBitsInBuffer;
	Boolean white = true, nextWhite = true, invert = ((*privates)->invert) ? true : false;
	Boolean wasTerminating = true;
	uchar *src = *srcData, dstMask = 0xff;
	long width = (*privates)->width;
	short count, dstBits = 8;
	ExtractTable *code;

	if (srcData == nil && dstData == nil) {
		gBitBuffer = gBitsInBuffer = 0;
		return noErr;
	}
	while (width || !wasTerminating) {
		if (gBitsInBuffer < 8) {
			gBitBuffer |= (ushort)*src++ << (8 - gBitsInBuffer);
			gBitsInBuffer += 8;
		}
		code = (white) ? &whiteExtract[gBitBuffer >> 8] : &blackExtract[gBitBuffer >> 8];
		if (code->leftover < 0) {
			gBitBuffer <<= 8;
			gBitsInBuffer -= 8;
			if (gBitsInBuffer < 8) {
				gBitBuffer |= (ushort)*src++ << (8 - gBitsInBuffer);
				gBitsInBuffer += 8;
			}
			count = -code->leftover;
			code = (white) ? &whiteExtract[code->count + (gBitBuffer >> (16 - count))] :
						&blackExtract[code->count + (gBitBuffer >> (16 - count))];
			gBitBuffer <<= (count - code->leftover);
			gBitsInBuffer -= (count - code->leftover);
		} else {
			gBitBuffer <<= (8 - code->leftover);
			gBitsInBuffer -= (8 - code->leftover);
		}
		count = code->count;
		if (count == -1) {
			return codecBadDataErr;
		}
		else if (count == -2) continue;
		if ((width -= count) < 0) {
			return codecBadDataErr;
		}
		if (wasTerminating = count < 64) nextWhite = !white;
		if (count >= dstBits) {
			if (white ^ invert) *dstData++ &= ~dstMask;
			else *dstData++ |= dstMask;
			count -= dstBits;
			while (count >= 8) {
				*dstData++ = (white ^ invert) ? 0 : 0xff;
				count -= 8;
			}
			dstBits = 8;
			dstMask = 0xff;
		}
		if (count) {
			if (white ^ invert) *dstData &= ~(dstMask ^ (dstMask >> count));
			else *dstData |= (dstMask ^ (dstMask >> count));
			dstMask >>= count;
			dstBits -= count;
		}
		white = nextWhite;
	}
	if ((*privates)->compression == compModCCITT3) {
		while (gBitsInBuffer >= 8) src--, gBitsInBuffer -= 8;
		gBitsInBuffer = gBitBuffer = 0;
	}
	*srcData = src;
	return noErr;
}

//=====================================================================================
// OSErr ExtractPackBits(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of PackBits-compressed 1, 4, or 8 bit data.
//=====================================================================================

static OSErr ExtractPackBits(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
{
	long bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
	Boolean invert = (*privates)->invert;
	uchar *src = *srcData, value;
	signed short count;
	
	if (srcData == nil && dstData == nil) return noErr;
	while (bytes) {
		count = (signed char)*src++;
		if (count < 0) {
			value = (invert) ? ~(*src++) : *src++;
			count = -count + 1;
			if ((bytes -= count) < 0) return codecBadDataErr;
			while (count--) *dstData++ = value;
		} else {
			count++;
			if ((bytes -= count) < 0) return codecBadDataErr;
			if (invert) while (count--) *dstData++ = ~(*src++);
			else while (count--) *dstData++ = *src++;
		}
	}
	*srcData = src;
	return noErr;
}

//=====================================================================================
// OSErr ExtractPackBitsRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of PackBits-compressed RGB data, skipping the alpha channel in the
// destination and ignoring anything other than the first 3 samples for each pixel.
//=====================================================================================

static OSErr ExtractPackBitsRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
{
	long bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
	ushort extra = (*privates)->samplesPerPixel - 3, sample = 0;
	Boolean invert = (*privates)->invert;
	uchar *src = *srcData, value;
	signed short count;
	
	if (srcData == nil && dstData == nil) return noErr;
	bytes *= (*privates)->samplesPerPixel;
	while (bytes) {
		count = (signed char)*src++;
		if (count < 0) {
			value = *src++;
			count = -count + 1;
			if ((bytes -= count) < 0) return codecBadDataErr;
			while (count--) {
				if (!sample) dstData++;
				if (sample < 3) *dstData++ = value;
				if (++sample == (*privates)->samplesPerPixel) sample = 0;
			}
		} else {
			count++;
			if ((bytes -= count) < 0) return codecBadDataErr;
			while (count--) {
				if (!sample) dstData++;
				if (sample < 3) *dstData++ = *src++;
				if (++sample == (*privates)->samplesPerPixel) sample = 0;
			}
		}
	}
	*srcData = src;
	return noErr;
}

//=====================================================================================
// OSErr ExtractLZW(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of LZW-compressed 1, 4, or 8 bit data.
//=====================================================================================

static OSErr ExtractLZW(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
{
#define gClearCode 256
#define gEndCode 257
	static ushort gTreeSize, gCodeSize, gFirstCode, gLastCode;
	static ulong gBitBuffer, gBitsInBuffer;
	static uchar *gStackPtr;
	ushort *parent = (ushort *)gTree;
	uchar *code = (uchar *)(parent + 4096);
	short i, j, currentCode, oldCode;
	ulong bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
	uchar *src;
	
	if (srcData == nil && dstData == nil) {
		gStackPtr = gStack;
		gCodeSize = 9;
		gBitsInBuffer = gBitBuffer = 0;
		gTreeSize = gEndCode + 1;
		return noErr;
	}
	src = *srcData;
	for (i = 0; i < bytes; i++) {
		if (gStackPtr != gStack) {
			if ((*privates)->prediction && i)
				*dstData = dstData[-1] + *(--gStackPtr), dstData++;
			else *dstData++ = *(--gStackPtr);
			continue; 
		}
		while (gCodeSize > gBitsInBuffer) {
			gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer); 
			gBitsInBuffer += 8;
		}
		currentCode = gBitBuffer >> (32 - gCodeSize);
		gBitBuffer <<= gCodeSize;
		gBitsInBuffer -= gCodeSize;
		if (currentCode == gEndCode) return errAllDone;
		else if (currentCode == gClearCode) {
			gCodeSize = 9; 
			gTreeSize = gEndCode + 1; 
			do {
				while (gCodeSize > gBitsInBuffer) {
					gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer); 
					gBitsInBuffer += 8;
				}
				currentCode = gBitBuffer >> (32 - gCodeSize);
				gBitBuffer <<= gCodeSize;
				gBitsInBuffer -= gCodeSize;
			} while (currentCode == gClearCode);
			if ((*privates)->prediction && i)
				*dstData = dstData[-1] + currentCode, dstData++;
			else *dstData++ = currentCode;
			gFirstCode = gLastCode = currentCode;
			continue;
		}
		oldCode = currentCode; 
		if (currentCode > gTreeSize) return codecBadDataErr;
		else if (currentCode == gTreeSize) { 
			*gStackPtr++ = gFirstCode; 
			currentCode = gLastCode; 
		}
		for (j = currentCode; j >= gClearCode; j = parent[j]) *gStackPtr++ = code[j];
		if ((*privates)->prediction && i)
			*dstData = dstData[-1] + j, dstData++;
		else *dstData++ = j;
		gFirstCode = j;
		if (gTreeSize != 4096) {	
			code[gTreeSize] = gFirstCode; 
			parent[gTreeSize] = gLastCode;
			if ((++gTreeSize >= ((1 << gCodeSize) - 1)) && (gCodeSize != 12)) gCodeSize++; 
		}
		gLastCode = oldCode;
	}
	*srcData = src;
	return noErr;
}

//=====================================================================================
// OSErr ExtractLZWRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
//=====================================================================================
// Pulls out a row of LZW-compressed RGB data, skipping the alpha channel in the
// destination and ignoring anything other than the first 3 samples for each pixel.
//=====================================================================================

static OSErr ExtractLZWRGB(uchar **srcData, uchar *dstData, TIFFPrivatesHandle privates)
{
#define gClearCode 256
#define gEndCode 257
	static ushort gTreeSize, gCodeSize, gFirstCode, gLastCode;
	static ulong gBitBuffer, gBitsInBuffer;
	static uchar *gStackPtr;
	ulong bytes = ((*privates)->width * (*privates)->bitsPerSample + 7) / 8L;
	ushort extra = 0, storeCount = 0, extraCount = 0, total;
	ushort *parent = (ushort *)gTree;
	uchar *code = (uchar *)(parent + 4096);
	short i, j, currentCode, oldCode;
	uchar *src;
	
	if (srcData == nil && dstData == nil) {
		gStackPtr = gStack;
		gCodeSize = 9;
		gBitsInBuffer = gBitBuffer = 0;
		gTreeSize = gEndCode + 1;
		return noErr;
	}
	bytes *= total = (*privates)->samplesPerPixel;
	extraCount = extra = total - 3;
	storeCount = 3;
	src = *srcData;
	for (i = 0; i < bytes; i++) {
		if (storeCount == 3) dstData++;
		if (gStackPtr != gStack) {
			if (storeCount) {
				if ((*privates)->prediction && i >= total)
					*dstData = dstData[-4] + *(--gStackPtr), dstData++;
				else *dstData++ = *(--gStackPtr);
				if (!--storeCount && !extraCount) storeCount = 3;
			} else {
				--gStackPtr;
				if (!--extraCount) storeCount = 3, extraCount = extra;
			}
			continue; 
		}
		while (gCodeSize > gBitsInBuffer) {
			gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer); 
			gBitsInBuffer += 8;
		}
		currentCode = gBitBuffer >> (32 - gCodeSize);
		gBitBuffer <<= gCodeSize;
		gBitsInBuffer -= gCodeSize;
		if (currentCode == gEndCode) return errAllDone;
		else if (currentCode == gClearCode) {
			gCodeSize = 9; 
			gTreeSize = gEndCode + 1; 
			do {
				while (gCodeSize > gBitsInBuffer) {
					gBitBuffer |= (ulong)*src++ << (24 - gBitsInBuffer); 
					gBitsInBuffer += 8;
				}
				currentCode = gBitBuffer >> (32 - gCodeSize);
				gBitBuffer <<= gCodeSize;
				gBitsInBuffer -= gCodeSize;
			} while (currentCode == gClearCode);
			if (storeCount) {
				if ((*privates)->prediction && i >= total)
					*dstData = dstData[-4] + currentCode, dstData++;
				else *dstData++ = currentCode;
				if (!--storeCount && !extraCount) storeCount = 3;
			} else if (!--extraCount) storeCount = 3, extraCount = extra;
			gFirstCode = gLastCode = currentCode; 
			continue;
		}
		oldCode = currentCode; 
		if (currentCode > gTreeSize) return codecBadDataErr;
		else if (currentCode == gTreeSize) { 
			*gStackPtr++ = gFirstCode; 
			currentCode = gLastCode; 
		}
		for (j = currentCode; j >= gClearCode; j = parent[j]) *gStackPtr++ = code[j];
		if (storeCount) {
			if ((*privates)->prediction && i >= total)
				*dstData = dstData[-4] + j, dstData++;
			else *dstData++ = j;
			if (!--storeCount && !extraCount) storeCount = 3;
		} else if (!--extraCount) storeCount = 3, extraCount = extra;
		gFirstCode = j;
		if (gTreeSize != 4096) {	
			code[gTreeSize] = gFirstCode; 
			parent[gTreeSize] = gLastCode;
			if ((++gTreeSize >= ((1 << gCodeSize) - 1)) && (gCodeSize != 12)) gCodeSize++; 
		}
		gLastCode = oldCode;
	}
	*srcData = src;
	return noErr;
}

//=====================================================================================
// ulong GetOneUnsigned(uchar *data, long offset)
//=====================================================================================
// Extracts an unsigned value from the data at the given offset.
//=====================================================================================

static ulong GetOneUnsigned(uchar *data, long offset)
{
	ushort type = GetShort(data, offset);
	ulong count = GetLong(data, offset + 2);

	if (type == typeByte) {
		if (count <= 4) return GetByte(data, offset + 6);
		else return GetByte(data, GetLong(data, offset + 6));
	} else if (type == typeShort) {
		if (count <= 2) return GetShort(data, offset + 6);
		else return GetShort(data, GetLong(data, offset + 6));
	} else if (type == typeLong) {
		if (count == 1) return GetLong(data, offset + 6);
		else return GetLong(data, GetLong(data, offset + 6));
	}
	return 0;
}

//=====================================================================================
// ulong GetByte(uchar *data, long offset)
//=====================================================================================
// Extracts an unsigned byte from the data at the given offset.
//=====================================================================================

static ulong GetByte(uchar *data, long offset)
{
	return data[offset];
}

//=====================================================================================
// ulong GetShort(uchar *data, long offset)
//=====================================================================================
// Extracts an unsigned byte from the data at the given offset.
//=====================================================================================

static ulong GetShort(uchar *data, long offset)
{
	if (*data == 'I') return (ulong)data[offset] + ((ulong)data[offset + 1] << 8);
	else return ((ulong)data[offset] << 8) + (ulong)data[offset + 1];
}

//=====================================================================================
// ulong GetLong(uchar *data, long offset)
//=====================================================================================
// Extracts an unsigned byte from the data at the given offset.
//=====================================================================================

static ulong GetLong(uchar *data, long offset)
{
	if (*data == 'I') return (ulong)data[offset] + ((ulong)data[offset + 1] << 8) + 
				((ulong)data[offset + 2] << 16) + ((ulong)data[offset + 3] << 24);
	else return ((ulong)data[offset] << 24) + ((ulong)data[offset + 1] << 16) + 
				((ulong)data[offset + 2] << 8) + (ulong)data[offset + 3];
}

//=====================================================================================
// Fixed GetRational(uchar *data, long offset)
//=====================================================================================
// Extracts a rational value from the data at the given offset, return it as a fixed-
// point value.
//=====================================================================================

static Fixed GetRational(uchar *data, long offset)
{
	ulong num = GetLong(data, offset);
	ulong den = GetLong(data, offset + 4);
	return FixRatio(num, den);
}
