-- color screen drawing module of silicon (pretty picture) program
-- last modified by E. McCreight, December 30, 1982  12:38 PM
DIRECTORY
  BitBltDefs,
  ChipOrient,
  InlineDefs,
  IODefs,
  AltoDefs,
  AltoFileDefs,
  multiGraphicsDefs,
  SegmentDefs,
  ProcessDefs,
  ppdddefs,ppddefs,
  ppdefs;

ppdrawCol: PROGRAM
  IMPORTS
    BitBltDefs, ChipOrient, ppdefs, ppddefs, InlineDefs,
    multiGraphicsDefs, ProcessDefs,
    SegmentDefs, ppdddefs
  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:grn			2:blue
  --	3: blue & grn		4:red			5:red & grn
  --	6:red & blue		7:red & grn & blue	8:ovg cut:maroon?
  --	9:contact cut: black	10:sel.outln,curs,wht	11:burr?
  --	12:outline undrawn cell	13:?			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 = 9;
  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];

  cscale: PROCEDURE [x, y: INTEGER] RETURNS [INTEGER, INTEGER] = INLINE
    BEGIN
    RETURN[((cxoff + x)*cScaleN)/cScaleD, ((cyoff + y)*cScaleN)/cScaleD];
    END;
  cscaleRect: PUBLIC PROCEDURE [x1, y1, x2, y2: INTEGER]
    RETURNS [BOOLEAN, INTEGER, INTEGER, INTEGER, INTEGER] =
    BEGIN
    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];
    [x1, y1] ← cscale[MAX[x1, cClipx1], MAX[y1, cClipy1]];
    [x2, y2] ← cscale[MIN[x2, cClipx2], MIN[y2, cClipy2]];
    RETURN[TRUE, x1, y1, x2, y2];
    END;
  csAndClip: PROCEDURE [x1, y1, x2, y2: INTEGER, cr: POINTER TO Rect]
    RETURNS [BOOLEAN, INTEGER, INTEGER, INTEGER, INTEGER] = INLINE
    BEGIN
    IF x1 > cr.x2 OR x2 < cr.x1 OR y1 > cr.y2 OR y2 < cr.y1 THEN
      RETURN[FALSE, 0, 0, 0, 0];
    [x1, y1] ← cscale[MAX[x1, cr.x1], MAX[y1, cr.y1]];
    [x2, y2] ← cscale[MIN[x2, cr.x2], MIN[y2, cr.y2]];
    RETURN[TRUE, x1, y1, x2, y2];
    END;
  outlineCol: PUBLIC PROCEDURE [
    w, x, y, z: INTEGER, q: color, clip: POINTER TO Rect ← NIL] =
    BEGIN
      oulColArea[w, x, w, z, q, clip,0,1];
      oulColArea[y, x, y, z, q, clip,1,1];
      oulColArea[w, z, y, z, q, clip,1,1];
      oulColArea[w, x, y, x, q, clip,1,0];
    END;

  -- 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[];
  logBitsPerPixel: CARDINAL = 2; -- 4 bits/point
  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] ← csAndClip[w, x, y, z, clip];
    IF bb 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[c-a, logBitsPerPixel];
      BLT.blk.dh ← 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 [bb, a, b, c, d] ← cscaleRect[w, x, y, z]
    ELSE [bb, a, b, c, d] ← csAndClip[w, x, y, z, clip];
    IF bb THEN
      BEGIN
      -- 0<=a<=c<= and 0<=b<=d
      BLT.blk.dlx ← BITSHIFT[a, logBitsPerPixel];
      BLT.blk.dty ← b;
      BLT.blk.dw ← BITSHIFT[c-a, logBitsPerPixel];
      BLT.blk.dh ← 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;


  whiteGray: GrayPattern;
  oulColArea: PROCEDURE [
    w, x, y, z: INTEGER, q: color, clip: POINTER TO Rect,offx,offy:INTEGER] =
    BEGIN
    a, b, c, d: INTEGER;
    bb: BOOLEAN;
    [bb, a, b, c, d] ← csAndClip[w, x, y, z, clip];
    IF bb THEN
      BEGIN
      IF q=white THEN
        DoColorArea[a, b, c-offx, d-offy, replace, TRUE, @whiteGray]
      ELSE
        BEGIN
        otherGray: GrayPattern ← GetColorGray[q];
        DoColorArea[a, b, c-offx, d-offy, replace, TRUE, @otherGray];
        END;
      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;
    IF clip = NIL THEN [bb, a, b, c, d] ← cscaleRect[r.x1, r.y1, r.x2, r.y2]
    ELSE [bb, a, b, c, d] ← csAndClip[r.x1, r.y1, r.x2, r.y2, clip];
    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;

  eraseScreen: PUBLIC PROCEDURE [w, x, y, z: INTEGER] =
    BEGIN
    DoColorArea[w, x, y, z, replace, FALSE, @backgroundGray];
    EraseArea[w, x, y, z];
    END;

  eraseArea: PUBLIC PROCEDURE [w, x, y, z: INTEGER] =
    BEGIN
    a, b, c, d: INTEGER;
    bb: BOOLEAN;
    [bb, a, b, c, d] ← cscaleRect[w, x, y, z];
    IF bb THEN DoColorArea[a, b, c, d, replace, FALSE, @backgroundGray];
    eraseBwArea[w, x, y, z];
    END;
  eraseColArea: PUBLIC PROCEDURE [w, x, y, z: INTEGER] =
    BEGIN
    a, b, c, d: INTEGER;
    bb: BOOLEAN;
    [bb, a, b, c, d] ← cscaleRect[w, x, y, z];
    IF bb THEN DoColorArea[a, b, c, d, replace, TRUE, @backgroundGray];
    END;

  drawArea: PUBLIC PROCEDURE [w, x, y, z: INTEGER, c: color] =
    BEGIN
    a, b, q, d: INTEGER;
    bb: BOOLEAN;
    [bb, a, b, q, d] ← cscaleRect[w, x, y, z];
    IF bb THEN
      BEGIN
      color: GrayPattern ← GetColorGray[c];
      mask: GrayPattern ← GetMaskGray[c];
      DoColorArea[a, b, q, d, replace, FALSE, @color, @mask];
      END;
    [bb, a, b, q, d] ← bwscaleRect[w, x, y, z];
    IF bb THEN BEGIN SetGrayLevel[c]; ReplaceGray[a, b, q, d]; END;
    END;

  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] ← csAndClip[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;

  --**** 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 ← [
    [0, 0, 0, 0], [0, 0, 0, 0], orColArea, rememberCol, outlineCol, drawColText, 1];

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

  updateColor: PUBLIC PROC =
    BEGIN

    ColorPaintList: PROC [head: LONG POINTER TO listPtr,
      background: BOOLEAN ← FALSE] =
      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.ob # NIL THEN
            BEGIN
            lp.ob.p.drawme[lp.idx][lp.ob, lp.lx, lp.ly, @colDrR];
            IF lp.selected AND NOT background THEN
              BEGIN
              ii: [0..1] = ChipOrient.Rot90[lp.idx];
              outlineCol[
                lp.lx, lp.ly, lp.lx + lp.ob.size[ii], lp.ly + lp.ob.size[1-ii],
                white, @colScreenRect];
              END;
            END;
          IF lp.gotText AND NOT background THEN
            BEGIN
            tp: LONG POINTER TO text prop ← getTextProp[lp];
            drawColText[lp.lx, lp.ly, 0, 0, tp.s, @colScreenRect];
            END;
          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];

    DO
      cmd ← getColNewRect[];
      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;
      colScreenRect ← [cClipx1, cClipy1, cClipx2, cClipy2];
      -- design coords of entire color screen, could be changed by user command
      colDrR.minSize ← (colGrain*cScaleD)/cScaleN;
      SELECT cmd.cmd FROM
        rect, all =>
          BEGIN
          cs: cellSEPtr = cellStack;
          r, cmprect: 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[cmd.r, colScreenRect];
              END;
          cmprect ← colDrR.bigr ←
            [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] ←
            cscaleRect[r.x1, r.y1, r.x2, r.y2]; -- scale it to screen coords

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

          IF cmd.ers THEN
            SELECT cmd.cmd FROM
              all => DoColorArea[0,0,xColorMax,yColorMax,replace,FALSE,
                @backgroundGray];
              ENDCASE => eraseColArea[r.x1, r.y1, r.x2, r.y2];

          cremIdx ← 0;
          IF cs#NIL THEN
            BEGIN

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

            RestoreBackground: PROC [r: Rect] =
              BEGIN
              clippedR: Rect = ClipRect[r, colDrR.r];
              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;
                  IF clip = NIL THEN [bb, a, b, c, d] ← cscaleRect[w, x, y, z]
                  ELSE [bb, a, b, c, d] ← csAndClip[w, x, y, z, clip];
                  IF bb THEN DoColorArea[a, b, c, d, replace, TRUE, @backgroundGray];
                  END;

                resBkgDrR: drRecord ←
                  [clippedR, clippedR, BkgColArea, BkgColArea, outlineCol, drawColText, 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;

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

            ChipOrient.DecomposeRect[r: r, test: cs.origBB,
              rInsideTest: fuzzColWithBackground,
              rOutsideTest: fuzzColWithBlack];
            ChipOrient.DecomposeRect[r: ChipOrient.BoundingRect[masterList],
              test: cs.origBB,
              rInsideTest: NoOpRect, rOutsideTest: RestoreBackground];
            END;

          colRemBkgIdx ← cremIdx;

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

          IF cs#NIL THEN
            BEGIN
            doColorRemember[first: 0, afterLast: colRemBkgIdx, background: TRUE,
              cellBB: cs.origBB]; -- make these dim
            END;
          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 ← ClipRect[r, [cClipx1, cClipy1, cClipx2, cClipy2]];
              modNeg: Point ← [x: (-ticksRect.x1) MOD t, y: (-ticksRect.y1) MOD t];
              ticksRect.x1 ← ticksRect.x1+modNeg.x+
                (IF modNeg.x<0 THEN -- Mesa screwed up !! -- t ELSE 0);
              ticksRect.y1 ← ticksRect.y1+modNeg.y+
                (IF modNeg.y<0 THEN -- Mesa screwed up !! -- t ELSE 0);
              IF NOT Empty[ticksRect] THEN
                BEGIN
                [, sx1, sy1, sx2, sy2] ←
                  cscaleRect[ticksRect.x1, ticksRect.y1, ticksRect.x2, ticksRect.y2];
                gonnaDoCrect[sx1, sy1, sx2, sy2];
                drawTicks[[x1: sx1, y1: sy1, x2: sx2, y2: sy2], scaledT];
                doneCrect[];
                END;
              END;
            END;
          END;

        sel =>
          BEGIN
          lp: listPtr = cmd.p;
          ii: [0..1] = ChipOrient.Rot90[lp.idx];
          r: Rect = [x1: lp.lx, y1: lp.ly,
            x2: lp.lx + lp.ob.size[ii], y2: lp.ly + lp.ob.size[1-ii]];
          sx1, sx2, sy1, sy2: Pixel;
          [, sx1, sx2, sy1, sy2] ← cscaleRect[r.x1, r.y1, r.x2, r.y2];
          gonnaDoCrect[sx1, sx2, sy1, sy2];
          outlineCol[r.x1, r.y1, r.x2, r.y2, white, @colScreenRect];
          doneCrect[];
          END;

        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 cremIdx >= remLen OR (l = cut AND cScale < 8) THEN RETURN;
    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.