/*
 * Revised: 3/12/92 - wade
 * Added gif_get_data function
 * Added global gNetGifPtr, gif_title, inImageCount, inImageRunningCount
 * Added #define NETGIFSIZE
 * Changed GIFtoPICT() to store the title in var namestore
 * Added gif_bind function
 * Added gif_store_image_size()
 * Revised get_byte function
 * modified GIFclose
 * redefined the feof macro
 *
 * Revised: 4/28/92 - mec
 * Replaced _many_ instances of 0 (zero) with noErr as appropriate
 * Replaced the construct
 *	#ifdef DEBUG
 *		DebugStr()
 *    with
 *	#ifdef DEBUGSTR
 *		DEBUGSTR()
 *		to allow switching of debugging environments and to reduce the number of
 *		defines required to make it work.  DEBUGSTR should either be undefined or
 *		defined as DebugStr or SysBreakStr.
 * "Ansi-fied" all functions using old-style headers.
 * Patched memory leaks occuring when reading is aborted or an invalid file is read.
 * Added code to set the cursor to a watch during window painting in several
 *	routines including gif_net_data (since it calls cmd_new_display which resets
 *	the cursor back to an arrow.)
 */

#include <Memory.h>
#include <StdLib.h>
#include <stdio.h>
#include <string.h>
#include <Strings.h>
#include <Dialogs.h>
#include <QDOffscreen.h>
#include <OSUtils.h>
#include <SegLoad.h>
#include <Palettes.h>

#ifndef __PACKAGES__
#include <Packages.h>
#endif

#ifndef __ERRORS__
#include <Errors.h>
#endif

#ifndef __FILES__
#include <Files.h>
#endif

#ifndef __RESOURCES__
#include <Resources.h>
#endif

#ifndef __STRING__ 
#include <String.h>
#endif


#include "documentRec.h"
#include "GIF.h"
#include "GIFstd.h"
#include "GIFerrs.h"
#include "pips.h"
#include "Utilities.h"

extern	WindowPtr	gListWindow;

#define	QD32Available	(kQD32Bit <= gQDVersion)
#define	System7orBetter	(0x700 <= (gSystemVersion & 0x0FFF))

#define LM_to_uint(a,b)                 (((b)<<8)|(a))

/* redefine the MPW's feof as we don't use it */
#define feof(stream)	(0)
/* 
 * START of GIF Stuff
 */
enum{init,show,close};

/* GLOBALS!!! */

GIFLogicalScreenPtr		theLogicalScreenPtr;	/* my own data structure - for the GIF header */
GIFImageDescriptorPtr	theImageDescriptorPtr;

PixMapPtr 	GIFppm;
DialogPtr	GIFth_handle;
Boolean		GIFthermometer;
Str255 		gif_title;

#define		kWatchCursorOffset		5
short		gWatchCursorIndex;

#define		kPanicMemorySize	4096
Ptr			gPanicMemory;

#define 	NETGIFSIZE  (GIF_BUF_SIZE + 4)  /* we add 4 bytes for the EOM from the server */
				  		/* if we ask for GIF_BUF_SIZE, we can get GIF_BUF_SIZE + 4 */
Handle 		gNetGifPtr;

#define INBUFSIZE 512
//static char inBuff[INBUFSIZE];  /* used only in LOCAL_GIF code */
static short inBuffCount = 0;   /* this is the size of the buffer used in gif_bind */
static short inBuffLoc = 0;
static short lastBuffErr = noErr;
static long  inImageCount = 0;  /* this is the total size of the image, e.g. file stat */
static long  inImageRunningCount = 0;  /* this is the number of bytes received at this time */
/* IMPORT INT bad_code_count;
 *
 * This value is the only other global required by the using program, and
 * is incremented each time an out of range code is read by the decoder.
 * When this value is non-zero after a decode, your GIF file is probably
 * corrupt in some way...
 */
INT bad_code_count;
IMPORT LOCALWORD decoder();



/* END OF GLOBALS */

#ifdef UNDEF
pascal Handle GIFNewHandle(Size byteCount) {
	long			amount;
	Str255			amtStr, tempStr;
	Handle			tmpHandle;
	OSErr			saveMErr;
	long			tempLong;
	short			itemHit;
	
	strcpy(tempStr, "\p\n        New Handle - ");
	DEBUGSTR(tempStr);
	amount = byteCount;
	numtostring(amount, amtStr);
	C2PStr( amtStr );
	DEBUGSTR(amtStr);
	tmpHandle = NewHandle(byteCount);
	
	saveMErr = MemError();
	if (saveMErr) {
		tempLong = saveMErr;
		numtostring(tempLong, amtStr);
		strcpy(tempStr, "\pMemory error - ");
		strcat(tempStr, amtStr);
		ParamText( tempStr, "", "", "" );
		itemHit = Alert( rUserAlert, nil );
	}

	return tmpHandle;
}

pascal void GIFDisposeHandle(Handle h)
{
	Size			theSize;
	long			amount;
	Str255			amtStr, tempStr;
	
	strcpy(tempStr, "\p\n           Disposing Handle - ");
	DEBUGSTR(tempStr);
	theSize = GetHandleSize(h);
	amount = theSize;
	numtostring(amount, amtStr);
	C2PStr( amtStr );
	DEBUGSTR(amtStr);
	
	DisposeHandle(h);
}

pascal Ptr GIFNewPtr(Size byteCount) {
	long			amount;
	Str255			amtStr, tempStr;
	Ptr			tmpPtr;
	OSErr			saveMErr;
	long			tempLong;
	short			itemHit;
	
	strcpy(tempStr, "\p\n        New Ptr - ");
	DEBUGSTR(tempStr);
	amount = byteCount;
	numtostring(amount, amtStr);
	C2PStr( amtStr );
	DEBUGSTR(amtStr);
	tmpPtr = NewPtr(byteCount);
	
	saveMErr = MemError();
	if (saveMErr) {
		tempLong = saveMErr;
		numtostring(tempLong, amtStr);
		strcpy(tempStr, "\pMemory error - ");
		strcat(tempStr, amtStr);
		ParamText( tempStr, "", "", "" );
		itemHit = Alert( rUserAlert, nil );
	}

	return tmpPtr;
}
pascal Ptr GIFNewPtrClear(Size byteCount) {
	long			amount;
	Str255			amtStr, tempStr;
	Ptr			tmpPtr;
	OSErr			saveMErr;
	long			tempLong;
	short			itemHit;
	
	strcpy(tempStr, "\p\n        New PtrClear - ");
	DEBUGSTR(tempStr);
	amount = byteCount;
	numtostring(amount, amtStr);
	C2PStr( amtStr );
	DEBUGSTR(amtStr);
	tmpPtr = NewPtrClear(byteCount);
	
	saveMErr = MemError();
	if (saveMErr) {
		tempLong = saveMErr;
		numtostring(tempLong, amtStr);
		strcpy(tempStr, "\pMemory error - ");
		strcat(tempStr, amtStr);
		ParamText( tempStr, "", "", "" );
		itemHit = Alert( rUserAlert, nil );
	}

	return tmpPtr;
}

pascal void GIFDisposePtr(Ptr h)
{
	Size			theSize;
	long			amount;
	Str255			amtStr, tempStr;
	
	strcpy(tempStr, "\p\n           Disposing Ptr - ");
	DEBUGSTR(tempStr);
	theSize = GetPtrSize(h);
	amount = theSize;
	numtostring(amount, amtStr);
	C2PStr( amtStr );
	DEBUGSTR(amtStr);
	
	DisposePtr(h);
}


//#define NewHandle GIFNewHandle
//#define DisposeHandle GIFDisposeHandle
//#define NewPtr GIFNewPtr
//#define NewPtrClear GIFNewPtrClear
//#define DisposePtr GIFDisposePtr
#endif

#ifdef DEBUGSTR
void MemoryCheck(const char * whereFrom)
{
	long			total, contig;
	Str255			tempStr;
	Str255			tempStr2;
	
	PurgeSpace(&total, &contig);

	numtostring(total, tempStr);
	strcpy(tempStr2, "\n");
	strcat(tempStr2, whereFrom);
	strcat(tempStr2, "\nTotal space: ");
	strcat(tempStr2, tempStr);

	numtostring(contig, tempStr);
	strcat(tempStr2, "\nContig space: ");
	strcat(tempStr2, tempStr);
	C2PStr( tempStr2 );
	DEBUGSTR(tempStr2);
#ifdef UNDEF
	strcpy(tempStr2, whereFrom);
	strcat(tempStr2, " - Dumping Log;LOG HEAPDUMP.LOG;HD;LOG;G");
	C2PStr( tempStr2 );
	DebugStr(tempStr2);
#endif
}
#endif




/* *************************************************************************** */
/* *********************** File manipulation routines ************************ */
/* *************************************************************************** */

/*
 * Function:	GIFopen
 * Purpose:		opens the data stream
 * Parameters:	filename - ptr to file name
 *				mode - mode to open file in
 * Returns:		file pointer return value from fopen.
 *
 */
#pragma segment graphics
FILE *GIFopen(const char *filename, const char *mode) {
	return( fopen(filename, mode));
}


/*
 * Function:	GIFclose
 * Purpose:		close the data stream
 * Parameters:	stream - file pointer
 * Returns:		return value from fclose.
 *
 */
#pragma segment graphics
int GIFclose(FILE *stream) {
	if (gNetGifPtr)
		DisposeHandle(	gNetGifPtr );
	return (fclose (stream));
}

/*
 * Function:	get_byte
 * Purpose:		fetch the next available byte from the data stream.
 * Parameters:	*none*
 * Returns:		next available byte, or (-1) to indicate a failure of some kind.
 *
 * Notes:		Steve - this is the "core" routine to fetch bytes from the data
 *			stream.  This version uses "fread" based on the stream ptr
 *			in theLogicalScreenPtr->GIF_file and buffers up to INBUFSIZE
 *			bytes in a static array.  This routine is called directly by
 *			the decoder code and indirectly via GIFread.
 */
#pragma segment graphics
INT get_byte()
{
	INT	theInt;
	int 	rc;


	if (inBuffLoc >= inBuffCount) {
		if (theLogicalScreenPtr->GIF_file) {
			HLock(gNetGifPtr);
			inBuffCount = fread(*gNetGifPtr, 1, INBUFSIZE, theLogicalScreenPtr->GIF_file);
			HUnlock(gNetGifPtr);
			if (inBuffCount == 0) {
				lastBuffErr = -1;
				return(-1);	/* read error */
				}
			else
				inBuffLoc = 0;
		} else {
			/* keep a count of the number of bytes received upto now */
			inImageRunningCount = inImageRunningCount + inBuffCount;
	
			/* get a gif image from the server & reset inBufCount */
			if (inBuffLoc == 0 || inImageRunningCount != inImageCount )
				rc = gif_net_data(gif_get_data);
			
			if (inImageRunningCount == inImageCount || rc != true) {
				lastBuffErr = -1;
				return(-1);	/* read error */
				}
			else
				inBuffLoc = 0;
		}
	}
	theInt = *(*gNetGifPtr + inBuffLoc);
	inBuffLoc++;
	theInt = theInt & 0xFF;

	return(theInt);
}


#pragma segment graphics
gif_bind(buf,length,offset)
char	*buf;
int	length;	/* this is the bytes read from the socket */
long	offset; /* this is the running total bytes read   */
{
	
	if (offset + length <= NETGIFSIZE) {
		bcopy(buf,(*gNetGifPtr)+offset,length);
		inBuffCount = offset + length;
	}
	/* else, we probably need an error message here */
	/* if it ever happens, we messed up.            */
}

#pragma segment graphics
gif_store_image_size(real_size)
long	real_size;
{
	/* store the image size in a global variable */
	/* real_size is determined in networkMgr.c   */
	inImageCount = real_size;
}

