/*
	File:		EI_GraphicsExport.c

	Description: QuickTime graphics exporter component

	Author:		QuickTime Engineering, era

	Copyright: 	© Copyright 2003 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):
	   <1>	 	04/20/03	era			first file
*/

/* Graphics exporter components provide a standard interface for exporting graphics to image
   files in a variety of formats. QuickTime selects a graphic exporter component based on the
   desired output format. A graphic image can be exported to a handle, a file, or a data reference.
   Graphics exporter components have component type 'grex' and, by convention, component subtype
   matching the Mac OS file type for the image file format. For example, the graphics exporter for
   PNG files has component subtype 'PNGf'.
   
   QuickTime provides a base graphics export component which provides abstractions that greatly
   simplify the work of format-specific graphics exporter components, while offering applications
   a rich interface. 

   Format-specific graphics exporters, are relatively simple components. When a format-specific exporter
   is opened, it opens and targets an instance of the base graphics exporter.  Subsequently, it delegates
   most of its calls to the base exporter instance. See the following URL for an illustrated example:
	
		< http://developer.apple.com/techpubs/quicktime/qtdevdocs/REF/refExporter.15.htm >
	
   The base exporter communicates with data handler components to write image file data. If necessary, it
   calls the Image Compression Manager to perform compression operations.
   
   There are three modes that format-specific exporters can operate in:
   
		Transcode - Data is transferred from one image file to another without decompressing and recompressing. 
				   (The data may be modified along the way, if appropriate.)
				   
		Using a Compressor - The graphics exporter provides an atom container identifying which compressor to use,
							 and any settings to be passed to that compressor.
							 
		Standalone Export - The graphics exporter component does all the work itself, without using an image compressor.
		
	When an application calls GraphicsExportDoExport, the base exporter asks the following questions:
	 
		Can QuickTime transcode? 

		It calls the format-specific exporter's GraphicsExportCanTranscode function. If the answer is yes,
		it calls the exporter's GraphicsExportDoTranscode function. 

	Otherwise, 
		Can QuickTime use a compressor? 

		It calls the format-specific exporter's GraphicsExportCanUseCompressor function. If the answer is yes,
		it calls the Image Compression Manager to compress using the compressor and parameters specified in the
		atom container. This is done in the base exporter's GraphicsExportDoUseCompressor function. (Usually the
		format-specific exporter does not need to override this function and should simply delegate it. If the
		format-specific exporter's format is a container format [such as a PICT or QuickTime Image], it can override
		and delegate this in order to encapsulate the compressed data in the container format.) 

	Otherwise, 
		QuickTime must perform a standalone export.
		
		If neither transcoding nor compressing is appropriate, it calls the format-specific exporter's
		GraphicsExportDoStandaloneExport function.
	
	Graphics Exporter Components Reference:
		< http://developer.apple.com/techpubs/quicktime/qtdevdocs/REF/refExporter.1.htm >
*/

#if TARGET_OS_WIN32
    #include "EIComponentWindowsPrefix.h"
#endif

#if __MACH__
    #include <Carbon/Carbon.h>
    #include <QuickTime/QuickTime.h>
#else
	#include <ConditionalMacros.h>
	#include <ImageCompression.h>
	#include <QDOffscreen.h>
    #include <Endian.h>
    #include <StdDef.h>	// for offsetof
#endif

#include "EI_Image.h"
#include "EI_GraphicsExportVersion.h"

#pragma mark- Data Structures

// Data structures
#if PRAGMA_STRUCT_ALIGN
	#pragma options align=packed
#elif PRAGMA_STRUCT_PACKPUSH
	#pragma pack(push, 1)
#elif PRAGMA_STRUCT_PACK
	#pragma pack(1)
#endif

typedef struct {
	UInt8	red;
	UInt8	green;
	UInt8	blue;
} PackedColor;

typedef struct {
	UInt8	opcode;
	UInt8	pixelData[1];
} RLE8Packet;

typedef struct {
	UInt8	opcode;
	UInt16	pixelData[1];
} RLE16Packet;

typedef struct {
	UInt8	opcode;
	UInt32	pixelData[1];
} RLE32Packet;

#if PRAGMA_STRUCT_ALIGN
	#pragma options align=reset
