{------------------------------------------------------------------------------##	Apple Macintosh Developer Technical Support##	"Skippy White's Famous High Level Off-Screen Map RoutinesÓ##	Offscreen.inc1.p	-	Pascal Source##	Copyright © 1989 Apple Computer, Inc.#	All rights reserved.#	The characters depicted herein (except Skippy White) are fictitious.##	Versions:	#				1.00				04/89#				1.01				06/92##	Components:	#				Offscreen.p			April 1, 1988#				Offscreen.inc1.p	April 1, 1988##	These routines provide a high-level interface to the QuickDraw & Color#	Manager routines which allow the creation and manipulation of off-screen#	bitmaps and pixmaps. They are designed to run on any machine with 128K or#	later ROMs (sorry 64K ROM fans).#	Note that the design incorporates the idea that you can go along pretending#	there is an offscreen buffer even when one couldnÕt be allocated, and the#	calls will do nothing.#------------------------------------------------------------------------------}CONST	noDepth = -2;	chunkyPix = 0;TYPE	PrivateHandle	= ^PrivatePtr;	PrivatePtr		= ^PrivateRecord;	PrivateRecord	= RECORD		requestedBounds	: Rect; {boundary rectangle for the pixmap}		{used to determine the depth, size, and dimensions of the pixmap}		requestedDepth	: INTEGER; {the requested depth of this map}		{if > 0, a private device will be created for the map}		requestedColors	: CTabHandle; {color table}		{our own copy of the color table that was passed in}		requestedPolite	: BOOLEAN; {whether he wanted it polite}		bits			: Handle; {handle to the off-screen bits}		{if NIL, there is no port or graphics device either}		notOurs			: BOOLEAN; {TRUE means that we didnÕt create the bits}		needs32Bits		: BOOLEAN; {you must be in 32 bit memory mode to get to bits}		bitsPort		: CGrafPtr; {GrafPort used for the off-screen map.}		bitsDevice		: GDHandle; {GDevice associated with the map}		{private device (if requestedDepth > 0)}		{(from the device list for requestedDepth = kMaxDepth)}		oldSeed			: LONGINT; {old CTSeed for the pixmap's color table}		{used to detect color updates}				invalRegion		: RgnHandle; {part of offscreen buffer that is invalid}		drawingPort		: GrafPtr; {supplied to BeginOffscreenDahling}		savedPort		: GrafPtr; {saves old GrafPort while drawing off screen}		savedMap		: BitMap; {used to save map when we donÕt use our port}		savedVisRgn		: RgnHandle; {saves the visRgn from a user-supplied port}		savedDevice		: GDHandle; {saves old GDevice while drawing off screen}	END; {PrivateRecord}VAR	theMac	: SysEnvRec;{---------- low-level service routines used by the rest of Offscreen ----------}{Fill out the pixmap according to the bounds, pixelType, pixelSize, cmpCount, cmpSize and pmTable parameters. All fields will be modified. The initial pixmap will be copied from the current GDeviceÕs pixmap, so you must modify the pmap^^.baseAddr before using it. dataSize will return with the number of bytes required for the pixel data. Note: it is the responsibility of the caller to fill in the baseAddr field of the pixmap. You may want to call NewImprovedGBuffer to see if the system will allocate the data space and then set baseAddr to point to it. This call cannot fail.}PROCEDURE InitGBufferPixmap(pmap: PixMapHandle; aBounds: Rect; aPixelType, aPixelSize, 	aCmpCount, aCmpSize: INTEGER; aPMTable: CTabHandle; VAR dataSize: LONGINT);	VAR	theGDevice:	GDHandle;BEGIN	theGDevice := GetGDevice;				{get the current device}	pmap^^:=theGDevice^^.gdPMap^^;			{start out with deviceÕs pixmap}	WITH pmap^^ DO BEGIN		bounds := aBounds;		pixelType := aPixelType;		pixelSize := aPixelSize; 		cmpCount := aCmpCount;		cmpSize := aCmpSize;		pmTable := aPMTable;		WITH bounds DO BEGIN			rowBytes := ((pixelSize * right + 15) DIV 16) * 2;			{calculate an even # of words}			dataSize := bottom * LONGINT(rowBytes); {calculate the size}			rowBytes := rowBytes + $8000;	{flag that itÕs a pixmap}		END; {WITH}	END; {WITH}END; {InitGBufferPixmap}	{Fill out a pixmap for use with an onscreen grafPort. It differs from InitGBufferPixmap in that it associates the buffer with an onscreen device. This will be the maximim- depth device which intersects the globalBounds rectangle. In the most common case you will pass in the portRect of a window after converting it to global coordinates. If no screen device intersects globalBounds, the function will return FALSE and device will return with NIL. If it returns TRUE then device will be a handle to the proper GDevice (see StartLeech, below). For the other information, see the comments at InitGBufferPixmap.}FUNCTION InitGBufferScreen(pmap: PixMapHandle; globalBounds: Rect; VAR device:GDHandle; 	 VAR dataSize: LONGINT): BOOLEAN;VAR	savedDevice:	GDHandle;BEGIN	InitGBufferScreen := FALSE;		device := GetMaxDevice(globalBounds);		{maximum-depth device for globalBounds}	IF device <> NIL THEN BEGIN		savedDevice := GetGDevice;				WITH globalBounds DO			SetRect(globalBounds, 0, 0, right - left, bottom - top);		{make globalBounds into a window-ish (zero-based) rectangle}					SetGDevice(device);						{set to device for InitGBufferPixmap}		WITH device^^.gdPMap^^ DO			InitGBufferPixmap(pmap, globalBounds, pixelType, pixelSize, 				cmpCount, cmpSize, pmTable, dataSize);			{set up the pixmap}					SetGDevice(savedDevice);		InitGBufferScreen := TRUE;	END; {IF device <> NIL}END; {InitGBufferScreen}{Allocate the data space for the bits of an offscreen buffer. Only do so if it can be allocated in an optimum place for graphics use. If the system doesnÕt have a superior place to allocate the bits, NIL is returned. needs32Bits will return TRUE if the bit space requires 32 bit addressing in order to access it. buffNotNeeded will return TRUE if performance will be improved by NOT buffering (ÒperformanceÓ means avoiding flicker, etc.)}FUNCTION NewImprovedGBuffer(dataSize: LONGINT; VAR needs32Bits: BOOLEAN;			VAR buffNotNeeded: BOOLEAN): Ptr;BEGIN	NewImprovedGBuffer := NIL;	needs32Bits := FALSE;	buffNotNeeded := FALSE;END; {NewImprovedGBuffer}{Free a buffer allocated via NewImprovedGBuffer.}PROCEDURE FreeEnhancedGBuffer(bits: Ptr);BEGINEND; {FreeEnhancedGBuffer}{Fill out the GDevice record and set device^^.gdPMap = pixmap. Errors can occur from not being able to build the ITable, etc.}FUNCTION InitGBufferDevice(device: GDHandle; pmap: PixMapHandle): OSErr;VAR	preferredResolution:	INTEGER;	inverseTable: 			ITabHandle;	PROCEDURE Out(error: OSErr);	BEGIN {Out}		IF error <> noErr THEN BEGIN			DisposHandle(Handle(device^^.gdITable));	{get rid of the inverse table}			InitGBufferDevice := error;			EXIT(InitGBufferDevice);		END; {IF error <> noErr}	END; {Out}BEGIN	device^^.gdITable := NIL;	inverseTable := ITabHandle(NewHandle(0));	{create the inverse table stub}	Out(MemError);								{bail if couldnÕt make the handle}	MakeITable(pmap^^.pmTable, inverseTable, preferredResolution);	Out(QDError);								{bail if MakeITable failed}		preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}		WITH device^^ DO BEGIN		gdType := clutType;						{no private devices for direct ones, right?}			gdITable := inverseTable;		gdResPref := preferredResolution;		gdFlags := 2 ** noDriver;		gdPMap := pmap;		gdRect := pmap^^.bounds;	END; {WITH}		InitGBufferDevice := noErr;END; {InitGBufferDevice}{---------- end of low-level service routines ----------}PROCEDURE InitOffscreen;{128K ROMs and new enough Palette Manager}VAR	error	: OSErr;BEGIN {InitOffscreen}	error := SysEnvirons(1, theMac);END; {InitOffscreen}(* ??? commented out since we no longer use it (but someone may want it, so should we provide it as a utility?)FUNCTION GetMaxAreaDevice(globalRect: Rect): GDHandle;{Find the greatest overlap device for the given global rectangle.}VAR	area			: LONGINT;	maxArea			: LONGINT;	device			: GDHandle;	intersection	: Rect;BEGIN {GetMaxAreaDevice}	GetMaxAreaDevice := NIL;	maxArea := 0;	device := GetDeviceList;	WHILE device <> NIL DO BEGIN		IF TestDeviceAttribute(device, screenDevice) THEN			IF TestDeviceAttribute(device, screenActive) THEN				IF SectRect(globalRect, device^^.gdRect, intersection) THEN BEGIN					WITH intersection DO						area := LONGINT(right - left) * LONGINT(bottom - top);					IF area > maxArea THEN BEGIN						GetMaxAreaDevice := device;						maxArea := area;					END; {IF area > maxArea}				END; {IF SectRect...}		device := GetNextDevice(device);	END; {WHILE device <> NIL}END; {GetMaxAreaDevice}*)FUNCTION MakeCleanColorTable(colors: CTabHandle; depth: INTEGER;							 VAR table: CTabHandle): OSErr;{This call takes a handle to a list of colors to be included in a color table. It returns a table suitable for use for a PixMap, with black and white at the correct offsets. If there is no default color table for the depth in question, it returns NIL for the table.}	FUNCTION IsWhite(rgb: RGBColor): BOOLEAN;	BEGIN {IsWhite}		IsWhite := (rgb.red = $FFFF) & (rgb.green = $FFFF) & (rgb.blue = $FFFF);	END; {IsWhite}	FUNCTION IsBlack(rgb: RGBColor): BOOLEAN;	BEGIN {IsBlack}		IsBlack := (rgb.red = 0) & (rgb.green = 0) & (rgb.blue = 0);	END; {IsBlack}VAR	tableIndex	: INTEGER;	colorIndex	: INTEGER;	aColor		: RGBColor;BEGIN {MakeCleanColorTable}	table := GetCTable(depth);	IF table = NIL THEN		MakeCleanColorTable := memFullErr	ELSE		WITH table^^ DO BEGIN			ctSeed := GetCTSeed; {get a fresh seed; we'll change the color table}			tableIndex := 1; {skip white in table we are making}			colorIndex := 0; {start with first color}			REPEAT				IF colorIndex > colors^^.ctSize THEN					Leave; {done with all the colors}				aColor := colors^^.ctTable[colorIndex].rgb;				colorIndex := colorIndex + 1; {advance to next color}				IF IsWhite(aColor) THEN					Cycle; {this one is white, try another}				IF IsBlack(aColor) THEN					Cycle; {this one is black, try another}		{$PUSH} {$R-}				ctTable[tableIndex].rgb := aColor; {use this color}		{$POP}				tableIndex := tableIndex + 1;			UNTIL tableIndex > ctSize - 1; {until the table is full}			MakeCleanColorTable := noErr;		END; {WITH}END; {MakeCleanColorTable}PROCEDURE DeallocateBits(offscreenHandle: Handle);{Get rid of the bits from an off-screen map.}BEGIN {DeallocateBits}	IF offscreenHandle <> NIL THEN		WITH PrivateHandle(offscreenHandle)^^ DO BEGIN			IF NOT notOurs THEN				DisposHandle(bits) {get rid of the bits}			ELSE				FreeEnhancedGBuffer(Ptr(bits));			bits := NIL;			IF bitsPort <> NIL THEN BEGIN				IF bitsPort^.visRgn <> NIL THEN BEGIN {visRgn of NIL means port is not open yet}					IF requestedDepth > 0 THEN {if we created a private device and color table}						DisposHandle(Handle(bitsPort^.portPixMap^^.pmTable)); {get rid of the colors}					ClosePort(GrafPtr(bitsPort));							DisposeRgn(invalRegion); {get rid of the invalidation region}				END; {IF ...bitsPort^.visRgn <> NIL}				DisposPtr(Ptr(bitsPort));			END; {IF bitsPort <> NIL}			bitsPort := NIL;			IF bitsDevice <> NIL THEN				IF requestedDepth > 0 THEN BEGIN {if we created a private device}					bitsDevice^^.gdPMap := NIL; {the pixmap was owned by the port}					DisposGDevice(bitsDevice);					bitsDevice := NIL;				END; {IF requestedDepth > 0}		END; {WITH}END; {DeallocateBits}FUNCTION GetMap(offscreenHandle: Handle): BitMapPtr;{Get a BitMapPtr for the bits in the offscreen handle. This is also the best way to see if the bits are allocated. Note that this has a side-effect of setting up the baseAddr properly.}VAR	map	: BitMapPtr;BEGIN {GetMap}	map := NIL;	IF offscreenHandle <> NIL THEN {if we have bits}		WITH PrivateHandle(offscreenHandle)^^ DO BEGIN			IF bits <> NIL THEN {if we seem to have bits, make sure that we really do}				IF StripAddress(bits^) = NIL THEN					DeallocateBits(offscreenHandle)				ELSE BEGIN					{find the map}					WITH bitsPort^ DO						IF portVersion > 0 THEN {old-style port?}							map := @portPixMap {this is really portBits!}						ELSE							map := BitMapPtr(portPixMap^);					IF notOurs THEN						map^.baseAddr := Ptr(bits)					ELSE						map^.baseAddr := bits^;				END; {ELSE}		END; {WITH}	GetMap := map;END; {GetMap}FUNCTION GetBitsHandle(offscreenHandle: Handle): Handle;BEGIN	GetBitsHandle := NIL;	IF offscreenHandle <> NIL THEN		WITH PrivateHandle(offscreenHandle)^^ DO			IF NOT notOurs THEN				GetBitsHandle := bits;END; {GetBitsHandle}FUNCTION AllocateBits(offscreenHandle: Handle; VAR buffNotNeeded: BOOLEAN): OSErr;{Allocate the bits, port, device, etc. for the off-screen map. If an error code is returned, the handle will be in the "dormant" stage. ??? define If the bits are already allocated, then nothing will happen.}VAR	oldPort				: GrafPtr;	bounder				: Rect;	map					: BitMapPtr;	bitsSize			: LONGINT;	colors				: CTabHandle;	preferredResolution	: INTEGER;	inverseTable		: ITabHandle;	totalSpace			: LONGINT;	contiguousSpace		: LONGINT;	use32Bits			: BOOLEAN;	PROCEDURE Out(error: OSErr);	BEGIN {Out}		IF error <> noErr THEN BEGIN			HUnlock(offscreenHandle);			DeallocateBits(offscreenHandle);			SetPort(oldPort);			AllocateBits := error;			EXIT(AllocateBits);		END; {IF error <> noErr}	END; {Out}BEGIN {AllocateBits}	IF offscreenHandle <> NIL THEN		IF GetMap(offscreenHandle) = NIL THEN BEGIN {if the bits are not already allocated}			GetPort(oldPort);			MoveHHi(offscreenHandle); {fly the handle high in the heap since we lock it down}			HLock(offscreenHandle);			WITH PrivateHandle(offscreenHandle)^^ DO BEGIN				bitsPort := CGrafPtr(NewPtrClear(SIZEOF(GrafPort))); {create a new port}				{NewPtrClear means that bits := NIL, invalRegion := NIL, notOurs := FALSE, etc.}				Out(MemError);				WITH requestedBounds DO					SetRect(bounder, 0, 0, right - left, bottom - top);				IF theMac.hasColorQD THEN					WITH bitsPort^ DO BEGIN						OpenCPort(bitsPort); {always use a color port}						portPixMap^^.pmTable := NIL; {in case we fail before we make a color table}												IF requestedDepth > 0 THEN BEGIN {if there is a private device}							{make a color table from the requested colors or a resource file or ROM}							Out(MakeCleanColorTable(requestedColors, requestedDepth, colors));														{now set up the pixmap of the port}							InitGBufferPixmap(portPixMap, bounder, chunkyPix, requestedDepth, 1, requestedDepth,								colors, {returns} bitsSize);									{make a private device}							bitsDevice := GDHandle(NewHandleClear(SIZEOF(GDevice)));							Out(MemError); {must have failed because of heap paucity}									inverseTable := ITabHandle(NewHandle(0)); {create the inverse table}							Out(MemError);							preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}									WITH bitsDevice^^ DO BEGIN {set up the other fields of the device}								gdType := clutType;								gdITable := inverseTable;								gdResPref := preferredResolution;								gdFlags := 2 ** noDriver;								gdPMap := portPixMap;							END; {WITH}							InitGDevice(0, -1, bitsDevice); {sets gdRect from pixmap now; in future may do more}							MakeITable(colors, inverseTable, preferredResolution);							Out(QDError); {MakeITable failed}						END ELSE BEGIN {when there is no private device}								{get the max. area device and set up the portÕs pixmap accordingly}								IF NOT InitGBufferScreen(portPixMap, requestedBounds, bitsDevice, {returns} bitsSize) THEN									Out(noDeviceIntersectErr); {no devices intersect bounds}																	oldSeed := portPixMap^^.pmTable^^.ctSeed; {used to detect color updates}						END;											map := BitMapPtr(portPixMap^); {point to the map so we can set the portRect & visRgn}{!!! ??? Do we need to call ForeColor and BackColor on the CGrafPort to set it up right?}					END {WITH}				ELSE BEGIN {Classic QuickDraw}					OpenPort(GrafPtr(bitsPort));					WITH GrafPtr(bitsPort)^, portBits DO BEGIN						bounds := bounder;						{figure out how much space we need for our bits}						WITH bounds DO BEGIN							rowBytes := ((right + 15) DIV 16) * 2;							{calculate an even # of words}							bitsSize := bottom * LONGINT(rowBytes); {calculate the size}						END; {WITH}						map := @portBits; {point to the map so we can set it up}					END; {WITH}				END;									WITH bitsPort^ DO BEGIN					portRect := map^.bounds; {port's portRect is same as bounds}					RectRgn(visRgn, portRect); {the visRgn must be changed}					invalRegion := NewRgn;					Out(MemError);					CopyRgn(visRgn, invalRegion);				END; {WITH}								bits := Handle(NewImprovedGBuffer(bitsSize, needs32Bits, buffNotNeeded));				notOurs := bits <> NIL;				IF NOT notOurs THEN BEGIN					IF requestedPolite THEN						contiguousSpace := MaxBlock {make sure we have room without purging}					ELSE						PurgeSpace(totalSpace, contiguousSpace); {make sure we have room with purging}					IF bitsSize + kOffscreenReserve > contiguousSpace THEN						{if there is not enough room for the bits & reserve block, error out}						Out(memFullErr);					bits := NewHandleClear(bitsSize); {get space for the bits}					Out(MemError);					IF requestedPolite THEN						HPurge(bits); {keep bits purgeable if we are polite}				END;							END; {WITH}			HUnlock(offscreenHandle);			SetPort(oldPort);		END; {IF GetMap(offscreenHandle) = NIL}	AllocateBits := noErr;END; {AllocateBits}PROCEDURE DisposeOffscreen(offscreenHandle: Handle);{Get rid of everything, including offscreenHandle itself.}BEGIN {DisposeOffscreen}	IF offscreenHandle <> NIL THEN BEGIN		DeallocateBits(offscreenHandle);		DisposHandle(Handle(PrivateHandle(offscreenHandle)^^.requestedColors));		DisposHandle(offscreenHandle);	END; {IF offscreenHandle <> NIL}END; {DisposeOffscreen}FUNCTION NewOffscreen(bounds: Rect; depth: INTEGER; colors: CTabHandle;					  memoryPolite: BOOLEAN; VAR buffNotNeeded: BOOLEAN; VAR offscreenHandle: Handle): OSErr;{Create an offscreenHandle. Return NIL if there is an error.}VAR	aHandle	: Handle;	PROCEDURE Out(error: OSErr);	BEGIN {Out}		IF error <> noErr THEN BEGIN			DisposHandle(aHandle); {dispose handle if we made it}			NewOffscreen := error;			offscreenHandle := NIL;			EXIT(NewOffscreen);		END; {IF error <> noErr}	END; {Out}BEGIN {NewOffscreen}	aHandle := NewHandleClear(SizeOf(PrivateRecord)); {make the private block}	Out(MemError);	MoveHHi(aHandle); {fly this high so it doesn't fragment the heap}	HLock(aHandle);	WITH PrivateHandle(aHandle)^^ DO BEGIN		requestedBounds := bounds;		IF theMac.hasColorQD THEN			requestedDepth := depth		ELSE			requestedDepth := noDepth;		IF requestedDepth > 0 THEN BEGIN {if we need a private device}			Out(HandToHand(Handle(colors)));			requestedColors := colors;		END;		requestedPolite := memoryPolite;	END; {WITH}	HUnlock(aHandle);	{now, we have filled in all of the fields of offscreenHandle}	NewOffscreen := AllocateBits(aHandle, buffNotNeeded);	offscreenHandle := aHandle;END; {NewOffscreen}FUNCTION CheckOffscreen(offscreenHandle: Handle; VAR drawNeeded: BOOLEAN): OSErr;VAR	error			: OSErr;	newSeed			: LONGINT;	buffNotNeeded	: BOOLEAN;BEGIN {CheckOffscreen}	drawNeeded := TRUE; {default we must draw if offscreen invalid}	error := noErr; {no error either}	IF offscreenHandle <> NIL THEN		WITH PrivateHandle(offscreenHandle)^^ DO BEGIN			IF requestedDepth = kMaxDepth THEN {if it's not a private device}				IF oldSeed <> bitsDevice^^.gdPMap^^.pmTable^^.ctSeed THEN {we have a color update, Houston}					DeallocateBits(offscreenHandle);			drawNeeded := GetMap(offscreenHandle) = NIL;			error := AllocateBits(offscreenHandle, buffNotNeeded); {allocate if deallocated}		END; {WITH}				IF GetMap(offscreenHandle) <> NIL THEN		drawNeeded := drawNeeded | (NOT EmptyRgn(PrivateHandle(offscreenHandle)^^.invalRegion));		{canÕt use the width for invalRegion since we may have done an AllocateBits}			CheckOffscreen := error;END; {CheckOffscreen}PROCEDURE ValidRectOffscreen(offscreenHandle: Handle; window: WindowPtr; validRect: Rect);VAR	validRgn	: RgnHandle;BEGIN	validRgn := NewRgn;	RectRgn(validRgn, validRect);	ValidRgnOffscreen(offscreenHandle, window, validRgn);	DisposeRgn(validRgn);END; {ValidRectOffscreen}PROCEDURE ValidRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; validRegion: RgnHandle);VAR	savedPort	: GrafPtr;	BEGIN	IF window <> NIL THEN BEGIN		GetPort(savedPort);		SetPort(window);		ValidRgn(validRegion);		SetPort(savedPort);	END;	IF GetMap(offscreenHandle) <> NIL THEN		WITH PrivateHandle(offscreenHandle)^^ DO			DiffRgn(invalRegion, validRegion, invalRegion);END; {ValidRgnOffscreen}PROCEDURE InvalRectOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRect: Rect);VAR	invalidRgn	: RgnHandle;BEGIN	invalidRgn := NewRgn;	RectRgn(invalidRgn, invalidRect);	InvalRgnOffscreen(offscreenHandle, window, invalidRgn);	DisposeRgn(invalidRgn);END; {InvalRectOffscreen}PROCEDURE InvalRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRgn: RgnHandle);VAR	savedPort	: GrafPtr;	BEGIN	IF window <> NIL THEN BEGIN		GetPort(savedPort);		SetPort(window);		InvalRgn(invalidRgn);		SetPort(savedPort);	END;	IF GetMap(offscreenHandle) <> NIL THEN		WITH PrivateHandle(offscreenHandle)^^ DO			UnionRgn(invalRegion, invalidRgn, invalRegion);END; {ValidRgnOffscreen}FUNCTION CheckBoundsOffscreen(offscreenHandle: Handle; newBounds: Rect;					   VAR drawNeeded: BOOLEAN): OSErr;VAR	somethingChanged	: BOOLEAN;	rightDevice			: GDHandle;BEGIN {CheckBoundsOffscreen}	IF offscreenHandle <> NIL THEN		WITH PrivateHandle(offscreenHandle)^^ DO BEGIN			{check to see if the size of the port changed}			WITH newBounds, bitsPort^.portRect.botRight DO				somethingChanged := (right - left <> h) | (bottom - top <> v);			IF NOT somethingChanged THEN BEGIN				{check to see if the depth of the screen changed}				rightDevice := NIL;				IF requestedDepth = kMaxDepth THEN					rightDevice := GetMaxDevice(newBounds);				somethingChanged := (rightDevice <> NIL) & (rightDevice <> bitsDevice);			END;			IF somethingChanged THEN BEGIN {we have a color update, Houston}				DeallocateBits(offscreenHandle);				requestedBounds := newBounds;			END;		END; {WITH}	CheckBoundsOffscreen := CheckOffscreen(offscreenHandle, drawNeeded);END; {CheckBoundsOffscreen}PROCEDURE BeginOffscreenDrawing(offscreenHandle: Handle; port: GrafPtr);BEGIN {BeginOffscreenDrawing}	IF GetMap(offscreenHandle) <> NIL THEN	{note side effect here of setting up map for drawing (stuffing baseAddr)}		WITH PrivateHandle(offscreenHandle)^^ DO BEGIN			IF NOT notOurs THEN {if we, not the system, allocated the bits}				{lock the bits down}				HLock(bits);			{we need the pixmap locked while we are using it}			IF theMac.hasColorQD THEN				HLock(Handle(bitsPort^.portPixMap));			drawingPort := port;			GetPort(savedPort);						{set up the port and the device}						IF drawingPort = NIL THEN				SetPort(GrafPtr(bitsPort))			ELSE BEGIN				SetPort(drawingPort);				{save the visRgn and replace it with our private one}				savedVisRgn := drawingPort^.visRgn;				drawingPort^.visRgn := bitsPort^.visRgn;								savedMap := drawingPort^.portBits;								IF theMac.hasColorQD THEN					SetPortPix(bitsPort^.portPixMap)				ELSE					SetPortBits(GrafPtr(bitsPort)^.portBits);			END; {for caller-supplied port case}						IF theMac.hasColorQD THEN BEGIN				savedDevice := GetGDevice;				SetGDevice(bitsDevice);			END; {IF theMac.hasColorQD}						WITH thePort^ DO				{intersect the invalidated offscreen region with private visRgn}				SectRgn(invalRegion, visRgn, visRgn);		END; {WITH}END; {BeginOffscreenDrawing}PROCEDURE EndOffscreenDrawing(offscreenHandle: Handle);TYPE	PMapHandlePtr	= ^PixMapHandle;	BEGIN {EndOffscreenDrawing}	IF GetMap(offscreenHandle) <> NIL THEN		WITH PrivateHandle(offscreenHandle)^^ DO BEGIN			{restore the map, device, port}						IF drawingPort <> NIL THEN BEGIN				SetPort(drawingPort);				drawingPort^.visRgn := savedVisRgn; {restore original visRgn}								IF theMac.hasColorQD THEN					SetPortPix(PMapHandlePtr(@savedMap)^)				ELSE					SetPortBits(savedMap);			END; {for caller-supplied port case}											IF theMac.hasColorQD THEN				SetGDevice(savedDevice);							SetPort(savedPort);			{we only need the pixmap locked while we are using it}			IF theMac.hasColorQD THEN				HUnlock(Handle(bitsPort^.portPixMap));			IF NOT notOurs THEN				{unlock the bits so they can float}				HUnlock(bits);						WITH bitsPort^ DO				RectRgn(visRgn, portRect); {set private visRgn back to full size}		END; {WITH}END; {EndOffscreenDrawing}PROCEDURE BeginUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);BEGIN	BeginUpdate(window);	IF GetMap(offscreenHandle) <> NIL THEN BEGIN		BeginOffscreenDrawing(offscreenHandle, window); {Õsects invalRegion with visRgn}		{validate the entire contents of the offscreen buffer}		ValidRectOffscreen(offscreenHandle, NIL, PrivateHandle(offscreenHandle)^^.bitsPort^.portRect);	END;END; {BeginUpdateOffscreen}PROCEDURE EndUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);BEGIN	IF GetMap(offscreenHandle) <> NIL THEN BEGIN		EndOffscreenDrawing(offscreenHandle);				SetPort(window);		ForeColor(blackColor);		BackColor(whiteColor);		{now copy from the offscreen buffer to the window (clipped to windowÕs visRgn)}		WITH PrivateHandle(offscreenHandle)^^ DO			CopyBits(GrafPtr(bitsPort)^.portBits, window^.portBits, bitsPort^.portRect,				window^.portRect, srcCopy, NIL);	END;	EndUpdate(window);END; {EndUpdateOffscreen}{Create an off-screen pixel map for a port. It will always use kMaxDepth and always be memoryPolite.} FUNCTION NewOffscreenForWindow(window: WindowPtr; VAR buffNotNeeded: BOOLEAN;							   VAR offscreenHandle: Handle): OSErr;VAR	savedPort	: GrafPtr;	globalRect	: Rect;BEGIN {NewOffscreenForWindow}	SetPort(window);	globalRect := window^.portRect; {calculate a global rect for this window}	WITH globalRect DO BEGIN		LocalToGlobal(topLeft);		LocalToGlobal(botRight);	END;	NewOffscreenForWindow := NewOffscreen(globalRect, kMaxDepth, NIL, TRUE,		buffNotNeeded, offscreenHandle);END; {NewOffscreenForWindow}(* ??? looks like these arenÕt neededÉ{Begin an update for the window specified, using the specified off-screen handle for bits. This sets the port to the window! Éand temporarily nukes the visRgn! ??? @@@ !!!}PROCEDURE BeginOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);BEGIN {BeginOffscreenUpdate}	BeginOffscreenDrawing(offscreenHandle); {???!!! 	SetPort(window); {override the offscreen port which was set by BOU}	BeginUpdate(window);	END; {BeginOffscreenUpdate}{Finish drawing to an off-screen port. Also copy the bits from the port to the screen.}PROCEDURE EndOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);{Call this when the rectangle of the off-screen map needs to change, perhaps due to a window moving on the main screen. This will also check for any change in depth.}FUNCTION UpdateOffscreenForWindow(offscreenHandle: Handle; window: WindowPtr;								  VAR drawNeeded: BOOLEAN): OSErr;*)END. {Offscreen}