-- color screen drawing module of silicon (pretty picture) program
-- last modified by E. McCreight, January 31, 1984 4:27 PM
DIRECTORY
BitBltDefs,
ChipOrient,
InlineDefs,
IODefs,
AltoDefs,
AltoFileDefs,
MiscDefs,
multiGraphicsDefs,
SegmentDefs,
ProcessDefs,
ppCache,
ppdddefs,ppddefs,
ppoutdefs,
ppdefs;

ppdrawCol: PROGRAM
IMPORTS
BitBltDefs, ChipOrient, InlineDefs, MiscDefs,
multiGraphicsDefs, ppCache, ppdddefs, ppddefs, ppdefs, ppoutdefs, ProcessDefs,
SegmentDefs
EXPORTS ppdefs, ppddefs =
BEGIN
OPEN ppdefs, ppddefs, ppdddefs,InlineDefs, multiGraphicsDefs,
SegmentDefs, IODefs;

cScale: PUBLIC INTEGER ← 9;
cScaleN: PUBLIC INTEGER ← 1;
cScaleD: PUBLIC INTEGER ← 1;
cxoff, cyoff: PUBLIC INTEGER ← 0;
selColOffset: CARDINAL ← 16;

cClipx1: PUBLIC INTEGER ← 0;
cClipx2: PUBLIC INTEGER ← xColorMax;
cClipy1: PUBLIC INTEGER ← 0;
cClipy2: PUBLIC INTEGER ← yColorMax;

-- Colors! note, in the array "colors", color 0 is at top, 15 at bottom
-- R G B

--Colors 0:background
1:grn2:blue
--
3: blue & grn4:red5:red & grn
--
6:red & blue7:red & grn & blue8:ovg cut:maroon?
--
9:contact cut: black10:sel.outln,curs,wht11:burr?
--
12:outline undrawn cell13:?14:yellow (dots)
--
15:outline - white (also dots, curs?)

cTabCnt: PUBLIC CARDINAL;
colorTabs: PUBLIC LONG POINTER TO ARRAY [0..0) OF ARRAY [0..49) OF CARDINAL ←
LOOPHOLE[1322400B];
currentCTab: PUBLIC CARDINAL;
anyCTChanges: PUBLIC BOOLEAN ← FALSE;

background: Color = 0;
maroon: Color = 8;

black: Color ← background; -- reset every time we repaint

Black: PROC RETURNS [Color] =
BEGIN
dimmestColor: Color ← 0;
dimmest: INTEGER ← 1000;
FOR color: [0..15] IN [0..15] DO
intensity: INTEGER ← 0;
FOR j: [0..2] IN [0..2] -- r, g, b -- DO
intensity ← intensity-getCurrenColor[color, j];
-- getCurrenColor returns dimmer more positive than brighter
ENDLOOP;
IF intensity<dimmest THEN {dimmestColor ← color; dimmest ← intensity};
ENDLOOP;
RETURN[dimmestColor];
END;

white: Color = 15;

-- orLtab: PUBLIC ARRAY level OF Color ← [1231B, 1020B, 1004B,
--1040B, 76B, 8, 33B, 10,
--
9,1,10,62B,10,8,10,10];
orLtab: PUBLIC ARRAY level OF Color ← [11B, 1, 4, 2, 76B, 8, 33B, 10,
9,1,10,62B,10,8,10,10];
showColorLevel: PUBLIC ARRAY level OF BOOLEAN ← ALL[TRUE];

-- note: meaning of levels = cut,dif,pol,met,impl,ovg,?,?
-- note: color of levels = blk,grn,red,blu,yell,mron,?,?
-- note: color number of lev= 9 , 1 , 4 , 2 , 16B, 8 ,?,?

colPatternTabs:PUBLIC ARRAY [0..colPatNum) OF ARRAY level OF CARDINAL;
colPatternBits:PUBLIC ARRAY [0..colPatNum) OF CARDINAL←ALL[0];

offset: Point -- = [cxoff, cyoff] -- ;

cscale: PROCEDURE [x: locNum] RETURNS [Pixel] = INLINE
{RETURN[x*cScaleN/cScaleD]};

