/*
	File:		ImageCompressionUtilities.c
	
	Description: Image Compression Utilities

	Author:		Peter Hoddie, Sean Allen, Chris Flick
	Revised by: Tim Monroe

	Copyright: 	© Copyright 1999 Apple Computer, Inc. All rights reserved.
	
	Disclaimer:	IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
				("Apple") in consideration of your agreement to the following terms, and your
				use, installation, modification or redistribution of this Apple software
				constitutes acceptance of these terms.  If you do not agree with these terms,
				please do not use, install, modify or redistribute this Apple software.

				In consideration of your agreement to abide by the following terms, and subject
				to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
				copyrights in this original Apple software (the "Apple Software"), to use,
				reproduce, modify and redistribute the Apple Software, with or without
				modifications, in source and/or binary forms; provided that if you redistribute
				the Apple Software in its entirety and without modifications, you must retain
				this notice and the following text and disclaimers in all such redistributions of
				the Apple Software.  Neither the name, trademarks, service marks or logos of
				Apple Computer, Inc. may be used to endorse or promote products derived from the
				Apple Software without specific prior written permission from Apple.  Except as
				expressly stated in this notice, no other rights or licenses, express or implied,
				are granted by Apple herein, including but not limited to any patent rights that
				may be infringed by your derivative works or by other works in which the Apple
				Software may be incorporated.

				The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
				WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
				WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
				PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
				COMBINATION WITH YOUR PRODUCTS.

				IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
				CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
				GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
				ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
				OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
				(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
				ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
				
	Change History (most recent first):

	   <5>	 	08/30/00	srk		Carbonization
	   <4>	 	12/16/98	rtm		removed orphaned prototype for compressTransparentRLEwithHitTesting
	   <3>	 	05/28/98	rtm		added some typecasting to minimize MSDev compiler warnings
	   <2>	 	03/22/98	rtm		made changes to RecompressPictureFileWithTransparency, as per Chris' fixes
	   <1>	 	03/27/98	rtm		existing file

*/


/************************************************************
*                                                           *
*    INCLUDE FILES                                          *
*                                                           *
*************************************************************/


#ifndef _IMAGECOMPRESSIONUTILITIES_
#include "ImageCompressionUtilities.h"

/************************************************************
*                                                           *
*    PROTOTYPES                                             *
*                                                           *
*************************************************************/

static pascal void extractStdPix( PixMap *src, Rect *srcRect, MatrixRecord *matrix, short mode, RgnHandle mask, PixMap *matte, Rect *matteRect, short flags );
static ImageDescriptionHandle createImageDescription( CodecType cType, PixMapHandle pixmap );
static void DrawPictureNoDither(PicHandle pic, const Rect *bounds);
static OSErr prepareFor16BitCompress(PicHandle *pic);
static pascal OSErr myPictureCompressDrawProc( short message, Rect * bounds, GWorldPtr drawingPort, OSType portType, void * refcon );
static pascal OSErr myImageCompressDrawProc( short message, Rect * bounds, GWorldPtr drawingPort, OSType portType, void * refcon );


/************************************************************
*                                                           *
*    CONSTANTS                                              *
*                                                           *
*************************************************************/

// We use the Animation compressor at 16 bit depth in these utilities
#define kCompressDepth	16

// A few macros to help with error handling
#define		BailIf(a, e) 		{if (a) 	{ err = e; goto bail; }}
#define		BailOSErr(a) 		{if ((err = a) != noErr)	goto bail;}
#define		BailMemErr(a)		{a; if ((err = MemError()) != noErr) goto bail;}

/************************************************************
*                                                           *
*    STRUCTURES                                             *
*                                                           *
*************************************************************/

// Used for compressing QuickDraw pictures with transparency/hittesting

typedef struct {
	PicHandle	picture;
} PictureCompressProcData;


// Used for recompressing QuickTime compressed data with transparency/hittesting

