//		offscreen_manager.c

#include "MemUtils.h"
#include "Carbon68kGlue.h"
#include "offscreen_manager.h"
#include "DeviceManager.h"

/**		DrawPict
	draws an offscreen into the current port, which itself may be an offscreen
**/
void		DrawPict(GWorldPtr theOffscreen, short h, short v)
{
	Rect		destRect, srcRect;
	CGrafPtr	thePort;

	GetPort((GrafPtr *)&thePort);
	GetPortBounds(theOffscreen, &destRect);
	srcRect = destRect;

	OffsetRect(&destRect, h - srcRect.left, v - srcRect.top);

	if (!CopyOffscreenToOffscreen(thePort, &srcRect, theOffscreen, &destRect))
		DebugStr("\pYou got hosed by CopyBitsÉ");		//		$$$
}

void		DrawPictWithMask(GWorldPtr theOffscreen, GWorldPtr theMask, short h, short v)
{
	Rect			srcRect, maskRect, destRect;
	CGrafPtr		thePort;
	Boolean			success = FALSE;
	PixMapHandle	offsPmH = GetPortPixMap(theOffscreen);
	PixMapHandle	maskPmH = GetPortPixMap(theMask);
	
	GetPort((GrafPtr *)&thePort);

	GetPortBounds(theOffscreen, &destRect);

	srcRect = maskRect = destRect;
	OffsetRect(&destRect, h - srcRect.left, v - srcRect.top);

	if (LockPixels(offsPmH))
	{
		if (LockPixels(maskPmH))
		{
			success = TRUE;

			CopyMask(	GetPortBitMapForCopyBits(theOffscreen), 
						GetPortBitMapForCopyBits(theMask), 
						GetPortBitMapForCopyBits(thePort), 
						&srcRect, &maskRect, &destRect);

			UnlockPixels(maskPmH);
		}
		UnlockPixels(offsPmH);
	}
	
	if (!success)
		DebugStr("\pYou got hosed by CopyMaskÉ");		//		$$$
}

/*******
 **
 **  PICT_to_offscreen()
 **
 **  Convert a PICT to an offscreen pixel map.  Returns NULL if the offscreen
 **  couldn't be allocated.
 **
 *******/
 
GWorldPtr	PICT_to_offscreen(PicHandle thePICT, int depth)
{
	GWorldPtr		offscreen = NULL;
	Rect			pictureFrame;
	SignedByte		hState;
		
	if (thePICT != NULL)
	{
		pictureFrame = (**thePICT).picFrame;
		OffsetRect(&pictureFrame, -pictureFrame.left, -pictureFrame.top);
		
		offscreen = CreateOffscreen(pictureFrame, depth, 0L);
	
		if (offscreen != NULL)
		{
			GWorldFlags		pixState;
			PortAndDevice	savedPortAndDevice;
	
			GetPortAndDevice(&savedPortAndDevice);
	
			UseOffscreen(offscreen, &pixState);
			LoadResource((Handle)thePICT);
			hState = HGetState((Handle)thePICT);
			HUnlock((Handle)thePICT);
			MoveHHi((Handle)thePICT);
			HLock((Handle)thePICT);
			DrawPicture(thePICT, &pictureFrame);
			HSetState((Handle)thePICT, hState);
			StopUsingOffscreen(offscreen, &pixState);
	
			SetPortAndDevice(&savedPortAndDevice);
		}
	}
	
	return(offscreen);
}

GWorldPtr	CreateWindowOffscreen(WindowPtr theWindow, short pixelDepth, long options)
{
	Rect		windowBounds;
	GrafPtr		savePort;
	GWorldPtr	offscreenPtr;

	GetPortBounds(GetWindowPort(theWindow), &windowBounds);
	GetPort(&savePort);
	SetPortWindowPort(theWindow);
	
	if (pixelDepth == 0) {	/* if we are using the screen depth */
		SetOrigin((short)(-windowBounds.left), (short)(-windowBounds.top));
	}

	offscreenPtr = CreateOffscreen(windowBounds, pixelDepth, options);
	SetOrigin(0,0);
	SetPort(savePort);
	return(offscreenPtr);		/*(  includes error of offscreenPtr == NULL  )*/
}

