-- ColorDeviceImpl.mesa
-- Last changed by Maureen Stone October 18, 1982 1:26 pm
-- Last changed by Doug Wyatt, September 1, 1982 3:50 pm

DIRECTORY
ColorDisplay,
GraphicsBasic USING [Box, Color, Trap, Vec],
GraphicsColor USING [blue, cyan, green, magenta, red, white, yellow],
CGArea USING [Empty, Ref, Remove],
ColorDevice USING [], -- only for EXPORTS
CGDevice USING [Ref, Rep],
CGMatrix USING [Inv, InvRel, IsTrivial, Make, Ref],
CGSource USING [Mode, Ref, Type],
CGStorage USING [qZone],
ColorMap USING [GetGrayTable, GrayTable, GetIndex, GrayRec],
CGColor USING [GetStipple],
Inline USING [BITAND, BITOR, BITSHIFT, BITXOR,
HighHalf, LongMult, LongNumber, LowByte, LowHalf],
BitBlt,
Real USING [Fix, FixC, RoundC];

ColorDeviceImpl: MONITOR
IMPORTS CGArea, CGColor, CGMatrix, CGStorage, BitBlt, Inline, Real,
CD: ColorDisplay, ColorMap
EXPORTS ColorDevice, GraphicsBasic = {
OPEN Area: CGArea, Matrix: CGMatrix, Source: CGSource,
CGDevice, GraphicsBasic;

DeviceObject: PUBLIC TYPE = CGDevice.Rep; -- exported to GraphicsBasic
Byte: TYPE = [0..256);

dataZone: ZONE = CGStorage.qZone;
repZone: ZONE = CGStorage.qZone;

Error: PUBLIC SAFE ERROR = CODE;
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BitBltTablePtr ← BitBlt.AlignedBBTable[@bbspace];

Data: TYPE = REF DataRep;
DataRep: TYPE = RECORD [
width,height: CARDINAL ← 0,
grayTable: ColorMap.GrayTable ← NIL,
lastColor: Color ← [tag: rgb, r: 0, g: 0, b: 0],
lastPixel: LONG CARDINAL,
stipple: CARDINAL ← 0,
--base and words per line all available thru ColorDisplay
matrix: Matrix.Ref ← NIL -- base transformation matrix
];

New: PUBLIC SAFE PROC[raster, height: CARDINAL ← 0] RETURNS[CGDevice.Ref] = CHECKED {
data: Data ← dataZone.NEW[DataRep];
IF raster = 0 THEN data.width ← CD.width ELSE data.width ← raster;
IF height = 0 THEN data.height ← CD.height ELSE data.height ← height;
data.grayTable ← ColorMap.GetGrayTable[];
data.matrix ← Matrix.Make[[1,0,0,-1,0,data.height]]; --should be a function of pixelsPerInch
RETURN[repZone.NEW[CGDevice.Rep ← [
GetMatrix: GetMatrix, GetBounds: GetBounds, Show: Show,
GetRaster: GetRaster, MoveBlock: MoveBlock, data: data]]];
};

GetMatrix: SAFE PROC[self: Ref] RETURNS[Matrix.Ref] = CHECKED {
data: Data ← NARROW[self.data];
RETURN[data.matrix];
};

GetBounds: SAFE PROC[self: Ref] RETURNS[Box] = CHECKED {
data: Data ← NARROW[self.data];
w: CARDINAL ← data.width;
h: CARDINAL ← data.height;
e: REAL = 0.1; -- small fudge factor
RETURN[[xmin: e, ymin: e, xmax: w-e, ymax: h-e]];
};

--bitmap speedup hack, not valid for color display
GetRaster: SAFE PROC[self: Ref] RETURNS[LONG POINTER,CARDINAL] = CHECKED {
RETURN[NIL,0] };

Show: ENTRY SAFE PROC[self: Ref, area: Area.Ref, src: Source.Ref, map: Matrix.Ref] = TRUSTED {
data: Data ← NARROW[self.data];
bitmap: BOOLEAN ← (src.type=array AND src.bps=0 AND CGMatrix.IsTrivial[map]);
invertHack: LONG CARDINAL ← data.lastPixel;
IF CD.curmode=CD.disconnected THEN RETURN; --colordisplay is disconnected
IF src.mode=invert THEN {
  IF CD.curmode.full THEN data.lastPixel ← -1 ELSE data.lastPixel ← 255}
ELSE SetColor[data,src.color]; --sets up the values for color
IF NOT CD.curmode.full AND src.bps >0 AND ~src.raw
THEN SetGrayTable[data,src.bps]; --sets up a lookup table for source grays
UNTIL Area.Empty[area] DO
trap: Trap ← Area.Remove[area];
IF bitmap AND trap.xbotL=trap.xtopL AND trap.xbotR=trap.xtopR THEN {
box: GraphicsBasic.Box ← [trap.xbotL, trap.ybot, trap.xbotR, trap.ytop];
ShowBitmap[data,box,src,map] }
ELSE ShowTrap[data,trap,src,map];
ENDLOOP;
IF src.mode=invert THEN data.lastPixel ← invertHack;
};

SetColor: PROC [data: Data, color: GraphicsBasic.Color] = {
IF color = data.lastColor THEN RETURN;
IF color.tag = stipple THEN data.stipple ← CGColor.GetStipple[color];
IF CD.curmode.full THEN data.lastPixel ← Pack[color.r,color.g,color.b]
ELSE data.lastPixel ← ColorMap.GetIndex[color.r,color.g,color.b];
data.lastColor ← color;
};

SetGrayTable: PROC [data: Data, srcBps: CARDINAL] = {
cmapGrays: ColorMap.GrayTable ← ColorMap.GetGrayTable[];
slen: REAL ← TwoToThe[srcBps];
scaler: REAL ← 255.0/(slen-1);
IF data.grayTable=NIL OR data.grayTable.length#slen
THEN data.grayTable ← NEW[ColorMap.GrayRec[TwoToThe[srcBps]]];
FOR i: CARDINAL IN [0..data.grayTable.length) DO
data.grayTable[i] ← cmapGrays[Real.RoundC[i*scaler]];
ENDLOOP;
};

--v is assumed to be 8 bps or less
TwoToThe: PROC[power: INTEGER] RETURNS[INTEGER] = {
RETURN[Inline.BITSHIFT[value: 2, count: power-1]];
};

GetBBColor: PROC[color: CARDINAL, bps: CARDINAL] RETURNS [CARDINAL] = {
grey,temp: CARDINAL ← 0;
IF color#0 THEN {
SELECT bps FROM
1 => temp ← Inline.BITAND[color,mask1];
2 => temp ← Inline.BITAND[color,mask2];
4 => temp ← Inline.BITAND[color,mask4];
8 => temp ← Inline.BITAND[color,mask8];
ENDCASE => ERROR Error;
FOR b: CARDINAL IN [0..16/bps) DO
  grey ← Inline.BITOR[temp,grey];
temp ← Inline.BITSHIFT[temp,bps];
ENDLOOP;
};
RETURN[grey];
};