#pragma segment graphics
INT gif_net_data(gif_msg)
int	gif_msg;
{
	Str255	list_buffer;
	Boolean	rc;
	static  char gif_node_id[NODE_LENGTH];
	Str255	meta;

	switch(gif_msg) {
		case gif_init:
			/* allocate storage where stuff from the network will be put */
			gNetGifPtr = NewHandle(NETGIFSIZE);
			rc = true;
			if (gNetGifPtr == nil) {
				OpenAlertDialog( MEMORY_ERROR, "gif_init");
				rc = false;  /* end in a nice way */
			}
			
			/* store the node id we are using */
			if (rc) rc = list_last_click_buffer(list_buffer);
			if (rc) rc = get_field(list_buffer,NODE,gif_node_id);
			
			/* now, get the title for display in the window banner */
			if (rc) rc = get_field(list_buffer,TITLE,gif_title);
			
			/* initialize some stuff per each image request */
			inImageCount = 0;
			inImageRunningCount = 0;
			gWatchCursorIndex = -1;
			wind_set_cursor(kWatchCursorOffset);
			break;
			
		case gif_get_data:
			/* for now, we are using the text_tr, someday, there may be */
			/* a special gif transaction */
			/* because compose_transaction(text_tr) uses global variables */
			/* assigned to the commandMgr.c routine, we need to call another */
			/* routine to get the formatted transaction */
			cmd_format_gif_transaction(meta,gif_node_id,inImageRunningCount);
			
			/* this routine will call the network stuff */
			/* the network routine will leave the data in a buffer */
			rc = cmd_new_display( meta, command, gif );
			wind_set_cursor((gWatchCursorIndex++ % 8) + kWatchCursorOffset);
			break;
			
		case gif_close:
			if (gNetGifPtr != nil)
				DisposeHandle(gNetGifPtr); 
			break;
			
		case gif_correct_size:
			/* we need to remove the EOM ('LF.CRLF') padding */
			inBuffCount = inBuffCount - 4;
			break;
	}
	
	return(rc);
}

/*
 * Function:	GIFfileName
 * Purpose:		Prompt for an existing filename using the StdFile dialogs
 *				(i.e., for an "Open..." menu item).
 * Parameters:	prompt - prompt for SFGetFile dialog
 *				filename - returned file name
 *				type - application signature - type of files to open.
 * Returns:		ptr to filename.
 *
 */
#pragma segment graphics
char *
GIFfileName( prompt, filename, type )
char * prompt;
char * filename;
OSType type;
{
	static Point dlogWhere = { 70, 40 };
	short numTypes = (type == '????') ? -1 : 1;
	SFReply imgFile;
	char pprompt[200];
	
	strcpy( pprompt, prompt );
	C2PStr( pprompt );
	SFGetFile( dlogWhere, pprompt, NULL, numTypes, &type, NULL, &imgFile );
	if (! imgFile.good)
	{
		filename = NULL;
		return( NULL );
	}

	SetVol( NULL, imgFile.vRefNum ); /* Go to the directory */
	P2CStr( (char *) imgFile.fName );
	strcpy( filename, (char *)imgFile.fName );
	return( filename );
}

/* *************************************************************************** */
/* *************************************************************************** */
/* *************************************************************************** */

/*
 * Function:	GIFread
 * Purpose:		fetch the next nmemb available bytes from the data stream.
 * Parameters:	ptr		- pointer to buffer which will hold the incoming bytes
 *				size	- (ignored - 1 assumed)
 *				nmemb	- number of bytes to read
 *				stream	- (ignored)
 * Returns:		number of bytes read, or failure code from get_byte.
 *
 */
#pragma segment graphics
size_t GIFread(unsigned char *ptr, size_t size, size_t nmemb, FILE *stream) {
#pragma unused(size)
#pragma unused(stream)
	short	count;
	short	theInt;
	
	for (count = 0; count < nmemb; count++) {
	   if ((theInt = get_byte()) < 0)
		  return(theInt);
	   ptr[count] = theInt;
	 }
	 return(nmemb);
}


/*
 * Function:	GIFeof
 * Purpose:		check to see if there was an input error on the last read
 * Parameters:	stream	- (ignored)
 * Returns:		false, or last failure code from get_byte.
 *
 */
#pragma segment graphics
int GIFeof(FILE *stream) {
#pragma unused(stream)
	if (0 == inBuffCount)
		return (lastBuffErr);
	else
		return (false);
}

/* *************************************************************************** */
/* *************************************************************************** */
/* *************************************************************************** */

/*
 * Dispose of the block of memory we have stashed away to make sure we can put
 * up an "out of memory" dialog.  This shorts the user 4K of memory which _could_
 * be used to store pictures, but keeps the machine from crashing.
 * This should probably be implemented using a GrowZone proc, but those are NASTY
 * and this seems to work.
 */
#pragma segment graphics
void DisposePanicMemory()
{
	if (gPanicMemory) DisposePtr(gPanicMemory);
	gPanicMemory = nil;
}


/*
 * Handle generic error messages.  
 * This needs to be re-written to use error codes and STR# resources, but should probably
 * remain seperate from the standard error handling code _unless_ that code is always
 * resident.
 */
#pragma segment graphics
void 
gif_error( char * s )
{
	char msg[255];
	char * p;
	short		itemHit;
	OSErr		saveMErr, saveRErr;
	long		tempLong;
	char		tempStr[255];

	saveMErr = MemError();
	saveRErr = ResError();

	DisposePanicMemory();

//	MemoryCheck("inside gif_error");

	gPanicMemory = nil;
	strcpy( msg, s );
	/* Kill newlines */
	for (p = msg; *p; p++)
		if (*p == '\n') *p = ' ';
	C2PStr( msg );
	ParamText( msg, "", "", "" );
	itemHit = Alert( rUserAlert, nil );
	
	if (saveMErr) {
		tempLong = saveMErr;
		numtostring(tempLong, tempStr);
		strcpy(msg, "\pMemory error - ");
		strcat(msg, tempStr);
		ParamText( msg, "", "", "" );
		itemHit = Alert( rUserAlert, nil );
	}

	if (saveRErr) {
		tempLong = saveRErr;
		numtostring(tempLong, tempStr);
		strcpy(msg, "\pResource error - ");
		strcat(msg, tempStr);
		ParamText( msg, "", "", "" );
		itemHit = Alert( rUserAlert, nil );
	}

	if (!gPanicMemory)	
		gPanicMemory = NewPtr(kPanicMemorySize);
}




/*
 * ClearGIFColorTable - set all of the colors in a color table to black
 *
 */
#pragma segment graphics
long ClearGIFColorTable(unsigned char	theColorMap[][])
{
	short		i;
	
	for (i = 0; (i < MAXCOLORMAPSIZE); i++) {
		theColorMap[i][CM_RED] = 0;
		theColorMap[i][CM_GREEN] = 0;
		theColorMap[i][CM_BLUE] = 0;
	}
	return(1);
}


#pragma segment graphics
long copyGIFColorTable(unsigned char oldColorMap[][], unsigned char	newColorMap[][])
{
	short		i;

/*
	for (i = 0; (i < MAXCOLORMAPSIZE); i++) {
		newColorMap[i][CM_RED] = oldColorMap[i][CM_RED] ;
		newColorMap[i][CM_GREEN] = oldColorMap[i][CM_GREEN] ;
		newColorMap[i][CM_BLUE] = oldColorMap[i][CM_BLUE] ;
	}
*/
	for (i = 0; (i < (MAXCOLORMAPSIZE * 3)); i++) {
		newColorMap[0][i] = oldColorMap[0][i] ;
	}
	return(1);
}


#pragma segment graphics
short makeMacColorMap(unsigned char oldColorMap[][], unsigned char	newColorMap[][])
{
	register short		i;
	register short		j;
	register short		k;
	short				newColor;		/* is the current color "unique" */

	newColorMap[0][CM_RED] = 0;			/* black */
	newColorMap[0][CM_GREEN] = 0;
	newColorMap[0][CM_BLUE] = 0;
	
	newColorMap[1][CM_RED] = 255;			/* white */
	newColorMap[1][CM_GREEN] = 255;
	newColorMap[1][CM_BLUE] = 255;
	
	k = 2;		/* next available slot */
	
	for (i = 0; (i < MAXCOLORMAPSIZE); i++) {		/* check all entries */
		newColor = 1;
		for (j = 0; (j < k); j++) {		/* check against all existing colors */
			if (oldColorMap[i][CM_RED] != newColorMap[j][CM_RED]) continue;
			if (oldColorMap[i][CM_GREEN] != newColorMap[j][CM_GREEN]) continue;
			if (oldColorMap[i][CM_BLUE] != newColorMap[j][CM_BLUE]) continue;
			newColor = 0;
		}
		
		if ((newColor) && (k < MAXCOLORMAPSIZE)) {
			newColorMap[k][CM_RED] = oldColorMap[i][CM_RED] ;
			newColorMap[k][CM_GREEN] = oldColorMap[i][CM_GREEN] ;
			newColorMap[k][CM_BLUE] = oldColorMap[i][CM_BLUE] ;
			k++;
		}
	}

	return(k);
}

/*****************************************************************
 * TAG( gif_get_setup )
 * 
 * Read the initialization information from an GIF file.
 *
 *	Returns noErr (0) on success, -1 if the file is not an GIF file,
 *	-2 if malloc of the color map failed, -3 if an immediate EOF
 *	is hit (empty input file), and -4 if an EOF is encountered reading
 *	the setup information.
 * Assumptions:
 * 	infile points to the "magic" number in an GIF file (usually
 * byte 0 in the file).
 * Algorithm:
 * 	Read in the setup info and fill in theLogicalScreen and theImageDescriptor.
 */
