/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information.       */
/* Contact email: mac@aarongiles.com                     */
/*********************************************************/

//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================

#if THINK_C
#include "THINK.Header"
#include <stddef.h>
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif

//=====================================================================================
// Includes specific to this module
//=====================================================================================

#include "SCRN.h"

//=====================================================================================
// SCRNPrivates: private data structure for SCRN images, maintained for each image.
//=====================================================================================

typedef struct SCRNPrivates SCRNPrivates, *SCRNPrivatesPtr, **SCRNPrivatesHandle;

struct SCRNPrivates
{
	OSType id;					// id flag to be sure that we got the right privates
	Boolean oldFormat;			// flag: true if this is an old-format image
};

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

static FSSpec gLastSpec;

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

static OSErr DecompressSCRNImage(uchar *dataStart, SCRNPrivatesHandle privates,
			PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length);
static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow);

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

extern Boolean idSCRN(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
{
#if applec
#pragma unused(theData, theSize)
#endif
	Boolean ok = false;
	OSErr theErr;
	long size;
	
	gLastSpec.vRefNum = 0;
	theErr = GetEOF(refNum, &size);
	if (theErr == noErr && size == (512 / 8 * 342)) ok = true;
	refNum = FSpOpenResFile(theSpec, fsRdPerm);
	if (refNum != -1) {
		Handle dummyHandle;
		SetResLoad(false);
		dummyHandle = GetResource(kPICTType, 0);
		if (dummyHandle) ReleaseResource(dummyHandle);
		SetResLoad(true);
		CloseResFile(refNum);
		if (dummyHandle) {
			gLastSpec = *theSpec;
			ok = true;
		}
	}
	return ok;
}

//=====================================================================================
// OSErr LoadSCRN(short theFile, ImageHandle theImage)
//=====================================================================================
// Loads the SCRN image into memory from disk.
//=====================================================================================

extern OSErr LoadSCRN(short theFile, ImageHandle theImage) 
{
	OSErr theErr = noErr;
	
	KeepSpinning();
	if (gLastSpec.vRefNum) {
		theFile = FSpOpenResFile(&gLastSpec, fsRdPerm);
		if (theFile != -1) {
			(*theImage)->data = GetResource(kPICTType, 0);
			if ((*theImage)->data) DetachResource((*theImage)->data);
			CloseResFile(theFile);
			if (!(*theImage)->data) gIntError = errNoLoadMemory, theErr = memFullErr;
		} else gIntError = errCantReadFile, theErr = ResError();
	} else theErr = DefaultLoad(theFile, theImage);
	return theErr;
}

//=====================================================================================
// OSErr OpenSCRN(ImageHandle theImage)
//=====================================================================================
// Initializes the image record for a SCRN image.
//=====================================================================================

extern OSErr OpenSCRN(ImageHandle theImage)
{
	SCRNPrivatesHandle privates;
	OSErr theErr = noErr;

	if (!(privates = (SCRNPrivatesHandle)NewHandleClear(sizeof(SCRNPrivates))))
		return memFullErr;
	(*theImage)->privateData = (Handle)privates;
	(*privates)->id = kSCRNType;
	(*privates)->oldFormat = (gLastSpec.vRefNum == 0);
	if ((*privates)->oldFormat) {
		(*theImage)->grect.right = 512;
		(*theImage)->grect.bottom = 342;
		(*theImage)->depth = 1;
		(*theImage)->qrect = (*theImage)->crect = (*theImage)->grect;
		(*theImage)->compression = 0;
		BlockMove(gString[strUncompressed], (*theImage)->compressionDesc, *gString[strUncompressed] + 1);
	} else {
		DisposeHandle((Handle)privates);
		(*theImage)->privateData = nil;
		theErr = OpenPICT(theImage);
	}
	return theErr;
}

//=====================================================================================
// OSErr DrawSCRN(Handle theHandle, JVDrawParamsHandle theParams)
//=====================================================================================
// Draws a SCRN image into the specified GWorld, from the srcRect into the dstRect,
// clipping as dstRgn specifies.
//=====================================================================================

extern OSErr DrawSCRN(Handle theHandle, JVDrawParamsHandle theParams)
{
	SCRNPrivatesHandle privates = (SCRNPrivatesHandle)(*theParams)->privateData;
	NestedProgress theProgress = (*theParams)->progress;
	char hState = HGetState(theHandle);
	OSErr theErr = noErr;
	GWorldPtr theGWorld;
	uchar *dataStart;
	
	if (!privates || (*privates)->id != kSCRNType || !(*privates)->oldFormat)
		return DrawPICT(theHandle, theParams);
	if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc, 
				codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
	HLockHi(theHandle);
	if (theErr == noErr) {
		dataStart = (uchar *)StripAddress(*theHandle);
		theGWorld = NewTempGWorld(512, 342, 1, nil);
		if (theGWorld) {
			theErr = DecompressSCRNImage(dataStart, privates, GetGWorldPixMap(theGWorld), 
						(ICMProgressProcRecordPtr)&theProgress, GetHandleSize(theHandle));
			if ((*theParams)->progress.aborted) theErr = codecAbortErr;
			DisposeGWorld(theGWorld);
		} else gIntError = errNoDrawMemory, theErr = memFullErr;
	}
	HSetState(theHandle, hState);
	if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc, 
				codecProgressUpdatePercent, 0x00010000L, theProgress.prog.progressRefCon);
	return theErr;
}