Line1: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..1B]];
Line2: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..3B]];
Line4: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..17B]];
Line8: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF [0..377B]];
Line16: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF CARDINAL];

--these routines put stuff in the bitmap
--assume that s is already masked

Put: PROC[dbase: LONG POINTER, drast: CARDINAL, x,y: CARDINAL, v: LONG CARDINAL] = {
xline: LONG POINTER ← dbase + Inline.LongMult[y,drast];
IF CD.curmode.full THEN {
pair: CD.Pair ← Inline.LowHalf[v];
blue: Byte ← Inline.LowByte[Inline.HighHalf[v]];
CD.SetPixel[x,y,pair.r,pair.g,blue];
}
ELSE {
short: CARDINAL ← Inline.LowHalf[v];
SELECT CD.curmode.bitsPerPixelA FROM
1 => LOOPHOLE[xline,LONG POINTER TO Line1][x] ← Inline.BITAND[short,mask1];
2 => LOOPHOLE[xline,LONG POINTER TO Line2][x] ← Inline.BITAND[short,mask2];
4 => LOOPHOLE[xline,LONG POINTER TO Line4][x] ← Inline.BITAND[short,mask4];
8 => LOOPHOLE[xline,LONG POINTER TO Line8][x] ← Inline.BITAND[short,mask8];
ENDCASE => ERROR Error;
};
};

