DIRECTORY
BitBltDefs: FROM "BitBltDefs",
LongDefs: FROM "LongDefs", --long LongDefs.BITBLT different for D0,Alto
GraphicsDefs: FROM "GraphicsDefs",
InlineDefs: FROM "InlineDefs",
SegmentDefs: FROM "SegmentDefs",
SystemDefs: FROM "SystemDefs";

BoxFns: PROGRAM IMPORTS LongDefs, InlineDefs, SegmentDefs, SystemDefs, GraphicsDefs EXPORTS GraphicsDefs =
BEGIN

Machine: SegmentDefs.MachineType = SegmentDefs.GetMemoryConfig[].AltoType;
DMachine: BOOLEAN = (Machine = D0) OR (Machine = Dorado);

Scale
: PROCEDURE [factor,c: INTEGER] RETURNS [INTEGER] = INLINE
BEGIN RETURN[(INTEGER[c*factor])/10];END;


Screen: POINTER TO GraphicsDefs.Bitmap;
CursorX: POINTER TO CARDINAL = LOOPHOLE[426B];
CursorY: POINTER TO CARDINAL = LOOPHOLE[427B];

NeedBoxInit: BOOLEAN ← TRUE;
Allocate: PROCEDURE [nwords: CARDINAL] RETURNS [LONG POINTER];
Free: PROCEDURE [LONG POINTER];
CreateBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box,width,height: CARDINAL] =
BEGIN
bm: GraphicsDefs.Bitmap;
longP: LONG POINTER;
IF NeedBoxInit THEN
BEGIN
NeedBoxInit ← FALSE;
[Allocate,Free] ← GraphicsDefs.GetXMAlloc[];
Screen ← GraphicsDefs.GetDefaultBitmapHandle[];
END;
bm ← [bank: ,nWords: ,nBits: width,nLines: height,nBitsPerPixel: Screen.nBitsPerPixel,
portraitMode: Screen.portraitMode,scaleFactor: Screen.scaleFactor,bits:];
bm.nWords ← IF bm.portraitMode
THEN ((Scale[bm.scaleFactor,width-1]+1)*bm.nBitsPerPixel+15)/16
ELSE ((Scale[bm.scaleFactor,height-1]+1)*bm.nBitsPerPixel+15)/16;
longP ← Allocate[
IF bm.portraitMode THEN bm.nWords*(Scale[bm.scaleFactor,bm.nLines-1]+1)
ELSE bm.nWords*(Scale[bm.scaleFactor,bm.nBits-1]+1)];
bm.bank ← InlineDefs.HighHalf[longP];
bm.bits ← InlineDefs.LowHalf[longP];
GraphicsDefs.EraseArea[0,0,width,height,@bm];
b↑ ← [boxBitmap: bm,savedBits: NIL,displayBitmap:,displayX:,displayY:,
displayed: FALSE,savedBank:,function: replace,type: normal,gray:
];
END;
MakeLongPointer: PROCEDURE [ptr: POINTER,bank: CARDINAL] RETURNS [LONG POINTER] = MACHINE CODE BEGIN END;
DestroyBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box,restore: BOOLEAN] =
BEGIN
IF restore THEN RemoveBox[b];
Free[MakeLongPointer[b.boxBitmap.bits,b.boxBitmap.bank]];
b.boxBitmap.bits ← NIL;
END;
DisplayBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box,x,y: CARDINAL, bitmap: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN
IF bitmap = NIL THEN bitmap ← Screen;
b.displayed ← FALSE;
b.displayBitmap ← bitmap;
MoveBox[b,x,y];
END;
CursorInBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box] RETURNS [BOOLEAN] =
BEGIN
RETURN
[CursorX↑ IN [CARDINAL[MAX[20,INTEGER[b.displayX]]] - 20..b.displayX + b.boxBitmap.nBits + 20)
ANDCursorY↑ IN [CARDINAL[MAX[20,INTEGER[b.displayY]]] - 20..b.displayY + b.boxBitmap.nLines + 20)];
END;
ReleaseBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box] =
BEGIN
IF b.savedBits = NIL THEN RETURN;
IF b.savedBank # 0 THEN Free[MakeLongPointer[b.savedBits,b.savedBank]]
ELSE SystemDefs.FreeSegment[b.savedBits];
b.savedBits ← NIL;
END;
RemoveBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box] =
BEGIN
savedFunction: BitBltDefs.BBoperation ← b.function;
savedType: GraphicsDefs.BoxType ← b.type;
savedBits: POINTER ← b.boxBitmap.bits;
savedBank: [0..77B] ← b.boxBitmap.bank;