CTabHandle	gCTab = NULL;

int			UpdateWindowOffscreen(	WindowPtr theWindow, GWorldPtr *offscreenPtr,
									short pixelDepth, GWorldFlags flags)
{
	Rect			windowBounds;
	GrafPtr			savePort;
	GWorldFlags		result;
	RGBColor		foreColor, backColor;

	GetPort(&savePort);

	GetPortBounds(GetWindowPort(theWindow), &windowBounds);
	SetPortWindowPort(theWindow);
	
	if (pixelDepth == 0) {	/* if we are using the screen depth */
		SetOrigin((short)(-windowBounds.left), (short)(-windowBounds.top));
	}

	GetForeColor(&foreColor);
	GetBackColor(&backColor);
	
	ForeColor(blackColor);					/* so that UpdateGWorld works correctly */
	BackColor(whiteColor);

	result = UpdateGWorld(offscreenPtr, pixelDepth, &windowBounds, gCTab, NULL, flags);
	gCTab = NULL;
	//	ClipRect(&(offscreenPtr->portRect));

	RGBForeColor(&foreColor);
	RGBBackColor(&backColor);
	
	//	result = result & EraseOffscreen(*offscreenPtr);

	SetOrigin(0,0);

	SetPort(savePort);
	
	if (result & gwFlagErr)
		return(FALSE);
	else
		return(TRUE);
}

int		EraseOffscreen(GWorldPtr offscreenPtr)
{
	PortAndDevice	savedPD;
	int				gwErr = 0;
	PixMapHandle	pmH = GetPortPixMap(offscreenPtr);
	GWorldFlags		pixState = GetPixelsState(pmH);
	
	if ((pixState & pixelsLocked) == 0) {
		gwErr = !LockPixels(pmH);
	}
	
	if (!gwErr) {
		Rect	portRect;
		
		GetPortAndDevice(&savedPD);
		SetGWorld(offscreenPtr, NULL);
		GetPortBounds(offscreenPtr, &portRect);

		//	ClipRect(&portRect);
		
		ForeColor(blackColor);
		PaintRect(&portRect);
		
		SetPixelsState(pmH, pixState);
		SetPortAndDevice(&savedPD);
	}
	
	return gwErr;
}

GWorldPtr	CreateOffscreen(Rect Size, short pixelDepth, long options)	
{
	return CreateOffscreenWithCTab(Size, pixelDepth, gCTab, options);
}


GWorldPtr	CreateOffscreenWithCTab(Rect Size, short pixelDepth, CTabHandle cTab, long options)	
{
	QDErr				result;
	GWorldPtr			offscreenPtr;
	unsigned long		flags = 0;
	
	if (options & k_pixel_purgeable) {
		flags |= pixPurge;
	}

	offscreenPtr = NULL;
	result = NewGWorld(&offscreenPtr, pixelDepth, &Size, cTab, NULL, flags);
	
	if (result < 0) {
		offscreenPtr = NULL;		/*(  returns NULL if the routine fails  )*/
	} else {
		PixMapHandle	pmH = GetPortPixMap(offscreenPtr);
		GWorldFlags		pixState = GetPixelsState(pmH);
		OSErr			gwErr		= noErr;
		
		if ((pixState & pixelsLocked) == 0) {
			gwErr = !LockPixels(pmH);
		}

		if (!gwErr) {
			PortAndDevice	savedPD;
			Rect			portRect;

			GetPortAndDevice(&savedPD);
			SetGWorld(offscreenPtr, NULL);
			
			GetPortBounds(offscreenPtr, &portRect);

			#if 0
				ClipRect(&portRect);	/*(* clip to the port of the offscreen *)*/
													/**( because of anticipated SetOrigin call )**/
			#endif
			
			PaintRect(&portRect);
			SetPortAndDevice(&savedPD);

			SetPixelsState(pmH, pixState);
		}
	}
	return(offscreenPtr);			/*(  includes error of offscreenPtr == NULL  )*/
}


