/*********************************************************/
/* 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 "GIF.h"

//=====================================================================================
// GIFData: structure holding information needed to decompress a GIF image; passed
// between the C and assembly language portions of this module.
//=====================================================================================

typedef struct GIFData {
	uchar *src;				/*   0 */
	uchar *dest;			/*   4 */
	uchar *stack;			/*   8 */
	uchar *end;				/*  12 */
	ulong *tree;			/*  16 */
#if USE_ASM
	ulong store[12];		/*  20 */	// this is specific to the 68020 implementation
#endif
	ushort charSize;		/*  68 */
	short width;			/*  70 */
	Boolean theReturn;		/*  72 */
} GIFData, *GIFDataPtr;

//=====================================================================================
// GIFPrivates: private data structure for GIF images, maintained for each image.
//=====================================================================================

typedef struct GIFPrivates
{
	long totalLines;
} GIFPrivates, *GIFPrivatesPtr, **GIFPrivatesHandle;

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

static long gTotalLines, gCurrentLines;
static GIFData gGIFData;
static JVDrawParamsHandle gGIFParams;

// globals specific to the C implementation of GIF decoding
#if !USE_ASM
static ushort gTreeSize, gCodeSize, gBytesLeft, gClearCode, gEndCode;
static ushort gFirstCode, gLastCode;
static ulong gBitBuffer, gBitsInBuffer;
static uchar *gStack, *gInput;
#endif

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

static Size FindNextGIFImage(Handle theData, Size startOffset);
static OSErr ExtractGIFComments(Handle theData, Handle *theComments);
static GWorldPtr MakeGIFGWorld(Handle theHandle, uchar *theData);
static PaletteHandle MakeGIFPalette(uchar *theData, short theSize);
static CTabHandle MakeGIFCTable(uchar *theData);
static OSErr DecompressGIFImage(uchar *theData, PixMapHandle srcPixMap, Rect *srcRect, 
			Rect *dstRect, RgnHandle dstRgn, ICMProgressProcRecordPtr progProc);
static Boolean DecodeInterlaced(uchar *theData, PixMapHandle srcPixMap, short iwidth, 
			short iheight, Rect *sRect, short *rowCount,
			ICMProgressProcRecordPtr progProc);
static Boolean DecodeNonInterlaced(uchar *theData, PixMapHandle srcPixMap, short iwidth, 
			short iheight, Rect *sRect, short *rowCount,
			ICMProgressProcRecordPtr progProc);
static Boolean DecodeRow(short width, uchar *src, uchar *dest);

#if USE_ASM && !THINK_C
extern void DoDecodeRow(GIFDataPtr theData);
extern void DoDecodeDummyRow(GIFDataPtr theData);
#else
static void DoDecodeRow(GIFDataPtr theData);
static void DoDecodeDummyRow(GIFDataPtr theData);
#endif

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