#pragma segment graphics
int
gif_get_setup()
{
    register FILE *infile = theLogicalScreenPtr->GIF_file;
    register int i;

    unsigned char       buf[16];	// buffer to read multi-byte "strings" into
	char	theSignature[4];	/* Signature from file */
	char	theVersion[4];		/* version from file */
	
    GIFread( theSignature, 1, 3, infile );  /* get the signature */
    if ( GIFeof( infile ) )
	return GIF_EMPTY;
	if (strncmp(theSignature, "GIF", 3) != 0)
	return GIF_NOT_GIF;		/* Signature mismatch */

    GIFread( theVersion, 1, 3, infile );  /* get the version */
    if ( GIFeof( infile ) )
	return GIF_EOF;
	if (strncmp(theVersion, "87a", 3) != 0)
	return GIF_NOT_GIF;		/* version mismatch */

    GIFread( buf, 1, 7, infile );  /* get the descriptor */
    if ( GIFeof( infile ) )
	return GIF_EOF;

	theLogicalScreenPtr->width           = LM_to_uint(buf[0],buf[1]);
	theLogicalScreenPtr->height          = LM_to_uint(buf[2],buf[3]);
	theLogicalScreenPtr->packedFields	 = buf[4];

	theLogicalScreenPtr->globalColorTableFlag = (theLogicalScreenPtr->packedFields >> 7) & 0x01;
	theLogicalScreenPtr->colorResolution = (theLogicalScreenPtr->packedFields >> 4) & 0x07;
	theLogicalScreenPtr->sortFlag = (theLogicalScreenPtr->packedFields >> 3) & 0x01;
	theLogicalScreenPtr->globalColorTableSizeBits = (theLogicalScreenPtr->packedFields) & 0x07;
	/* calculated fields */
	theLogicalScreenPtr->globalColorTableSize = (2 << theLogicalScreenPtr->globalColorTableSizeBits);

	theLogicalScreenPtr->backColorIndex = buf[5];
	theLogicalScreenPtr->pixelAspectRatio = buf[6];

	if (theLogicalScreenPtr->globalColorTableFlag) {		
		ClearGIFColorTable (theLogicalScreenPtr->colorMap);
			
		for (i = 0;(i < theLogicalScreenPtr->globalColorTableSize);i++) {
			GIFread( buf, 1, 3, infile );  /* rgb value */
			if (GIFeof(infile)) return GIF_EOF;
			theLogicalScreenPtr->colorMap[i][CM_RED] = buf[0] ;
			theLogicalScreenPtr->colorMap[i][CM_GREEN] = buf[1] ;
			theLogicalScreenPtr->colorMap[i][CM_BLUE] = buf[2] ;
		}
	} else {
		ClearGIFColorTable(&theLogicalScreenPtr->colorMap);
	}

/* This ends the section of code to get the Logical Screen information 		*/
/* What follows gets the image descriptor for the first image, and should 	*/
/* probably be moved into another function, to allow for multiple images.	*/
/* In fact, the following code should probably be moved into the section to	*/
/* generate the image, so it could be called once for each image in the file*/
/* There should also be code to _at least_ ignore any extensions or comments*/
/* in the file.  For now, it seems to work.	*/


/*	while (1) { */
		GIFread( buf, 1, 1, infile );  /* get the next char */

/*
        if (buf[0] == ';')
            return FALSE;
		if (buf[0] == '!') {
            if (! ReadOK(fd,&c,1))
                EasyFail("No extention function code -- EOF\n",TRUE);
            if (IgnoreExtention(fd))
                return(TRUE);
        }
*/
        if (buf[0] != ',') {
            return GIF_NOT_GIF;		/* actually, too many images */
        }

    GIFread( buf, 1, 9, infile );  /* get the descriptor */
    if ( GIFeof( infile ) )
	return GIF_EOF;

	theImageDescriptorPtr->leftPos			= LM_to_uint(buf[0],buf[1]);
	theImageDescriptorPtr->topPos			= LM_to_uint(buf[2],buf[3]);
	theImageDescriptorPtr->imageWidth		= LM_to_uint(buf[4],buf[5]);
	theImageDescriptorPtr->imageHeight		= LM_to_uint(buf[6],buf[7]);
	theImageDescriptorPtr->packedFields		= buf[8];
	theImageDescriptorPtr->localColorTableFlag		= (theImageDescriptorPtr->packedFields >> 7) & 0x01;
	theImageDescriptorPtr->interlaceFlag			= (theImageDescriptorPtr->packedFields >> 6) & 0x01;
	theImageDescriptorPtr->sortFlag					= (theImageDescriptorPtr->packedFields >> 5) & 0x01;
	/* skip reserved fields */
	theImageDescriptorPtr->localColorTableSizeBits	= (theImageDescriptorPtr->packedFields) & 0x07;
	/* calculated fields */
	theImageDescriptorPtr->localColorTableSize		= (2 << theImageDescriptorPtr->localColorTableSizeBits);
	theImageDescriptorPtr->curLine = theImageDescriptorPtr->topPos;
	theImageDescriptorPtr->curPass = 0;

	if (theImageDescriptorPtr->localColorTableFlag) {
		/* if there is a local color table then load it in */
		ClearGIFColorTable (theImageDescriptorPtr->colorMap);
			
		for (i = 0;(i < theImageDescriptorPtr->localColorTableSize);i++) {
			GIFread( buf, 1, 3, infile );  /* rgb value */
			if (feof(infile)) return GIF_EOF;
			theImageDescriptorPtr->colorMap[i][CM_RED] = buf[0] ;
			theImageDescriptorPtr->colorMap[i][CM_GREEN] = buf[1] ;
			theImageDescriptorPtr->colorMap[i][CM_BLUE] = buf[2] ;
		}
	} else {
		/* otherwise copy the global color table, and its size as well */
		copyGIFColorTable(&theLogicalScreenPtr->colorMap, &theImageDescriptorPtr->colorMap);
		theImageDescriptorPtr->localColorTableSize = theLogicalScreenPtr->globalColorTableSize;
	}

	theLogicalScreenPtr->colorCount = theImageDescriptorPtr->localColorTableSize;

	theImageDescriptorPtr->curPass = 0;
	theImageDescriptorPtr->curLine = 0;

    if ( !feof( infile ) )
	return GIF_SUCCESS;	/* success! */
    else
    {
	return GIF_EOF;
    }
}






/* IMPORT INT out_line(pixels, linelen)
 *     UBYTE pixels[];
 *     INT linelen;
 *
 *   - This function takes a full line of pixels (one byte per pixel) and
 * displays them (or does whatever your program wants with them...).  It
 * should return zero, or negative if an error or some other event occurs
 * which would require aborting the decode process...  Note that the length
 * passed will almost always be equal to the line length passed to the
 * decoder function, with the sole exception occurring when an ending code
 * occurs in an odd place in the GIF file...  In any case, linelen will be
 * equal to the number of pixels passed...
 */

#pragma segment graphics
INT out_line(UTINY pixels[], INT linelen)
{
	register short	i;
	register short	j;
	register gif_pixel *qdptr;
/*
	register gif_pixel *rptr, *gptr, *bptr, *qdptr;
	RGBColor	pixelColor;
	short	startColor;
	short	startPixel;
*/
	LOCALWORD	thePass;
	LOCALWORD	theLine;
	LOCALWORD	numLines;
	INT		linenum;
	INT		amtDone;
	float		percent = 0.0;
	long	startPos;
	char MMUMode = 1;		/* 1 = 32 bit mode */

	if (!theImageDescriptorPtr->interlaceFlag) {			/* not doing interlacing */

		/* use the curPass variable to make sure we init curLine */

		if (theImageDescriptorPtr->curPass != 0) {
			theImageDescriptorPtr->curLine = theImageDescriptorPtr->curLine + 1;
		} else {
			theImageDescriptorPtr->curPass = 1;
			theImageDescriptorPtr->curLine = 0;		/* just to make sure */
		}
		amtDone = theImageDescriptorPtr->curLine;

	} else {						/* doing interlacing */
		thePass = theImageDescriptorPtr->curPass;
		theLine = theImageDescriptorPtr->curLine;
		numLines = theImageDescriptorPtr->imageHeight + theImageDescriptorPtr->topPos;
		
		switch (thePass) {
			case 0:
				thePass = 1;
				theLine = theImageDescriptorPtr->topPos /* + 0*/;
				amtDone = 0;
				break;
			case 1:
				theLine = theLine + 8;
				if (theLine >= numLines) {
					theLine = theImageDescriptorPtr->topPos + 4;
					thePass = 2;
				}
				amtDone = ((theLine + 4) / 16) + (numLines / 16);
				break;
			case 2:
				theLine = theLine + 8;
				if (theLine >= numLines) {
					theLine = theImageDescriptorPtr->topPos + 2;
					thePass = 3;
				}
				amtDone = ((theLine + 4) / 8) + (numLines / 8);
				break;
			case 3:
				theLine = theLine + 4;
				if (theLine >= numLines) {
					theLine = theImageDescriptorPtr->topPos + 1;
					thePass = 4;
				}
				amtDone = ((theLine + 2) / 4) + (numLines / 4);
				break;
			case 4:
				theLine = theLine + 2;
				if (theLine > numLines) {
					theLine = numLines;
#ifdef DEBUGSTR
					DEBUGSTR("\pToo many lines in nextLine");
#endif
				}
				amtDone = ((theLine + 1) / 2) + (numLines / 2);
				break;
			default:
#ifdef DEBUGSTR
				DEBUGSTR("\pUnknown pass number in nextLine");
#endif
				break;
			}	/* switch */
			theImageDescriptorPtr->curPass = thePass;
			theImageDescriptorPtr->curLine = theLine;
		}	/* else interlacing */


	linenum = theImageDescriptorPtr->curLine;

	linelen = linelen - 1;


		/*mec*/
		startPos = (long) (linenum) * (long) (GIFppm->rowBytes & 0x7FFF) + (long)0;
		qdptr = (gif_pixel *) &(GIFppm->baseAddr[startPos]);
/*		qdptr = (gif_pixel *) &(GIFppm->baseAddr[(long) (linenum) * (long) (GIFppm->rowBytes & 0x7FFF) + (long)0]); */
		/*
		 * Note qdptr may be pointing off to memory out on a 32 bit NuBus card.
		 * If so, then the MMU must be swapped into 32 bit mode to access the
		 * memory.  Leave it to Spencer to turn this up...
		 */
#ifdef DOMMUSWAP
		SwapMMUMode( &MMUMode );
#endif
				
		/* 8 bits per pixel */

		for (i = 0; i <= linelen; i++)
		{
			j = pixels[i];
			*qdptr++ = j;
			theImageDescriptorPtr->colorMapUsage[j]++;
		}

#ifdef DOMMUSWAP
		SwapMMUMode( &MMUMode );
#endif


		if (GIFthermometer && ((amtDone % 10) == 0)) {
			percent = (float) (0.0 + ((double) (amtDone)) / (double) (theImageDescriptorPtr->imageHeight));
			dlog_thermometer(show,&GIFth_handle,percent," " );
			wind_set_cursor((gWatchCursorIndex++ % 8) + kWatchCursorOffset);
		}

	return (noErr);
}



/* add a (counted) colorMap into the color collection - will sort later */
#pragma segment graphics
short addColorMap(unsigned char newColorMap[MAXCOLORMAPSIZE][3], long	newColorUsage[MAXCOLORMAPSIZE])
{
	register short		i;
	register short		j;
	register short		k;
	short				newColor;		/* is the current color "unique" */

	k = theLogicalScreenPtr->allColorCount;		/* next available slot */
	
	for (i = 0; (i < MAXCOLORMAPSIZE); i++) {		/* check all entries */
		newColor = 1;

		for (j = 0; (j < k); j++) {		/* check against all existing colors */
			if (newColorMap[i][CM_RED] != theLogicalScreenPtr->allColorRed[j]) continue;
			if (newColorMap[i][CM_GREEN] != theLogicalScreenPtr->allColorGreen[j]) continue;
			if (newColorMap[i][CM_BLUE] != theLogicalScreenPtr->allColorBlue[j]) continue;
			if (newColorUsage[i] > 0) {

			theLogicalScreenPtr->allColorUsage[j] = theLogicalScreenPtr->allColorUsage[j] + newColorUsage[i] ;
			}
			newColor = 0;
			j = k;
		}

		if (newColorUsage[i] > 0) {
				if ((newColor) && (k < MAXBIGMAPSIZE)) {
					theLogicalScreenPtr->allColorRed[k] = newColorMap[i][CM_RED] ;
					theLogicalScreenPtr->allColorGreen[k] = newColorMap[i][CM_GREEN] ;
					theLogicalScreenPtr->allColorBlue[k] = newColorMap[i][CM_BLUE] ;
					theLogicalScreenPtr->allColorUsage[k] = theLogicalScreenPtr->allColorUsage[k] + newColorUsage[i] ;
					k++;
				}
		}
	}
	theLogicalScreenPtr->allColorCount = k;		/* next available slot */

	return(k);
}




/* sort a colorMap */
#pragma segment graphics
short sortAllColorMap()
{
	register short		i;
	register short		j;
	unsigned char		tempRed;
	unsigned char		tempGreen;
	unsigned char		tempBlue;
	long				tempUsage;
	
	for (i = (theLogicalScreenPtr->allColorCount - 1); (i > 3); i--) {		// check all entries
		for (j = 2; (j < i); j++) {		// check adjacent entries, ignoring black & white
			// simple bubble-sort to migrate the most-often used colors to the beginning
			// of the color array if the usage of "j" is less than the usage of "j+1" then
			// swap the usages and colors themselves
			if (theLogicalScreenPtr->allColorUsage[j] < theLogicalScreenPtr->allColorUsage[j+1]) {
				tempRed   = theLogicalScreenPtr->allColorRed[j] ;
				tempGreen = theLogicalScreenPtr->allColorGreen[j] ;
				tempBlue  = theLogicalScreenPtr->allColorBlue[j] ;
				tempUsage = theLogicalScreenPtr->allColorUsage[j];
				
				theLogicalScreenPtr->allColorRed[j] = theLogicalScreenPtr->allColorRed[j+1];
				theLogicalScreenPtr->allColorGreen[j] = theLogicalScreenPtr->allColorGreen[j+1];
				theLogicalScreenPtr->allColorBlue[j] = theLogicalScreenPtr->allColorBlue[j+1];
				theLogicalScreenPtr->allColorUsage[j] = theLogicalScreenPtr->allColorUsage[j+1];
				
				theLogicalScreenPtr->allColorRed[j+1] = tempRed ;
				theLogicalScreenPtr->allColorGreen[j+1] = tempGreen ;
				theLogicalScreenPtr->allColorBlue[j+1] = tempBlue ;
				theLogicalScreenPtr->allColorUsage[j+1] = tempUsage ;
			}
		}
	}

	return(theLogicalScreenPtr->allColorCount);
}


 
/*
 * Find the "best" color screen (with the most colors)
 */