--"transparent mode"
Transparent: PROC[dbase: LONG POINTER, drast: CARDINAL, x,y: CARDINAL, value: LONG CARDINAL] = {
xline: LONG POINTER ← dbase + Inline.LongMult[y,drast];
IF CD.curmode.full THEN {
pair: CD.Pair ← Inline.LowHalf[value];
blue: Byte ← Inline.LowByte[Inline.HighHalf[value]];
r,g,b: Byte;
[r,g,b] ← CD.GetPixel[x,y];
pair.r ← MIN[pair.r+r,255];
pair.g ← MIN[pair.g+g,255];
blue ← MIN[blue+b,255];
CD.SetPixel[x,y,pair.r,pair.g,blue];
}
ELSE SELECT CD.curmode.bitsPerPixelA FROM
1 => IF value=1 THEN LOOPHOLE[xline,LONG POINTER TO Line1][x] ← value;
2 => {v: CARDINALLOOPHOLE[xline,LONG POINTER TO Line2][x];
v ← MIN[v+value,3];
LOOPHOLE[xline,LONG POINTER TO Line2][x] ← v};
4 => {v: CARDINALLOOPHOLE[xline,LONG POINTER TO Line4][x];
v ← MIN[v+value,15];
LOOPHOLE[xline,LONG POINTER TO Line4][x] ← v};
8 => {v: CARDINALLOOPHOLE[xline,LONG POINTER TO Line8][x];
v ← MIN[v+value,255];
LOOPHOLE[xline,LONG POINTER TO Line8][x] ← v};
ENDCASE => ERROR Error;
};

--"invert mode"
Invert: PROC[dbase: LONG POINTER, drast: CARDINAL, x,y: CARDINAL] = {
xline: LONG POINTER ← dbase + Inline.LongMult[y,drast];
IF CD.curmode.full THEN {
r,g,b: Byte;
temp: Byte ← 377B;
[r,g,b] ← CD.GetPixel[x,y];
r ← Inline.BITXOR[temp,r];
g ← Inline.BITXOR[temp,g];
b ← Inline.BITXOR[temp,b];
CD.SetPixel[x,y,r,g,b];
}
ELSE {
temp: CARDINAL ← 177777B;
SELECT CD.curmode.bitsPerPixelA FROM
1 => {v: CARDINALLOOPHOLE[xline,LONG POINTER TO Line1][x];
v ← Inline.BITXOR[v,temp]; v ← Inline.BITAND[v,mask1];
LOOPHOLE[xline,LONG POINTER TO Line2][x] ← v};
2 => {v: CARDINALLOOPHOLE[xline,LONG POINTER TO Line2][x];
v ← Inline.BITXOR[v,temp]; v ← Inline.BITAND[v,mask2];
LOOPHOLE[xline,LONG POINTER TO Line2][x] ← v};
4 => {v: CARDINALLOOPHOLE[xline,LONG POINTER TO Line4][x];
v ← Inline.BITXOR[v,temp]; v ← Inline.BITAND[v,mask4];
LOOPHOLE[xline,LONG POINTER TO Line4][x] ← v};
8 => {v: CARDINALLOOPHOLE[xline,LONG POINTER TO Line8][x];
v ← Inline.BITXOR[v,temp]; v ← Inline.BITAND[v,mask8];
LOOPHOLE[xline,LONG POINTER TO Line8][x] ← v};
ENDCASE => ERROR Error;
};
};

--this gets a source sample from an array of values
AGet: PROC[src: Source.Ref, x,y: CARDINAL] RETURNS[value: LONG CARDINAL] = {
xline: LONG POINTER ← src.xbase + Inline.LongMult[y,src.xrast];
SELECT src.bps FROM
0 => value ← LOOPHOLE[xline,LONG POINTER TO Line1][x];
1 => value ← LOOPHOLE[xline,LONG POINTER TO Line1][x];
2 => value ← LOOPHOLE[xline,LONG POINTER TO Line2][x];
4 => value ← LOOPHOLE[xline,LONG POINTER TO Line4][x];
8 => value ← LOOPHOLE[xline,LONG POINTER TO Line8][x];
ENDCASE => ERROR Error;
};

mask1: CARDINAL = 1B;
mask2: CARDINAL = 3B;
mask4: CARDINAL = 17B;
mask8: CARDINAL = 377B;