//=====================================================================================
// OSErr FixSCRN(ImageHandle theImage, Handle *finalData, Boolean palette)
//=====================================================================================
// Called to convert a JPEG image into a SCRN image; here we just convert it to a PICT
// and then save.
//=====================================================================================

extern OSErr FixSCRN(ImageHandle theImage, Handle *finalData, Boolean palette)
{
	return FixPICT(theImage, finalData, palette);
}

//=====================================================================================
// OSErr SaveSCRN(FSSpec *theSpec, Handle theHandle, GWorldPtr thePreview,
//			Rect *theRect, Handle privates)
//=====================================================================================
// Called to save a SCRN image to disk.
//=====================================================================================

extern OSErr SaveSCRN(FSSpec *theSpec, Handle theHandle, GWorldPtr thePreview,
			Rect *theRect, Handle thePrivates) 
{
	SCRNPrivatesHandle privates = (SCRNPrivatesHandle)thePrivates;
	Handle resPict = theHandle;
	short theFile;
	OSErr theErr;

	if (privates && (*privates)->id == kSCRNType && (*privates)->oldFormat)
		return DefaultSave(theSpec, theHandle, thePreview, theRect, kSCRNType);
	if (MakeMemAvailable(GetHandleSize(theHandle))) {
		theErr = HandToHand(&resPict);
		if (theErr == noErr) {
			KeepSpinning();
			FSpCreateResFile(theSpec, kCreator, kSCRNType, 0);
			KeepSpinning();
			theFile = FSpOpenResFile(theSpec, fsRdWrPerm);
			if (theFile) {
				SpinIndef();
				AddResource(resPict, kPICTType, 0, gString[strJPEGViewPICT]);
				if ((theErr = ResError()) == noErr) {
					WriteResource(resPict);
					KeepSpinning();
					ReleaseResource(resPict);
					CloseResFile(theFile);
					return noErr;
				} else gIntError = errCantWriteFile;
				CloseResFile(theFile);
			} else gIntError = errCantOpenFile, theErr = fnOpnErr;
			FSpDelete(theSpec);
			DisposeHandle(resPict);
		} else gIntError = errNoSaveMemory, theErr = memFullErr;
	} else gIntError = errNoSaveMemory, theErr = memFullErr;
	KeepSpinning();
	return theErr;
}

//=====================================================================================
// OSErr DecompressSCRNImage(uchar *dataStart, SCRNPrivatesHandle privates,
//			PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
//=====================================================================================
// Oversees the decompression of a SCRN image, handling banding, and calling the
// appropriate function to perform the actual decompression.
//=====================================================================================

static OSErr DecompressSCRNImage(uchar *dataStart, SCRNPrivatesHandle privates,
			PixMapHandle dstPixMap, ICMProgressProcRecordPtr prog, Size length)
{
#if applec
#pragma unused(privates)
#endif
	uchar *ptr, *rowStart, *dst, *dataEnd = dataStart + length;
	char pState = GetPixelsState(dstPixMap);
	RgnHandle dstRgn = qd.thePort->visRgn;
	ulong bandRow = 0, row, col;
	char mmuMode = true32b;
	OSErr theErr = noErr;
	
	LockPixels(dstPixMap);
	rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
	ptr = dataStart;
	for (row = 0; row < 342; row++) {
		if (ptr < dataStart || ptr > dataEnd) {
			theErr = codecBadDataErr;
			break;
		}
		SwapMMUMode(&mmuMode);
		dst = rowStart;
		for (col = 0; col < 64; col++) *dst++ = *ptr++;
		SwapMMUMode(&mmuMode);
		rowStart += ((*dstPixMap)->rowBytes & 0x3fff);
		if (!(row & 63) && prog->progressProc)
			theErr = (OSErr)CallICMProgressProc(prog->progressProc, 
						codecProgressUpdatePercent,
						FixRatio(row, 342), prog->progressRefCon);
		if (++bandRow == Height(&(*dstPixMap)->bounds)) {
			CopyPixMapToDestination(dstPixMap, row - bandRow + 1);
			bandRow = 0;
			rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
		}
		if (theErr != noErr) break;
	}
	if (theErr == noErr && bandRow) CopyPixMapToDestination(dstPixMap, row - bandRow);
	SetPixelsState(dstPixMap, pState);
	return theErr;
}

//=====================================================================================
// void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
//=====================================================================================
// Copies the band contained in srcPixMap to the destination, offset from the top by
// startRow.
//=====================================================================================

static void CopyPixMapToDestination(PixMapHandle srcPixMap, short startRow)
{
	Rect srcRect = (*srcPixMap)->bounds, dstRect = (*srcPixMap)->bounds;
	PixMapHandle dstPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
	char srcState = HGetState((Handle)srcPixMap);
	char dstState = HGetState((Handle)dstPixMap);
	
	OffsetRect(&dstRect, 0, startRow);
	HLock((Handle)srcPixMap);
	HLock((Handle)dstPixMap);
	CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, &srcRect, &dstRect,
				srcCopy + ditherCopy, qd.thePort->visRgn);
	HSetState((Handle)dstPixMap, dstState);
	HSetState((Handle)srcPixMap, srcState);
}