#elif PRAGMA_STRUCT_PACKPUSH
	#pragma pack(pop)
#elif PRAGMA_STRUCT_PACK
	#pragma pack()
#endif

typedef struct {
	ComponentInstance self;
	ComponentInstance parent;
	ComponentInstance delegateComponent;
} EI_GraphicsExportGlobalsRecord, *EI_GraphicsExportGlobals;

#pragma mark- Internal Prototypes

// Prototypes
static ComponentResult WriteImageFrameHeader(GraphicsExportComponent ci, CTabHandle cTabHdl, short width, short height, short depth, Size compressBufferSize);
static ComponentResult WriteColorTable(GraphicsExportComponent ci , CTabHandle cTabHdl);
static OSErr CompressRLE(Ptr baseAddr, long rowBytes, short depth, short width, short height, Ptr compressBuffer, Size *compressBufferSizePtr);
static void  CompressRLE8(UInt8 *srcPtr, Size srcSize, Ptr compressBuffer, Size *compressBufferSizePtr);
static void  CompressRLE16(UInt16 *srcPtr, Size srcSize, Ptr compressBuffer, Size *compressBufferSizePtr);
static void  CompressRLE32(UInt32 *srcPtr, Size srcSize, Ptr compressBuffer, Size *compressBufferSizePtr);

#pragma mark- Component Dispatch

// Setup required for ComponentDispatchHelper.c
#define CALLCOMPONENT_BASENAME()	EI_GraphicsExport
#define	CALLCOMPONENT_GLOBALS()		EI_GraphicsExportGlobals storage

#define GRAPHICSEXPORT_BASENAME()	CALLCOMPONENT_BASENAME()
#define	GRAPHICSEXPORT_GLOBALS()	CALLCOMPONENT_GLOBALS()

#define COMPONENT_BASENAME()		CALLCOMPONENT_BASENAME()
#define	COMPONENT_GLOBALS()			CALLCOMPONENT_GLOBALS()

#define COMPONENT_DISPATCH_FILE		"EI_GraphicsExportDispatch.h"
#define COMPONENT_UPP_SELECT_ROOT()	GraphicsExport					// root for Type's UPP_PREFIX and SELECT_PREFIX	
#define	GET_DELEGATE_COMPONENT()	(storage->delegateComponent)	// how to find who we delegate to

#if __MACH__
	#include <CoreServices/Components.k.h>
	#include <QuickTime/ImageCompression.k.h>
	#include <QuickTime/ComponentDispatchHelper.c>
#else
	#include <Components.k.h>
	#include <ImageCompression.k.h>
	#include <ComponentDispatchHelper.c>
#endif

#pragma mark -

//============================================================================
//
//	Standard component calls
//

// Component Open Request - Required
pascal ComponentResult EI_GraphicsExportOpen(EI_GraphicsExportGlobals store, ComponentInstance self)
{
	ComponentResult err = noErr;

	store = (EI_GraphicsExportGlobals)NewPtrClear(sizeof(EI_GraphicsExportGlobalsRecord));
	if (err = MemError()) goto bail;

	SetComponentInstanceStorage(self, (Handle)store);

	store->self = self;
	store->parent = self;

	err = OpenADefaultComponent(GraphicsExporterComponentType, kBaseGraphicsExporterSubType, &store->delegateComponent);
	if (err) goto bail;

	err = ComponentSetTarget(store->delegateComponent, self);
	if (err) goto bail;

bail:
	return err;
}

// Component Close Request - Required
pascal ComponentResult EI_GraphicsExportClose(EI_GraphicsExportGlobals store, ComponentInstance self)
{
#pragma unused(self)

	if (store) {
		if (store->delegateComponent)
			CloseComponent(store->delegateComponent);

		DisposePtr((Ptr)store);
	}

	return noErr;
}

// Component Version Request - Required
pascal ComponentResult EI_GraphicsExportVersion(EI_GraphicsExportGlobals store)
{
#pragma unused(store)

	#if TARGET_CPU_68K || TARGET_OS_WIN32
		return kEI_GraphicsExportVersion;
	#else
		return kEI_GraphicsExportVersionPPC;
	#endif
}