--this converts from a bps bit array value to a 24 bps color
FullTint: PROC[in: LONG CARDINAL, bps: INTEGER, color: GraphicsBasic.Color] RETURNS[out: LONG CARDINAL] = {OPEN GraphicsColor;
  intensity: REAL ← REAL[in]/(TwoToThe[bps]);
v: Byte ← Real.RoundC[intensity*255];
SELECT color FROM
red => out ← Pack[v,0,0];
green => out ← Pack[0,v,0];
blue => out ← Pack[0,0,v];
magenta => out ← Pack[v,0,v];
cyan => out ← Pack[0,v,v];
yellow => out ← Pack[v,v,0];
white => out ← Pack[v,v,v];
ENDCASE => {
ir: Byte ← Real.RoundC[intensity*color.r];
ig: Byte ← Real.RoundC[intensity*color.g];
ib: Byte ← Real.RoundC[intensity*color.b];
out ← Pack[ir,ig,ib];
};
RETURN[out];
};


white: LONG CARDINAL ← Pack[377B,377B,377B];
black: LONG CARDINAL ← 0;
Pack: PROC[r,g,b: Byte] RETURNS [LONG CARDINAL] = {
rg: CD.Pair;
out: Inline.LongNumber ← [num[0,0]];
rg.r ← r; rg.g ← g;
out.lowbits ← LOOPHOLE[rg,CARDINAL];
out.highbits ← LOOPHOLE[b,CARDINAL];
RETURN[out.lc];
};

Masks: TYPE = ARRAY [0..16) OF WORD;
masksArray: Masks ← [
100000B, 040000B, 020000B, 010000B, 004000B, 002000B, 001000B, 000400B,
000200B, 000100B, 000040B, 000020B, 000010B, 000004B, 000002B, 000001B];
masks: POINTER TO Masks ← @masksArray;