#pragma segment graphics
Boolean ColorScreen( Rect * best )
{
	GDHandle QDDev;
	Boolean isMainScreen;
	int bestScreen = 0;

	QDDev = GetDeviceList();
	isMainScreen = TestDeviceAttribute(QDDev, mainScreen); 
	while (QDDev)
	{
		if ((*((*QDDev)->gdPMap))->pixelSize >= bestScreen)
		{
			bestScreen = (*((*QDDev)->gdPMap))->pixelSize;
			*best = (*QDDev)->gdRect;
			isMainScreen = TestDeviceAttribute(QDDev, mainScreen); 
		}
		QDDev = GetNextDevice( QDDev );
	}
	return( isMainScreen );

#ifdef UNDEF		
	QDDev = GetDeviceList();
	isMainScreen = (*QDDev)->gdFlags & (1<<mainScreen); 
	while (QDDev)
	{
		if ((*((*QDDev)->gdPMap))->pixelSize >= bestScreen)
		{
			bestScreen = (*((*QDDev)->gdPMap))->pixelSize;
			*best = (*QDDev)->gdRect;
			isMainScreen = (*QDDev)->gdFlags & (1<<mainScreen); 
		}
		QDDev = GetNextDevice( QDDev );
	}
	return( isMainScreen );
#endif
}

/*
 * Find the depth of the "best" color screen (with the most colors)
 */
#pragma segment graphics
short MaxScreenDepth()
{
	GDHandle QDDev;
	short bestScreen = 0;
		
	QDDev = GetDeviceList();
	while (QDDev)
	{
		if ((*((*QDDev)->gdPMap))->pixelSize >= bestScreen)
		{
			bestScreen = (*((*QDDev)->gdPMap))->pixelSize;
		}
		QDDev = GetNextDevice( QDDev );
	}
	return( bestScreen );
}

/*
 * Create a window on the most colorful screen.
 */
#pragma segment graphics
DocumentPeek 
MakeWindow(int width, int height, char * title)
{
	Rect r, best;
	static int vOffset = 40, hOffset = 20, rowcount = 1;
	WindowPtr win;

	Ptr			 storage;
	Boolean		 mainIsBest;


	storage = NewPtr(sizeof(DocumentRecord));
	if ( storage != nil ) {
		C2PStr( title );
		if (QD32Available)
			mainIsBest = ColorScreen( &best ) ;
		else {
			best = qd.screenBits.bounds;
			mainIsBest = true;
		}

		// base the window size on the Offsets, width, height, and enough pixels for the
		// scroll bars
		width = hOffset + width + (kScrollbarWidth - 1);
		height = vOffset + height + (kScrollbarWidth - 1);
		SetRect( &r, hOffset, vOffset, width, height );

		// move the window onto the "best" screen (the one with the deepest pixel depth)
		OffsetRect( &r, best.left, best.top );

		// if the image is bigger than the "best" screen then take the offsets back
		// out and put in the upper left corner of the "best" screen
		if ((width > (best.right - best.left)) || (height > (best.bottom - best.top))) {
			r.left = best.left + 2;					// two pixels for a border
			r.right = best.left + (width - 2) - hOffset;		// pull the offset back out, two pixel border
			r.top = best.top + 2;					// two pixel border
			r.bottom = best.top + (height - 2) - vOffset;		// pull the offset back out, two pixel border
			if (mainIsBest)							// if the "best" screen is the main screen
				r.top = r.top + DEFAULT_MENU_TOP;	//	then leave room for the menu bar
			else
				r.top = r.top + 18;					//	magic number for titlebar height

		} 

		if (r.right > best.right)
			r.right = best.right - 2;
		if (r.bottom > best.bottom)
			r.bottom = best.bottom - 2;

		if (QD32Available) {
			win = (WindowPtr) NewCWindow( storage, &r, title,
								true, documentProc, (WindowPtr) -1L, true, 0L );
		} else {
			win = (WindowPtr) NewWindow( storage, &r, title,
								true, documentProc, (WindowPtr) -1L, true, 0L );
		}

		(((WindowPeek) win) -> windowKind) = gif;


		P2CStr( title );	/* Munge it back... */
		if ( win == nil ) {
			gif_error( "Couldn't create window - out of memory" );
			return (DocumentPeek)nil;
		} else {
			// Set up the window position for the next window.  This staggers
			// the windows as they're opened
			hOffset += 20;
			vOffset += 20;
			if (vOffset > 200)
			{
				rowcount++;
				vOffset = 20 + rowcount * 20;
				hOffset = rowcount * 20;
				if (hOffset > 300) hOffset = 20;
			}	 
		}
	}
	
	return (DocumentPeek)storage;
}



/* 
 * Copy an Offscreen gworld to an onscreen window
 */
#pragma segment graphics
void CopyGWtoWindow( GWorldPtr gw, WindowPtr window )
{
	RGBColor savefg, savebg, c;
	Rect srcRect, dstRect;
	GrafPtr savePort;
	short		vValue, hValue;
	PixMapHandle myPixMapHandle;
	short		theMode;
	
	vValue = MaxScreenDepth();
	if (vValue > 7) theMode = srcCopy;
	if (vValue < 8) theMode = ditherCopy;
	
	vValue = GetCtlValue(((DocumentPeek)window)->docVScroll);
	hValue = GetCtlValue(((DocumentPeek)window)->docHScroll);
	
	GetForeColor( &savefg );		/* Robert sez ya gotta do this */
	GetBackColor( &savebg );
	c.red = c.green = c.blue = 0;
	RGBForeColor( &c );
	c.red = c.green = c.blue = 0xFFFF;
	RGBBackColor( &c );
	GetPort( &savePort );
	SetPort( window );

	if (System7orBetter)			// Get the PixMap pointer the approved way
		myPixMapHandle = GetGWorldPixMap(gw);
	else
		myPixMapHandle = gw->portPixMap;

	srcRect = (*(myPixMapHandle))->bounds;

	dstRect = window->portRect;

	
	/* take away room for the scroll bars, leaving the last pixel */
	dstRect.right -= (kScrollbarWidth - 1);
	dstRect.bottom -= (kScrollbarWidth - 1);

/* mec */
	srcRect.top = vValue;
	srcRect.left = hValue;
	srcRect.bottom = srcRect.top + (dstRect.bottom - dstRect.top);
	srcRect.right = srcRect.left + (dstRect.right - dstRect.left);

	if ( LockPixels( myPixMapHandle ))
	{
	    CopyBits( (BitMap *) *myPixMapHandle, (BitMap *) &(window->portBits), 
	    		  &srcRect, &dstRect, /*ditherCopy*/ theMode, NULL );
    }
 	UnlockPixels( myPixMapHandle );
 	SetPort( savePort );
 	
	RGBForeColor( &savefg );
	RGBBackColor( &savebg );
}

#pragma segment graphics
void GIF_Draw(DocumentPeek window)
{
	if (QD32Available) {
		CopyGWtoWindow( window->gw, (WindowPtr)window );
	} else {
		GrafPtr savePort;
		
		GetPort(&savePort);
		SetPort((WindowPtr)window);
		BitMapToWind(window);
		
		SetPort(savePort);
	}
}

/*
 * Create a window for displaying an image.  GIFPict's
 * info is stashed in the WRefCon.
 */
#pragma segment graphics
void
CreateGIFWindow( GWorldPtr gw, char * name)
{
	DocumentPeek imgWin;
	PaletteHandle myPalette;
	register short		i;
	CTabHandle	tempCtabHandle = NULL;
	Rect 		aRect;

	imgWin = MakeWindow( gw->portRect.right - gw->portRect.left, 
					  gw->portRect.bottom - gw->portRect.top, name );
	if (!imgWin) {
		gif_error( "Can't create image window - out of memory" );
		return;
	}

	imgWin->gw = gw;

	imgWin->docTE = nil;
	imgWin->docVScroll = nil;
	imgWin->docHScroll = nil;
	imgWin->docClik = nil;

	if (!QD32Available) {
		imgWin->imageType = kBitmapBased;	/* Bitmap-based */
	} else {
		imgWin->imageType = kGWorldBased;	/* GWorld-based */
	
		myPalette = GetPalette((WindowPtr) imgWin);
		if (myPalette) {
			gif_error("* - window already had a palette, getting rid of it.");
			DisposePalette (myPalette);
		}
	
	
		// Create a new Color Table and populate it with color information
		tempCtabHandle = (CTabHandle) 
							NewHandle( sizeof( ColorTable ) + sizeof( ColorSpec ) * theLogicalScreenPtr->allColorCount );
		if ( !tempCtabHandle )
		{
			gif_error( "No memory for Color Table" );
			GIF_CloseWindow((WindowPtr)imgWin);
			return;
		}
	
		// Populate the ColorTable
		HLock( (Handle)tempCtabHandle );			/* I hate handles */
		(*tempCtabHandle)->ctSeed = GetCTSeed();
		(*tempCtabHandle)->ctFlags = 0x8000;			/* this is a device... */
		(*tempCtabHandle)->ctSize = theLogicalScreenPtr->allColorCount - 1;
		
		for (i = 0; i < theLogicalScreenPtr->allColorCount; i++)
		{
			(*tempCtabHandle)->ctTable[i].value = i;
			(*tempCtabHandle)->ctTable[i].rgb.red = theLogicalScreenPtr->allColorRed[i] << 8;
			(*tempCtabHandle)->ctTable[i].rgb.green = theLogicalScreenPtr->allColorGreen[i] << 8;
			(*tempCtabHandle)->ctTable[i].rgb.blue = theLogicalScreenPtr->allColorBlue[i] << 8;
		}
		HUnlock( (Handle)tempCtabHandle );
	
	
		// Create a new palette, copying our color table into it 
		myPalette = NewPalette(theLogicalScreenPtr->allColorCount, tempCtabHandle,
			pmTolerant, 0x1000);	/* $5000 is a magic cookie from IM V */
	
		/* CTable was copied into window, so get rid of our copy */
		// this is not really a CTable - just a data block we interpret as one
		if (tempCtabHandle)
			DisposeHandle( (Handle)tempCtabHandle );
	
		SetPalette((WindowPtr) imgWin, myPalette, true);
	
		myPalette = GetPalette((WindowPtr) imgWin);
		if (!myPalette) {
			gif_error( "Unable to create Palette for window." );
			GIF_CloseWindow((WindowPtr)imgWin);
			return;
		}
		
		ActivatePalette((WindowPtr) imgWin);

	}
		
	imgWin->docVScroll = NewControl((WindowPtr) imgWin, &aRect, "", true,
									0, 0, 0, scrollBarProc, 0);
	imgWin->docHScroll = NewControl((WindowPtr) imgWin, &aRect, "", true,
									0, 0, 0, scrollBarProc, 0);

	if ((imgWin->docVScroll) && (imgWin->docHScroll)) {		// If we got both controls
		AdjustGIFScrollbars((WindowPtr) imgWin, true);
	} else {
		gif_error( "Error adding scroll bars to window");
		GIF_CloseWindow((WindowPtr)imgWin);
		return;
	}

	GIF_Draw(imgWin);
	
	DrawControls((WindowPtr)imgWin);
	DrawGrowIcon((WindowPtr)imgWin);

}


#pragma segment graphics
short
getPixel ( long offset )
{
	gif_pixel aPixel;

	aPixel = GIFppm->baseAddr[offset];
	return (short)aPixel;
}

#pragma segment graphics
void
xsetPixel ( long offset, short thePixel)
{
	if (thePixel > 255) thePixel = 255;
	if (thePixel < 0) thePixel = 0;
	GIFppm->baseAddr[offset] = (unsigned char)thePixel;
}

//
//////////////////////////////////////////////////////////////////////////
//
// function setBit - set the _bit_ at position offset within a the bitmap
//	pointed to by whichPort.
//
//
#pragma segment graphics
void
setBit ( Ptr baseAddr, long offset)
{
	long byteOff, bitOff;
	short theThing;
	long filler;
	
	filler = offset;
	theThing = filler;
	byteOff = offset >> 3;
	bitOff = 7 - (offset & 7);
	theThing = 1 << bitOff;
	baseAddr[byteOff] |= theThing;
//	baseAddr[offset >> 3] |= (1 << (offset & 7));
}

#pragma segment graphics
void
addErrToPixel ( long offset, short theError)
{
	gif_pixel inPixel;
	short outPixel;

	inPixel = GIFppm->baseAddr[offset];
	outPixel = (short)inPixel + theError;
	if (outPixel > 255) outPixel = 255;
	if (outPixel < 0) outPixel = 0;
	GIFppm->baseAddr[offset] = (unsigned char)outPixel;
}


