/*
 * 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.)
 *
 * Modified June 26, 1992 - fixed the temporary memory problem for B&W images - 
 *		important safety tip - If you NewHandle _DON'T_ DisposePtr.
 *
 * Revised: 6/10/93 - edward
 * #undef feof before redefining it to make MPW C 3.3 happy
 * changed WRITE_ERROR to GIF_WRITE_ERROR is GIFtoPICT so it wouldn't conflict with WRITE_ERROR
 *  defined in pips.h
 *
 * Revised: 7/15/93 - edward
 * removed enum {init,show,close} which is now in pips.h
 *
 * Revision 4.1b3 9/7/93 - edward
 * increased meta from 256 to 1024 chars
 *
 * Revision 4.2WWW 3/7/93 - edward
 * Replaced all references to gif with NT_GIF
 * Included node.h, StandardFile.h
 *
 * Revision 4.2a1 5/2/94 - edward
 * MakeWindow uses wind_set_type to set the window type
 *
 * Revision 4.2a4 7/5/94 - edward
 * Porting to MW C/C++ 68K.  The headers are only included if we're not using MW
 */
 
/*
	Obligatory ugly legal stuff...

This software, data and/or documentation contain trade secrets and 
confidential information which are proprietary to Baylor University. Their use 
or disclosure in whole or in part without the express written permission of 
Baylor University is prohibited.

This software, data and/or documentation are also unpublished works protected  
under the copyright laws of the United States of America.  If these works 
become published, the following notice shall apply:

                     Copyright (C) 1992 Baylor University
                               All Rights Reserved

The name of Baylor University may not be used to endorse or promote products  
derived from this software without specific prior written permission.  

THIS SOFTWARE, DATA AND/OR DOCUMENTATION ARE PROVIDED "AS IS" AND WITHOUT ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

When permission has been granted to make copies of this software, data and/or 
documentation, the above notices must be retained on all copies.

Permission may be granted only to colleges and universities to copy and use 
this software in source code form as a part of TechInfo.  Written requests 
should be made to:
Director, Center for Computing and Information Systems
Dr. D. L. Hardcastle, Director
Baylor University
PO Box 97268
Waco, Tx 76798-7268
HardcastleD@Baylor. edu


 */

// 7/5/94 edward - If we're compiling with MW all of these headers are precompiled in TIFast*
#ifndef __MWERKS__

#ifndef __MEMORY__
#include <Memory.h>
#endif

#ifndef __STDLIB__
#include <StdLib.h>
#endif

#ifndef __STDIO__
#include <stdio.h>
#endif

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

#ifndef __STRINGS__
#include <Strings.h>
#endif

#ifndef __DIALOGS__
#include <Dialogs.h>
#endif

#ifndef __OSUTILS__
#include <OSUtils.h>
#endif

#ifndef __SEGLOAD__
#include <SegLoad.h>
#endif

#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif

#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

#ifndef __DESK__ 
#include <Desk.h>
#endif

#ifndef __MENUS__
#include <Menus.h>
#endif

#include "pips.h"
#ifdef TE32K
#include "TE32K.h"
#endif
#include "documentRec.h"
#include <Lists.h>
#include <StandardFile.h>
#include "prototype.h"
#include "Utilities.h"
#include "node.h"

#endif

#ifndef __QDOFFSCREEN__
#include <QDOffscreen.h>
#endif

#ifndef __PALETTES__
#include <Palettes.h>
#endif

#ifndef __GESTALTEQU__
#include <GestaltEqu.h>
#endif

#include "GIF.h"
#include "GIFstd.h"
#include "GIFerrs.h"

// pips.h is being included by means of including TIFast* in each file.
// pips.h defines WORD, but not in the sense that GIFdecoder.c does
#ifdef __PIPS__
#undef WORD
#define	WORD		LOCALWORD
#endif

// un-comment this for bitmap testing
//#define	QD32Available	(false)

extern	WindowPtr	gListWindow;

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

/* redefine the MPW's feof as we don't use it */
#ifdef feof				/* 6/10/93 - edward begin */
#undef feof
#endif					/* 6/10/93 - edward end */
#define feof(stream)	(0)
/* 
 * START of GIF Stuff
 */

/* GLOBALS!!! */

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

