/*********************************************************/
/* 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"
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif

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

#include "QuickDrawUtils.h"

//=====================================================================================
// PortStackItem: structure to store port and device information for when we push/pop 
// the current port.
//=====================================================================================

typedef struct PortStackItem {
	CGrafPtr port;
	GDHandle device;
} PortStackItem, *PortStackPtr, **PortStackHandle;

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

static PortStackItem gPortStack[64];
static PortStackPtr gPortStackPtr = gPortStack;

//=====================================================================================
// void PushPort(void)
//
// Pushes the current port and device onto the port stack.
//
// External calls: none
//=====================================================================================

void PushPort(void)
{
	GetGWorld(&gPortStackPtr->port, &gPortStackPtr->device);
	gPortStackPtr++;
}

//=====================================================================================
// void PopPort(void)
//
// Pops a port and device from the port stack into the current graphics environment.
//
// External calls: none
//=====================================================================================

void PopPort(void)
{
	gPortStackPtr--;
	SetGWorld(gPortStackPtr->port, gPortStackPtr->device);
}

//=====================================================================================
// void MySetPort(CGrafPtr thePort)
//
// Sets the current port; if nil is passed as the port, the current port is set to
// the window manager's port.
//
// External calls: none
//=====================================================================================

void MySetPort(CGrafPtr thePort) 
{
	if (!thePort) GetCWMgrPort(&thePort);
	if (thePort) SetGWorld(thePort, nil);
}

extern GWorldPtr NewTempGWorld(short width, short height, short depth, CTabHandle colors)
{
	Boolean oldState = GetQDxDispatchPatchState();
	GWorldPtr theGWorld = nil;
	OSErr theErr = noErr;
	Rect theRect;
	
	SetRect(&theRect, 0, 0, width, height);
	do {
		InstallQDxDispatchPatch(false);
		theErr = NewGWorld(&theGWorld, depth, &theRect, colors, nil, keepLocal + useTempMem);
		SetQDxDispatchPatchState(oldState);
		if (theErr != noErr) {
			if (IsMemAvailable(EstGWorldSize(&theRect, depth)))
				theErr = NewGWorld(&theGWorld, depth, &theRect, colors, nil, keepLocal);
			else theErr = memFullErr;
		}
		if (theErr != noErr) {
			if (theRect.bottom == 8) {
				theGWorld = nil;
				break;
			}
			theRect.bottom -= height / 8;
			if (theRect.bottom < 8) theRect.bottom = 8;
		}
	} while (theErr != noErr);
	return (theErr == noErr) ? theGWorld : nil;
}

//=====================================================================================
// GWorldPtr MyNewGWorld(Rect *theRect, short theDepth, CTabHandle theColors, 
//     GDHandle theDevice, Boolean whiteBack, Boolean inTempMem)
//
// Creates a new GWorld, erases it, and initializes the colors.
//
// External calls: none
//=====================================================================================

GWorldPtr MyNewGWorld(Rect *theRect, short theDepth, CTabHandle theColors, 
	GDHandle theDevice, Boolean whiteBack, Boolean inTempMem)
{
	Boolean oldState = GetQDxDispatchPatchState();
	long flags = keepLocal;
	GWorldPtr theGWorld;
	
	PushPort();
	MySetPort(nil);
	if (inTempMem) {
		flags += useTempMem;
		InstallQDxDispatchPatch(false);
	}
	if (NewGWorld(&theGWorld, theDepth, theRect, theColors, theDevice, flags) == noErr) {
		MySetPort(theGWorld);
		LockPixels(GetGWorldPixMap(theGWorld));
		RectRgn(theGWorld->visRgn, theRect);
		RectRgn(theGWorld->clipRgn, theRect);
		RGBForeColor(&gBlack);
		RGBBackColor(&gWhite);
		if (!whiteBack) PaintRect(theRect);
		else EraseRect(theRect);
		UnlockPixels(GetGWorldPixMap(theGWorld));
		PopPort();
		if (inTempMem) SetQDxDispatchPatchState(oldState);
		return theGWorld;
	}
	PopPort();
	if (inTempMem) SetQDxDispatchPatchState(oldState);
	return nil;
}

extern void MaxRect(Rect *srcRect, Rect *boundingRect, Rect *dstRect)
{
	dstRect->top = dstRect->left = 0;
	if (Width(srcRect) > Height(srcRect)) {
		dstRect->right = Width(boundingRect);
		dstRect->bottom = (long)Height(srcRect) * Width(boundingRect) / (long)Width(srcRect);
	} else {
		dstRect->right = (long)Width(srcRect) * Height(boundingRect) / (long)Height(srcRect);
		dstRect->bottom = Height(boundingRect);
	}
	OffsetRect(dstRect, boundingRect->left, boundingRect->top);
}

extern void MaxSquare(Rect *srcRect, Rect *squareRect)
{
	*squareRect = *srcRect;
	if (Width(srcRect) > Height(srcRect)) {
		squareRect->left += (Width(srcRect) - Height(srcRect)) >> 1;
		squareRect->right = squareRect->left + Height(srcRect);
	} else {
		squareRect->top += (Height(srcRect) - Width(srcRect)) >> 1;
		squareRect->bottom = squareRect->top + Width(srcRect);
	}
}

extern void CopyGWorldToGWorld(GWorldPtr gw1, GWorldPtr gw2, Rect *srcRect, Rect *dstRect,
			short mode, RgnHandle mask)
{
	PixMapHandle pm1 = GetGWorldPixMap(gw1), pm2 = GetGWorldPixMap(gw2);
	GWorldFlags state1 = GetPixelsState(pm1), state2 = GetPixelsState(pm2);
	char hstate1 = HGetState((Handle)pm1), hstate2 = HGetState((Handle)pm2);
	
	PushPort();
	MySetPort(gw2);
	HLock((Handle)pm1);
	HLock((Handle)pm2);
	LockPixels(pm1);
	LockPixels(pm2);
	CopyBits((BitMap *)*pm1, (BitMap *)*pm2, srcRect, dstRect, mode, mask);
	SetPixelsState(pm1, state1);
	SetPixelsState(pm2, state2);
	HSetState((Handle)pm1, hstate1);
	HSetState((Handle)pm2, hstate2);
	PopPort();
}

//=====================================================================================
// void GlobalRect(Rect *theRect, WindowPtr theWindow)
//
// Converts the given rectangle to global coordinates, relative to the window.
//
// External calls: none
//=====================================================================================

void GlobalRect(Rect *theRect, WindowPtr theWindow)
{
	Boolean change = (theWindow != qd.thePort);
	Point thePoint = { 0, 0 };
	
	if (change) {
		PushPort();
		MySetPort((CGrafPtr)theWindow);
	}
	LocalToGlobal(&thePoint);
	if (change) PopPort();
	OffsetRect(theRect, thePoint.h, thePoint.v);
}

//=====================================================================================
// void GlobalRgn(RgnHandle theRgn, WindowPtr theWindow)
//
// Converts the given region to global coordinates, relative to the window.
//
// External calls: none
//=====================================================================================

void GlobalRgn(RgnHandle theRgn, WindowPtr theWindow)
{
	Boolean change = (theWindow != qd.thePort);
	Point thePoint = { 0, 0 };
	
	if (change) {
		PushPort();
		MySetPort((CGrafPtr)theWindow);
	}
	LocalToGlobal(&thePoint);
	if (change) PopPort();
	OffsetRgn(theRgn, thePoint.h, thePoint.v);
}

//=====================================================================================
// void LocalRect(Rect *theRect, WindowPtr theWindow)
//
// Converts the given rectangle to local coordinates, relative to the window.
//
// External calls: none
//=====================================================================================

void LocalRect(Rect *theRect, WindowPtr theWindow)
{
	Boolean change = (theWindow != qd.thePort);
	Point thePoint = { 0, 0 };

	if (change) {
		PushPort();
		MySetPort((CGrafPtr)theWindow);
	}
	GlobalToLocal(&thePoint);
	if (change) PopPort();
	OffsetRect(theRect, thePoint.h, thePoint.v);
}

//=====================================================================================
// void LocalRgn(RgnHandle theRgn, WindowPtr theWindow)
//
// Converts the given region to local coordinates, relative to the window.
//
// External calls: none
//=====================================================================================

void LocalRgn(RgnHandle theRgn, WindowPtr theWindow)
{
	Boolean change = (theWindow != qd.thePort);
	Point thePoint = { 0, 0 };
	
	if (change) {
		PushPort();
		MySetPort((CGrafPtr)theWindow);
	}
	GlobalToLocal(&thePoint);
	if (change) PopPort();
	OffsetRgn(theRgn, thePoint.h, thePoint.v);
}

//=====================================================================================
// Boolean Clipped(Rect *theRect, RgnHandle clipRgn)
//
// Determines if the given rectangle is clipped by the region.
//
// External calls: none
//=====================================================================================

Boolean Clipped(Rect *theRect, RgnHandle clipRgn)
{
	RgnHandle rectRgn, clippedRgn;
	Boolean result = false;
	
	if (!clipRgn || !theRect) return false;
	if (rectRgn = NewRgn()) {
		if (clippedRgn = NewRgn()) {
			RectRgn(rectRgn, theRect);
			SectRgn(rectRgn, clipRgn, clippedRgn);
			result = !EqualRgn(rectRgn, clippedRgn);
			DisposeRgn(clippedRgn);
		}
		DisposeRgn(rectRgn);
	}
	return result;
}