extern Boolean idGIF(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
{
#if applec
#pragma unused(refNum, theSpec)
#endif
	return (theSize > 6 &&
			theData[0] == 'G' &&
			theData[1] == 'I' &&
			theData[2] == 'F' &&
			theData[3] == '8' &&
			(theData[4] == '7' || theData[4] == '9') &&
			theData[5] == 'a');
}

//=====================================================================================
// OSErr OpenGIF(ImageHandle theImage)
//=====================================================================================
// Initializes the image record for a GIF image.
//=====================================================================================

extern OSErr OpenGIF(ImageHandle theImage)
{
	long theLen = GetHandleSize((*theImage)->data), totalLines;
	char hState = HGetState((*theImage)->data);
	Rect imageRect = { 0, 0, 0, 0 };
	Size imageOffset;
	Handle theHandle;
	uchar *theData;

	if (!((*theImage)->privateData = NewHandleClear(sizeof(GIFPrivates))))
		return memFullErr;
	HLock((*theImage)->data);
	theData = (uchar *)*(*theImage)->data;
	SetRect(&(*theImage)->grect, 0, 0, (theData[7] << 8) + theData[6], 
				(theData[9] << 8) + theData[8]);
	(*theImage)->compression = kGIFType;
	BlockMove(gString[strLZWGIF], (*theImage)->desc.name, gString[strLZWGIF][0] + 1);
	imageOffset = FindNextGIFImage((*theImage)->data, 0);
	if (imageOffset != -1) {
		theData = (uchar *)*(*theImage)->data + imageOffset;
		if (theData[9] & 0x80) {
			(*theImage)->depth = (theData[9] & 7) + 1;
			(*theImage)->ipalette = MakeGIFPalette(theData + 10, (*theImage)->depth);
		} else {
			(*theImage)->depth = (*((uchar *)*(*theImage)->data + 10) & 7) + 1;
			(*theImage)->ipalette = MakeGIFPalette((uchar *)*(*theImage)->data + 13, 
						(*theImage)->depth);
		}
		ExtractGIFComments((*theImage)->data, &theHandle);
		(*theImage)->comments = theHandle;
		imageRect.top = (theData[4] << 8) + theData[3];
		imageRect.left = (theData[2] << 8) + theData[1];
		imageRect.bottom = imageRect.top + (theData[8] << 8) + theData[7];
		imageRect.right = imageRect.left + (theData[6] << 8) + theData[5];
		totalLines = Height(&imageRect);
		(*theImage)->grect = imageRect;
		while ((imageOffset = FindNextGIFImage((*theImage)->data, imageOffset)) != -1) {
			theData = (uchar *)*(*theImage)->data + imageOffset;
			imageRect.top = (theData[4] << 8) + theData[3];
			imageRect.left = (theData[2] << 8) + theData[1];
			imageRect.bottom = imageRect.top + (theData[8] << 8) + theData[7];
			imageRect.right = imageRect.left + (theData[6] << 8) + theData[5];
			totalLines += Height(&imageRect);
			UnionRect(&imageRect, &(*theImage)->grect, &(*theImage)->grect);
		}
	} else return gIntError = errCorruptImage, codecBadDataErr;
	(*theImage)->qrect = (*theImage)->crect = (*theImage)->grect;
	HSetState((*theImage)->data, hState);
	(*(GIFPrivatesHandle)(*theImage)->privateData)->totalLines = totalLines;
	BlockMove(gString[strLZWGIF], (*theImage)->compressionDesc, *gString[strLZWGIF] + 1);
	return noErr;
}

//=====================================================================================
// OSErr DrawGIF(Handle theHandle, JVDrawParamsHandle theParams)
//=====================================================================================
// Draws a GIF image, whose data is in theHandle, to the destination described in the
// drawing parameters.
//=====================================================================================

extern OSErr DrawGIF(Handle theHandle, JVDrawParamsHandle theParams)
{
	NestedProgress theProgress = (*theParams)->progress;
	long dataLen = GetHandleSize(theHandle);
	Rect srcRect = (*theParams)->bounds;
	char hState = HGetState(theHandle);
	PixMapHandle gifPixMap;
	Handle gifDataHandle;
	OSErr theErr = noErr;
	Size imageOffset = 0;
	GWorldPtr gifGWorld;
	uchar *dataStart;

	gTotalLines = (*(GIFPrivatesHandle)(*theParams)->privateData)->totalLines;
	gCurrentLines = 0;
	gGIFParams = theParams;
	gifDataHandle = NewHandleClear(4096 + 4096 * 4);
	if (!gifDataHandle) gifDataHandle = TempNewHandle(4096 + 4096 * 4, &theErr);
	if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc, 
				codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
	if (gifDataHandle && theErr == noErr) {
		HLockHi(gifDataHandle);
		HLockHi(theHandle);
		gGIFData.stack = (uchar *)StripAddress(*gifDataHandle);
		gGIFData.tree = (ulong *)(gGIFData.stack + 4096);
		imageOffset = FindNextGIFImage(theHandle, 0);
		if (imageOffset != -1) {
			do {
				dataStart = (uchar *)StripAddress(*theHandle) + imageOffset;
				if (gifGWorld = MakeGIFGWorld(theHandle, dataStart)) {
					gifPixMap = GetGWorldPixMap(gifGWorld);
					gGIFData.end = (uchar *)StripAddress(*theHandle) + dataLen;
					theErr = DecompressGIFImage(dataStart, gifPixMap, &srcRect, &srcRect, 
								qd.thePort->visRgn, (ICMProgressProcRecordPtr)&theProgress);
					if ((*theParams)->progress.aborted) theErr = codecAbortErr;
					DisposeGWorld(gifGWorld);
				} else gIntError = errNoDrawMemory, theErr = memFullErr;
				if (theErr == noErr) imageOffset = FindNextGIFImage(theHandle, imageOffset);
			} while (theErr == noErr && imageOffset != -1);
		} else gIntError = errCorruptImage, theErr = codecBadDataErr;
		HSetState(theHandle, hState);
		DisposeHandle(gifDataHandle);
	} else gIntError = errNoDrawMemory, theErr = memFullErr;
	if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc, 
				codecProgressUpdatePercent, 0x00010000L, theProgress.prog.progressRefCon);
	return theErr;
}