#pragma segment graphics
void
doDither(GrafPtr offscreen)
{
	register long i,linenum, rows, columns;
	float		percent = 0.0;
	char MMUMode = 1;		/* 1 = 32 bit mode */
	register gif_pixel *qdptr;
	double	tempDouble;
	gif_pixel aPixel;
	short	blackest, whitest, medGrey;
	short 	thePixel;
	short	theErr;
	long	inRowOffset, inRowOffset1;
	long	outRowOffset, outRowOffset1;
	Ptr		bitBase;

/*
	unsigned char greymap[256] = {
	// 00 - 0f
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	// 10 - 1f
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	// 20 - 2f
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
	0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
	// 30 - 3f
	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
	0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
	// 40 - 4f
	0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21,
	0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x27, 0x28,
	// 50 - 5f
	0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	// 60 - 6f
	0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
	0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
	// 70 - 7f
	0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
	0x50, 0x51, 0x52, 0x53, 0x55, 0x56, 0x57, 0x58,
	// 80 - 8f
	0x59, 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
	0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6a, 0x6b,
	// 90 - 9f
	0x6c, 0x6e, 0x70, 0x72, 0x73, 0x74, 0x76, 0x78,
	0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88,
	// a0 - af
	0x8a, 0x8c, 0x8f, 0x91, 0x93, 0x95, 0x98, 0x9a,
	0x9c, 0x9f, 0xa1, 0xa4, 0xa6, 0xa9, 0xab, 0xae,
	// b0 - bf
	0xb0, 0xb2, 0xb3, 0xb5, 0xb7, 0xb9, 0xba, 0xbc,
	0xbd, 0xbe, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca,
	// c0 - cf
	0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd9, 0xdb,
	0xdd, 0xe0, 0xe3, 0xe6, 0xe8, 0xeb, 0xed, 0xef,
	// e0 - ef
	0xf2, 0xf5, 0xf8, 0xfb, 0xfe, 0xfe, 0xfe, 0xfe,
	0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
	// f0 - ff
	0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
	0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
	};
*/

	bitBase = offscreen->portBits.baseAddr;

	if (GIFthermometer)
		dlog_thermometer(close,&GIFth_handle,0.0,"");

	dlog_thermometer(init,&GIFth_handle,0.0,"\pDithering Image...");


	blackest = 255;
	whitest = 0;

	/* grey-out the color map */
	for (i = 0;(i < theImageDescriptorPtr->localColorTableSize);i++) {
		tempDouble = ((double)theImageDescriptorPtr->colorMap[i][CM_RED] * 0.30) +
					((double)theImageDescriptorPtr->colorMap[i][CM_GREEN] * 0.59) +
					((double)theImageDescriptorPtr->colorMap[i][CM_BLUE] * 0.11);

		if (tempDouble > 255.0) tempDouble = 255.0;

		medGrey = (unsigned char)tempDouble ;
/*
		medGrey = greymap[medGrey];
*/
		
		if (medGrey < blackest) blackest = medGrey;
		if (medGrey > whitest) whitest = medGrey;

		/* use the red part of the RGB only */
		theImageDescriptorPtr->colorMap[i][CM_RED] = medGrey ;
	}

	medGrey = (blackest + whitest) / 2;

#ifdef DOMMUSWAP
	SwapMMUMode( &MMUMode );
#endif

	/* first pass - colormap entry to grey value */

	rows = theLogicalScreenPtr->height - 1;
	for (linenum = 0; linenum <= (rows); linenum++)
	{
		if (GIFthermometer && ((linenum % 10) == 0)) {
			percent = ((float) (0.0 + ((double) (linenum)) / (double) (theImageDescriptorPtr->imageHeight))) * 0.25;
			dlog_thermometer(show,&GIFth_handle,percent," " );
			wind_set_cursor((gWatchCursorIndex++ % 8) + kWatchCursorOffset);
		}

		qdptr = (gif_pixel *) &(GIFppm->baseAddr[(long) (linenum) * (long) (GIFppm->rowBytes & 0x7FFF) + (long)0]);

		for (i = 0; i < (theLogicalScreenPtr->width); i++)
		{
			aPixel = *qdptr;
			aPixel = theImageDescriptorPtr->colorMap[aPixel][CM_RED];
			*qdptr++ = aPixel;
		}
	}

	/* second pass - do the dithering */
	columns = theLogicalScreenPtr->width - 1;

	for (linenum = 0; (linenum < rows); linenum++)
	{
		if (GIFthermometer && ((linenum % 10) == 0)) {
			percent = 0.25 + (((float) (0.0 + ((double) (linenum)) / (double) (theImageDescriptorPtr->imageHeight))) * 0.75);
			dlog_thermometer(show,&GIFth_handle,percent," " );
			wind_set_cursor((gWatchCursorIndex++ % 8) + kWatchCursorOffset);
		}

		inRowOffset = (long) (linenum) * (long) (GIFppm->rowBytes & 0x7FFF);
		inRowOffset1 = (long) (linenum + 1) * (long) (GIFppm->rowBytes & 0x7FFF);

		// the output array is a BITmap - so 8 pixels fit per byte
		outRowOffset = 8L * (long) (linenum) * (long) (offscreen->portBits.rowBytes & 0x7FFF);
		outRowOffset1 = 8L * (long) (linenum + 1) * (long) (offscreen->portBits.rowBytes & 0x7FFF);

		if (linenum & 1) {
			/* left-to-right */
			/* first pixel */
						
			thePixel = getPixel(inRowOffset);
			if (thePixel > medGrey) {
				theErr = thePixel - whitest;	/* white */
				thePixel = 1;
			} else {
				theErr = thePixel - blackest;	/* black */
				thePixel = 0;
			}
//			setPixel(outRowOffset,thePixel);
			if (!thePixel)
				setBit(bitBase, outRowOffset);
			if (theErr != 0) {
				addErrToPixel(inRowOffset + 1, ((7 * theErr) >> 4));
				addErrToPixel(inRowOffset1 + 1, (theErr >> 4));
				addErrToPixel(inRowOffset1, ((5  * theErr) >> 4));
			}


			/* middle pixels */
			for (i = 1; i < (columns); ++i)
			{
				thePixel = getPixel(inRowOffset + i);
				if (thePixel > medGrey) {
					theErr = thePixel - whitest;	/* white */
					thePixel = 1;
				} else {
					theErr = thePixel - blackest;	/* black */
					thePixel = 0;
				}
//				setPixel(outRowOffset + i,thePixel);
				if (!thePixel)
					setBit(bitBase, outRowOffset + i);
				if (theErr != 0) {
					addErrToPixel(inRowOffset + i+1, ((7 * theErr) >> 4));
					addErrToPixel(inRowOffset1 + i+1, (theErr >> 4));
					addErrToPixel(inRowOffset1 + i,  ((5 * theErr) >> 4));
					addErrToPixel(inRowOffset1 + i-1, ((3 * theErr) >> 4));	/* x + x + x = 3x */
				}
			}

			/* last pixel */
			thePixel = getPixel(inRowOffset + columns);
			if (thePixel > medGrey) {
				theErr = thePixel - whitest;	/* white */
				thePixel = 1;
			} else {
				theErr = thePixel - blackest;	/* black */
				thePixel = 0;
			}
//			setPixel(outRowOffset + columns,thePixel);
			if (!thePixel)
				setBit(bitBase, outRowOffset + columns);
			if (theErr != 0) {
				addErrToPixel(inRowOffset1 + columns, ((5 * theErr) >> 4));
				addErrToPixel(inRowOffset1 + columns-1, ((3 * theErr) >> 4));
			}


		} else {
			/* right-to-left */

			/* first pixel */
			thePixel = getPixel(inRowOffset + columns);
			if (thePixel > medGrey) {
				theErr = thePixel - whitest;	/* white */
				thePixel = 1;
			} else {
				theErr = thePixel - blackest;	/* black */
				thePixel = 0;
			}
//			setPixel(outRowOffset + columns,thePixel);
			if (!thePixel)
				setBit(bitBase, outRowOffset + columns);
			if (theErr != 0) {
				addErrToPixel(inRowOffset + columns-1, ((7 * theErr) >> 4));
				addErrToPixel(inRowOffset1 + columns-1, (theErr >> 4));
				addErrToPixel(inRowOffset1 + columns, ((5 * theErr) >> 4));
			}



			/* middle pixels */
			for (i = (columns - 1); (i > 0); --i)
			{
				thePixel = getPixel(inRowOffset + i);
				if (thePixel > medGrey) {
					theErr = thePixel - whitest;	/* white */
					thePixel = 1;
				} else {
					theErr = thePixel - blackest;	/* black */
					thePixel = 0;
				}
//				setPixel(outRowOffset + i,thePixel);
				if (!thePixel)
					setBit(bitBase, outRowOffset + i);
				if (theErr != noErr) {
					addErrToPixel(inRowOffset + i-1, ((7 * theErr) >> 4));
					addErrToPixel(inRowOffset1 + i-1, (theErr >> 4));
					addErrToPixel(inRowOffset1 + i, ((5 * theErr) >> 4));
					addErrToPixel(inRowOffset1 + i+1, ((3 * theErr) >> 4));
				}
			}

			/* last pixel */
			thePixel = getPixel(inRowOffset);
			if (thePixel > medGrey) {
				theErr = thePixel - whitest;	/* white */
				thePixel = 1;
			} else {
				theErr = thePixel - blackest;	/* black */
				thePixel = 0;
			}
//			setPixel(outRowOffset,thePixel);
			if (!thePixel)
				setBit(bitBase, outRowOffset);
			if (theErr != noErr) {
				addErrToPixel(inRowOffset1, ((5 * theErr) >> 4));
				addErrToPixel(inRowOffset1 + 1, ((3 * theErr) >> 4));
			}


		}
	}


	/* last line */

	linenum = rows;

	inRowOffset = (long) (linenum) * (long) (GIFppm->rowBytes & 0x7FFF);

	// the output array is a BITmap - so 8 pixels fit per byte
	outRowOffset = 8L * (long) (linenum) * (long) (offscreen->portBits.rowBytes & 0x7FFF);

	if (linenum & 1) {
		/* left-to-right */
		/* first pixel */
					
		thePixel = getPixel(inRowOffset);
		if (thePixel > medGrey) {
			theErr = thePixel - whitest;	/* white */
			thePixel = 1;
		} else {
			theErr = thePixel - blackest;	/* black */
			thePixel = 0;
		}
//		setPixel(outRowOffset,thePixel);
		if (!thePixel)
			setBit(bitBase, outRowOffset);
		if (theErr != noErr) {
			addErrToPixel(inRowOffset + 1, ((7 * theErr) >> 4));
		}


		/* middle pixels */
		for (i = 1; i < (columns); ++i)
		{
			thePixel = getPixel(inRowOffset + i);
			if (thePixel > medGrey) {
				theErr = thePixel - whitest;	/* white */
				thePixel = 1;
			} else {
				theErr = thePixel - blackest;	/* black */
				thePixel = 0;
			}
//			setPixel(outRowOffset + i,thePixel);
			if (!thePixel)
				setBit(bitBase, outRowOffset + i);
			if (theErr != noErr) {
				addErrToPixel(inRowOffset + i+1, ((7 * theErr) >> 4));
			}
		}

		/* skip the last pixel */

		/* last pixel */
		thePixel = getPixel(inRowOffset + columns);
		if (thePixel > medGrey) {
			theErr = thePixel - whitest;	/* white */
			thePixel = 1;
		} else {
			theErr = thePixel - blackest;	/* black */
			thePixel = 0;
		}
//		setPixel(outRowOffset + columns,thePixel);
		if (!thePixel)
			setBit(bitBase, outRowOffset + columns);


	} else {
		/* right-to-left */

		/* first pixel */
		thePixel = getPixel(inRowOffset + columns);
		if (thePixel > medGrey) {
			theErr = thePixel - whitest;	/* white */
			thePixel = 1;
		} else {
			theErr = thePixel - blackest;	/* black */
			thePixel = 0;
		}
//		setPixel(outRowOffset + columns,thePixel);
		if (!thePixel)
			setBit(bitBase, outRowOffset + columns);
		if (theErr != noErr) {
			addErrToPixel(inRowOffset + columns-1, ((7 * theErr) >> 4));
		}



		/* middle pixels */
		for (i = (columns - 1); (i > 0); --i)
		{
			thePixel = getPixel(inRowOffset + i);
			if (thePixel > medGrey) {
				theErr = thePixel - whitest;	/* white */
				thePixel = 1;
			} else {
				theErr = thePixel - blackest;	/* black */
				thePixel = 0;
			}
//			setPixel(outRowOffset + i,thePixel);
			if (!thePixel)
				setBit(bitBase, outRowOffset + i);
			if (theErr != noErr) {
				addErrToPixel(inRowOffset + i-1, ((7 * theErr) >> 4));
			}
		}

		/* skip the last pixel */
		/* last pixel */
		thePixel = getPixel(inRowOffset);
		if (thePixel > medGrey) {
			theErr = thePixel - whitest;	/* white */
			thePixel = 1;
		} else {
			theErr = thePixel - blackest;	/* black */
			thePixel = 0;
		}
//		setPixel(outRowOffset,thePixel);
		if (!thePixel)
			setBit(bitBase, outRowOffset);


	}

	if (GIFthermometer) {
		dlog_thermometer(show,&GIFth_handle,1.00," " );
	}
	
#ifdef DOMMUSWAP
	SwapMMUMode( &MMUMode );
#endif
}