PixMapHandle	GIFpmh;			// was myPixMapHandle
PixMapPtr 	GIFpmp;
Ptr		GIFpmpBaseAddr;		// used in dithering code
DialogPtr	GIFth_handle;
Boolean		GIFthermometer;
Str255 		gif_title;

// Global UPPs
DlgHookUPP gMySFHook;

extern 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;

// globals for the SFPPut dialog stuff
static Boolean	gSavingAsPICT = false;	        // Saving as PICT or GIF
Boolean	gCanSaveAsPICT;	        // Can We Save as PICT
Boolean	gCanSaveAsGIF;	        // Can We Save as GIF

short		gGifBufSize;		// current GIF read buffer size


#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;
WORD decoder(WORD linewidth);

/* END OF GLOBALS */

#ifdef UNDEF

static int IgnoreExtensions(FILE *infile);
static size_t GIFread(unsigned char *ptr, size_t size, size_t nmemb, FILE *stream);
static int GIFeof(FILE *stream);

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(void)
{
	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
void gif_bind(char *buf, int length, 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
void gif_store_image_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(int gif_msg)
{
	Str255	list_buffer;
	Boolean	rc;
	static  char gif_node_id[NODE_LENGTH];
	unsigned char	meta[1024];

	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);
			gGifBufSize = 1024;						// _First_ read is small
			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, NT_GIF );
			wind_set_cursor((gWatchCursorIndex++ % 8) + kWatchCursorOffset);
			gGifBufSize = GIF_BUF_SIZE;				// after _First_ read increase buffer size

			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(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
static size_t GIFread(unsigned char *ptr, size_t, size_t nmemb, FILE *)
{
	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
static int GIFeof(FILE *)
{
	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(void)
{
	if (gPanicMemory) DisposePtr(gPanicMemory);
	gPanicMemory = nil;
}

/*
 * Handle error messages for GIF code.  
 * This is seperate from the standard error handling code because
 * 1) I can make sure it is in a resident segment.
 * 2) I'd like to handle reporting the actual MacOS err codes when possible.
 * 3) I need to deal with the panic_memory problem.
 *
 */
void gif_error( short message_id, short errNum )
{
	short		itemHit;
	Str255		message;
	Str255		tempStr;
	char		errStr[256];
	long		tempLong;

	DisposePanicMemory();

	/* make sure that cursor is in an arrow shape for the alert box */
	wind_set_cursor(0); 
	
	/* get string message from resource */
	GetIndString(message, kGIFStrs, message_id);
	
	if (message[0] == '\0')
		strcpy(message, "\pUnknown error.  Please report circumstances which caused this.");
	
	if (kDontDoNumber != errNum) {		// if the errNum is non-zero
		tempLong = errNum;
		NumToString(tempLong, tempStr);
		P2CStr( tempStr );

		strcpy( errStr, " (" );
		strcat( errStr, tempStr);
		strcat( errStr, ")." );
		C2PStr( errStr );
	} else {
		errStr[0] = '\0';
	}

	ParamText(message, errStr, "", "");
	
	itemHit = NoteAlert ( NOTE_ID, NIL_POINTER );

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

}



/*
 * 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 old_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 old_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);
		p2cstr(tempStr);
		strcpy(msg, "\pMemory error - ");
		strcat(msg, tempStr);
		ParamText( msg, "", "", "" );
		itemHit = Alert( rUserAlert, nil );
	}

	if (saveRErr) {
		tempLong = saveRErr;
		NumToString(tempLong, tempStr);
		p2cstr(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[][3])
{
	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[][3], unsigned char newColorMap[][3])
{
	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[][3], unsigned char newColorMap[][3])
{
	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);
}


static int IgnoreExtensions(FILE *infile)
{
	static char	buf[256];
	unsigned char	count;


	GIFread( buf, 1, 1, infile );  // get the next char - type of extension
	if ( GIFeof( infile ) )
		return GIF_EOF;

	do {
		GIFread( &count, 1, 1, infile );  // get the next char - length
		if ( GIFeof( infile ) )
			return GIF_EOF;
	
		if (count != 0) {
			GIFread( buf, 1, count, infile );  // get the whole block
			if ( GIFeof( infile ) )
				return GIF_EOF;
		}
	} while (count != 0);

	return false;
}




/*****************************************************************
 * 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(void)
{
    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) && (strncmp(theVersion, "89a", 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.	*/


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

	if (buf[0] == ';')		// GIF terminator
		return false;

	if (buf[0] == '!') {	// Extension
		if (IgnoreExtensions(infile))	// ignore ALL of the extensionsl
			return(GIF_NOT_GIF);
	}

	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;

	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
					if (!theLogicalScreenPtr->warningIssued) {
						gif_error( kGIFOutLineErr, kDontDoNumber );
						theLogicalScreenPtr->warningIssued = true;
					}
				}
				amtDone = ((theLine + 1) / 2) + (numLines / 2);
				break;
			default:
#ifdef DEBUGSTR
				DEBUGSTR("\pUnknown pass number in nextLine");
#endif
				if (!theLogicalScreenPtr->warningIssued) {
					gif_error( kGIFOutLineErr, kDontDoNumber );
					theLogicalScreenPtr->warningIssued = true;
				}
				break;
			}	/* switch */
			theImageDescriptorPtr->curPass = thePass;
			theImageDescriptorPtr->curLine = theLine;
		}	/* else interlacing */


	linenum = theImageDescriptorPtr->curLine;

	linelen = linelen - 1;

	if (linenum < 0) {
#ifdef DEBUGSTR
		DEBUGSTR("\pNegative linenum in out_line");
#endif
		if (!theLogicalScreenPtr->warningIssued) {
			gif_error( kGIFOutLineErr, kDontDoNumber );
			theLogicalScreenPtr->warningIssued = true;
		}
	} else if (linenum >= theLogicalScreenPtr->height ) {
#ifdef DEBUGSTR
		DEBUGSTR("\plinenum too big in out_line");
#endif
// skip this one for now.  for some reason the satellite GIFs are triggering it.
// mec - 072092
//		if (!theLogicalScreenPtr->warningIssued) {
//			gif_error( kGIFOutLineErr, kDontDoNumber );
//			theLogicalScreenPtr->warningIssued = true;
//		}
	}
	else
	{

/*mec*/
//		startPos = (long) (linenum) * (long) (GIFpmp->rowBytes & 0x7FFF) + (long)0;
//		qdptr = (gif_pixel *) &(GIFpmp->baseAddr[startPos]);
		qdptr = (gif_pixel *) &(GIFpmp->baseAddr[(long) (linenum) * (long) (GIFpmp->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_thermometer,&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(void)
{
	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(void)
{
	GDHandle QDDev;
	short bestScreen = 0;
		
	QDDev = GetDeviceList();
	while (QDDev)
	{
		if ((*((*QDDev)->gdPMap))->pixelSize >= bestScreen)
		{
			bestScreen = (*((*QDDev)->gdPMap))->pixelSize;
		}
		QDDev = GetNextDevice( QDDev );
	}
	return( bestScreen );
}

//
///////////////////////////////////////////////////////////////////////////////////
// MakeWindow - 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 = 0;		// initialize to zero - we'll re-init later
	static int hOffset = -18;	// simple initialize - we add 20 later on
	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;
		}
#ifdef UNDEF
		// 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;
#endif
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
		// Set up the window position for the next window.  This staggers
		// the windows as they're opened

		// horizontal case is simple
		hOffset += 20;

		// vertical is a little more complex, have to set the vOffset to be too big
		// to force the vertical reset which follows
		if (vOffset < 1) {
			vOffset = best.bottom - best.top;	// offset the window by the height of the screen
		} else
			vOffset += 20;

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

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

		// add in the current stagger offsets
		OffsetRect( &r, hOffset, vOffset );
		
		// if the window won't fit on the screen as is, try pulling the offsets back out
		// one at a time
		if (r.right > best.right) {				// first check the horizontal fit
			OffsetRect( &r, -hOffset, 0 );		// if no go take the offset back out
			OffsetRect( &r, 2, 0 );				// but leave a 2-pixel border
			hOffset = r.left - best.left;		// reset the horiz offset
		}
		if (r.bottom > best.bottom) {			// next check the vertical fit
			OffsetRect( &r, 0, -vOffset );		// if no go take the offset back out
			OffsetRect( &r, 0, 2 );				// but leave a 2-pixel border
			if (mainIsBest)						// if the "best" screen is the main screen
				OffsetRect( &r, 0, DEFAULT_MENU_TOP );	//	then leave room for the menu bar
			else
				OffsetRect( &r, 0, 18 );		// else leave room for the window's titlebar
			vOffset = r.top - best.top;			// reset the vert offset
		}
		
		// if it _still_ won't fit then make the window smaller (again, a 2-pixel border)
		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 );
		}

		wind_set_type(win, NT_GIF);	/* edward 5/2/94 - maintain the abstraction */

		P2CStr( title );	// Put it back
		if ( win == nil ) {				// just in case, some other kind of error???
			if (storage)
				DisposePtr(storage);
			return (DocumentPeek)nil;
		}
	}
	
	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.  The GIF info is stored in the DocumentRecord's
 * data fields.
 */
#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( kNoMemWind, kDontDoNumber );
		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) {
			old_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( kNoMemCTAB, kDontDoNumber );
			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( kNoPaletteErr, kDontDoNumber );
			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( kScrollBarErr, kDontDoNumber );
		GIF_CloseWindow((WindowPtr)imgWin);
		return;
	}

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

}


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

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

