{
	File:		BitEdit.i

	Written by:	Steve Horowitz, Peter Alley, and Craig Carper

	Copyright:	© 1990 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		 <3>	  2/5/90	dba		get rid of BitMapPtr, since it is now in QuickDraw.p
		 <1>	 1/22/90	JRM		declare BitmapPtr as a type

	To Do:
}

{ BitEdit IMPLEMENTATION section }

{ 8-18-87 }
{ version 2.0A1 10-29-89 by Craig Carper }
{ written by Steve Horowitz; further development by Craig Carper }

{  CHANGES
		¥ (01-03-90 crc)	BEBounds: Added LockHands, UnlockHands.
			DrawLasso: Renamed local variable selRect to selBBox to avoid conflict with field name in
				BESelectRect.
			This file was changed to provide the implementation section only; it is included by the
				interface file BitEdit.p.
			Renamed fields of BitEditRecord to those used in interface file.
		¥ (12-12-89 crc)	BEInvalRect: Limit argument rectangle to it's intersection with rEdit.  BEInvalRect is
				called internally with this argument set to the selection, part of which may lie outside
				rEdit--causing an invalid update of the views.
			DragSelection: If dragging a copy, set the wasErased flag to true without erasing the original.
		¥ (12-05-89 crc)	BEObject: Eliminated "idle" and "select" parameter.  "select" was only used to prevent
			making an undo copy when dragging out a selection--this is inconsistent with other functions, and
			does not appear to introduce any anomilies.  "idle" was folded into the "flags" parameter.
			BEPaste: Changed so that if pasting into a moved selection, the selection is replaced without
				first putting it down.
			BESelect: Fixed to always save undo state before creating a new selection
		¥ (12-04-89 crc)	Many changes so Undo restores selections:
			BESetupUndo: Renamed SetupUndo, which is not exported.
			BEUndoBegin,BEUndoEnd: Replacements for BESetupUndo to allow freezing the
				undo state across multiple calls to the BitEdit package.  These calls are counting, so
				if nested, undo is not re-enabled untill first BEUndoBegin is matched by a BEUndoEnd.
		¥ (12-01-89 crc)	Collected all selection variables into a record structure.
		¥ (11-30-89 crc)	Implemented fShapeUnbounded in OvalDraw, RectDraw, and RndRectDraw.
		¥ (11-29-89 crc)
			IMPORTANT: These changes result in a 2.0 package that is INCOMPATABLE with the 1.0 version;
				some existing routines were eliminated, others had changes to their parameter list.  In any
				event, all clients must relink since this version of the package is INCOMPATABLE with
				previous versions of BEPackLib.a.
			BEPaintBucket: Fills with current pattern; non-color support (untested).
			BERotate: Fix for non-color support (untested).
			BESetupOffScreen: Renamed to BESetupUndo.
			BEScroll: Deleted (never implemented).
			BELasso: Deleted (subsumed by BESelect).
			BEDiagonalLine, BEFillOval, BEFillRect, BEFillRndRect, BECircle, BEFillCircle, BESquare,
			BEFillSquare, BERndSquare, BEFillRndSquare: Deleted (subsumed by other routines).
			BELine, BEOval, BERec, BEObject: Added flags parameter.
			BERndRect: Renamed BERoundRect, added flags parameter, moved BitEditHandle parameter to end of parameter list.
			BEFlipSelection, BERotateSelection, BENudgeSelection, BEDupSelection:
				Renamed to BEFlip, BERotate, BENudge, and BEDuplicate, respectively.
			BESelect: Added flags parameter.
			BERect: Added refCon field.
			BEPutSelBits: Renamed to BEPutSelection;
		¥ (11-26-89 crc)	BEColor, BEPaintBucket: New routines.
		¥ (11-01-89 crc)	BENudgeSelection: always draws image without ants, so caller can always copy
				the image without picking up the ants.
			BEObject: Invalidate pattern before and after drawing into the image, in case client uses
				the same pattern to draw in a different GDevice.
		¥ (10-31-89 crc)	CenterScrap: changed to align rects larger than the edit image with the top
				left; this allows pasting of larger PICTs.
			BEPaste: Expand clip rgn & vis rgn to bounds of selection before drawing PICT;
				eliminated redundant code to erase selection bits after creating a selection.
		¥ (10-30-89 crc)  BEPutSelBits: eliminated useless call to SetupRects.
				??? Is invalid for BEInvalRect to be called between LockHands and UnlockHands ???
			BEDupSelection: new routine.
			ConstrainNudge: new routine.
		¥ (10-29-89 crc)	MarqueeDance: Called by BEIdle in place of MarqueeDraw; allows selRect to remain
				partially outside the edit area (for rotate).  Uses selRect instead of selection rectangle,
				and allows ants to march out of the edit area.
			BESetSelect: Constrain argument rectangle to editRect, then further constrain the bottom right
				of selRect to be one pixel inside the bottom right of editRect.  Also, moved call to
				BEKillSelection ahead of DoQuickPortToDup, so ants are not copied.
			DragSelection: Allow marquee selection to be dragged beyond edge of edit rectangle as long as
				one edge of selection remains over the edit rectangle.
			SetupRects, SetUpSelBits: Undid change described below, as this caused partially offscreen
				selections to be scaled when copied to image.
		¥ (10-28-89 crc)	EraseUnderSelection: new routine, called by DragSelection, BENudgeSelection, and
				BERotateSelection.
			BERotateSelection, GetPix, SetPix, SetupExtract: new routines.
		¥ (10-27-89 crc)	SetupRects, SetUpSelBits: After calling StretchRect, intersect with bounds rect
				of bitmap so any selection established by BESetSelect remains withing the bitmap.
			BEFlipSelection:  New routine.
		¥ (10-02-89 crc)	BEDraw: Call BWOffScreen if offHandle is Nil.
				When drawing directly to the screen, don't need to lock & unlock offHandle.
			BEView: Moved initialization of offPix to BENew; eliminated call to ResetOffScreen
				since BEDraw will call it if necessary.
		¥ ( 9-29-89 crc)	BEActivate: On deactivate, discard offscreen bits & pixMap.  This significantly
				reduces the amount of memory used by the BitEdit record.
		¥ ( 9-23-89 crc)	BESquare, BEFillSquare, BERndSquare, BEFillRndSquare, BECircle,
				BEFillCircle, BEDiagonalLine: New exported routines.
			SquareRect, ClipAnchorRect, SquareDraw, FillSquareDraw, RndSquareDraw,
				FillRndSquareDraw, CircleDraw, FillCircleDraw, DiagonalLineDraw, WidthHeightRect: New
				routines.
			RectDraw, FillRectDraw, RndRectDraw, FillRndRectDraw, OvalDraw, FillOvalDraw: Constrained
				to keep within edit area.
		¥ ( 7-24-89 crc)	BEDraw: Constructs offscreen pixMap based on deepest device, not on the
			window's port (which always reflects the main screen).
			BEView: Do not initialize offPix (requires knowing deepest device).
			BWOffscreen: Eliminated GrafPort^ parameter (not used).
			ResetOffscreen: Takes GDHandle instead of a GrafPort.
		¥ ( 7-17-89 crc)	Note: The BitEdit package assumes that only PixMaps are edited in color
			grafports.  The package will fail if a BitMap is passed to BENew when the current port is
			a color graf port.  The package looks into the port's BitMap to find the bounds, for example,
			without checking to see if the port contains a PixMap instead.  This is probably due to the
			fact that BitEdit was first a BitMap only editor, and color was added later.
		¥ ( 5-31-89 crc)	Changed MyDisposPixMap to take a PixMapHandle instead of a VAR PixMapHandle,
			which bad news if the argument lies within an unlocked relocatable object.
			Changed GetBWTable parameter to not be VAR since it was not necessary.
		¥ ( 5-24-89 crc)	BEDraw calls ResetOffscreen if the port's color table has changed, which means
			that the inverse table no longer corresponds to the color table in offPix.
		¥ (crc)  Added ImageCt2DeviceCt to create a device color table from an image color table.
			BENew was incorrectly installing an image color table as a device color table.
		¥ (crc)  Added the internal routine CopyPixels for transferring pixels to off-screen bitmaps.
			This is to get around a bug (fixed in 32-bit QD) that causes CopyBits to colorize pixels.
	(Peter Alley)
		¥ Added CopySetToPort to BEPaste
		¥ Added BESetupOffScreen to the end of BENew so that undo before cut, copy or clear
			will not put garbage into the bitmap.
		¥ Added BESelection routine 8/5/88.
 }

{USES Memtypes, Quickdraw, OSIntf, ToolIntf, PackIntf}

CONST
	antSpeed = 6;

	{ Bit flags for shape drawing }
	fShapeUnframed = $1;	{ INTERNAL USE ONLY }

	{ Bit flags for MarqueeDraw }
	fUseSelection			= $00;	{ Keep the current selection rectangle }
	fSetSelection			= $01;	{ Set the selection rectangle }

	{ Bit flags interpreted by BEObject }
	fIdle						= $8000;

PROCEDURE FixRect(VAR r: Rect); EXTERNAL;
PROCEDURE GetSelPat(patIndex: INTEGER; VAR pat: Pattern); EXTERNAL;
PROCEDURE PaintBits(blacken, erase: BOOLEAN; stPt, endPt: Point; hbe: BEHandle); FORWARD;
PROCEDURE SetUpSelBits(r: Rect; hbe: BEHandle); FORWARD;
PROCEDURE SetupUndo(hbe: BEHandle); FORWARD;
PROCEDURE MarqueeDraw(r: Rect; flags: BEDrawFlags; hbe: BEHandle); FORWARD;
PROCEDURE DrawLasso(startPt: Point; hbe: BEHandle); FORWARD;
PROCEDURE DragSelection(startPt: Point; flags: BESelectFlags; hbe: BEHandle); FORWARD;
PROCEDURE AlignBits(hbe: BEHandle); FORWARD;
PROCEDURE CenterScrap(VAR r:Rect; hbe: BEHandle); FORWARD;
PROCEDURE HiliteLasso(hbe: BEHandle); FORWARD;
PROCEDURE TrimBBox(dstBuf: QDPtr; VAR dstRect: Rect; rowB: INTEGER); EXTERNAL;
PROCEDURE CalcEdges(srcBuf,dstBuf: QDPtr;
		        topVert,height,rowBytes: INTEGER;
		        lassoBlack: BOOLEAN); EXTERNAL;
PROCEDURE ClearHandle(block: Handle); EXTERNAL;
PROCEDURE LockHands(hbe: BEHandle); FORWARD;
PROCEDURE UnlockHands(hbe: BEHandle); FORWARD;
PROCEDURE SetupRects(VAR r1, r2: Rect; hbe: BEHandle); FORWARD;
FUNCTION  GetRgnMax: INTEGER; EXTERNAL;
FUNCTION	 NewPtrClr(logicalSize: Size): Ptr; EXTERNAL;
PROCEDURE ClipAnchorRect (VAR r: Rect; VAR rClip: Rect); FORWARD;
PROCEDURE SquareRect (VAR r: Rect); FORWARD;
PROCEDURE WidthHeightRect (VAR r: Rect; VAR pt: Point); FORWARD;
FUNCTION  UIntMul(i,j: INTEGER): LONGINT; INLINE
														{ move	(sp)+,d0 } $301F,
														{ mulu	(sp)+,d0 } $C0DF,
														{ move.l	d0,(sp)  } $2E80;
PROCEDURE CallDrawProc(r:Rect; flags: BEDrawFlags; hbe: BEHandle; proc: ProcPtr); INLINE
														{ move.l	(sp)+,a0 } $205F,
														{ jsr	(a0) } $4E90;

{ ---- Begin BitEdit Routines ---- }

PROCEDURE StretchRect(VAR r: Rect);

BEGIN
	r.right := r.right + 1;
	r.bottom := r.bottom + 1;
END;


PROCEDURE LockHands(hbe: BEHandle);
{ Lock down the handles to the bits and set the baseAddr field }
{ of the bitMap or pixMap (lock it if it's a pixMap) }

BEGIN
	HLock(Handle(hbe));

	WITH hbe^^ DO
		BEGIN
		IF dupHandle <> Nil THEN
			BEGIN
			HLock(dupHandle);
			IF isColor THEN
			{ lock the pixMap handle }
				BEGIN
				HLock(Handle(dupPix));
				dupPix^^.baseAddr := dupHandle^;
				END
			ELSE
				dupBits.baseAddr := dupHandle^;
			END;
		END; { WITH hbe^^ }

	WITH hbe^^.selection DO
		BEGIN
		IF selHandle <> Nil THEN
			BEGIN
			HLock(selHandle);
			IF hbe^^.isColor THEN
			{ lock the pixMap handle }
				BEGIN
				HLock(Handle(selPix));
				selPix^^.baseAddr := selHandle^;
				END
			ELSE
				selBits.baseAddr := selHandle^;
			END;

		IF maskHandle <> Nil THEN
			BEGIN
			HLock(maskHandle);
			IF hbe^^.isColor THEN
			{ lock the pixMap handle }
				BEGIN
				HLock(Handle(maskPix));
				maskPix^^.baseAddr := maskHandle^;
				END
			ELSE
				maskBits.baseAddr := maskHandle^;
			END;
		END; { WITH hbe^^.selection }
END; { LockHands }


PROCEDURE UnlockHands(hbe: BEHandle);
{ unlock the handles to the Bits and unlock the pixMap handle if we're in color }

BEGIN
	HUnlock(Handle(hbe));

	WITH hbe^^ DO
		BEGIN
		IF dupHandle <> Nil THEN
			BEGIN
			HUnlock(dupHandle);
			IF isColor THEN
				HUnlock(Handle(dupPix));
			END;
		END; { WITH hbe^^ }

	WITH hbe^^.selection DO
		BEGIN
		IF selHandle <> Nil THEN
			BEGIN
			HUnlock(selHandle);
			IF hbe^^.isColor THEN
				HUnlock(Handle(selPix));
			END;

		IF maskHandle <> Nil THEN
			BEGIN
			HUnlock(maskHandle);
			IF hbe^^.isColor THEN
				HUnlock(Handle(maskPix));
			END;
		END; { WITH hbe^^.selection }
END; { UnlockHands }


PROCEDURE SetupRects(VAR r1, r2: Rect; hbe: BEHandle);

VAR
	b: BOOLEAN;

BEGIN
	WITH hbe^^, hbe^^.selection DO
		BEGIN
		r1 := selRect;
		StretchRect(r1);

		IF maskHandle <> Nil THEN
			BEGIN
			IF isColor THEN
				r2 := maskPix^^.bounds
			ELSE
				r2 := maskBits.bounds;
			InsetRect(r2, 1, 1);
			END;
		END;
END; { SetupRects }


PROCEDURE GetMIndex(VAR mIndex: INTEGER);

VAR
	now: INTEGER;

BEGIN
	now := LoWrd(TickCount);
	now := now DIV antSpeed;
	mIndex := BAnd(now, 7);
END;

{ cc }
PROCEDURE CopyPixels (hpmSource, hpmDest: PixMapHandle);

BEGIN
	BlockMove (hpmSource^^.baseAddr, hpmDest^^.baseAddr,
				 INTEGER(BAND(hpmDest^^.rowBytes, $7FFF))*(hpmDest^^.bounds.bottom-hpmDest^^.bounds.top));
END;

PROCEDURE CSelToPort(hbe: BEHandle; r1: Rect; isDrag: BOOLEAN);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
	IF isDrag THEN
		r := r1;
	WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(selection.selPix^)^, beGrafPtr^.portBits, selection.selPix^^.bounds, r, srcCopy, Nil)
		ELSE
			CopyBits(selection.selBits, beGrafPtr^.portBits, selection.selBits.bounds, r, srcCopy, Nil);
END;


PROCEDURE OSelToPort(hbe: BEHandle; r1: Rect; isDrag: BOOLEAN);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
	IF isDrag THEN
		r := r1;
	WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(selection.selPix^)^, beGrafPtr^.portBits, srcRect, r, srcOr, Nil)
		ELSE
			CopyBits(selection.selBits, beGrafPtr^.portBits, srcRect, r, srcOr, Nil);
END;


PROCEDURE MaskToPort(hbe: BEHandle; r1: Rect; isDrag: BOOLEAN);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
	IF isDrag THEN
		r := r1;
	WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(selection.maskPix^)^, beGrafPtr^.portBits, srcRect, r, srcBic, Nil)
		ELSE
			CopyBits(selection.maskBits, beGrafPtr^.portBits, srcRect, r, srcBic, Nil);
END;


PROCEDURE CSelToDup(hbe: BEHandle);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
	WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(selection.selPix^)^, BitMapPtr(dupPix^)^, selection.selPix^^.bounds, r,
						srcCopy, Nil)
		ELSE
			CopyBits(selection.selBits, dupBits, selection.selBits.bounds, r, srcCopy, Nil);
END;


PROCEDURE OSelToDup(hbe: BEHandle);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
	WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(selection.selPix^)^, BitMapPtr(dupPix^)^, srcRect, r, srcOr, Nil)
		ELSE
			CopyBits(selection.selBits, dupBits, srcRect, r, srcOr, Nil);
END;


PROCEDURE MaskToDup(hbe: BEHandle);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
		WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(selection.maskPix^)^, BitMapPtr(dupPix^)^, srcRect, r, srcBic, Nil)
		ELSE
			CopyBits(selection.maskBits, dupBits, srcRect, r, srcBic, Nil);