typedef struct {
	ImageDescriptionHandle	imageDesc;
	Handle					imageData;
} CompressedImageCompressProcData;


typedef struct {
	CGrafPtr tempPort;

	Handle data;
	ImageDescriptionHandle idh;
} extractPictRecord;




// PICT to compressed image conversion

//	Given a QuickDraw picture, extract QuickTime compressed image data and description, if any.
//
//	It does this by creating a temporary CGrafPort, installing a QuickDraw bottleneck
//	routine for the StdPix call, and then drawing the picture. Since this routine will
//	be called whenever QuickTime compressed image data is encountered, it can
//	stash away any found compressed data and description. On DrawPicture's return, the
//	stashed data and description is retrieved and returned to the caller.
//
//	Worth noting in this utility is definition of the extractPictRecord. This is a 
//	structure consisting of a CGrafPort structure followed by fields for a handle to 
//	image data and an ImageDescription. This layout allows ExtractCompressData to open
//	a CGrafPort using the embedded CGrafPort field, and set this as the current port. 
//	Then, in the StdPix routine extractStdPix, the current port can be retrieved and 
//	cast to a pointer to an extractPictRecord to get access to the other fields.
//


OSErr ExtractCompressData( PicHandle thePict, Handle *dataOut, ImageDescriptionHandle *idh )
{
	OSErr				err = noErr;
	extractPictRecord 	state;
	CQDProcs 			procs;
	GrafPtr 			savePort;
	Rect 				bounds;

	if ( dataOut )
		*dataOut = nil;
	if ( idh )
		*idh = nil;

	GetPort( &savePort );
#ifdef TARGET_OS_WIN32
	state.tempPort = (CGrafPtr)NewPtr(sizeof(CGrafPort));
	OpenCPort(state.tempPort);
#else
	state.tempPort = CreateNewPort();
#endif
	SetStdCProcs( &procs );
	procs.newProc1 = (UniversalProcPtr)NewStdPixUPP(extractStdPix);
#ifdef TARGET_OS_WIN32
	state.tempPort->grafProcs = &procs;
#else
	SetPortGrafProcs(state.tempPort, &procs);
#endif


	state.data = nil;
	state.idh = nil;

	MacSetPort( (GrafPtr)state.tempPort );
	HidePen();

	bounds = (**thePict).picFrame;
	DrawPicture(thePict, &bounds);	

	MacSetPort( savePort );
#ifdef TARGET_OS_WIN32
	CloseCPort(state.tempPort);
#else
	DisposePort(state.tempPort);
#endif
	DisposeStdPixUPP((StdPixUPP)procs.newProc1);
	
	*dataOut	= state.data;
	*idh		= state.idh;

	return err;
}


pascal void extractStdPix( PixMap *src, Rect *srcRect, MatrixRecord *matrix, short mode, RgnHandle mask, PixMap *matte, Rect *matteRect, short flags )
{
#if TARGET_OS_MAC
#pragma unused(srcRect,matrix,mode,mask,matte,matteRect,flags)
#endif
	extractPictRecord	*state;

	GetPort( (GrafPtr *)&state );

	if  ( state->idh == nil ) {
		ImageDescriptionHandle	desc;
		Ptr 					data;
		long					bufferSize;
		OSErr 					err;
		if ( (err=GetCompressedPixMapInfo(src, &desc, &data, &bufferSize, nil, nil)) == noErr ) {
			state->idh = desc;
			HandToHand( (Handle *)&state->idh );
			PtrToHand( data, &state->data, bufferSize );
		}
	}
}