#define getPixel(a1)  (short)(GIFpmpBaseAddr[a1] & 0xff)

#pragma segment graphics
void xsetPixel (long offset, short thePixel)
{
	if (thePixel > 255) thePixel = 255;
	if (thePixel < 0) thePixel = 0;
	GIFpmp->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 xsetBit(Ptr baseAddr, long offset)
{
	baseAddr[offset >> 3] |= (1 << (7 - (offset & 7)));
}

#define setBit(a1, b1) 	a1[b1 >> 3] |= (1 << (7 - (b1 & 7)))

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

	inPixel = GIFpmpBaseAddr[offset];
	outPixel = (short)inPixel + theError;
	if (outPixel > 255)
		GIFpmpBaseAddr[offset] = 255;
	else if (outPixel < 0)
		GIFpmpBaseAddr[offset] = 0;
	else
		GIFpmpBaseAddr[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;
	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;
	GIFpmpBaseAddr = GIFpmp->baseAddr;

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

	dlog_thermometer(init_thermometer,&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;

	/* 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_thermometer,&GIFth_handle,percent," " );
			wind_set_cursor((gWatchCursorIndex++ % 8) + kWatchCursorOffset);
		}

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

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

	/* 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_thermometer,&GIFth_handle,percent," " );
			wind_set_cursor((gWatchCursorIndex++ % 8) + kWatchCursorOffset);
		}

		inRowOffset = (long) (linenum) * (long) (GIFpmp->rowBytes & 0x7FFF);
		inRowOffset1 = (long) (linenum + 1) * (long) (GIFpmp->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 */
			} else {
				theErr = thePixel - blackest;	/* black */
				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 */
				} else {
					theErr = thePixel - blackest;	/* black */
					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 */
			} else {
				theErr = thePixel - blackest;	/* black */
				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 */
			} else {
				theErr = thePixel - blackest;	/* black */
				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 */
				} else {
					theErr = thePixel - blackest;	/* black */
					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 */
			} else {
				theErr = thePixel - blackest;	/* black */
				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) (GIFpmp->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 */
		} else {
			theErr = thePixel - blackest;	/* black */
			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 */
			} else {
				theErr = thePixel - blackest;	/* black */
				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 */
		}
		else
		{
			theErr = thePixel - blackest;	/* black */
			setBit(bitBase, outRowOffset + columns);
		}


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

		/* first pixel */
		thePixel = getPixel(inRowOffset + columns);
		if (thePixel > medGrey) {
			theErr = thePixel - whitest;	/* white */
		}
		else
		{
			theErr = thePixel - blackest;	/* black */
			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 */
			}
			else
			{
				theErr = thePixel - blackest;	/* black */
				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 */
		} else {
			theErr = thePixel - blackest;	/* black */
			setBit(bitBase, outRowOffset);
		}


	}

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



/* 
 * 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;
	gif_pixel		back_color;
//	PixMapHandle 	myPixMapHandle;
	Handle			tempPixMapBits;
	GrafPtr			offscreenBitmap;


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

	GIFthermometer = false;
	gPanicMemory = nil;

	gPanicMemory = NewPtr(kPanicMemorySize);
	if ( !gPanicMemory )
	{
		gif_error( kDecodeNoMem, kDontDoNumber );
		return;
	}

	theLogicalScreenPtr = (GIFLogicalScreenPtr)NewPtr(sizeof(GIFLogicalScreen));
	if ( !theLogicalScreenPtr )
	{
		gif_error( kNoMemLScr, kDontDoNumber );
		DisposePanicMemory();
		return;
	}

	theImageDescriptorPtr = (GIFImageDescriptorPtr)NewPtr(sizeof(GIFImageDescriptor));
	if ( !theImageDescriptorPtr )
	{
		DisposePanicMemory();
		DisposPtr((Ptr)theLogicalScreenPtr);
		gif_error( kNoMemIDes, kDontDoNumber );
		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" );
		if (!(theLogicalScreenPtr->GIF_file)) {
			DisposePanicMemory();
			DisposPtr((Ptr)theLogicalScreenPtr);
			DisposPtr((Ptr)theImageDescriptorPtr);
			gif_error( kNoOpenErr, kDontDoNumber );
			return;
		}
		
		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( kGifNotGif, kDontDoNumber );
			break;
			
		case GIF_EMPTY:
			gif_error( kGifFileEmpty, kDontDoNumber );
			break;
			
		case GIF_EOF:
			gif_error( kHeader2Short, kDontDoNumber );
			break;
			
		default:
			gif_error( kHeaderFileErr, kDontDoNumber );
			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) {
		offscreenBitmap = nil;
		CreateOffscreenBitMap(&offscreenBitmap, &r);

		GIFpmh = (PixMapHandle)NewHandle(sizeof(PixMap));

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

		// this code is supposed to allocate a handle to a block of
		// temporary memory if the normal allocation failed.  Unfortunately
		// I have tracked a system-locking problem back to the use of a locked
		// temporary-memory handle, so this code is gone for now.  mec
		if (!tempPixMapBits) {
			if ((GetGestaltResult(gestaltOSAttr)) & gestaltRealTempMemory) {
				tempPixMapBits = TempNewHandle((r.right * r.bottom), &err);
			}
		}

		if (!offscreenBitmap || (!GIFpmh || !tempPixMapBits)) {
			gif_error( kNoMemOScr, kDontDoNumber );
			if (fromFile)
				GIFclose( theLogicalScreenPtr->GIF_file );
			DisposPtr((Ptr)theLogicalScreenPtr);
			DisposPtr((Ptr)theImageDescriptorPtr);
			if (GIFpmh)
				DisposeHandle((Handle)GIFpmh);
			if (tempPixMapBits)
				DisposeHandle(tempPixMapBits);
			if (offscreenBitmap)
				DestroyOffscreenBitMap(offscreenBitmap);
			return;
		}

		HLock(tempPixMapBits);
		(*GIFpmh)->baseAddr = *tempPixMapBits;
		(*GIFpmh)->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( kNoMemCmap, kDontDoNumber );
				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( kNoMemCmap, kDontDoNumber );
				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( kNoMemOScr, kDontDoNumber );
			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)
			GIFpmh = GetGWorldPixMap(mygw);
		else
			GIFpmh = mygw->portPixMap;
	
		if (! LockPixels( GIFpmh ))
		{
			gif_error( kNoPixelLock, kDontDoNumber );
			if (fromFile)
				GIFclose( theLogicalScreenPtr->GIF_file );
			DisposPtr((Ptr)theLogicalScreenPtr);
			DisposPtr((Ptr)theImageDescriptorPtr);
			DisposeGWorld( mygw );
			return;
		}
	}

	GIFthermometer = true;
	c2pstr( filename );
	dlog_thermometer(init_thermometer,&GIFth_handle,0.0,filename);
	p2cstr( filename );
	
	HLock( (Handle)GIFpmh );
	GIFpmp = *(GIFpmh);


	/* 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 *) &(GIFpmp->baseAddr[0]);
			
	/* 8 bits per pixel in offscreen image */
	
	back_color = theLogicalScreenPtr->backColorIndex;
	
	for (j = 0; j < theLogicalScreenPtr->height; j++)
	{
		for (i = 0; i < (GIFpmp->rowBytes & 0x7FFF); i++)
		{
			*qdptr++ = back_color; 		// was 0xFF;
		}
	}
#ifdef DOMMUSWAP
	SwapMMUMode( &MMUMode );
#endif

	theLogicalScreenPtr->warningIssued = false;
	decodeResult = decoder(theImageDescriptorPtr->imageWidth);
	if (decodeResult != noErr)
	{
		switch ( decodeResult )
		{
		case OUT_OF_MEMORY:
			gif_error( kDecodeNoMem, kDontDoNumber );
			break;
			
		case BAD_CODE_SIZE:
			gif_error( kDecodeBadCode, kDontDoNumber );
			break;
			
		case READ_ERROR:
			gif_error( kDecodeReadErr, kDontDoNumber );
			break;
			
		case GIF_WRITE_ERROR:
			gif_error( kDecodeWriteErr, kDontDoNumber );
			break;
			
		case OPEN_ERROR:
			gif_error( kDecodeOpenErr, kDontDoNumber );
			break;
			
		case CREATE_ERROR:
			gif_error( kDecodeCreatErr, kDontDoNumber );
			break;
			
		default:
			gif_error( kDecodeUnkErr, kDontDoNumber );
			break;
		}
	}


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

	if (decodeResult == noErr) {

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

	}	// if decodeResult == noErr

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

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

	if (GIFthermometer)
		dlog_thermometer(close_thermometer,&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 (GIFpmh)
				DisposeHandle((Handle)GIFpmh);
			CreateGIFWindow( (GWorldPtr)offscreenBitmap, filename );
		}
	} else {						// error decoding - clean up behind ourselves
		if (QD32Available)
			DisposeGWorld( mygw );
		else {
			HUnlock(tempPixMapBits);		// Unlock the handle to the PixMap
			DisposeHandle(tempPixMapBits);
			DisposeHandle((Handle)GIFpmh);
		}
	}


	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)
{
	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);