void		DisposeOffscreen(GWorldPtr OffscreenPtr)
{
	if (OffscreenPtr != NULL)
		DisposeGWorld(OffscreenPtr);
}


int			CopyFromOffscreen(	WindowPtr theWindow, Rect *srcRect,
								GWorldPtr offscreenPtr, Rect *destRect)
{
	int		result;

	result = FALSE;

	if (offscreenPtr != NULL) {
		PixMapHandle	pmH = GetPortPixMap(offscreenPtr);
		GWorldFlags		pixState	= GetPixelsState(pmH);
		OSErr			gwErr		= noErr;
		
		if ((pixState & pixelsLocked) == 0) {
			gwErr = !LockPixels(pmH);
		}
		
		if (!gwErr) {
			CGrafPtr		savePort;
			GDHandle		saveDevice;
			PenState		thePenState;
			const BitMap	*srcBits, *destBits;
	
			GetGWorld(&savePort, &saveDevice);
			SetPortWindowPort(theWindow);
			GetPenState(&thePenState);
			ForeColor(blackColor);
			
			if (0) {
			//	RGBBackColor(colorizeColor0);
			} else {
				BackColor(whiteColor);
			}
			
			srcBits		= GetPortBitMapForCopyBits(offscreenPtr);
			destBits	= GetPortBitMapForCopyBits(GetWindowPort(theWindow));
			
			CopyBits(srcBits, destBits, 
					 srcRect, destRect, 
					 thePenState.pnMode, NULL);

			BackColor(whiteColor);

			SetGWorld(savePort, saveDevice);

			SetPixelsState(pmH, pixState);
			result = TRUE;
		}
	}

	return(result);
}

int			CopyRgnFromOffscreen(	WindowPtr theWindow, RgnHandle theRgn,
								GWorldPtr offscreenPtr)
{
	int		result;

	result = FALSE;

	if (offscreenPtr != NULL) {
		PixMapHandle	pmH = GetPortPixMap(offscreenPtr);
		GWorldFlags		pixState	= GetPixelsState(pmH);
		OSErr			gwErr		= noErr;
		
		if ((pixState & pixelsLocked) == 0) {
			gwErr = !LockPixels(pmH);
		}
		
		if (!gwErr) {
			CGrafPtr		savePort;
			GDHandle		saveDevice;
			PenState		thePenState;
			Rect			copyRect;
			const BitMap	*srcBits, *destBits;
	
			GetGWorld(&savePort, &saveDevice);
			SetPortWindowPort(theWindow);
			GetPenState(&thePenState);
			ForeColor(blackColor);
			BackColor(whiteColor);

			GetPortBounds(GetWindowPort(theWindow), &copyRect);

			srcBits		= GetPortBitMapForCopyBits(offscreenPtr);
			destBits	= GetPortBitMapForCopyBits(GetWindowPort(theWindow));
			
			CopyBits(srcBits, destBits, 
					 &copyRect, &copyRect, thePenState.pnMode, theRgn);

			SetGWorld(savePort, saveDevice);

			SetPixelsState(pmH, pixState);
			result = TRUE;
		}
	}
	
	return(result);
}