IF NOT b.displayed THEN RETURN;
b.function ← replace;b.type ← normal;
b.boxBitmap.bits ← b.savedBits;
b.boxBitmap.bank ← b.savedBank;
PaintBox[b];
b.function ← savedFunction;b.type ← savedType;
b.boxBitmap.bits ← savedBits;
b.boxBitmap.bank ← savedBank;
b.displayed ← FALSE;
ReleaseBox[b];
END;
MoveBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box,cornerX,cornerY: CARDINAL] =
BEGIN
bm: GraphicsDefs.Bitmap ← b.displayBitmap↑;
bbox: GraphicsDefs.Bitmap ← b.boxBitmap;
x: ARRAY [0..SIZE[BitBltDefs.BBTable]] OF UNSPECIFIED;
mBLT: POINTER TO BitBltDefs.BBTable ← @x[LOOPHOLE[@x,CARDINAL] MOD 2];
savedX: CARDINAL ← cornerX;
savedY: CARDINAL ← cornerY;
bDisplayX: CARDINAL ← b.displayX;
bDisplayY: CARDINAL ← b.displayY;

IF NOT b.displayed THEN -- first time, do initialization
BEGIN
src,dest: GraphicsDefs.Rectangle;
xOff: CARDINAL = IF INTEGER[cornerX] < 0 THEN -cornerX ELSE 0;
yOff: CARDINAL = IF INTEGER[cornerY] < 0 THEN -cornerY ELSE 0;
IF b.savedBits = NIL THEN
BEGIN
nScanLines: CARDINAL ←
Scale[bm.scaleFactor,(IF bm.portraitMode THEN bbox.nLines ELSE bbox.nBits)-1]+1;
longP: LONG POINTER ← Allocate[bbox.nWords*nScanLines];
b.savedBank ← InlineDefs.HighHalf[longP];
b.savedBits ← InlineDefs.LowHalf[longP];
END;
b.displayed ← TRUE;
src ← [cornerX+xOff,cornerY+yOff,cornerX+bbox.nBits-1,cornerY+bbox.nLines-1];
dest ← [xOff,yOff,bbox.nBits-1,bbox.nLines-1];
bbox.bits ← b.savedBits;
bbox.bank ← b.savedBank;
GraphicsDefs.TransferRectangle[Screen,@bbox,@src,@dest];
END
ELSE --"normal" case
BEGIN
maxWidth,maxHeight: CARDINAL;
mBLT.ptrs ← long;
mBLT.dbca ← mBLT.sbca ← NIL;
mBLT.pad ← 0;
mBLT.sourcetype ← block;
mBLT.function ← replace;
mBLT.unused ← 0;

IF bm.portraitMode THEN
BEGIN
cornerX ← Scale[bm.scaleFactor,cornerX]*bm.nBitsPerPixel;
cornerY ← Scale[bm.scaleFactor,cornerY];
bDisplayX ← Scale[bm.scaleFactor,bDisplayX]*bm.nBitsPerPixel;
bDisplayY ← Scale[bm.scaleFactor,bDisplayY];
bbox.nLines ← (Scale[bm.scaleFactor,b.boxBitmap.nLines-1]+1);
bbox.nBits ← (Scale[bm.scaleFactor,b.boxBitmap.nBits-1]+1)*bm.nBitsPerPixel;
bm.nLines ← (Scale[bm.scaleFactor,b.displayBitmap.nLines-1]+1);
bm.nBits ← (Scale[bm.scaleFactor,b.displayBitmap.nBits-1]+1)*bm.nBitsPerPixel;
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[bm.scaleFactor,(bm.nLines-1)-(cornerY+b.boxBitmap.nLines-1)];
cornerY ← Scale[bm.scaleFactor,cornerX];cornerX ← t*bm.nBitsPerPixel;
t ← Scale[bm.scaleFactor,(bm.nLines-1) - (bDisplayY+b.boxBitmap.nLines-1)];
bDisplayY ← Scale[bm.scaleFactor,bDisplayX];bDisplayX ← t*bm.nBitsPerPixel;
bbox.nLines ← Scale[bm.scaleFactor,b.boxBitmap.nBits-1]+1;
bbox.nBits ← (Scale[bm.scaleFactor,b.boxBitmap.nLines-1]+1)*bm.nBitsPerPixel;
bm.nLines ← Scale[bm.scaleFactor,b.displayBitmap.nBits-1]+1;
bm.nBits ← (Scale[bm.scaleFactor,b.displayBitmap.nLines-1]+1)*bm.nBitsPerPixel;
END;