/*	}	*/
}

void
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 "whichDoc"'s "gw" parameter.
//////////////////////////////////////////////////////////////////////////////
//
#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
pascal short MySFHook(short MySFItem, DialogPtr theDialog)
{
	Handle  itemToChange;	        /*needed for GetDItem and SetCtlValue*/
	Rect	itemBox;		        /*needed for GetDItem*/
	short	itemType;	       		/*needed for GetDItem*/

	switch (MySFItem) {
		case kFirstTime:
			/* before the dialog is drawn, our hook gets called with a -1 (kFirstTime)...*/
			/* as the item so we can change things like button titles, etc. */

			if (!gCanSaveAsPICT) {
				GetDItem(theDialog,kPictFileButton,&itemType,&itemToChange,&itemBox);
				HiliteControl((ControlHandle)itemToChange,255);
				gSavingAsPICT = false;
			} else {
				GetDItem(theDialog,kPictFileButton,&itemType,&itemToChange,&itemBox);
				HiliteControl((ControlHandle)itemToChange,0);
			}

			if (!gCanSaveAsGIF) {
				GetDItem(theDialog,kGIFFileButton,&itemType,&itemToChange,&itemBox);
				HiliteControl((ControlHandle)itemToChange,255);
				gSavingAsPICT = true;
			} else {
				GetDItem(theDialog,kGIFFileButton,&itemType,&itemToChange,&itemBox);
				HiliteControl((ControlHandle)itemToChange,0);
			}

			if (gSavingAsPICT) {
				/*Here we will set the kGIFFileButton to OFF, the kPictFileButton to ON*/
				GetDItem(theDialog,kGIFFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOff);
				GetDItem(theDialog,kPictFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOn);
			} else {
				/*Here we will set the kPictFileButton to OFF, the kGIFFileButton to ON*/
				GetDItem(theDialog,kPictFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOff);
				GetDItem(theDialog,kGIFFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOn);
			}

		
			return MySFItem;	 /*pass back the same item we were sent*/
			break;
		
		/*Here we will turn the kGIFFileButton OFF, the kPictFileButton ON and redraw the list*/
		case kPictFileButton: 
			if (!gSavingAsPICT)
			{
				GetDItem(theDialog,kGIFFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOff);
				GetDItem(theDialog,kPictFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOn);
				gSavingAsPICT=true;
			}  /*if !gSavingAsPICT */
			return MySFItem;
			break;
		
		/*Here we will turn the kPictFileButton OFF, the kGIFFileButton ON and redraw the list*/
		case kGIFFileButton:
			if (gSavingAsPICT)
			 {   
				GetDItem(theDialog,kPictFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOff);
				GetDItem(theDialog,kGIFFileButton,&itemType,&itemToChange,&itemBox);
				SetCtlValue((ControlHandle)itemToChange,kBtnOn);
				gSavingAsPICT=false;
			}  /*if not gSavingAsPICT */
			return MySFItem;	 /*pass back the same item we were sent*/
			break;
		
		/*!!!!!!very important !!!!!!!! We must pass SF's 'standard' item hits back to SF*/
		default:
				return(MySFItem);	/* the item hit was one of SF's standard items... */
	}  /*switch*/
  	return(MySFItem);	/* return what we got */
}  /*MySFHook*/




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

	SFReply pictFileInfo;
	Str255 thePrompt;
	OSErr	fileErr;
	short	fileRefNum;
	OSType	theType;
	OSType	theCreator;
	FInfo	myFInfo;
	Str255	creatorStr;

	strcpy(thePrompt, "\pSave Image As:");
