; ; File: Regions.a ; ; Contains: QuickDraw routines to operate on regions. ; ; Copyright: © 1981-1992 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 1/25/93 kc Roll in Shannon Holland's alpha channel fix to DrawRgn. ; 6/11/92 stb stb Synch with QDciPatchROM.a; comments added to ; StdRgn, MapRgn ; 5/21/92 kc Change the name of QuickDraws wrapper for NewHandle to ; NewHandleWrapper to avoid name conflict with the glue. ; <10> 8/22/91 JSM Don’t use TRUE and FALSE as labels. ; <9> 7/23/91 KON Fix 32-bit clean crimes in BitMapRgn. ; <8> 10/30/90 KON Forgot to make all the changes before checking in; This updates ; to the current version on the ci in QDciPatch. [SMC] ; <7> 10/30/90 KON Make pmVersion=4 pixmaps work and create rgns as large as 64K ; (not 32K). ; <6> 9/7/90 KON Fix misleading comment and enter Comment of the Week contest. ; <5> 9/7/90 KON Don't remap wideopen rectangular regions since maprect will blow ; up. ; <4> 8/28/90 KON Remove benign redefinition of pixmapTooDeepErr (-148) which is ; now defined in SysErr.a. ; <3> 8/2/90 gbm change TEMPRECT to TEMPRECTANGLE to avoid global conflict ; <2> 7/24/90 gbm axe duplicate defs ; <•1.6> 7/14/89 BAL For Aurora: Final CQD ; <1.5> 6/30/89 BAL Now uses equate for qdStackXtra ; <•1.4> 5/29/89 BAL Blasting in 32-Bit QuickDraw version 1.0 Final ; 9/26/88 BAL Provided RectWithinRgn, a variant of TrimRect ; 9/25/88 BAL Rewrote TrimRect for a better than 5X performance improvement ; 11/8/87 DAF Fixed pict opcode recorded by StdRgn ; 6/4/87 CRC set patMode even if user forgets to ; 1/3/87 CRC pass color param to stretch ; 10/9/86 EHB Added mask parameters to stretchbits calls ; 9/5/86 EHB Added FillCRgn ; 8/16/86 EHB In FillRgn added support for color grafports ; 6/18/86 EHB Fixed up references to PortBits in DrawRgn for color ports Call ; StretchBits instead of RgnBlt ; BLANKS ON STRING ASIS ;----------------------------------------------------------- ; ; ; **** ***** *** *** *** * * *** ; * * * * * * * * * * * * ; * * * * * * * ** * * ; **** *** * ** * * * * * * *** ; * * * * * * * * * ** * ; * * * * * * * * * * * * ; * * ***** *** *** *** * * *** ; ;----------------------------------------------------------- StdRgn PROC EXPORT IMPORT PutPicVerb,DPutPicOp,PutPicRgn IMPORT PutRgn,FrRgn,PushVerb,DrawRgn,StdDevLoop ;--------------------------------------------------------------- ; ; PROCEDURE StdRgn(verb: GrafVerb; rgn: RgnHandle); ; ; A6 OFFSETS OF PARAMS AFTER LINK: ; ; has fix from QDciPatchROM.a stb PARAMSIZE EQU 6 VERB EQU PARAMSIZE+8-2 ;GRAFVERB RGN EQU VERB-4 ;LONG, RGNHANDLE LINK A6,#0 ;NO LOCALS MOVEM.L D6-D7/A2-A4,-(SP) ;SAVE REGS MOVEQ #0,D7 ;CLEAR LOWORD OF PICT OPCODE DAF MOVE.B VERB(A6),D7 ;GET VERB _CheckPic ;SET UP A4,A3 AND CHECK PICSAVE BLE.S NOTPIC ;BRANCH IF NOT PICSAVE MOVE.B D7,-(SP) ;PUSH VERB JSR PutPicVerb ;PUT ADDITONAL PARAMS TO THEPIC MOVE #$80,D0 ;PUT RGNNOUN IN HI NIBBLE ADD D7,D0 ;PUT VERB IN LO NIBBLE JSR DPutPicOp ;PUT OPCODE TO THEPIC MOVE.L RGN(A6),-(SP) ;PUSH RGNHANDLE JSR PutPicRgn ;PUT REGION TO THEPIC ; CALL STANDARD LOOP TO DRAW TO ALL DEVICES NOTPIC PEA StdDraw ;PUSH ADDRESS OF DRAW ROUTINE PEA GetRect ;PUSH ADDRESS OF RECT ROUTINE _StdDevLoop ;DRAW TO ALL DEVICES GOHOME MOVEM.L (SP)+,D6-D7/A2-A4 ;RESTORE REGS UNLINK PARAMSIZE,'STDRGN ' ;--------------------------------------------------------------- ; ; PROCEDURE GetRect(VAR theRect: rect); ; ; RETURN THE OBJECT'S RECTANGLE ; GetRect MOVE.L (SP)+,D0 ;GET RETURN ADDRESS MOVE.L (SP)+,A1 ;GET DST RECT MOVE.L RGN(A6),A0 ;GET REGION MOVE.L (A0),A0 ;POINT AT IT LEA RGNBBOX(A0),A0 ;POINT TO BOUNDING BOX MOVE.L (A0)+,(A1)+ ;SET TOPLEFT MOVE.L (A0),(A1) ;SET BOTRIGHT MOVE.L D0,A0 ;GET RETURN ADDRESS JMP (A0) ;AND RETURN ;--------------------------------------------------------------- ; ; PROCEDURE StdDraw; ; ; DRAW THE OBJECT ; StdDraw MOVE.L RGN(A6),-(SP) ;PUSH RGNHANDLE _PushVerb ;PUSH MODE AND PATTERN TST.B D7 ;IS VERB FRAME ? BNE.S NOTFR ;NO, CONTINUE TST.L RGNSAVE(A3) ;YES, IS RGNSAVE TRUE ? BEQ.S NOTRGN ;NO, CONTINUE MOVE.L RGN(A6),-(SP) ;YES, PUSH RGNHANDLE MOVE.L RGNBUF(A4),-(SP) ;PUSH RGNBUF PEA RGNINDEX(A4) ;PUSH VAR RGNINDEX PEA RGNMAX(A4) ;PUSH VAR RGNMAX _PutRgn ;ADD INVERSION PTS TO THERGN NOTRGN JSR FrRgn ;FrRgn(rgn,pnMode,pnPat) BRA.S drawDone NOTFR JSR DrawRgn ;DrawRgn(rgn,mode,pat); drawDone DONE RTS FrameRgn PROC EXPORT EXPORT CallRgn,PaintRgn,EraseRgn,InvertRgn,FillRgn,FillCRgn IMPORT SetFillPat ;----------------------------------------------------- ; ; PROCEDURE FrameRgn(* rgn: RgnHandle *); ; MOVEQ #FRAME,D0 ;VERB = FRAME BRA.S CallRgn ;SHARE COMMON CODE ;----------------------------------------------------- ; ; PROCEDURE PaintRgn(* rgn: RgnHandle *); ; PaintRgn MOVEQ #PAINT,D0 ;VERB = PAINT BRA.S CallRgn ;SHARE COMMON CODE ;-------------------------------------------------------- ; ; PROCEDURE EraseRgn(* rgn: RgnHandle *); ; EraseRgn MOVEQ #ERASE,D0 ;VERB = ERASE BRA.S CallRgn ;SHARE COMMON CODE ;-------------------------------------------------------- ; ; PROCEDURE InvertRgn(* rgn: RgnHandle *); ; InvertRgn MOVEQ #INVERT,D0 ;VERB = INVERT BRA.S CallRgn ;SHARE COMMON CODE ;-------------------------------------------------------- ; ; PROCEDURE FillRgn(* rgn: RgnHandle; pat: Pattern *); ; FillRgn MOVEQ #0,D0 ;FLAG = FillRgn BRA.S SHARE ; => USE COMMON CODE ;---------------------------------------------------------- ; ; PROCEDURE FillCRgn(rgn: RgnHandle; PPH: PixPatHandle); ; FillCRgn MOVEQ #1,D0 ;FLAG = FillCRgn SHARE MOVE.L (SP)+,A0 ;POP RETURN ADDR MOVE.L (SP)+,A1 ;POP ADDR OF PATTERN MOVE.L A0,-(SP) ;PUT RETURN ADDR BACK MOVE.L A1,-(SP) ;PUSH ADDR OF PATTERN _SETFILLPAT ;FILLPAT := PAT MOVEQ #FILL,D0 ;VERB = FILL ; BRA.S CallRgn ;SHARE COMMON CODE ;--------------------------------------------------------------- ; ; PROCEDURE CallRgn(rgn: RgnHandle); ; ; code shared by FrameRgn, PaintRgn, EraseRgn, InvertRgn, and FillRgn. ; enter with verb in D0. ; CallRgn MOVE.L (SP)+,A0 ;POP RETURN ADDR MOVE.L (SP)+,A1 ;POP RGN MOVE.B D0,-(SP) ;PUSH VERB MOVE.L A1,-(SP) ;PUSH RGN MOVE.L A0,-(SP) ;RESTORE RETURN ADDR MOVE.L GRAFGLOBALS(A5),A0 ;POINT TO LISAGRAF GLOBALS MOVE.L THEPORT(A0),A0 ;GET CURRENT GRAFPORT MOVE.L GRAFPROCS(A0),D0 ;IS GRAFPROCS NIL ? MOVE.L JStdRgn,A0 ;get piece of trap table BEQ.S USESTD ;YES, USE STD PROC MOVE.L D0,A0 MOVE.L RGNPROC(A0),A0 ;NO, GET PROC PTR USESTD JMP (A0) ;GO TO IT DrawRgn PROC EXPORT IMPORT STRETCHBITS IMPORT PortToMap ;-------------------------------------------------------- ; ; PROCEDURE DrawRgn(rgn: RgnHandle; mode: INTEGER; pat: Pattern); ; ; A6 OFFSETS OF PARAMS AFTER LINK: ; PARAMSIZE EQU 10 RGN EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE MODE EQU RGN-2 ;WORD PAT EQU MODE-4 ;LONG, ADDR OF PATTERN BBOX EQU -8 ;LOCAL COPY OF RGNBBOX VARSIZE EQU BBOX ;SIZE OF LOCALS LINK A6,#VARSIZE ;ALLOCATE STACK FRAME MOVE.L RGN(A6),A0 ;GET THE REGION HANDLE MOVE.L (A0),A0 ;POINT TO THE RGN MOVE.L RGNBBOX(A0),BBOX(A6) ;COPY RGNBBOX.TOPLEFT MOVE.L RGNBBOX+4(A0),BBOX+4(A6) ;COPY RGNBBOX.BOTRIGHT MOVE.L GRAFGLOBALS(A5),A0 ;POINT TO LISAGRAF GLOBALS MOVE.L THEPORT(A0),A0 ;GET CURRENT PORT TST PNVIS(A0) ;IS PNVIS NEG ? BMI.S DONE ;YES, QUIT MOVE.L A0,A1 ;SAVE A COPY _PORTTOMAP ;GET BIT/PIXMAP IN A0 MOVE.L A0,-(SP) ;PUSH SRCBITS CLR.L -(SP) ;NO MASKBITS MOVE.L A0,-(SP) ;PUSH DSTBITS PEA BBOX(A6) ;PUSH SRCRECT CLR.L -(SP) ;NO MASKRECT PEA BBOX(A6) ;PUSH DSTRECT MOVE MODE(A6),D1 ;PUSH MODE ; OR #$8,D1 ;set the pattern bit in case the user forgot to ; _GetStreamMode ;strip mode ; MOVE.W D1,-(SP) ;save stripped mode ; MOVE.L PAT(A6),-(SP) ;PUSH PAT MOVE.L CLIPRGN(A1),-(SP) ;PUSH CLIPRGN MOVE.L VISRGN(A1),-(SP) ;PUSH VISRGN MOVE.L RGN(A6),-(SP) ;PUSH RGN CLR -(SP) ;pass multicolor flag false _STRETCHBITS ;CALL STRETCHBITS DONE UNLINK PARAMSIZE,'DRAWRGN ' FrRgn PROC EXPORT IMPORT FrmRect,NewRgn,CopyRgn,InsetRgn,DiffRgn,DrawRgn ;-------------------------------------------------------- ; ; PROCEDURE FrRgn(rgn: RgnHandle; mode: INTEGER; pat: Pattern); ; ; A6 OFFSETS OF PARAMS AFTER LINK: ; PARAMSIZE EQU 10 RGN EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE MODE EQU RGN-2 ;WORD PAT EQU MODE-4 ;LONG, ADDR OF PATTERN LINK A6,#0 MOVEM.L D7/A3-A4,-(SP) ;SAVE REGS MOVE.L GRAFGLOBALS(A5),A4 ;POINT TO LISAGRAF GLOBALS MOVE.L THEPORT(A4),A3 ;GET CURRENT PORT TST PNVIS(A3) ;IS PNVIS NEG ? BMI.S DONE ;YES, QUIT ; ; special case rectangular region for speed. ; MOVE.L RGN(A6),A0 ;GET RGNHANDLE MOVE.L (A0),A0 ;DE-REFERENCE IT CMP #10,RGNSIZE(A0) ;IS IT RECTANGULAR ? BNE.S NOTRECT ;NO, CONTINUE PEA RGNBBOX(A0) ;YES, PUSH ADDR OF BBOX bsr.l FRMRECT ;FRAME IT BRA.S DONE ;AND QUIT NOTRECT CLR.L -(SP) ;MAKE ROOM FOR FCN RESULT _NEWRGN ;ALLOCATE TEMPRGN MOVE.L (A7)+,D7 ;PUT TEMPRGN IN D7 MOVE.L RGN(A6),-(SP) ;PUSH RGN MOVE.L D7,-(SP) ;PUSH TEMPRGN _COPYRGN ;COPY RGN INTO TEMPRGN MOVE.L D7,-(SP) ;PUSH TEMPRGN MOVE.L PNSIZE(A3),-(SP) ;PUSH PNSIZE _INSETRGN ;InsetRgn(tempRgn,pnSize); MOVE.L RGN(A6),-(SP) ;PUSH RGN MOVE.L D7,-(SP) ;PUSH TEMPRGN MOVE.L D7,-(SP) ;PUSH TEMPRGN _DIFFRGN ;DiffRgn(rgn,tempRgn,tempRgn); MOVE.L D7,-(SP) ;PUSH TEMPRGN MOVE MODE(A6),-(SP) ;PUSH MODE MOVE.L PAT(A6),-(SP) ;PUSH PAT JSR DRAWRGN ;DrawRgn(tempRgn,mode,pat); MOVE.L D7,A0 ;GET TEMPRGN _DisposHandle ;DISCARD IT DONE MOVEM.L (SP)+,D7/A3-A4 ;RESTORE REGS UNLINK PARAMSIZE,'FRRGN ' NewRgn FUNC EXPORT IMPORT NewHandleWrapper ;--------------------------------------------------- ; ; FUNCTION NewRgn; ; Allocate a new region and set it to the empty region. ; Altered not to SysErr; if memerr then return nil. ; moveq #10,d0 ;pass BYTECOUNT=10 _NewHandle ;ALLOCATE A RELOCATABLE OBJECT MOVE.L A0,4(SP) ;STORE INTO NEWRGN RESULT tst.w d0 bne.s @retNil ;if couldn't get it, return nil MOVE.L (A0),A0 ;DE-REFERENCE HANDLE MOVE #10,(A0)+ ;INSTALL RGNSIZE=10 CLR.L (A0)+ ;INSTALL RGNBBOX=(0,0,0,0) CLR.L (A0)+ @retNil RTS ;RETURN TO CALLER DisposeRgn PROC EXPORT ;--------------------------------------------------- ; ; PROCEDURE DisposeRgn(rgn: RgnHandle); ; MOVE.L (SP)+,A1 ;pop return addr MOVE.L (SP)+,A0 ;pop handle _DisposHandle ;discard it JMP (A1) ;and return OpenRgn PROC EXPORT IMPORT NewHandleWrapper,HidePen ;--------------------------------------------------- ; ; PROCEDURE OpenRgn; ; MOVEM.L D3/A2-A4,-(SP) ;SAVE REGS MOVE.L GRAFGLOBALS(A5),A4 ;POINT TO QUICKDRAW GLOBALS MOVE.L THEPORT(A4),A0 ;GET CURRENT GRAFPORT MOVE.L #1,D0 MOVE.L D0,RGNSAVE(A0) ;RGNSAVE := TRUE CLR RGNINDEX(A4) ;RGNINDEX := 0 CLR.L -(SP) ;MAKE ROOM FOR FCN RESULT MOVE #256,-(SP) ;PUSH BYTE COUNT = 256 MOVE (SP),RGNMAX(A4) ;RGNMAX := 256 TOO; JSR NewHandleWrapper MOVE.L (SP)+,RGNBUF(A4) ;RGNBUF := NEWHANDLE(RGNMAX) _HidePen MOVEM.L (SP)+,D3/A2-A4 ;RESTORE REGS RTS ;AND RETURN CloseRgn PROC EXPORT IMPORT ShowPen,SortPoints,CullPoints,PackRgn ;--------------------------------------------------- ; ; PROCEDURE CloseRgn(* dstRgn: RgnHandle *); ; Sort array of inversion points and pack into region. ; ; A6 OFFSETS OF PARAMS AND LOCALS AFTER LINK: ; PARAMSIZE EQU 4 ;TOTAL BYTES OF PARAMETERS DSTRGN EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE PTCOUNT EQU -2 ;WORD VARSIZE EQU PTCOUNT ;SIZE OF LOCAL VARS LINK A6,#VARSIZE MOVEM.L D3/A2-A4,-(SP) ;SAVE REGS MOVE.L GRAFGLOBALS(A5),A4 ;POINT TO QUICKDRAW GLOBALS MOVE.L THEPORT(A4),A0 ;GET CURRENT GRAFPORT TST.L RGNSAVE(A0) ;IS RGNSAVE TRUE ? BEQ.S DONE ;NO, ABORT CLR.L RGNSAVE(A0) ;YES, RGNSAVE := FALSE _SHOWPEN ;UNDO THE HIDEPEN FROM OPENRGN MOVE RGNINDEX(A4),D0 ;GET CURRENT RGNINDEX cmp.w #-147,qderr ;rgnTooBigErr? bne.s @rgnOK ;no, return the region moveq #0,d0 ;yes, return an empty region move d0,qdErr ;clear error so it doesn't bite us. @rgnOK LSR #2,D0 ;DIV BY 4 MOVE D0,PTCOUNT(A6) ;FOR PTCOUNT MOVE.L RGNBUF(A4),A3 ;GET RGNBUF HANDLE MOVE.L (A3),-(SP) ;PUSH BUF POINTER MOVE D0,-(SP) ;PUSH PTCOUNT _SORTPOINTS ;QUICKSORT IN VH ORDER MOVE.L (A3),-(SP) ;PUSH BUF POINTER PEA PTCOUNT(A6) ;PUSH VAR PTCOUNT _CULLPOINTS ;CANCEL DUPLICATE PAIRS MOVE.L A3,-(SP) ;PUSH RGNBUF HANDLE MOVE PTCOUNT(A6),-(SP) ;PUSH (UPDATED) PTCOUNT MOVE.L DSTRGN(A6),-(SP) ;PUSH DSTRGN _PACKRGN ;PACK POINTS INTO DSTRGN MOVE.L A3,A0 ;GET RGNBUF HANDLE _DisposHandle ;DISCARD IT DONE MOVEM.L (SP)+,D3/A2-A4 ;RESTORE REGS UNLINK PARAMSIZE, 'CLOSERGN' CopyRgn PROC EXPORT IMPORT SetHSize ;--------------------------------------------------- ; ; PROCEDURE CopyRgn(* srcRgn,dstRgn: RgnHandle *); ; PARAMSIZE EQU 8 SRCRGN EQU PARAMSIZE+8-4 ;RGNHANDLE DSTRGN EQU SRCRGN-4 ;RGNHANDLE LINK A6,#0 ;ESTABLISH STACK FRAME MOVE.L SRCRGN(A6),A0 ;GET SRCRGN HANDLE MOVE.L DSTRGN(A6),A1 ;GET DSTRGN HANDLE CMP.L A0,A1 ;ARE THEY THE SAME? BEQ.S DONE ;YES, QUIT MOVE.L (A0),A0 ;DE-REFERENCE SRCRGN HANDLE MOVE.L (A1),A1 ;DE-REFERENCE DSTRGN HANDLE MOVE RGNSIZE(A0),D0 ;GET SRC SIZE CMP RGNSIZE(A1),D0 ;IS DST SIZE SAME AS SRC ? BEQ.S COPY ;YES, CONTINUE MOVEM.L D0/D3/A2,-(SP) ;SAVE REGS AND BYTECOUNT MOVE.L DSTRGN(A6),-(SP) ;PUSH DSTRGN HANDLE MOVE D0,-(SP) ;PUSH NEWSIZE=SRC SIZE JSR SetHSize ;CHANGE SIZE OF DST MOVEM.L (SP)+,D0/D3/A2 ;RESTORE REGS AND BYTECOUNT MOVE.L SRCRGN(A6),A0 ;GET SRCRGN HANDLE MOVE.L DSTRGN(A6),A1 ;GET DSTRGN HANDLE MOVE.L (A0),A0 ;DE-REFERENCE SRCRGN HANDLE MOVE.L (A1),A1 ;DE-REFERENCE DSTRGN HANDLE COPY LSR #2,D0 ;LONGCOUNT := BYTECOUNT DIV 4 BCC.S EVEN ;WAS THERE AN ODD WORD ? MOVE (A0)+,(A1)+ ;YES, DO IT TO MAKE EVEN BRA.S EVEN ;AND CONTINUE COPYLP MOVE.L (A0)+,(A1)+ ;COPY A LONG OF SRC TO DST EVEN DBRA D0,COPYLP ;LOOP FOR ALL LONGS DONE UNLINK PARAMSIZE,'COPYRGN ' SetEmptyRgn PROC EXPORT IMPORT SetRectRgn ;--------------------------------------------------- ; ; PROCEDURE SetEmptyRgn(rgn: RgnHandle); ; MOVE.L (SP)+,A0 ;POP RETURN ADDR CLR.L -(SP) ;PUSH LEFT, TOP CLR.L -(SP) ;PUSH RIGHT,BOTTOM MOVE.L A0,-(SP) ;RESTORE RETURN ADDR JMP SETRECTRGN ;DOUBLE UP ON CODE SetRectRgn PROC EXPORT IMPORT SetHSize ;--------------------------------------------------- ; ; PROCEDURE SetRectRgn(rgn: RgnHandle; left,top,right,bottom: INTEGER); ; make a rectangular region from 4 integers. ; LINK A6,#0 ;ESTABLISH STACK FRAME MOVEM.L D3/A2,-(SP) ;SAVE REGS MOVE.L 16(A6),A0 ;GET RGN HANDLE MOVE.L (A0),A1 ;DE-REFERENCE HANDLE MOVEQ #10,D0 CMP RGNSIZE(A1),D0 ;IS RGNSIZE ALREADY 10 ? BEQ.S SIZEOK ;YES, CONTINUE MOVE.L A0,-(SP) ;PUSH RGNHANDLE MOVE D0,-(SP) ;PUSH SIZE = 10 BYTES JSR SetHSize ;CHANGE SIZE OF REGION MOVE.L 16(A6),A0 ;GET RGN HANDLE MOVE.L (A0),A1 ;DE-REFERENCE HANDLE MOVE #10,RGNSIZE(A1) ;INSTALL SIZE = 10 SIZEOK MOVE.L 12(A6),RGNBBOX+TOPLEFT(A1) ;INSTALL RGNBBOX TOPLEFT MOVE.L 8(A6),RGNBBOX+BOTRIGHT(A1) ;INSTALL RGNBBOX BOTRIGHT MOVE RGNBBOX+LEFT(A1),D0 CMP RGNBBOX+RIGHT(A1),D0 ;IS LEFT >= RIGHT ? BGE.S EMPTY ;YES, SET TO EMPTY MOVE RGNBBOX+TOP(A1),D0 CMP RGNBBOX+BOTTOM(A1),D0 ;IS TOP < BOTTOM ? BLT.S DONE ;YES, CONTINUE EMPTY CLR.L RGNBBOX+TOPLEFT(A1) CLR.L RGNBBOX+BOTRIGHT(A1) DONE MOVEM.L (SP)+,D3/A2 ;RESTORE REGS UNLINK 12,'SETRECTR' RectRgn PROC EXPORT IMPORT SetRectRgn ;--------------------------------------------------- ; ; PROCEDURE RectRgn(rgn: RgnHandle; r: Rect); ; make a rectangular region from a rectangle ; MOVE.L (SP)+,A0 ;POP RETURN ADDR MOVE.L (SP)+,A1 ;POP ADDR OF RECT MOVE.L (A1)+,-(SP) ;PUSH LEFT,TOP MOVE.L (A1)+,-(SP) ;PUSH RIGHT,BOTTOM MOVE.L A0,-(SP) ;RESTORE RETURN ADDR JMP SETRECTRGN ;DOUBLE UP ON CODE OffsetRgn PROC EXPORT ;--------------------------------------------------------- ; ; PROCEDURE OffsetRgn(rgn: RgnHandle; dh,dv: INTEGER); ; MOVE.L (SP)+,A1 ;POP RETURN ADDR MOVE (SP)+,D1 ;POP DV MOVE (SP)+,D0 ;POP DH MOVE.L (SP)+,A0 ;POP RGN HANDLE MOVE.L (A0),A0 ;DE-REFERENCE IT ADD #2,A0 ;POINT TO RGNBBOX ;------------------------------------------------------ ; ; OFFSET THE BOUNDING BOX ; ADD D1,(A0)+ ;ADD DV TO TOP ADD D0,(A0)+ ;ADD DH TO LEFT ADD D1,(A0)+ ;ADD DV TO BOTTOM ADD D0,(A0)+ ;ADD DH TO RIGHT ;--------------------------------------------------------------------- ; ; IF NON-RECTANGULAR REGION, OFFSET THE COORDINATES TOO. ; CMP #10,-10(A0) ;IS REGION RECTANGULAR ? BEQ.S DONE ;IF SO, WE'RE ALL DONE NXTVERT ADD D1,(A0)+ ;OFFSET VERTICAL COORD DV NXTHOR ADD D0,(A0)+ ;OFFSET LEFT COORD DH ADD D0,(A0)+ ;OFFSET RIGHT COORD DH CMP #32767,(A0) ;HORIZ TERMINATOR ? BNE NXTHOR ;NO, LOOP ADD #2,A0 ;YES, SKIP OVER TERMINATOR CMP #32767,(A0) ;IS NEXT VERT = 32767 ? BNE NXTVERT ;NO, LOOP FOR MORE DONE JMP (A1) ;RETURN InsetRgn PROC EXPORT IMPORT InsetRect,SortPoints IMPORT NewHandleWrapper,RgnOp,PackRgn ;--------------------------------------------------------- ; ; PROCEDURE InsetRgn(rgn: RgnHandle; dh,dv: INTEGER); ; ; Inset a region by (dh,dv). Outset if dh or dv neg ; ; A6 OFFSETS OF PARAMS AFTER LINK: ; PARAMSIZE EQU 8 RGN EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE DH EQU RGN-2 ;WORD DV EQU DH-2 ;WORD LINK A6,#0 ;NO LOCAL VARS MOVEM.L D3-D7/A2-A4,-(SP) ;SAVE REGS MOVE.L DV(A6),D6 ;GET DV AND DH BOTH BEQ.S JGOHOME ;QUIT IF BOTH ARE ZERO MOVE.L RGN(A6),A4 ;GET RGNHANDLE MOVE.L (A4),A3 ;DE-REFERENCE IT MOVE (A3)+,D7 ;GET RGNSIZE ;-------------------------------------------------------------- ; ; IF RECTANGULAR REGION, CALL INSETRECT AND CHECK FOR EMPTY. ; CMP #10,D7 ;IS RGN RECTANGULAR ? BNE.S NOTRECT ;NO, CONTINUE MOVE.L A3,-(SP) ;PUSH ADDR OF RGNBBOX MOVE.L D6,-(SP) ;PUSH DH AND DV _INSETRECT ;InsetRect(rgn^^.rgnbbox,dh,dv); MOVE LEFT(A3),D0 ;GET BBOX LEFT CMP RIGHT(A3),D0 ;IS LEFT >= RIGHT ? BGE.S EMPTY ;YES, RETURN EMPTY RGN MOVE TOP(A3),D0 ;GET BBOX TOP CMP BOTTOM(A3),D0 ;IS TOP >= BOTTOM ? BLT.S JGOHOME ;NO, CONTINUE EMPTY CLR.L (A3)+ ;SET RGNBBOX TO (0,0,0,0) TO CLR.L (A3)+ ;RETURN EMPTY REGION JGOHOME BRA.S GOHOME ;ALL DONE ;----------------------------------------------------- ; ; THE REGION IS NOT RECTANGULAR. ALLOCATE A POINT BUFFER IN THE HEAP. ; NOTRECT ADD D7,D7 ;TRY BYTES NEEDED = RGNSIZE*2 CLR.L -(SP) ;ROOM FOR FCN RESULT MOVE D7,-(SP) ;PUSH BYTESNEEDED JSR NewHandleWrapper ;ALLOCATE PTBUF MOVE.L (SP)+,A3 ;PUT PTBUF HANDLE IN A3 BSR.S HINSET ;INSET HORIZ AND FLIP SWAP D6 ;GET DV INSTEAD OF DH BSR.S HINSET ;INSET VERTICAL AND FLIP BACK BRA.S DONE ;ALL DONE ;-------------------------------------------------------------------------- ; ; LOCAL ROUTINE TO INSET HORIZONTALLY, SWAP H AND V COORDS, AND RE-SORT ; ; RgnOp(rgn,rgn,bufHandle,maxBytes,op,dh,TRUE); ; HINSET CLR.W -(SP) ;ROOM FOR FCN RESULT MOVE.L A4,-(SP) ;PUSH RGNA = USER RGN MOVE.L A4,-(SP) ;PUSG RGNB = USER RGN ALSO MOVE.L A3,-(SP) ;PUSH BUFHANDLE MOVE D7,-(SP) ;PUSH MAXBYTES MOVE #8,-(SP) ;PUSH OP = INSET MOVE D6,-(SP) ;PUSH DH ST -(SP) ;PUSH OKGROW = TRUE _RGNOP MOVE.W (SP)+,D5 ;GET PTCOUNT IN D5 ; ; SWAP VERT AND HORIZ COORDS OF ALL INVERSION POINTS ; MOVE.L (A3),A0 ;DE-REFERENCE PTBUF HANDLE MOVE D5,D1 ;COPY PTCOUNT BRA.S SWAP2 ;GO TO LOOP START SWAPLP MOVE.L (A0),D0 ;GET A POINT SWAP D0 ;SWAP ITS COORDINATES MOVE.L D0,(A0)+ ;PUT IT BACK INTO ARRAY SWAP2 DBRA D1,SWAPLP ;LOOP FOR ALL POINTS ; ; RE-SORT THE POINTS IN V.H ORDER ; MOVE.L (A3),-(SP) ;PUSH PTBUF PTR MOVE D5,-(SP) ;PUSH PTCOUNT _SORTPOINTS ;RE-SORT INTO VH ORDER ; ; PACK THE RESULTING POINTS ; MOVE.L A3,-(SP) ;PUSH PTBUF HANDLE MOVE D5,-(SP) ;PUSH PTCOUNT MOVE.L A4,-(SP) ;PUSH RGN HANDLE _PACKRGN ;PackRgn(ptBuf,ptCount,rgn); RTS ;RETURN TO LOCAL CALLER ;---------------------------------------------- ; ; DISCARD POINT BUFFER AND QUIT. ; DONE MOVE.L A3,A0 ;GET PTBUF HANDLE _DisposHandle ;DISCARD IT GOHOME MOVEM.L (SP)+,D3-D7/A2-A4 ;RESTORE REGS UNLINK PARAMSIZE,'INSETRGN' EmptyRgn FUNC EXPORT IMPORT EmptyRect ;--------------------------------------------------- ; ; FUNCTION EmptyRgn(rgn: RgnHandle): BOOLEAN; ; MOVE.L (SP)+,A0 ;POP RETURN ADDR MOVE.L (SP)+,A1 ;POP RGNHANDLE MOVE.L (A1),A1 ;DE-REFERENCE HANDLE PEA RGNBBOX(A1) ;PUSH RGNBBOX MOVE.L A0,-(SP) ;PUSH RETURN ADDR JMP ([4*$ae+$e00]) ;USE EMPTYRECT CODE EqualRgn FUNC EXPORT ;------------------------------------------------------- ; ; FUNCTION EqualRgn(rgnA,rgnB: RgnHandle): BOOLEAN; ; ; RETURNS TRUE IF RGNA AND RGNB DESCRIBE THE SAME AREA ; MOVE.L 4(SP),A0 ;GET RGNA HANDLE MOVE.L 8(SP),A1 ;GET RGNB HANDLE CMP.L A0,A1 ;ARE THEY THE SAME ? BEQ.S RETTRUE ;YES, THE REGIONS ARE THE SAME MOVE.L (A0),A0 ;DE-REFERENCE RGN A MOVE.L (A1),A1 ;DE-REFERENCE RGN B MOVE (A0),D0 ;GET RGNSIZE A MOVE D0,D1 ;MAKE AN EXTRA COPY LSR #2,D1 ;DIV BY 4 FOR LONG COUNT SUB #1,D1 ;INIT DBRA LOOP COUNT LOOP CMPM.L (A0)+,(A1)+ ;COMPARE A LONG DBNE D1,LOOP ;LOOK TILL DIFFERENT OR COUNT BNE.S RETFALSE ;DIFFERENT --> FALSE AND #3,D0 ;GET 0..3 FINISH UP BYTES BEQ.S RETTRUE ;IF NO MORE, WE MADE IT LOOP2 CMPM.B (A0)+,(A1)+ ;COMPARE A BYTE BNE.S RETFALSE ;BR IF DIFFERENT SUB #1,D0 BNE.S LOOP2 ;LOOP LAST 1..3 BYTES RETTRUE MOVE.L #1,D0 ;TRUE BRA.S DONE RETFALSE MOVE.L #0,D0 ;FALSE DONE MOVE.B D0,12(SP) ;SET RESULT MOVE.L (SP)+,A0 ;POP RETURN ADDR ADD #8,SP ;STRIP PARAMETERS JMP (A0) ;RETURN SectRgn PROC EXPORT EXPORT DoRgnOp,UnionRgn,DiffRgn,XorRgn IMPORT EqualRgn,CopyRgn,RSect,RectRgn,SetEmptyRgn IMPORT NewHandleWrapper,RgnOp,PackRgn ;--------------------------------------------------------- ; ; PROCEDURE SectRgn(srcRgnA,srcRgnB,dstRgn: RgnHandle); ; calculate the intersection of two regions. ; MOVEQ #0,D0 ;OP = SECT BRA.S DoRgnOp ;SHARE COMMON CODE ;--------------------------------------------------------- ; ; PROCEDURE UnionRgn(srcRgnA,srcRgnB,dstRgn: RgnHandle); ; calculate the union of two regions. ; UnionRgn MOVEQ #4,D0 ;OP = UNION BRA.S DoRgnOp ;SHARE COMMON CODE ;--------------------------------------------------------- ; ; PROCEDURE DiffRgn(srcRgnA,srcRgnB,dstRgn: RgnHandle); ; calculate the difference A-B of two regions ; DiffRgn MOVEQ #2,D0 ;OP = DIFF BRA.S DoRgnOp ;SHARE COMMON CODE ;--------------------------------------------------------- ; ; PROCEDURE XorRgn(srcRgnA,srcRgnB,dstRgn: RgnHandle); ; calculate the exclusive or of two regions ; XorRgn MOVEQ #6,D0 ;OP = DIFF ;FALL INTO DORGNOP ;--------------------------------------------------------- ; ; PROCEDURE DoRgnOp(srcRgnA,srcRgnB,dstRgn: RgnHandle); ; ; Computes the Intersection, Difference, Union, or Xor of two regions. ; ; enter with op in D0. ; op = 0: SECT ; 2: DIFF A-B ; 4: UNION ; 6: XOR ; ; ; A6 OFFSETS OF PARAMS AND LOCALS AFTER LINK: ; PARAMSIZE EQU 12 RGNA EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE RGNB EQU RGNA-4 ;LONG, RGNHANDLE DSTRGN EQU RGNB-4 ;LONG, RGNHANDLE ; TEMPRECTANGLE EQU -8 ;RECT VARSIZE EQU TEMPRECTANGLE ;TOTAL LOCALS ; DoRgnOp LINK A6,#VARSIZE ;ALLOCATE STACK FRAME MOVEM.L D3-D7/A2-A4,-(SP) ;SAVE REGS MOVE D0,D5 ;COPY OP INTO D5 MOVE.L RGNA(A6),A2 ;GET RGNA MOVE.L RGNB(A6),A3 ;GET RGNB MOVE.L DSTRGN(A6),A4 ;GET DSTRGN MOVE.L #2,D7 ; ; ARE THE TWO INPUT REGIONS THE SAME ? ; CLR.B -(SP) ;MAKE ROOM FOR FCN RESULT MOVE.L A2,-(SP) ;PUSH RGNA MOVE.L A3,-(SP) ;PUSH RGNB _EQUALRGN ;CALL EQUALRGN TST.B (SP)+ ;ARE THEY THE SAME ? BEQ.S NOTSAME ;NO, CONTINUE ; ; THE TWO REGIONS ARE THE SAME. IF SECT OR UNION ; THEN COPY RGNA INTO DSTRGN, ELSE ZERO OUT DSTRGN. ; AND D7,D5 ;WAS OP SECT OR UNION ? BNE.S ZERO ;NO, ZERO OUT DSTRGN COPY MOVE.L A2,-(SP) ;PUSH RGNA MOVE.L A4,-(SP) ;PUSH DSTRGN _COPYRGN ;COPY RGNA INTO DSTRGN BRA.S JDONE ;AND QUIT ZERO MOVE.L A4,-(SP) ;PUSH DSTRGN _SetEmptyRgn ;SET IT TO EMPTY BRA.S JDONE ;AND QUIT ; ; IF OP = DIFF AND RGNB = EMPTY, COPY RGNA INTO DST ; NOTSAME MOVE.L (A2),A0 ;DE-REFERENCE RGNA MOVE.L (A3),A1 ;DE-REFERENCE RGNB CMP D7,D5 ;IS OP = DIFF ? BGT.S UNIXOR ;NO, ITS UNION OR XOR BLT.S BBOXES ;NO, IT'S SECT MOVE RGNBBOX+LEFT(A1),D0 ;GET BBOX LEFT CMP RGNBBOX+RIGHT(A1),D0 ;IS BBOX LEFT >= RIGHT ? BGE COPY ;YES, COPY RGNA INTO DST ; ; IF op = SECT OR DIFF, THEN INTERSECT THE BOUNDING BOXES. ; BBOXES PEA RGNBBOX(A0) ;PUSH RGNA^^.RGNBBOX PEA RGNBBOX(A1) ;PUSH RGNB^^.RGNBBOX MOVE D7,-(SP) ;PUSH NRECTS = 2 PEA TEMPRECTANGLE(A6) ;PUSH DST = TEMPRECTANGLE _RSECT ;CALC INTERSECTION BNE.S NOTEMPTY ;BR IF RESULT NOT EMPTY ; ; THE BOUNDING BOXES DON'T INTERSECT. ; IF OP = SECT, THEN RETURN EMPTY. ; IF OP = DIFF, THEN COPY RGNA INTO DSTRGN. ; TST D5 ;IS OP = SECT ? BEQ ZERO ;YES, RETURN EMPTY BRA COPY ;NO, COPY SRCA INTO DSTRGN ; ; IF OP = SECT, THEN CHECK FOR BOTH INPUTS RECTANGULAR ; NOTEMPTY MOVE.L (A2),A0 ;DE-REFERENCE RGNA MOVE.L (A3),A1 ;DE-REFERENCE RGNB TST D5 ;IS OP = SECT ? BNE.S NOTEASY ;NO, CONTINUE MOVEQ #10,D0 CMP RGNSIZE(A0),D0 ;IS RGNA RECTANGULAR ? BNE.S NOTEASY ;NO, CONTINUE CMP RGNSIZE(A1),D0 ;IS RGNB RECTANGULAR ? BNE.S NOTEASY ;NO, CONTINUE MOVE.L A4,-(SP) ;PUSH DSTRGN PEA TEMPRECTANGLE(A6) ;PUSH TEMPRECTANGLE _RECTRGN ;RectRgn(dstRgn,tempRect); JDONE BRA.S DONE ; ; OP = UNION OR XOR: IF EITHER REGION IS EMPTY, COPY THE OTHER. ; UNIXOR MOVE RGNBBOX+LEFT(A1),D0 ;GET RGNB BBOX LEFT CMP RGNBBOX+RIGHT(A1),D0 ;IS RGNB BBOX LEFT >= RIGHT ? BGE COPY ;YES, COPY RGNA INTO DST MOVE RGNBBOX+LEFT(A0),D0 ;GET RGNA BBOX LEFT CMP RGNBBOX+RIGHT(A0),D0 ;IS RGNA BBOX LEFT >= RIGHT ? BLT.S NOTEASY ;NO, CONTINUE MOVE.L A3,A2 ;YES, GET RGNB INSTEAD BRA COPY ;COPY RGNB INTO DST NOTEASY MOVE RGNSIZE(A0),D4 ;GET RGNA RGNSIZE ADD RGNSIZE(A1),D4 ;ADD RGNB RGNSIZE ADD D4,D4 ;TRY DOUBLE FOR BYTECOUNT CLR.L -(SP) ;MAKE ROOM FOR FCN RESULT MOVE D4,-(SP) ;PUSH BYTECOUNT JSR NewHandleWrapper ;ALLOCATE PTBUF MOVE.L (SP)+,A3 ;GET PTBUF HANDLE IN A3 ; ; PtCount := RgnOp(srcA,srcB,bufHandle,maxBytes,op,0,TRUE); ; CLR.W -(SP) ;MAKE ROOM FOR FCN RESULT MOVE.L RGNA(A6),-(SP) ;PUSH RGNA MOVE.L RGNB(A6),-(SP) ;PUSH RGNB MOVE.L A3,-(SP) ;PUSH BUFHANDLE MOVE D4,-(SP) ;PUSH MAXBYTES MOVE D5,-(SP) ;PUSH OP CLR.W -(SP) ;PUSH DH=0 ST -(SP) ;PUSH OKGROW = TRUE _RGNOP MOVE (SP)+,D6 ;GET PTCOUNT MOVE.L A3,-(SP) ;PUSH PTBUF HANDLE MOVE D6,-(SP) ;PUSH PTCOUNT MOVE.L A4,-(SP) ;PUSH DSTRGN _PACKRGN ;PackRgn(ptBuf,ptCount,dstRgn); MOVE.L A3,A0 ;GET PTBUF HANDLE _DisposHandle ;DISCARD IT DONE MOVEM.L (SP)+,D3-D7/A2-A4 ;RESTORE REGS UNLINK PARAMSIZE,'DORGNOP ' PtInRgn FUNC EXPORT ;------------------------------------------------------------ ; ; FUNCTION PtInRgn(pt: Point; rgn: RgnHandle): BOOLEAN; ; ; TESTS IF A GIVEN POINT IS INSIDE A REGION. ; ; A6 OFFSETS OF PARAMETERS AFTER LINK: ; PARAMSIZE EQU 8 ;SIZE OF PARAMETERS RESULT EQU PARAMSIZE+8 ;BOOLEAN PT EQU RESULT-4 ;POINT, VALUE RGN EQU PT-4 ;LONG, HANDLE ENTRY LINK A6,#0 ;NO LOCAL VARS MOVE.L D3,-(SP) ;SAVE REG MOVE PT+H(A6),D1 ;GET TEST HORIZ MOVE PT+V(A6),D2 ;GET TEST VERT CLR D3 ;INIT INSIDE:=FALSE ;----------------------------------------------------------- ; ; FIRST CHECK BOUNDING BOX ; MOVE.L RGN(A6),A0 ;GET RGN HANDLE MOVE.L (A0),A0 ;DE-REFERENCE IT CMP RGNBBOX+LEFT(A0),D1 ;IS PT.H < BBOX LEFT ? BLT.S DONE ;YES, RETURN FALSE CMP RGNBBOX+RIGHT(A0),D1 ;IS PT.H >= BBOX RIGHT ? BGE.S DONE ;YES, RETURN FALSE CMP RGNBBOX+TOP(A0),D2 ;IS PT.V < BBOX TOP ? BLT.S DONE ;YES, RETURN FALSE CMP RGNBBOX+BOTTOM(A0),D2 ;IS PT.V >= BBOX BOT ? BGE.S DONE ;YES, RETURN FALSE CMP #10,RGNSIZE(A0) ;IS REGION RECTANGULAR ? BNE.S NOTRECT ;NO, CONTINUE NOT D3 ;YES, RETURN TRUE BRA.S DONE ;------------------------------------------------------------------ ; ; PT IS INSIDE BOUNDING BOX AND REGION IS NOT RECTANGULAR. ; LOOK AT THE INVERSION POINTS TO DETERMINE IF PT IN REGION. ; NOTRECT LEA RGNDATA(A0),A0 ;POINT TO FIRST VERT COORD NXTVERT CMP (A0)+,D2 ;IS NEXT VERT > PT.V ? BLT.S DONE ;YES, QUIT NEXTHOR MOVE (A0)+,D0 ;GET HORIZ COORD CMP #32767,D0 ;IS IT THE TERMINATOR ? BEQ NXTVERT ;YES, GET NEXT VERT COORD CMP D1,D0 ;IS HORIZ <= PT.H ? BGT NEXTHOR ;NO, IGNORE THIS POINT NOT D3 ;YES, TOGGLE INSIDE BRA NEXTHOR ;AND GO FOR MORE POINTS DONE NEG.B D3 ;BOOLEAN RESULT IS 0 OR 1 MOVE.B D3,RESULT(A6) ;RETURN BOOLEAN FCN RESULT MOVE.L (SP)+,D3 ;RESTORE REG UNLINK PARAMSIZE,'PTINRGN ' RectInRgn FUNC EXPORT ;-------------------------------------------------- ; ; FUNCTION RectInRgn(r: Rect; rgn: RgnHandle): BOOLEAN; ; ; Returns TRUE if any part of the rectangle intersects the region. ; ; ; A6 OFFSETS OF PARAMETERS AFTER LINK: ; PARAMSIZE EQU 8 ;TOTAL SIZE OF PARAMETERS RESULT EQU PARAMSIZE+8 ;BYTE, BOOLEAN RECT EQU RESULT-4 ;LONG, VAR ADDR RGN EQU RECT-4 ;LONG, RGNHANDLE ;------------------------------------------------------ ; ; A6 OFFSETS OF LOCAL VARIABLES AFTER LINK: ; MINRECT EQU -8 ;RECTANGLE SAVESTACK EQU MINRECT-4 ;LONG STATE EQU SAVESTACK-RGNREC ;REGION STATE RECORD VARSIZE EQU STATE ;TOTAL SIZE OF VARIABLES LINK A6,#VARSIZE ;ALLOCATE LOCAL VARIABLES MOVEM.L D0-D7/A1-A5,-(SP) ;SAVE REGISTERS MOVE.L SP,SAVESTACK(A6) ;REMEMBER STACK START CLR.B RESULT(A6) ;INIT BOOLEAN RESULT TO FALSE MOVE.L RGN(A6),A1 ;GET REGION HANDLE MOVE.L (A1),A1 ;DE-REFERENCE IT ;-------------------------------------------------------------- ; ; FIRST CHECK IF RECTANGLE INTERSECTS BOUNDING BOX OF REGION ; MOVE.L RECT(A6),-(SP) ;PUSH POINTER TO RECT PEA RGNBBOX(A1) ;PUSH POINTER TO RGN BBOX MOVE #2,-(SP) ;PUSH NRECTS=2 PEA MINRECT(A6) ;PUSH ADDR WHERE TO PUT RESULT _RSECT ;CALC INTERSECTION BEQ.S GOHOME ;QUIT IF NO INTERSECTION CMP #10,RGNSIZE(A1) ;IS REGION RECTANGULAR ? BEQ.S RETTRUE ;YES, RETURN TRUE ;------------------------------------------------------------ ; ; THE REGION IS NON-RECTANGULAR AND THE RECTANGLE INTERSECTS ; THE REGION'S BOUNDING BOX. WE WILL PLAY BACK THE PORTION OF ; THE REGION WITHIN MINRECT AND SEE IF ANY PART OF THE REGION ; IS ACTUALLY INSIDE THE RECTANGLE. ; ;------------------------------------------------- ; ; INITIALIZE RGN STATE RECORD AT TOP. ; MOVE.L A1,A0 ;GET RGNPTR IN A0 LEA STATE(A6),A1 ;GET STATE RECORD IN A1 MOVE MINRECT+LEFT(A6),D0 ;MINH IN D0 MOVE MINRECT+RIGHT(A6),D1 ;MAXH IN D1 MOVE D0,D2 ;BUFLEFT:=MINH _INITRGN ;INIT RECORD, ALLOCATE BUFFER ;------------------------------------------------------------ ; ; PLAY THE REGION BACK INTO SCAN BUFFER UNTIL IT GETS DOWN TO ; MINRECT, THEN CHECK EACH SCANLINE FOR NON-ZERO PLAYBACK. ; QUIT AND RETURN TRUE IF NON-ZERO FOUND BEFORE RGN GOES BEYOND MINRECT. ; MOVE SCANSIZE(A1),D5 ;GET BUFSIZE= # LONGS -1 MOVE MINRECT+TOP(A6),D0 _SEEKRGN ;SEEK THE REGION TO MINRECT TOP MOVE MINRECT+BOTTOM(A6),D6 TESTBUF MOVE.L SCANBUF(A1),A0 ;POINT TO BUFFER START MOVE D5,D0 ;INIT LOOP COUNT TO BUFSIZE NXTLONG TST.L (A0)+ ;IS SCAN BUF NON-ZERO ? DBNE D0,NXTLONG ;TEST LONGS TILL NON ZERO OR END BNE.S RETTRUE ;WE FOUND A NON-ZERO, RETURN TRUE MOVE NEXTV(A1),D0 ;GET NEXT VERTICAL IN RGN CMP D0,D6 ;IS NEXT RGN VERT BEYOND BOTTOM ? BLE.S GOHOME ;YES, RETURN FALSE _SEEKRGN ;NO, SEEK TO NEXT VERT CHANGE BRA.S TESTBUF ;AND SEE IF IT IS NON-ZERO RETTRUE ADDQ.B #1,RESULT(A6) ;SET BOOLEAN RESULT TO TRUE GOHOME MOVE.L SAVESTACK(A6),SP ;STRIP SCAN BUFFER IF ANY MOVEM.L (SP)+,D0-D7/A1-A5 ;RESTORE REGISTERS UNLINK PARAMSIZE,'RECTINRG' IF 0 THEN OldTrimRect FUNC EXPORT IMPORT RgnOp,TrimRgn ;------------------------------------------------------ ; ; FUNCTION TrimRect(rgn: RgnHandle; VAR dstRect: Rect): CCR TRISTATE; ; ; RESULT IN CONDITION CODES: ; ; = RESULT RECTANGULAR, DSTRECT TRIMMED ; < RESULT EMPTY, DSTRECT NOT MODIFIED ; > RESULT NON-RECT, DSTRECT NOT MODIFIED ; ; If the intersection of rgn and dstRect is rectangular, ; then return EQUAL and put the intersection into dstRect. ; If the intersection is empty or not rectangular, then ; return FALSE and don't modify dstRect. ; ; Does not call the storage allocator. ; ; 1. Fake up a rect rgn on the stack from dstRect ; 2. Call RgnOp with max bytes = 24, OKGROW = FALSE ; 3a. If ptCount = 4 THEN result rect, return TRUE and update dstRect. ; 3b. If ptCount < 4 THEN result empty, return TRUE and clear dstRect. ; 3c. If ptCount > 4 THEN result not rect, return FALSE ; PARAMSIZE EQU 8 RGN EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE DSTRECT EQU RGN-4 ;LONG, ADDR OF DSTRECT PTDATA EQU -24 ;ROOM FOR 6 POINTS PTMASTER EQU PTDATA-4 ;LONG, FAKE MASTER REGION EQU PTMASTER-10 ;ROOM FOR RECT RGN DATA RGNMASTER EQU REGION-4 ;LONG VARSIZE EQU RGNMASTER bra TrimRgn LINK A6,#VARSIZE ;ALLOCATE STACK FRAME MOVEM.L D0-D2/A1,-(SP) ;SAVE ALL REGS USED LEA PTDATA(A6),A0 ;POINT TO BUFFER MOVE.L A0,PTMASTER(A6) ;INSTALL INTO FAKE MASTER LEA REGION(A6),A1 ;POINT TO REGION DATA MOVE.L A1,RGNMASTER(A6) ;INSTALL INTO FAKE MASTER MOVE #10,(A1)+ ;INSTALL RGNSIZE = 10 MOVE.L DSTRECT(A6),A0 ;POINT TO DSTRECT MOVE.L (A0)+,(A1)+ ;COPY DSTRECT TOPLEFT MOVE.L (A0)+,(A1)+ ;COPY DSTRECT BOTRIGHT ; ; RgnOp(rgn,rectRgn,BufHandle,24,sect,0,FALSE) ; CLR.W -(SP) ;ROOM FOR FCN RESULT MOVE.L RGN(A6),-(SP) ;PUSH RGN PEA RGNMASTER(A6) ;PUSH FAKE RGNHANDLE PEA PTMASTER(A6) ;PUSH BUFHANDLE MOVE #24,-(SP) ;PUSH MAXBYTES = 24 CLR.L -(SP) ;PUSH OP = SECT, DH = 0 CLR.B -(SP) ;PUSH OKGROW = FALSE _RGNOP ;RgnOp(rgn,rectRgn,buf,64,op,dh,FALSE); MOVE.L DSTRECT(A6),A0 ;POINT TO DSTRECT CMP #4,(SP)+ ;IS PT COUNT = 4 ? BNE.S DONE ;NO, RETURN < OR > IN CCR MOVE.L PTDATA(A6),(A0)+ ;UPDATE DSTRECT.TOPLEFT MOVE.L PTDATA+12(A6),(A0)+ ;UPDATE DSTRECT.BOTRIGHT SUB D0,D0 ;SET EQUAL FLAG DONE MOVEM.L (SP)+,D0-D2/A1 ;RESTORE ALL REGS UNLK A6 ;RELEASE STACK FRAME RTD #PARAMSIZE ;STRIP PARAMETERS AND RETURN ENDIF TrimRect FUNC EXPORT ;------------------------------------------------------ ; ; FUNCTION TrimRgn(rgn: RgnHandle; VAR dstRect: Rect; Trim: integer): CCR TRISTATE; ; ; Quickly determine the complexity of the interection of a ; rectangle and a region. If the intersection is rectangular, ; return the intersection in dstRect. ; ; If Trim is false, dstRect will not be altered: ; NON-RECT will be returned if intersection ≠ dstRect ; ; RESULT IN CONDITION CODES: ; ; = RESULT RECTANGULAR ; < RESULT EMPTY ; > RESULT NON-RECT ; ; CLOBBERS: A0 ; ; Does not call the storage allocator. ; ; 1. Allocate 2 inversion pair buffers on stack ; 2. Play rgn (trimmed to rect.left, right) into buffers until rect.top reached ; if more than 2 points in buffer -> NON-RECT result ; 3. Play rgn until rect.bottom, ; if pair in buffer changes -> NON-RECT result ; else if buffer empty then -> EMPTY/FULL result ; else Trimmed RECT result ; ; ASSUMES: RgnBBox completely encloses Rect ; ; NOTE: Only the sides of the dstRect are actually trimmed, ; no top/bottom trimming is performed ; ; PARAMSIZE EQU 10 RGN EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE DSTRECT EQU RGN-4 ;LONG, ADDR OF DSTRECT TRIM EQU DSTRECT-2 ;WORD, SHOULD DST BE TRIMMED? VARSIZE EQU 0 LINK A6,#VARSIZE ;ALLOCATE STACK FRAME MOVEM.L D0-D7/A1-A5,-(SP) ;SAVE ALL REGS USED MOVE.L DSTRECT(A6),A0 ;POINT TO DSTRECT MOVEM.L (A0),D4/D5 ;D4 = MinRect.Top, Left ;D5 = MinRect.Bottom, Right MOVE.L D4,D6 SWAP D6 ;D6 = MinRect.Top MOVE.L RGN(A6),A3 ;get RgnHandle MOVE.L (A3),A3 MOVE.W RGNSIZE(A3),D2 ;get rgnSize in D2 CMP #10,D2 ;Rect Rgn? BEQ DONE1 ;return with RECT intersect true ADD #RGNDATA,A3 ;point at rgnData _StackAvail ;get stack avail in D0.L SUB.L #qdStackXtra,D0 ;subtract slop factor <1.5> BAL EXT.L D2 ;make it long CMP.L D2,D0 ;enough space? BGE.S STKOK ;yes, go allocate MOVE.W D5,D2 SUB.W D4,D2 ;get Right - Left EXT.L D2 ;make it long LSL.L #2,D2 ;2 max buffers = 2 (width * 2) CMP.L D0,D2 ;enough space? BLE.S STKOK ;yes, go allocate ; _debugger ;I wish I had a better solution here... BRA DONE1 ;return with cc = GT => NON-RECT STKOK LSR.L #1,D2 ; BCLR #0,D2 ;make bufSize even MOVE.L SP,A0 ;Save stack base in A0 SUB.L D2,SP ;allocate one buffer MOVE.L SP,A4 ;remember buffer pointer SUB.L D2,SP ;allocate one buffer MOVE.L SP,A5 ;remember buffer pointer ;------------------------------------------------------------- ; ; Seek into region until ThisV >= minRect.Top ;; MOVEQ #0,D2 ;init parity to out=even MOVE.W #32767,D7 ;get a MAXINT for comparisons MOVE.L A4,A1 ;set up src1 ptr bra.s NXTV EQUAL CMP D7,D0 ;ALL DONE ? BNE.S XorScam ;no, go back for more NXTV MOVE.W D7,(A1) ;flag end of buffer CMP.W (A3)+,D6 ;is minRect.Top < thisV? BLT.S INRECT ;yes, go check result MOVE.L A4,A2 ;set up src1 ptr MOVE.L A5,A1 ;set up dest ptr EXG A4,A5 ;swap the point buffers ;------------------------------------------------------------- ; ; Each input scan is a sorted array of integers terminated by 32767. ; ; Combine region inversion point data (A3) with that in current ; buffer (A2) and place result in (A1). Discard inversion points not ; between Left = D4 and Right = D5. Eliminate duplicate points. ; Maintain count of inversion points to the left of D4 in D2. ; ; NOTE: This routine is similar to XorScan except that ; it trims the inversion pairs to minRect.Left, Right ; XorScam MOVE (A2)+,D1 ;get next B NEXTA MOVE (A3)+,D0 ;get next A NEXT CMP D1,D0 ;find the leftmost BEQ.S EQUAL ;equal points cancel BLT.S ALESS ;A is less BLESS MOVE D1,(A1)+ ;B is pre-clipped so put to dst MOVE (A2)+,D1 ;get next B BRA.S NEXT ;loop for more ALESS CMP.W D4,D0 ;is A less than or equal to minRect.Left? BLE.S FLIP ;yes, flip parity CMP.W D0,D5 ;is A greater than or equal to minRect.Right? BLE.S NEXTA ;yes, discard MOVE D0,(A1)+ ;PUT A TO DST ;move.l a0,d0 ;move.l a1,d3 ;chk.l d0,d3 ;have we gone to far? BRA.S NEXTA ;loop for more FLIP ADDQ #1,D2 ;toggle even/odd state BRA.S NEXTA ;loop for more ;------------------------------------------------------------- ; ; Scan through region pointed to by A3 until ; 1) Find an odd number of points to the left of D4 -or- ; 2) Find a point between D4 and D5 -or- ; 3) Find a MAXINT (D7) ; Scam MOVE (A3)+,D0 ;get next A CMP.W D4,D0 ;is A less than or equal to minRect.Left? BLE.S FLIP2 ;yes, flip parity CMP.W D0,D7 ;is A MAXINT? BEQ.S NXTV2 ;go get next vert CMP.W D0,D5 ;is A greater than or equal to minRect.Right? BLE.S Scam ;yes, discard BRA.S BAIL ;this would alter the result so bail FLIP2 MOVE (A3)+,D0 ;get another A CMP.W D4,D0 ;is it also less than or equal to minRect.Left? BLE.S Scam ;yes, parity preserved BAIL CMP.W #0,D7 ;NON-RECT, set CC GT and return BRA.S DONE ;------------------------------------------------------------- ; ; If the initial scanline of intersection is rectangular then ; 'Scam' through the region until ThisV >= minRect.Bottom. ; Else abort and return a NON-RECT result ;; INRECT SUB.L A4,A1 ;Get pnt count * 2 MOVE A1,D3 ;make a copy CMP #4,D3 ;is pnt count > 2? BGT.S DONE ;NON-RECT so bail out BLT.S RECT ;less than 2 pnts => rectangular so far AND #1,D2 ;2 pnts => must check parity, is parity even? BNE.S BAIL ;no, return NON-RECT RECT MOVE.L D5,D6 SWAP D6 ;D6 = MinRect.Bottom SUB #2,A3 ;back up to thisV NXTV2 CMP.W (A3)+,D6 ;is thisV < minRect.Bottom? BGT.S SCAM ;yes, go scan more of region TST TRIM(A6) ;is trimming allowed? BNE.S TRIMOK ;yes, go to it TST D3 ;no, there must be zero points BEQ.S NOTRIM ;check for empty/full result BRA.S BAIL ;return failure ;------------------------------------------------------------- ; ; The entire intersection is complete and the result is still ; rectangular. First check for the trivial FULL/EMPTY intersections then ; process any partial rectangular intersections. ; TRIMOK MOVE.L DSTRECT(A6),A1 ;point to dstRect MOVE D2,D0 ;prepare for DONE2 fall thru CMP #2,D3 ;How many words in scan buffer? BLT.S NOTRIM ;none, go decide between full/empty BGT.S TRIM2 ;two, must trim both left and right ;one, could be left or right BTST #0,D0 ;check parity BNE.S TRIMR ;odd, must trim right MOVE (A4),Left(A1) ;even, trim left and return RECT BRA.S DONE2 TRIM2 MOVE (A4)+,Left(A1) ;trim Left and then Right TRIMR MOVE (A4),Right(A1) ;trim right and return RECT BRA.S DONE2 NOTRIM MOVEQ #1,D0 AND D0,D2 ;make even/odd count 0/1 DONE2 CMP D0,D2 ;set cc LT if even, EQ if odd DONE MOVE.L A0,SP ;strip buffers, restore stack ptr DONE1 MOVEM.L (SP)+,D0-D7/A1-A5 ;RESTORE ALL REGS UNLK A6 ;RELEASE STACK FRAME RTD #PARAMSIZE ;STRIP PARAMETERS AND RETURN MapRgn PROC EXPORT IMPORT MapRect,NewHandleWrapper,PutRgn,MapPt IMPORT SortPoints,CullPoints,PackRgn ;------------------------------------------------------------- ; ; PROCEDURE MapRgn(rgn: RgnHandle; fromRect,toRect: Rect); ; ; A6 OFFSETS OF PARAMETERS AND LOCALS AFTER LINK: ; ; fix from QDciPatchROM.a is in as <7Sept90 KON> stb PARAMSIZE EQU 12 RGN EQU PARAMSIZE+8-4 ;LONG, RGNHANDLE FROMRECT EQU RGN-4 ;LONG, ADDR OF RECT TORECT EQU FROMRECT-4 ;LONG, ADDR OF RECT INDEX EQU -2 ;INTEGER SIZE EQU INDEX-2 ;WORD PTCOUNT EQU SIZE-2 ;WORD VARSIZE EQU PTCOUNT ;TOTAL BYTES LINK A6,#VARSIZE ;ALLOCATE STACK FRAME MOVEM.L D3/D6-D7/A2-A4,-(SP) ;SAVE REGS ; ; QUIT FAST IF FROMRECT = TORECT ; MOVE.L FROMRECT(A6),A0 ;POINT TO FROMRECT MOVE.L TORECT(A6),A1 ;POINT TO TORECT CMPM.L (A0)+,(A1)+ ;IS TOPLEFT SAME ? BNE.S NOTSAME ;NO, CONTINUE CMPM.L (A0)+,(A1)+ ;YES, IS BOTRIGHT SAME TOO ? BEQ.S JDONE ;IF SO, JUST QUIT ; ; SPECIAL CASE RECTANGULAR RGN <7Sept90 KON> ; NOTSAME MOVE.L RGN(A6),A4 ;GET RGNHANDLE <7Sept90 KON> MOVE.L (A4),A0 ;DE-REFERENCE RGN <7Sept90 KON> CMP #10,RGNSIZE(A0) ;IS RGN RECTANGULAR ? <7Sept90 KON> BNE.S NOTRECT ;NO, CONTINUE <7Sept90 KON> ; ; Don't do anything if region is rectangular and wide open since MapRect will choke <7Sept90 KON> ; a0 points to rgn ; cmp.l #$80018001,2(a0) ; <7Sept90 KON> bne.s @notWide ; <7Sept90 KON> cmp.l #$7fff7fff,6(a0) ;wide open?; <7Sept90 KON> beq.s JDone ;Yes, Don't remap <7Sept90 KON> @notWide PEA RGNBBOX(A0) ;YES, PUSH RGNBBOX MOVE.L FROMRECT(A6),-(SP) ;PUSH FROMRECT MOVE.L TORECT(A6),-(SP) ;PUSH TO RECT _MAPRECT ;MapRect(rgn^^.rgnBBox,from,to); JDONE BRA.S DONE NOTRECT CLR.L -(SP) ;ROOM FOR FCN RESULT MOVE #256,-(SP) ;PUSH BYTECOUNT = 256 MOVE (SP),SIZE(A6) ;SIZE := 256 BYTES JSR NewHandleWrapper ;ALLOCATE PTBUF MOVE.L (SP)+,A3 ;GET PTBUF HANDLE IN A3 CLR INDEX(A6) ;INDEX := 0 MOVE.L A4,-(SP) ;PUSH RGN MOVE.L A3,-(SP) ;PUSH PTBUF HANDLE PEA INDEX(A6) ;PUSH VAR INDEX PEA SIZE(A6) ;PUSH VAR SIZE _PUTRGN ;UNPACK RGN INTO INVERSION PTS MOVE INDEX(A6),D7 ;GET INDEX LSR #2,D7 ;PTCOUNT := INDEX DIV 4 ; ; MAP ALL INVERSION POINTS ; MOVE D7,D6 ;COPY PTCOUNT FOR LOOP COUNT MOVE.L (A3),A2 ;DE-REFERENCE PTBUF HANDLE BRA.S MORE ;GO TO LOOP START LOOP MOVE.L A2,-(SP) ;PUSH ADDR OF AN INV PT MOVE.L FROMRECT(A6),-(SP) ;PUSH FROMRECT MOVE.L TORECT(A6),-(SP) ;PUSH TORECT _MAPPT ;MAP THIS POINT ADD.L #4,A2 ;BUMP TO NEXT POINT MORE DBRA D6,LOOP ;LOOP ALL INVERSION POINTS MOVE.L (A3),-(SP) ;PUSH PTBUF PTR MOVE D7,-(SP) ;PUSH PTCOUNT _SORTPOINTS ;SortPoints(ptBuf^,ptCount) MOVE.L (A3),-(SP) ;PUSH PTBUF PTR MOVE D7,PTCOUNT(A6) ;PUT PTCOUNT IN MEMORY PEA PTCOUNT(A6) ;PUSH VAR PTCOUNT _CULLPOINTS ;CullPoints(ptBuf^,ptCount) MOVE.L A3,-(SP) ;PUSH PTBUF HANDLE MOVE PTCOUNT(A6),-(SP) ;PUSH PTCOUNT MOVE.L A4,-(SP) ;PUSH RGN _PACKRGN ;PackRgn(ptBuf,ptCount,rgn); MOVE.L A3,A0 ;PUSH PTBUF HANDLE _DisposHandle ;DISCARD IT DONE MOVEM.L (SP)+,D3/D6-D7/A2-A4 ;RESTORE REGS UNLINK PARAMSIZE,'MAPRGN ' ENDPROC ;________________________________________________________________________________ ; ; version of Tuesday, March 28, 1989 ; Purpose: ; To convert bitmaps to regions. ; ; PRINT PUSH,OFF ; INCLUDE 'Traps.a' ; INCLUDE 'ToolEqu.a' ; INCLUDE 'QuickEqu.a' ; INCLUDE 'SysEqu.a' ; PRINT POP ; LOAD 'AIncludes.d' ;________________________________________________________________________________ ; ; FUNCTION BitMapRgn(region:RgnHandle; bMap:BitMap): OSErr; INLINE $A8D7; ; ; Given a region and bitmap, BitMapRgn makes the region a bounding ; region for the 'map. If it can't get memory it will return a ; Memory Manager-type error and an empty region gibbley. Note that ; the region might also be empty with no error (if the bounds is an ; empty rectangle or there are no 1 bits in the bitmap). Lastly, ; if the region would have to exceed 32K it returns a result of ; -500 (rgnTooBigErr). ; ; The bMap parameter may be a pointer to a bitmap, a pointer to a ; pixmap, or a pointer to a portBits field in a color grafport. ; In the latter two cases, if the pixmap is not 1-bit deep, an error ; result of -148 (pixmapTooDeepErr) is returned. ; ; (the nibble state machine idea is from the Finder MaskToRgn routine) ; ; History ; 2/19/88 RBB changed to take in washing and handle rect. & empties properly ; also now finds minimum rectangle to enclose region ; *** version of 2/22/88 *** ; 2/23/88 RBB putting in numerous optimizations recommended by Darin ; 4/4/88 RBB trying to re-do lost work from March ; DBA ; 3/28/89 CSD adjusted setup to know about pixmaps and portBits ; ;________________________________________________________________________________ ; ;Theory ; We scan each line of the bitmap and pump inversion points (ip's) into the region ; to put the areas with ones in the bitmap into the region and the areas ; with zeroes outside the region. ; ; In order to keep track of where we are in "inversion land" we use two ; techniques: ; The first is a scanline buffer which records the changes ; (zeroes to ones and vice versa) as we go. Wherever a change occurs (a ; 1 next to a 0 in the buffer) we need to put out an inversion point. ; The second is a togglin' flag which tells us whether we are "inverted" or not. ; Since we use a state machine in the innermost (nibble) loop to churn out ; ip's, the input to the state machine must be complemented if the flag is set. ; The loop stuff looks like this: ; outer line loop (grows handle in anticipation of worst case for next line) ; longword loop for current line (puts out inter-long ip's as needed) ; loop for 4 nibbles in current long (calls state maching for each nibble) ; ;________________________________________________________________________________ BitMapRgn PROC EXPORT BMFrame RECORD {A6Link},DECR result DS.W 1 paramTop EQU * regionH DS.L 1 bMapPtr DS.L 1 paramSize EQU paramTop-* return DS.L 1 A6Link DS.L 1 rowLongs DS.L 1 ;number of longwords per line rightMask DS.L 1 ;mask for rightmost long of each line slHandle DS.L 1 ;handle to scanline buffer numLines DS.W 1 ;number of lines in bitmap rowNumBytes DS.W 1 ;rowbytes from the bitmap startSize DS.L 1 ;size of region at start of line lastLineH DS.L 1 ;last line (zeroes) handle handSize DS.L 1 ;size of handle (avoid calls to GetHandleSize) max2Add DS.L 1 ;worst case for bytes we could add for next line MMUSave ds.b 2 ;keep it even! SrcPixMap ds.b PMREC+CTREC+20 ;room for pixmap and color table localSize EQU * ENDR WITH BMFrame LINK A6,#localSize MOVEM.L A2-A5/D3-D7,-(SP) ;save work registers CLR.L slHandle(A6) ;no scanline handle, yet CLR.W result(A6) ;function result presumed zero at start MOVE.L regionH(A6),A0 MOVE.L (A0),A2 MOVEQ #0,D0 MOVE.W (A2),D0 ;get size of region MOVE.L D0,handSize(A6) ;save it long ; ; Convert bitmap/pixmap to a pixmap ; move.l bMapPtr(a6),a1 lea SrcPixMap(a6),a2 _BitsToPix ;get boundary rectangle so we can tell how to process the bitmap lea SrcPixMap(A6),A1 ;get bitmap pointer CMP.W #1, pmPixelSize(A1) ;is it 1 bit per pixel deep? BEQ.S @1 ;if yes, we're fine MOVE.W #pixmapTooDeepErr, D0 ;return an error otherwise BRA BMRBadEmpty ;clean up and bail out @1 MOVE.W rowBytes(A1), rowNumBytes(A6) ;get the rowbytes from the bit/pixmap ANDI.W #$7FFF, rowNumBytes(A6) ;mask off pixmap flag MOVE.L bounds+topLeft(A1),D2 ;get topLeft MOVE.W bounds+right(A1),D0 ;get right ;figure the number of longs per row (according to width, not rowbytes) ;so we can get a scanline buffer SUB.W D2,D0 ;right - left BLE BMREmptyOut ;if empty rect. then empty region EXT.L D0 MOVE.L D0,D4 ADD.L D4,D4 ;double width for 2 bytes/ip ADDQ.L #4+2,D4 ;add 4 bytes for y value and $7FFF word ;add 2 more for the $7FFF if the last line ADD.L D4,D4 ;double, just 'cause I feel like it! MOVE.L D4,max2Add(A6) ;save max. bytes for a given line MOVEQ #32,D7 ;(side effect: clear high word of D7) DIVU D7,D0 ;number of longs = width/32 ;get a mask for the rightmost long into rightMask MOVE.L D0,D3 ;save remainder(hi word) SWAP D3 ;get remainder from width/32 MOVEQ #-1,D1 ;default rightmost long mask TST.W D3 ;zero remainder? BEQ.S @0 ;yes, $FFFF is a good mask ADDQ.W #1,D0 ;we need one more long SUB.W D3,D7 ;32 - remainder = zero bits to shift in ASL.L D7,D1 ;get proper mask @0 MOVE.L D1,rightMask(A6) EXT.L D0 MOVE.L D0,rowLongs(A6) ;save # of longs ASL.L #2,D0 ;longs => bytes ;get the scanline buffer (D0 = number of bytes per line) _NewHandle clear ;get a scanline buffer (of zeroes) BNE BMRBadEmpty ;if we failed then return a NIL handle MOVE.L A0,slHandle(A6) ;save buffer handle ;figure the number of lines MOVE.L D2,D3 SWAP D3 ;get top MOVE.W bounds+bottom(A1),D0 ;get bottom SUB.W D3,D0 ;bottom - top BLE BMREmptyOut ;if empty rect. then empty region MOVE.W D0,numLines(A6) ;number of lines MOVE.L baseAddr(A1),a4 ;point to start of map MOVE.W #rgnData,D7 ;initial region size ;OK, now we start the loops. ; A1 will point to the bitmap long, ; A2 to the region. ; A3 points to the current scanline buffer long. ; A4 will point to the row in the map. ; A5 points to the current word (= size + A2) ; D1 holds the current long (modified). ; D2 holds the leftmost coordinate of bitmap.bounds. ; D3 has the y coordinate, and ; D4 the x coordinate (high word stays clear!). ; D5 has number of longs remaining for current line. ; D6 holds the (on or off) value of the "beam" (for the line). ; D7 holds the size outside the longword loop (used as scratch while nibbling). ; (we assume at the very end that D7's high word has remained clear) ; ; goto 32-bit mode ; moveq #true32b,d0 ;switch to 32 bit addressing _rSwapMMUMode ;get previous mode in d0.b (can trash a0/a1/a2, d0/d1/d2) move.b d0,MMUsave(a6) ;save previous state for later BMRHScramLine MOVE.L regionH(A6),A2 MOVE.L (A2),d0 _StripAddress ;clean it up <9> move.l d0,a2 ;point to start of region BMRLineLoop LEA (A2,D7.L),A5 ;point to new region start + size MOVE.L handSize(A6),D1 ;get handle size SUB.l D7,D1 ;handle size - region size CMP.L max2Add(A6),D1 ;is there enough for worst case on next line? BGE.S @1 ;skippy if so MOVE.L handSize(A6),D0 ;get handle size ADD.L max2Add(A6),D0 ;add more than enough for worst case on next line MOVE.L D0,handSize(A6) ;save new size ; ; go back to previous mode ; move.b MMUsave(a6),d0 ;get previous MMU state in d0 _rSwapMMUMode ;get previous mode in d0.b (can trash a0/a1/a2, d0/d1/d2) MOVE.L handSize(A6),d0 ;save new size MOVE.L regionH(A6),A0 ;region handle _SetHandleSize BNE BMRBadEmpty ;if we failed then return a NIL handle ; ; goto 32-bit mode ; moveq #true32b,d0 ;switch to 32 bit addressing _rSwapMMUMode BRA.S BMRHScramLine ;rederef. handle and recompute current pointer @1 MOVE.W D2,D4 ;get current x coordinate from left MOVEQ #0,D6 ;beam initially off MOVE.L A4,A1 ;start of current line into map pointer MOVE.L rowLongs(A6),D5 ;longs remaining for current line MOVE.L slHandle(A6),A3 ;A3 points to the current "differences" long MOVE.L (A3),A3 ; Note: within this loop we assume that nothing will be done to move the heap MOVE.W D3,D0 ;get y position BSR OutputRgnWord ;output y pos to region MOVE.l D7,startSize(A6) ;save size at line start (a la Derwood) BRA NextBMRLong ;enter the long loop BMRLongLoop MOVE.L (A1)+,D0 ;fetch the next long for this line BMRLastLEntry MOVE.L (A3),D1 ;get differences long EOR.L D0,D1 ;compute the differences BNE BMRDiff ;if not the same, skip ahead BMRSame ;since we want to skip this long (it matches the previous line) we need to ;put out an ip if the beam is on TST.B D6 ;beam on? BEQ.S @1 ;skip if not MOVE.W D4,(A5)+ ;pump it out MOVEQ #0,D6 ;beam off @1 ADD.W #32,D4 ;slip to next long's x coordinate @2 ADDQ.W #4,A3 ;to next changes buffer long BRA NextBMRLong ;---------------------------------------------------------------------------------------- ; Start of State Machine ; Handle state 0001 BMRState1 ADDQ.W #3,D4 ;bump x by 3 State1Common MOVE.W D4,(A5)+ ;generate one ;Tog1StateDone ADDQ.W #1,D4 ;bump x by one more TogStateDone NOT.B D6 ;toggle state RTS ; Handle state 0010 BMRState2 ADDQ.W #2,D4 ;bump x by 2 MOVE.W D4,(A5)+ ;generate one Gen1BumpBy1 BSR.S Gen1InvPoint ;and another one BumpBy1 ADDQ.W #1,D4 ;bump once more RTS ;state doesn't change ; Handle state 0011 BMRState3 ADDQ.W #2,D4 ;bump x by 2 MOVE.W D4,(A5)+ ;generate one ADDQ.W #2,D4 ;bump BRA.S TogStateDone ;toggle the state ; Handle state 0100 BMRState4 BSR.S Gen1InvPoint BSR.S Gen1InvPoint BumpBy2 ADDQ.W #2,D4 RTS ; Handle state 0101 BMRState5 BSR.S BMRState4 ;start out as state 4 SUBQ #1,D4 BRA.S State1Common ;use common code ; Handle state 0110 BMRState6 BSR.S Gen1InvPoint ADDQ.W #1,D4 BRA.S Gen1BumpBy1 ; Handle state 0111 BMRState7 BSR.S Gen1InvPoint ADDQ.W #3,D4 BRA.S TogStateDone ; Gen1InvPoint bumps x by one and then generates a horizontal inversion point Gen1InvPoint ADDQ.W #1,D4 ;bump by 1, first MOVE.W D4,(A5)+ ;add x value (ip) to region RTS ; Handle State 1000 BMRState8 MOVE.W D4,(A5)+ BSR.S Gen1InvPoint ADDQ.W #3,D4 RTS ; Handle State 1001 BMRState9 MOVE.W D4,(A5)+ BSR.S Gen1InvPoint ADDQ.W #2,D4 BRA.S State1Common ; Handle State 1010 (most complicated case) BMRState10 MOVE.W D4,(A5)+ BSR.S Gen1InvPoint BSR.S Gen1InvPoint BRA.S Gen1BumpBy1 ; Handle State 1011 BMRState11 MOVE.W D4,(A5)+ BSR.S Gen1InvPoint BSR.S Gen1InvPoint ADDQ.W #2,D4 BRA.S TogStateDone ; Handle State 1100 BMRState12 MOVE.W D4,(A5)+ ADDQ.W #2,D4 MOVE.W D4,(A5)+ BRA.S BumpBy2 ; Handle State 1101 BMRState13 BSR.S BMRState12 SUBQ #1,D4 BRA.S State1Common ; Handle State 1110 BMRState14 MOVE.W D4,(A5)+ ADDQ.W #3,D4 MOVE.W D4,(A5)+ BRA.S BumpBy1 ; State table BMRHandler BRA.S BMRState0 BRA.S BMRState1 BRA.S BMRState2 BRA.S BMRState3 BRA.S BMRState4 BRA.S BMRState5 BRA.S BMRState6 BRA.S BMRState7 BRA.S BMRState8 BRA.S BMRState9 BRA.S BMRState10 BRA.S BMRState11 BRA.S BMRState12 BRA.S BMRState13 BRA.S BMRState14 ; Handle State 15 or 1111 BMRState15 MOVE.W D4,(A5)+ ;generate one now NOT.B D6 ;toggle the state ; Handle State 0 or 0000 BMRState0 ADDQ.W #4,D4 RTS ; End of the State Guys ;---------------------------------------------------------------------------------------- BMRDiff MOVE.L D0,(A3)+ ;fix up scanline buffer for next time ; this long is different from the last one, so output a bunch ; of inversion points by pumping it through the state machine, a nibble ; at a time. MOVEQ #3,D7 ;4 bytes to process (D7 high word clear) MOVEQ #0,D0 ;prevent need to mask for first nibble ; here is the loop where we feed it through a nibble at a time. ; it's worth it to special case a whole byte of 0 BMRByteLoop ROL.L #8,D1 ;get next (topmost) byte TST.B D1 ;is it zero? BNE.S BMRNibble ;if not, 4 bits at a time TST.B D6 BNE.S BMRNibble ;if beam on, must pass through ;the top 8 are zero, so we can save some time ADDQ.W #8,D4 ;bump x BRA.S BMRNextByte ;take care of the rightmost long for a line BMRLastLong MOVE.L (A1),D0 ;fetch the long from the bitmap AND.L rightMask(A6),D0 ;mask off right bits that aren't in map BRA BMRLastLEntry ;go process this long ; handle the first nibble BMRNibble MOVE.B D1,D0 ;get byte EOR.B D6,D0 ;invert nibble when beam is on LSR.B #4,D0 ;get 1st nibble ADD.W D0,D0 ;double for word index JSR BMRHandler(D0.W) ;invoke the handler ; handle the second nibble MOVE.B D1,D0 ;get byte again EOR.B D6,D0 ;invert nibble when beam is on AND.W #%1111,D0 ;mask to it ADD.W D0,D0 ;double for word index JSR BMRHandler(D0.W) ;invoke the handler BMRNextByte DBRA D7,BMRByteLoop ;loop for all 8 nibbles ; bump to the next long NextBMRLong SUBQ.W #1,D5 ;decrement longword index BGT BMRLongLoop ;not at end, loop for whole line BEQ.S BMRLastLong ;process last long for this line ; we've reached the end of the (this) line BMREOL MOVE.l A5,D7 ;current region pointer SUB.l A2,D7 ;figga region size CMP.l startSize(A6),D7 ;did we add inv. pts to this line? BEQ.S BMRNoLine ;br = no, so back up BLT BMR64KErr ;if the size decreased, we overflowed ; if the state is on, generate one last inversion point TST.B D6 BEQ.S @1 MOVE.W D4,(A5)+ ;generate a last one ADDQ.l #2,D7 ;keep sizable advantage @1 ; end the scan line with the traditional $7FFF BSR.S OutputLastRgnWord BMREOL2 ADDQ.W #1,D3 ;bump y position MOVE.W D2,D4 ;start x at left again ADD.W rowNumBytes(A6),A4 ;bump to next row in map SUBQ.W #1,numLines(A6) BGT BMRLineLoop ;if we're not done then do next line BLT.S BMRFinis ;br if really done ; as the last line process an imaginary line of zeroes to end the region… move.b MMUsave(a6),d0 ;get previous MMU state in d0 <9> _rSwapMMUMode ;(can trash a0/a1/a2, d0/d1/d2) <9> MOVE.L rowLongs(A6),D0 ASL.L #2,D0 ;longs => bytes _NewHandle clear ;get a full line of zero bits BNE BMRBadEmpty ;if we failed then return a NIL handle MOVE.L A0,lastLineH(A6) ;save handle MOVE.L (A0),A4 ;start of current line ; ; Since we just allocated this handle, there is no need to strip it since the flags are guaranteed ; to be zero. ; moveq #true32b,d0 ;switch to 32 bit addressing <9> _rSwapMMUMode ; <9> BRA BMRHScramLine ;do this last one (and rederef handle) BMRNoLine SUBQ.L #2,A5 ;back up pointer SUBQ.l #2,D7 ;back down size BRA.S BMREOL2 ;go for next line ; Append the "end of line" token to the region OutputLastRgnWord MOVE.W #$7FFF,D0 ; OutputRgnWord takes the word in D0, appends it to the region, ; and leaves the condition codes set for ADDQ.l D7 (which contains the length) OutputRgnWord MOVE.W D0,(A5)+ ;put a word to the region ADDQ.l #2,D7 ;ink the size RTS ; all done: Restore MMU Mode, clean up, and output the final $7FFF ; BMRFinis move.b MMUsave(a6),d0 ;get previous MMU state in d0 _rSwapMMUMode ;get previous mode in d0.b (can trash a0/a1/a2, d0/d1/d2) MOVE.L lastLineH(A6),A0 _DisposHandle ;get rid of that last line of zeroes CMP.l #10,D7 ;is region empty of inversion points? BEQ.S BMREmptyOut ;skip if so (it's an empty region) BSR.S OutputLastRgnWord ;put End-O-Region ($7FFF) word ; ; check if rgn > 64K ; cmp.l #$0000FFFF,d7 BGT.S BMR64KErr ;if larger than $FFFF, we overflowed! ; find the smallest rectangle that encompasses all the inversion points ; A0 will point to the current region word, A1 to the start of the line ; D1 will have the smallest x, D2 the largest x, D4 will contain $7FFF ; D3 gets the smallest y value (which we know at the start) LEA rgnData(A2),A0 ;point A0 past the rgnBBox MOVE.W #$7FFF,D4 MOVE.W D4,D1 ;smallest x so far = $7FFF MOVE.W #$8000,D2 ;largest x so far = -32768 MOVE.W (A0),D3 ;smallest y there is BRA.S BMRPackStart ;enter loop BMRPackY MOVE.L A0,A1 ;remember where the y value is (sort of) CMP.W (A0)+,D1 ;less than smallest x so far? BLE.S @1 ;skip if not MOVE.W -2(A0),D1 ;new smallest x @1 CMP.W (A0)+,D4 ;end of line? BNE.S @1 ;if not then keep looking CMP.W -4(A0),D2 ;last x greater than largest x so far? BGE.S BMRPackStart ;skip if not MOVE.W -4(A0),D2 ;new largest x BMRPackStart MOVE.W (A0)+,D0 ;get next word (y value or $7FFF) CMP.W D4,D0 ;if $7FFF then we're done BNE.S BMRPackY ;otherwise loop SWAP D3 ;top into top word MOVE.W D1,D3 ;left into bottom word MOVE.W -2(A1),D4 ;bottom (from last y at start of line) SWAP D4 ;move bottom to high word MOVE.W D2,D4 ;get right CMP.l #28,D7 ;size = 28? (do we have a rect. region?) BEQ.S BMRRect ;skip if so BRA.S BMROut ;return complex region ;the region would exceed 64K, so we have to error out, man BMR64KErr move.b MMUsave(a6),d0 ;get previous MMU state in d0 _rSwapMMUMode ;get previous mode in d0.b (can trash a0/a1/a2, d0/d1/d2) MOVE.W #rgnTooBigErr,D0 ;if >32K needed return error ;we come here after failing a SetHandleSize (or NewHandle) BMRBadEmpty MOVE.W D0,result(A6) ;OSErr function result ; emptify the region on errors (or when it should be empty with no error) BMREmptyOut MOVE.L regionH(A6),A0 ;handle to region MOVE.L (A0),A2 ;point to it CLR.L D3 ;(0, 0) to topLeft CLR.L D4 ;(0, 0) to botRight BMRRect MOVEQ #10,D7 ;the size of the region = 10 ;output the region with size (longword, high word clear) in D7 ;D3 comes in with topLeft, D4 with botRight BMROut MOVE.W D7,(A2)+ ;the size of the region MOVE.L D3,(A2)+ ;topLeft to rgnBBox MOVE.L D4,(A2) ;botRight to rgnBBox MOVE.L D7,D0 ;size MOVE.L regionH(A6),A0 ;handle to region _SetHandleSize BMRDspSL MOVE.L slHandle(A6),A0 _DisposHandle ;get rid of the scanline buffer (even if NIL) BMRDone MOVEM.L (SP)+,A2-A5/D3-D7 ;restore work registers UNLK A6 MOVE.L (SP)+,A0 ;pop return address ADD #paramSize,SP ;pop params JMP (A0) ENDWITH ENDPROC