//=====================================================================================
// Size FindNextGIFImage(Handle theData, Size startOffset)
//=====================================================================================
// Given a handle containing a GIF image and an offset into that handle's data, this
// routine locates the next GIF image.  The initial offset must either point to the
// beginning of a GIF stream, or to a previously-found GIF image.
//=====================================================================================

static Size FindNextGIFImage(Handle theData, Size startOffset) 
{
	uchar *theStart = (uchar *)StripAddress(*theData), *theAdr;
	uchar *theEnd = theStart + GetHandleSize(theData);
	Size theSize;

	theAdr = theStart + startOffset;
	if (*theAdr == 'G') {

		// Skip over global color table and global screen descriptors here
		if (theAdr[10] & 0x80) theAdr += 3 * (1 << ((theAdr[10] & 7) + 1));
		theAdr += 13;
		if (theAdr >= theEnd) return -1;
	} else if (*theAdr == 0x2c) {

		// Skip over previous image color table, descriptors, and data here
		if (theAdr[9] & 0x80) theAdr += 3 * (1 << ((theAdr[9] & 7) + 1));
		theAdr += 11;
		while (theSize = *theAdr++) {
			theAdr += theSize;
			if (theAdr > theEnd) return -1;
		}
	} else return -1;

	// Skip over any other GIF89 extensions here
	while (*theAdr == 0x21) {
		theAdr += 2;
		while (theSize = *theAdr++) {
			theAdr += theSize;
			if (theAdr >= theEnd) return -1;
		}
	}
	
	// If the next code is not an image descriptor, forget it
	if (*theAdr != 0x2c) return -1;

	// Otherwise, return the offset of the image
	return (Size)(theAdr - theStart);
}

//=====================================================================================
// OSErr ExtractGIFComments(Handle theData, Handle *theComments)
//=====================================================================================
// Extracts the raw comments contained in the given GIF data into a handle.
//=====================================================================================

static OSErr ExtractGIFComments(Handle theData, Handle *theComments)
{
	char hState = HGetState(theData);
	uchar *theAdr = (uchar *)StripAddress(*theData);
	uchar *theEnd = theAdr + GetHandleSize(theData);
	Boolean isComment, bad = false;
	long theSize, weird, i;
	OSErr theErr = noErr;
	
	*theComments = nil;

	// Skip over global color table and global screen descriptors here
	if (theAdr[10] & 0x80) theAdr += 3 * (1 << ((theAdr[10] & 7) + 1));
	theAdr += 13;
	
	// Loop over all data, including GIF89 extensions and image descriptors
	while (theAdr < theEnd && theErr == noErr) {
	
		// For extensions, skip it all if not a comment, or else accumulate the
		// data into the comments handle
		if (*theAdr == 0x21) {
			theAdr++;
			isComment = (*theAdr++ == 0xfe);
			while (theSize = *theAdr++) {
				if (isComment) {
					for (weird = i = 0; (i < theSize) && !bad; i++) {
						if (theAdr[i] >= 0x20 && theAdr[i] <= 0x7f) continue;
						else if (theAdr[i] == 0x0a) theAdr[i] = 0x0d;
						else if (theAdr[i] == 0x0d && theAdr[i + 1] == 0x0a) 
							theAdr[++i] = 0x00;
						else weird++;
					}
					if (!bad) bad = (weird > (theSize >> 4));
					if (bad) isComment = false;
					else {
						if (!*theComments) theErr = PtrToHand(theAdr, theComments, theSize);
						else theErr = PtrAndHand(theAdr, *theComments, theSize);
					}
				}
				theAdr += theSize;
				if (theAdr >= theEnd) break;
			}
		
		// For image descriptors, simply skip over all the extra data
		} else if (*theAdr == 0x2c) {
			if (theAdr[9] & 0x80) theAdr += 3 * (1 << ((theAdr[9] & 7) + 1));
			theAdr += 11;
			while (theSize = *theAdr++) {
				theAdr += theSize;
				if (theAdr >= theEnd) break;
			}
		
		// Otherwise, we've hit the end; get out of here
		} else break;
	}
	HSetState(theData, hState);
	return theErr;
}