//	SFPutFile( dlogWhere, thePrompt, fileName, NULL, &pictFileInfo );
	SFPPutFile( dlogWhere, thePrompt, fileName, gMySFHook, &pictFileInfo, 600, NULL);
	if (! pictFileInfo.good)
	{
		return ( 0 );
	}

	SetVol( NULL, pictFileInfo.vRefNum ); /* Go to the directory */
	
	if (gSavingAsPICT) {
		theType = 'PICT';
		theCreator = 'ttxt';
	} else {
		// set up defaults
		theType = 'GIFf';
		theCreator = 'GCon';

		GetIndString(creatorStr, kGIFStrs, kGIFCreator);
		if (creatorStr[0] == '\4') {	// 4 - char title
			theCreator = creatorStr[4];
			theCreator += (creatorStr[3] << 8);
			theCreator += (creatorStr[2] << 16);
			theCreator += (creatorStr[1] << 24);
		}

	}

	fileErr = Create( pictFileInfo.fName, pictFileInfo.vRefNum, theCreator, theType );
	if (fileErr != noErr) {
		if (fileErr != dupFNErr)
			return 0;
	} else {
		fileErr = GetFInfo(pictFileInfo.fName,pictFileInfo.vRefNum, &myFInfo);
		if (fileErr != noErr) {
			gif_error( kErrGetFile, fileErr );
			return 0;
		}
		if (gSavingAsPICT) {
			myFInfo.fdType = 'PICT';
			myFInfo.fdCreator = 'ttxt';
		} else {
			myFInfo.fdType = 'GIFf';
			myFInfo.fdCreator = 'GCon';
		}
		fileErr = SetFInfo(pictFileInfo.fName,pictFileInfo.vRefNum, &myFInfo); 
		if (fileErr != noErr) {
			gif_error( kErrSetFile, fileErr );
			return 0;
		}
	}
	
	
	fileErr = FSOpen(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 GIFSaveAsPICT(WindowPtr whichWindow, short theFileRefNum)
{
	DocumentPeek	theDoc;
	PicHandle		thePicture;
	Boolean			success;

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

	if (theDoc->imageType == kGWorldBased)
	{
		gw = theDoc->gw;
		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( kNoPixelLock, kDontDoNumber );
			return;
		}
		else
		{
			thePicture = OpenPicture( &gw->portRect );
			CopyBits( (BitMap *) *myPixMapHandle, (BitMap *) *myPixMapHandle, 
					  &srcRect, &srcRect, srcCopy, NULL );
			ClosePicture();
			UnlockPixels( myPixMapHandle );
		}
		
		SetGWorld( saveCPort, saveDevice );
	}
	else
	{	// BitMapBased
		offScreen = (GrafPtr)theDoc->gw;

		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( kErrPICTWrite, kDontDoNumber );
	}
	
}