static ImageDescriptionHandle createImageDescription( CodecType cType, PixMapHandle pixmap )
{
	ImageDescriptionHandle	desc = nil;
	
	if ( ( desc = (ImageDescription **)NewHandleClear(sizeof(ImageDescription)) ) != nil ) {
		ImageDescription 	*dp = *desc;
		Rect				bounds = (**pixmap).bounds;

		dp->idSize 			= sizeof(ImageDescription);
		dp->cType 			= cType;	
		dp->spatialQuality 	= codecNormalQuality;
		dp->width			= bounds.right - bounds.left;
		dp->height 			= bounds.bottom - bounds.top;
		dp->hRes 			= 72L << 16;
		dp->vRes 			= 72L << 16;
		dp->dataSize 		= ((**pixmap).rowBytes & 0x3fff) * dp->height;
		dp->depth 			= (**pixmap).pixelSize;
		dp->clutID 			= -1;
		
		if ( SetImageDescriptionCTable( desc, (**pixmap).pmTable ) ) {
			DisposeHandle( (Handle) desc );
			desc = nil;
		}
	}
	return desc;
}

pascal void noDitherBitsProc(BitMap *srcBits, Rect *srcRect, Rect *dstRect, short mode, RgnHandle maskRgn);
pascal void noDitherBitsProc(BitMap *srcBits, Rect *srcRect, Rect *dstRect, short mode, RgnHandle maskRgn)
{
	mode &= ~ditherCopy;
	StdBits(srcBits, srcRect, dstRect, mode, maskRgn);
}

//	DrawPictureNoDither is used to draw a QuickDraw picture but to turn convert any
//	use of ditherCopy graphics modes to graphics modes that don't use ditherCopy. It
//	only performs this with the stdbits bottleneck routine. Since ditherCopy is simply
//	ORed in with other graphics modes, the replaced bottleneck routine only needs to
//	AND off the ditherCopy flag.
void DrawPictureNoDither(PicHandle pic, const Rect *bounds)
{		
	CQDProcs procs;
	GrafPtr savePort;
	CQDProcsPtr saveProcs;

	GetPort(&savePort);
#ifdef TARGET_OS_WIN32
	saveProcs = (CQDProcsPtr)savePort->grafProcs;
#else
	saveProcs = GetPortGrafProcs(savePort);
#endif
	SetStdCProcs( &procs );

	procs.bitsProc = (QDBitsUPP)NewQDBitsProc(noDitherBitsProc);
#ifdef TARGET_OS_WIN32
	savePort->grafProcs = (QDProcsPtr)&procs;
#else
	SetPortGrafProcs(savePort, &procs);
#endif

	DrawPicture(pic, bounds);

#ifdef TARGET_OS_WIN32
	savePort->grafProcs = (QDProcsPtr)saveProcs;
#else
	SetPortGrafProcs(savePort, saveProcs);
#endif

	DisposeQDBitsUPP((QDBitsUPP)procs.bitsProc);
}


//
// The following is a routine that can be used to clean up the colors in a picture
// before being compressed with the Animation compressor in 16-bits at codecNormal
// quality. The QuickTime Animation compressor can operate in both a lossless and
// lossy manner. This routine is meant to help when compressing in a lossy manner.
//
// By clean up, what's meant is the forcing of any colors that are sufficiently close
// to white to colors that are not as close. Since the Animation compressor will
// perform threshholding of the image's colors when codecNormal quality is specified,
// without this preprocessing, colors very close to white can be mapped to white
// in the compression. If white was chosen as the key color, pixels that are
// close to white could possibly become transparent as well.
//
// While not used by other routines in this file, prepareFor16BitCompress is provided
// as an example of how this might be done.
//
// Note that with codecLossless quality, there is no remapping of colors so this
// step is unnecessary.
//

#define kThreshold (255 - 16)