int			CopyOffscreenToOffscreen(	GWorldPtr srcOffscreen, Rect *srcRect,
									GWorldPtr destOffscreen, Rect *destRect)
{
	int		result;

	result = FALSE;

	if ((srcOffscreen != NULL) && (destOffscreen != NULL))	{
		PixMapHandle	srcPmH = GetPortPixMap(srcOffscreen), 
						dstPmH = GetPortPixMap(destOffscreen);
		GWorldFlags		srcPixState	= GetPixelsState(srcPmH);
		GWorldFlags		dstPixState	= GetPixelsState(dstPmH);
		OSErr			gwErr		= noErr;
		
		if (!gwErr && (srcPixState & pixelsLocked) == 0) {
			gwErr = !LockPixels(srcPmH);
		}
		
		if (!gwErr && (dstPixState & pixelsLocked) == 0) {
			gwErr = !LockPixels(dstPmH);
		}
		
		if (!gwErr) {
			CGrafPtr		savePort;
			GDHandle		saveDevice;
			PenState		thePenState;
			const BitMap	*srcBits, *destBits;
			
			GetGWorld(&savePort, &saveDevice);
			GetPenState(&thePenState);					/* so the user can set it */
			SetGWorld(destOffscreen, NULL);
				
			ForeColor(blackColor);
			BackColor(whiteColor);

			srcBits		= GetPortBitMapForCopyBits(srcOffscreen);
			destBits	= GetPortBitMapForCopyBits(destOffscreen);

			CopyBits(
				srcBits, destBits,
				srcRect, destRect, 
				thePenState.pnMode, NULL);

			SetGWorld(savePort, saveDevice);

			result = TRUE;
		}

		SetPixelsState(srcPmH, srcPixState);
		SetPixelsState(dstPmH, dstPixState);
	}

	return result;
}
/* ______________________________________________________________________________ */


void	GetPortAndDevice(PortAndDevice *savedPortAndDevice)
{
	GetGWorld(&(savedPortAndDevice->port), &(savedPortAndDevice->device));
}


void	SetPortAndDevice(PortAndDevice *savedPortAndDevice)
{
	SetGWorld(savedPortAndDevice->port, savedPortAndDevice->device);
}

/* ______________________________________________________________________________ */

Boolean		DrawOffscreen(
	GWorldPtr		theOffscreen, 
	PortAndDevice	*savePD, 
	GWorldFlags		*pixState)
{
	GetPortAndDevice(savePD);
	return(UseOffscreen(theOffscreen, pixState));
}

void		StopDrawingOffscreen(
	GWorldPtr		theOffscreen, 
	PortAndDevice	*savePD, 
	GWorldFlags		*pixState)
{
	StopUsingOffscreen(theOffscreen, pixState);
	SetPortAndDevice(savePD);
}

int			UseOffscreen(GWorldPtr offscreenPtr, GWorldFlags *pixState)
{
	int		result;

	result = FALSE;

	if (offscreenPtr != NULL) {
		PixMapHandle	pmH			= GetPortPixMap(offscreenPtr);
		OSErr			gwErr		= noErr;

		*pixState	= GetPixelsState(pmH);

		if ((*pixState & pixelsLocked) == 0) {
			gwErr = !LockPixels(pmH);
		}
		
		if (!gwErr) {
			SetGWorld(offscreenPtr, NULL);
			result = TRUE;
		}
	}

	return(result);
}

void	StopUsingOffscreen(GWorldPtr offscreenPtr, GWorldFlags *pixState)
{
	PixMapHandle	pmH = GetPortPixMap(offscreenPtr);

	SetPixelsState(pmH, *pixState);
}
/*
int		CheckOffscreenPixMap(GWorldPtr OffscreenPtr)
{
	int		result;

	result = FALSE;

	if (OffscreenPtr != NULL)
		if (LockPixels(OffscreenPtr->portPixMap))	{
			UnlockPixels(OffscreenPtr->portPixMap);
			result = TRUE;
		}

	return(result);
}
*/


#if 0