#pragma segment graphics
void GIFSaveImage(WindowPtr whichWindow)
{
	DocumentPeek	theDoc;
	short			theFileRefNum;
	Str255			theFileName;

    QDErr			err;
	GWorldPtr		gw;
    GWorldPtr		tmpGW;
	
	GrafPtr			tmpOff;
	GrafPtr			offScreen;           // the off-screen BitMap
		
	
	theDoc = (DocumentPeek)whichWindow;
	
	if (!theDoc) {			// can't save if there isn't a window
		SysBeep(1);
		return;
	}

	gCanSaveAsPICT = true;
	gCanSaveAsGIF = true;

	// Pre-flight the PICT saving as much as possible
	/*
	 * 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) {
			gCanSaveAsPICT = false;
		} else
			DisposeGWorld( tmpGW );
	
	} else {	// BitMapBased
		offScreen = (GrafPtr)theDoc->gw;
		
		if (!CreateOffscreenBitMap(&tmpOff, &(offScreen->portBits.bounds))) {
			gCanSaveAsPICT = false;
		} else
			DestroyOffscreenBitMap(tmpOff);
	}


	gCanSaveAsGIF = PreFlightGIFBuffers();

	if (!gCanSaveAsPICT && !gCanSaveAsGIF) {
		gif_error( kNoMemSave, kDontDoNumber );
		return;
	}


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

	wind_set_cursor(kWatchCursorOffset);		// watch cursor

	UpdateEventLoop();

	if (gSavingAsPICT)
		GIFSaveAsPICT(whichWindow, theFileRefNum);
	else
		GIFSaveAsGIF(whichWindow, theFileRefNum);

	wind_set_cursor(0); 						// back to the arrow
	
}


/*
 *	UpdateEventLoop:
 *
 * Get update events as long as there are some, and handle them by calling DoEvent.
 *
 */
#pragma segment Main
void UpdateEventLoop(void)
{
	Boolean		gotEvent;
	EventRecord	event;

	do {
		/* use WNE if it is available */
		if ( gHasWaitNextEvent )
			gotEvent = WaitNextEvent(updateMask, &event, 0, NULL);
		else {
			SystemTask();
			gotEvent = GetNextEvent(updateMask, &event);
		}
		if ( gotEvent ) {
			/* make sure we have the right cursor before handling the event */
			DoEvent(&event);
		}
	} while ( gotEvent );	/* loop until we didn't get one */
} /*UpdateEventLoop*/

Boolean GIF_SendFile(void)
{
	static Point dlogWhere = { 70, 40 };
	short numTypes = 1;
	SFReply imgFile;
	short	fileRefNum;
	OSErr	fileErr;
	char	* theGIFPtr;
	OSType	theType;
	long	buffLen;
	long	fileLen;
	long	bytesToRead;
	Boolean	returnCode;

	theGIFPtr = NULL;

	theType = 'GIFf';
	
	returnCode = false;		// assume a failure until we get the file open
	
	SFGetFile( dlogWhere, "\pGIF file:", NULL, numTypes, &theType, NULL, &imgFile );
	if (! imgFile.good)
		return( returnCode );

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

	fileErr = FSOpen(imgFile.fName, imgFile.vRefNum, &fileRefNum);
	if (fileErr != noErr) {
		gif_error( kGIFLoadReadErr, fileErr );
		return( returnCode );
	}

	fileErr = GetEOF(fileRefNum, &fileLen);
	if (fileErr != noErr) {
		gif_error( kGIFLoadReadErr, fileErr );
		fileErr = FSClose( fileRefNum );
		return( returnCode );
	}
	
	buffLen = fileLen;
	if (buffLen > GIF_BUF_SIZE)
		buffLen = GIF_BUF_SIZE;

	theGIFPtr = NewPtrClear(buffLen);
	
	if (!theGIFPtr) {
		// not enough memory
		gif_error( kGIFLoadMemErr, kDontDoNumber );
		return( returnCode );
	} else {
		returnCode = true;		// now assume success unless there is a problem
		do {
			bytesToRead = buffLen;
			if (buffLen > fileLen)
				bytesToRead = fileLen;
			fileErr = FSRead(fileRefNum, &bytesToRead, theGIFPtr);
			if (fileErr == noErr) {
				returnCode = (!cmd_send_gif( theGIFPtr, bytesToRead ));		//	cmd_send_gif returns false on success
				if (returnCode)
					fileLen -= bytesToRead;
				else
					gif_error( kGIFLoadReadErr, fileErr );
			} else {
				returnCode = false;
				DisposePtr(theGIFPtr);
				theGIFPtr = NULL;
				// read error
				gif_error( kGIFLoadReadErr, fileErr );
			}
		} while ((fileLen > 0) && (returnCode));
	}

	// we don't touch the returnCode here - the function can complete successfully
	// even if we can't close the file.
	fileErr = FSClose( fileRefNum );
	if (fileErr != noErr) {
		gif_error( kGIFLoadClosErr, fileErr );
	}

	if (theGIFPtr)
		DisposePtr(theGIFPtr);
	return (returnCode);
}

void gif_init_upps(void)
{
	gMySFHook = NewDlgHookProc(MySFHook);
}