END;


PROCEDURE DupToPort(hbe: BEHandle);

BEGIN
	WITH hbe^^ DO
		IF isColor THEN
			{ cc }
			CopyPixels (dupPix, CGrafPtr(beGrafPtr)^.portPixMap)
			{ CopyBits(BitMapPtr(dupPix^)^, beGrafPtr^.portBits, dupPix^^.bounds,
						cGrafPtr(beGrafPtr)^.portPixMap^^.bounds, srcCopy, Nil) }
		ELSE
			CopyBits(dupBits, beGrafPtr^.portBits, dupBits.bounds,
						beGrafPtr^.portBits.bounds, srcCopy, Nil);
END;


PROCEDURE DupToSel(hbe: BEHandle);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
	WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(dupPix^)^, BitMapPtr(selection.selPix^)^, r, selection.selPix^^.bounds, srcCopy, Nil)
		ELSE
			CopyBits(dupBits, selection.selBits, r, selection.selBits.bounds, srcCopy, Nil);
END;

{ As modified by cc }
PROCEDURE OldPortToDup(hbe: BEHandle);

VAR
	{ cc } rgbFore, rgbBack, rgbZeros, rgbOnes: RGBColor;
	{ cc } thePort: GrafPtr;
	{ cc } theDevice: GDHandle;
	{ cc } pixelOnes, pixelZeros: LONGINT;
BEGIN
	WITH hbe^^ DO
		IF isColor THEN
			BEGIN
			{ Set foreground color to black to prevent unpredictable colorizing during
			  copy bits }
			GetForeColor (rgbFore);
			GetBackColor (rgbBack);
			Index2Color (0, rgbZeros);
			Index2Color (BSL(1,bePixelSize)-1, rgbOnes);
			RGBForeColor (rgbOnes);
			RGBBackColor (rgbZeros);
			pixelOnes := Color2Index (rgbOnes);
			pixelZeros := Color2Index (rgbZeros);
			theDevice := GetGDevice;
			GetPort (thePort);
			CopyBits(beGrafPtr^.portBits, BitMapPtr(dupPix^)^, cGrafPtr(beGrafPtr)^.portPixMap^^.bounds,
						dupPix^^.bounds, srcCopy, Nil);
			RGBForeColor (rgbFore);
			RGBBackColor (rgbBack);
			END
		ELSE
			CopyBits(beGrafPtr^.portBits, dupBits, beGrafPtr^.portBits.bounds, dupBits.bounds,
						srcCopy, Nil);
END;

PROCEDURE PortToDup(hbe: BEHandle);

BEGIN
	WITH hbe^^ DO
		IF isColor THEN
			{ cc }
			BEGIN
				{
				CopyPixels (CGrafPtr(beGrafPtr)^.portPixMap, dupPix)
				}
				OldPortToDup (hbe)
				{
				CopyBits(beGrafPtr^.portBits, BitMapPtr(dupPix^)^, cGrafPtr(beGrafPtr)^.portPixMap^^.bounds,
						dupPix^^.bounds, srcCopy, Nil)
				}
			END
		ELSE
			CopyBits(beGrafPtr^.portBits, dupBits, beGrafPtr^.portBits.bounds, dupBits.bounds,
						srcCopy, Nil);
END;


PROCEDURE EdgeToPort(hbe: BEHandle; mode: INTEGER);

VAR
	r, srcRect: Rect;

BEGIN
	SetupRects(r, srcRect, hbe);
	WITH hbe^^ DO
		IF isColor THEN
			CopyBits(BitMapPtr(selection.edgePix^)^, beGrafPtr^.portBits, srcRect, r, mode, Nil)
		ELSE
			CopyBits(selection.edgeBits, beGrafPtr^.portBits, srcRect, r, mode, Nil);
END;


PROCEDURE CopySelToPort(hbe: BEHandle);

VAR
	r: Rect;

BEGIN
	IF hbe^^.marquee THEN
		CSelToPort(hbe, r, False)
	ELSE
		BEGIN
		MaskToPort(hbe, r, False);
		OSelToPort(hbe, r, False);
		END;
END;


PROCEDURE DoQuickDupToPort(hbe: BEHandle);

BEGIN
	BEDrawBegin(hbe);
	LockHands(hbe);
	DupToPort(hbe);
	UnlockHands(hbe);
	BEDrawEnd(hbe);
END;


PROCEDURE DoQuickPortToDup(hbe: BEHandle);

BEGIN
	BEDrawBegin(hbe);
	PortToDup(hbe);
	BEDrawEnd(hbe);
END;


PROCEDURE MyDisposPixMap(p: PixMapHandle);

VAR
	n: Handle;

BEGIN
	IF p <> Nil THEN
		BEGIN
		n := NewHandle(0);
		p^^.pmTable := CTabHandle(n);
		DisposPixMap(p);
		END;
END;


PROCEDURE BEActivate(activate: BOOLEAN; hbe: BEHandle);
{ handle activate/deactivate events }

VAR
   hUse: BEHandle;

BEGIN
	IF NOT EmptyRect(hbe^^.selection.selRect) AND NOT activate THEN
		BEGIN
		LockHands(hbe);
		BEDrawBegin(hbe);
		CopySelToPort(hbe);
		BEDrawEnd(hbe);
		UnlockHands(hbe);

		BEInvalRect(hbe^^.beEditRect, hbe);
		END;

	hUse:= hbe;
	REPEAT
	IF activate THEN
		hUse^^.active := TRUE
	ELSE
		BEGIN
		hUse^^.active := FALSE;
		IF hbe^^.offHandle <> Nil THEN
			BEGIN
			DisposHandle(hbe^^.offHandle);
			hbe^^.offHandle := Nil;
			IF hbe^^.isColor THEN
				BEGIN
				DisposPixMap(hbe^^.offPix);
				hbe^^.offPix := Nil
				END;
			END;
		END;
	hUse := hUse^^.sibling;
	UNTIL hUse = hbe;
END; { BEActivate }


PROCEDURE BEDoDraw(draw: BOOLEAN; hbe: BEHandle);
{ turn drawing on and off }

VAR
   hUse: BEHandle;

BEGIN
	hUse := hbe;
	REPEAT
	IF draw THEN
		hUse^^.drawEnabled := True
	ELSE
		hUse^^.drawEnabled := False;
	hUse := hUse^^.sibling;
	UNTIL hUse = hbe;
END; { BEDoDraw }


PROCEDURE AlignBits(hbe: BEHandle);
{ aligns the big offscreen bitmap with the screen for maximum copybits speed }

VAR
	bitNum, width: INTEGER;

BEGIN
	WITH hbe^^ DO
	BEGIN
		bitNum := (beViewRect.left - beGrafPtr^.portBits.bounds.left) MOD 32;
		IF bitNum < 0 THEN bitNum := bitNum + 32;
		WITH offBits.bounds DO
		BEGIN
			width := right - left;
			left := beViewRect.left - bitNum;
			right := left + width;
		END;
	END;
END; { AlignBits }


PROCEDURE BEBitmap(VAR bm: Bitmap; hbe: BEHandle);
{ set up a bitmap according to a BitEdit record }

BEGIN
	WITH hbe^^, bm DO
		BEGIN
		baseAddr := POINTER(ORD(editHandle^) + editOffset);
		rowBytes := beRowBytes;
		bounds := beBounds;
		END;
END; { BEBitmap }


PROCEDURE BEPixmap(VAR bm: PixMapHandle; hbe: BEHandle);
{ set up a pixmap according to a BitEdit record }

BEGIN
	WITH hbe^^ DO
		BEGIN
		editPix^^.baseAddr := POINTER(ORD(editHandle^) + editOffset);
		bm := editPix;
		END;
END; { BEBitmap }


FUNCTION BEBit(pt: Point; hbe: BEHandle): BOOLEAN;
{ Get the value of the pixel at pt }

VAR
	myRGB: RGBColor;

BEGIN
	BEBit := FALSE;
	BEPtXForm(pt, hbe);
	WITH hbe^^ DO
		BEGIN
		IF PtInRect(pt, beEditRect) THEN
			BEGIN
			BEDrawBegin(hbe);
			IF isColor THEN
				BEGIN
				GetCPixel(pt.h, pt.v, myRGB);
				IF (myRGB.red = bePixColor.red) &
					(myRGB.green = bePixColor.green) &
					(myRGB.blue = bePixColor.blue) THEN
					BEBit := True;
				END
			ELSE
				BEBit := GetPixel(pt.h, pt.v);
			BEDrawEnd(hbe);
			END;
		END;
END; { BEBit }


PROCEDURE BEColor (pt: Point; VAR rgb: RGBColor; hbe: BEHandle);
{ Returns in rgb the color of the pixel at pt; returns white if pt is outside beEditRect or
  if the BitEdit object is not in color }

BEGIN
	WITH rgb DO
		BEGIN red := 0; green := 0; blue := 0; END;
	BEPtXForm (pt, hbe);
	IF PtInRect(pt,hbe^^.beEditRect) AND hbe^^.isColor THEN
		BEGIN
		BEDrawBegin (hbe);
		GetCPixel (pt.h, pt.v, rgb);
		BEDrawEnd (hbe);
		END;
END; { BEColor }


PROCEDURE BEPaintBucket (startPt: Point; hbe: BEHandle);
{ Paint the bit/pixel at point startPt (in window coordinates) and all adjacent bits/pixels of the same value
  with 1/the current color }

VAR
	bmMask:	BitMap;
	r:			Rect;
	port:		GrafPtr;
	rgbBlack:RGBColor;
	hpmPat:	PixMapHandle;
	bmPat:	BitMap;
	p:			Ptr;

BEGIN
	IF BESelection(hbe) THEN
		BEPutSelection (hbe)
	ELSE
		SetupUndo (hbe);

	{ Get the rectangle enclosing the image }
	r := hbe^^.beBounds;

	{ Setup the mask }
	bmMask.rowBytes := ((r.right - r.left) + 7) DIV 8;		{ minimum number of bytes }
	bmMask.rowBytes := BAND (bmMask.rowBytes+1, $FFFE);	{ make it an even number of bytes for SeedCFill }
	bmMask.bounds := r;
	bmMask.baseAddr := NewPtr (UIntMul(bmMask.rowBytes, (r.bottom - r.top)));
	hbe^^.error := MemError;
	If hbe^^.error <> NoErr THEN
		Exit(BEPaintBucket);

	{ Expand the pattern }
	IF hbe^^.isColor THEN
		BEGIN
		hpmPat := hbe^^.editPix;
		hbe^^.error := HandToHand (Handle (hpmPat));
		IF hbe^^.error <> NoErr THEN
			BEGIN
			DisposPtr (bmMask.baseAddr);
			Exit(BEPaintBucket);
			END;
		p := NewPtr (UIntMul(BAND (hpmPat^^.rowBytes, $7FFF), (r.bottom - r.top)));
		hpmPat^^.baseAddr := p;
		hbe^^.error := MemError;
		IF hbe^^.error <> NoErr THEN
			BEGIN
			DisposPtr (bmMask.baseAddr);
			HUnlock (Handle (hpmPat));
			DisposHandle (Handle (hpmPat));
			Exit(BEPaintBucket);
			END;
		END
	ELSE
		BEGIN
		bmPat.bounds := r;
		bmPat.rowBytes := hbe^^.beRowBytes;
		bmPat.baseAddr := NewPtr (UIntMul(bmPat.rowBytes, (r.bottom - r.top)));
		hbe^^.error := MemError;
		IF hbe^^.error <> NoErr THEN
			BEGIN
			DisposPtr (bmMask.baseAddr);
			Exit(BEPaintBucket);
			END;
		END;

	{ Create the pattern }
	LockHands(hbe);
	BEDrawBegin (hbe);
	IF hbe^^.isColor THEN
		BEGIN
		WITH rgbBlack DO
			BEGIN red := 0; green := 0; blue := 0; END;
		HLock (Handle (hpmPat));
		SetPortPix (hpmPat);
		RGBForeColor(hbe^^.bePixColor);
		hbe^^.bePixPat^^.patXValid := -1;
		FillCRect(r, hbe^^.bePixPat);
		RGBForeColor (rgbBlack);
		HUnlock (Handle (hpmPat));
		END
	ELSE
		BEGIN
		SetPortBits (bmPat);
		FillRect (r, hbe^^.beFillPat);
		END;
	BEDrawEnd (hbe);
	UnlockHands(hbe);

	{ Translate window point into bitmap local coordinates }
	BEPtXForm (startPt, hbe);

	LockHands(hbe);
	BEDrawBegin (hbe);
	GetPort (port);

	IF hbe^^.isColor THEN
		BEGIN
		{ Calculate the mask }
		SeedCFill (port^.portBits, bmMask, r, r, startPt.h, startPt.v, nil, 0);

		{ Spread the pattern }
		HLock (Handle (hpmPat));
		CopyMask (BitMapPtr(hpmPat^)^, bmMask, port^.portBits, r, r, r);
		HUnlock (Handle (hpmPat));

		{ Discard expanded pattern }
		DisposPtr (hpmPat^^.baseAddr);
		DisposHandle (Handle (hpmPat));
		END
	ELSE
		BEGIN
		SeedFill (port^.portBits.baseAddr, bmMask.baseAddr, hbe^^.beRowBytes, bmMask.rowBytes, (r.bottom-r.top),
						bmMask.rowBytes DIV 2, startPt.h, startPt.v);
		CopyMask (bmPat, bmMask, port^.portBits, r, r, r);

		{ Discard expanded pattern data }
		DisposPtr (bmPat.baseAddr);
		END;

	BEDrawEnd (hbe);
	UnlockHands(hbe);

	{ Force redraw }
	BEInvalRect (r, hbe);

	{ Discard mask }
	DisposPtr (bmMask.baseAddr);
END; { BEPaintBucket }


PROCEDURE DoSaveBits(hbe: BEHandle);

BEGIN
	WITH hbe^^ DO
		BEGIN
		IF isColor THEN
			savePixMap := cGrafPtr(beGrafPtr)^.portPixMap
		ELSE
			saveBitMap := beGrafPtr^.portBits;
		END;
END;


PROCEDURE DoRestoreBits(hbe: BEHandle);

BEGIN
	WITH hbe^^ DO
		BEGIN
		IF isColor THEN
			SetPortPix(savePixMap)
		ELSE
			SetPortBits(saveBitMap);
		END;
END;


FUNCTION  BEInSelection(pt: Point; hbe: BEHandle): BOOLEAN;
{ tell whether or not the given point is over a selection. }
{ returns False if there is no selection. }

VAR
	r: Rect;
	saveBits: PixMapHandle;
	tPt: Point;
	myC: RGBColor;

BEGIN
	WITH hbe^^, hbe^^.selection DO
		BEGIN
		BEInSelection := False;
		IF NOT EmptyRect(selRect) OR (maskHandle <> Nil) THEN
			BEGIN
			r := selRect;
			StretchRect(r);
			BEPtXForm(pt, hbe);
			IF marquee THEN
				BEGIN
				IF PtInRect(pt, r) THEN
					BEInSelection := True;
				END
			ELSE
				IF PtInRect(pt, r) THEN
					BEGIN
					DoSaveBits(hbe);
					tPt := r.topLeft;

					IF isColor THEN
						BEGIN
						maskPix^^.baseAddr := maskHandle^;
						SetPortPix(maskPix);
						SubPt(maskPix^^.bounds.topLeft, tPt);
						END
					ELSE
						BEGIN
						maskBits.baseAddr := maskHandle^;
						SetPortBits(maskBits);
						SubPt(maskBits.bounds.topLeft, tPt);
						END;

					SubPt(tPt, pt);
					pt.h := pt.h + 1;
					pt.v := pt.v + 1;

					IF isColor THEN
						BEGIN
						GetCPixel(pt.h, pt.v, myC);
						WITH myC DO
							IF (red <> $FFFF) | (blue <> $FFFF) | (green <> $FFFF) THEN
								BEInSelection := True;
						END
					ELSE
						IF GetPixel(pt.h, pt.v) THEN
							BEInSelection := True;
					DoRestoreBits(hbe);
					END;
			END;
		END;
END; { BEInSelection }


FUNCTION  BESelection(hbe: BEHandle): BOOLEAN;
{ tell whether or not there is a selection. }
{ returns False if there is no selection. }

BEGIN
	WITH hbe^^.selection DO
		BEGIN
		IF EmptyRect(selRect) AND (maskHandle = Nil) THEN
			BESelection := FALSE
		ELSE
			BESelection := TRUE;
		END;
END;	{ BESelection }


PROCEDURE GetBWTable(tempTable: CTabHandle);

VAR
	i: INTEGER;

BEGIN
	WITH tempTable^^ DO
		FOR i := 0 to ctSize DO
			BEGIN
			IF (ctTable[i].rgb.red <> $FFFF) OR
				(ctTable[i].rgb.green <> $FFFF) OR
				(ctTable[i].rgb.blue <> $FFFF) THEN
					BEGIN
					ctTable[i].rgb.red := 0;
					ctTable[i].rgb.green := 0;
					ctTable[i].rgb.blue := 0;
					END;
			END;
END;


FUNCTION myGetPixel(x, y: INTEGER; hbe: BEHandle): BOOLEAN;

VAR
	 myC: RGBColor;