OSErr prepareFor16BitCompress(PicHandle *pic)
{
	OSErr err = noErr;
	PicHandle newPicture = nil;
	Rect r;
	GWorldPtr gw = nil;
	CGrafPtr savePort;
	GDHandle saveGD;
	short rowBytes;
	Ptr baseAddr;
	long w, h;
	PixMapHandle pm;

	GetGWorld(&savePort, &saveGD);

	r = (***pic).picFrame;
	MacOffsetRect(&r, (short)-r.left, (short)-r.top);

	err = NewGWorld(&gw, 32, &r, nil, nil, useTempMem);
	if (err) {
		err = NewGWorld(&gw, 32, &r, nil, nil, 0);
		if (err) goto bail;
	}

	pm = GetGWorldPixMap(gw);
	LockPixels(pm);
	SetGWorld(gw, nil);
	EraseRect(&r);

	DrawPicture(*pic, &r);

	baseAddr = GetPixBaseAddr(pm);
	rowBytes = (**pm).rowBytes & 0x3fff;
	for (h=0; h<r.bottom; h++) {
		long *pixelPtr = (long *)baseAddr;

		for (w=0; w<r.right; w++, pixelPtr++) {
			UInt8 r, g, b, a;
			long pixel = *pixelPtr;

			if ((pixel & 0x0ffffff) == 0x00ffffff)		// pure white
				continue;

			a = (pixel >> 24) & 0x0ff;
			r = (pixel >> 16) & 0x0ff;
			g = (pixel >>  8) & 0x0ff;
			b = (pixel >>  0) & 0x0ff;
			if ((r > kThreshold) && (g > kThreshold) && (b > kThreshold)) {
				r = g = b = kThreshold;
				*pixelPtr = (a << 24) | (kThreshold << 16) | (kThreshold << 8) | (kThreshold << 0);
			}
		}

		baseAddr += rowBytes;
	}

	newPicture = OpenPicture(&r);
	CopyBits((BitMap *)&pm, (BitMap *)&pm, &r, &r, ditherCopy, nil);

	ClosePicture();
	UnlockPixels(pm);

bail:
	if (gw)
		DisposeGWorld(gw);

	if (err == noErr) {
		if (newPicture) {
			KillPicture(*pic);
			*pic = newPicture;
		}
	}

	SetGWorld(savePort, saveGD);

	return err;
}


/*
	---------------- Callback based routines for compressing with hittesting and transparency ----------------
 */

/*
	RecompressWithTransparencyFromProc
	
	This is a routine either called indirectly through RecompressCompressedImageWithTransparency, 
	RecompressPictureWithTransparency, or RecompressPictureFileWithTransparency or called directly.
	It takes a callback procedure which is responsible for returning the dimensions of the
	area of some type of drawable entity and for actually drawing that entity.
	
		includeHitTesting		TRUE if hit testing data should be added to the compressed data
		keyColor				A RGBColor that should be used as the transparency color. Pass nil
								if the image doesn't have transparent portions.
		hitTestRegion			A RgnHandle specifying an area that is to be used as the hit test
								area. If you pass this, you must also set includeHitTesting to TRUE.
								This is optional as the callback procedure can perform drawing
								itself of the hit test area which is often suitable when both the
								image and hit test area were painted in a drawing program.
		idh						Returned ImageDescriptionHandle for the compressed image data
		imageData				Returned Handle to the compressed image data

 */