// Component Target Request
// 		Allows another component to "target" you i.e., you call another component whenever
// you would call yourself (as a result of your component being used by another component)
pascal ComponentResult EI_GraphicsExportTarget(EI_GraphicsExportGlobals store, ComponentInstance target)
{
	store->parent = target;

	return noErr;
}

#pragma mark -

//============================================================================
//
//	Export routines
//

// GraphicsExportDoStandaloneExport
//		Performs a standalone graphics export operation. If both GraphicsExportCanTranscode
// and GraphicsExportCanUseCompressor reply FALSE, the base graphics exporter makes this call
// of the format-specific exporter to perform the export. This function is used for internal
// communication between the base and format-specific graphics exporter. Applications will not
// usually need to call it directly.
pascal ComponentResult EI_GraphicsExportDoStandaloneExport(EI_GraphicsExportGlobals store)
{
	ImageHeader  		   imgHeader;
	ImageDescriptionHandle desc = NULL;
	GWorldPtr    		   gw = NULL;
	PixMapHandle		   hPixMap = NULL;
	Ptr					   baseAddr, compressBuffer;
	long				   rowBytes;
	CTabHandle			   cTabHdl;
	Rect				   bounds;
	short				   width, height, depth;
	Size		    	   compressBufferSize;
	ComponentResult err;

	// Get the image description describing the input image for this
	// graphics export operation -- Remember, you are responsible for
	// disposing of the returned image description handle
	err = GraphicsExportGetInputImageDescription(store->parent, &desc);
	if (err) goto bail;

	// Get the dimensions of the input image for this graphics export operation
	err = GraphicsExportGetInputImageDimensions(store->parent, &bounds);
	if (err) goto bail;
	
	// Setup the Electric Image header...
	imgHeader.imageVersion = EndianU16_NtoB(5);
	imgHeader.imageFrames = EndianU32_NtoB(1); // always a single frame
	
	// ...and write it
	err = GraphicsExportWriteOutputData(store->parent, &imgHeader, sizeof(ImageHeader));
	if (err) goto bail;

	depth = ((**desc).depth == 24) ? k32ARGBPixelFormat : (**desc).depth;
	
	// Create a GWorld to draw the input image into
	err = QTNewGWorld(&gw, depth, &bounds, NULL, NULL, kICMTempThenAppMemory);
	if (err) goto bail;
	
	hPixMap = GetGWorldPixMap(gw);

	LockPixels(hPixMap);
	
	// Draw the input image -- NULL for srcRect indicates no source extraction
	err = GraphicsExportDrawInputImage(store->parent, gw, NULL, NULL, &bounds);
	if (err) goto bail;

	baseAddr = GetPixBaseAddr(hPixMap);
	rowBytes = QTGetPixMapHandleRowBytes(hPixMap);
	cTabHdl = (**hPixMap).pmTable;
	MacOffsetRect(&bounds, -bounds.left, -bounds.top);

	width = bounds.right;
	height = bounds.bottom;
	
	// Allocate memory enough to store maximum compressed data
	compressBuffer = NewPtrClear(width * height * depth * 2);
	err = MemError();
	if (err) goto bail;
	
	// Compress the pixels from the GWorld so we get our RLE compressed image data
	err = CompressRLE(baseAddr, rowBytes, depth, width, height, compressBuffer, &compressBufferSize);
	if (err) goto bail;

	// Write the image frame header
	err = WriteImageFrameHeader(store->parent, cTabHdl, width, height, depth, compressBufferSize);
	if (err) goto bail;

	// Write the colorTable
	err = WriteColorTable(store->parent, cTabHdl);
	if (err) goto bail;
	
	// Write the image data
	err = GraphicsExportWriteOutputData(store->parent, compressBuffer, compressBufferSize);

bail:
	if (compressBuffer) DisposePtr(compressBuffer);
	if (desc) DisposeHandle((Handle)desc);
	if (gw) DisposeGWorld(gw);

	return err;
}

//============================================================================
//
//	Format-specific information
//