/* 
 * GIF to PICT file
 */
#pragma segment graphics
void
GIFtoPICT(Boolean fromFile)
{
    GWorldPtr		mygw;
    char			namestore[256];
    char 			* filename = namestore;
	register long	i,j;
	long			mapSize;
	CTabPtr			macCtab = NULL;
	CTabHandle		macCtabHandle = NULL;
	Rect			r;
	QDErr			err;
	char 			MMUMode = 1;		/* 1 = 32 bit mode */
	short			decodeResult;
	register gif_pixel *qdptr;
	PixMapHandle 	myPixMapHandle;
	Handle			tempPixMapBits;
	GrafPtr			offscreenBitmap;


//#ifdef DEBUGSTR
//	MemoryCheck("New GIF Image");
//#endif

	GIFthermometer = false;
	gPanicMemory = nil;

	gPanicMemory = NewPtr(kPanicMemorySize);

	theLogicalScreenPtr = (GIFLogicalScreenPtr)NewPtr(sizeof(GIFLogicalScreen));
	if ( !theLogicalScreenPtr )
	{
		gif_error( "No memory for Logical Screen" );
		DisposePanicMemory();
		return;
	}

	theImageDescriptorPtr = (GIFImageDescriptorPtr)NewPtr(sizeof(GIFImageDescriptor));
	if ( !theImageDescriptorPtr )
	{
		DisposePanicMemory();
		DisposPtr((Ptr)theLogicalScreenPtr);
		gif_error( "No memory for Image Descriptor" );
		return;
	}
	
	if (fromFile)
	{
	/********************* Open the File *********************************/
		if ( !GIFfileName( "GIF file:", filename, 'GIFf' ) ) {
			DisposePanicMemory();
			DisposPtr((Ptr)theLogicalScreenPtr);
			DisposPtr((Ptr)theImageDescriptorPtr);
			return;
		}
	
		theLogicalScreenPtr->GIF_file = GIFopen( filename, "rb" );
		CHECK( theLogicalScreenPtr->GIF_file, "Can't open GIF file\n" );
		
		gNetGifPtr = NewHandle(INBUFSIZE);
		
		gWatchCursorIndex = -1;
		wind_set_cursor(kWatchCursorOffset);


	/********************* Open the File *********************************/
	} else {
		theLogicalScreenPtr->GIF_file = (FILE *)0;
		strcpy(namestore,gif_title);
	}

	/* reset the counters - unnecessary the first time, but... */
	inBuffCount = 0; 
	inBuffLoc = 0;
	MMUMode = 1;

	/* set all of the colors to black, with no pixels used */
	for (i = 0; i < MAXBIGMAPSIZE; i++)
	{
		theLogicalScreenPtr->allColorRed[i] = 0;
		theLogicalScreenPtr->allColorGreen[i] = 0;
		theLogicalScreenPtr->allColorBlue[i] = 0;
		theLogicalScreenPtr->allColorUsage[i] = 0L;
	}

	/* set color #1 to white */
	theLogicalScreenPtr->allColorRed[1] = 255;
	theLogicalScreenPtr->allColorGreen[1] = 255;
	theLogicalScreenPtr->allColorBlue[1] = 255;

	/* two colors used so far, black and white */
	theLogicalScreenPtr->allColorCount = 2;

/* I need to split out the Image stuff so it can be repeated */
	for (i = 0; i < MAXCOLORMAPSIZE; i++)
	{
		theImageDescriptorPtr->colorMapUsage[i] = 0L;
	}


	err = gif_get_setup();

	/*
	 * Report error if it dies...
	 */
	if (err != GIF_SUCCESS)
	{
		switch ( err )
		{
		case GIF_NOT_GIF:
			gif_error( "Tried to read a non-GIF file" );
			break;
			
		case GIF_EMPTY:
			gif_error( "GIF file was empty" );
			break;
			
		case GIF_EOF:
			gif_error( "GIF header was incomplete" );
			break;
			
		default:
			gif_error( "Error encountered reading GIF file header" );
			break;
		}
		if (fromFile)
			GIFclose( theLogicalScreenPtr->GIF_file );
		DisposePanicMemory();
		DisposPtr((Ptr)theLogicalScreenPtr);
		DisposPtr((Ptr)theImageDescriptorPtr);
		return;
	}


	/* r is the rect of the image */

	r.left = 0;
	r.right = theLogicalScreenPtr->width;
	r.top = 0;
	r.bottom = theLogicalScreenPtr->height;

	// new code to set up for minimum document sizes
	// we need to make sure the Bit/PixMap is at least the minimum window size
	// (MINDOCDIM) less kScrollBarWidth, so growing, scrolling, etc. work
	// properly (otherwise we end up with tiny little windows with scroll bars
	// which look and behave strangely
	if (r.right < (MINDOCDIM - kScrollbarWidth))
		r.right = (MINDOCDIM - kScrollbarWidth);
	if (r.bottom < (MINDOCDIM - kScrollbarWidth))
		r.bottom = (MINDOCDIM - kScrollbarWidth);
	
	
	if (!QD32Available) {
		myPixMapHandle = (PixMapHandle)NewHandle(sizeof(PixMap));

		tempPixMapBits = NewHandle(r.right * r.bottom);

		if (!tempPixMapBits) {
			if ((GetGestaltResult(gestaltOSAttr)) & gestaltRealTempMemory) {
				tempPixMapBits = TempNewHandle((r.right * r.bottom), &err);
			}
		}
		
		offscreenBitmap = nil;
		CreateOffscreenBitMap(&offscreenBitmap, &r);
		if (!offscreenBitmap || (!myPixMapHandle || !tempPixMapBits)) {
			gif_error( "Cannot convert GIF file, no memory for off-screen buffers" );
			if (fromFile)
				GIFclose( theLogicalScreenPtr->GIF_file );
			DisposPtr((Ptr)theLogicalScreenPtr);
			DisposPtr((Ptr)theImageDescriptorPtr);
			if (myPixMapHandle)
				DisposeHandle((Handle)myPixMapHandle);
			if (tempPixMapBits)
				DisposeHandle(tempPixMapBits);
			if (offscreenBitmap)
				DestroyOffscreenBitMap(offscreenBitmap);
			return;
		}

		HLock(tempPixMapBits);
		(*myPixMapHandle)->baseAddr = *tempPixMapBits;
		(*myPixMapHandle)->rowBytes = r.right;
	} else {
	
		/* 
		 * If given a valid color map, convert it into a QuickDraw
		 * color map.
		 */
		if (theLogicalScreenPtr->colorCount > 2)
		{		
	/* create a ColorTable - size based on theImageDescriptorPtr->localColorTableSize */
	
			mapSize = theImageDescriptorPtr->localColorTableSize;
			
			macCtabHandle = (CTabHandle) 
								NewHandle( sizeof( ColorTable ) + sizeof( ColorSpec ) * mapSize );
			if ( !macCtabHandle )
			{
				gif_error( "No memory for color map" );
				DisposPtr((Ptr)theLogicalScreenPtr);
				DisposPtr((Ptr)theImageDescriptorPtr);
				return;
			}
	
	/* initialize the ColorTable */
	
			HLock( (Handle)macCtabHandle );			/* I hate handles */
			macCtab = *macCtabHandle;
			macCtab->ctSeed = GetCTSeed();
			macCtab->ctFlags = 0;			/* Not a device... */
			macCtab->ctSize = mapSize - 1;
	
			for (i = 0; i < mapSize; i++)
			{
				macCtab->ctTable[i].value = i;
				macCtab->ctTable[i].rgb.red = theImageDescriptorPtr->colorMap[i][CM_RED] << 8;
				macCtab->ctTable[i].rgb.green = theImageDescriptorPtr->colorMap[i][CM_GREEN] << 8;
				macCtab->ctTable[i].rgb.blue = theImageDescriptorPtr->colorMap[i][CM_BLUE] << 8;
			}
	
			/* populate the ColorTable */
			if (MaxScreenDepth() == 1) {
				macCtab->ctSize = 1;
				macCtab->ctTable[0].value = 0;
				macCtab->ctTable[0].rgb.red = 0x0000;
				macCtab->ctTable[0].rgb.green = 0x0000;
				macCtab->ctTable[0].rgb.blue = 0x0000;
				macCtab->ctTable[1].value = 1;
				macCtab->ctTable[1].rgb.red = 0xFF00;
				macCtab->ctTable[1].rgb.green = 0xFF00;
				macCtab->ctTable[1].rgb.blue = 0xFF00;
			}
	
			HUnlock( (Handle)macCtabHandle );
		}
		else		/* Black & White image */
		{
	/*
	 * mec - testing 2 color image
	*/
	/* create a ColorTable - size based on theImageDescriptorPtr->localColorTableSize */
	
			mapSize = theImageDescriptorPtr->localColorTableSize;
			
			macCtabHandle = (CTabHandle) 
								NewHandle( sizeof( ColorTable ) + sizeof( ColorSpec ) * mapSize );
			if ( !macCtabHandle )
			{
				gif_error( "No memory for color map" );
				DisposPtr((Ptr)theLogicalScreenPtr);
				DisposPtr((Ptr)theImageDescriptorPtr);
				return;
			}
	
			/* initialize the ColorTable */
	
			HLock( (Handle)macCtabHandle );			/* I hate handles */
			macCtab = *macCtabHandle;
			macCtab->ctSeed = GetCTSeed();
			macCtab->ctFlags = 0;			/* Not a device... */
			macCtab->ctSize = 1;
			macCtab->ctTable[0].value = 0;
			macCtab->ctTable[0].rgb.red = 0x0000;
			macCtab->ctTable[0].rgb.green = 0x0000;
			macCtab->ctTable[0].rgb.blue = 0x0000;
			macCtab->ctTable[1].value = 1;
			macCtab->ctTable[1].rgb.red = 0xFF00;
			macCtab->ctTable[1].rgb.green = 0xFF00;
			macCtab->ctTable[1].rgb.blue = 0xFF00;
	
			HUnlock( (Handle)macCtabHandle );
	
			/*
			 * mec - testing 2 color image
				macCtabHandle = GetCTable( 8 + 32 );	/* Built-in grayscale */
		}
	
		// 
		// Allocate an off-screen buffer ("GWorld" in MacSpeak) for storing the image.
		//
		err = NewGWorld( &mygw, 8, &r, macCtabHandle, NULL, 0 );
		//
		// if the first try failed and we have real temporary memory then try again using
		// temporary memory
		//
		if (err) {
			if ((GetGestaltResult(gestaltOSAttr)) & gestaltRealTempMemory)
				err = NewGWorld( &mygw, 8, &r, macCtabHandle, NULL, useTempMem );
		}

		if (err) {
			gif_error( "Error converting GIF file, no memory for off-screen buffer" );
			if (fromFile)
				GIFclose( theLogicalScreenPtr->GIF_file );
			DisposPtr((Ptr)theLogicalScreenPtr);
			DisposPtr((Ptr)theImageDescriptorPtr);
			return;
		}
		
		/* CTable was copied into gworld, so get rid of our copy */
		if (macCtabHandle)
			DisposCTable( macCtabHandle );
	
		/* get the pixMapHandle the approved way... */
		if (System7orBetter)
			myPixMapHandle = GetGWorldPixMap(mygw);
		else
			myPixMapHandle = mygw->portPixMap;
	
		if (! LockPixels( myPixMapHandle ))
		{
			gif_error( "Can't lock memory for image" );
			if (fromFile)
				GIFclose( theLogicalScreenPtr->GIF_file );
			DisposPtr((Ptr)theLogicalScreenPtr);
			DisposPtr((Ptr)theImageDescriptorPtr);
			DisposeGWorld( mygw );
			return;
		}
	}

	GIFthermometer = true;
	c2pstr( filename );
	dlog_thermometer(init,&GIFth_handle,0.0,filename);
	p2cstr( filename );
	
	HLock( (Handle)myPixMapHandle );
	GIFppm = *(myPixMapHandle);


	/* clear the image */

	/*
	 * Note qdptr may be pointing off to memory out on a 32 bit NuBus card.
	 * If so, then the MMU must be swapped into 32 bit mode to access the
	 * memory.  Leave it to Spencer to turn this up...
	 */
#ifdef DOMMUSWAP
	SwapMMUMode( &MMUMode );
#endif

	qdptr = (gif_pixel *) &(GIFppm->baseAddr[0]);
			
	/* 8 bits per pixel */
	
	for (j = 0; j < theLogicalScreenPtr->height; j++)
	{
		for (i = 0; i < (GIFppm->rowBytes & 0x7FFF); i++)
		{
			*qdptr++ = 0xFF;
		}
	}
#ifdef DOMMUSWAP
	SwapMMUMode( &MMUMode );
#endif


	decodeResult = decoder(theImageDescriptorPtr->imageWidth);
	if (decodeResult != noErr)
	{
		switch ( decodeResult )
		{
		case OUT_OF_MEMORY:
			gif_error( "Out of Memory decoding GIF file" );
			break;
			
		case BAD_CODE_SIZE:
			gif_error( "Bad Code Size decoding GIF file" );
			break;
			
		case READ_ERROR:
			gif_error( "Read Error decoding GIF file" );
			break;
			
		case WRITE_ERROR:
			gif_error( "Write Error decoding GIF file" );
			break;
			
		case OPEN_ERROR:
			gif_error( "Open Error decoding GIF file" );
			break;
			
		case CREATE_ERROR:
			gif_error( "Create Error decoding GIF file" );
			break;
			
		default:
			gif_error( "Unknown Error decoding GIF file" );
			break;
		}
	}


	if (GIFthermometer) {
		dlog_thermometer(show,&GIFth_handle,1.00," " );
		}

	if (decodeResult == noErr) {

		i = addColorMap(theImageDescriptorPtr->colorMap, theImageDescriptorPtr->colorMapUsage);
		i = sortAllColorMap();
	
		if (!QD32Available)
			doDither(offscreenBitmap);

	}	// if decodeResult == noErr

	HUnlock( (Handle)myPixMapHandle );
	if (QD32Available)
		UnlockPixels( myPixMapHandle );

	if (fromFile)
		GIFclose( theLogicalScreenPtr->GIF_file );

	if (GIFthermometer)
		dlog_thermometer(close,&GIFth_handle,0.0,"");

	if (decodeResult == noErr) {	// got a good GIF file, try to display it
		if (QD32Available)			// if we have color then create the window with the new GWorld
			CreateGIFWindow( mygw, filename );
		else {						// for B&W we have some more cleanup to do, then we can
			HUnlock(tempPixMapBits);// create the window with our BitMap
			DisposeHandle(tempPixMapBits);
			if (myPixMapHandle)
				DisposeHandle((Handle)myPixMapHandle);
			CreateGIFWindow( (GWorldPtr)offscreenBitmap, filename );
		}
	} else {						// error decoding - clean up behind ourselves
		if (QD32Available)
			DisposeGWorld( mygw );
		else {
			DisposePtr((*myPixMapHandle)->baseAddr);
			DisposeHandle((Handle)myPixMapHandle);
		}
	}


	DisposPtr((Ptr)theLogicalScreenPtr);
	DisposPtr((Ptr)theImageDescriptorPtr);

	DisposePanicMemory();
}