ShowBitmap: PROC[data: Data, rect: Box, src: CGSource.Ref, map: CGMatrix.Ref] = {
type: CGSource.Type ← src.type;
mode: CGSource.Mode ← src.mode;
xmin, xmax, ymin, ymax: NAT;
xmin ← Rnd[rect.xmin]; xmax ← Rnd[rect.xmax];
ymin ← Rnd[rect.ymin]; ymax ← Rnd[rect.ymax];
IF NOT(xmin<xmax AND ymin<ymax) THEN RETURN; -- nothing to do
IF type=array AND src.bps=0 AND CGMatrix.IsTrivial[map] THEN {
pixel: LONG CARDINAL ← data.lastPixel;
tx: INTEGER ← RndI[map.m.e];
ty: INTEGER ← RndI[map.m.f];
-- map transforms source coordinates to device coordinates
-- Here, we know map is a simple translation, so subtracting map's
-- translation elements will take us from device to source
sxmin: INTEGER ← xmin - tx;
symin: INTEGER ← ymin - ty;
sBase: LONG POINTER ← src.xbase;
sRast: CARDINAL ← src.xrast;
sLine: LONG POINTER ← sBase + Inline.LongMult[symin, sRast];
y: CARDINAL ← ymin;
cxmin: INTEGERCD.cursorPosition.x;
cymin: INTEGERCD.cursorPosition.y;
cxmax: INTEGER ← cxmin+CD.cursorWidth;
cymax: INTEGER ← cymin+CD.cursorHeight;
hidden: BOOLEAN ← (cxmax>xmin AND cxmin<xmax AND cymax>ymin AND cymin<ymax);
CD.SetLock[];
IF hidden THEN CD.HideCursor[];
IF CD.curmode.full THEN {
baseA: LONG POINTERCD.baseA;
wplA: CARDINALCD.wplA;
baseB: LONG POINTERCD.baseB;
wplB: CARDINALCD.wplB;
lineA: LONG POINTER TO Line16 ← baseA+Inline.LongMult[y, wplA];
lineB: LONG POINTER TO Line8 ← baseB+Inline.LongMult[y, wplB];
pixel1A: CARDINAL ← Inline.LowHalf[data.lastPixel];
pixel0A: CARDINAL ← Inline.LowHalf[white];
pixel1B: [0..256) ← Inline.HighHalf[data.lastPixel] MOD 256;
pixel0B: [0..256) ← Inline.HighHalf[white] MOD 256;
DO
sAddr: LONG POINTER TO CARDINAL ← sLine+sxmin/16;
sWord: CARDINAL ← sAddr^;
si: CARDINAL ← sxmin MOD 16;

SELECT mode FROM
opaque => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0
THEN { lineA[x] ← pixel1A; lineB[x] ← pixel1B }
ELSE { lineA[x] ← pixel0A; lineB[x] ← pixel0B };
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
transparent => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0
THEN { lineA[x] ← pixel1A; lineB[x] ← pixel1B };
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
invert => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN {
lineA[x] ← Inline.BITXOR[lineA[x],177777B];
lineB[x] ← Inline.BITXOR[lineB[x],377B] };
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
ENDCASE;

IF (y ← y+1)=ymax THEN EXIT;
sLine ← sLine + sRast; -- next source line
lineA ← lineA + wplA; lineB ← lineB + wplB; -- next destination lines
ENDLOOP;
}
ELSE IF CD.curmode.bitsPerPixelA=8 THEN {
baseA: LONG POINTERCD.baseA;
wplA: CARDINALCD.wplA;
lineA: LONG POINTER TO Line8 ← baseA+Inline.LongMult[y, wplA];
pixel1: [0..256) ← Inline.LowHalf[data.lastPixel] MOD 256;
pixel0: [0..256) ← Inline.LowHalf[white] MOD 256;
DO
sAddr: LONG POINTER TO CARDINAL ← sLine+sxmin/16;
sWord: CARDINAL ← sAddr^;
si: CARDINAL ← sxmin MOD 16;

SELECT mode FROM
opaque => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN lineA[x] ← pixel1 ELSE lineA[x] ← pixel0;
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
transparent => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN lineA[x] ← pixel1;
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
invert => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN lineA[x] ← Inline.BITXOR[lineA[x],377B];
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
ENDCASE;

IF (y ← y+1)=ymax THEN EXIT;
sLine ← sLine + sRast; -- move to next source line
lineA ← lineA + wplA; -- move to next destination line
ENDLOOP;
}
ELSE IF CD.curmode.bitsPerPixelA=4 THEN {
baseA: LONG POINTERCD.baseA;
wplA: CARDINALCD.wplA;
lineA: LONG POINTER TO Line4 ← baseA+Inline.LongMult[y, wplA];
pixel1: [0..16) ← Inline.LowHalf[data.lastPixel] MOD 16;
pixel0: [0..16) ← Inline.LowHalf[white] MOD 16;
DO
sAddr: LONG POINTER TO CARDINAL ← sLine+sxmin/16;
sWord: CARDINAL ← sAddr^;
si: CARDINAL ← sxmin MOD 16;

SELECT mode FROM
opaque => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN lineA[x] ← pixel1 ELSE lineA[x] ← pixel0;
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
transparent => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN lineA[x] ← pixel1;
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
invert => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN lineA[x] ← Inline.BITXOR[lineA[x],17B];
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
ENDCASE;

IF (y ← y+1)=ymax THEN EXIT;
sLine ← sLine + sRast; -- move to next source line
lineA ← lineA + wplA; -- move to next destination line
ENDLOOP;
}
ELSE{
dbase: LONG POINTERCD.baseA;
drast: CARDINALCD.wplA;
pixel: LONG CARDINAL ← data.lastPixel;
DO
sAddr: LONG POINTER TO CARDINAL ← sLine+sxmin/16;
sWord: CARDINAL ← sAddr^;
si: CARDINAL ← sxmin MOD 16;

SELECT mode FROM
opaque => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN Put[dbase, drast, x, y, pixel]
ELSE Put[dbase, drast, x, y, white];
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
transparent => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN Put[dbase, drast, x, y, pixel];
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
invert => FOR x: CARDINAL IN[xmin..xmax) DO
IF Inline.BITAND[sWord, masks[si]]#0 THEN Invert[dbase, drast, x, y];
IF (si ← si+1)=16 THEN { sWord ← (sAddr ← sAddr+1)^; si ← 0 };
ENDLOOP;
ENDCASE;

IF (y ← y+1)=ymax THEN EXIT;
sLine ← sLine + sRast; -- move to next source line
-- table.dLine ← table.dLine+drast;
ENDLOOP;
};
CD.ClearLock[];
CD.RestoreCursor[];
}
ELSE ERROR;
};