cscaleRect: PUBLIC PROCEDURE [x1, y1, x2, y2: INTEGER]
RETURNS [BOOLEAN, INTEGER, INTEGER, INTEGER, INTEGER] =
BEGIN -- assumes untranslated design space
t: INTEGER;
IF x1 > x2 THEN BEGIN t ← x1; x1 ← x2; x2 ← t; END;
IF y1 > y2 THEN BEGIN t ← y1; y1 ← y2; y2 ← t; END;
IF x1 > cClipx2 OR x2 < cClipx1 OR y1 > cClipy2 OR y2 < cClipy1 THEN
RETURN[FALSE, 0, 0, 0, 0];
RETURN[TRUE,
cscale[cxoff+MAX[x1, cClipx1]],
cscale[cyoff+MAX[y1, cClipy1]],
cscale[cxoff+MIN[x2, cClipx2]],
cscale[cyoff+MIN[y2, cClipy2]]];
END;

cClipScale: PROCEDURE [x1, y1, x2, y2: INTEGER, cr: POINTER TO Rect]
RETURNS [BOOLEAN, INTEGER, INTEGER, INTEGER, INTEGER] = INLINE
BEGIN -- assumes design space translated by offset
IF x1 > cr.x2 OR x2 < cr.x1 OR y1 > cr.y2 OR y2 < cr.y1 THEN
RETURN[FALSE, 0, 0, 0, 0];
RETURN[TRUE, cscale[MAX[x1, cr.x1]], cscale[MAX[y1, cr.y1]],
cscale[MIN[x2, cr.x2]], cscale[MIN[y2, cr.y2]]];
END;

outlineCol: PUBLIC PROCEDURE [
w, x, y, z: INTEGER, q: color, clip: POINTER TO Rect ← NIL] = {NULL};

-- Note: The following two procedures are thought to be
-- executed very often, and have therefore been optimized heavily. The code
-- has been taken from multiGraphicsGefs and cbitfns.

BLT: BLTBlockPtr ← AllocateBLT[];
destRaster: RasterDesc;
colBitsPerPixel: CARDINAL = 4; -- 4 bits/pixel
logBitsPerPixel: CARDINAL = 2;
levColorGray, levMaskGray: ARRAY level OF GrayPattern;

orColArea: PUBLIC PROCEDURE [
w, x, y, z: INTEGER, l: level, clip: POINTER TO Rect ← NIL] =
BEGIN
a, b, c, d: INTEGER;
bb: BOOLEAN;
-- w, x, y, and z form an oriented rectangle
[bb, a, b, c, d] ← cClipScale[w, x, y, z, clip];
IF bb AND showColorLevel[l] THEN
BEGIN
-- 0<=a<=c<= and 0<=b<=d
BLT.blk.function ← paint;
BLT.blk.dlx ← BITSHIFT[a, logBitsPerPixel];
BLT.blk.dty ← b;
BLT.blk.dw ← BITSHIFT[MAX[1, c-a], logBitsPerPixel];
BLT.blk.dh ← MAX[1, d-b];
SetupBitBltGray[BLT: BLT, gray: @levColorGray[l]];
BitBltDefs.BITBLT[@BLT.blk];
END;
END;

levMaskGrayPtrs: ARRAY level OF POINTER TO GrayPattern;

repColArea: PUBLIC PROCEDURE [
w, x, y, z: INTEGER, l: level, clip: POINTER TO Rect ← NIL] =
BEGIN
a, b, c, d: INTEGER;
bb: BOOLEAN;
IF clip = NIL THEN clip ← @universe;
[bb, a, b, c, d] ← cClipScale[w, x, y, z, clip];
IF bb AND showColorLevel[l] THEN
BEGIN
-- 0<=a<=c<= and 0<=b<=d
BLT.blk.dlx ← BITSHIFT[a, logBitsPerPixel];
BLT.blk.dty ← b;
BLT.blk.dw ← BITSHIFT[MAX[1, c-a], logBitsPerPixel];
BLT.blk.dh ← MAX[1, d-b];
IF levMaskGrayPtrs[l]#NIL THEN
BEGIN
BLT.blk.function ← erase;
SetupBitBltGray[BLT: BLT, gray: levMaskGrayPtrs[l]];
BitBltDefs.BITBLT[@BLT.blk];
BLT.blk.function ← paint;
END
ELSE BLT.blk.function ← replace;
SetupBitBltGray[BLT: BLT, gray: @levColorGray[l]];
BitBltDefs.BITBLT[@BLT.blk];
END;
END;

