/*********************************************************/
/* 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 "CustomCopyBits.h"
#include "QuickDrawUtils.h"

#if !USE_ASM
#if __MWERKS__
#define kStackRowWidth 1024
#else
#define kStackRowWidth 1750
#endif
#endif

//=====================================================================================
// CopyData: structure for passing data between the C and assembly language portions of
// the code
//=====================================================================================

#if USE_ASM
typedef struct DitherErrors {
	short red;
	short green;
	short blue;
} DitherErrors, *DitherErrorsPtr;

typedef struct RgnBuffer {
	short yCount;
	short zero;
	short buffer[127];
} RgnBuffer, *RgnBufferPtr;
#else
typedef struct DitherErrors {
	long red;
	long green;
	long blue;
} DitherErrors, *DitherErrorsPtr;

typedef struct RgnBuffer {
	long yCount;
	long zero;
	long buffer[127];
} RgnBuffer, *RgnBufferPtr;
#endif

typedef (*DitherToDestPtr)(ulong *srcRow, void *dstRow, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns);

typedef struct CopyData {
	// Needed for all copy routines
	ColorSpec *ctAddr;		// color table address
	uchar *itAddr; 			// inverse color table address
	DitherErrorsPtr evenAddr,// even dithering buffer
		oddAddr;			// odd dithering buffer
	DitherToDestPtr even,	// address of even row dithering routine
		odd;	 			// address of odd row dithering routine
	short *errTable;		// address of dithering table
	long srcRow, 			// row bytes in source pixmap
		dstRow; 			// row bytes in destination pixmap
	Ptr srcBase, 			// base address of source pixmap
		dstBase;			// base address of destination pixmap
#if USE_ASM
	short rgnBuffer[512], 	// buffer for holding region data
		width, 				// total width of image
		height,				// total height of image
		evodd;				// word holding the even/odd row status
#else
	long rgnBuffer[512],
		width,	 			// total width of image
		height,				// total height of image
		evodd;				// word holding the even/odd row status
#endif
	RgnHandle theRgn;		// handle to the clipping region
	Rect boxRect;			// outer rect of the region

	// Needed only for CopyScaled routines
#if USE_ASM
	short srcHeight, 		// height of the source rectangle
		srcWidth, 			// width of same
		dstHeight, 			// height of the destination rectangle
		dstWidth,			// width of same
		xRemainder, 		// starting remainder in x
		yRemainder, 		// starting remainder in y
		columns, 			// internal counter for columns
		rows;				// internal counter for rows
#else
	long srcHeight, 		// height of the source rectangle
		srcWidth, 			// width of same
		dstHeight, 			// height of the destination rectangle
		dstWidth,			// width of same
		xRemainder, 		// starting remainder in x
		yRemainder, 		// starting remainder in y
		columns, 			// internal counter for columns
		rows;				// internal counter for rows
#endif
	Ptr outputAddr,			// internal holder for destination address
		sTable[6];			// pointers into the scaling table
} CopyData, *CopyDataPtr, **CopyDataHandle;

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

static Point gZeroPoint = { 0, 0 };
static RgnHandle gTheRgn = nil;
static CopyData gCopyData;

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

static void InitCopyData(void);

#if !USE_ASM
static void CopyUnscaledFrom32(CopyDataPtr theData);
static void CopyReallyFastScaledFrom32(CopyDataPtr theData);
static void CopyFastScaledFrom32(CopyDataPtr theData);
static void CopyScaledFrom32(CopyDataPtr theData);
static void CopyTo32(ulong *srcRow, void *dstRow, uchar *iTable, ColorSpec *cTable,
			DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, long *rgnBuffer, 
			short *errorMap, long columns);
static void DitherCopyTo16Even(ulong *srcRow, void *dstRow, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns);
static void DitherCopyTo16Odd(ulong *srcRow, void *dstRow, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns);
static void DitherCopyTo8Even(ulong *srcRow, void *dstRow, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns);
static void DitherCopyTo8Odd(ulong *srcRow, void *dstRow, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns);
static short *InitRegion(RgnPtr rgn, RgnBufferPtr rgnBuffer, Rect *boxRect);
static short *UpdateRegion(short *rgnData, RgnBufferPtr rgnBuffer, Rect *boxRect);
#elif THINK_C
static void CopyUnscaledFrom32(CopyDataPtr theData);
static void CopyReallyFastScaledFrom32(CopyDataPtr theData);
static void CopyScaledFrom32(CopyDataPtr theData);
static void CopyFastScaledFrom32(CopyDataPtr theData);
static void CopyTo32(void);
static void DitherCopyTo16Even(void);
static void DitherCopyTo16Odd(void);
static void DitherCopyTo8Even(void);
static void DitherCopyTo8Odd(void);
static void InitRegion(void);
static void UpdateRegion(void);
#else
extern void CopyUnscaledFrom32(CopyDataPtr theData);
extern void CopyReallyFastScaledFrom32(CopyDataPtr theData);
extern void CopyScaledFrom32(CopyDataPtr theData);
extern void CopyFastScaledFrom32(CopyDataPtr theData);
extern void CopyTo32(void);
extern void DitherCopyTo16Even(void);
extern void DitherCopyTo16Odd(void);
extern void DitherCopyTo8Even(void);
extern void DitherCopyTo8Odd(void);
#endif

//=====================================================================================
// void InitCopyData(void)
//
// One-time initialization of several static fields in the global CopyData structure
//
// External calls: none
//=====================================================================================

void InitCopyData(void)
{
	gCopyData.theRgn = gTheRgn = NewRgn();
	gCopyData.errTable = gErrTable;
	gCopyData.sTable[0] = (Ptr)gScaleTable;
	gCopyData.sTable[1] = gCopyData.sTable[0] + (3 << 8);
	gCopyData.sTable[2] = gCopyData.sTable[1] + (4 << 8);
	gCopyData.sTable[3] = gCopyData.sTable[2] + (5 << 8);
	gCopyData.sTable[4] = gCopyData.sTable[3] + (6 << 8);
	gCopyData.sTable[5] = gCopyData.sTable[4] + (7 << 8);
}

//=====================================================================================
// void DoCustomCopy(PixMap *src, PixMap *dst, Rect *srcRect, Rect *dstRect, 
//     Boolean fast, short depth)
//
// Performs the appropriate variation of our custom CopyBits routine, according to the
// fast, depth, and srcRect/dstRect parameters.
//
// External calls: GlobalRect, GlobalRgn, Width, Height
//=====================================================================================

void DoCustomCopy(PixMap *src, PixMap *dst, Rect *srcRect, Rect *dstRect, 
	Boolean fast, short depth)
{
	GDHandle theGDevice = GetGDevice();
	Boolean onScreen = ((*theGDevice)->gdFlags & (1L << screenActive)) != 0;
	char mode = true32b;
	CTabHandle theCTab;
	ITabHandle theITab;
	Rect globDRect;

	// Initialize the global copy data, if necessary
	if (!gTheRgn) InitCopyData();

	// Initialize the even/odd function pointers
	switch (depth) {
		case 32:
			gCopyData.even = (DitherToDestPtr)CopyTo32;
			gCopyData.odd = (DitherToDestPtr)CopyTo32;
			break;
		case 16:
			gCopyData.even = (DitherToDestPtr)DitherCopyTo16Even;
			gCopyData.odd = (DitherToDestPtr)DitherCopyTo16Odd;
			break;
		case 8:
			gCopyData.even = (DitherToDestPtr)DitherCopyTo8Even;
			gCopyData.odd = (DitherToDestPtr)DitherCopyTo8Odd;
			break;
	}
		
	// Globalize the destination rectangle
	globDRect = *dstRect;
	if (onScreen) GlobalRect(&globDRect, qd.thePort);

	// Calculate the global intersection of the clipRgn, visRgn, and dstRect
	// Note that we set the evodd value while the rectangle is still *local*
	RectRgn(gCopyData.theRgn, dstRect);
	SectRgn(qd.thePort->clipRgn, gCopyData.theRgn, gCopyData.theRgn);
	SectRgn(qd.thePort->visRgn, gCopyData.theRgn, gCopyData.theRgn);
	gCopyData.evodd = (*gCopyData.theRgn)->rgnBBox.top;
	if (onScreen) GlobalRgn(gCopyData.theRgn, qd.thePort);
	
	// If the resulting region is empty, return before we do anything
	if (EmptyRgn(gCopyData.theRgn)) return;

	// Use the resulting region bounding box as our real dst rect
	gCopyData.boxRect = (*gCopyData.theRgn)->rgnBBox;
	gCopyData.height = Height(&gCopyData.boxRect);
	gCopyData.width = Width(&gCopyData.boxRect);

	// Calculate widths and heights
	gCopyData.dstHeight = Height(dstRect);
	gCopyData.dstWidth = Width(dstRect);
	gCopyData.srcHeight = Height(srcRect);
	gCopyData.srcWidth = Width(srcRect);

	// Get the base addresses, offset by the dst and src rectangles
	gCopyData.srcBase = GetPixBaseAddr(&src);
	gCopyData.dstBase = GetPixBaseAddr(&dst);
	gCopyData.srcRow = src->rowBytes & 0x3fff;
	gCopyData.dstRow = dst->rowBytes & 0x3fff;
	gCopyData.srcBase += gCopyData.srcRow *
		((srcRect->top - src->bounds.top) +
		((long)(gCopyData.boxRect.top - globDRect.top) * 
		 (long)gCopyData.srcHeight / (long)gCopyData.dstHeight));
	gCopyData.srcBase += ((srcRect->left - src->bounds.left) +
		(long)(gCopyData.boxRect.left - globDRect.left) * 
		(long)gCopyData.srcWidth / (long)gCopyData.dstWidth) << 2;
	gCopyData.dstBase += gCopyData.dstRow *
		(gCopyData.boxRect.top - dst->bounds.top) + 
		(((long)(gCopyData.boxRect.left - dst->bounds.left) * (long)dst->pixelSize) >> 3);

	// Calculate initial remainders for the source at this point
	gCopyData.xRemainder = gCopyData.dstWidth -
		((long)(gCopyData.boxRect.left - globDRect.left) * (long)gCopyData.srcWidth) % 
		(long)gCopyData.dstWidth;
#if USE_ASM
	gCopyData.xRemainder = ((long)gCopyData.xRemainder << 10) / (long)gCopyData.srcWidth;
#else
	gCopyData.xRemainder = ((long)gCopyData.xRemainder << 16) / (long)gCopyData.srcWidth;
#endif
	gCopyData.yRemainder = gCopyData.dstHeight -
		((long)(gCopyData.boxRect.top - globDRect.top) * (long)gCopyData.srcHeight) % 
		(long)gCopyData.dstHeight;
#if USE_ASM
	gCopyData.yRemainder = ((long)gCopyData.yRemainder << 10) / (long)gCopyData.srcHeight;
#else
	gCopyData.yRemainder = ((long)gCopyData.yRemainder << 16) / (long)gCopyData.srcHeight;
#endif

	// Get handles to the color tables
	theCTab = (*(*theGDevice)->gdPMap)->pmTable;
	theITab = (*theGDevice)->gdITable;

	// Shield the cursor if necessary and get the final pointers
	if (onScreen) ShieldCursor(&gCopyData.boxRect, gZeroPoint);
	if (theITab) gCopyData.itAddr = (*theITab)->iTTable;
	if (theCTab) gCopyData.ctAddr = (*theCTab)->ctTable;

	// Do the copy
	SwapMMUMode(&mode);
	if (EqualSizeRect(srcRect, dstRect)) CopyUnscaledFrom32(&gCopyData);
	else if (fast) CopyReallyFastScaledFrom32(&gCopyData);//CopyFastScaledFrom32(&gCopyData);
	else CopyScaledFrom32(&gCopyData);
	SwapMMUMode(&mode);
	if (onScreen) ShowCursor();
}

#if !USE_ASM

//=====================================================================================
// Generic C substitutes for the assembly functions defined below.
//=====================================================================================

static void CopyUnscaledFrom32(CopyDataPtr theData)
{
	#include "CopyUnscaledFrom32.c"
}

static void CopyReallyFastScaledFrom32(CopyDataPtr theData)
{
	#include "CopyReallyFastScaledFrom32.c"
}

static void CopyFastScaledFrom32(CopyDataPtr theData)
{
	#include "CopyFastScaledFrom32.c"
}

static void CopyScaledFrom32(CopyDataPtr theData)
{
	#include "CopyScaledFrom32.c"
}

static void CopyTo32(ulong *srcRow, void *dstRow, uchar *iTable, ColorSpec *cTable,
			DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, long *rgnBuffer, 
			short *errorMap, long columns)
{
	#include "CopyTo32.c"
}

static void DitherCopyTo16Even(ulong *srcRow, void *dstRowStart, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns)
{
	#include "DitherCopyTo16Even.c"
}

static void DitherCopyTo16Odd(ulong *srcRow, void *dstRowStart, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns)
{
	#include "DitherCopyTo16Odd.c"
}

static void DitherCopyTo8Even(ulong *srcRow, void *dstRowStart, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns)
{
	#include "DitherCopyTo8Even.c"
}

static void DitherCopyTo8Odd(ulong *srcRow, void *dstRowStart, uchar *iTable, 
			ColorSpec *cTable, DitherErrorsPtr eBuffer, DitherErrorsPtr oBuffer, 
			long *rgnBuffer, short *errorMap, long columns)
{
	#include "DitherCopyTo8Odd.c"
}

static short *InitRegion(RgnPtr rgn, RgnBufferPtr rgnBuffer, Rect *boxRect)
{
	#include "InitRegion.c"
}

static short *UpdateRegion(short *rgnData, RgnBufferPtr rgnBuffer, Rect *boxRect)
{
	#include "UpdateRegion.c"
}

#elif THINK_C

//=====================================================================================
// Assembly language interfaces for THINK C; here we define the offsets of all fields
// within the CopyData structure, and then provide C function wrappers around asm { }
// statements that #include the actual assembly code.  In MPW, we just need to compile
// CustomCopyBits.a to get the assembly routines.
//=====================================================================================

#define ctAddr offsetof(CopyData, ctAddr)		/*    0 */
#define itAddr offsetof(CopyData, itAddr)		/*    4 */
#define evenAddr offsetof(CopyData, evenAddr)	/*    8 */
#define oddAddr offsetof(CopyData, oddAddr)		/*   12 */
#define even offsetof(CopyData, even)			/*   16 */
#define odd offsetof(CopyData, odd)				/*   20 */
#define errTable offsetof(CopyData, errTable)	/*   24 */
#define srcRow offsetof(CopyData, srcRow)		/*   28 */
#define dstRow offsetof(CopyData, dstRow)		/*   32 */
#define srcBase offsetof(CopyData, srcBase)		/*   36 */
#define dstBase offsetof(CopyData, dstBase)		/*   40 */
#define rgnBuffer offsetof(CopyData, rgnBuffer)	/*   44 */
#define width offsetof(CopyData, width)			/* 1068 */
#define height offsetof(CopyData, height)		/* 1070 */
#define evodd offsetof(CopyData, evodd)			/* 1072 */
#define theRgn offsetof(CopyData, theRgn)		/* 1074 */
#define boxRect offsetof(CopyData, boxRect)		/* 1078 */
#define srcHeight offsetof(CopyData, srcHeight)	/* 1086 */
#define srcWidth offsetof(CopyData, srcWidth)	/* 1088 */
#define dstHeight offsetof(CopyData, dstHeight)	/* 1090 */
#define dstWidth offsetof(CopyData, dstWidth)	/* 1092 */
#define xRemainder offsetof(CopyData, xRemainder)/* 1094 */
#define yRemainder offsetof(CopyData, yRemainder)/* 1096 */
#define columns offsetof(CopyData, columns)		/* 1098 */
#define rows offsetof(CopyData, rows)			/* 1100 */
#define outputAddr offsetof(CopyData, outputAddr)/* 1102 */
#define sTable offsetof(CopyData, sTable)		/* 1106 */

