mac-rom/Toolbox/BitEdit/BitEdit.i
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

4250 lines
99 KiB
OpenEdge ABL

{
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 }