//=====================================================================================
// PaletteHandle MakeGIFPalette(uchar *theData, short theSize)
//=====================================================================================
// Creates a palette for a GIF image, substituting white and black for the brightest
// and darkest colors.
//=====================================================================================

static PaletteHandle MakeGIFPalette(uchar *theData, short theSize)
{
	ushort dist, wdist = (10 * 256), bdist = (10 * 256);
	ushort whitest = 256, blackest = 256, i, cur;
	PaletteHandle theResult;
	RGBColor theColor;
	
	theSize = 1 << theSize;
	if (!(theResult = NewPalette(256, nil, pmTolerant, 0))) return nil;
	if (theSize == 256) {
		uchar *color = theData;
		for (i = 0; i < 256; i++) {
			dist = 3 * (ushort)color[0] + 6 * (ushort)color[1] + (ushort)color[2];
			if (dist < bdist) {
				bdist = dist;
				blackest = i;
			}
			dist = (10 << 8) - dist;
			if (dist < wdist) {
				wdist = dist;
				whitest = i;
			}
			color += 3;
		}
	}
	theColor.red = theColor.green = theColor.blue = 0xffff;
	SetEntryColor(theResult, 0, &theColor);
	theColor.red = theColor.green = theColor.blue = 0;
	SetEntryColor(theResult, 1, &theColor);
	for (i = 0, cur = 2; i < theSize; i++) {
		if (i != blackest && i != whitest) {
			theColor.red = (ushort)(*theData << 8) + *theData;
			theData++;
			theColor.green = (ushort)(*theData << 8) + *theData;
			theData++;
			theColor.blue = (ushort)(*theData << 8) + *theData;
			theData++;
			SetEntryColor(theResult, cur++, &theColor);
		}
	}
	for ( ; cur < 256; cur++) {
		GetEntryColor(gColorPalette[8], cur, &theColor);
		SetEntryColor(theResult, cur, &theColor);
	}
	return theResult;
}

//=====================================================================================
// GWorldPtr MakeGIFGWorld(Handle theHandle, uchar *theData)
//=====================================================================================
// Creates a suitably large offscreen GWorld for drawing bands of a GIF image into.
//=====================================================================================

static GWorldPtr MakeGIFGWorld(Handle theHandle, uchar *theData)
{
	ushort theHeight = (theData[8] << 8) + theData[7];
	ushort theWidth = (theData[6] << 8) + theData[5];
	GWorldPtr theGWorld = nil;
	CTabHandle theColors;
	
	if (theData[9] & 0x80) theData += 10;
	else theData = (uchar *)*theHandle + 13;
	if (theColors = MakeGIFCTable(theData)) {
		theGWorld = NewTempGWorld(theWidth, theHeight, 8, theColors);
		DisposeHandle((Handle)theColors);
	}
	return theGWorld;
}

//=====================================================================================
// CTabHandle MakeGIFCTable(uchar *theData)
//=====================================================================================
// Creates a color table handle for the given palette.
//=====================================================================================

static CTabHandle MakeGIFCTable(uchar *theData)
{
	CTabHandle theColors;
	OSErr theErr = noErr;
	ColorSpec *ctData;
	short i;
	
	theColors = (CTabHandle)NewHandle(sizeof(ColorTable) + 255 * sizeof(ColorSpec));
	if (!theColors) 
		theColors = (CTabHandle)TempNewHandle(sizeof(ColorTable) + 255 * sizeof(ColorSpec), &theErr);
	if (theColors && theErr == noErr) {
		(*theColors)->ctSeed = GetCTSeed();
		(*theColors)->ctFlags = 0x8000;
		(*theColors)->ctSize = 255;
		ctData = &(*theColors)->ctTable[0];
		for (i = 0; i < 256; i++) {
			ctData->rgb.red = (ushort)(*theData++) << 8;
			ctData->rgb.green = (ushort)(*theData++) << 8;
			ctData->rgb.blue = (ushort)(*theData++) << 8;
			ctData++;
		}
	} else theColors = nil;
	return theColors;
}