RgnHandle				CalcMaskRgn(GWorldPtr originGWorld, RGBColor *seedColor)
{
	Rect			pixRect = originGWorld->portRect;
	BitMap			myBitsyBitmap;
	RgnHandle		maskRgn, diffRgn;
	PortAndDevice	savePD;
	RGBColor		saveColor;
	
	GetPortAndDevice(&savePD);
	UseOffscreen(originGWorld);
	
	maskRgn = NewRgn();
	
	if (LockPixels(originGWorld->portPixMap))
	{
		short	rowBytes	= ((pixRect.right - pixRect.left + 15) / 16) * 2;
		long	size		= rowBytes * (long)(pixRect.bottom - pixRect.top);
		RgnHandle	additionalRgn;

		myBitsyBitmap.rowBytes	= rowBytes;
		myBitsyBitmap.baseAddr	= TrackNewPtr("calc mask rgn", size);
		myBitsyBitmap.bounds	= pixRect;
		memset(myBitsyBitmap.baseAddr, 0, size);

		SeedCFill(	(BitMap *)(*originGWorld->portPixMap),
					&myBitsyBitmap,
					&pixRect, &pixRect, pixRect.left,		pixRect.top,		NULL, 0);
		BitMapToRegion(maskRgn, &myBitsyBitmap);

			RgnHandle	additionalRgn;

			SeedCFill(	(BitMap *)(*originGWorld->portPixMap),
						&myBitsyBitmap,
						&pixRect, &pixRect, pixRect.left,		pixRect.bottom-1,	NULL, 0);
		}

		BitMapToRegion(maskRgn, &myBitsyBitmap);
		UnlockPixels(originGWorld->portPixMap);
		DisposPtr(myBitsyBitmap.baseAddr);
	}
	diffRgn = NewRgn();
	RectRgn(diffRgn, &pixRect);
	XorRgn(maskRgn, diffRgn, maskRgn);
	DisposeRgn(diffRgn);

	StopUsingOffscreen(originGWorld);
	SetPortAndDevice(&savePD);

	return(maskRgn);
}
#endif

#if 0

RgnHandle				CalcMaskRgn(GWorldPtr originGWorld, RGBColor *seedColor)
{
	Rect			pixRect = originGWorld->portRect;
	Rect			pixMapRect = (**(originGWorld->portPixMap)).bounds;
	BitMap			myBitsyBitmap;
	RgnHandle		maskRgn;
	PortAndDevice	savedPD;
	
	GetPortAndDevice(&savedPD);
	
	maskRgn = NewRgn();
	
	if (UseOffscreen(originGWorld)) {
		short	rowBytes	= ((pixMapRect.right - pixMapRect.left + 15) / 16) * 2;
		long	size		= rowBytes * (long)(pixMapRect.bottom - pixMapRect.top);
		
		myBitsyBitmap.rowBytes	= rowBytes;
		myBitsyBitmap.baseAddr	= TrackNewPtr("calc mask rgn", size);
		myBitsyBitmap.bounds	= pixMapRect;
		memset(myBitsyBitmap.baseAddr, 0, size);

		CalcCMask(	(BitMap *)(*originGWorld->portPixMap),
					&myBitsyBitmap,
					&pixMapRect, &pixMapRect, seedColor, (ProcPtr)davesProc, 0);

		BitMapToRegion(maskRgn, &myBitsyBitmap);
		DisposPtr(myBitsyBitmap.baseAddr);

		StopUsingOffscreen(originGWorld);
	}
	{
		RgnHandle	boundRgn;
		boundRgn = NewRgn();
		RectRgn(boundRgn, &pixRect);
		SectRgn(boundRgn, maskRgn, maskRgn);
		DisposeRgn(boundRgn);
	}
#if 0
	ForeColor(whiteColor);
	PaintRgn(maskRgn);
	ForeColor(blackColor);
#endif
	SetPortAndDevice(&savedPD);

	return(maskRgn);
}

pascal	Boolean		davesProc(RGBColor *paramColor, long* paramLong)
{
	register	MatchRec	*theRec;	
	
	theRec = (MatchRec *)((**(*(GDHandle *)TheGDevice)).gdRefCon);

	*paramLong = (	*(char *)&(paramColor->red)		!= *(char *)&(theRec->red)
				||	*(char *)&(paramColor->green)	!= *(char *)&(theRec->green)
				||	*(char *)&(paramColor->blue)	!= *(char *)&(theRec->blue)	);

	return(TRUE);
}

#endif