ShowTrap: PROC[data: Data, trap: Trap, src: Source.Ref, map: Matrix.Ref] = {
easy: BOOLEANIF src.mode=invert OR (src.type=const AND src.mode=opaque) THEN TRUE ELSE FALSE;
yb,yt,yl,xl,xr: CARDINAL ← 0;
rdxl,rdxr: REAL ← 0; -- left and right x increments
rsdx,rsdy: REAL ← 0; -- increments for source position
fsdx,fsdy: Frac;
dbase: LONG POINTER;
drast: CARDINAL;
fat: BOOLEAN ← src.fat;
stipple: BOOLEAN ← (data.lastColor.tag=stipple);
DoLine: PROC[xmin,xmax,y: CARDINAL] = {
cx: INTEGERCD.cursorPosition.x;
cy: INTEGERCD.cursorPosition.y;
CD.SetLock[];
IF INTEGER[y] IN [cy..cy+CD.cursorHeight] AND
NOT (cx+CD.cursorWidth<INTEGER[xmin] OR cx>INTEGER[xmax])
THEN CD.HideCursor[];
IF stipple OR src.type=tile THEN StippleLine[xmin,xmax,y] --the altoworld special case
ELSE {IF easy
THEN EasyLine[xmin,xmax,y] --split here for easy and hard lines
ELSE HardLine[xmin,xmax,y]};
CD.ClearLock[];
CD.RestoreCursor[];
};

EasyLine: PROC[xmin,xmax,y: CARDINAL] = { --solid colors only
gray,bpp,ppw: CARDINAL ← 0;
bb^ ← [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: @gray, bit: 0],
srcDesc: [gray[gray: [yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]],
width: 0, height: 1, flags: [disjoint: TRUE, gray: TRUE, srcFunc: null,
dstFunc: IF src.mode=invert THEN xor ELSE null]];
IF CD.curmode.full THEN {
bpp ← 16;
ppw ← 1;
gray ← Inline.LowHalf[data.lastPixel]; --red and green together
}
ELSE {
bpp ← CD.curmode.bitsPerPixelA;
ppw ← 16/bpp;
gray ← GetBBColor[data.lastPixel,bpp]; --colormap index
};
bb.dst.word ← CD.baseA + Inline.LongMult[y,CD.wplA] + xmin/ppw;
bb.dst.bit ← (xmin MOD ppw)*bpp;
bb.dstBpl ← CD.wplA*16;
bb.width ← (xmax - xmin)*bpp;
BitBlt.BITBLT[bb];
IF CD.curmode.full THEN {
blue: CARDINAL ← Inline.HighHalf[data.lastPixel];
bpp ← 8;
ppw ← 2;
gray ← GetBBColor[Inline.LowByte[blue],bpp]; --get the blue color
bb.dst.word ← CD.baseB + Inline.LongMult[y,CD.wplB] + xmin/ppw;
bb.dst.bit ← (xmin MOD ppw)*bpp;
bb.dstBpl ← CD.wplB*16;
bb.width ← (xmax - xmin)*bpp;
BitBlt.BITBLT[bb];
};
}; 

StippleLine: PROC[xmin,xmax,y: CARDINAL] = {
base: CARDINALIF src.type=tile THEN 16 ELSE 4;
sx: CARDINAL;
yoff: CARDINAL ← base*(y MOD base);
bit: CARDINAL;
bits: LONG POINTER TO Line1 ←
IF src.type=tile THEN LOOPHOLE[src.xbase] ELSE LOOPHOLE[@data.stipple];
FOR x: CARDINAL IN[xmin..xmax) DO
sx ← x MOD base + yoff;
bit ← bits[sx];
IF bit=0 THEN {
IF src.mode=opaque THEN Put[dbase,drast,x,y,white]
ELSE LOOP;
}
ELSE{ --bit=1
IF src.mode=invert THEN Invert[dbase,drast,x,y]
ELSE Put[dbase,drast,x,y,data.lastPixel];
};
ENDLOOP;
};

HardLine: PROC[xmin,xmax,y: CARDINAL] = {
rsx,rsy: REAL; -- current source position
fsx,fsy: Frac;
sx,sy: CARDINAL; -- truncated source position
value: LONG CARDINAL;

IF src.type=array AND src.bps=0 THEN FOR x: CARDINAL IN[xmin..xmax) DO
xline: LONG POINTER;
bit: CARDINAL;
IF x=xmin THEN {
[[rsx,rsy]] ← Matrix.Inv[map,[x+0.5,y+0.5]];
fsx ← FixF[rsx]; fsy ← FixF[rsy];
}
ELSE {fsx ← AddF[fsx,fsdx]; fsy ← AddF[fsy,fsdy]};
xline ← src.xbase + Inline.LongMult[IntF[fsy],src.xrast];
bit ← LOOPHOLE[xline,LONG POINTER TO Line1][IntF[fsx]];
IF bit=0 THEN {
IF src.mode=opaque THEN Put[dbase,drast,x,y,white]
ELSE LOOP;
}
ELSE{ --bit=1
IF src.mode=invert THEN Invert[dbase,drast,x,y]
ELSE Put[dbase,drast,x,y,data.lastPixel];
};
ENDLOOP


ELSE FOR x: CARDINAL IN[xmin..xmax) DO
IF x=xmin THEN {
[[rsx,rsy]] ← Matrix.Inv[map,[x+0.5,y+0.5]];
fsx ← FixF[rsx]; fsy ← FixF[rsy];
}
ELSE {fsx ← AddF[fsx,fsdx]; fsy ← AddF[fsy,fsdy]};
-- Get a sample from the source
SELECT src.type FROM
proc => value ← src.Get[FltF[fsx],FltF[fsy]];
array => {
sv: LONG CARDINAL;
sx ← IntF[fsx];
sy ← IntF[fsy];
sv ← AGet[src,sx,sy];
IF CD.curmode.full THEN value ← FullTint[sv,src.bps,src.color]
ELSE value ← (IF src.raw THEN sv ELSE data.grayTable[sv]);
};
  const => value ← data.lastPixel;
  ENDCASE;
IF src.mode=transparent THEN Transparent[dbase,drast,x,y,value]
ELSE Put[dbase,drast,x,y,value];
ENDLOOP;
};

-- main code for PTrap starts here
-- compute yb (bottom of first scanline) and yt (top of last scanline)
dbase ← CD.baseA;
drast ← CD.wplA;
IF fat THEN { yb𡤋ot[trap.ybot]; yt←Top[trap.ytop] }
ELSE { yb←Rnd[trap.ybot]; yt←Rnd[trap.ytop] };
IF yb<yt THEN yl←yt-1 -- yl is last line
ELSE { IF yb>yt OR fat THEN ERROR Error; RETURN };
-- Setup for calls on DoLine
IF src.type=array THEN {
[[rsdx,rsdy]] ← Matrix.InvRel[map,[1,0]];
fsdx ← FixF[rsdx]; fsdy ← FixF[rsdy];
};
rdxl←(trap.xtopL-trap.xbotL); -- delta x, left
rdxr←(trap.xtopR-trap.xbotR); -- delta x, right
IF NOT(fat AND yb=yl) THEN {
rdy: REAL←trap.ytop-trap.ybot; -- delta y
rdxl←rdxl/rdy; rdxr←rdxr/rdy; -- dx/dy, left right
};
IF fat THEN {
rxlb,rxrb,rxlt,rxrt: REAL ← 0; -- x at bottom and top of current line
ltop: BOOLEAN ← rdxl<0; -- TRUE means leftmost x is at top of line
rtop: BOOLEAN ← rdxr>0; -- TRUE means rightmost x is at top of line
FOR y: CARDINAL IN[yb..yl] DO -- for each scan line
IF y=yb THEN { rxlb←trap.xbotL; rxrb←trap.xbotR } -- first line
ELSE { rxlb←rxlt; rxrb←rxrt }; -- successive lines
IF y=yl THEN { rxlt←trap.xtopL; rxrt←trap.xtopR } -- last line
ELSE IF y=yb THEN { -- first line, if yl>yb
d: REAL←(yb+1)-trap.ybot; -- distance to top of line
rxlt←rxlb+d*rdxl; rxrt←rxrb+d*rdxr }
ELSE { rxlt←rxlb+rdxl; rxrt←rxrb+rdxr }; -- middle lines
xl𡤋ot[IF ltop THEN rxlt ELSE rxlb];
xr←Top[IF rtop THEN rxrt ELSE rxrb];
IF xl<xr THEN DoLine[xmin: xl, xmax: xr, y: y];
ENDLOOP;
}
ELSE {
rxl,rxr: REAL ← 0; -- left and right x at middle of current line
FOR y: CARDINAL IN[yb..yl] DO -- for each scan line
IF y=yb THEN { -- first line
d: REAL←(yb+0.5)-trap.ybot; -- distance to middle of line
rxl←trap.xbotL+d*rdxl; rxr←trap.xbotR+d*rdxr }
ELSE { rxl←rxl+rdxl; rxr←rxr+rdxr }; -- successive lines
xl←Rnd[rxl]; xr←Rnd[rxr];
IF xl<xr THEN DoLine[xl,xr,y];
ENDLOOP;
};
};