BEGIN
	IF hbe^^.isColor THEN
		BEGIN
		myGetPixel := False;
		GetCPixel(x, y, myC);
		WITH myC DO
			IF (red <> $FFFF) OR (blue <> $FFFF) OR (green <> $FFFF) THEN
				myGetPixel := True;
		END
	ELSE
		myGetPixel := GetPixel(x,y);
END;


PROCEDURE BEBounds(VAR minimalRect: Rect; hbe: BEHandle);
{ returns the minimal bounds rect of the bitmap }

VAR
    x, y: INTEGER;
	 tempRect: Rect;

BEGIN
	LockHands(hbe);
	WITH hbe^^.beEditRect DO
		SetRect(minimalRect, right, bottom, left, top);
	tempRect := minimalRect;
	WITH hbe^^, beEditRect DO
		BEGIN
		BEDrawBegin(hbe);
		HideCursor;

	{ check for left boundary }
		y := top;
		x := left;
		WHILE y < bottom DO
			BEGIN
			WHILE x < right DO
				BEGIN
				IF myGetPixel(x, y, hbe) THEN
					IF minimalRect.left > x THEN minimalRect.left := x;
				x := x + 1;
				END;
			y := y + 1;
			x := left;
			END;

	{ check for right boundary }
		y := top;
		x := right - 1;
		WHILE y < bottom DO
			BEGIN
			WHILE x >= left DO
				BEGIN
				IF myGetPixel(x, y, hbe) THEN
					IF minimalRect.right < x THEN minimalRect.right := x;
				x := x - 1;
				END;
			y := y + 1;
			x := right - 1;
			END;

	{ check for top boundary }
		y := top;
		x := left;
		WHILE x < right DO
			BEGIN
			WHILE y < bottom DO
				BEGIN
				IF myGetPixel(x, y, hbe) THEN
					IF minimalRect.top > y THEN minimalRect.top := y;
				y := y + 1;
				END;
			x := x + 1;
			y := top;
			END;

	{ check for bottom boundary }
		y := bottom - 1;
		x := left;
		WHILE x < right DO
			BEGIN
			WHILE y >= top DO
				BEGIN
				IF myGetPixel(x, y, hbe) THEN
					IF minimalRect.bottom < y THEN minimalRect.bottom := y;
				y := y - 1;
				END;
			x := x + 1;
			y := bottom - 1;
			END;

		IF EqualRect(tempRect, minimalRect) THEN
			SetRect(minimalRect, 0, 0, 0, 0)
		ELSE
			BEGIN
			StretchRect(minimalRect);
			END;

		BEDrawEnd(hbe);
		ShowCursor;
		END;

UnlockHands(hbe);
END; { BEBounds }


PROCEDURE MySetDupBits(hbe: BEHandle);

BEGIN
	WITH hbe^^ DO
		BEGIN
		IF isColor THEN
			SetPortPix(dupPix)
		ELSE
			SetPortBits(dupBits);
		END;
END;


PROCEDURE BEClear(hbe: BEHandle);
{ Clears the selection }

VAR
	r, srcRect: Rect;

BEGIN
	IF NOT EmptyRect(hbe^^.selection.selRect) THEN
		BEGIN
		SetupRects(r, srcRect, hbe);
		LockHands(hbe);

		IF NOT hbe^^.selection.wasErased THEN
			BEGIN
			BEDrawBegin(hbe);
			CopySelToPort(hbe);
			IF hbe^^.marquee THEN
				BEGIN
				MySetDupBits(hbe);
				EraseRect(r);
				END
			ELSE
				MaskToDup(hbe);
			BEDrawEnd(hbe);
			END;

		UnlockHands(hbe);
		BEKillSelection(hbe);

		END;

END; { BEClear }


FUNCTION BEGetView(pt: Point; VAR hbe: BEHandle): BOOLEAN;

VAR
   found: Boolean;
	hUse: BEHandle;

BEGIN
	 hUse := hbe;
	 REPEAT
	    found := PtInRect(pt, hUse^^.beViewRect);
		 IF found THEN
		    hbe := hUse
		 ELSE
		    hUse:= hUse^^.sibling;
	 UNTIL hUse = hbe;
	 BEGetView := found;
END; { BEGetView }


PROCEDURE BEUpdateRect(r: Rect; hbe: BEHandle);
{ Redraws the given rectangle (in screen space) }

VAR
    theRect: rect;
	 hUse: BEHandle;
	 t: Boolean;

BEGIN
	hUse:= hbe;
	REPEAT
		IF SectRect(r, hUse^^.beViewRect, theRect) THEN
			BEGIN
				BEPtXForm(theRect.topLeft, hUse);
				BEPtXForm(theRect.botRight, hUse);
				InsetRect(theRect, -1, -1);
				t := SectRect(theRect, hUse^^.beEditRect, theRect);
				BEDraw(theRect, hUse);
			END;
		hUse:= hUse^^.sibling;
	UNTIL hUse = hbe;
END; { BEUpdateRect }


PROCEDURE BEInvalRect(r: Rect; hbe: BEHandle);

VAR
	hFirst: BEHandle;
	b: BOOLEAN;

BEGIN
	b := SectRect (r, hbe^^.beEditRect, r);
	hFirst := hbe;
	REPEAT
		BEDraw(r, hbe);
		hbe := hbe^^.sibling;
	UNTIL hFirst = hbe;
END; { BEInvalRect }


PROCEDURE PaintBits(blacken, erase: BOOLEAN; stPt, endPt: Point; hbe: BEHandle);

VAR
	testPt: Point;
	t: BOOLEAN;
	r: Rect;
	saveRgn: Handle;
	blackRGB: RGBColor;

BEGIN
	testPt := stPt;
	BEPtXForm(stPt, hbe);
	BEPtXForm(endPt, hbe);
	GetPenState(hbe^^.savePen);

	WITH blackRGB DO
		BEGIN
		red := 0; blue := 0; green := 0;
		END;

	IF (LongInt(stPt) <> LongInt(endPt)) |
	   (blacken <> BEBit(testPt, hbe)) | erase THEN
	  BEGIN
			BEDrawBegin(hbe);
			PenNormal;

			IF NOT blacken THEN PenMode(patBic);
			IF erase THEN penSize(hbe^^.beEraserSize.h, hbe^^.beEraserSize.v);

			IF hbe^^.isColor THEN
				RGBForeColor(hbe^^.bePixColor);

		{ draw the line in small bitmap }
			MoveTo(stPt.h, stPt.v);
			LineTo(endPt.h, endPt.v);

			IF hbe^^.isColor THEN
				RGBForeColor(blackRGB);

			BEDrawEnd(hbe);

		{ figure out what the smallest rectangle to copy is }
		   Pt2Rect(stPt, endPt, r);

		{ expand rect to enclose all pixels }
			StretchRect(r);

			IF erase THEN
				BEGIN
				r.bottom := r.bottom + (hbe^^.beEraserSize.v - 1);
				r.right := r.right + (hbe^^.beEraserSize.h - 1);
				END;

		{ do our own clipping }
			t := SectRect(hbe^^.beEditRect, r, r);

		{ Draw all the views - turn off region collection }
			saveRgn := hbe^^.beGrafPtr^.rgnSave;
			hbe^^.beGrafPtr^.rgnSave := Nil;

			BEInvalRect(r, hbe);

		{ turn region collection back on }
			hbe^^.beGrafPtr^.rgnSave := saveRgn;

	END;
	SetPenState(hbe^^.savePen);
END; { PaintBits }


PROCEDURE BWOffScreen (hbe: BEHandle);

VAR
	n: Handle;
	bitNum: INTEGER;

BEGIN
	WITH hbe^^,offBits DO
	BEGIN
	{ figure out rowbytes from rectangle }
		rowBytes := ((beViewRect.right - beViewRect.left) + 7) DIV 8;

	{ allocate out to long word boundary + 1 long word }
		rowBytes := rowBytes + rowBytes MOD 4 + 4;

	{ figure out which bit in a long word the left edge occupies }
		bitNum := (beViewRect.left - beGrafPtr^.portBits.bounds.left) MOD 32;

	{ make sure answer is positive }
		IF bitNum < 0 THEN bitNum := bitNum + 32;

		with bounds do
		 begin
			top := beViewRect.top;
			left := beViewRect.left - bitNum;
			bottom := beViewRect.bottom;
			right := left + 8 * rowBytes;
		 end;
	END;

	n := NewHandle(hbe^^.offBits.rowBytes * (hbe^^.beViewRect.bottom - hbe^^.beViewRect.top));
	hbe^^.error := MemError;
	If hbe^^.error <> NoErr THEN		{ if not enough memory set error field true and leave }
		Exit(BWOffScreen);

	hbe^^.offHandle := n;
END;


PROCEDURE ResetOffScreen(hgd: GDHandle; hbe: BEHandle);
{ routine to reset the pixmap to the new pixel depth of the screen }

VAR
	viewCTHndl, theHndl: Handle;
	mySize: LongInt;
	myErr: OSErr;
	np: PixMapHandle;

BEGIN
	{ if we already have an offBits, trash it }
	IF hbe^^.offHandle <> Nil THEN
		BEGIN
		DisposHandle(hbe^^.offHandle);
		IF hbe^^.isColor THEN
			DisposPixMap(hbe^^.offPix);
		END;

	IF NOT hbe^^.isColor THEN
		BEGIN
		BWOffScreen (hbe);
		Exit(ResetOffScreen);
		END;

	np := NewPixMap;
	hbe^^.error := MemError;
	IF hbe^^.error <> NoErr THEN
		Exit(ResetOffScreen);
	hbe^^.offPix := np;
	DisposHandle (Handle(hbe^^.offPix^^.pmTable));

	viewCTHndl := Handle (hgd^^.gdPMap^^.pmTable);
	myErr := HandToHand (viewCTHndl);

	WITH hbe^^.offPix^^ DO
	BEGIN
	{ figure out rowbytes from rectangle }
		rowBytes := ((((hgd^^.gdPMap^^.pixelSize * (hbe^^.beViewRect.right - hbe^^.beViewRect.left)) + 15))
							DIV 16) * 2;
		pixelSize := hgd^^.gdPMap^^.pixelSize;
		cmpSize := hgd^^.gdPMap^^.pixelSize;
		pmTable := CTabHandle(viewCTHndl);
		bounds := hbe^^.beViewRect;
	END;

	WITH hbe^^ DO
		mySize := UIntMul((beViewRect.bottom - beViewRect.top), offPix^^.rowBytes);

	theHndl := NewHandle(mySize);
	hbe^^.error := MemError;

	hbe^^.offHandle := theHndl;
	hbe^^.offPix^^.rowBytes := hbe^^.offPix^^.rowBytes + $8000;
END;


PROCEDURE DoMyMapRect(hbe: BEHandle; VAR r: Rect; srcRect: Rect);

VAR
  rvr: Rect;

BEGIN
	WITH srcRect DO
		BEGIN
		bottom := top + 1;
		right := left + 1;
		END;

	rvr.topLeft := hbe^^.beViewRect.topLeft;
	WITH rvr, hbe^^ DO
		BEGIN
		bottom := top + fatbitsSize.v + fatbitsDelta.v;
		right := left + fatbitsSize.h + fatbitsDelta.h;
		END;

	MapRect(r, srcRect, rvr);
END;


PROCEDURE BEDraw(r: Rect; hbe: BEHandle);

TYPE
	TAPat = RECORD
		CASE INTEGER OF
			0: (pat: Pattern);
			1: (lPat0, lPat1: LONGINT);
		END;

VAR
	myWhite: TAPat;
	bigR, bmr, rvr: Rect;
	bm: BitMap;
	pm: PixMapHandle;
	i: INTEGER;
	t: Boolean;
	saveDevice, maxDevice:	GDHandle;
	globalR:	Rect;