OSErr RecompressWithTransparencyFromProc( CompressDrawProc drawProc, void * drawProcRefcon, 
													Boolean includeHitTesting,
													RGBColor *keyColor, 
													RgnHandle hitTestRegion,
													ImageDescriptionHandle *idh, Handle * imageData )
{
	OSErr 						err = noErr;
	Rect						bounds;
	CGrafPtr					savePort;
	GDHandle					saveDevice;
	GWorldPtr					gwImage = nil;		// always used
	GWorldPtr					gwPrev = nil;		// used if compressing with transparency (via keycolor)
	GWorldPtr					gwMap = nil;		// used if compressing with hittesting data
	ImageDescriptionHandle		desc = nil;			// resulting image description
	ImageDescriptionHandle		gwMapDesc = nil;
	ImageSequence				seq = 0;			// compress sequence
	ImageSequenceDataSource		mapSource = 0L;
	Ptr							data = nil;
	long 						dataSize;
	UInt8 						similarity;
	Boolean						includeTransparency = false;
	RGBColor					saveBackColor;
		
	GetGWorld( &savePort, &saveDevice );
	
	if( !drawProc || !idh || !imageData ){
		err = paramErr;
		goto bail;
	}
	
	// tell callback that it should initialize any storage it needs
	err = drawProc( kRecoProcInitMsg, nil, nil, 0, drawProcRefcon );
	if(err) goto bail;
	
	// determine bounds to use for compression
	err = drawProc( kRecoProcGetBoundsMsg, &bounds, nil, 0, drawProcRefcon );
	if(err) goto bail;
	
	err = QTNewGWorld(&gwImage, kCompressDepth, &bounds, nil, nil, kICMTempThenAppMemory);
	if(err) goto bail;

	if( keyColor ) {
		includeTransparency = true;
	}
	
	// Include hit testing? If so, we need a previous buffer and an 8-bit GWorld for the mask data
	if( includeHitTesting ) {
		err = QTNewGWorld(&gwPrev, kCompressDepth, &bounds, nil, nil, kICMTempThenAppMemory);
		if(err) goto bail;

		err = QTNewGWorld(&gwMap, 8, &bounds, nil, nil, kICMTempThenAppMemory);
		if(err) goto bail;
	}
	
	LockPixels( GetGWorldPixMap(gwImage) );
	
	if( gwPrev )
		LockPixels( GetGWorldPixMap(gwPrev) );
	
	if( gwMap )
		LockPixels( GetGWorldPixMap(gwMap) );
	
	if(gwPrev) {
		SetGWorld( gwPrev, nil );
		ClipRect( &bounds );
		
		GetBackColor( &saveBackColor );
		if( keyColor )
			RGBBackColor( keyColor );
		
			EraseRect( &bounds );
			
		RGBBackColor( &saveBackColor );
	}
	
	if( gwMap ) {
		SetGWorld( gwMap, nil );
		
		EraseRect( &bounds );			// paint background white

		err = drawProc( kRecoProcDrawMsg, &bounds, gwMap, kRecoProcHitTestingImageType, drawProcRefcon );
		if(err) goto bail;
		
		if( hitTestRegion ) {
			RGBColor blackRGB;
			
			blackRGB.red = blackRGB.green = blackRGB.blue = 0;
			
			RGBForeColor( &blackRGB );
			MacPaintRgn( hitTestRegion );
		}

		gwMapDesc = createImageDescription(kRawCodecType, GetGWorldPixMap(gwMap));
		err = MemError();
		if(err) goto bail;
	}
	
	SetGWorld( gwImage, nil );
	ClipRect( &bounds );
	EraseRect( &bounds );
	
	desc = (ImageDescriptionHandle) NewHandle(sizeof(ImageDescription));


	// NOTE: We pass codecLosslessQuality so that the key color if any is matched exactly. This avoids colors within
	// some threshhold different from the key color being taken as equivalent to the key color. Alternatively, we
	// could perform some threshhold processing on the source image's pixels and pass codecNormalQuality.
	if( includeHitTesting ) {
		// Allocate a compression sequence and add source data for hittest mask
		err = CompressSequenceBegin(&seq, GetGWorldPixMap(gwPrev), nil, nil, nil, kCompressDepth, kAnimationCodecType, 
											anyCodec, codecLosslessQuality, codecLosslessQuality, 2, nil, 0, desc);

		// with hit testing, we have to add a data source to hold the mask data
		err = CDSequenceNewDataSource(seq, &mapSource, kRecoProcHitTestingImageType, 1, (Handle)gwMapDesc, nil, nil);
		if (err) goto bail;

		err = CDSequenceSetSourceData(mapSource, GetPixBaseAddr(GetGWorldPixMap(gwMap)), (**gwMapDesc).dataSize);
		if (err) goto bail;

		// What's the maximum size the compressed data could be--including hit-test data?
		err = GetCSequenceMaxCompressionSize(seq, GetGWorldPixMap(gwPrev), &dataSize);
	}
	else
	{	// not hit-testing so we only need the image buffer
		err = CompressSequenceBegin( &seq, GetGWorldPixMap(gwImage), nil, &bounds, nil, kCompressDepth, kAnimationCodecType, 0, 
									codecLosslessQuality, codecLosslessQuality, 2, nil, 0, desc );
		if(err) goto bail;
		
		// What's the maximum size the compressed data could be?
		err = GetCSequenceMaxCompressionSize(seq, GetGWorldPixMap(gwImage), &dataSize);
	}
	if (err) goto bail;
	

	data = NewPtr( dataSize );
	if(noErr != (err = MemError())) goto bail;
	
	if( includeHitTesting /* with or without transparency */ ) {
		// With hittesting, we use two buffers. Actually we don't have to but do so to show how it can be
		// done. Also, this code was based upon some older code that did.
		
		
		// compress the GWorld painted with the keyColor exclusively
		err = CompressSequenceFrame( seq, GetGWorldPixMap(gwPrev), nil, 0, data, &dataSize, &similarity, nil );
		if ( err ) goto bail;

		err = SetCSequencePrev(seq, GetGWorldPixMap(gwPrev), nil);
		if (err) goto bail;

		// draw the image into the GWorld over area painted with keyColor so that if picture is transparent already
		// areas it doesn't paint will be in the key color
		SetGWorld( gwImage, nil );
		
		GetBackColor( &saveBackColor );
		if( keyColor )
			RGBBackColor( keyColor );
			
			EraseRect( &bounds );
		
		RGBBackColor( &saveBackColor );
	
		err = drawProc( kRecoProcDrawMsg, &bounds, gwImage, kRecoProcOriginalImageType, drawProcRefcon );
		if(err) goto bail;

		// now compress the GWorld holding the image drawn on top of the keyColor
		err = CompressSequenceFrame(seq, GetGWorldPixMap(gwImage), nil, 0, data, &dataSize, &similarity, nil);
		if (err) goto bail;
		
		// At this point, data points to the image data for just the difference between the two (thus generating transparency) 
		// Also, hit testing data is contained in the image data if it was specified.
	}
	else if( includeTransparency ) {
		// For transparency case without hittesting, we get by with only using a single buffer so we special case the
		// code here. This is also for clarity.
	
		// compress the GWorld painted with the keyColor exclusively
		err = CompressSequenceFrame( seq, GetGWorldPixMap(gwImage), nil, codecFlagUpdatePrevious, data, &dataSize, &similarity, nil );
		if ( err ) goto bail;

		// draw the image into the GWorld over area painted with keyColor so that if picture is transparent already
		// areas it doesn't paint will be in the key color
		SetGWorld( gwImage, nil );
		
		GetBackColor( &saveBackColor );
		if( keyColor )
			RGBBackColor( keyColor );
			
			EraseRect( &bounds );
		
		RGBBackColor( &saveBackColor );
	
		err = drawProc( kRecoProcDrawMsg, &bounds, gwImage, kRecoProcOriginalImageType, drawProcRefcon );
		if(err) goto bail;

		// now compress the GWorld holding the image drawn on top of the keyColor
		err = CompressSequenceFrame(seq, GetGWorldPixMap(gwImage), nil, codecFlagUpdatePrevious, data, &dataSize, &similarity, nil);
		if (err) goto bail;
		
		// At this point, data points to the image data for just the difference between the two (thus generating transparency) 
		// Also, hit testing data is contained in the image data if it was specified.
	}
	else
	{
		SetGWorld( gwImage, nil );

		// draw the image into the GWorld
		err = drawProc( kRecoProcDrawMsg, &bounds, gwImage, kRecoProcOriginalImageType, drawProcRefcon );
		if(err) goto bail;

		// compress the GWorld containing the image painted on white
		err = CompressSequenceFrame( seq, GetGWorldPixMap(gwImage), nil, 0, data, &dataSize, &similarity, nil );
		if ( err ) goto bail;
		
		// At this point, data points to the image data for just the image, newly compressed. Also, hit testing data is contained
		// in the image data if it was specified.
	}
	
	CDSequenceEnd( seq );
	seq = 0;
	
	// free the GWorlds and drop references so we have more memory for PtrToHand
	if( gwImage )	DisposeGWorld( gwImage );
	gwImage = nil;
	if( gwMap )	DisposeGWorld( gwMap );
	gwMap = nil;
	if( gwPrev ) DisposeGWorld( gwPrev );
	gwPrev = nil;
	
	err = PtrToHand( data, imageData, dataSize );
	if ( err ) goto bail;
	
	*idh = desc;
	desc = nil;				// forget about this name for ImageDescriptionHandle so dispose below doesn't catch it
	
bail:
	// tell callback to dispose of anything it allocated. We pass 'err ' in portType if an error occurred
	drawProc( kRecoProcDisposeMsg, nil, nil, err ? FOUR_CHAR_CODE('err ') : 0, drawProcRefcon );
	
	CDSequenceEnd( seq );
	SetGWorld( savePort, saveDevice );
	
	if(gwImage)		DisposeGWorld( gwImage );
	if(gwMap )		DisposeGWorld( gwMap );
	if(gwPrev )		DisposeGWorld( gwPrev );
	if(desc) 		DisposeHandle((Handle) desc );
	if(gwMapDesc)	DisposeHandle((Handle) gwMapDesc );
	if(data)		DisposePtr( data );
		
	return err;
}