allOnes: LONG CARDINAL = LAST[LONG CARDINAL];

Bot: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r]] };
Top: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r]+1] };
Fix: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r]] };
Rnd: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r+.5]] };
RndI: PROC[r: REAL] RETURNS[INTEGER] = INLINE {
RETURN[Real.FixC[r+(LAST[INTEGER]+.5)]-LAST[INTEGER]] };

Frac: TYPE = Inline.LongNumber;
fscl: Frac = [num[lowbits: 0, highbits: 1]];

FixF: PROC[r: REAL] RETURNS[Frac] = INLINE {
RETURN[[li[Real.Fix[r*fscl.li]]]] };

FltF: PROC[f: Frac] RETURNS[REAL] = INLINE {
r: REAL ← f.li; RETURN[r/fscl.li] };

AddF: PROC[a,b: Frac] RETURNS[Frac] = INLINE {
RETURN[[li[a.li+b.li]]] };

IntF: PROC[f: Frac] RETURNS[INTEGER] = INLINE {
RETURN[LOOPHOLE[f.highbits,INTEGER]] };

MoveBlock: SAFE PROC[self: Ref, width, height, fromX, fromY, toX, toY: NAT] = CHECKED {
data: Data ← NARROW[self.data];
wmax: CARDINAL ← data.width;
hmax: CARDINAL ← data.height;
IF CD.curmode=CD.disconnected THEN RETURN; --colordisplay is disconnected
IF width>0 AND height>0 AND
fromX<wmax AND fromY<hmax AND toX<wmax AND toY<hmax THEN TRUSTED {
sx: CARDINAL ← fromX;
sy: CARDINAL ← fromY;
dx: CARDINAL ← toX;
dy: CARDINAL ← toY;
w: CARDINALMIN[width, wmax-sx, wmax-dx];
h: CARDINALMIN[height, hmax-sy, hmax-dy];
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BitBltTablePtr ← BitBlt.AlignedBBTable[@bbspace];
bpp: CARDINAL ← IF CD.curmode.full THEN 16 ELSE CD.curmode.bitsPerPixelA;
ppw: CARDINAL ← 16/bpp;
bb^ ← [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0],
srcDesc: [srcBpl[0]], width: 0, height: 16,
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE,
   gray: FALSE, srcFunc: null, dstFunc: null]];