/*	AdjustGIFScrollValues
 *
 *	Re-calculate the max and the current values for the window's scrollbars.
 *	kScrollTweek compensates for off-by-one requirements of the scrollbars
 *	to have borders coincide with the growbox. 
 */

#pragma segment Main
void AdjustGIFScrollValues(WindowPtr window, Boolean needsResize)
{
	#pragma unused ( needsResize)

	DocumentPeek doc;
	Rect		srcRect,dstRect;
	short		max;
	short		value;
	short		tempShort;

	doc = (DocumentPeek) window;

	dstRect = ((WindowPtr) window)->portRect;
	srcRect = doc->gw->portRect;

	tempShort = dstRect.right - (dstRect.left+kScrollbarWidth) + 1;
	if (tempShort < srcRect.right)
		max = srcRect.right - tempShort;
	else
		max = 0;

	SetCtlMax(doc->docHScroll, max);
	value = GetCtlValue(doc->docHScroll);
	if (value > max)
		SetCtlValue(doc->docHScroll, max);

			
	tempShort = dstRect.bottom - (dstRect.top+kScrollbarWidth) + 1;
	if (tempShort < srcRect.bottom)
		max = srcRect.bottom - tempShort;
	else
		max = 0;

	SetCtlMax(doc->docVScroll, max);
	value = GetCtlValue(doc->docVScroll);
	if (value > max)
		SetCtlValue(doc->docVScroll, max);

 }


/*	AdjustGIFScrollSizes
 *
 *	Re-calculate the position and size of the viewRect and the scrollbars.
 *	kScrollTweek compensates for off-by-one requirements of the scrollbars
 *	to have borders coincide with the growbox. 
 */
#pragma segment Main
void AdjustGIFScrollSizes(WindowPtr window)
{
	DocumentPeek doc;
	
	doc = (DocumentPeek) window;
	MoveControl(doc->docVScroll, window->portRect.right - kScrollbarAdjust, -1);
	SizeControl(doc->docVScroll, kScrollbarWidth, (window->portRect.bottom - 
				window->portRect.top) - (kScrollbarAdjust - kScrollTweek));
	MoveControl(doc->docHScroll, -1, window->portRect.bottom - kScrollbarAdjust);
	SizeControl(doc->docHScroll, (window->portRect.right - 
				window->portRect.left) - (kScrollbarAdjust - kScrollTweek),
				kScrollbarWidth);
} /*AdjustGIFScrollSizes*/

/*
 * AdjustGIFScrollbars
 *
 * Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
 * and we don't want that). If the controls are to be resized as well, call the procedure to do that,
 * then call the procedure to adjust the maximum and current values. Finally re-enable the controls
 * by jamming a $FF in their contrlVis fields. 
 */
#pragma segment Main
void AdjustGIFScrollbars(WindowPtr window, Boolean needsResize)
{
	DocumentPeek doc;
	
	doc = (DocumentPeek) window;
	/* First, turn visibility of scrollbars off so we wonÕt get unwanted redrawing */
	(*doc->docVScroll)->contrlVis = kControlInvisible;	/* turn them off */
	(*doc->docHScroll)->contrlVis = kControlInvisible;
	if ( needsResize ) {
		InvalRect(&(*doc->docVScroll)->contrlRect);			/* invalidate the old scroll bars */
		InvalRect(&(*doc->docHScroll)->contrlRect);
		AdjustGIFScrollSizes(window);						/* move & size as needed */
	}
	AdjustGIFScrollValues(window, needsResize);			/* fool with max and current value */
	/* Now, restore visibility in case we never had to ShowControl during adjustment */
	(*doc->docVScroll)->contrlVis = kControlVisible;	/* turn them on */
	(*doc->docHScroll)->contrlVis = kControlVisible;
} /* AdjustGIFScrollbars */


/*
 *	GIFActionProc:
 *
 * 	Determines how much to change the value of the vertical scrollbar by and how
 *	much to scroll the TE record. 
 */
#pragma segment Main
pascal void GIFActionProc(ControlHandle control, short part)
{
	short		amount;
	WindowPtr	window;
	DocumentPeek doc;
	short		scrollType;
	short		oldValue, newValue, max;
	Rect 		winRect;
	
	
	if ( part != 0 ) {				/* if it was actually in the control */
		window = (*control)->contrlOwner;
		oldValue = GetCtlValue(control);
		
		doc = (DocumentPeek) window;
		if ( control == doc->docVScroll )
			scrollType = 1;
		else
			scrollType = 2;

		switch ( part ) {
			case inUpButton:
			case inDownButton:		/* scroll one scrollBarWidth's worth of pixels */
				amount = kScrollbarWidth;
				break;
			case inPageUp:			/* one page */
			case inPageDown:
				winRect = window->portRect;
				if ( control == doc->docVScroll )
					amount = (winRect.bottom - winRect.top) - (kScrollbarWidth + 1);
				else
					amount = (winRect.right - winRect.left) - (kScrollbarWidth + 1);
				break;
		}
		if ( (part == inUpButton) || (part == inPageUp) )
			amount = -amount;		/* reverse direction for "Up" controls */
		
		/* amount is now the amount to scroll */
		newValue = oldValue + amount;
		max = GetCtlMax(control);
		if (newValue < 0)
			newValue = 0;
		if (newValue > max)
			newValue = max;
		
		amount = newValue - oldValue;
		
		if (amount != 0) {
			SetCtlValue(control, newValue);
			ScrollGIFWindow(window, -amount, scrollType);
		}
			
	}
} /* GIFActionProc */


#pragma segment Main
void ScrollGIFWindow(WindowPtr window, short amount, short scrollType)
{
	GrafPtr		savePort;
	int			absUnits;
	RgnHandle	tmpRgn;
	Rect		theRect;
	
	GetPort(&savePort);
	SetPort(window);
	absUnits = amount < 0 ? -amount : amount;
	
/*	if absUnits > linesVis { /# more than 1 page */
/*		InvalRect /# the whole page */
/*	else {	*/
		/* window rectangle, less scroll bars */
		theRect = window->portRect;
		theRect.right -= (kScrollbarWidth - 1);
		theRect.bottom -= (kScrollbarWidth - 1);
		tmpRgn = NewRgn();
		if (scrollType == 1)	/* vertical scrollbar */
			ScrollRect (&theRect, 0, amount, tmpRgn);
		else
			ScrollRect (&theRect, amount, 0, tmpRgn);
		InvalRgn(tmpRgn);
		DisposeRgn(tmpRgn);
		UpdateGIFWindow(window, scrollType);
		SetPort(savePort);
/*	}	*/
}

UpdateGIFWindow(WindowPtr window, short scrollType)
{
	GrafPtr		savePort;

	GetPort(&savePort);
	SetPort(window);
	BeginUpdate(window);
	if (!scrollType) {
		DrawControls(window);
		DrawGrowIcon(window);
	}
	GIF_Draw((DocumentPeek)window);
	EndUpdate(window);
	SetPort(savePort);
}