maxWidth ← MIN[bbox.nBits+MIN[INTEGER[bDisplayX],0],
bm.nBits-1+MIN[INTEGER[bDisplayX],0]-bDisplayX];
maxHeight ← MIN[bbox.nLines+MIN[INTEGER[bDisplayY],0],
bm.nLines-1+MIN[INTEGER[bDisplayY],0]-bDisplayY];
mBLT.dlbca ← MakeLongPointer[bm.bits,bm.bank];
mBLT.dbmr ← bm.nWords;
mBLT.slbca ← MakeLongPointer[b.savedBits,b.savedBank];
mBLT.sbmr←bbox.nWords;
--restore
mBLT.dh ← maxHeight;
mBLT.sty ← MAX[-INTEGER[bDisplayY],0];
mBLT.dty ← MAX[INTEGER[bDisplayY],0];
IF INTEGER[cornerX] < INTEGER[bDisplayX] THEN
BEGIN--restore right
mBLT.slx ← bbox.nBits-MIN[bDisplayX-cornerX,bbox.nBits];
mBLT.dlx ← bDisplayX+mBLT.slx;
mBLT.dw ←
IF INTEGER[mBLT.dlx+1] < INTEGER[bm.nBits] THEN MIN[bDisplayX-cornerX,
(bm.nBits-1)-mBLT.dlx,bbox.nBits] ELSE 0;
END
ELSE
BEGIN--restore left
mBLT.dw ← MIN[cornerX-bDisplayX,maxWidth];
mBLT.slx ← MAX[-INTEGER[bDisplayX],0];
mBLT.dlx ← bDisplayX + mBLT.slx;
END;
LongDefs.BITBLT[mBLT];
mBLT.dw ← maxWidth;
mBLT.slx ← ABS[MIN[INTEGER[bDisplayX],0]];
mBLT.dlx ← bDisplayX+mBLT.slx;
IF INTEGER[cornerY] < INTEGER[bDisplayY] THEN
BEGIN--restore bottom
mBLT.sty ← bbox.nLines-MIN[bDisplayY-cornerY,bbox.nLines];
mBLT.dty ← bDisplayY+mBLT.sty;
mBLT.dh ←
IF INTEGER[mBLT.dty+1] < INTEGER[bm.nLines] THEN MIN[bDisplayY-cornerY,
(bm.nLines-1)-mBLT.dty,bbox.nLines] ELSE 0;
END
ELSE
BEGIN--restore top
mBLT.dh ← MIN[cornerY-bDisplayY,maxHeight];
mBLT.sty ← MAX[-INTEGER[bDisplayY],0];
mBLT.dty ← bDisplayY + mBLT.sty;
END;
LongDefs.BITBLT[mBLT];
--move savedBits
mBLT.dlbca ← mBLT.slbca ← MakeLongPointer[b.savedBits,b.savedBank];
mBLT.dbmr ← mBLT.sbmr ← bbox.nWords;
mBLT.dh ← bbox.nLines;
mBLT.sty ← 0;
mBLT.dty ← 0;
IF INTEGER[cornerX] < INTEGER[bDisplayX] THEN
BEGIN--move right
mBLT.slx ← 0;
mBLT.dlx ← MIN[bDisplayX-cornerX,bbox.nBits];
mBLT.dw ← MIN[bbox.nBits-mBLT.dlx,bbox.nBits];
END
ELSE
BEGIN--move left
mBLT.dlx ← 0;
mBLT.slx ← cornerX-bDisplayX;
mBLT.dw ← MIN[bbox.nBits-mBLT.slx,bbox.nBits];
END;
LongDefs.BITBLT[mBLT];
mBLT.dw ← bbox.nBits;
mBLT.slx ← 0;
mBLT.dlx ← 0;
IF INTEGER[cornerY] < INTEGER[bDisplayY] THEN
BEGIN--move down
mBLT.dty ← MIN[bDisplayY-cornerY,bbox.nLines];
mBLT.dh ← MIN[bbox.nLines-mBLT.dty,bbox.nLines];
mBLT.sty ← 0;
LongDefs.BITBLT[mBLT];
END
ELSE
BEGIN--move up (bad news for LongDefs.BITBLT)
i: CARDINAL;
da,sa: POINTER;
deltaY: CARDINAL ← cornerY-bDisplayY;
da ← b.savedBits;
sa ← da + deltaY*bbox.nWords;
IF b.savedBank = 0 THEN FOR i IN [deltaY..bbox.nLines) DO
InlineDefs.COPY[to: da,from: sa,nwords: bbox.nWords];
da ← da + bbox.nWords;
sa ← sa + bbox.nWords;
ENDLOOP
ELSE
FOR i IN [deltaY..bbox.nLines) DO
InlineDefs.LongCOPY[to: MakeLongPointer[da,b.savedBank],
from: MakeLongPointer[sa,b.savedBank],nwords: bbox.nWords];
da ← da + bbox.nWords;
sa ← sa + bbox.nWords;
ENDLOOP;
END;
--save
maxWidth ← MIN[bbox.nBits,(bm.nBits-1)-cornerX];
maxHeight ← MIN[bbox.nLines,(bm.nLines-1)-cornerY];
mBLT.dlbca ← MakeLongPointer[b.savedBits,b.savedBank];mBLT.sbmr←bbox.nWords;
mBLT.slbca ← MakeLongPointer[bm.bits,bm.bank];mBLT.sbmr ← bm.nWords;
mBLT.dty ← MAX[-INTEGER[cornerY],0];
mBLT.dh ← maxHeight - mBLT.dty;
mBLT.sty ← cornerY+mBLT.dty;
IF INTEGER[cornerX] < INTEGER[bDisplayX] THEN
BEGIN--save left
mBLT.dlx ← MAX[-INTEGER[cornerX],0];
mBLT.slx ← cornerX + mBLT.dlx;
mBLT.dw ← MIN[bDisplayX - cornerX,maxWidth];
mBLT.dw ← mBLT.dw - MIN[mBLT.dw,mBLT.dlx];
END
ELSE
BEGIN--save right
mBLT.dlx ← bbox.nBits - MIN[cornerX-bDisplayX,bbox.nBits];
mBLT.slx ← cornerX + mBLT.dlx;
mBLT.dw ← MIN[cornerX - bDisplayX,maxWidth];
END;
LongDefs.BITBLT[mBLT];
mBLT.dlx ← MAX[-INTEGER[cornerX],0];
mBLT.dw ← maxWidth - mBLT.dlx;
mBLT.slx ← cornerX+mBLT.dlx;
IF INTEGER[cornerY] < INTEGER[bDisplayY] THEN
BEGIN--save top
mBLT.dty ← MAX[-INTEGER[cornerY],0];
mBLT.sty ← cornerY + mBLT.dty;
mBLT.dh ← MIN[bDisplayY - cornerY,maxHeight];
mBLT.dh ← mBLT.dh - MIN[mBLT.dh,mBLT.dty];
END
ELSE
BEGIN--save bottom
mBLT.dh ← MIN[cornerY - bDisplayY,maxHeight];
mBLT.dty ←bbox.nLines-MIN[cornerY-bDisplayY,bbox.nLines];
mBLT.sty ← cornerY + mBLT.dty;
END;
LongDefs.BITBLT[mBLT];
END; -- end of "real" MoveRectangle code
b.displayX ← savedX;
b.displayY ← savedY;
PaintBox[b];
END;
PaintBox: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Box] =
BEGIN
bm: GraphicsDefs.Bitmap ← b.displayBitmap↑;
bbox: GraphicsDefs.Bitmap ← b.boxBitmap;
magicBitmap: LONG POINTER TO ARRAY [0..0) OF CARDINAL ← NIL;
offX,offY: INTEGER;
cornerX: INTEGER ← b.displayX;
cornerY: INTEGER ← b.displayY;