IF (sx+w)>dx AND (dx+w)>sx AND (sy+h)>dy AND (dy+h)>sy THEN {
bb.flags.disjoint ← FALSE; -- the rectangles overlap
IF dy=sy THEN bb.flags.disjointItems ← FALSE; -- so do the items
IF dy>sy OR (dy=sy AND dx>sx) THEN { -- reverse direction
bb.flags.direction ← backward; sy ← sy + (h-1); dy ← dy + (h-1);
};
};
bb.dst.word ← CD.baseA + Inline.LongMult[dy, CD.wplA] + dx/ppw;
bb.dst.bit ← (dx MOD ppw)*bpp;
bb.src.word ← CD.baseA + Inline.LongMult[sy, CD.wplA] + sx/ppw;
bb.src.bit ← (sx MOD ppw)*bpp;
bb.dstBpl ← bb.srcDesc.srcBpl ← CD.wplA*16;
bb.width ← w*bpp; bb.height ← h;
BitBlt.BITBLT[bb];
IF CD.curmode.full THEN {
bpp ← 8; ppw ← 2;
bb.dst.word ← CD.baseB + Inline.LongMult[dy,CD.wplB] + dx/ppw;
bb.dst.bit ← (dx MOD ppw)*bpp;
bb.src.word ← CD.baseB + Inline.LongMult[sy, CD.wplB] + sx/ppw;
bb.src.bit ← (sx MOD ppw)*bpp;
bb.dstBpl ← bb.srcDesc.srcBpl ← CD.wplB*16;
bb.width ← w*bpp;
BitBlt.BITBLT[bb];
};
};
};


}.