// GraphicsExportGetDefaultFileTypeAndCreator
//		Returns the suggested file type and creator for a graphics export operation.
// This function, along with GraphicsExportGetDefaultFileNameExtension and GraphicsExportGetMIMETypeList,
// returns information about the image format supported by a format-specific graphics exporter. Format-specific
// exporters must implement all three of these calls. 
pascal ComponentResult EI_GraphicsExportGetDefaultFileTypeAndCreator(EI_GraphicsExportGlobals store, OSType *fileType, OSType *fileCreator)
{
#pragma unused(store)

	if (!fileType && !fileCreator) return paramErr;
	
	if (fileType)
		*fileType = FOUR_CHAR_CODE('EIDI');

	if (fileCreator)
		#if TARGET_API_MAC_OSX
			*fileCreator = FOUR_CHAR_CODE('TVOD');
		#else 
			*fileCreator = FOUR_CHAR_CODE('ogle');
		#endif
	
	return noErr;
}

// GraphicsExportGetDefaultFileNameExtension
//		Returns the suggested file name extension for a graphics export operation. 
// File name extensions are returned as upper-case big-endian four-character codes. For example, the extension .png
// would be returned as 'PNG ' (0x504E4720 ). This function, along with GraphicsExportGetDefaultFileTypeAndCreator
// and GraphicsExportGetMIMETypeList, returns information about the image format supported by a format-specific graphics
// exporter. Format-specific exporters must implement all three of these calls.
pascal ComponentResult EI_GraphicsExportGetDefaultFileNameExtension(EI_GraphicsExportGlobals store, OSType *fileNameExtension)
{
#pragma unused(store)
	
	if (!fileNameExtension) return paramErr;
	
	*fileNameExtension = FOUR_CHAR_CODE('EIM ');
	
	return noErr;
}

// GraphicsExportGetMIMETypeList
//		Returns MIME types and other information about the graphics format in a graphics export operation. 
// This function creates and returns a QuickTime atom container that contains the format's name, as a string
// in an atom of type 'desc' (kMimeInfoDescriptionTag ), and optionally the MIME type as a string in an atom
// of type 'mime' (kMimeInfoMimeTypeTag ). This function, along with GraphicsExportGetDefaultFileTypeAndCreator
// and GraphicsExportGetDefaultFileNameExtension, returns information about the image format supported by a
// format-specific graphics exporter. Format-specific exporters must implement all three of these calls.
pascal ComponentResult EI_GraphicsExportGetMIMETypeList(EI_GraphicsExportGlobals store, void *qtAtomContainerPtr)
{
	return GetComponentResource((Component)store->self, FOUR_CHAR_CODE('mime'), 2048, (Handle *)qtAtomContainerPtr);
}

#pragma mark -

//============================================================================
//
//	Format-specific Routines
//

// WriteImageFrameHeader
//		The routine writes the image frame header out to the file, see 'EI_Image.h'.
static ComponentResult WriteImageFrameHeader(GraphicsExportComponent ci, CTabHandle cTabHdl, short width, short height, short depth, Size compressBufferSize)
{
	ImageFrame	    imgFrame;
	
	// Setup the frame header
	imgFrame.frameTime = 0;
	imgFrame.frameRect.top = 0;						
	imgFrame.frameRect.left = 0;					
	imgFrame.frameRect.bottom = EndianU16_NtoB(height);						
	imgFrame.frameRect.right = EndianU16_NtoB(width);					
	imgFrame.frameBitDepth = (depth == 32 ? 24 : depth);
	imgFrame.frameType = (depth > 8 ? 0 : 1);
	imgFrame.framePackRect = imgFrame.frameRect;
	imgFrame.framePacking = 1;
	imgFrame.frameAlpha = (depth == 32 ? 8 : 0);
	imgFrame.frameSize = EndianU32_NtoB(compressBufferSize);
	imgFrame.framePalettes = EndianU16_NtoB((**cTabHdl).ctSize + 1);
	imgFrame.frameBackground = EndianU16_NtoB((**cTabHdl).ctSize);
	
	return GraphicsExportWriteOutputData(ci, &imgFrame, sizeof(ImageFrame));
}