/*
	myPictureCompressDrawProc
	
	Helper routine to be used with RecompressWithTransparencyFromProc to compress QuickDraw Pictures.
 */

static pascal OSErr myPictureCompressDrawProc( short message, Rect * bounds, GWorldPtr drawingPort, OSType drawingImageType, void * refcon )
{
#if TARGET_OS_MAC
#pragma unused(drawingPort)
#endif
	OSErr err = noErr;
	PictureCompressProcData * data = refcon;
	Rect r;

	switch( message ) {
		case kRecoProcInitMsg:
			break;
			
		case kRecoProcDisposeMsg:
			break;
			
		case kRecoProcGetBoundsMsg:
			r = (**data->picture).picFrame;
			
			r.left = EndianS16_BtoN(r.left);
			r.top = EndianS16_BtoN(r.top);
			r.bottom = EndianS16_BtoN(r.bottom);
			r.right = EndianS16_BtoN(r.right);
			
			MacOffsetRect(&r, (short)-r.left, (short)-r.top );
			
			*bounds = r;
			break;
			
		case kRecoProcDrawMsg:
			r = (**data->picture).picFrame;
			
			r.left = EndianS16_BtoN(r.left);
			r.top = EndianS16_BtoN(r.top);
			r.bottom = EndianS16_BtoN(r.bottom);
			r.right = EndianS16_BtoN(r.right);

			MacOffsetRect( &r, (short)-r.left, (short)-r.top );

			if( kRecoProcOriginalImageType == drawingImageType )
				DrawPictureNoDither( data->picture, &r );
			break;
		default:
			err = -1;
	}

	return err;
}	
	