void CopyUnscaledFrom32(CopyDataPtr theData)
{
	asm {
		#include "CopyUnscaledFrom32.a"
	}
}

void CopyScaledFrom32(CopyDataPtr theData)
{
	asm {
		#include "CopyScaledFrom32.a"
	}
}

void CopyReallyFastScaledFrom32(CopyDataPtr theData)
{
	asm {
		#include "CopyReallyFastScaledFrom32.a"
	}
}

void CopyFastScaledFrom32(CopyDataPtr theData)
{
	asm {
		#include "CopyFastScaledFrom32.a"
	}
}

void CopyTo32(void)
{
	asm {
		#include "CopyTo32.a"
	}
}

void DitherCopyTo16Even(void)
{
	asm {
		#include "DitherCopyTo16Even.a"
	}
}

void DitherCopyTo16Odd(void)
{
	asm {
		#include "DitherCopyTo16Odd.a"
	}
}

void DitherCopyTo8Even(void)
{
	asm {
		#include "DitherCopyTo8Even.a"
	}
}

void DitherCopyTo8Odd(void)
{
	asm {
		#include "DitherCopyTo8Odd.a"
	}
}

void InitRegion(void)
{
	asm {
		#include "InitRegion.a"
	}
}

void UpdateRegion(void)
{
	asm {
		#include "UpdateRegion.a"
	}
}

#endif
