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

BitmapFns: PROGRAM IMPORTS LongDefs, InlineDefs, SegmentDefs, SystemDefs EXPORTS GraphicsDefs =
BEGIN OPEN GeneralDisplayDefs;

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

Screen
: POINTER TO GraphicsDefs.Bitmap ← NIL;
Scale: PROCEDURE [factor,c: INTEGER] RETURNS [INTEGER] = INLINE
BEGIN RETURN[(INTEGER[c*factor])/10];END;

--data structures for D0 style color display
colorData
: TYPE = MACHINE DEPENDENT RECORD
[
addr: [0..16),
r,g,b,unused: BOOLEAN,
data: [0..256)
];
colorEntry: TYPE = RECORD
[
red,green,blue: colorData
];
colorTable: TYPE = RECORD --must be hexAligned
[
front: ARRAY [0..2) OF UNSPECIFIED,
entry: ARRAY [0..16) OF colorEntry,
back: ARRAY [0..10) OF UNSPECIFIED,
hexAlignPad: ARRAY [0..16) OF UNSPECIFIED
];

--data structures for D1 style color display from GeneralDisplayDefs

--data structure for Alto type displays
DCB
: TYPE = MACHINE DEPENDENT RECORD
[ Link: POINTER TO DCB,
Resolution: [0..1],
Invert: [0..1],
Offset: [0..100B),
Width: [0..400B),
Bitmap: POINTER TO ARRAY OF UNSPECIFIED,
Long: BOOLEAN,
Height: [0..77777B],
LongBitmap: LONG POINTER TO ARRAY OF UNSPECIFIED
];
DCBSeal: POINTER = LOOPHOLE[177423B];

--global variables
ColorBitmap
: POINTER TO POINTER = LOOPHOLE[414B];
ColorBitmapBank: POINTER TO CARDINAL = LOOPHOLE[415B];
ColorMap: POINTER TO POINTER = LOOPHOLE[416B];
ColorMapBank: POINTER TO CARDINAL = LOOPHOLE[417B];
ColorCtrlBlk: ColorControlBlock;
AChanCtrlBlk: ChannelControlBlock;
ColorMonitor: MonitorControlBlock;
cx: ARRAY [0..SIZE[colorTable]] OF UNSPECIFIED;
ColorTable: POINTER TO colorTable ← @cx[LOOPHOLE[@cx,CARDINAL] MOD 16];
ColorTable8: POINTER TO ATableImage;
x,xx: ARRAY [0..SIZE[BitBltDefs.BBTable]] OF UNSPECIFIED;
DCBhead: POINTER TO POINTER TO DCB = LOOPHOLE[420B];
SetPointBLT: POINTER TO BitBltDefs.BBTable ←
@x[LOOPHOLE[@x,CARDINAL] MOD 2];
BLT: POINTER TO BitBltDefs.BBTable ←
@xx[LOOPHOLE[@xx,CARDINAL] MOD 2];

DefaultAllocate: PROCEDURE [nwords: CARDINAL] RETURNS [LONG POINTER] ←
BEGIN
IF DMachine THEN RETURN [SegmentDefs.LongDataSegmentAddress[
SegmentDefs.NewDataSegment[
SegmentDefs.DefaultXMBase,(nwords+255)/256]]]
ELSE RETURN[SystemDefs.AllocateSegment[nwords]];
END;
DefaultFree: PROCEDURE [XVM: LONG POINTER] ←
BEGIN
IF DMachine THEN
SegmentDefs.DeleteDataSegment[SegmentDefs.LongVMtoDataSegment[XVM]]
ELSE SystemDefs.FreeSegment[InlineDefs.LowHalf[XVM]];
END;

SetXMAlloc: PUBLIC PROCEDURE [Allocate: PROCEDURE [nwords: CARDINAL] RETURNS [LONG POINTER],Free: PROCEDURE [LONG POINTER]] =
BEGIN
DefaultAllocate ← Allocate;
DefaultFree ← Free;
END;

GetXMAlloc
: PUBLIC PROCEDURE RETURNS [Allocate: PROCEDURE [nwords: CARDINAL] RETURNS [LONG POINTER],Free: PROCEDURE [LONG POINTER]] =
BEGIN
RETURN[DefaultAllocate,DefaultFree];
END;

--even dot distribution, maximum flicker
--
GrayThresholds: ARRAY [0..16) OF CARDINAL ←
--
[00,08,02,10,
--
14,04,12,06,
--
03,11,01,09,
--
13,07,15,05
--
];
GrayThresholds: ARRAY [0..16) OF CARDINAL ←
[15,01,05,11, --minimum flicker
06,08,12,02,
07,09,13,03,
14,00,04,10
];
GrayLevel: CARDINAL ← 0;
Gray0,Gray1,Gray2,Gray3: CARDINAL ← 177777B;