/*
	myImageCompressDrawProc
	
	Helper routine to be used with RecompressWithTransparencyFromProc to compress QuickTime compressed image data.
 */
static pascal OSErr myImageCompressDrawProc( short message, Rect * bounds, GWorldPtr drawingPort, OSType drawingImageType, void * refcon )
{
#if TARGET_OS_MAC
#pragma unused(drawingImageType)
#endif

	OSErr err = noErr;
	CompressedImageCompressProcData * data = refcon;
	Rect r;
	
	switch( message ) {
	case kRecoProcInitMsg:
		break;
	case kRecoProcDisposeMsg:
		break;
	case kRecoProcGetBoundsMsg:
		r.left = r.top = 0;
		r.right = (**data->imageDesc).width;
		r.bottom = (**data->imageDesc).height;
		
		*bounds = r;
		break;
	case kRecoProcDrawMsg:
		{
			SignedByte saveState;
			
			r.left = r.top = 0;
			r.right = (**data->imageDesc).width;
			r.bottom = (**data->imageDesc).height;
			
			saveState = HGetState( data->imageData );
			HLockHi( data->imageData );
			
			if( kRecoProcOriginalImageType == drawingImageType )
				err = DecompressImage( *data->imageData, data->imageDesc, GetGWorldPixMap(drawingPort), &r, &r, srcCopy, nil );
			
			HSetState( data->imageData, saveState );
		}
		break;
	default:
		err = -1;
	}

	return err;
}	