//
//////////////////////////////////////////////////////////////////////////////
// CreateOffscreenBitMap - Create an offscreen BitMap.  BitMap size is set by
//			the Rect inBounds.  A GrafPtr to the BitMap is returned in
//			newOffscreen.  The function returns true if the bitmap was created
//			successfully, otherwise false.  This code was taken in toto from
//			Tech Note#120:	Principia Off-Screen Graphics Environments.
//////////////////////////////////////////////////////////////////////////////
//
#pragma segment graphics
Boolean CreateOffscreenBitMap(GrafPtr *newOffscreen, Rect *inBounds)
{
  GrafPtr savePort;
  GrafPtr newPort;

  GetPort(&savePort);    // need this to restore thePort after OpenPort

  newPort = (GrafPtr) NewPtr(sizeof(GrafPort));    // allocate the grafPort
  if (MemError() != noErr)
    return false;                 // failed to allocate the off-screen port
  /*
  the call to OpenPort does the following . . . 
    allocates space for visRgn (set to screenBits.bounds) and clipRgn (set wide open)
    sets portBits to screenBits
    sets portRect to screenBits.bounds
    etc. (see IM I-163,164)
    side effect: does a SetPort(&offScreen)
  */
  OpenPort(newPort);
  // make BitMap the size of the bounds that caller supplied
  newPort->portRect = *inBounds;
  newPort->portBits.bounds = *inBounds;
  RectRgn(newPort->clipRgn, inBounds);    // avoid wide-open clipRgn, to be safe
  RectRgn(newPort->visRgn, inBounds);     // in case newBounds is > screen bounds

  // rowBytes is size of row, it must be rounded up to an even number of bytes
  newPort->portBits.rowBytes = ((inBounds->right - inBounds->left + 15) >> 4) << 1;

  // number of bytes in BitMap is rowBytes * number of rows
  // see notes at end of Technical Note about using _NewHandle rather than _NewPtr
  newPort->portBits.baseAddr =
           NewPtr(newPort->portBits.rowBytes * (long) (inBounds->bottom - inBounds->top));
  if (MemError()!=noErr) {   // check to see if we had enough room for the bits
    SetPort(savePort);
    ClosePort(newPort);      // dump the visRgn and clipRgn
    DisposPtr((Ptr)newPort); // dump the GrafPort
    return false;            // tell caller we failed
    }
  // since the bits are just memory, let's clear them before we start
  EraseRect(inBounds);     // OpenPort did a SetPort(newPort) so we are ok
  *newOffscreen = newPort;
  SetPort(savePort);
  return true;               // tell caller we succeeded!
}

//
//////////////////////////////////////////////////////////////////////////////
// DestroyOffscreenBitMap - get rid of an off-screen BitMap created by the
// previous function. This code was taken in toto from Tech Note#120:
// Principia Off-Screen Graphics Environments.
//////////////////////////////////////////////////////////////////////////////
//

#pragma segment graphics
void DestroyOffscreenBitMap(GrafPtr oldOffscreen)
{
  ClosePort(oldOffscreen);                       /* dump the visRgn and clipRgn */
  DisposPtr(oldOffscreen->portBits.baseAddr);    /* dump the bits */
  DisposPtr((Ptr)oldOffscreen);                  /* dump the port */
}



//
//////////////////////////////////////////////////////////////////////////////
// BitMapToWind - copy a BitMap into the window.  the BitMap's GrafPtr comes
//		from the window's refcon.  (See WindToBitMap.)
//////////////////////////////////////////////////////////////////////////////
//
#pragma segment graphics
void BitMapToWind(DocumentPeek whichDoc) {
	GrafPtr		offscreen;           // the off-screen BitMap
	short		vValue, hValue;
	Rect		srcRect, dstRect;
	WindowPtr	whichWindow = (WindowPtr)whichDoc;

	// Get the current control settings
	vValue = GetCtlValue(whichDoc->docVScroll);
	hValue = GetCtlValue(whichDoc->docHScroll);

	// get the window's BitMap
	offscreen = (GrafPtr)whichDoc->gw;

	// dstRect is based on the window's portRect
	dstRect = whichWindow->portRect;

	// take away room for the scroll bars
	dstRect.right -= kScrollbarAdjust;
	dstRect.bottom -= kScrollbarAdjust;

	// Base the srcRect (location in the offscreen bitmap) on the current control
	// settings and the size of the destination rectangle
	srcRect.top = vValue;
	srcRect.left = hValue;
	srcRect.bottom = srcRect.top + (dstRect.bottom - dstRect.top);
	srcRect.right = srcRect.left + (dstRect.right - dstRect.left);
		
	// can't draw if no BitMap
	if (offscreen != NULL) {
		CopyBits(&offscreen->portBits, &(*whichWindow).portBits,
			   &srcRect, &dstRect, srcCopy, 0L);

	}
}



#pragma segment graphics
void
GIF_CloseWindow(WindowPtr window) {
	GrafPtr 		oldPort;
	DocumentPeek	theDoc;
	PaletteHandle	myPalette;
	
	theDoc = (DocumentPeek)window;
	
	GetPort( &oldPort );
	SetPort(window);

	// Get rid of the offscreen image
	if (theDoc->imageType == kGWorldBased) {
		DisposeGWorld( theDoc->gw );
		myPalette = GetPalette(window);
		if (myPalette)
			DisposePalette (myPalette);
	} else {
		DestroyOffscreenBitMap((GrafPtr)(theDoc->gw));
	}

	// Close the window, then dispose the Pointer seperately.  This is because
	// the window manager doesn't have a clue about our document records.
	CloseWindow(window);
	DisposePtr((Ptr)window);

	// The following is necessary because of assumptions in the list
	// window display code which I have not tracked down yet. It makes
	// sure that the current port is one techinfo knows about. - mec
	if (window == oldPort) {		// if we WERE the front window
		oldPort = FrontWindow();
		if (oldPort == nil)			// if no windows are currently displayed
			oldPort = gListWindow;	// then use gListWindow
	}
	SetPort(oldPort);

//#ifdef DEBUGSTR
//	MemoryCheck("Window Closed.");
//#endif

}

//
// reset sizeRect.right and sizeRect.bottom to the images's maximum size
// iff that is SMALLER than sizeRect's current values
// GIF_GetSizeRect does not touch the .top or .left fields of tempRect, so it
// may be called either before or after they have been set, but the .bottom
// and .right fields should be set to "reasonable" values - like the current
// screen size or something.
//
#pragma segment graphics
void GIF_GetSizeRect(WindowPtr window, Rect * tempRect)
{
	Rect			gwRect;
	DocumentPeek 	theDoc;
	PixMapHandle	myPixMapHandle;

	theDoc = (DocumentPeek)window;

	// get the size of the PixMap/BitMap
	if (theDoc->imageType == kGWorldBased) {

		if (System7orBetter)			// Get the PixMap pointer the approved way
			myPixMapHandle = GetGWorldPixMap(theDoc->gw);
		else
			myPixMapHandle = ( theDoc->gw )->portPixMap;
	
		gwRect = (*(myPixMapHandle))->bounds;

//		gwRect = (*(( theDoc->gw )->portPixMap))->bounds;
	} else 
		gwRect = (( (GrafPtr)theDoc->gw )->portBits).bounds;

	// Add room for the ScrollBars, and the final pixel
	gwRect.right += (kScrollbarWidth);
	gwRect.bottom += (kScrollbarWidth);

	// fix up tempRect
	//if (tempRect->right > gwRect.right)
		tempRect->right = gwRect.right;
	//if (tempRect->bottom > gwRect.bottom)
		tempRect->bottom = gwRect.bottom;
}


#pragma segment graphics
short NewPictFile(StringPtr fileName)
{
	static Point dlogWhere = { 70, 40 };

	SFReply pictFileInfo;
	Str255 thePrompt;
	OSErr	fileErr;
	short	fileRefNum;

	strcpy(thePrompt, "\pSave PICT file as:");
	SFPutFile( dlogWhere, thePrompt, fileName, NULL, &pictFileInfo );
	if (! pictFileInfo.good)
	{
		return ( 0 );
	}

	SetVol( NULL, pictFileInfo.vRefNum ); /* Go to the directory */

	fileErr = Create( pictFileInfo.fName, pictFileInfo.vRefNum, 'ttxt', 'PICT' );
	if ((fileErr != noErr) && (fileErr != dupFNErr))
		return 0;
	
	fileErr = FSOpen((Str255)(pictFileInfo.fName), pictFileInfo.vRefNum, &fileRefNum);
	if (fileErr != noErr)
		return 0;
	SetEOF(fileRefNum, 0);
	strcpy(fileName, pictFileInfo.fName);
	
	return ( fileRefNum );

}

//
// The Apple convention is that the first 512 (kPictHeaderSize) bytes of a
// PICT file are ignored (usually NULL's).
//
#pragma segment graphics
Boolean WritePictHeader(short theRefNum)
{
	short		i;
	long		len;
	unsigned char nulls[kPictHeaderSize];
	OSErr		fileErr;

	for (i = 0; i < kPictHeaderSize; i++) nulls[i] = 0;
	len = kPictHeaderSize;
	fileErr = SetFPos( theRefNum, fsFromStart, 0);
	fileErr = FSWrite( theRefNum, &len, nulls );
	return (fileErr == noErr);
}


#pragma segment graphics
Boolean WritePictData(short theRefNum, PicHandle thePic)
{
	long		len;
	OSErr		fileErr;

	len = GetHandleSize( (Handle)thePic ); /* Don't use (*thePic)->picSize; it's only 16 bits! */
	fileErr = SetFPos( theRefNum, fsFromStart, kPictHeaderSize);
	HLock( (Handle)thePic );
	fileErr = FSWrite( theRefNum, &len, *thePic );
	HUnlock( (Handle)thePic );
	return (fileErr == noErr);
	
}


#pragma segment graphics
void GIFSaveImage(WindowPtr whichWindow)
{
	DocumentPeek	theDoc;
	short			theFileRefNum;
	Str255			theFileName;
	PicHandle		thePicture;
	Boolean			success;
	
    QDErr			err;

	GWorldPtr		gw;
    GDHandle		saveDevice;
    CGrafPtr		saveCPort;
    GWorldPtr		tmpGW;
	
	GrafPtr			savePort;
	GrafPtr			offScreen;           // the off-screen BitMap
	GrafPtr			tmpOff;
	
	PixMapHandle	myPixMapHandle;
	Rect			srcRect;
	
	
	theDoc = (DocumentPeek)whichWindow;
	
	if (!theDoc) {			// can't save if there isn't a window
		SysBeep(1);
		return;
	}

	/*
	 * Creating the quickdraw picture requires making a copy
	 * of the picture in memory.  This pre-flights the operation
	 * so we know (with a reasonable degree of confidence) if it can succede
	 */
	if (theDoc->imageType == kGWorldBased) {
		gw = theDoc->gw;
		 
		err = NewGWorld( &tmpGW, (*(gw->portPixMap))->pixelSize, &gw->portRect, NULL, NULL, 0 );
		if (err) {
			gif_error( "Not enough memory to make a PICT file" );
			return;
		} else
			DisposeGWorld( tmpGW );
	
	} else {	// BitMapBased
		offScreen = (GrafPtr)theDoc->gw;
		
		if (!CreateOffscreenBitMap(&tmpOff, &(offScreen->portBits.bounds))) {
			gif_error( "Not enough memory to make a PICT file" );
			return;
		} else
			DestroyOffscreenBitMap(tmpOff);
	}


	GetWTitle(whichWindow, theFileName);
	theFileRefNum = NewPictFile(&theFileName);
	if (!theFileRefNum) {
		return;
	}
	SetWTitle(whichWindow, theFileName);


	if (theDoc->imageType == kGWorldBased) {
		GetGWorld( &saveCPort, &saveDevice );
		SetGWorld( (CGrafPtr) gw, NULL );
		
		if (System7orBetter)			// Get the PixMap pointer the approved way
			myPixMapHandle = GetGWorldPixMap(gw);
		else
			myPixMapHandle = gw->portPixMap;
	
		srcRect = (*(myPixMapHandle))->bounds;
		
		if ( !LockPixels( myPixMapHandle )) {
			gif_error( "Can't lock pixels in memory" );
			return;
		} else {
			thePicture = OpenPicture( &gw->portRect );
			CopyBits( (BitMap *) *myPixMapHandle, (BitMap *) *myPixMapHandle, 
					  &srcRect, &srcRect, srcCopy, NULL );
			ClosePicture();
			UnlockPixels( myPixMapHandle );
		}
		
		SetGWorld( saveCPort, saveDevice );
	} else {
		GetPort( &savePort );
		SetPort( offScreen );
		thePicture = OpenPicture( &gw->portRect );

		CopyBits(&offScreen->portBits, &offScreen->portBits,
			   &(offScreen->portBits.bounds), &(offScreen->portBits.bounds),
			   srcCopy, 0L);
		ClosePicture();
		SetPort( savePort );
	}

	success = (thePicture != NULL);
	
	if (success)
		success = WritePictHeader(theFileRefNum);

	if (success)
		success = WritePictData(theFileRefNum, thePicture);
	
	success = (noErr == FSClose(theFileRefNum));

	if (thePicture)
		KillPicture( thePicture );

	if (!success) {
		gif_error("Error writing Pict file");
	}
	
}