fuzzColWithBackground: PROC [r: Rect] = {fuzzOutColArea[r]};

fuzzColWithBlack: PROC [r: Rect] = {fuzzOutColArea[r, black]};

fuzzPattern: Color ← 64;
fuzzOutColArea: PUBLIC PROCEDURE [r: Rect, color: Color ← background,
clip: POINTER TO Rect ← NIL] =
BEGIN
background: CARDINAL = 0;
a, b, c, d: INTEGER;
bb: BOOLEAN;
[bb, a, b, c, d] ← cClipScale[r.x1, r.y1, r.x2, r.y2,
IF clip#NIL THEN clip ELSE @universe];
IF bb THEN
BEGIN
fuzzColorGray: GrayPattern ← GetColorGray[fuzzPattern+color];
fuzzMaskGray: GrayPattern ← GetMaskGray[fuzzPattern+color];
DoColorArea[a, b, c, d, replace, TRUE, @fuzzColorGray, @fuzzMaskGray];
END;
END;

backgroundGray: GrayPattern;

setSelColor: PUBLIC PROCEDURE [a: CARDINAL] = BEGIN selColOffset ← a; END;

drawMark: PUBLIC PROCEDURE [x, y: INTEGER] =
BEGIN
a, b, c, d: INTEGER;
bb: BOOLEAN;
[bb, a, b, c, d] ← cscaleRect[x, y, x, y];
IF bb THEN
BEGIN
colorPat: GrayPattern;
SetColorFromSource[source: @cursorBox, x: a, y: b, fn: erase];
colorPat ← GetColorGray[c: black];
SetColorFromSource[source: @cursorBox, x: a, y: b,
fn: paint, sourceType: andgray, gray: @colorPat];
colorPat ← GetColorGray[c: black-maroon];
SetColorFromSource[source: @insideCBox, x: a, y: b, fn: invert,
sourceType: andgray, gray: @colorPat];
END;
END;

drawColText: PROCEDURE [
x, y, sx, sy: INTEGER, s: STRING, clip: POINTER TO Rect] =
BEGIN
a, b, c, d: INTEGER;
bb: BOOLEAN;
[bb, a, b, c, d] ← cClipScale[x, y, x, y, clip];
c ← MIN[a + 5, colWidth];
d ← MIN[b + 5, colHeight];
IF bb THEN PutColorArea[x1: a, y1: b, x2: c, y2: d, c: white];
END;

whiteGray: GrayPattern;

ShowSelected: PROC [lp: listPtr, saveCursor: BOOLEAN] =
BEGIN
bb: BOOLEAN;
size: Point = ChipOrient.Size[size: [x: lp.ob.size[0], y: lp.ob.size[1]], orient: lp.idx];
sx1, sx2, sy1, sy2: Pixel;
[bb, sx1, sy1, sx2, sy2] ← cscaleRect[lp.lx, lp.ly, lp.lx+size.x, lp.ly+size.y];
IF bb THEN
BEGIN
ref: Rect = ChipOrient.MapRect[itemInCell: [1,1,2,2],
cellSize: [x: lp.ob.size[0], y: lp.ob.size[1]], cellInstOrient: lp.idx,
cellInstPos: [x: lp.lx, y: lp.ly]];
IF saveCursor THEN gonnaDoCrect[sx1, sy1, sx2, sy2];
DoColorArea[sx1, sy1, sx2, sy1, replace, FALSE, @whiteGray];
DoColorArea[sx1, sy1, sx1, sy2, replace, FALSE, @whiteGray];
DoColorArea[sx1, sy2, sx2, sy2, replace, FALSE, @whiteGray];
DoColorArea[sx2, sy1, sx2, sy2, replace, FALSE, @whiteGray];
[, sx1, sy1, sx2, sy2] ← cscaleRect[ref.x1, ref.y1, ref.x2, ref.y2];
DoColorArea[sx1, sy1, sx2, sy2, replace, FALSE, @whiteGray];
-- mark reference corner
IF saveCursor THEN doneCrect[];
END;
END;


--**** stuff that used to be in ppprocs:

cremArray: LONG DESCRIPTOR FOR ARRAY OF rems ← DESCRIPTOR[
LongDataSegmentAddress[NewDataSegment[DefaultANYBase, remPages]], remLen];
cremIdx: CARDINAL ← 0;

colDrR: drRecord ←
[ -- expressed in design coordinates translated by [cxoff, cyoff]
[0, 0, 0, 0], [0, 0, 0, 0], orColArea, rememberCol, outlineCol, drawColText, 1];

abortColor: PUBLIC BOOLEAN ← FALSE;
savCBbox: PUBLIC Rect;

updateColor: PUBLIC PROC =
BEGIN OPEN ProcessDefs;
DO
bp: LONG CARDINAL;
dangerousUpdateColor[! ANY =>
{MiscDefs.CallDebugger[
"Color repainter signalled, ↑P to continue after next backup pass"]; CONTINUE}];
bp ← ppoutdefs.backupPass;
WHILE ppoutdefs.backupPass=bp DO Pause[SecondsToTicks[1]] ENDLOOP;
ENDLOOP;
END;

dangerousUpdateColor: PROC =
BEGIN
ColorPaintList: PROC [head: LONG POINTER TO listPtr,
background: BOOLEAN ← FALSE, pushee: listPtr ← NIL] =
BEGIN
next: listPtr;
finished: BOOLEAN ← FALSE;
WHILE NOT (finished OR abortColor) DO
FOR lp: listPtr ← head↑, next WHILE lp # NIL DO
next ← lp.nxt;
IF abortColor OR lp.deleted THEN EXIT;
IF lp # pushee THEN
BEGIN
ppCache.DrawWithCaching[ob: lp.ob, orient: lp.idx,
x: lp.lx+offset.x, y: lp.ly+offset.y,
pr: @colDrR, dest: @destRaster];
IF lp.selected AND NOT background THEN
ShowSelected[lp: lp, saveCursor: FALSE];
END;
IF NOT background AND lp.props # NIL THEN
drawColText[lp.lx+offset.x, lp.ly+offset.y, 0, 0, "prop", @colScreenRect];
REPEAT
FINISHED => finished ← TRUE;
ENDLOOP;
ENDLOOP;
END; -- of ColorPaintList

doColorRemember: PROC [first, afterLast: CARDINAL,
background: BOOLEAN ← FALSE, cellBB: Rect ← [0,0,0,0]] =
BEGIN
r: Rect;
l: level;
FOR i: CARDINAL IN [first..afterLast) DO
IF abortColor THEN RETURN;
[r: r, l: l] ← cremArray[i];
repColArea[r.x1, r.y1, r.x2, r.y2, l];
IF background THEN
ChipOrient.DecomposeRect[r: r, test: cellBB, rInsideTest: fuzzColWithBackground,
rOutsideTest: fuzzColWithBlack];
ENDLOOP;
END; -- of doColorRemember

colScreenRect: Rect;
cmd: sCmd;
ProcessDefs.SetPriority[0];
abortColor ← FALSE;
SetupColorAreaBLT[BLT: BLT, x1: 0, x2: 0, y1: 0, y2: 0, fn: replace];
destRaster.nPixels ← [x: xColorMax*colBitsPerPixel, y: yColorMax];
destRaster.scanLineWords ← BLT.blk.dbmr;
destRaster.map ← LOOPHOLE[BLT.blk.dlbca];
DO
cmd ← getColNewRect[];
black ← Black[];
backgroundGray ← GetColorGray[background];
whiteGray ← GetColorGray[white];
FOR l: level IN level DO
levColorGray[l] ← GetColorGray[orLtab[l]];
levMaskGray[l] ← GetMaskGray[orLtab[l]];
levMaskGrayPtrs[l] ← IF levMaskGray[l]#solid THEN @levMaskGray[l] ELSE NIL;
ENDLOOP;
ppCache.SetupCacheGrayTables[];
offset ← [cxoff, cyoff];
colScreenRect ← [cClipx1+offset.x, cClipy1+offset.y,
cClipx2+offset.x, cClipy2+offset.y];
-- translated design coords of entire color screen, can be changed by user command
colDrR.minSize ← (colGrain*cScaleD)/cScaleN;
SELECT cmd.cmd FROM
rect, all =>
BEGIN
cs: cellSEPtr = cellStack;
r, cmprect, xcbb: Rect;
colRemBkgIdx: CARDINAL;
markX, markY: locNum;
onColorScreen: BOOLEAN;
sx1, sx2, sy1, sy2: INTEGER; -- screen units
SELECT cmd.cmd FROM
all => r ← colScreenRect;
ENDCASE =>
BEGIN
cmd.r ← cannonRect[cmd.r];
r ← ClipRect[[x1: cmd.r.x1+offset.x, y1: cmd.r.y1+offset.y,
x2: cmd.r.x2+offset.x, y2: cmd.r.y2+offset.y],
colScreenRect];
END;
colDrR.bigr ← cmprect ← -- expanded to include wells and the like
[x1: MAX[colScreenRect.x1, r.x1 - wellSurround],
x2: MIN[colScreenRect.x2, r.x2 + wellSurround],
y1: MAX[colScreenRect.y1, r.y1 - wellSurround],
y2: MIN[colScreenRect.y2, r.y2 + wellSurround]];
colDrR.r ← r ← IF NOT cmd.ers THEN r ELSE cmprect;

[onColorScreen, sx1, sy1, sx2, sy2] ←
cClipScale[r.x1, r.y1, r.x2, r.y2, @colScreenRect];
-- scale design rect to screen coords

gonnaDoCrect[sx1, sy1, sx2, sy2]; -- remove cursor from this screen rectangle

IF cmd.ers THEN
BEGIN
a, b, c, d: INTEGER;
bb: BOOLEAN;
[bb, a, b, c, d] ← cClipScale[r.x1, r.y1, r.x2, r.y2, @colScreenRect];
IF bb THEN DoColorArea[a, b, c, d, replace, TRUE, @backgroundGray];
END;

cremIdx ← 0;
IF cs#NIL THEN
BEGIN

NoOpRect: PROC [r: Rect] = {NULL};

RestoreBackground: PROC [r: Rect] =
BEGIN
clippedR: Rect = ClipRect[r, cmprect];
IF NOT Empty[clippedR] THEN
BEGIN

BkgColArea: PROC [w, x, y, z: INTEGER, l: level,
clip: POINTER TO Rect ← NIL] =
BEGIN
a, b, c, d: INTEGER;
bb: BOOLEAN;
[bb, a, b, c, d] ← cClipScale[w, x, y, z,
IF clip#NIL THEN clip ELSE @universe];
IF bb THEN DoColorArea[a, b, c, d, replace, TRUE, @backgroundGray];
END;

resBkgDrR: drRecord ←
[clippedR, clippedR, BkgColArea, BkgColArea, NIL, NIL, 1];

FOR lp: listPtr ← masterList, lp.nxt WHILE lp#NIL DO
IF lp.ob#NIL THEN
lp.ob.p.drawme[lp.idx][lp.ob, lp.lx, lp.ly, @resBkgDrR];
ENDLOOP;
END;
END;

mbb: Rect = ChipOrient.BoundingRect[masterList];
xmbb: Rect = [x1: mbb.x1+offset.x, y1: mbb.y1+offset.y,
x2: mbb.x2+offset.x, y2: mbb.y2+offset.y];

xcbb ← [x1: cs.origBB.x1+offset.x, y1: cs.origBB.y1+offset.y,
x2: cs.origBB.x2+offset.x, y2: cs.origBB.y2+offset.y];

FOR bkg: cellSEPtr ← cs, bkg.nxt
WHILE bkg#NIL AND bkg.instance#NIL DO
IF abortColor THEN EXIT;
ColorPaintList[head: @bkg.lp, background: TRUE, pushee: bkg.instance];
ENDLOOP;

ChipOrient.DecomposeRect[r: r, test: xcbb,
rInsideTest: fuzzColWithBackground,
rOutsideTest: fuzzColWithBlack];
ChipOrient.DecomposeRect[r: xmbb, test: xcbb,
rInsideTest: NoOpRect, rOutsideTest: RestoreBackground];
END;

colRemBkgIdx ← cremIdx;

ColorPaintList[IF cellNameMode THEN @cnList ELSE @masterList];

IF cs#NIL THEN
doColorRemember[first: 0, afterLast: colRemBkgIdx, background: TRUE,
cellBB: xcbb]; -- make these dim
doColorRemember[first: colRemBkgIdx, afterLast: cremIdx];

doneCrect[]; -- put the cursor back

[markX, markY] ← markPnt;
[onColorScreen, sx1, sy1, , ] ←
cscaleRect[markX, markY, markX, markY];
IF onColorScreen AND NOT abortColor THEN
BEGIN
gonnaDoCrect[sx1, sy1, sx1 + 16, sy1 + 16];
drawMark[markX, markY];
doneCrect[];
END;

IF tickGrid # 0 AND NOT abortColor THEN
BEGIN
t: locNum = tickGrid*Lambda;
scaledT: Pixel = (t*cScaleN)/cScaleD;
IF scaledT >= tickOff*2 THEN -- we should paint ticks
BEGIN
ticksRect: Rect = [x1: r.x1+Mod[-r.x1, t], y1: r.y1+Mod[-r.y1, t],
x2: r.x2, y2: r.y2];
IF NOT Empty[ticksRect] THEN
BEGIN
[, sx1, sy1, sx2, sy2] ← cClipScale[ticksRect.x1, ticksRect.y1,
ticksRect.x2, ticksRect.y2, @universe];
gonnaDoCrect[sx1, sy1, sx2, sy2];
drawTicks[[x1: sx1, y1: sy1, x2: sx2, y2: sy2], scaledT];
doneCrect[];
END;
END;
END;
END;

sel => ShowSelected[lp: cmd.p, saveCursor: TRUE];
ENDCASE => ERROR;
ENDLOOP;
END;

drawTicks: PROC [r: ScreenRect, t: Pixel] =
BEGIN
FOR y: Pixel ← r.y1, y+t WHILE y <= r.y2 DO
IF abortColor THEN RETURN;
FOR x: Pixel ← r.x1, x+t WHILE x <= r.x2 DO
PutColorPoint[x: x, y: y, c: white];
ENDLOOP;
ENDLOOP;
END;

redoCBBox: PUBLIC PROCEDURE =
BEGIN
r, s: ScreenRect ← savCBbox;
r.x1 ← r.x2;
reDrawRect[r, 1, TRUE, FALSE, FALSE];
r.x2 ← r.x1 ← s.x1;
reDrawRect[r, 1, TRUE, FALSE, FALSE];
r.x2 ← s.x2;
r.y1 ← r.y2;
reDrawRect[r, 1, TRUE, FALSE, FALSE];
r.y2 ← r.y1 ← s.y1;
reDrawRect[r, 1, TRUE, FALSE, FALSE];
END;

rememberCol: PROCEDURE [
x1, y1, x2, y2: INTEGER, l: level, clip: POINTER TO Rect] =
BEGIN
IF showColorLevel[l] AND cremIdx<remLen AND
((l # cut AND l#cut2) OR cScale>=8) THEN
BEGIN
cremArray[cremIdx].r ← ClipRect[[x1, y1, x2, y2], clip↑];
IF NOT Empty[cremArray[cremIdx].r] THEN
BEGIN
cremArray[cremIdx].l ← l;
cremIdx ← cremIdx + 1;
END;
END;
END;

END.