//=====================================================================================
// OSErr DecompressGIFImage(uchar *theData, PixMapHandle srcPixMap, Rect *srcRect, 
//			Rect *dstRect, RgnHandle dstRgn, ICMProgressProcRecordPtr progProc)
//=====================================================================================
// Decompresses a single image in a GIF to the appropriate destination rectangle.
//=====================================================================================

static OSErr DecompressGIFImage(uchar *theData, PixMapHandle srcPixMap, Rect *srcRect, 
			Rect *dstRect, RgnHandle dstRgn, ICMProgressProcRecordPtr progProc)
{
	Rect imageSrcRect, imageDstRect, bandSrcRect, bandDstRect, localSrcRect;
	PixMapHandle dstPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
	short bandHeight = Height(&(*srcPixMap)->bounds), rowCount = 0;
	Boolean interlaced = theData[9] & 0x40, done = false;
	NestedProgress theProc = (*gGIFParams)->progress;
	OSErr theErr = noErr;

	// calculate the source & destination rectangle for this image
	imageSrcRect.top = (theData[4] << 8) + theData[3];
	imageSrcRect.left = (theData[2] << 8) + theData[1];
	imageSrcRect.bottom = imageSrcRect.top + (theData[8] << 8) + theData[7];
	imageSrcRect.right = imageSrcRect.left + (theData[6] << 8) + theData[5];
	imageDstRect = imageSrcRect;
	MapRect(&imageDstRect, srcRect, dstRect);

	// skip over the descriptor and local color table, if any
	if (theData[9] & 0x80) theData += 3 * (1 << ((theData[9] & 7) + 1));
	theData += 10;

	// reset the data pointers and LZW decoder for non-interlaced images
	if (!interlaced) DecodeRow(Width(&imageSrcRect), theData, nil);

	// set up the progress function
	(*gGIFParams)->progress.begin = FixRatio(gCurrentLines, gTotalLines);
	(*gGIFParams)->progress.begin = theProc.begin +
				FixMul((*gGIFParams)->progress.begin, theProc.end - theProc.begin);
	(*gGIFParams)->progress.end = FixRatio(gCurrentLines + Height(&imageSrcRect), gTotalLines);
	(*gGIFParams)->progress.end = theProc.begin +
				FixMul((*gGIFParams)->progress.end, theProc.end - theProc.begin);
	if ((*gGIFParams)->progress.prog.progressProc) 
		CallICMProgressProc((*gGIFParams)->progress.prog.progressProc, 
					codecProgressUpdatePercent, (*gGIFParams)->progress.begin, 
					(*gGIFParams)->progress.prog.progressRefCon);

	// initialize the band source and destination rectangles
	bandSrcRect = imageSrcRect;
	bandSrcRect.bottom = bandSrcRect.top + bandHeight;

	// now loop until we've drawn all the bands
	do {
	
		// decompress a band; for this we need the bandSrcRect relative to the
		// image rect at (0,0)
		localSrcRect = bandSrcRect;
		OffsetRect(&localSrcRect, -imageSrcRect.left, -imageSrcRect.top);
		LockPixels(srcPixMap);
		if (interlaced) 
			done = DecodeInterlaced(theData, srcPixMap, Width(&imageSrcRect), 
				   Height(&imageSrcRect), &localSrcRect, &rowCount, progProc);
		else done = DecodeNonInterlaced(theData, srcPixMap, Width(&imageSrcRect),
					Height(&imageSrcRect), &localSrcRect, &rowCount, progProc);
		UnlockPixels(srcPixMap);

		// calculate the appropriate destination rectangle and CopyBits there
		bandDstRect = bandSrcRect;
		MapRect(&bandDstRect, srcRect, dstRect);
		if (!dstRgn || bandDstRect.bottom >= (*dstRgn)->rgnBBox.top) {
			char sState = HGetState((Handle)srcPixMap), dState = HGetState((Handle)dstPixMap);
			HLock((Handle)srcPixMap);
			HLock((Handle)dstPixMap);
			LockPixels(srcPixMap);
			OffsetRect(&localSrcRect, -localSrcRect.left, -localSrcRect.top);
			CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, &localSrcRect,
						&bandDstRect, srcCopy, dstRgn);
			UnlockPixels(srcPixMap);
			HSetState((Handle)dstPixMap, dState);
			HSetState((Handle)srcPixMap, sState);
		}
		if ((done && !interlaced) || (*gGIFParams)->progress.aborted ||
					bandDstRect.bottom >= dstRect->bottom ||
					(dstRgn && bandDstRect.bottom >= (*dstRgn)->rgnBBox.bottom)) break;

		// if we hit a bad spot of data, set the errors and break out
		if (!gGIFData.end && !done) {
			gIntError = errCorruptImage, theErr = codecBadDataErr;
			break;
		}

		// if we've drawn all we need to, exit prematurely
		if (dstRgn && bandDstRect.top > (*dstRgn)->rgnBBox.bottom) break;

		// otherwise, set up for the next band
		bandSrcRect.top += bandHeight;
		bandSrcRect.bottom += bandHeight;
		if (bandSrcRect.bottom > imageSrcRect.bottom) 
			bandSrcRect.bottom = imageSrcRect.bottom;
	} while (bandDstRect.top < imageDstRect.bottom);

	// clean up after the progress function
	if ((*gGIFParams)->progress.prog.progressProc)
		CallICMProgressProc((*gGIFParams)->progress.prog.progressProc,
					codecProgressUpdatePercent, (*gGIFParams)->progress.end, 
					(*gGIFParams)->progress.prog.progressRefCon);
	(*gGIFParams)->progress.begin = theProc.begin;
	(*gGIFParams)->progress.end = theProc.end;
	KeepSpinning();
	gCurrentLines += Height(&imageSrcRect);

	// if we were aborted, signal the abort error
	return theErr;
}