IF (cornerX > INTEGER[b.displayBitmap.nBits]) OR
(cornerY > INTEGER[b.displayBitmap.nLines]) OR
(INTEGER[cornerX+bbox.nBits] <= 0) OR
(INTEGER[cornerY+bbox.nLines] <= 0) THEN RETURN; --trivial exclusion

--coords convert
IF bm.portraitMode THEN
BEGIN
cornerX ← Scale[bm.scaleFactor,cornerX]*bm.nBitsPerPixel;cornerY ← Scale[bm.scaleFactor,cornerY];
bbox.nLines ← (Scale[bm.scaleFactor,b.boxBitmap.nLines-1]+1);
bbox.nBits ← (Scale[bm.scaleFactor,b.boxBitmap.nBits-1]+1)*bm.nBitsPerPixel;
bm.nLines ← (Scale[bm.scaleFactor,b.displayBitmap.nLines-1]+1);
bm.nBits ← (Scale[bm.scaleFactor,b.displayBitmap.nBits-1]+1)*bm.nBitsPerPixel;
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[bm.scaleFactor,(bm.nLines-1)-(cornerY+b.boxBitmap.nLines-1)];
cornerY ← Scale[bm.scaleFactor,cornerX];cornerX ← t*bm.nBitsPerPixel;
bbox.nLines ← Scale[bm.scaleFactor,b.boxBitmap.nBits-1]+1;
bbox.nBits ← (Scale[bm.scaleFactor,b.boxBitmap.nLines-1]+1)*bm.nBitsPerPixel;
bm.nLines ← Scale[bm.scaleFactor,b.displayBitmap.nBits-1]+1;
bm.nBits ← (Scale[bm.scaleFactor,b.displayBitmap.nLines-1]+1)*bm.nBitsPerPixel;
END;
offX ← IF cornerX < 0 THEN -cornerX ELSE 0;
offY ← IF cornerY < 0 THEN -cornerY ELSE 0;