SetGrayThresholds: PUBLIC PROCEDURE [new: POINTER TO ARRAY [0..0) OF CARDINAL,
outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
1=>
BEGIN
i: CARDINAL;
FOR i IN [0..16] DO GrayThresholds[i] ← new[i];
ENDLOOP;
END;
4=>
BEGIN
i: CARDINAL;
ColorTable.front ← ALL[0];
ColorTable.back ← ALL[0];
FOR i IN [0..17B] DO
ColorTable.entry[i].red ← [i,TRUE,FALSE,FALSE,FALSE,new[i]];
ColorTable.entry[i].green ← [i,FALSE,TRUE,FALSE,FALSE,new[i]];
ColorTable.entry[i].blue ← [i,FALSE,FALSE,TRUE,FALSE,new[i]];
ENDLOOP;
END;
8=>
BEGIN
i: CARDINAL;
FOR i IN [0..1024) DO
ColorTable8[i].zeroHigh ← ColorTable8[i].zeroLow ← 0;
ENDLOOP;
FOR i IN [0..256) DO
ColorTable8[i*4] ← [0,new[i] MOD 16,new[i],0,new[i],new[i]/16];
ENDLOOP;
pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
END;
ENDCASE=>ERROR;
END;

halftoneWord
: TYPE = MACHINE DEPENDENT RECORD
[
unused: [0..7777B],
val1,val2,val3,val4: BOOLEAN
];
SetGrayLevel: PUBLIC PROCEDURE [intensity: CARDINAL,
outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
1=>
BEGIN
h: halftoneWord;
value,scanline: CARDINAL;

IF GrayLevel = intensity THEN RETURN;
GrayLevel ← intensity;

FOR scanline IN [0..4) DO
h.val1 ← intensity <= GrayThresholds[scanline*4+0];
h.val2 ← intensity <= GrayThresholds[scanline*4+1];
h.val3 ← intensity <= GrayThresholds[scanline*4+2];
h.val4 ← intensity <= GrayThresholds[scanline*4+3];
h.unused ← 0;
value ← LOOPHOLE[h];
value ← value + value*16;
value ← value + value*256;
SELECT scanline FROM
0=> Gray0←value;
1=> Gray1←value;
2=> Gray2←value;
3=> Gray3←value;
ENDCASE;
ENDLOOP;
END;
4=>
BEGIN
intensity ← 15 - MIN[intensity,15];
Gray0 ← Gray1 ← Gray2 ← Gray3 ←
intensity + intensity*16 + intensity*256 + intensity*4096;
END;
8=> Gray0 ← Gray1 ← Gray2 ← Gray3 ← intensity + intensity*256;
ENDCASE=>ERROR;
BLT.gray0 ← Gray0;BLT.gray1 ← Gray1;
BLT.gray2 ← Gray2;BLT.gray3 ← Gray3;
END;

SetRed
: PUBLIC PROCEDURE[gray: [0..256),newVal: [0..256),outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
8=>BEGIN ColorTable8[gray*4].redHigh ← newVal/16;
ColorTable8[gray*4].redLow ← newVal MOD 16;
pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
END;
ENDCASE;
END;

SetGreen
: PUBLIC PROCEDURE[gray: [0..256),newVal: [0..256),outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
8=>BEGIN
ColorTable8[gray*4].green ← newVal;
pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
END;
ENDCASE;
END;

SetBlue
: PUBLIC PROCEDURE[gray: [0..256),newVal: [0..256),outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
8=>BEGIN
ColorTable8[gray*4].blue ← newVal;
pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
END;
ENDCASE;
END;

GetRed
: PUBLIC PROCEDURE[gray: [0..256),outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [CARDINAL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
8=>RETURN[ColorTable8[gray*4].redHigh*16+ColorTable8[gray*4].redLow];
ENDCASE=>RETURN[0];
END;

GetGreen
: PUBLIC PROCEDURE[gray: [0..256),outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [CARDINAL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
8=>RETURN[ColorTable8[gray*4].green];
ENDCASE=>RETURN[0];
END;

GetBlue
: PUBLIC PROCEDURE[gray: [0..256),outputDevice: POINTER TO GraphicsDefs.Bitmap ← NIL] RETURNS [CARDINAL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
8=>RETURN[ColorTable8[gray*4].blue];
ENDCASE=>RETURN[0];
END;

GetGrayBlock
: PUBLIC PROCEDURE RETURNS [gray0,gray1,gray2,gray3: CARDINAL] =
BEGIN RETURN [Gray0,Gray1,Gray2,Gray3];END;

SetDefaultBitmap: PUBLIC PROCEDURE [width,height: CARDINAL] =
BEGIN
IF Screen = NIL THEN --ought to be able to return a couple of screens...
Screen ← SystemDefs.AllocateHeapNode[SIZE[GraphicsDefs.Bitmap]];
Screen.nBits ← width;
Screen.nLines ← height;
END;

TurnOnGraphics
: PUBLIC PROCEDURE [bitsPerPixel: CARDINAL ← 1,portraitMode: BOOLEAN ← TRUE,scalePercent: CARDINAL ← 100] RETURNS [POINTER TO GraphicsDefs.Bitmap] =
BEGIN
scaleFactor: CARDINAL ← scalePercent/10;
IF Screen = NIL THEN --ought to be able to return a couple of screens...
BEGIN
Screen ← SystemDefs.AllocateHeapNode[SIZE[GraphicsDefs.Bitmap]];
SELECT bitsPerPixel FROM
1=> IF portraitMode THEN
BEGIN
Screen.nBits ← (608*10)/scaleFactor;
Screen.nLines ← (800*10)/scaleFactor;
END
ELSE
BEGIN
Screen.nBits ← (800*10)/scaleFactor;
Screen.nLines ← (608*10)/scaleFactor;
END;
4,8=> IF portraitMode THEN
BEGIN
Screen.nBits ← (640*10)/scaleFactor;
Screen.nLines ← (480*10)/scaleFactor;
END
ELSE
BEGIN
Screen.nBits ← (480*10)/scaleFactor;
Screen.nLines ← (640*10)/scaleFactor;
END;
ENDCASE=> ERROR;
END;
Screen.nBitsPerPixel ← bitsPerPixel;
Screen.portraitMode ← portraitMode;
Screen.scaleFactor ← scaleFactor;
Screen.nWords ← IF portraitMode
THEN 2*bitsPerPixel*((Scale[scaleFactor,Screen.nBits]+31)/32)
ELSE 2*bitsPerPixel*((Scale[scaleFactor,Screen.nLines]+31)/32) ;
SetPointBLT↑ ← [ptrs: long,pad: 0,sourcealt: FALSE,sourcetype: gray,
dw: Screen.nBitsPerPixel,dh: 1,
destalt: ,function: ,dbca: NIL,dbmr: ,dlx:,dty:,
gray0: 177777B,gray1: 177777B,gray2: 177777B,gray3:177777B,
sbca: NIL,unused: 0,sbmr: 0,slx:,sty:,slbca:,dlbca:];
BLT↑ ← [ptrs: long,pad: 0,sourcealt: FALSE,sourcetype: gray,
dw: ,dh: ,
destalt: ,function: ,dbca: NIL,dbmr: ,dlx:,dty:,
gray0: 177777B,gray1: 177777B,gray2: 177777B,gray3:177777B,
sbca: NIL,unused: 0,sbmr: 0,slx:,sty:,slbca:,dlbca:];
SELECT Screen.nBitsPerPixel FROM
1=>
BEGIN
myDCB: POINTER TO DCB ←
IF DMachine THEN SystemDefs.AllocateHeapNode[SIZE[DCB]+1]
ELSE SystemDefs.AllocateSegment[Screen.nWords*Screen.nLines+SIZE[DCB]+1];
myScreen: LONG POINTER TO ARRAY OF UNSPECIFIED ←
IF DMachine THEN DefaultAllocate[Screen.nWords*Screen.nLines+1] ELSE NIL;
IF InlineDefs.BITAND[LOOPHOLE[InlineDefs.LowHalf[myScreen],CARDINAL],1]=1 THEN
myScreen←myScreen+1;
IF InlineDefs.BITAND[LOOPHOLE[myDCB,CARDINAL],1]=1 THEN
myDCB←myDCB+1;
IF DMachine THEN
BEGIN
Screen.bank←InlineDefs.HighHalf[myScreen];
Screen.bits←InlineDefs.LowHalf[myScreen];
END
ELSE
BEGIN
Screen.bank ← 0;
Screen.bits ← LOOPHOLE[myDCB+SIZE[DCB]];
END;
myDCB↑←[Link: LOOPHOLE[0],Resolution: 0,Invert: 0,Offset: 0,
Width: Screen.nWords,
Bitmap: IF DMachine THEN DCBSeal ELSE LOOPHOLE[Screen.bits],
Long: DMachine,Height: Screen.nLines/2,LongBitmap: myScreen];
EraseArea[0,0,Screen.nBits,Screen.nLines];
DCBhead↑←myDCB;
END;
4=>
BEGIN
normalThresholds: ARRAY [0..17B] OF CARDINAL ←
[0,17,34,51,68,85,102,119,136,153,170,187,204,221,238,255];
SetGrayThresholds[LOOPHOLE[@normalThresholds]];
ColorBitmap↑ ← LOOPHOLE[0];--InlineDefs.LowHalf[longP];
ColorBitmapBank↑ ← 4;--InlineDefs.HighHalf[longP];
ColorMap↑ ← ColorTable;
ColorMapBank↑ ← 0;
Screen.bank ← 4;--1;
Screen.bits←LOOPHOLE[ColorBitmap↑];
EraseArea[0,0,Screen.nBits,Screen.nLines];
END;
8=>
BEGIN
normalThresholds: ARRAY [0..256) OF CARDINAL;
i: CARDINAL;
FOR i IN [0..256) DO normalThresholds[i] ← i;ENDLOOP;
ColorTable8 ← SystemDefs.AllocateSegment[SIZE[ATableImage]];
SetGrayThresholds[LOOPHOLE[@normalThresholds]];
Screen.bank ← 4;
Screen.bits←NIL;
ColorCtrlBlk ←
[ATable: ColorTable8,
BTable: NIL,CTable: NIL,MiniMixer: NIL,
VBlank: [VBtoVS: 3,VStoVS: 3],VStoVB: 20B,VisibleLines: 240,
HRamMaxAddr: 379,HBlank: [HBLeadLength: 6,HSTrailAddr: 47],
HBTrailLength: 6,PClock: [unused: 0,PClockMul: 130B,PClockDiv: 14B],
reserved: 0
];
AChanCtrlBlk ←
[NIL,Screen.nWords,MakeLongPointer[Screen.bits,Screen.bank],
Screen.nLines/2,Screen.nBits+377B,MarginOffset[Ramtek525],
[unused: 0,
b24BitsPerPixel: FALSE,AByPass: FALSE,BByPass: FALSE,A8B2: TRUE,
Resolution: full,Size8: TRUE,Size4:FALSE,Size2: FALSE,Size1: FALSE
]
];
ColorMonitor ←
[177456B,[0,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE],
@AChanCtrlBlk,NIL,@ColorCtrlBlk
];
EraseArea[0,0,Screen.nBits,Screen.nLines];
pMonitorHead↑ ← @ColorMonitor;
END;
ENDCASE => ERROR;
RETURN[Screen];
END;

GetDefaultBitmapHandle: PUBLIC PROCEDURE RETURNS [POINTER TO GraphicsDefs.Bitmap] =
BEGIN RETURN[Screen];END;

--first, the dummy routines using default bitMap (Screen)
PutPoint: PUBLIC PROCEDURE[x,y: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL]=
BEGIN SetPoint[x,y,replace,b];END;
ErasePoint: PUBLIC PROCEDURE [x,y: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetPoint[x,y,erase,b];END;
XorPoint: PUBLIC PROCEDURE [x,y: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetPoint[x,y,invert,b];END;

PutLine
: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetLine[x1,y1,x2,y2,replace,b];END;
EraseLine: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetLine[x1,y1,x2,y2,erase,b];END;
XorLine: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetLine[x1,y1,x2,y2,invert,b];END;

PutArea
: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetArea[x1,y1,x2,y2,paint,b];END;
EraseArea: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetArea[x1,y1,x2,y2,erase,b];END;
XorArea: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetArea[x1,y1,x2,y2,invert,b];END;
ReplaceArea: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetArea[x1,y1,x2,y2,replace,b];END;

PutGray
: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetGray[x1,y1,x2,y2,paint,b];END;
EraseGray: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetGray[x1,y1,x2,y2,erase,b];END;
XorGray: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetGray[x1,y1,x2,y2,invert,b];END;
ReplaceGray: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN SetGray[x1,y1,x2,y2,replace,b];END;

PutBitmap
: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Bitmap,x,y: CARDINAL,dest: POINTER TO GraphicsDefs.Bitmap] =
BEGIN SetBitmap[b,x,y,paint,dest];END;
EraseBitmap: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Bitmap,x,y: CARDINAL,dest: POINTER TO GraphicsDefs.Bitmap] =
BEGIN SetBitmap[b,x,y,erase,dest];END;
XorBitmap: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Bitmap,x,y: CARDINAL,dest: POINTER TO GraphicsDefs.Bitmap] =
BEGIN SetBitmap[b,x,y,invert,dest];END;
ReplaceBitmap: PUBLIC PROCEDURE[b: POINTER TO GraphicsDefs.Bitmap,x,y: CARDINAL,dest: POINTER TO GraphicsDefs.Bitmap]=
BEGIN SetBitmap[b,x,y,replace,dest];END;

--and the real guys
SetPoint: PUBLIC PROCEDURE [x,y: CARDINAL,fn: BitBltDefs.BBoperation,b: POINTER TO GraphicsDefs.Bitmap] =
BEGIN
logBitsPerPixel: CARDINAL;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← SELECT b.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;
IF NOT x IN [0..b.nBits) THEN RETURN;
IF NOT y IN [0..b.nLines) THEN RETURN;
-- save your ass
IF b.portraitMode THEN
BEGIN x ← Scale[b.scaleFactor,x];y ← Scale[b.scaleFactor,y];END
ELSE
BEGIN t: CARDINAL ← Scale[b.scaleFactor,b.nLines-y];y ← Scale[b.scaleFactor,x];x ← t;END;
SetPointBLT.function ← fn;
SetPointBLT.dlbca ← MakeLongPointer[b.bits,b.bank];
SetPointBLT.dbmr ← b.nWords;
SetPointBLT.dlx ← InlineDefs.BITSHIFT[x,logBitsPerPixel];
SetPointBLT.dty ← y;
LongDefs.BITBLT[SetPointBLT];
END;


MakeLongPointer: PROCEDURE [ptr: POINTER,bank: UNSPECIFIED] RETURNS [LONG POINTER] = MACHINE CODE BEGIN END;

SetLine: PUBLIC PROCEDURE [x1,y1,x2,y2: INTEGER,fn: BitBltDefs.BBoperation,b: POINTER TO GraphicsDefs.Bitmap] =
BEGIN
hstep,vstep,hsign,vsign,count: INTEGER;
horizontal: BOOLEAN;
xMax,yMax: INTEGER;
logBitsPerPixel: CARDINAL;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← SELECT b.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;

IF b.portraitMode THEN
BEGIN
x1 ← Scale[b.scaleFactor,x1];y1 ← Scale[b.scaleFactor,y1];x2 ← Scale[b.scaleFactor,x2];y2 ← Scale[b.scaleFactor,y2];
xMax ← Scale[b.scaleFactor,b.nBits];yMax ← Scale[b.scaleFactor,b.nLines];
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[b.scaleFactor,(b.nLines-1)-y1];y1 ← Scale[b.scaleFactor,x1];x1 ← t;
t ← Scale[b.scaleFactor,(b.nLines-1)-y2];y2 ← Scale[b.scaleFactor,x2];x2 ← t;
xMax ← Scale[b.scaleFactor,b.nLines];yMax ← Scale[b.scaleFactor,b.nBits];
END;

SetPointBLT.function ← fn;
SetPointBLT.dbmr ← b.nWords;
SetPointBLT.dlbca ← MakeLongPointer[b.bits,b.bank];

hstep ← x1-x2;
vstep ← y1-y2;
hsign ← IF hstep >= 0 THEN 1 ELSE -1;
vsign ← IF vstep >= 0 THEN 1 ELSE -1;
horizontal←ABS[hstep] > ABS[vstep];
count ← IF horizontal THEN ABS[vstep]+(ABS[hstep]-ABS[vstep])/2
ELSE ABS[hstep]+(ABS[vstep]-ABS[hstep])/2 ;

SetPointBLT.dlx ← InlineDefs.BITSHIFT[x2,logBitsPerPixel];SetPointBLT.dty ← y2;
IF INTEGER[x2] IN [0..xMax) AND INTEGER[y2] IN [0..yMax) THEN LongDefs.BITBLT[SetPointBLT];

UNTIL (x1 = x2) AND (y1 = y2) DO
IF count >= ABS[vstep] THEN-- go horizontal
BEGIN
x2 ← x2 + hsign;
count ← count - ABS[vstep];
IF horizontal AND INTEGER[x2] IN [0..xMax) AND INTEGER[y2] IN [0..yMax) THEN
BEGIN
SetPointBLT.dlx ← InlineDefs.BITSHIFT[x2,logBitsPerPixel];
SetPointBLT.dty ← y2;
LongDefs.BITBLT[SetPointBLT];
END;
END
ELSE BEGIN
y2 ← y2+vsign;
count ← count + ABS[hstep];
IF NOT horizontal AND INTEGER[x2] IN [0..xMax) AND INTEGER[y2] IN [0..yMax) THEN
BEGIN
SetPointBLT.dlx ← InlineDefs.BITSHIFT[x2,logBitsPerPixel];
SetPointBLT.dty ← y2;
LongDefs.BITBLT[SetPointBLT];
END;
END;
ENDLOOP;
SetPointBLT.dlx ← InlineDefs.BITSHIFT[x2,logBitsPerPixel];SetPointBLT.dty ← y2;
IF INTEGER[x2] IN [0..xMax) AND INTEGER[y2] IN [0..yMax) THEN LongDefs.BITBLT[SetPointBLT];

END;

SetArea: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,fn: BitBltDefs.BBoperation,b: POINTER TO GraphicsDefs.Bitmap] =
BEGIN
logBitsPerPixel: CARDINAL;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← SELECT b.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;

IF INTEGER[x1] > INTEGER[x2] THEN BEGIN t: CARDINAL ← x1;x1 ← x2;x2 ← t;END;
IF INTEGER[y1] > INTEGER[y2] THEN BEGIN t: CARDINAL ← y1;y1 ← y2;y2 ← t;END;
IF INTEGER[x2] < 0 OR INTEGER[y2] < 0 OR
INTEGER[x1] >= INTEGER[b.nBits] OR INTEGER[y1] >= INTEGER[b.nLines]
THEN RETURN;
x1 ← MAX[0,INTEGER[x1]];y1 ← MAX[0,INTEGER[y1]];
x2 ← MIN [x2,b.nBits-1];y2 ← MIN[y2,b.nLines-1];
IF b.portraitMode THEN
BEGIN
x1 ← Scale[b.scaleFactor,x1];y1 ← Scale[b.scaleFactor,y1];x2 ← Scale[b.scaleFactor,x2];y2 ← Scale[b.scaleFactor,y2];
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[b.scaleFactor,(b.nLines-1)-y1];y1 ← Scale[b.scaleFactor,x1];
x1 ← Scale[b.scaleFactor,(b.nLines-1)-y2];y2 ← Scale[b.scaleFactor,x2];x2 ← t;
END;

BLT.sourcetype ← gray;
BLT.function ← fn;
BLT.dlbca ← MakeLongPointer[b.bits,b.bank];
BLT.dbmr ← b.nWords;
BLT.dlx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
BLT.dty ← y1;
BLT.dw ← InlineDefs.BITSHIFT[(x2+1-x1),logBitsPerPixel];
BLT.dh ← (y2+1-y1);
BLT.gray0 ← BLT.gray1 ← BLT.gray2 ← BLT.gray3 ← 177777B;
LongDefs.BITBLT[BLT];
BLT.gray0 ← Gray0;BLT.gray1 ← Gray1;
BLT.gray2 ← Gray2;BLT.gray3 ← Gray3;
END;

SetGray: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,fn: BitBltDefs.BBoperation,b: POINTER TO GraphicsDefs.Bitmap] =
BEGIN
logBitsPerPixel: CARDINAL;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← SELECT b.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;

IF INTEGER[x1] > INTEGER[x2] THEN BEGIN t: CARDINAL ← x1;x1 ← x2;x2 ← t;END;
IF INTEGER[y1] > INTEGER[y2] THEN BEGIN t: CARDINAL ← y1;y1 ← y2;y2 ← t;END;
IF INTEGER[x2] < 0 OR INTEGER[y2] < 0 OR
INTEGER[x1] >= INTEGER[b.nBits] OR INTEGER[y1] >= INTEGER[b.nLines]
THEN RETURN;
x1 ← MAX[0,INTEGER[x1]];y1 ← MAX[0,INTEGER[y1]];
x2 ← MIN [x2,b.nBits-1];y2 ← MIN[y2,b.nLines-1];
IF b.portraitMode THEN
BEGIN
x1 ← Scale[b.scaleFactor,x1];y1 ← Scale[b.scaleFactor,y1];x2 ← Scale[b.scaleFactor,x2];y2 ← Scale[b.scaleFactor,y2];
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[b.scaleFactor,(b.nLines-1)-y1];y1 ← Scale[b.scaleFactor,x1];
x1 ← Scale[b.scaleFactor,(b.nLines-1)-y2];y2 ← Scale[b.scaleFactor,x2];x2 ← t;
END;

BLT.sourcetype ← gray;
BLT.function ← fn;
BLT.dlbca ← MakeLongPointer[b.bits,b.bank];
BLT.dbmr ← b.nWords;
BLT.dlx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
BLT.dty ← y1;
BLT.dw ← InlineDefs.BITSHIFT[(x2+1-x1),logBitsPerPixel];
BLT.dh ← (y2+1-y1);
BLT.sty ← 0; --DORADO looks here
LongDefs.BITBLT[BLT];
END;

MaskGray: PUBLIC PROCEDURE [x1,y1,x2,y2: CARDINAL,b: POINTER TO GraphicsDefs.Bitmap] =
BEGIN
logBitsPerPixel: CARDINAL;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← SELECT b.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;

IF INTEGER[x1] > INTEGER[x2] THEN BEGIN t: CARDINAL ← x1;x1 ← x2;x2 ← t;END;
IF INTEGER[y1] > INTEGER[y2] THEN BEGIN t: CARDINAL ← y1;y1 ← y2;y2 ← t;END;
IF INTEGER[x2] < 0 OR INTEGER[y2] < 0 OR
INTEGER[x1] >= INTEGER[b.nBits] OR INTEGER[y1] >= INTEGER[b.nLines]
THEN RETURN;
x1 ← MAX[0,INTEGER[x1]];y1 ← MAX[0,INTEGER[y1]];
x2 ← MIN [x2,b.nBits-1];y2 ← MIN[y2,b.nLines-1];
IF b.portraitMode THEN
BEGIN
x1 ← Scale[b.scaleFactor,x1];y1 ← Scale[b.scaleFactor,y1];x2 ← Scale[b.scaleFactor,x2];y2 ← Scale[b.scaleFactor,y2];
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[b.scaleFactor,(b.nLines-1)-y1];y1 ← Scale[b.scaleFactor,x1];
x1 ← Scale[b.scaleFactor,(b.nLines-1)-y2];y2 ← Scale[b.scaleFactor,x2];x2 ← t;
END;

BLT.sourcetype ← andgray;
BLT.function ← replace;
BLT.dlbca ← MakeLongPointer[b.bits,b.bank];
BLT.dbmr ← b.nWords;
BLT.dlx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
BLT.dty ← y1;
BLT.dw ← InlineDefs.BITSHIFT[(x2+1-x1),logBitsPerPixel];
BLT.dh ← (y2+1-y1);
BLT.slbca ← MakeLongPointer[b.bits,b.bank];
BLT.sbmr ← b.nWords;
BLT.slx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
BLT.sty ← y1;
LongDefs.BITBLT[BLT];
END;

SetBitmap: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Bitmap,
x,y: CARDINAL,fn: BitBltDefs.BBoperation,dest: POINTER TO GraphicsDefs.Bitmap] =
BEGIN
x2,y2: CARDINAL;
sx,sy,dy: CARDINAL ← 0;
logBitsPerPixel: CARDINAL;
IF dest = NIL THEN dest ← Screen;
logBitsPerPixel ← SELECT dest.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;
x2 ← x+b.nBits-1;
y2 ← y+b.nLines-1;

IF x >= dest.nBits OR y >= dest.nLines THEN RETURN;
IF x2 >= dest.nBits THEN x2 ← dest.nBits - 1;
IF y2 >= dest.nLines THEN BEGIN dy ← y2-(dest.nLines-1);y2 ← dest.nLines-1;END;
IF INTEGER[x] < 0 THEN BEGIN sx ← -x;x ← 0;END;
IF INTEGER[y] < 0 THEN BEGIN sy ← -y;y ← 0;END;

IF dest.portraitMode THEN
BEGIN
x ← Scale[dest.scaleFactor,x];y ← Scale[dest.scaleFactor,y];x2 ← Scale[dest.scaleFactor,x2];y2 ← Scale[dest.scaleFactor,y2];
sx ← Scale[dest.scaleFactor,sx];sy ← Scale[dest.scaleFactor,sy];
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[dest.scaleFactor,(dest.nLines-1)-y];y ← Scale[dest.scaleFactor,x];
x ← Scale[dest.scaleFactor,(dest.nLines-1)-y2];
y2 ← MIN[Scale[dest.scaleFactor,x2],y+Scale[dest.scaleFactor,b.nBits]-1];x2 ← MIN[t,x+Scale[dest.scaleFactor,b.nLines]-1];
sy ← Scale[dest.scaleFactor,sx];sx ← Scale[dest.scaleFactor,dy];
END;

BLT.sourcetype ← block;
BLT.function ← fn;
BLT.dlbca ← MakeLongPointer[dest.bits,dest.bank];
BLT.dbmr ← dest.nWords;
BLT.dlx ← InlineDefs.BITSHIFT[x,logBitsPerPixel];
BLT.dty ← y;
BLT.dw ← InlineDefs.BITSHIFT[(x2+1-x),logBitsPerPixel];
BLT.dh ← (y2+1-y);
BLT.slbca ← MakeLongPointer[b.bits,b.bank];
BLT.sbmr ← b.nWords;
BLT.slx ← InlineDefs.BITSHIFT[sx,logBitsPerPixel];
BLT.sty ← sy;
LongDefs.BITBLT[BLT];
END;

PutGrayBitmap: PUBLIC PROCEDURE [b: POINTER TO GraphicsDefs.Bitmap,
x,y: CARDINAL,dest: POINTER TO GraphicsDefs.Bitmap ← NIL] =
BEGIN
x2,y2: CARDINAL;
sx,sy,dy: CARDINAL ← 0;
logBitsPerPixel: CARDINAL;
IF dest = NIL THEN dest ← Screen;
logBitsPerPixel ← SELECT dest.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;
x2 ← x+b.nBits-1;
y2 ← y+b.nLines-1;

IF x >= dest.nBits OR y >= dest.nLines THEN RETURN;
IF x2 >= dest.nBits THEN x2 ← dest.nBits - 1;
IF y2 >= dest.nLines THEN BEGIN dy ← y2-(dest.nLines-1);y2 ← dest.nLines-1;END;
IF INTEGER[x] < 0 THEN BEGIN sx ← -x;x ← 0;END;
IF INTEGER[y] < 0 THEN BEGIN sy ← -y;y ← 0;END;

IF dest.portraitMode THEN
BEGIN
x ← Scale[dest.scaleFactor,x];y ← Scale[dest.scaleFactor,y];x2 ← Scale[dest.scaleFactor,x2];y2 ← Scale[dest.scaleFactor,y2];
sx ← Scale[dest.scaleFactor,sx];sy ← Scale[dest.scaleFactor,sy];
END
ELSE
BEGIN t: CARDINAL;
t ← Scale[dest.scaleFactor,(dest.nLines-1)-y];y ← Scale[dest.scaleFactor,x];
x ← Scale[dest.scaleFactor,(dest.nLines-1)-y2];
y2 ← MIN[Scale[dest.scaleFactor,x2],y+Scale[dest.scaleFactor,b.nBits]-1];x2 ← MIN[t,x+Scale[dest.scaleFactor,b.nLines]-1];
sy ← Scale[dest.scaleFactor,sx];sx ← Scale[dest.scaleFactor,dy];
END;

--first, erase
BLT.sourcetype ← block;
BLT.function ← erase;
BLT.dlbca ← MakeLongPointer[dest.bits,dest.bank];
BLT.dbmr ← dest.nWords;
BLT.dlx ← InlineDefs.BITSHIFT[x,logBitsPerPixel];
BLT.dty ← y;
BLT.dw ← InlineDefs.BITSHIFT[(x2+1-x),logBitsPerPixel];
BLT.dh ← (y2+1-y);
BLT.slbca ← MakeLongPointer[b.bits,b.bank];
BLT.sbmr ← b.nWords;
BLT.slx ← sx;
BLT.sty ← sy;
LongDefs.BITBLT[BLT];

--now, paint gray bits
BLT.sourcetype ← andgray;
BLT.function ← paint;
LongDefs.BITBLT[BLT];
END;

TransferRectangle: PUBLIC PROCEDURE [src,dest: POINTER TO GraphicsDefs.Bitmap,
srcRectangle,destRectangle: POINTER TO GraphicsDefs.Rectangle,
fn: BitBltDefs.BBoperation ← replace,type: BitBltDefs.BBsourcetype ← block] =
BEGIN
srcWidth,destWidth,srcHeight,destHeight: CARDINAL;
myBitmap: GraphicsDefs.Bitmap;
myRectangle: GraphicsDefs.Rectangle;
svd: POINTER TO GraphicsDefs.Bitmap ← NIL;
svdRectangle: POINTER TO GraphicsDefs.Rectangle;
x,y,count: CARDINAL;
dy: CARDINAL ← 0;
logBitsPerPixel: CARDINAL ← SELECT dest.nBitsPerPixel FROM 1=>0,2=>1,4=>2,ENDCASE=>3;
funnyFinishProc: PROCEDURE =
BEGIN
BLT.slbca ← MakeLongPointer[dest.bits,dest.bank];
BLT.dlbca ← MakeLongPointer[svd.bits,svd.bank];
BLT.dbmr ← svd.nWords;
BLT.slx ← BLT.sty ← 0;
BLT.dlx ← svdRectangle.x1;
BLT.dty ← svdRectangle.y1;
BLT.dw ← destWidth;
BLT.dh ← destHeight;
BLT.sbmr ← dest.nWords;
BLT.function ← fn;
BLT.sourcetype ← type;
LongDefs.BITBLT[BLT];
SystemDefs.FreeSegment[dest.bits];
END;

--first check trivial exclusion
IF
(srcRectangle.x1 > INTEGER[src.nBits]) OR
(INTEGER[srcRectangle.x2] < 0) OR
(srcRectangle.y1 > INTEGER[src.nLines]) OR
(INTEGER[srcRectangle.y2] < 0) OR
(INTEGER[destRectangle.x2] < 0) OR
(INTEGER[destRectangle.y2] < 0) THEN
BEGIN
gotcha: BOOLEAN;
gotcha ← TRUE;
RETURN;
END;

--clipping for safety, not accuracy (will cause distortion...)
IF CARDINAL[destRectangle.x1] >= dest.nBits OR
CARDINAL[destRectangle.y1] >= dest.nLines THEN RETURN;
IF CARDINAL[destRectangle.x2] >= dest.nBits THEN
destRectangle.x2 ← dest.nBits-1;
IF CARDINAL[destRectangle.y2] >= dest.nLines THEN
BEGIN
dy ← destRectangle.y2-(dest.nLines-1);
destRectangle.y2 ← dest.nLines-1;
END;

srcWidth ← (srcRectangle.x2 - srcRectangle.x1)+1;
destWidth ← (destRectangle.x2 - destRectangle.x1)+1;
srcHeight ← (srcRectangle.y2 - srcRectangle.y1)+1;
destHeight ← (destRectangle.y2 - destRectangle.y1)+1;

BLT.sourcetype ← type;
BLT.function ← fn;
BLT.dlbca ← MakeLongPointer[dest.bits,dest.bank];
BLT.dbmr ← dest.nWords;
BLT.slbca ← MakeLongPointer[src.bits,src.bank];
BLT.sbmr ← src.nWords;
IF srcWidth = destWidth AND srcHeight = destHeight THEN --easy
BEGIN
x1: CARDINAL ← destRectangle.x1;
y1: CARDINAL ← destRectangle.y1;
x2: CARDINAL ← destRectangle.x2;
y2: CARDINAL ← destRectangle.y2;
sx: CARDINAL ← srcRectangle.x1;
sy: CARDINAL ← srcRectangle.y1;

IF INTEGER[x1] < 0 THEN BEGIN sx ← sx-x1;x1 ← 0;END;
IF INTEGER[y1] < 0 THEN BEGIN sy ← sy-y1;y1←0;END;
IF dest.portraitMode THEN
BEGIN
x1 ← Scale[dest.scaleFactor,x1];y1 ← Scale[dest.scaleFactor,y1];x2 ← Scale[dest.scaleFactor,x2];y2 ← Scale[dest.scaleFactor,y2];
BLT.slx ← InlineDefs.BITSHIFT[Scale[dest.scaleFactor,sx],logBitsPerPixel];
BLT.sty ← Scale[dest.scaleFactor,sy];
END
ELSE
BEGIN t: CARDINAL ← Scale[dest.scaleFactor,(dest.nLines-1)-y1];y1 ← Scale[dest.scaleFactor,x1];
x1 ← Scale[dest.scaleFactor,(dest.nLines-1)-y2];y2 ← Scale[dest.scaleFactor,x2];x2 ← t;
BLT.slx ←
InlineDefs.BITSHIFT[Scale[dest.scaleFactor,dy+(src.nLines-1)-srcRectangle.y2],logBitsPerPixel];
BLT.sty ← Scale[dest.scaleFactor,sx];
IF INTEGER[BLT.slx] < 0 THEN
BEGIN
x1 ← InlineDefs.BITSHIFT[x1-BLT.slx,-logBitsPerPixel];BLT.slx←0;
END;
END;

BLT.dw ← InlineDefs.BITSHIFT[(x2+1-x1),logBitsPerPixel];
BLT.dh ← (y2+1-y1);
BLT.dlx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
BLT.dty ← y1;
LongDefs.BITBLT[BLT];
RETURN;
END; --easy case

--rescaling case
BLT.function ← replace;
BLT.sourcetype ← block;
IF (destHeight < srcHeight)
OR (fn # replace) OR (type # block) THEN
BEGIN
svd ← dest;
dest ← @myBitmap;
dest.bank ← 0;
dest.nWords ← (destWidth+15)/16;
--note: if fn = paint:
--
1) we could allocate ((..)*srcHeight), and be smarter later
--
2) we could ignore this if destHeight=srcHeight
dest.nLines ← destHeight;
dest.bits ← SystemDefs.AllocateSegment[((destWidth+15)/16)*
MAX[srcHeight,destHeight]];
dest.nBitsPerPixel ← svd.nBitsPerPixel;
BLT.dbmr ← dest.nWords;
BLT.dlbca ← dest.bits;

myRectangle ← [0,0,destWidth-1,destHeight-1];
svdRectangle ← destRectangle;
destRectangle ← @myRectangle;
END;

BLT.dty ← destRectangle.y1;
BLT.dw ← dest.nBitsPerPixel;
BLT.dh ← srcHeight;
BLT.slx ← srcRectangle.x1;
BLT.sty ← srcRectangle.y1;

count ← 0;
FOR x IN [CARDINAL[destRectangle.x1]..CARDINAL[destRectangle.x2]] DO
BLT.dlx ← x;
LongDefs.BITBLT[BLT];
count ← count+srcWidth;
IF count >= destWidth THEN
DO
BLT.slx ← BLT.slx + 1;
count ← count - destWidth;
IF count < destWidth THEN BEGIN BLT.function←replace;EXIT;END;
BLT.function←paint;
LongDefs.BITBLT[BLT];
ENDLOOP;
ENDLOOP;
IF destHeight = srcHeight THEN
BEGIN
IF svd # NIL THEN funnyFinishProc[];
RETURN;
END;

BLT.dlx ← destRectangle.x1;
BLT.dw ← destWidth;
BLT.dh ← 1;
BLT.slx ← destRectangle.x1;
BLT.sbmr ← BLT.dbmr;
BLT.slbca ← BLT.dlbca;
count ← 0;
IF destHeight > srcHeight THEN
BEGIN
BLT.sty ← destRectangle.y1 + srcHeight-1;
FOR y DECREASING IN [CARDINAL[destRectangle.y1]..CARDINAL[destRectangle.y2]] DO
BLT.dty ← y;
LongDefs.BITBLT[BLT];
count ← count + srcHeight;
IF count >= destHeight THEN
BEGIN
BLT.sty ← BLT.sty-1;
count ← count - destHeight;
END;
ENDLOOP;
END;
IF destHeight < srcHeight THEN
BEGIN
BLT.sty ← destRectangle.y1;
FOR y IN [CARDINAL[destRectangle.y1]..CARDINAL[destRectangle.y2]] DO
BLT.dty ← y;
LongDefs.BITBLT[BLT];
count ← count + srcHeight;
DO
BLT.sty ← BLT.sty+1;
count ← count - destHeight;
IF count < destHeight THEN BEGIN BLT.function←replace;EXIT;END;
BLT.function←paint;
LongDefs.BITBLT[BLT];
ENDLOOP;
ENDLOOP;
END;
IF svd # NIL THEN funnyFinishProc[];
END;

END.