/*
	RecompressCompressedImageWithTransparency
	
	Given an ImageDescriptionHandle and a handle to image data, generate new RLE compressed data
	with optional hitTesting and transparency.
 */
OSErr RecompressCompressedImageWithTransparency( ImageDescriptionHandle originalDesc, Handle originalImageData,
										RGBColor *keyColor, 
										RgnHandle hitTestRegion,
										ImageDescriptionHandle *idh, Handle * imageData )
{
	OSErr err = noErr;
	CompressedImageCompressProcData params;
	
	params.imageDesc = originalDesc;
	params.imageData = originalImageData;
	
	err = RecompressWithTransparencyFromProc( myImageCompressDrawProc, &params, 
										(Boolean)(hitTestRegion != nil), 
										keyColor, 
										hitTestRegion, 
										idh, imageData );
	
	return err;
}

/*
	RecompressPictureWithTransparency
	
	Given a QuickDraw PicHandle, generate new RLE compressed data with optional hitTesting and transparency.
 */
OSErr RecompressPictureWithTransparency( PicHandle originalPicture,
										RGBColor *keyColor, 
										RgnHandle hitTestRegion,
										ImageDescriptionHandle *idh, Handle * imageData )
{
	OSErr err = noErr;
	PictureCompressProcData params;
	
	params.picture = originalPicture;
	
	err = RecompressWithTransparencyFromProc( myPictureCompressDrawProc,
											&params, 
											(Boolean)(hitTestRegion != nil), 
											keyColor, 
											hitTestRegion, 
											idh,
											imageData );
	
	return err;
}

/*
	RecompressPictureFileWithTransparency
	
	Given a QuickDraw PICT file, generate new RLE compressed data with optional hitTesting and transparency.
	This function uses GetCompressedImageFromPicture to do the actual work on the PicHandle retrieved from
	the PICT file.
 */
OSErr RecompressPictureFileWithTransparency( FSSpec * spec, 
										RGBColor *keyColor, 
										RgnHandle hitTestRegion,
										ImageDescriptionHandle *idh, Handle * imageData )
{
	OSErr 		err = noErr;
	short		sourceRefNum = 0;
	PicHandle	picture = nil;
	long		eof;
	long		countBytes;
	
	*idh = nil;
	*imageData = nil;	
	
	BailOSErr(FSpOpenDF( spec, fsRdPerm, &sourceRefNum ));
	
	BailOSErr(GetEOF( sourceRefNum, &eof ));
	eof -= 512;
	
	BailOSErr(SetFPos( sourceRefNum, fsFromStart, 512 ));
			
	picture = (PicHandle) NewHandle(eof);
	err = MemError();
	BailOSErr(err);

	countBytes = eof;
	HLock((Handle) picture);
	BailOSErr( FSRead( sourceRefNum, &countBytes, *picture) );
	HUnlock((Handle) picture);
	
	BailOSErr( RecompressPictureWithTransparency( picture,  keyColor, hitTestRegion,
								idh, imageData ));
								
bail:
	if ( picture )			DisposeHandle((Handle) picture );
	if ( sourceRefNum )		FSClose( sourceRefNum );
		
	return err;
}

#endif