BEGIN
	IF hbe^^.drawEnabled THEN
		BEGIN
		GetPenState (hbe^^.savePen);
		GetClip (hbe^^.saveClip);

		HLock (Handle(hbe));
		HLock (hbe^^.editHandle);

		bigR := r;
		bmr.topLeft := hbe^^.beBounds.topLeft;

		DoMyMapRect(hbe, bigR, bmr);

		t := SectRect(hbe^^.beViewRect, hbe^^.realRect, bmr);
		ClipRect(bmr);

		myWhite.lPat0 := 0;
		myWhite.lPat1 := 0;

		IF LongInt(hbe^^.fatbitsDelta) <> 0 THEN
			{ Image to offscreen pixMap to avoid flicker }
			BEGIN

			{ Find deepest screen intersecting fatbits rectangle }
			IF hbe^^.isColor THEN
				BEGIN
				saveDevice := GetGDevice;
				globalR := hbe^^.beViewRect;
				LocalToGlobal (globalR.topLeft);
				LocalToGlobal (globalR.botRight);
				maxDevice := GetMaxDevice (globalR);
				SetGDevice (maxDevice);
				END;

			{ check to see if pixel depth of screen has changed }
			{ or if the port's color table has changed -- 5/24/89 crc }
			IF hbe^^.isColor THEN
				BEGIN
				IF (hbe^^.offPix = Nil)
					|
					(maxDevice^^.gdPMap^^.pixelSize <> hbe^^.offPix^^.pixelSize)
				   |
				   (maxDevice^^.gdPMap^^.pmTable^^.ctSeed <> hbe^^.offPix^^.pmTable^^.ctSeed)
				THEN
					ResetOffscreen (maxDevice, hbe);
				END
			ELSE
				IF (hbe^^.offHandle = Nil) THEN
					BWOffScreen (hbe);

			IF hbe^^.error <> NoErr THEN
				BEGIN
				HUnlock (Handle(hbe));
				HUnlock (hbe^^.editHandle);
				SetPenState(hbe^^.savePen);
				SetClip(hbe^^.saveClip);
				Exit(BEDraw);
				END;

			WITH hbe^^ DO
				BEGIN
				HLock (offHandle);
				IF isColor THEN
					HLock(Handle(offPix));

				IF isColor THEN
					offPix^^.baseAddr := offHandle^
				ELSE
					offBits.baseAddr := offHandle^;

				{ make a pixmap out of bitEdit record }
				IF isColor THEN
					BEGIN
					BEPixMap(pm, hbe);
					HLock(Handle(cGrafPtr(beGrafPtr)^.portPixMap))		{ crc: Why this? }
					END
				ELSE
					BEBitmap(bm, hbe);

				{ align the offscreen bitmaps }
				IF NOT isColor THEN
					AlignBits(hbe);
				END;

			DoSaveBits(hbe);

			{ enlarge bits into offscreen }
			IF hbe^^.isColor THEN
				CopyBits (BitMapPtr(pm^)^, BitMapPtr(hbe^^.offPix^)^, r, bigR, srcCopy, Nil)
			ELSE
				CopyBits (bm, hbe^^.offBits, r, bigR, srcCopy, Nil);

			{ draw the grid lines }
				PenNormal;
				PenMode(patBic);

			IF hbe^^.isColor THEN
				SetPortPix(hbe^^.offPix)
			ELSE
				SetPortBits(hbe^^.offBits);

			{ draw horizontals }
			i:= bigR.top;
			WHILE i <= bigR.bottom DO
				BEGIN
				MoveTo(bigR.left, i);
				LineTo(bigR.right, i);
				i := i + hbe^^.fatbitsSize.v + hbe^^.fatbitsDelta.v;
				END;

			{ draw verticals }
			i:= bigR.left;
			WHILE i <= bigR.right DO
				BEGIN
				MoveTo(i, bigR.top);
				LineTo(i, bigR.bottom);
				i := i + hbe^^.fatbitsSize.h + hbe^^.fatbitsDelta.h;
				END;

			{ Dim out the pixels }
			IF ((TAPat(hbe^^.fatbitsPat).lPat0 <> myWhite.lPat0) |
				(TAPat(hbe^^.fatbitsPat).lPat1 <> myWhite.lPat1)) THEN
				{ if pattern is white then don't paintrect }
					BEGIN
					PenPat(hbe^^.fatbitsPat);
					PenMode(patBic);
					PaintRect(bigR);
					END;

			DoRestoreBits(hbe);

			{ copy FatBits to screen }
			IF hbe^^.isColor THEN
				BEGIN
				SetGDevice (saveDevice);
				CopyBits(BitMapPtr(hbe^^.offPix^)^, hbe^^.beGrafPtr^.portBits,
							bigR, bigR, srcCopy, Nil);
				END
			ELSE
				CopyBits(hbe^^.offBits, hbe^^.beGrafPtr^.portBits, bigR, bigR, srcCopy, Nil);

			IF hbe^^.isColor THEN
				BEGIN
				HUnlock(Handle(hbe^^.offPix));
				HUnlock(Handle(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap))
				END;
			HUnlock(hbe^^.offHandle);
			END
		ELSE
			{ no grid lines, just blit to screen }
			BEGIN
			IF hbe^^.isColor THEN
				BEGIN
				BEPixMap(pm, hbe);
				CopyBits(BitMapPtr(pm^)^, hbe^^.beGrafPtr^.portBits, r, bigR, srcCopy, Nil);
				END
			ELSE
				BEGIN
				BEBitmap(bm, hbe);
				CopyBits(bm, hbe^^.beGrafPtr^.portBits, r, bigR, srcCopy, Nil);
				END;

			{ Dim out the pixels }
			IF ((TAPat(hbe^^.fatbitsPat).lPat0 <> myWhite.lPat0) |
				(TAPat(hbe^^.fatbitsPat).lPat1 <> myWhite.lPat1)) THEN
				{ if pattern is white then don't paintrect }
					BEGIN
					PenPat(hbe^^.fatbitsPat);
					PenMode(patBic);
					PaintRect(bigR);
					END;
			END;

		SetPenState(hbe^^.savePen);
		SetClip(hbe^^.saveClip);
		HUnlock(hbe^^.editHandle);
		HUnlock(Handle(hbe));
		END;
END; { BEDraw }

FUNCTION AllocatePixMap (pmTable: CTabHandle; hbe: BEHandle): PixMapHandle;
{ Returns Nil if a PixMap cannot be allocated.  If pmTable is not Nil,
  it is used as the color table. }

VAR
	hpm: PixMapHandle;

BEGIN
	AllocatePixMap := Nil;

	hpm := NewPixMap;
	IF MemError = NoErr THEN
		BEGIN
		IF pmTable <> Nil THEN
			BEGIN
			DisposHandle (Handle(hpm^^.pmTable));
			hpm^^.pmTable := pmTable
			END;
		AllocatePixMap := hpm;
		END;
END; { AllocatePixMap }


PROCEDURE NewSelPixMaps (hbe: BEHandle);
{ This routine should be called once to allocate and partially initialize PixMaps
  for repeated use in selections.  This routine requires its argument handle to
  be locked! }

VAR
	hpm: PixMapHandle;
	myWhite, myBlack: RGBColor;
	pmTable: CTabHandle;

BEGIN
	WITH hbe^^.selection DO
		BEGIN
		selPix := AllocatePixMap(hbe^^.beColorTable, hbe);
		IF selPix = Nil THEN
			EXIT (NewSelPixMaps);

		selPix^^.pixelSize := hbe^^.bePixelSize;
		selPix^^.cmpSize := hbe^^.bePixelSize;

		maskPix := AllocatePixMap (Nil, hbe);
		IF maskPix = Nil THEN
			EXIT (NewSelPixMaps);

		maskPix^^.pixelSize := 1;
		maskPix^^.cmpSize := 1;

		WITH myBlack DO
			BEGIN
			red := 0; blue := 0; green := 0;
			END;

		WITH myWhite DO
			BEGIN
			red := $FFFF; blue := $FFFF; green := $FFFF;
			END;

		{ Is this handle zero length? }
		WITH maskPix^^.pmTable^^ DO
			BEGIN
			ctSeed := 1;
			ctFlags := $8000;
			ctSize := 1;
			ctTable[0].value := 0;
			ctTable[1].value := 0;
			ctTable[0].rgb := myWhite;
			ctTable[1].rgb := myBlack;
			END;

		pmTable := maskPix^^.pmTable;

		edgePix := AllocatePixMap (pmTable, hbe);
		IF edgePix = Nil THEN
			EXIT (NewSelPixMaps);

		edgePix^^.pixelSize := 1;
		edgePix^^.cmpSize := 1;
		END;

	WITH hbe^^.undoSelection DO
		BEGIN
		selPix := AllocatePixMap(hbe^^.beColorTable, hbe);
		IF selPix = Nil THEN
			EXIT (NewSelPixMaps);

		maskPix := AllocatePixMap (pmTable, hbe);
		IF maskPix = Nil THEN
			EXIT (NewSelPixMaps);

		edgePix := AllocatePixMap (pmTable, hbe);
		IF edgePix = Nil THEN
			EXIT (NewSelPixMaps);
		END;
END; { NewSelPixMaps }


PROCEDURE DisposeSelPixMaps (hbe: BEHandle);
BEGIN
	MyDisposPixMap (hbe^^.undoSelection.edgePix);
	MyDisposPixMap (hbe^^.undoSelection.maskPix);
	MyDisposPixMap (hbe^^.undoSelection.selPix);

	MyDisposPixMap (hbe^^.selection.edgePix);
	IF  hbe^^.selection.maskPix <> Nil THEN
		DisposPixMap   (hbe^^.selection.maskPix);
	MyDisposPixMap (hbe^^.selection.selPix);
END;


PROCEDURE KillSelection (VAR selection: BESelectRec);
{ NOTE: if the argument is referenced by a BEHandle, make sure the handle is locked }

BEGIN
	WITH selection DO
		BEGIN
		IF edgeHandle <> Nil THEN
			BEGIN
			DisposHandle(edgeHandle);
			edgeHandle := Nil;
			END;

		IF maskHandle <> Nil THEN
			BEGIN
			DisposHandle(maskHandle);
			maskHandle := Nil;
			END;

		IF selHandle <> Nil THEN
			BEGIN
			DisposHandle(selHandle);
			selHandle := Nil;
			END;

		SetRect(selRect, 0, 0, 0, 0);
		END;
END; { KillSelection }


PROCEDURE KillUndoSelection (hbe: BEHandle);

BEGIN
	HLock (Handle (hbe));
	KillSelection (hbe^^.undoSelection);
	HUnlock (Handle (hbe));
END; { KillUndoSelection }


PROCEDURE SetupUndoSelection (hbe: BEHandle);
{ Save a copy of the current selection for undo.  The current selection is not effected }

LABEL
	1, 2, 3;
VAR
	err:		OSErr;
	maskHandle, edgeHandle, selHandle: Handle;

BEGIN
	IF hbe^^.undoFreeze = 0 THEN
		BEGIN
		err := noErr;

		maskHandle := hbe^^.selection.maskHandle;
		IF maskHandle <> Nil THEN
			err := HandToHand (Handle(maskHandle));
		IF err <> noErr THEN
			GOTO 1;

		edgeHandle := hbe^^.selection.edgeHandle;
		IF edgeHandle <> Nil THEN
			err := HandToHand (Handle(edgeHandle));
		IF err <> noErr THEN
			GOTO 2;

		selHandle := hbe^^.selection.selHandle;
		IF selHandle <> Nil THEN
			err := HandToHand (Handle(selHandle));
		IF err <> noErr THEN
			GOTO 3;

		KillUndoSelection (hbe);

		hbe^^.undoSelection.maskHandle := maskHandle;
		hbe^^.undoSelection.edgeHandle := edgeHandle;
		hbe^^.undoSelection.selHandle  := selHandle;

		{ Update the bitMaps or pixMaps (note that all color tables are shared) }
		IF hbe^^.isColor THEN
			BEGIN
			hbe^^.undoSelection.maskPix^^ := hbe^^.selection.maskPix^^;
			hbe^^.undoSelection.edgePix^^ := hbe^^.selection.edgePix^^;
			hbe^^.undoSelection.selPix^^  := hbe^^.selection.selPix^^;
			END
		ELSE
			BEGIN
			hbe^^.undoSelection.maskBits := hbe^^.selection.maskBits;
			hbe^^.undoSelection.edgeBits := hbe^^.selection.edgeBits;
			hbe^^.undoSelection.selBits  := hbe^^.selection.selBits;
			END;

		hbe^^.undoSelection.selRect   := hbe^^.selection.selRect;
		hbe^^.undoSelection.wasErased := hbe^^.selection.wasErased;

		{ Normal exit }
		GOTO 1;

		{ Error exits }
		3:	DisposHandle(Handle(edgeHandle));
		2:	DisposHandle(Handle(maskHandle));

		{ Normal and error exit }
		1: hbe^^.error := err;
		END;

END; { SetupUndoSelection }


PROCEDURE BEKillSelection(hbe: BEHandle);
{ kills the selection rect without putting down the bits }

VAR
	r: Rect;
	n: Handle;

BEGIN
	IF BESelection(hbe) THEN
		BEGIN
		r := hbe^^.selection.selRect;
		StretchRect(r);

	{ Copy current selection for undo, then kill it }
		SetupUndo (hbe);
		HLock(Handle(hbe));
		KillSelection(hbe^^.selection);
		HUnlock(Handle(hbe));

	{ copy from dupBits to BEBits }
		DoQuickDupToPort(hbe);

	{ hide selection in all views }
		BEInvalRect(r, hbe);
		END;

END; { BEKillSelection }


PROCEDURE BEPutSelection(hbe: BEHandle);
{ puts the marquee selection bits or lasso bits down and then kills the selection }

BEGIN
	IF BESelection(hbe) THEN
		BEGIN
		BEUndoBegin (hbe);
		LockHands(hbe);

	{ plunk down bits }
		IF hbe^^.marquee THEN
			CSelToDup(hbe)
		ELSE
			BEGIN
			MaskToDup(hbe);
			OSelToDup(hbe);
			END;

		UnlockHands(hbe);

		BEKillSelection(hbe);
		BEUndoEnd (hbe);
		END;

END; { BEPutSelection }


PROCEDURE BEClick(pt: Point; hbe: BEHandle);
{ Toggle the bit at the given screen point.  If selection up hide it. }

VAR
	blacken: BOOLEAN;
	savePt: Point;

BEGIN
{ make sure there is no selection }
	IF BESelection(hbe) THEN
		BEPutSelection(hbe)
	ELSE
		SetupUndo(hbe);

	blacken := NOT BEBit(pt, hbe);

	LONGINT(savePt) := LONGINT(pt);

	PaintBits(blacken, False, savePt, pt, hbe);

	WHILE WaitMouseUp DO
		BEGIN
		GetMouse(pt);
		IF LONGINT(savePt) <> LONGINT(pt) THEN
			PaintBits(blacken, False, savePt, pt, hbe);

		LONGINT(savePt) := LONGINT(pt);
		END;

END; { BEClick }


PROCEDURE BEErase(pt: Point; hbe: BEHandle);
{ Erase the bit at the given screen point.  If selection up hide it. }

VAR
	savePort: GrafPtr;
	savePt: Point;
	hUse: BEHandle;

BEGIN
{ make sure there is no selection }
	IF BESelection(hbe) THEN
		BEPutSelection(hbe)
	ELSE
		SetupUndo(hbe);

	LONGINT(savePt) := LONGINT(pt);

	PaintBits(False, True, savePt, pt, hbe);

	WHILE WaitMouseUp DO
		BEGIN
		GetMouse(pt);
		IF LONGINT(savePt) <> LONGINT(pt) THEN
			PaintBits(False, True, savePt, pt, hbe);

		LONGINT(savePt) := LONGINT(pt);
		END;

END; { BEErase }


PROCEDURE BECopy(hbe: BEHandle);
{ Copy selection to scrap as bitmap picture }

VAR
    pict: PicHandle;
    x: LONGINT;
	 r, srcRect: Rect;

BEGIN
	IF NOT EmptyRect(hbe^^.selection.selRect) THEN
		BEGIN
		SetupRects(r, srcRect, hbe);

		BEDrawBegin(hbe);
		LockHands(hbe);

		pict := OpenPicture(r);
		CopySelToPort(hbe);
		ClosePicture;

		UnlockHands(hbe);
		BEDrawEnd(hbe);

		x := ZeroScrap;
		HLock(Handle(pict));
		x := PutScrap(GetHandleSize(Handle(pict)), 'PICT', POINTER(pict^));
		HUnlock(Handle(pict));
		KillPicture(pict);
		END;

END; { BECopy }


PROCEDURE BECut(hbe: BEHandle);
{ Cut the selection }

BEGIN
	BECopy(hbe);
	BEClear(hbe);
END; { BECut }


PROCEDURE BEDispose(hbe: BEHandle);
{ Dispose the bit edit record and the offscreen bitmaps }

BEGIN
	BEPutSelection(hbe);
	KillUndoSelection (hbe);

	IF hbe^^.offHandle <> Nil THEN
		BEGIN
		DisposHandle(hbe^^.offHandle);
		IF hbe^^.isColor THEN
			DisposPixMap(hbe^^.offPix);
		END;

	IF hbe^^.isColor THEN
		BEGIN
		DisposGDevice(hbe^^.beGDevice);
		MyDisposPixMap(hbe^^.dupPix);
		MyDisposPixMap(hbe^^.editPix);
		MyDisposPixMap(hbe^^.undoPix);
		DisposeSelPixMaps (hbe);
		END;

	DisposHandle(hbe^^.dupHandle);
	DisposHandle(hbe^^.undoHandle);
	DisposeRgn(hbe^^.saveClip);
	DisposeRgn(hbe^^.saveVis);
	DisposHandle(Handle(hbe));
END; { BEDispose }


PROCEDURE BEDrawBegin(hbe: BEHandle);
{ Prepares the grafPort for drawing into the bits offscreen }

VAR
    savePort: GrafPtr;
	 theDevice: GDHandle;
	 myPixel:RGBColor;

BEGIN
	GetPort(savePort);

	WITH hbe^^, beGrafPtr^ DO BEGIN
		SetPort(beGrafPtr);
		DoSaveBits(hbe);

		HLock(editHandle);
		IF hbe^^.isColor THEN
			BEGIN
			BEPixMap(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap, hbe);
			HLock(Handle(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap));
			END
		ELSE
			BEBitMap(portBits, hbe);

		GetPenState(savePen);

		GetClip(saveClip);
		ClipRect(beEditRect);

		CopyRgn(visRgn, saveVis);
		IF hbe^^.isColor THEN
			RectRgn(visRgn, cGrafPtr(hbe^^.beGrafPtr)^.portPixMap^^.bounds)
		ELSE
			RectRgn(visRgn, hbe^^.beGrafPtr^.portBits.bounds);
	END;

	SetPort(savePort);

	IF hbe^^.isColor THEN
		BEGIN
		theDevice := GetGDevice;
		SetGDevice(hbe^^.beGDevice);
		hbe^^.beGDevice := theDevice;
		GetCPixel(0,0, myPixel);
		SetCPixel(0,0, myPixel);
		END;

END; { BEDrawBegin }


PROCEDURE BEDrawEnd(hbe: BEHandle);
{ Releases the grafport for drawing into the bits }

VAR
    savePort: GrafPtr;
	 theDevice: GDHandle;
	 myPixel: RGBColor;

BEGIN
	IF hbe^^.isColor THEN
		BEGIN
		theDevice := GetGDevice;
		SetGDevice(hbe^^.beGDevice);
		hbe^^.beGDevice := theDevice;
		END;

	WITH hbe^^, beGrafPtr^ DO
		BEGIN
		GetPort(savePort);
		SetPort(beGrafPtr);

		IF hbe^^.isColor THEN
			HUnlock(Handle(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap));
		DoRestoreBits(hbe);

		HUnlock(editHandle);

		SetPenState(savePen);
		SetClip(saveClip);

		CopyRgn(saveVis, visRgn);

		SetPort(savePort);

		IF hbe^^.isColor THEN
			BEGIN
			GetCPixel(0,0, myPixel);
			SetCPixel(0,0, myPixel);
			END;
		END;

END; { BEDrawEnd }


PROCEDURE BEIdle(hbe: BEHandle);
{ Dance the selection rectangle or lasso if active and selection is there }

BEGIN
	IF hbe^^.active THEN
		WITH hbe^^.selection DO
			BEGIN
			IF (maskHandle <> Nil) THEN
				HiliteLasso(hbe)
			ELSE
				IF (NOT EmptyRect(selRect)) THEN
					BEObject(selRect.topLeft, @MarqueeDraw, BOR (fUseSelection, fIdle), hbe);
			END;
END; { BEIdle }


PROCEDURE BEObject(startPt: Point; drawProc: ProcPtr; flags: BEDrawFlags; hbe: BEHandle);

VAR
	endPt, testPt: Point;
	r, saveRect, theRect: Rect;
	t, firstFlag: Boolean;
	idle:	Boolean;
	mIndex: INTEGER;
	myBlack: RGBColor;

BEGIN
{ invalidate our pattern in case it has been expanded using another gDevice }
	IF hbe^^.isColor THEN
		hbe^^.bePixPat^^.patXValid := -1;

	idle := BAND (flags, fIdle) <> 0;

{ make sure there is no selection }
	IF NOT idle THEN
		BEGIN
		IF BESelection(hbe) THEN
			BEPutSelection(hbe)
		ELSE
			SetupUndo(hbe);
		END;

	WITH myBlack DO
		BEGIN
		red := 0; blue := 0; green := 0;
		END;

	LockHands(hbe);

	IF idle THEN
		saveRect := hbe^^.selection.selRect
	ELSE
		SetRect(saveRect,0,0,0,0);

	firstFlag := True;
	BEPtXForm(startPt, hbe);
	testPt := startPt;

{ make a copy of bits }
	IF NOT idle THEN
		DoQuickPortToDup(hbe);

	PenNormal;
	WHILE idle | WaitMouseUp DO
		BEGIN
			GetMouse(endPt);
			BEPtXForm(endPt, hbe);

			IF idle THEN
				BEGIN
				startPt := hbe^^.selection.selRect.topLeft;
				endPt := hbe^^.selection.selRect.botRight;
				END;

		GetMIndex(mIndex);

		IF (LongInt(endPt) <> LongInt(testPt)) | firstFlag | (NOT EmptyRect(hbe^^.selection.selRect)
			& (mIndex <> hbe^^.oldIndex)) THEN
		BEGIN
			firstFlag := False;
			testPt := endPt;
			r.topLeft := startPt;
			r.botRight := endPt;

		{ refresh BEBits from dupBits or selBits }

			BEDrawBegin(hbe);
			IF idle THEN
				CSelToPort(hbe, r, False)
			ELSE
				DupToPort(hbe);

		{ draw shape }
			IF hbe^^.isColor THEN
				RGBForeColor(hbe^^.bePixColor);

			PenSize(hbe^^.bePenSize.h, hbe^^.bePenSize.v);
			CallDrawProc(r, flags, hbe, drawProc);

			IF hbe^^.isColor THEN
				RGBForeColor(myBlack);

			BEDrawEnd(hbe);

		{ figure out what the smallest rectangle to copy is }
			Pt2Rect(startPt, endPt, r);

		{ expand rect to enclose all pixels }
			r.bottom := r.bottom + 1 + hbe^^.bePenSize.v;
			r.right := r.right + 1 + hbe^^.bePenSize.h;

		{ find out what the big rect is }
			UnionRect(r, saveRect, theRect);

			saveRect := r;

		{ Draw all the views }
			BEInvalRect(theRect, hbe);

		   END; { of draw }

		IF idle THEN Leave;

	END; { stilldown loop }

	UnlockHands(hbe);

{ invalidate our pattern in case client uses it on another gDevice }
	IF hbe^^.isColor THEN
		hbe^^.bePixPat^^.patXValid := -1;
END; { BEObject }


PROCEDURE BELink(hbe1, hbe2: BEHandle);
{ Link in hbe1 with hbe2 in the circular list }

VAR
    hbe: BEHandle;

BEGIN
    hbe := hbe2^^.sibling;
    hbe2^^.sibling := hbe1^^.sibling;
    hbe1^^.sibling := hbe;
END; { BELink }

{ cc }
{ ImageCt2DeviceCt creates a device color table from an image color table.
  The device color table always contains 2**pixelDepth entries, and entry i
  contains the rgb color for pixel value i.  In other words, for a device
  color table, pixel value = table index.  For image color tables, this equivalence
  is not necessarily true.
}
FUNCTION ImageCt2DeviceCt (hctImage: CTabHandle; pixelDepth: INTEGER): CTabHandle;

VAR
	hctDevice:	CTabHandle;
	i:			INTEGER;

BEGIN
	hctDevice := CTabHandle (NewHandle (8+BSL(1,pixelDepth)*8));
	HLock (Handle (hctDevice));
	WITH hctDevice^^ DO
		BEGIN
		ctSeed := 0;
		ctFlags := $8000;
		ctSize := BSL(1,pixelDepth)-1;
		FOR i := ctSize DOWNTO 0 DO
			WITH ctTable[i] DO
				BEGIN
				value := 0;
				rgb.red := 0;
				rgb.green := 0;
				rgb.blue := 0;
				END;
		FOR i := hctImage^^.ctSize DOWNTO 0 DO
			WITH ctTable[hctImage^^.ctTable[i].value].rgb DO
				BEGIN
				red := hctImage^^.ctTable[i].rgb.red;
				green := hctImage^^.ctTable[i].rgb.green;
				blue := hctImage^^.ctTable[i].rgb.blue;
				END;
		END;
	HUnlock (Handle (hctDevice));
	ImageCt2DeviceCt := hctDevice;
END; { ImageCt2DeviceCt }


FUNCTION BENew(editHandle: Handle; colorTable: CTabHandle; offset: LongInt; rowBytes: integer; pixelSize: integer; bounds: Rect): BEHandle;
{ Allocate a record and offscreen pixmaps/bitmaps }
{ If there was not enough memory to allocate pixmaps/bitmaps then return Nil in BEHandle }

VAR
	hbe: BEHandle;
	p, u : Handle;
	err: OSErr;
	theDevice: GDHandle;
	ctabTemp: CTabHandle;

BEGIN
	hbe := BEHandle(NewHandle(SIZEOF(BERec)));
	err := MemError;
	IF err <> NoErr THEN
		BEGIN
		BENew := Nil;
		Exit(BeNew);
		END;

	HLock(Handle(hbe));

{ get storage for little offscreen bitmap }
	p := NewHandle(GetHandleSize(editHandle) - offset);
	err := MemError;
	If err <> NoErr THEN		{ if not enough memory set handle to Nil and leave }
		BEGIN
		DisposHandle(Handle(hbe));
		BENew := Nil;
		Exit(BeNew);
		END;

{ get storage for undo offscreen bitmap }
	u := NewHandle(GetHandleSize(editHandle) - offset);
	err := MemError;
	If err <> NoErr THEN		{ if not enough memory set handle to Nil and leave }
		BEGIN
		DisposHandle(Handle(hbe));
		BENew := Nil;
		Exit(BeNew);
		END;

{ can't do the NewPixMap calls within the WITH statement }
{ scramble bug, But its Locked so maybe we can?!?##!??}
	hbe^^.editHandle := editHandle;
	WITH hbe^^ DO BEGIN
		editOffset := offset;
		beRowBytes := rowBytes;

		bePixelSize := pixelSize;
		beColorTable := colorTable;

		GetPort(beGrafPtr);
		IF BAND(beGrafPtr^.portBits.rowBytes, $0000C000) = $0000C000 THEN
			isColor := True
		ELSE
			isColor := False;

		beBounds := bounds;
		offHandle := Nil;
		offPix := Nil;

		beEraserSize.v := 1;
		beEraserSize.h := 1;

		bePenSize.v := 1;
		bePenSize.h := 1;

{ setup offscreen pixMaps for objects/undo }
		IF hbe^^.isColor THEN
			BEGIN
			dupPix := NewPixMap;
			error := MemError;
			IF error <> NoErr THEN
				BEGIN
				DisposHandle(Handle(hbe));
				BENew := Nil;
				Exit(BeNew);
				END;
			DisposHandle(Handle(dupPix^^.pmTable));
			dupPix^^.rowBytes := beRowBytes;
			dupPix^^.bounds := beBounds;
			dupPix^^.pixelSize := bePixelSize;
			dupPix^^.cmpSize := bePixelSize;
			dupPix^^.pmTable := beColorTable;
			END
		ELSE
			BEGIN
			dupBits.rowBytes := beRowBytes;
			dupBits.bounds := beBounds;
			END;
		dupHandle := p;

		IF hbe^^.isColor THEN
			BEGIN
			undoPix := NewPixMap;
			error := MemError;
			IF error <> NoErr THEN
				BEGIN
				DisposHandle(Handle(hbe));
				BENew := Nil;
				Exit(BeNew);
				END;
			DisposHandle(Handle(undoPix^^.pmTable));
			undoPix^^.rowBytes := beRowBytes;
			undoPix^^.bounds := beBounds;
			undoPix^^.pixelSize := bePixelSize;
			undoPix^^.cmpSize := bePixelSize;
			undoPix^^.pmTable := beColorTable;
			END
		ELSE
			BEGIN
			undoBits.rowBytes := beRowBytes;
			undoBits.bounds := beBounds;
			END;
		undoHandle := u;

		IF hbe^^.isColor THEN
			BEGIN
			editPix := NewPixMap;
			error := MemError;
			IF error <> NoErr THEN
				BEGIN
				DisposHandle(Handle(hbe));
				BENew := Nil;
				Exit(BeNew);
				END;
			DisposHandle(Handle(editPix^^.pmTable));
			editPix^^.rowBytes := beRowBytes;
			editPix^^.bounds := beBounds;
			editPix^^.pixelSize := bePixelSize;
			editPix^^.cmpSize := bePixelSize;
			editPix^^.pmTable := beColorTable;
			END;

		WITH selection DO
			BEGIN
			selHandle := Nil;
			selPix := Nil;
			maskHandle := Nil;
			maskPix := Nil;
			edgeHandle := Nil;
			edgePix := Nil;
			SetRect(selRect, 0, 0, 0, 0);
			END;
		undoSelection := selection;
		IF hbe^^.isColor THEN
			BEGIN
			NewSelPixMaps (hbe);
			IF MemError <> noErr THEN
				BEGIN
				DisposeSelPixMaps (hbe);
				DisposHandle(Handle(hbe));
				BENew := Nil;
				Exit(BeNew);
				END;
			END;
		undoFreeze := 0;
		oldIndex := -1;

		error := noErr;
		active := False;
		drawEnabled := True;

		saveClip := NewRgn;
		saveVis := NewRgn;
		sibling := hbe;

		IF hbe^^.isColor THEN
			BEGIN
			beGDevice := NewGDevice(0,-1);
			error := MemError;
			IF error <> NoErr THEN
				BEGIN
				DisposHandle(Handle(hbe));
				BENew := Nil;
				Exit(BeNew);
				END;
			DisposHandle(Handle(beGDevice^^.gdPMap^^.pmTable));
			{ Since beGDevice is not locked, must not dereference it on lhs if rhs may move memory }
			ctabTemp := ImageCt2DeviceCt (beColorTable, bePixelSize);
			beGDevice^^.gdPMap^^.pmTable := ctabTemp;
			SetDeviceAttribute(beGDevice, GDDevType, True);
			WITH beGDevice^^ DO
				BEGIN
				gdID := 0;
				gdType := 0;
				gdSearchProc := Nil;
				gdCompProc := Nil;
				gdPMap^^.baseAddr := Nil;
				gdPMap^^.rowBytes := beRowBytes;
				gdPMap^^.bounds := beBounds;
				gdPMap^^.pmVersion := 0;
				gdPMap^^.packType := 0;
				gdPMap^^.packSize := 0;
				gdPMap^^.hRes := 72;
				gdPMap^^.vRes := 72;
				gdPMap^^.pixelType := 0;
				gdPMap^^.pixelSize := bePixelSize;
				gdPMap^^.cmpCount := 1;
				gdPMap^^.cmpSize := bePixelSize;
				gdPMap^^.planeBytes := 0;
				gdPMap^^.pmReserved := 0;
				gdNextGD := Nil;
				gdRect := beBounds;
				gdMode := 0;
				gdReserved := 0;
				END;

			theDevice := GetGDevice;
			SetGDevice(beGDevice);
			MakeITable(Nil, Nil, 0);
			SetGDevice(theDevice);
			END;
	END;
	HUnlock(Handle(hbe));

	{ Initialize the undo buffer with the current bits in case the user does an undo first. }
	SetupUndo (hbe);	{ 7/7/88 PEA }
	BENew := hbe;
END; { BENew }


PROCEDURE CenterScrap(VAR r:Rect; hbe: BEHandle);
{ Center scrap rectangle in current window if it will fit; if not, align it with the topLeft }

VAR
	width,height: INTEGER;

BEGIN
	WITH hbe^^ DO
		BEGIN
		width := r.right - r.left;
		height := r.bottom - r.top;

		WITH r DO
			BEGIN
			left := (beEditRect.left + beEditRect.right - width) DIV 2;
			top  := (beEditRect.top + beEditRect.bottom - height) DIV 2;
			IF (left < beEditRect.left) THEN
				left := beEditRect.left;
			IF (top < beEditRect.top) THEN
				top := beEditRect.top;
			right := left + width;
			bottom := top + height;
			END;
		END;
END; { CenterScrap }


PROCEDURE BEPaste(hbe: BEHandle);
{ paste the picture from the scrap into the selection.  If no selection make one }

VAR
    err, o: LongInt;
	 pHndl: PicHandle;

BEGIN
	IF NOT EmptyRect(hbe^^.selection.selRect) AND (NOT hbe^^.marquee) THEN
		BEPutSelection(hbe)
	ELSE
		SetupUndo(hbe);

	pHndl := PicHandle(NewHandle(0));
	err := GetScrap(Handle(pHndl), 'PICT', o);

	IF err >= 0 THEN
		BEGIN
		IF EmptyRect(hbe^^.selection.selRect) THEN
			BEGIN
			hbe^^.selection.selRect := pHndl^^.picFrame;
			CenterScrap(hbe^^.selection.selRect, hbe);
			hbe^^.selection.selRect.right := hbe^^.selection.selRect.right - 1;
			hbe^^.selection.selRect.bottom := hbe^^.selection.selRect.bottom - 1;
			hbe^^.marquee := True;
			SetUpSelBits(hbe^^.selection.selRect, hbe);
			IF hbe^^.error <> NoErr THEN
				BEGIN
				SetRect(hbe^^.selection.selRect, 0, 0, 0, 0);
				DisposHandle(Handle(pHndl));
				Exit(BEPaste);
				END;
			END;

		IF NOT EmptyRect(hbe^^.selection.selRect) THEN
			BEGIN
			hbe^^.selection.wasErased := True;

			LockHands(hbe);
			DoQuickPortToDup(hbe);

			BEDrawBegin(hbe);
			IF hbe^^.isColor THEN
				BEGIN
				SetPortPix(hbe^^.selection.selPix);
				ClipRect(hbe^^.selection.selPix^^.bounds);
				RectRgn(hbe^^.beGrafPtr^.visRgn, hbe^^.selection.selPix^^.bounds);
				EraseRect(hbe^^.selection.selPix^^.bounds);
				DrawPicture(pHndl, hbe^^.selection.selPix^^.bounds);
				END
			ELSE
				BEGIN
				SetPortBits(hbe^^.selection.selBits);
				ClipRect(hbe^^.selection.selBits.bounds);
				RectRgn(hbe^^.beGrafPtr^.visRgn, hbe^^.selection.selBits.bounds);
				EraseRect(hbe^^.selection.selBits.bounds);
				DrawPicture(pHndl, hbe^^.selection.selBits.bounds);
				END;
			BEDrawEnd(hbe);

			{ Put the bits down so that other views derived from the bits can be updated.
				PEA 4/1/88 }
			BEDrawBegin (hbe);
			CopySelToPort (hbe);
			BEDrawEnd (hbe);

			hbe^^.marquee := True;

			{ And paint them - PEA 4/1/88 }
			BEInvalRect (hbe^^.selection.selRect, hbe);

			UnlockHands(hbe);

			END
		ELSE
			SysBeep(20);
		END;
	DisposHandle(Handle(pHndl));

END; { BEPaste }


PROCEDURE BEPtUnXForm(VAR pt: Point; hbe: BEHandle);
{ Transforms the given fatbit point into screen point }

BEGIN
    WITH hbe^^ DO BEGIN
        pt.h := beViewRect.left + (pt.h - beEditRect.left) * (fatbitsDelta.h + fatbitsSize.h);
        pt.v := beViewRect.top + (pt.v - beEditRect.top) * (fatbitsDelta.v + fatbitsSize.v);
    END;
END; { BEPtUnXForm }


PROCEDURE BEPtXForm(VAR pt: Point; hbe: BEHandle);
{ Transforms the given screen point into fatbits point }

BEGIN
    WITH hbe^^ DO BEGIN
        pt.h := beEditRect.left + (pt.h - beViewRect.left) DIV (fatbitsDelta.h + fatbitsSize.h);
        pt.v := beEditRect.top + (pt.v - beViewRect.top) DIV (fatbitsDelta.v + fatbitsSize.v);
    END;
END; { BEPtXForm }


PROCEDURE LineDraw(r: Rect; flags: BEDrawFlags; hbe: BEHandle);
{ draw a line between the points specified in topleft and bottomright of rect,
  constrained to horizontal, vertical, or diagonal lines according to flags }

VAR
	hv:			Point;
	lineFlags:	BEDrawFlags;
BEGIN
	WITH r DO BEGIN
		WidthHeightRect (r, hv);
		lineFlags := BAND(flags, fShapeLineMask);
		IF lineFlags = fLineOnAxis THEN
			BEGIN
			IF hv.v > hv.h THEN
				right := left
			ELSE
				bottom := top;
			END
		ELSE IF lineFlags = fLineOnDiagonal THEN
			BEGIN
			IF hv.h > hv.v*2 THEN
				bottom := top
			ELSE IF hv.v > hv.h*2 THEN
				right := left
			ELSE
				SquareRect (r);
			END;
		MoveTo(left, top);
		LineTo(right, bottom);
	END;
END; { LineDraw }


PROCEDURE BELine(startPt: Point; flags: BEDrawFlags; hbe: BEHandle);
{ Drag out a line starting at given point }

BEGIN
    BEObject(startPt, @LineDraw, flags, hbe);
END; { BELine }


PROCEDURE OvalDraw( r: Rect; flags: BEDrawFlags; hbe: BEHandle );
{ Draw an oval in r; flags specify if framed, filled, or both, and if circle }

BEGIN
	IF BAND (flags, fShapeUnbounded) = 0 THEN
		ClipAnchorRect (r, hbe^^.beEditRect);
	IF BAND (flags, fShapeSymmetric)  <> 0 THEN
		SquareRect (r);
	FixRect(r);
	StretchRect(r);
	IF BAND (flags, fShapeFill) <> 0 THEN
		IF hbe^^.isColor THEN
			FillCOval(r, hbe^^.bePixPat)
		ELSE
			FillOval(r, hbe^^.beFillPat);
	IF BAND (flags, fShapeUnframed) = 0 THEN
		BEGIN
		PenSize(hbe^^.bePenSize.h, hbe^^.bePenSize.v);
		FrameOval(r);
		END;
END; { OvalDraw }


PROCEDURE BEOval(startPt: Point; flags: BEDrawFlags; hbe: BEHandle);
{ Drag out an oval starting at given point }

BEGIN
    BEObject(startPt, @OvalDraw, flags, hbe);
END; { BEOval }


PROCEDURE RndRectDraw( r: Rect; flags: BEDrawFlags; hbe: BEHandle );
{ Draw a round-cornered rect }

BEGIN
	IF BAND (flags, fShapeUnbounded) = 0 THEN
		ClipAnchorRect (r, hbe^^.beEditRect);
	IF BAND (flags, fShapeSymmetric) <> 0 THEN
		SquareRect (r);
	FixRect(r);
	StretchRect(r);
	IF BAND (flags, fShapeFill) <> 0 THEN
		IF hbe^^.isColor THEN
			FillCRoundRect(r, Point(hbe^^.beDrawParm).v, Point(hbe^^.beDrawParm).h, hbe^^.bePixPat)
		ELSE
			FillRoundRect(r, Point(hbe^^.beDrawParm).v, Point(hbe^^.beDrawParm).h, hbe^^.beFillPat);
	IF BAND (flags, fShapeUnframed) = 0 THEN
		BEGIN
		PenSize(hbe^^.bePenSize.h, hbe^^.bePenSize.v);
		FrameRoundRect(r, Point(hbe^^.beDrawParm).v, Point(hbe^^.beDrawParm).h);
		END;
END; { RndRectDraw }


PROCEDURE BERoundRect(startPt: Point; flags: BEDrawFlags; ovalWidth, ovalHeight: INTEGER; hbe: BEHandle);
{ Drag out a rect starting at given point }

BEGIN
	 Point(hbe^^.beDrawParm).v := ovalWidth;
	 Point(hbe^^.beDrawParm).h := ovalHeight;
    BEObject(startPt, @RndRectDraw, flags, hbe);
END; { BERoundRect }


PROCEDURE RectDraw(r: Rect; flags: BEDrawFlags; hbe: BEHandle);
{ Draw a rect }

BEGIN
	IF BAND (flags, fShapeUnbounded) = 0 THEN
		ClipAnchorRect (r, hbe^^.beEditRect);
	IF BAND (flags, fShapeSymmetric) <> 0 THEN
		SquareRect (r);
	FixRect(r);
	StretchRect(r);
	IF BAND (flags, fShapeFill) <> 0 THEN
		IF hbe^^.isColor THEN
			FillCRect(r, hbe^^.bePixPat)
		ELSE
			FillRect(r, hbe^^.beFillPat);
	IF BAND (flags, fShapeUnframed) = 0 THEN
		BEGIN
		PenSize(hbe^^.bePenSize.h, hbe^^.bePenSize.v);
		FrameRect(r);
		END;
END; { RectDraw }


PROCEDURE BERect(startPt: Point; flags: BEDrawFlags; hbe: BEHandle);
{ Drag out a rect starting at given point }

BEGIN
    BEObject(startPt, @RectDraw, flags, hbe);
END; { BERect }


PROCEDURE ClipAnchorRect (VAR r: Rect; VAR rClip: Rect);
{ Constrains the bottom right corner of r to lie within a rectangle
  defined by rClip shrunk on the bottom and right by one pixel.
	rClip is not changed.
}

BEGIN
	IF r.right >= rClip.right THEN
		r.right := rClip.right-1
	ELSE IF r.right < rClip.left THEN
		r.right := rClip.left;
	IF r.bottom >= rClip.bottom THEN
		r.bottom := rClip.bottom-1
	ELSE IF r.bottom < rClip.top THEN
		r.bottom := rClip.top;
END; { ClipAnchorRect }


PROCEDURE WidthHeightRect (VAR r: Rect; VAR pt: Point);

BEGIN
	pt.h := ABS (r.right - r.left);
	pt.v := ABS (r.bottom - r.top);
END; { WidthHeightRect }


PROCEDURE SquareRect (VAR r: Rect);
{ Convert a rectangle to the smallest contained square }

VAR
	height,width,hs,ws: INTEGER;
BEGIN
	IF r.right < r.left THEN
		BEGIN
		ws := -1;
		width := r.left - r.right;
		END
	ELSE
		BEGIN
		ws := 1;
		width := r.right - r.left;
		END;
	IF r.bottom < r.top THEN
		BEGIN
		hs := -1;
		height := r.top - r.bottom;
		END
	ELSE
		BEGIN
		hs := 1;
		height := r.bottom - r.top;
		END;

	IF width > height THEN
		r.right := r.left + (height*ws)
	ELSE
		r.bottom := r.top + (width*hs);
END; { SquareRect }


PROCEDURE MarqueeDraw(r: Rect; flags: BEDrawFlags; hbe: BEHandle);
{ If the fSetSelection bit of flags is false, uses the current selection rectangle rather than setting it;
  this allows the curent selection to remain partially outside the edit rectangle }

VAR
	mIndex: INTEGER;
	savePat: Pattern;
	tempRect: Rect;
	t: Boolean;
	myBlack: RGBColor;

BEGIN
	WITH myBlack DO
		BEGIN
		red := 0; blue := 0; green := 0;
		END;

	IF BAND (flags, fSquareMarquee)  <> 0 THEN
		SquareRect (r);
	FixRect(r);

	WITH hbe^^ DO
		BEGIN
		IF BAND (flags, fSetSelection) <> 0 THEN
			BEGIN
			t := SectRect(r, beEditRect, r);
			IF r.right = beEditRect.right THEN r.right := r.right - 1;
			IF r.bottom = beEditRect.bottom THEN r.bottom := r.bottom - 1;
			selection.selRect := r;
			END
		ELSE
			r := selection.selRect;
		StretchRect(r);

		GetMIndex(mIndex);

		IF NOT EmptyRect(selection.selRect) AND active THEN
			BEGIN
			PenSize(1,1);
			GetSelPat(mIndex, savePat);
			PenPat(savePat);
			IF isColor THEN
				RGBForeColor(myBlack);
			FrameRect(r);
			oldIndex := mIndex;
			END;
		END;
END; { MarqueeDraw }


PROCEDURE SetUpSelBits(r: Rect; hbe: BEHandle);

VAR
	p: Handle;
	pix: PixMapHandle;
	rb: INTEGER;
	b: BOOLEAN;

BEGIN
	HLock(Handle(hbe));
	WITH hbe^^.selection DO
		BEGIN
		IF hbe^^.marquee THEN
			BEGIN
			StretchRect(r);
			END;

		IF hbe^^.isColor THEN
		{ setup a temporary offscreen pixMap the size of the selection }
			WITH selPix^^ DO
				BEGIN
				rowBytes := ((((hbe^^.bePixelSize * (r.right - r.left)) + 15)) DIV 16) * 2;
				bounds := r;
				rb := rowBytes;
				rowBytes := rowBytes + $8000;
				END
		ELSE
			WITH selBits DO
				BEGIN
				rowBytes := (((r.right - r.left) + 15) DIV 16) * 2;
				bounds := r;
				rb := rowBytes;
				END;

		p := NewHandle(UIntMul((r.bottom - r.top), rb));
		hbe^^.error := MemError;
		If hbe^^.error <> NoErr THEN
			BEGIN
			HUnlock(Handle(hbe));
			Exit(SetUpSelBits);
			END;
		selHandle := p;
		END;

	HUnlock(Handle(hbe));
	LockHands(hbe);

{ copy from the dupBits to the selBits bitmap }
	IF hbe^^.marquee THEN
		DupToSel(hbe);

	UnlockHands(hbe);

END; { SetUpSelBits }


PROCEDURE EraseUnderSelection (hbe: BEHandle);
{ erase area under the selection prior to moving the selected bits }

VAR
	r, srcRect: Rect;

BEGIN
	IF NOT hbe^^.selection.wasErased THEN
		BEGIN
		LockHands(hbe);
		WITH hbe^^ DO
			BEGIN
			SetupRects(r, srcRect, hbe);

			BEDrawBegin(hbe);
			MySetDupBits(hbe);
			IF marquee THEN
				EraseRect(r)
			ELSE
				MaskToDup(hbe);
			BEDrawEnd(hbe);
			selection.wasErased := True;
			END;
		UnlockHands(hbe);
		END;
END; { EraseUnderSelection }


PROCEDURE DragSelection(startPt: Point; flags: BESelectFlags; hbe: BEHandle);

VAR
	testPt, deltaPt, endPt: Point;
	oldSelRect, r, cRect, srcRect: Rect;
	pt: Ptr;
	rLimit: Rect;
	vhSel: Point;

BEGIN
	SetupUndo(hbe);

	BEPtXForm(startPt, hbe);
	testPt := startPt;

	IF BAND(flags,fDragCopy) = 0 THEN
		EraseUnderSelection (hbe)
	ELSE IF hbe^^.selection.wasErased THEN
		BEGIN
	{ plunk down bits }
		IF hbe^^.marquee THEN
			CSelToDup(hbe)
		ELSE
			BEGIN
			MaskToDup(hbe);
			OSelToDup(hbe);
			END;
		END
	ELSE
		hbe^^.selection.wasErased := TRUE;

	SetupRects(r, srcRect, hbe);

	LockHands(hbe);

{ figure out bounding rect within which the drag point must remain }
	WITH hbe^^, cRect DO
		BEGIN
		IF marquee THEN
			BEGIN
			{ Allow selection to move beyond edit rectangle, as long as one edge is visible }
			vhSel.h := selection.selRect.right - selection.selRect.left;
			vhSel.v := selection.selRect.bottom - selection.selRect.top;
			rLimit.top := beEditRect.top - vhSel.v;
			rLimit.left := beEditRect.left - vhSel.h;
			rLimit.bottom := beEditRect.bottom + vhSel.v;
			rLimit.right := beEditRect.right + vhSel.h;
			END
		ELSE
			{ For lasso selections, keep entire selection within edit rectangle }
			rLimit := beEditRect;

		left := rLimit.left + (startPt.h - selection.selRect.left);
		top := rLimit.top + (startPt.v - selection.selRect.top);
		right := rLimit.right - (selection.selRect.right - startPt.h);
		bottom := rLimit.bottom - (selection.selRect.bottom - startPt.v);
		IF NOT marquee THEN
			bottom := bottom + 1;
		END;

{ draw image without ants on mousedown }
	BEDrawBegin(hbe);
	DupToPort(hbe);
	CopySelToPort(hbe);
	BEDrawEnd(hbe);

{ invalidate and redraw the rects }
	BEInvalRect(r, hbe);

	WHILE WaitMouseUp DO
		BEGIN
			GetMouse(endPt);
			BEPtXForm(endPt, hbe);

			endPt := Point(PinRect(cRect, endPt));

		IF (LongInt(endPt) <> LongInt(testPt)) THEN
			BEGIN { mouse moved }
			deltaPt := endPt;

		{ figure out distance moved }
			SubPt(testPt, deltaPt);

			oldSelRect := r;
			OffSetRect(r, deltaPt.h, deltaPt.v);

		{ get into BEBits and move the image }
			BEDrawBegin(hbe);

			UnionRect(oldSelRect, r, oldSelRect);

		{ copy from dupBits to BEBits }
			WITH hbe^^ DO
			IF isColor THEN
				{ cc }
				CopyPixels (dupPix, CGrafPtr(beGrafPtr)^.portPixMap)
				{ CopyBits(BitMapPtr(dupPix^)^, beGrafPtr^.portBits, oldSelRect, oldSelRect, srcCopy, Nil) }
			ELSE
				CopyBits(dupBits, beGrafPtr^.portBits, oldSelRect, oldSelRect, srcCopy, Nil);

			IF hbe^^.marquee THEN
				CSelToPort(hbe, r, True)
			ELSE
				BEGIN
				MaskToPort(hbe, r, True);
				OSelToPort(hbe, r, True);
				END;

			BEDrawEnd(hbe);

		{ invalidate and redraw the rects }
			BEInvalRect(oldSelRect, hbe);

			testPt := endPt;

			END; { mouse moved }

		END; { waitmouseup loop }

	r.bottom := r.bottom - 1;
	r.right := r.right - 1;
	hbe^^.selection.selRect := r;

	UnlockHands(hbe);

END; { DragSelection }


PROCEDURE BESelect(startPt: Point; flags: BESelectFlags; hbe: BEHandle);
{ Drag out a selection marquee or lasso starting at given point.
  The selection is moved if startPt is inside the current selection,
  even if that selection type is different than that specified in flags. }

VAR
	kind:	BESelectFlags;
BEGIN
	IF BEInSelection(startPt, hbe) THEN
		DragSelection(startPt, flags, hbe)
	ELSE
		BEGIN
		kind := BAND (flags, fSelectKindMask);
		IF kind = fSelectLasso THEN
			DrawLasso(startPt, hbe)
		ELSE
			BEGIN
			flags := BAND(flags, fSquareMarquee);
			BEObject(startPt, @MarqueeDraw, BOR(flags, fSetSelection), hbe);
			hbe^^.marquee := True;
			IF NOT EmptyRect(hbe^^.selection.selRect) THEN
				BEGIN
				SetUpSelBits(hbe^^.selection.selRect, hbe);
				IF hbe^^.error <> NoErr THEN
					SetRect(hbe^^.selection.selRect, 0, 0, 0, 0);
				END;
			hbe^^.selection.wasErased := False;
			END
		END;
END; { BESelect }


PROCEDURE ConstrainNudge (VAR deltaPt: Point; hbe: BEHandle);

VAR
	cRect: Rect;
	newPt: Point;

BEGIN
	WITH hbe^^, cRect DO
		BEGIN
		IF marquee THEN
			BEGIN
			{ Allow selection to move beyond edit rectangle, as long as one edge is visible }
			top := beEditRect.top - (selection.selRect.bottom - selection.selRect.top);
			left := beEditRect.left - (selection.selRect.right - selection.selRect.left);
			botRight := beEditRect.botRight;
			END
		ELSE
			BEGIN
			{ For lasso selections, keep entire selection within edit rectangle }
			topLeft := beEditRect.topLeft;
			right := beEditRect.right - (selection.selRect.right - selection.selRect.left);
			bottom := beEditRect.bottom - (selection.selRect.bottom - selection.selRect.top) + 1;
			END;

	{ constrain deltaPt }
		newPt := selection.selRect.topLeft;
		AddPt (deltaPt, newPt);
		newPt := Point(PinRect(cRect, newPt));
		deltaPt := newPt;
		SubPt (selection.selRect.topLeft, deltaPt);
		END;
END; { ConstrainNudge }


PROCEDURE BEDuplicate (deltaPt: Point; hbe: BEHandle);
{ Duplicate the selection.  DeltaPt is the preferred displacement and the minimal
  margin on the bottom and right.  It should be positive.
 }

VAR
	rInval:		Rect;
	origPt:		Point;

BEGIN
	IF BESelection(hbe) THEN
		BEGIN
		SetupUndo (hbe);

		LockHands(hbe);

	{ plunk down bits }
		IF hbe^^.marquee THEN
			CSelToDup(hbe)
		ELSE
			BEGIN
			MaskToDup(hbe);
			OSelToDup(hbe);
			END;

	{ draw image without ants }
		BEDrawBegin(hbe);
		DupToPort(hbe);

		rInval := hbe^^.selection.selRect;

	{ Move Selection }
	{ If too close to bottom, move to the top; if too close to right edge, move to left.
	  The term "deltaPt.v <> origPt.v" is for lasso selections (which are constrained to the
	  edit area; the second term is for marquee selections (which may overhang).
	}
		origPt := deltaPt;
		ConstrainNudge (deltaPt, hbe);
		WITH hbe^^, hbe^^.selection DO
			BEGIN
			IF (deltaPt.v <> origPt.v) OR ((selRect.top + deltaPt.v) > (beEditRect.bottom - deltaPt.v)) THEN
				deltaPt.v := (beEditRect.top + deltaPt.v) - selRect.top;
			IF (deltaPt.h <> origPt.h) OR ((selRect.left + deltaPt.h) > (beEditRect.right - deltaPt.h)) THEN
				deltaPt.h := (beEditRect.left + deltaPt.h) - selRect.left;
			OffsetRect (selRect, deltaPt.h, deltaPt.v);
			END;

		CopySelToPort (hbe);
		BEDrawEnd(hbe);

		hbe^^.selection.wasErased := True;

		UnlockHands(hbe);

		UnionRect (rInval, hbe^^.selection.selRect, rInval);
		StretchRect (rInval);
		BEInvalRect(rInval, hbe);
		END;
END; { BEDuplicate }


FUNCTION  BENudge(deltaPt: Point; hbe: BEHandle): BOOLEAN;

VAR
	oldSelRect, r, srcRect: Rect;

BEGIN
	BENudge := FALSE;
	IF BESelection(hbe) THEN
		BEGIN
		SetupUndo (hbe);

		EraseUnderSelection (hbe);

	{ draw image without ants }
		LockHands(hbe);
		BEDrawBegin(hbe);
		DupToPort(hbe);
		BEDrawEnd(hbe);
		UnlockHands(hbe);

		ConstrainNudge (deltaPt, hbe);

		IF LongInt(DeltaPt) <> 0 THEN
			BEGIN
			LockHands(hbe);

			SetupRects(r, srcRect, hbe);

		{ invalidate and redraw the rects }
			BEInvalRect(r, hbe);

			oldSelRect := r;
			OffSetRect(r, deltaPt.h, deltaPt.v);

		{ get into BEBits and move the image }
			BEDrawBegin(hbe);

			UnionRect(oldSelRect, r, oldSelRect);

		{ copy from dupBits to BEBits }
			WITH hbe^^ DO
			IF isColor THEN
				{ cc }
				CopyPixels (dupPix, CGrafPtr(beGrafPtr)^.portPixMap)
				{ CopyBits(BitMapPtr(dupPix^)^, beGrafPtr^.portBits, oldSelRect, oldSelRect, srcCopy, Nil) }
			ELSE
				CopyBits(dupBits, beGrafPtr^.portBits, oldSelRect, oldSelRect, srcCopy, Nil);

			IF hbe^^.marquee THEN
				CSelToPort(hbe, r, True)
			ELSE
				BEGIN
				MaskToPort(hbe, r, True);
				OSelToPort(hbe, r, True);
				END;

			BEDrawEnd(hbe);

		{ invalidate and redraw the rects }
			BEInvalRect(oldSelRect, hbe);

			r.bottom := r.bottom - 1;
			r.right := r.right - 1;
			hbe^^.selection.selRect := r;

			UnlockHands(hbe);
			BENudge := TRUE;
			END
		END;

END; { BENudge }


PROCEDURE BEFlip(vertical: BOOLEAN; hbe: BEHandle);
{ flip the selection; if there is no selection or a lasso selection, flip the entire image }
{ Note that a lasso selection is canceled before fliping the entire image }

VAR
	rInval, srcRect:	Rect;
	r:						Rect;
	rgbPt:				RGBColor;
	cpyHandle:			Handle;			{ copy of selection bits }
	cpyPix:				PixMapHandle;
	cpyBits:				BitMap;
	cpyRect, selRect: Rect;
	i, limit:			INTEGER;

BEGIN
	IF BESelection(hbe) AND hbe^^.marquee THEN
		BEGIN
		SetupUndo(hbe);

		SetupRects(rInval, srcRect, hbe);

		LockHands(hbe);

		WITH hbe^^ DO
			BEGIN
			{ Copy the selection }
			cpyHandle := selection.selHandle;
			error := HandToHand(cpyHandle);
			IF error <> NoErr THEN Exit(BEFlip);
			HLock (cpyHandle);

			IF isColor THEN
				BEGIN
				cpyPix := selection.selPix;
				error := HandToHand(Handle (cpyPix));
				IF error <> NoErr THEN
					BEGIN
					HUnlock (cpyHandle);
					DisposHandle (cpyHandle);
					Exit(BEFlip);
					END;
				HLock (Handle (cpyPix));
				cpyPix^^.baseAddr := cpyHandle^;
				r := selection.selPix^^.bounds;
				END
			ELSE
				BEGIN
				cpyBits := selection.selBits;
				cpyBits.baseAddr := cpyHandle^;
				r := selection.selBits.bounds;
				END;

			BEDrawBegin(hbe);

			IF vertical THEN
				limit := r.bottom - r.top
			ELSE
				limit := r.right - r.left;

			FOR i := 0 TO limit DO
				BEGIN
				IF vertical THEN
					BEGIN
					SetRect (cpyRect, r.left, r.top+i, r.right, r.top+i+1);
					SetRect (selRect, r.left, r.bottom-i-1, r.right, r.bottom-i);
					END
				ELSE
					BEGIN
					SetRect (cpyRect, r.left+i, r.top, r.left+i+1, r.bottom);
					SetRect (selRect, r.right-i-1, r.top, r.right-i, r.bottom);
					END;
				IF isColor THEN
					CopyBits(BitMapPtr(cpyPix^)^, BitMapPtr(selection.selPix^)^, cpyRect, selRect, srcCopy, Nil)
				ELSE
					CopyBits(cpyBits, selection.selBits, cpyRect, selRect, srcCopy, Nil);
				END;

			HUnlock (cpyHandle);
			DisposHandle (cpyHandle);
			IF isColor THEN
				BEGIN
				HUnlock (Handle (cpyPix));
				DisposHandle (Handle (cpyPix));
				END;

			DupToPort(hbe);
			CopySelToPort(hbe);

			BEDrawEnd(hbe);
			UnlockHands(hbe);
			END;

	{ invalidate and redraw the rects }
		BEInvalRect(rInval, hbe);

		END;
END; { BEFlip }


PROCEDURE SetupExtract (bm: BitMapPtr; pt: Point; VAR offset, mask, shift: INTEGER);
{	Returns offset, mask and shift.  Offset is the displacement from baseAddr to the byte containing
	the argument point.  Mask can be used to extract the pixel, and shift used to move the pixel
	value into the least significant bits of the byte.  This routine uses integer math to compute
	bit offsets, so the width of the BitMap should be less than 4K for 8-bit pixels.
}

VAR
	pixelSize:	INTEGER;
	rowBytes:	INTEGER;
	row:			INTEGER;
	rowOffset:	INTEGER;
	pixOffset:	INTEGER;
	bitOffset:	INTEGER;
	byteOffset:	INTEGER;
	pm:			PixMapPtr;

BEGIN
IF BAND (bm^.rowBytes, $8000) <> 0 THEN
	BEGIN
	pm := PixMapPtr (bm);
	pixelSize := pm^.pixelSize;
	END
ELSE
	pixelSize := 1;
rowBytes := BAND (bm^.rowBytes, $7FFF);
row := pt.v - bm^.bounds.top;
rowOffset := UIntMul (row, rowBytes);
pixOffset := pt.h - bm^.bounds.left;
bitOffset := UIntMul(pixOffset,pixelSize);
byteOffset := bitOffset DIV 8;
bitOffset := bitOffset MOD 8;
offset := rowOffset + byteOffset;
shift := (8 - pixelSize - bitOffset);
mask := BitShift(1, pixelSize) - 1;
mask := BitShift(mask, shift);
END;

FUNCTION GetPix (bm: BitMapPtr; pt: Point): INTEGER;

VAR
	byte: INTEGER;
	offset, mask, shift: INTEGER;
	p:	Ptr;

BEGIN
IF PtInRect (pt, bm^.bounds) THEN
	BEGIN
	SetupExtract (bm, pt, offset, mask, shift);
	p := POINTER(ORD(bm^.baseAddr) + offset);
	byte := p^;
	GetPix := BitShift (BAND (byte, mask), -shift);
	END
ELSE
	GetPix := 0;
END; { GetPix }


PROCEDURE SetPix (bm: BitMapPtr; pt: Point; VAR pixel: INTEGER);

VAR
	byte: INTEGER;
	offset, mask, shift: INTEGER;
	p:	Ptr;

BEGIN
IF PtInRect (pt, bm^.bounds) THEN
	BEGIN
	SetupExtract (bm, pt, offset, mask, shift);
	p := POINTER(ORD(bm^.baseAddr) + offset);
	byte := BAND (p^, BXOR ($FFFF, mask));
	pixel := BAND (BitShift (pixel,shift), mask);
	byte := BOR (byte, pixel);
	p^ := byte;
	END
END; { SetPix }


PROCEDURE BERotate(hbe: BEHandle);
{ Requires a selection in order to perform a rotate, since rotate may move some bits off the
  edit area--without a selection these bits are lost forever.  For now, this selection must be
  a rectangular marquee.
 }

VAR
	rInval, srcRect:	Rect;
	rectBounds:			Rect;
	rotHandle:			Handle;
	rotPix:				PixMapHandle;
	rotBits:				BitMap;
	hSel, vSel:			INTEGER;
	ptSel, ptRot:		Point;
	widthSel:			INTEGER;
	width, height:		INTEGER;
	pixel:				INTEGER;
	vhAdjust:			Point;

BEGIN
	IF NOT EmptyRect(hbe^^.selection.selRect) AND (hbe^^.marquee) THEN
		BEGIN
		SetupUndo(hbe);

		EraseUnderSelection (hbe);

		SetupRects(rInval, srcRect, hbe);
		LockHands(hbe);

		WITH hbe^^ DO
			BEGIN
			{ Copy the selection }
			IF isColor THEN
				BEGIN
				{ Make sure topLeft of selection is at (0,0) }
				OffsetRect (selection.selPix^^.bounds, -selection.selPix^^.bounds.left, -selection.selPix^^.bounds.top);

				rotPix := selection.selPix;
				error := HandToHand(Handle (rotPix));
				IF error <> NoErr THEN
					Exit(BERotate);
				HLock (Handle (rotPix));

				{ Bounds of selPix are (0,0,hbe,v); bounds of rotated image are (0,0,v,hbe) }
				WITH rotPix^^ DO
					BEGIN
					bounds.bottom := selection.selPix^^.bounds.right;
					bounds.right := selection.selPix^^.bounds.bottom;
					rowBytes := ((((pixelSize * (bounds.right - bounds.left)) + 15)) DIV 16) * 2;
					rotHandle := NewHandle (UIntMul((bounds.bottom - bounds.top), rowBytes));
					hbe^^.error := MemError;
					IF hbe^^.error <> NoErr THEN
						BEGIN
						HUnlock (Handle (rotPix));
						DisposHandle (Handle (rotPix));
						Exit(BERotate);
						END;
					rowBytes := rowBytes + $8000;
					HLock (rotHandle);
					baseAddr := rotHandle^;
					END;
				rectBounds := selection.selPix^^.bounds;
				END
			ELSE
				BEGIN
				{ Make sure topLeft of selection is at (0,0) }
				OffsetRect (selection.selBits.bounds, -selection.selBits.bounds.left, -selection.selBits.bounds.top);
				rotBits := selection.selBits;
				rotBits.bounds.bottom := selection.selBits.bounds.right;
				rotBits.bounds.right := selection.selBits.bounds.bottom;
				rotBits.rowBytes := (((rotBits.bounds.right - rotBits.bounds.left) + 15) DIV 16) * 2;
				rotHandle := NewHandle (UIntMul((rotBits.bounds.bottom - rotBits.bounds.top), rotBits.rowBytes));
				hbe^^.error := MemError;
				IF hbe^^.error <> NoErr THEN
					Exit(BERotate);
				HLock (rotHandle);
				rotBits.baseAddr := rotHandle^;
				rectBounds := selection.selBits.bounds;
				END;

			{ Map pixels from selection to rotated image }
			widthSel := rectBounds.right - rectBounds.left;
			FOR vSel := rectBounds.top TO rectBounds.bottom-1 DO
				FOR hSel := rectBounds.left TO rectBounds.right-1 DO
					BEGIN
					SetPt (ptSel, hSel, vSel);
					SetPt (ptRot, vSel, -hSel-1+widthSel);
					IF hbe^^.isColor THEN
						BEGIN
						pixel := GetPix (BitMapPtr (selection.selPix^), ptSel);
						SetPix (BitMapPtr (rotPix^), ptRot, pixel);
						END
					ELSE
						BEGIN
						pixel := GetPix (@selection.selBits, ptSel);
						SetPix (@rotBits, ptRot, pixel);
						END;
					END;

			{ Replace selection with rotated image }
			DisposHandle(selection.selHandle);
			selection.selHandle := rotHandle;
			IF isColor THEN
				BEGIN
				MyDisposPixMap(selection.selPix);
				selection.selPix := rotPix;
				END
			ELSE
				selection.selBits := rotBits;

			{ Adjust selRect to keep center of selection stationary }
			WITH selection.selRect DO
				BEGIN
				{ Note that former selection width is now bounds.bottom of rotPix or rotBit }
				width := right - left;
				height := bottom - top;
				top := top + (height - width) DIV 2;
				left := left + (width - height) DIV 2;
				bottom := top + width;
				right := left + height;

				{ But don't allow selection to move completely beyond the edit rectangle }
				IF bottom <= beEditRect.top THEN
					vhAdjust.v := (beEditRect.top+1) - bottom
				ELSE IF top >= beEditRect.bottom THEN
					vhAdjust.v := (beEditRect.bottom-1) - top
				ELSE
					vhAdjust.v := 0;
				IF right <= beEditRect.left THEN
					vhAdjust.h := (beEditRect.left+1) - right
				ELSE IF left >= beEditRect.right THEN
					vhAdjust.h := (beEditRect.right-1) - left
				ELSE
					vhAdjust.h := 0;
				OffsetRect (selection.selRect,  vhAdjust.h, vhAdjust.v);
				END;

			{ Must invalidate area under selection before and after rotation }
			UnionRect (rInval, selection.selRect, rInval);

			{ Copy rotated selection to image }
			BEDrawBegin(hbe);
			DupToPort(hbe);
			CopySelToPort(hbe);
			BEDrawEnd(hbe);

			UnlockHands(hbe);

			END;

	{ invalidate and redraw the rects }
		BEInvalRect(rInval, hbe);
	END;
END; { BERotate }


PROCEDURE BESetSelect(r: Rect; hbe: BEHandle);
{ sets the selection rectangle to the given rect, removing existing one if there }

VAR
	b:		BOOLEAN;

BEGIN
	IF BESelection(hbe) THEN
		BEKillSelection(hbe)
	ELSE
		SetupUndo(hbe);

	LockHands(hbe);
	DoQuickPortToDup(hbe);
	UnlockHands(hbe);

	WITH hbe^^ DO
		BEGIN
		BEPtXForm(r.topLeft, hbe);
		BEPtXForm(r.botRight, hbe);
		b := SectRect(r, beEditRect, r);
		IF r.right = beEditRect.right THEN r.right := r.right - 1;
		IF r.bottom = beEditRect.bottom THEN r.bottom := r.bottom - 1;
		selection.selRect := r;
		selection.wasErased := False;
		marquee := True;
		END;

	IF NOT EmptyRect(hbe^^.selection.selRect) THEN
		BEGIN
		SetUpSelBits(hbe^^.selection.selRect, hbe);
		IF hbe^^.error <> NoErr THEN
			SetRect(hbe^^.selection.selRect, 0, 0, 0, 0);
		END;

END; { BESetSelect }


PROCEDURE DrawLasso(startPt: Point; hbe: BEHandle);
{ routine to implement arbitrary selection }

VAR
	words: INTEGER;
	lassoRgn, tempRgn: RgnHandle;
	savePt, pt: Point;
	selBBox, r: Rect;
	s: Handle;
	tempBits, mTBits: BitMap;
	memLimit, theSize: LONGINT;
	i: INTEGER;
	tempTable, saveTable: CTabHandle;
	saveColor, myBlack: RGBColor;
	np: PixMapHandle;

BEGIN
{ make sure there is no selection }
	IF BESelection(hbe) THEN
		BEPutSelection(hbe)
	ELSE
		SetupUndo(hbe);

{ make a copy of the BEBits into dupBits buffer }
	LockHands(hbe);
	DoQuickPortToDup(hbe);
	UnlockHands(hbe);

	OpenRgn;
	ShowPen;

	savePt := startPt;

	WITH myBlack DO
		BEGIN
		red := 0; blue := 0; green := 0;
		END;

	saveColor := hbe^^.bePixColor;
	hbe^^.bePixColor := myBlack;
	PaintBits(True, False, savePt, startPt, hbe);

	memLimit := BitShift(CompactMem(maxSize), -1);
	IF memLimit > 15000 THEN memLimit := 15000;

	WHILE WaitMouseUp AND (GetRgnMax < memLimit) DO
		BEGIN
		GetMouse(pt);
		BEPtXForm(pt, hbe);
		pt := Point(PinRect(hbe^^.beEditRect, pt));
      IF pt.v = hbe^^.beEditRect.bottom THEN pt.v := pt.v - 1;
      IF pt.h = hbe^^.beEditRect.right  THEN pt.h := pt.h - 1;
		BEPtUnXform(pt, hbe);

		IF LONGINT(savePt) <> LONGINT(pt) THEN
			PaintBits(True, False, savePt, pt, hbe);

		LONGINT(savePt) := LONGINT(pt);
		END;

	PaintBits(True, False, savePt, startPt, hbe);
	hbe^^.bePixColor := saveColor;

{ got the region! }
	lassoRgn := NewRgn;
	CloseRgn(lassoRgn);

{ if it's empty, just blit bits back to screen }
	IF NOT EmptyRgn(lassoRgn) THEN
		BEGIN
	{ enclose all the pixels which we lassoed }
		tempRgn := NewRgn;
		CopyRgn(lassoRgn, tempRgn);
		OffSetRgn(tempRgn, 1, 0);
		UnionRgn(tempRgn, lassoRgn, lassoRgn);
		OffSetRgn(tempRgn, 0, 1);
		UnionRgn(tempRgn, lassoRgn, lassoRgn);
		OffSetRgn(tempRgn, -1, 0);
		UnionRgn(tempRgn, lassoRgn, lassoRgn);

	{ get the rectangle }
		selBBox := lassoRgn^^.rgnBBox;

		tempBits.bounds := selBBox;
		tempBits.rowBytes := ((((selBBox.right - selBBox.left) + 15)) DIV 16) * 2;
		tempBits.baseAddr := NewPtrClr(UIntMul((selBBox.bottom - selBBox.top),tempBits.rowBytes));
		hbe^^.error := MemError;
		IF hbe^^.error <> NoErr THEN
			Exit(DrawLasso);

		LockHands(hbe);

		WITH hbe^^ DO
			IF isColor THEN
				BEGIN
				tempTable := dupPix^^.pmTable;
				error := HandToHand(Handle(tempTable));
				IF error <> NoErr THEN Exit(DrawLasso);

				GetBWTable(tempTable);

				saveTable := dupPix^^.pmTable;
				dupPix^^.pmTable := tempTable;

			{ get a copy of the selected bits into temporary bitmap (black/white only) }
				CopyBits(BitMapPtr(dupPix^)^, tempBits, selBBox, selBBox, srcCopy, lassoRgn);

				dupPix^^.pmTable := saveTable;
				END
			ELSE
				CopyBits(dupBits, tempBits, selBBox, selBBox, srcCopy, lassoRgn);

		UnlockHands(hbe);

	{ trim the rectangle }
	{ must give TrimBBox a 0,0 upper coord }
		OffSetRect(selBBox, -tempBits.bounds.left, -tempBits.bounds.top);
		TrimBBox(tempBits.baseAddr, selBBox, tempBits.rowBytes);
		selBBox.bottom := selBBox.bottom + 1;

	{ now offset rectangle back to where it was }
		OffSetRect(selBBox, tempBits.bounds.left, tempBits.bounds.top);

	{ don't need tempBits ptr anymore }
		DisposPtr(tempBits.baseAddr);

		IF NOT EmptyRect(selBBox) THEN
			BEGIN

		{ flag that this is a lasso selection }
			hbe^^.marquee := False;

			WITH hbe^^.selection DO
				BEGIN
			{ it was not erased yet }
				wasErased := False;

			{ setup selRect the way that all routines expect it }
				selRect := selBBox;
				selRect.right := selRect.right - 1;
				selRect.bottom := selRect.bottom - 1;
				END;

		{ expand the bitmap into which we place the bits }
			r := selBBox;
			InsetRect(r, -1, -1);

		{ setup the selection bitmap }
			SetUpSelBits(r, hbe);
			IF hbe^^.error <> NoErr THEN
				BEGIN
				SetRect(hbe^^.selection.selRect, 0, 0, 0, 0);
				Exit(DrawLasso);
				END;

			LockHands(hbe);

		{ clear the bitmap because we use a maskRgn in copyBits }
			ClearHandle(hbe^^.selection.selHandle);

		{ put only selected bits into selBits bitmap }
			IF hbe^^.isColor THEN
				CopyBits(BitMapPtr(hbe^^.dupPix^)^, BitMapPtr(hbe^^.selection.selPix^)^, selBBox, selBBox,
							srcCopy, lassoRgn)
			ELSE
				CopyBits(hbe^^.dupBits, hbe^^.selection.selBits, selBBox, selBBox, srcCopy, lassoRgn);

			UnlockHands(hbe);

			IF hbe^^.isColor THEN
				WITH hbe^^, selection.maskPix^^ DO
					BEGIN
					rowBytes := ((((selection.selPix^^.bounds.right - selection.selPix^^.bounds.left) + 15))
									DIV 16) * 2;
					bounds := selection.selPix^^.bounds;
					theSize := UIntMul((bounds.bottom - bounds.top), rowBytes);
					rowBytes := rowBytes + $8000;
					words := ((bounds.right - bounds.left) + 15) DIV 16;
					tempBits.rowBytes := rowBytes - $8000;
					tempBits.bounds := bounds;
					mTBits.rowBytes := rowBytes - $8000;
					mTBits.bounds := bounds;
					END
			ELSE
				WITH hbe^^, selection.maskBits DO
					BEGIN
					bounds := selection.selBits.bounds;
					rowBytes := selection.selBits.rowBytes;
					theSize := GetHandleSize(selection.selHandle);
					words := ((bounds.right - bounds.left) + 15) DIV 16;
					tempBits.rowBytes := rowBytes;
					tempBits.bounds := bounds;
					mTBits.rowBytes := rowBytes;
					mTBits.bounds := bounds;
					END;

		{ make a bitmap for the mask }
			s := NewHandle(theSize);
			hbe^^.error := MemError;
			IF hbe^^.error <> NoErr THEN
				Exit(DrawLasso);

			hbe^^.selection.maskHandle := s;

			LockHands(hbe);

			tempBits.baseAddr := NewPtrClr(GetHandleSize(hbe^^.selection.maskHandle));
			hbe^^.error := MemError;
			IF hbe^^.error <> NoErr THEN SysBeep(20);

			mTBits.baseAddr := NewPtrClr(GetHandleSize(hbe^^.selection.maskHandle));
			hbe^^.error := MemError;
			IF hbe^^.error <> NoErr THEN SysBeep(20);

			WITH hbe^^ DO
				IF isColor THEN
					BEGIN
					saveTable := selection.selPix^^.pmTable;
					selection.selPix^^.pmTable := tempTable;
					CopyBits(BitMapPtr(selection.selPix^)^, tempBits, selection.selPix^^.bounds, tempBits.bounds, srcCopy, Nil);
					selection.selPix^^.pmTable := saveTable;
					END
				ELSE
					CopyBits(selection.selBits, tempBits, selection.selBits.bounds, tempBits.bounds, srcCopy, Nil);

		{ calculate the mask bits }
			CalcMask(tempBits.baseAddr, mTBits.baseAddr,  { srcPtr, dstPtr }
						tempBits.rowBytes, mTBits.rowBytes,  { srcRow, dstRow }
						tempBits.bounds.bottom - tempBits.bounds.top, words); { height, words }

			IF hbe^^.isColor THEN
				CopyBits(mTBits, BitMapPtr(hbe^^.selection.maskPix^)^, mTBits.bounds, hbe^^.selection.maskPix^^.bounds,
							srcCopy, Nil)
			ELSE
				CopyBits(mTBits, hbe^^.selection.maskBits, mTBits.bounds, hbe^^.selection.maskBits.bounds,
							srcCopy, Nil);

			DisposPtr(tempBits.baseAddr);
			DisposPtr(mTBits.baseAddr);

		{ make sure the mask is correct }
			BEDrawBegin(hbe);
			IF hbe^^.isColor THEN
				BEGIN
				SetPortPix(hbe^^.selection.maskPix);
				RectRgn(tempRgn, hbe^^.selection.maskPix^^.bounds);
				END
			ELSE
				BEGIN
				SetPortBits(hbe^^.selection.maskBits);
				RectRgn(tempRgn, hbe^^.selection.maskBits.bounds);
				END;

			DiffRgn(tempRgn, lassoRgn, tempRgn);
			EraseRgn(tempRgn);
			BEDrawEnd(hbe);

			DisposeRgn(tempRgn);

			s := NewHandle(GetHandleSize(hbe^^.selection.maskHandle));
			hbe^^.error := MemError;
			IF hbe^^.error <> NoErr THEN
				Exit(DrawLasso);

			hbe^^.selection.edgeHandle := s;

		{ setup a pixMap for the edges }
			IF hbe^^.isColor THEN
				WITH hbe^^.selection DO
					BEGIN
					edgePix^^.bounds    := maskPix^^.bounds;
					edgePix^^.rowBytes  := maskPix^^.rowBytes;
					END
			ELSE
				WITH hbe^^.selection DO
					BEGIN
					edgeBits.bounds   := maskBits.bounds;
					edgeBits.rowBytes := maskBits.rowBytes;
					END;

			UnlockHands(hbe);
			END; { selBBox not empty }

		IF hbe^^.isColor THEN
			DisposHandle(Handle(tempTable));
		END; { lassoRgn not empty }

	DisposeRgn(lassoRgn);

{ copy original bits back to screen to get rid of lasso }
	DoQuickDupToPort(hbe);

	BEInvalRect(hbe^^.beEditRect, hbe);

END; { DrawLasso }


PROCEDURE HiliteLasso(hbe: BEHandle);

VAR
	now, mIndex: INTEGER;
	r, srcRect: Rect;
	thePat: Pattern;

BEGIN
	GetMIndex(mIndex);

	SetupRects(r, srcRect, hbe);

	IF mIndex <> hbe^^.oldIndex THEN
		BEGIN
		hbe^^.oldIndex := mIndex;

		HLock(hbe^^.selection.edgeHandle);
		IF hbe^^.isColor THEN
			hbe^^.selection.edgePix^^.baseAddr := hbe^^.selection.edgeHandle^
		ELSE
			hbe^^.selection.edgeBits.baseAddr := hbe^^.selection.edgeHandle^;

	{ start with a clean bitmap }
		ClearHandle(hbe^^.selection.edgeHandle);

		LockHands(hbe);
		WITH hbe^^.selection DO
			IF hbe^^.isColor THEN
				CalcEdges(maskPix^^.baseAddr, edgePix^^.baseAddr, 0,
							maskPix^^.bounds.bottom - maskPix^^.bounds.top,
							(maskPix^^.rowBytes - $8000), True)
			ELSE
				CalcEdges(maskBits.baseAddr, edgeBits.baseAddr, 0,
							maskBits.bounds.bottom - maskBits.bounds.top,
							maskBits.rowBytes, True);
		UnlockHands(hbe);

		BEDrawBegin(hbe);
		GetSelPat(mIndex, thePat);
		PenPat(thePat);
		PenMode(patBic);
		EdgeToPort(hbe, srcBic);
		WITH hbe^^.selection DO
			IF hbe^^.isColor THEN
				BEGIN
				SetPortPix(edgePix);
				PaintRect(edgePix^^.bounds);
				END
			ELSE
				BEGIN
				SetPortBits(edgeBits);
				PaintRect(edgeBits.bounds);
				END;
		BEDrawEnd(hbe);

		BEDrawBegin(hbe);
		EdgeToPort(hbe, srcOr);
		BEDrawEnd(hbe);

		HUnlock(hbe^^.selection.edgeHandle);

		BEInvalRect(r, hbe);

		END;

END; { HiliteLasso }


PROCEDURE SetupUndo(hbe: BEHandle);
{ copy bit image to offscreen for undo }

VAR
	pm: PixMapHandle;
	bm: BitMap;

BEGIN
	IF hbe^^.undoFreeze = 0 THEN
		BEGIN
		IF BESelection(hbe) THEN
			BEGIN
			SetupUndoSelection(hbe);
			DoQuickDupToPort(hbe);
			END
		ELSE
			KillUndoSelection(hbe);

		HLock(hbe^^.undoHandle);
		HLock(Handle(hbe^^.editHandle));

	{ setup offscreen bitmap baseAddr }
		IF hbe^^.isColor THEN
			BEGIN
			hbe^^.undoPix^^.baseAddr := hbe^^.undoHandle^;
			BEPixmap(pm, hbe);
			HLock(Handle(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap));
			END
		ELSE
			BEGIN
			hbe^^.undoBits.baseAddr := hbe^^.undoHandle^;
			BEBitmap(bm, hbe);
			END;

	{ copy from BitEdit record bits to offscreen buffer }
		BEDrawBegin(hbe);
		IF hbe^^.isColor THEN
			CopyBits(BitMapPtr(pm^)^, BitMapPtr(hbe^^.undoPix^)^, pm^^.bounds, hbe^^.undoPix^^.bounds,
						srcCopy, Nil)
		ELSE
			CopyBits(bm, hbe^^.undoBits, bm.bounds, hbe^^.undoBits.bounds, srcCopy, Nil);
		BEDrawEnd(hbe);

		IF hbe^^.isColor THEN
			HUnlock(Handle(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap));
		HUnlock(hbe^^.undoHandle);
		HUnlock(Handle(hbe^^.editHandle));
		END;

END; { SetupUndo }


PROCEDURE BEUndoBegin(hbe: BEHandle);
{ Save an undo snapshot and retain it until the next call to BEUndoEnd }

BEGIN
	SetupUndo (hbe);
	hbe^^.undoFreeze := hbe^^.undoFreeze + 1;
END; { BEUndoBegin }


PROCEDURE BEUndoEnd(hbe: BEHandle);
{ Allow the next BitEdit routine to save a new undo state }

BEGIN
	IF hbe^^.undoFreeze > 0 THEN
		hbe^^.undoFreeze := hbe^^.undoFreeze - 1;
END; { BEUndoEnd }


PROCEDURE NoColorUndo(hbe: BEHandle);

VAR
	bm, tempBits: Bitmap;

BEGIN
{ setup undo bitmap baseAddr }
	HLock(hbe^^.undoHandle);
	hbe^^.undoBits.baseAddr := hbe^^.undoHandle^;

{ make a bitmap out of bitEdit record }
	HLock(Handle(hbe^^.editHandle));
	BEBitmap(bm, hbe);

{ set up a temporary bitmap }
	tempBits.baseAddr := NewPtr(GetHandleSize(Handle(hbe^^.undoHandle)));
	hbe^^.error := MemError;

	tempBits.bounds := hbe^^.undoBits.bounds;
	tempBits.rowBytes := hbe^^.undoBits.rowBytes;

	IF hbe^^.error = NoErr THEN
	{ swap the bits }
		BEGIN
			CopyBits(bm, tempBits, bm.bounds, tempBits.bounds, srcCopy, Nil);
			CopyBits(hbe^^.undoBits, bm, hbe^^.undoBits.bounds, bm.bounds, srcCopy, Nil);
			CopyBits(tempBits, hbe^^.undoBits, tempBits.bounds, hbe^^.undoBits.bounds, srcCopy, Nil);
		END
	ELSE
	{ copy from offscreen buffer to BitEdit record bits }
		CopyBits(hbe^^.undoBits, bm, hbe^^.undoBits.bounds, bm.bounds, srcCopy, Nil);

	HUnlock(hbe^^.undoHandle);
	HUnlock(Handle(hbe^^.editHandle));

	IF tempBits.baseAddr <> Nil THEN
		DisposPtr(tempBits.baseAddr);

END;


PROCEDURE BEUndo(hbe: BEHandle);
{ routine to undo the last change }

VAR
	bm, tempBits: PixMapHandle;
	p: Ptr;
	n: Handle;
	tempSel:	BESelectRec;

BEGIN
	IF BESelection (hbe) THEN
		BEGIN
	{ draw image without ants }
		LockHands(hbe);
		BEDrawBegin(hbe);
		DupToPort(hbe);
		BEDrawEnd(hbe);
		UnlockHands(hbe);
		END;

	IF NOT hbe^^.isColor THEN
		NoColorUndo(hbe)
	ELSE
		BEGIN
	{ setup undo bitmap baseAddr }
		HLock(hbe^^.undoHandle);
		hbe^^.undoPix^^.baseAddr := hbe^^.undoHandle^;

	{ make a bitmap out of BitEdit record }
		HLock(Handle(hbe^^.editHandle));
		BEPixmap(bm, hbe);
		HLock(Handle(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap));

	{ set up a temporary bitmap }
		tempBits := NewPixMap;
		hbe^^.error := MemError;
		IF hbe^^.error <> NoErr THEN
			Exit(BEUndo);

		p := NewPtr(GetHandleSize(Handle(hbe^^.undoHandle)));
		hbe^^.error := MemError;
		IF hbe^^.error <> NoErr THEN
			BEGIN
			DisposPixMap (tempBits);
			Exit(BEUndo);
			END;
		DisposHandle(Handle(tempBits^^.pmTable));

		tempBits^^.baseAddr := p;
		tempBits^^.bounds := hbe^^.undoPix^^.bounds;
		tempBits^^.rowBytes := hbe^^.undoPix^^.rowBytes;
		tempBits^^.pixelSize := hbe^^.undoPix^^.pixelSize;
		tempBits^^.cmpSize := hbe^^.undoPix^^.cmpSize;
		tempBits^^.pmTable := hbe^^.undoPix^^.pmTable;

		IF hbe^^.error = NoErr THEN
		{ swap the bits }
			BEGIN
				CopyBits(BitMapPtr(bm^)^, BitMapPtr(tempBits^)^, bm^^.bounds, tempBits^^.bounds, srcCopy, Nil);
				CopyBits(BitMapPtr(hbe^^.undoPix^)^, BitMapPtr(bm^)^, hbe^^.undoPix^^.bounds, bm^^.bounds, srcCopy, Nil);
				CopyBits(BitMapPtr(tempBits^)^, BitMapPtr(hbe^^.undoPix^)^, tempBits^^.bounds, hbe^^.undoPix^^.bounds, srcCopy, Nil);
			END
			ELSE
		{ copy from offscreen buffer to BitEdit record bits }
				CopyBits(BitMapPtr(hbe^^.undoPix^)^, BitMapPtr(bm^)^, hbe^^.undoPix^^.bounds, bm^^.bounds, srcCopy, Nil);

		HUnlock(Handle(cGrafPtr(hbe^^.beGrafPtr)^.portPixMap));
		HUnlock(hbe^^.undoHandle);
		HUnlock(Handle(hbe^^.editHandle));

		IF tempBits^^.baseAddr <> Nil THEN
			BEGIN
			DisposPtr(tempBits^^.baseAddr);
			MyDisposPixMap(tempBits);
			END;
		END;

{ Restore selection }
	tempSel := hbe^^.undoSelection;
	hbe^^.undoSelection := hbe^^.selection;
	hbe^^.selection := tempSel;

{ Selection must be a lasso if a mask exists }
	hbe^^.marquee := (hbe^^.selection.maskHandle = Nil);

	IF BESelection (hbe) THEN
		BEGIN
		LockHands(hbe);
		BEDrawBegin(hbe);
		PortToDup(hbe);
		CopySelToPort(hbe);
		BEDrawEnd(hbe);
		UnlockHands(hbe);
		END;

	{ force all views to be redrawn }
	BEInvalRect(hbe^^.beEditRect, hbe);

END; { BEUndo }


PROCEDURE BEView(editRect: Rect; viewRect: Rect; bitsSize: Point; bitsDelta: Point; hbe: BEHandle);
{ Sets up a view of the bits }

BEGIN
	WITH hbe^^ DO BEGIN
		beEditRect := editRect;
		beViewRect := viewRect;
		fatbitsSize := bitsSize;
		fatbitsDelta := bitsDelta;
		StuffHex(@beFillPat, 'FFFFFFFFFFFFFFFF');
		StuffHex(@fatbitsPat, '0000000000000000');

		realRect := beEditRect;
		DoMyMapRect(hbe, realRect, realRect);
		END;
END; { BEView }