// WriteColorTable
//		This rountine writes out the color table and get called after the image frame header is written.
static ComponentResult WriteColorTable(GraphicsExportComponent ci, CTabHandle cTabHdl)
{
	PackedColor	colors[256];
	ColorSpec	*ctTable = (**cTabHdl).ctTable;
	UInt16		i, numOfColorEntries = (**cTabHdl).ctSize + 1;	// ctSize is number of entries in array of ColorSpec records minus 1

	for (i = 0; i < numOfColorEntries; i++) {
		colors[i].red = (UInt8)(ctTable[i].rgb.red >> 8);
		colors[i].green = (UInt8)(ctTable[i].rgb.green >> 8);
		colors[i].blue = (UInt8)(ctTable[i].rgb.blue >> 8);
	}

	return GraphicsExportWriteOutputData(ci, (Ptr)colors, sizeof(PackedColor) * numOfColorEntries);
}

#pragma mark -

// CompressRLE
//		Main compress routine, this function will call the appropriate RLE compression
// method depending on the pixel depth of the source image.
static OSErr CompressRLE(Ptr baseAddr, long rowBytes, short depth, short width, short height, Ptr compressBuffer, Size *compressBufferSizePtr)
{
	Handle hdl;
	Ptr    tempPtr;
	short  i;
	Size   widthByteSize = (depth * width + 7) >> 3;
	OSErr  err;

	// Make a temp buffer for the source
	hdl = NewHandleClear(height * widthByteSize);
	err = MemError();
	if (err) goto bail;
	
	HLock(hdl);
	
	tempPtr = *hdl;
	
	// Get rid of row bytes padding
	for (i = 0; i < height; i++) {
		BlockMoveData(baseAddr, tempPtr, widthByteSize);
		
		tempPtr += widthByteSize;
		baseAddr += rowBytes;
	}

	// Compress
	switch (depth) {
	case 1:
		CompressRLE8((UInt8 *)*hdl, height * widthByteSize, compressBuffer, compressBufferSizePtr);
		break;
	case 8:
		CompressRLE8((UInt8 *)*hdl, height * widthByteSize, compressBuffer, compressBufferSizePtr);
		break;
	case 16:
		CompressRLE16((UInt16 *)*hdl, height * (widthByteSize >> 1), compressBuffer, compressBufferSizePtr);
		break;
	case 32:
		CompressRLE32((UInt32 *)*hdl, height * (widthByteSize >> 2), compressBuffer, compressBufferSizePtr);
		break;
	}
	
bail:
	if (hdl)
		DisposeHandle(hdl);

	return err;
}

// CompressRLE8
static void CompressRLE8(UInt8 *srcPtr, Size srcSize, Ptr compressBuffer, Size *compressBufferSizePtr)
{
	UInt8	   prevPixel, currentPixel;
	UInt8	   sameCharCount;
	UInt8	   diffCharCount;
	RLE8Packet *packetPtr;
		
	// Initialize some stuff
	sameCharCount = 1;
	diffCharCount = 0;

	packetPtr = (RLE8Packet *)compressBuffer;

	prevPixel = *srcPtr++;

	while (srcSize >= 2 ) {
		currentPixel = *srcPtr++;
		
		if (prevPixel == currentPixel) {
			// If diffCharCount > 0, we are holding pixels which should be read literally
			if (diffCharCount > 0) {
				packetPtr->opcode = 127 + diffCharCount;
				
				packetPtr = (RLE8Packet *)((Ptr)packetPtr + offsetof(RLE8Packet, pixelData[diffCharCount]));
				diffCharCount = 0;
			}
			
			if (sameCharCount < 128) {
				sameCharCount++;
			} else {
				packetPtr->opcode = sameCharCount - 1;
				packetPtr->pixelData[0] = prevPixel;
				
				packetPtr = (RLE8Packet *)((Ptr)packetPtr + offsetof(RLE8Packet, pixelData[1]));
				sameCharCount = 1;
			}
		} else {
			// If sameCharCount > 1, we are holding pixels which should be read repeatedly
			if (sameCharCount > 1) {
				packetPtr->opcode = sameCharCount - 1;
				packetPtr->pixelData[0] = prevPixel;
								
				packetPtr = (RLE8Packet *)((Ptr)packetPtr + offsetof(RLE8Packet, pixelData[1]));
				sameCharCount = 1;
			} else {
				packetPtr->pixelData[diffCharCount++] = prevPixel;
				
				if (diffCharCount == 128) {
					packetPtr->opcode = 127 + diffCharCount;
					
					packetPtr = (RLE8Packet *)((Ptr)packetPtr + offsetof(RLE8Packet, pixelData[diffCharCount]));
					diffCharCount = 0;
				}
			}
			
			prevPixel = currentPixel;
		}
		
		srcSize--;
	}
	
	// If sameCharCount > 1, we are holding pixels which should be read repeatedly
	// If not so, we are holding pixels which should be read literally
	if (sameCharCount > 1) {
		packetPtr->opcode = sameCharCount - 1;
		packetPtr->pixelData[0] = prevPixel;

		packetPtr = (RLE8Packet *)((Ptr)packetPtr + offsetof(RLE8Packet, pixelData[1]));
	} else {
		packetPtr->pixelData[diffCharCount++] = prevPixel;
		packetPtr->opcode = 127 + diffCharCount;
		
		packetPtr = (RLE8Packet *)((Ptr)packetPtr + offsetof(RLE8Packet, pixelData[diffCharCount]));
	}
		
	*compressBufferSizePtr = (Ptr)packetPtr - compressBuffer;
}