//=====================================================================================
// Boolean DecodeInterlaced(uchar *theData, PixMapHandle srcPixMap, short iwidth, 
//			short iheight, Rect *sRect, ICMProgressProcRecordPtr progProc)
//=====================================================================================
// Organizes the decoding of an interlaced GIF image band.
//=====================================================================================

static Boolean DecodeInterlaced(uchar *theData, PixMapHandle srcPixMap, short imageWidth, 
			short imageHeight, Rect *srcRect, short *rowCount,
			ICMProgressProcRecordPtr progProc)
{
	static short gRowCount;
	uchar *baseAdr = (uchar *)GetPixBaseAddr(srcPixMap);
	long rowBytes = (*srcPixMap)->rowBytes & 0x3fff;
	uchar *theAdr;
	OSErr theErr;
	short row;
	
	// if the top of the source rectangle is 0, reset our row counter
	if (!srcRect->top) gRowCount = 0;
	
	// reset the data pointers and LZW decoder
	DecodeRow(imageWidth, theData, nil);

	// do the first set of interlacing (starting at row 0, every 8 rows)
	for (row = 0; row < imageHeight; row += 8) {
		if (row >= srcRect->top && row < srcRect->bottom) {
			theAdr = baseAdr + rowBytes * (row - srcRect->top);
			(*rowCount)++;
			if (!(*rowCount & 63) && progProc->progressProc) {
				theErr = (OSErr)CallICMProgressProc(progProc->progressProc,
							codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight), 
							progProc->progressRefCon);
				if (theErr == codecAbortErr) return true;
			}
			if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
		} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
	}

	// do the second set of interlacing (starting at row 4, every 8 rows)
	for (row = 4; row < imageHeight; row += 8) {
		if (row >= srcRect->top && row < srcRect->bottom) {
			theAdr = baseAdr + rowBytes * (row - srcRect->top);
			(*rowCount)++;
			if (!(*rowCount & 63) && progProc->progressProc) {
				theErr = (OSErr)CallICMProgressProc(progProc->progressProc, 
							codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight), 
							progProc->progressRefCon);
				if (theErr == codecAbortErr) return true;
			}
			if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
		} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
	}

	// do the third set of interlacing (starting at row 2, every 4 rows)
	for (row = 2; row < imageHeight; row += 4) {
		if (row >= srcRect->top && row < srcRect->bottom) {
			theAdr = baseAdr + rowBytes * (row - srcRect->top);
			(*rowCount)++;
			if (!(*rowCount & 63) && progProc->progressProc) {
				theErr = (OSErr)CallICMProgressProc(progProc->progressProc, 
							codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight), 
							progProc->progressRefCon);
				if (theErr == codecAbortErr) return true;
			}
			if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
		} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
	}

	// do the last set of interlacing (starting at row 1, every other row)
	for (row = 1; row < imageHeight; row += 2) {
		if (row >= srcRect->top && row < srcRect->bottom) {
			theAdr = baseAdr + rowBytes * (row - srcRect->top);
			(*rowCount)++;
			if (!(*rowCount & 63) && progProc->progressProc) {
				theErr = (OSErr)CallICMProgressProc(progProc->progressProc, 
							codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight), 
							progProc->progressRefCon);
				if (theErr == codecAbortErr) return true;
			}
			if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
		} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
	}
	return false;
}

