/*********************************************************/
/* 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 "PNTG.h"

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

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

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

extern Boolean idPNTG(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
{
#if applec
#pragma unused(theData, theSize, theSpec)
#endif
	OSErr theErr;
	long size;
	
	theErr = GetEOF(refNum, &size);
	return (theErr == noErr && size <= (512 + 720 * 73));
}

//=====================================================================================
// OSErr OpenPNTG(ImageHandle theImage)
//=====================================================================================
// Initializes the image record for a PNTG image.
//=====================================================================================

extern OSErr OpenPNTG(ImageHandle theImage)
{
	OSErr theErr = noErr;

	(*theImage)->grect.right = 576;
	(*theImage)->grect.bottom = 720;
	(*theImage)->depth = 1;
	(*theImage)->qrect = (*theImage)->crect = (*theImage)->grect;
	(*theImage)->compression = kPackBitsCompression;
	BlockMove(gString[strPackBits], (*theImage)->compressionDesc, *gString[strPackBits] + 1);
	return theErr;
}

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

extern OSErr DrawPNTG(Handle theHandle, JVDrawParamsHandle theParams)
{
	NestedProgress theProgress = (*theParams)->progress;
	char hState = HGetState(theHandle);
	OSErr theErr = noErr;
	GWorldPtr theGWorld;
	uchar *dataStart;
	
	if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc, 
				codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
	HLockHi(theHandle);
	if (theErr == noErr) {
		dataStart = (uchar *)StripAddress(*theHandle);
		theGWorld = NewTempGWorld(576, 720, 1, nil);
		if (theGWorld) {
			theErr = DecompressPNTGImage(dataStart, 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 DecompressPNTGImage(uchar *dataStart, PixMapHandle dstPixMap,
//			ICMProgressProcRecordPtr prog, Size length)
//=====================================================================================
// Oversees the decompression of a PNTG image, handling banding, and calling the
// appropriate function to perform the actual decompression.
//=====================================================================================

static OSErr DecompressPNTGImage(uchar *dataStart, PixMapHandle dstPixMap,
			ICMProgressProcRecordPtr prog, Size length)
{
	uchar *ptr, *rowStart, *dst, *dataEnd = dataStart + length;
	char pState = GetPixelsState(dstPixMap);
	RgnHandle dstRgn = qd.thePort->visRgn;
	ulong bandRow = 0, row;
	char mmuMode = true32b;
	OSErr theErr = noErr;
	
	LockPixels(dstPixMap);
	rowStart = (uchar *)GetPixBaseAddr(dstPixMap);
	ptr = dataStart + 512;
	for (row = 0; row < 720; row++) {
		if (ptr < dataStart || ptr > dataEnd) {
			theErr = codecBadDataErr;
			break;
		}
		SwapMMUMode(&mmuMode);
		dst = rowStart;
		UnpackBits((Ptr *)&ptr, (Ptr *)&dst, 576 / 8);
		SwapMMUMode(&mmuMode);
		rowStart += ((*dstPixMap)->rowBytes & 0x3fff);
		if (!(row & 63) && prog->progressProc)
			theErr = (OSErr)CallICMProgressProc(prog->progressProc, 
						codecProgressUpdatePercent,
						FixRatio(row, 720), 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);
}