// CompressRLE16
static void CompressRLE16(UInt16 *srcPtr, Size srcSize, Ptr compressBuffer, Size *compressBufferSizePtr)
{
	UInt16		prevPixel, currentPixel;
	UInt8		sameCharCount;
	UInt8		diffCharCount;
	RLE16Packet	*packetPtr;
		
	// Initialize some stuff
	sameCharCount = 1;
	diffCharCount = 0;

	packetPtr = (RLE16Packet *)compressBuffer;

	prevPixel = *srcPtr++;

	while (srcSize >= 2) {
		currentPixel = *srcPtr++;
		
		if (prevPixel == currentPixel) {
			// If diffCharCount > 0, we are holding pixels which should be read literally
			if (diffCharCount > 0) {
				packetPtr->opcode = 127 + diffCharCount;
				
				packetPtr = (RLE16Packet *)((Ptr)packetPtr + offsetof(RLE16Packet, pixelData[diffCharCount]));
				diffCharCount = 0;
			}
			
			if (sameCharCount < 128) {
				sameCharCount++;
			} else {
				packetPtr->opcode = sameCharCount - 1;
				packetPtr->pixelData[0] = prevPixel;

				packetPtr = (RLE16Packet *)((Ptr)packetPtr + offsetof(RLE16Packet, pixelData[1]));
				sameCharCount = 1;
			}
		} else {
			// If sameCharCount > 1, we are holding pixels which should be read repeatedly
			if (sameCharCount > 1)
			{
				packetPtr->opcode = sameCharCount - 1;
				packetPtr->pixelData[0] = prevPixel;

				packetPtr = (RLE16Packet *)((Ptr)packetPtr + offsetof(RLE16Packet, pixelData[1]));
				sameCharCount = 1;
			} else {
				packetPtr->pixelData[diffCharCount++] = prevPixel;
				
				if (diffCharCount == 128)
				{
					packetPtr->opcode = 127 + diffCharCount;
	
					packetPtr = (RLE16Packet *)((Ptr)packetPtr + offsetof(RLE16Packet, pixelData[diffCharCount]));
					diffCharCount = 0;
				}
			}
			
			prevPixel = currentPixel;
		}
		
		srcSize--;
	}
	
	// If sameCharCount > 1, we are holding pixels which should be read repeatedly
	// If not so, we are holding pixels which should be read literally
	if (sameCharCount > 1) {
		packetPtr->opcode = sameCharCount - 1;
		packetPtr->pixelData[0] = prevPixel;

		packetPtr = (RLE16Packet *)((Ptr)packetPtr + offsetof(RLE16Packet, pixelData[1]));
	} else {
		packetPtr->pixelData[diffCharCount++] = prevPixel;
		packetPtr->opcode = 127 + diffCharCount;

		packetPtr = (RLE16Packet *)((Ptr)packetPtr + offsetof(RLE16Packet, pixelData[diffCharCount]));
	}
		
	*compressBufferSizePtr = (Ptr)packetPtr - compressBuffer;
}