BEGIN
maxWidth: CARDINAL =
MIN[bbox.nBits-offX,(bm.nBits-offX-1)-cornerX];
maxHeight: CARDINAL =
MIN[bbox.nLines-offY,(bm.nLines-offY-1)-cornerY];
x: ARRAY [0..SIZE[BitBltDefs.BBTable]] OF UNSPECIFIED;
pBLT: POINTER TO BitBltDefs.BBTable ← @x[LOOPHOLE[@x,CARDINAL] MOD 2];

pBLT.ptrs ← long;
pBLT.dbca ← pBLT.sbca ← NIL;
pBLT.pad ← 0;
pBLT.sourcetype ← block;
pBLT.function ← b.function;
IF (b.type # normal) OR (b.function # replace) THEN
BEGIN
magicBitmap ← Allocate[bbox.nWords*bbox.nLines];
InlineDefs.LongCOPY[from: MakeLongPointer[b.savedBits,b.savedBank],
to: magicBitmap,
nwords: bbox.nWords*bbox.nLines];
pBLT.dlbca ← magicBitmap;pBLT.dbmr ← bbox.nWords;
pBLT.dlx ← offX;pBLT.dty←offY;
pBLT.slbca←MakeLongPointer[bbox.bits,bbox.bank];
pBLT.sbmr←bbox.nWords;pBLT.slx←offX;pBLT.sty←offY;
pBLT.dh ← maxHeight;pBLT.dw ← maxWidth;
SELECT b.type FROM
outline =>
BEGIN
dx: CARDINAL ← bm.nBitsPerPixel;
dy: CARDINAL ← 1;
pBLT.function ← IF b.function=paint THEN erase ELSE paint;
pBLT.dw ← maxWidth-dx;pBLT.slx←offX+dx;
LongDefs.BITBLT[pBLT]; --left
pBLT.slx←offX;pBLT.dlx←offX+dx;
LongDefs.BITBLT[pBLT]; --right
pBLT.dw ← maxWidth;pBLT.dlx ← offX;
pBLT.dh ← maxHeight-dy;pBLT.sty ← offY+dy;
LongDefs.BITBLT[pBLT]; --above
pBLT.sty ← offY;pBLT.dty ← offY+dy;
LongDefs.BITBLT[pBLT]; --below
pBLT.dty←offY;pBLT.dh ← maxHeight;
pBLT.function ← b.function;
LongDefs.BITBLT[pBLT]; --true colors
END;
gray => --must call external routine which knows about gray matrix
BEGIN
destBitmap: GraphicsDefs.Bitmap ← b.boxBitmap; --all but bits are same
destBitmap.bank ← InlineDefs.HighHalf[magicBitmap];
destBitmap.bits ← InlineDefs.LowHalf[magicBitmap];
GraphicsDefs.SetGrayLevel[b.gray];
GraphicsDefs.PutGrayBitmap[@b.boxBitmap,0,0,@destBitmap];
END;
ENDCASE =>
LongDefs.BITBLT[pBLT];
pBLT.function ← replace;
END;
--BLT
pBLT.dlbca ← MakeLongPointer[bm.bits,bm.bank];pBLT.dbmr ← bm.nWords;
pBLT.dlx ← cornerX+offX;pBLT.dty←cornerY+offY;
IF magicBitmap = NIL THEN pBLT.slbca←MakeLongPointer[bbox.bits,bbox.bank]
ELSE pBLT.slbca←magicBitmap;
pBLT.sbmr←bbox.nWords;pBLT.slx←offX;pBLT.sty←offY;
pBLT.dh ← maxHeight;pBLT.dw ← maxWidth;
LongDefs.BITBLT[pBLT];
IF magicBitmap # NIL THEN Free[magicBitmap];
END;
END;

END.