//=====================================================================================
// Boolean DecodeNonInterlaced(uchar *theData, PixMapHandle srcPixMap, short imageWidth, 
//			short imageHeight, Rect *srcRect, ICMProgressProcRecordPtr progProc)
//=====================================================================================
// Organizes the decoding of an interlaced GIF image band.
//=====================================================================================

static Boolean DecodeNonInterlaced(uchar *theData, PixMapHandle srcPixMap, short imageWidth, 
			short imageHeight, Rect *srcRect, short *rowCount,
			ICMProgressProcRecordPtr progProc)
{
#if applec
#pragma unused(srcRect)
#endif
	uchar *baseAdr = (uchar *)GetPixBaseAddr(srcPixMap);
	long rowBytes = (*srcPixMap)->rowBytes & 0x3fff;
	short height = Height(&(*srcPixMap)->bounds);
	OSErr theErr;
	short row;
	
	for (row = 0; row < height; row++) {
		if (DecodeRow(imageWidth, theData, baseAdr) || !gGIFData.end) return true;
		baseAdr += rowBytes;
		(*rowCount)++;
		if (!(*rowCount & 63) && progProc->progressProc) {
			theErr = (OSErr)CallICMProgressProc(progProc->progressProc,
						codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight), 
						progProc->progressRefCon);
			if (theErr == codecAbortErr) return true;
		}
	}
	return false;
}

//=====================================================================================
// Boolean DecodeRow(short width, uchar *src, uchar *dest)
//=====================================================================================
// Decodes a single row of a GIF image.
//=====================================================================================

static Boolean DecodeRow(short width, uchar *src, uchar *dest)
{
	char mmuMode = true32b;
	
	gGIFData.width = width;
	gGIFData.src = src;
	gGIFData.dest = dest;
	SwapMMUMode(&mmuMode);
	if (dest != (uchar *)-1) DoDecodeRow(&gGIFData);
	else DoDecodeDummyRow(&gGIFData);
	SwapMMUMode(&mmuMode);
	return gGIFData.theReturn;
}

#if !USE_ASM

//=====================================================================================
// Generic C substitutes for the assembly functions defined below.
//=====================================================================================

static void DoDecodeRow(GIFDataPtr theData)
{
#include "DecodeRow.c"
}

static void DoDecodeDummyRow(GIFDataPtr theData)
{
#include "DecodeDummyRow.c"
}

#elif THINK_C

//=====================================================================================
// Assembly language interfaces for THINK C; here we define the offsets of all fields
// within the GIFData structure, and then provide C function wrappers around asm { }
// statements that #include the actual assembly code.  In MPW, we just need to compile
// GIF.a to get the assembly routines.
//=====================================================================================

#define src offsetof(GIFData, src)				/*   0 */
#define dest offsetof(GIFData, dest)			/*   4 */
#define stack offsetof(GIFData, stack)			/*   8 */
#define theEnd offsetof(GIFData, end)			/*  12 */
#define tree offsetof(GIFData, tree)			/*  16 */
#define store offsetof(GIFData, store)			/*  20 */
#define charSize offsetof(GIFData, charSize)	/*  68 */
#define width offsetof(GIFData, width)			/*  70 */
#define theReturn offsetof(GIFData, theReturn)	/*  72 */

static void DoDecodeRow(GIFDataPtr theData)
{
	asm {
		#include "DecodeRow.a"
	}
}

static void DoDecodeDummyRow(GIFDataPtr theData)
{
	asm {
		#include "DecodeDummyRow.a"
	}
}
#endif