// CompressRLE32
static void CompressRLE32(UInt32 *srcPtr, Size srcSize, Ptr compressBuffer, Size *compressBufferSizePtr)
{
	UInt32		prevPixel, currentPixel;
	UInt8		sameCharCount;
	UInt8		diffCharCount;
	RLE32Packet	*packetPtr;
		
	// Initialize some stuff
	sameCharCount = 1;
	diffCharCount = 0;

	packetPtr = (RLE32Packet *)compressBuffer;

	prevPixel = *srcPtr++;

	while (srcSize >= 2) {
		currentPixel = *srcPtr++;
		
		if (prevPixel == currentPixel) {
			// If diffCharCount > 0, we are holding pixels which should be read literally
			if (diffCharCount > 0) {
				packetPtr->opcode = 127 + diffCharCount;

				packetPtr = (RLE32Packet *)((Ptr)packetPtr + offsetof(RLE32Packet, pixelData[diffCharCount]));
				diffCharCount = 0;
			}
			
			if (sameCharCount < 128) {
				sameCharCount++;
			} else {
				packetPtr->opcode = sameCharCount - 1;
				packetPtr->pixelData[0] = prevPixel;

				packetPtr = (RLE32Packet *)((Ptr)packetPtr + offsetof(RLE32Packet, pixelData[1]));
				sameCharCount = 1;
			}
		} else {
			// If sameCharCount > 1, we are holding pixels which should be read repeatedly
			if (sameCharCount > 1) {
				packetPtr->opcode = sameCharCount - 1;
				packetPtr->pixelData[0] = prevPixel;

				packetPtr = (RLE32Packet *)((Ptr)packetPtr + offsetof(RLE32Packet, pixelData[1]));
				sameCharCount = 1;
			} else {
				packetPtr->pixelData[diffCharCount++] = prevPixel;
				
				if (diffCharCount == 128) {
					packetPtr->opcode = 127 + diffCharCount;

					packetPtr = (RLE32Packet *)((Ptr)packetPtr + offsetof(RLE32Packet, pixelData[diffCharCount]));
					diffCharCount = 0;
				}
			}
			
			prevPixel = currentPixel;
		}
		
		srcSize--;
	}
	
	// If sameCharCount > 1, we are holding pixels which should be read repeatedly
	// If not so, we are holding pixels which should be read literally
	if (sameCharCount > 1) {
		packetPtr->opcode = sameCharCount - 1;
		packetPtr->pixelData[0] = prevPixel;

		packetPtr = (RLE32Packet *)((Ptr)packetPtr + offsetof(RLE32Packet, pixelData[1]));
	} else {
		packetPtr->pixelData[diffCharCount++] = prevPixel;
		packetPtr->opcode = 127 + diffCharCount;
		
		packetPtr = (RLE32Packet *)((Ptr)packetPtr + offsetof(RLE32Packet, pixelData[diffCharCount]));
	}
		
	*compressBufferSizePtr = (Ptr)packetPtr - compressBuffer;
}

#pragma mark-

// When building the *Application Version Only* make our component available for use by applications (or other clients).
// Once the Component Manager has registered a component, applications can find and open the component using standard
// Component Manager routines.
#if !STAND_ALONE && !TARGET_OS_WIN32
void EI_GraphicsExporterRegister(void);
void EI_GraphicsExporterRegister(void)
{
	ComponentDescription foo;	
	#if !TARGET_API_MAC_CARBON
		ComponentRoutineUPP componentEntryPoint = NewComponentRoutineProc(EI_GraphicsExportComponentDispatch);
	#else
		ComponentRoutineUPP componentEntryPoint = NewComponentRoutineUPP((ComponentRoutineProcPtr)EI_GraphicsExportComponentDispatch);
	#endif

  	foo.componentType = GraphicsExporterComponentType;
  	foo.componentSubType = FOUR_CHAR_CODE('EIDI');
  	foo.componentManufacturer = kAppleManufacturer;
  	foo.componentFlags = 0;
  	foo.componentFlagsMask = 0;

	RegisterComponent(&foo, componentEntryPoint, registerComponentGlobal, NULL, NULL, NULL);
}
#endif // !STAND_ALONE && !TARGET_OS_WIN32
