-- Copyright (C) 1983, 1984  by Xerox Corporation. All rights reserved. 
-- DisplayImplD.mesa - last edited by 
-- Rick	        12-Nov-83 14:51:57  
-- Bruce	24-Feb-83 16:08:25  
-- Daniels	 7-Jun-84 14:56:02  

DIRECTORY
  BitBlt USING [BitBltFlags],
  DisplayFormat USING [CircleType],
  Display USING [paintGrayFlags],
  DisplayOps USING [
    AbsPlace, Color, HasUnder, LogError, FillList, FillObject, Intersect, Shade],
  DisplayInternal USING [],
  SpecialDisplay USING [DashCnt, defaultContext, LineStyle, solid, Special],
  Window USING [BoxHandle, Place],
  WindowOps USING [
    AbsoluteBoxHandle, bitmapAddress, Bounds, DisplayList, GetContext,
    lock, Object, RecList, ScanLine, ScreenBox, SpecialTimesWpl];

DisplayImplD: MONITOR LOCKS WindowOps.lock
  IMPORTS DisplayOps, SpecialDisplay, WindowOps
  EXPORTS DisplayInternal, SpecialDisplay, Window =
  BEGIN

  -- exported types
  Handle: TYPE = LONG POINTER TO Object;
  Object: PUBLIC TYPE = WindowOps.Object;

  FillHandle: TYPE = LONG POINTER TO FillObject;
  FillObject: PUBLIC TYPE = DisplayOps.FillObject;

  -- copied types because John doesn't like them
  LineStyle: TYPE = SpecialDisplay.LineStyle;
  Special: TYPE = SpecialDisplay.Special;

  -- circles

  Circle: PUBLIC ENTRY PROC [
    window: Handle, place: Window.Place, radius: INTEGER,
    circleType: DisplayFormat.CircleType, bounds: Window.BoxHandle ← NIL] = {
    -- This routine observes that:
    -- radius IN [0..8000]
    -- place.x, place.y IN [-8000.. 8000]
    -- Also the circle bouding box intersects each reclist and before any integer
    -- math is done, the circle is in each reclist. Four scanlines and four
    -- bitIndexes are maintained to determine xys in all eight octants.  Scanlines
    -- start top, middle, middle, bottom.  BitIndexes start left, middle, middle,
    -- right.  All end on the 45s.
    ENABLE UNWIND => NULL;
    absBounds: WindowOps.ScreenBox =
      IF bounds = NIL THEN WindowOps.Bounds[window]
      ELSE WindowOps.AbsoluteBoxHandle[window, bounds];
    startTwoxma: INTEGER ← 1;
    startTwoymb: INTEGER ← 2*radius - 1;
    abs: Window.Place = DisplayOps.AbsPlace[window, place];
    IF ~window.inTree THEN RETURN;
    FOR r: WindowOps.RecList ← WindowOps.DisplayList[window], r.link 
      UNTIL r = NIL DO
      IF DisplayOps.Intersect[r, absBounds] THEN {
        IF abs.x - radius < r.box.left OR  --  clipped?
          abs.x + radius > r.box.right OR abs.y + radius > r.box.bottom OR
          abs.y - radius < r.box.top OR DisplayOps.HasUnder[r] THEN {
          [] ← SpCircleInternal[
            r, FALSE, window, place, radius, circleType, bounds,
            SpecialDisplay.solid, Display.paintGrayFlags];
          RETURN};
        {		-- not clipped, so these values can't be negative
	wpl: CARDINAL = SpecialDisplay.defaultContext.wpl;
        bitAddress1: WindowOps.ScanLine ←
          WindowOps.bitmapAddress + WindowOps.SpecialTimesWpl[abs.y - radius];
        bitAddress2: WindowOps.ScanLine ←
          WindowOps.bitmapAddress + WindowOps.SpecialTimesWpl[abs.y];
        bitAddress3: WindowOps.ScanLine ← bitAddress2;
        bitAddress4: WindowOps.ScanLine ←
          WindowOps.bitmapAddress + WindowOps.SpecialTimesWpl[abs.y + radius];
        bitIndex1: INTEGER ← abs.x - radius;
        bitIndex2, bitIndex3: INTEGER ← abs.x;
        bitIndex4: INTEGER ← abs.x + radius;
        newError: INTEGER;
        twoxma: INTEGER ← startTwoxma;
        twoymb: INTEGER ← startTwoymb;
        error: INTEGER ← (radius - 1)/2;
        IF circleType[0] THEN {
	    bitIndex1 ← bitIndex1 + 1; bitIndex2 ← bitIndex2 + 1};
	  IF circleType[1] THEN {
	    bitIndex1 ← bitIndex1 - 1; bitIndex2 ← bitIndex2 - 1};
	  IF circleType[2] THEN {
	    bitIndex3 ← bitIndex3 - 1; bitIndex4 ← bitIndex4 - 1};
	  IF circleType[3] THEN {
	    bitIndex3 ← bitIndex3 + 1; bitIndex4 ← bitIndex4 + 1};
	  IF circleType[4] THEN {
	    bitAddress1 ← bitAddress1 + wpl;
	    bitAddress2 ← bitAddress2 + wpl};
	  IF circleType[5] THEN {
	    bitAddress1 ← bitAddress1 - wpl;
	    bitAddress2 ← bitAddress2 - wpl};
	  IF circleType[6] THEN {
	    bitAddress3 ← bitAddress3 - wpl; 
	    bitAddress4 ← bitAddress4 - wpl};
	  IF circleType[7] THEN {
	    bitAddress3 ← bitAddress3 + wpl;
	    bitAddress4 ← bitAddress4 + wpl};
        -- Provided this loop quits when it's supposed to, less thanradius/2times,
        -- twoxma IN [0..radius]
        -- twoymb IN [0..2*radius]
        -- error starts at [0..radius/2],  and stays IN [-twoymb..twoymb] 
        -- as twoymb goes to zero.
        UNTIL bitIndex3 > bitIndex4 DO
          bitAddress1[bitIndex2] ← TRUE;  -- set a bit
          bitAddress1[bitIndex3] ← TRUE;
          bitAddress2[bitIndex1] ← TRUE;
          bitAddress2[bitIndex4] ← TRUE;
          bitAddress3[bitIndex1] ← TRUE;
          bitAddress3[bitIndex4] ← TRUE;
          bitAddress4[bitIndex2] ← TRUE;
          bitAddress4[bitIndex3] ← TRUE;
          bitIndex2 ← bitIndex2 - 1;
          bitIndex3 ← bitIndex3 + 1;
          bitAddress2 ← bitAddress2 - wpl;
          bitAddress3 ← bitAddress3 + wpl;
          error ← error + twoxma;
          IF ABS[newError ← error - twoymb] < ABS[error] THEN {
            error ← newError;
            bitIndex1 ← bitIndex1 + 1;
            bitIndex4 ← bitIndex4 - 1;
            bitAddress1 ← bitAddress1 + wpl;
            bitAddress4 ← bitAddress4 - wpl;
            twoymb ← twoymb - 2};
          twoxma ← twoxma + 2;
          ENDLOOP}};
      ENDLOOP};

  SpecialCircle: PUBLIC ENTRY PROC [
    window: Handle, place: Window.Place, radius: INTEGER,
    circleType: DisplayFormat.CircleType, bounds: Window.BoxHandle,
    dashes: SpecialDisplay.LineStyle, flags: BitBlt.BitBltFlags,
    context: Special ← SpecialDisplay.defaultContext] = {
    ENABLE UNWIND => NULL;
    filled: BOOLEAN = context.alloc # NIL;
    firstRec: WindowOps.RecList = DisplayOps.FillList[window, filled];
    SpCircleInternal[
      firstRec, filled, window, place, radius, circleType, bounds, dashes, flags,
      context]};

  SpCircleInternal: INTERNAL PROC [
    firstRec: WindowOps.RecList, filled: BOOLEAN, window: Handle,
    place: Window.Place, radius: INTEGER, circleType: DisplayFormat.CircleType,
    bounds: Window.BoxHandle, dashes: SpecialDisplay.LineStyle,
    flags: BitBlt.BitBltFlags, context: Special ← SpecialDisplay.defaultContext]={
    -- This routine observes that:
    -- radius IN [0..8000]
    -- place.x, place.y IN [-8000.. 8000]
    -- Also the circle bouding box intersects Display.clip.box.
    -- Display.clip.box.left, right IN [0.. 1000]
    -- Display.clip.box.top, bottom IN [0..800]
    -- Four scanlines and four bitIndexes are maintained to determine xys in
    -- all eight octants.  Scanlines start top, middle, middle, bottom. BitIndexes
    -- start left, middle, middle, right.  All end on the 45s.
    L: PROCEDURE [l: LONG POINTER] RETURNS [LONG ORDERED POINTER] = INLINE {
      RETURN[LOOPHOLE[l]]};
    absBounds: WindowOps.ScreenBox =
      IF bounds = NIL THEN WindowOps.Bounds[window]
      ELSE WindowOps.AbsoluteBoxHandle[window, bounds];
    abs: Window.Place = DisplayOps.AbsPlace[window, place];
    newError: INTEGER;
    bitIndex2, bitIndex3: INTEGER ← abs.x;
    dashCnt, dashSum: CARDINAL;
    widths: ARRAY [0..SpecialDisplay.DashCnt) OF CARDINAL;
    inc1, inc2, inc3, inc4, inc5, inc6: CARDINAL;
    paint: DisplayOps.Color = DisplayOps.Shade[flags];
    fillLength: INTEGER;
    fillcnt1,fillcnt2,fillFudge1,fillFudge2,fillFudge3,fillFudge4,index: INTEGER;
    fill: FillHandle;
    inLeft, outLeft, inRight, outRight, inTop, outTop, inBottom, 
      outBottom: BOOLEAN ← FALSE;
    FOR i: CARDINAL IN [0..SpecialDisplay.DashCnt) DO
      widths[i] ← dashes.widths[i]; ENDLOOP;
    inc1 ← 0;
    inc2 ← widths[0]*10;
    inc3 ← inc2 + widths[1]*10;
    inc4 ← inc3 + widths[2]*10;
    inc5 ← inc4 + widths[3]*10;
    inc6 ← inc5 + widths[4]*10;
    FOR r: WindowOps.RecList ← firstRec, r.link UNTIL r = NIL DO
      IF DisplayOps.Intersect[r, absBounds] THEN {
        int: WindowOps.ScreenBox = [
	  left: MAX[r.box.left, absBounds.left],
	  right: MIN[r.box.right, absBounds.right],
	  top: MAX[r.box.top, absBounds.top],
	  bottom: MIN[r.box.bottom, absBounds.bottom]];
	ctx: Special = WindowOps.GetContext[r, context];
        minBitAddress: LONG ORDERED POINTER = LOOPHOLE[ 
	  ctx.bmAddress + WindowOps.SpecialTimesWpl[INTEGER[int.top], ctx]];
        maxBitAddress: LONG ORDERED POINTER = LOOPHOLE[
	  ctx.bmAddress + WindowOps.SpecialTimesWpl[INTEGER[int.bottom - 1],ctx]];
        minBitIndex: INTEGER ← MAX[0, int.left];
        maxBitIndex: INTEGER ← int.right - 1;
	IF int.top = int.bottom THEN LOOP;
	IF filled THEN {
	  minBitIndex ← MAX[0, minBitIndex];
	  maxBitIndex ← absBounds.right;
          fillcnt1 ← fillcnt2 ← 0;
          fillFudge1 ← abs.y - int.top - radius; -- bA1 in fill place
	  fillFudge2 ← fillFudge3 ← abs.y - int.top; -- bA2, bA3 in fill place
	  fillFudge4 ← abs.y - int.top + radius; -- bA4 in fill place
	  fillLength ← int.bottom - int.top;
	  fill ← ctx.alloc[window, int.top, fillLength];
	  fill.xs[0] ← minBitIndex};
        FOR i: CARDINAL IN [0..dashes.thickness) DO  -- for thicking
          error: INTEGER ← (radius - 1)/2;
          twoxma: INTEGER ← 1;
          twoymb: INTEGER ← 2*radius - 1;
          bitAddress1: WindowOps.ScanLine ←
            LOOPHOLE[ctx.bmAddress + WindowOps.SpecialTimesWpl[
                       INTEGER[abs.y - radius + i], ctx]];
          bitAddress2: WindowOps.ScanLine ← LOOPHOLE[
	    ctx.bmAddress + WindowOps.SpecialTimesWpl[INTEGER[abs.y], ctx]];
          bitAddress3: WindowOps.ScanLine ← bitAddress2;
          bitAddress4: WindowOps.ScanLine ←
            LOOPHOLE[ctx.bmAddress + WindowOps.SpecialTimesWpl[
                       INTEGER[abs.y + radius - i], ctx]];
          bitIndex1: INTEGER ← abs.x - radius + i;
          bitIndex4: INTEGER ← abs.x + radius - i;
          bitIndex2 ← bitIndex3 ← abs.x;
	  IF circleType[0] THEN {
	    bitIndex1 ← bitIndex1 + 1; bitIndex2 ← bitIndex2 + 1};
	  IF circleType[1] THEN {
	    bitIndex1 ← bitIndex1 - 1; bitIndex2 ← bitIndex2 - 1};
	  IF circleType[2] THEN {
	    bitIndex3 ← bitIndex3 - 1; bitIndex4 ← bitIndex4 - 1};
	  IF circleType[3] THEN {
	    bitIndex3 ← bitIndex3 + 1; bitIndex4 ← bitIndex4 + 1};
	  IF circleType[4] THEN {
	    bitAddress1 ← bitAddress1 + ctx.wpl;
	    bitAddress2 ← bitAddress2 + ctx.wpl;
	    fillFudge1 ← fillFudge1 + 1;
	    fillFudge2 ← fillFudge2 + 1};
	  IF circleType[5] THEN {
	    bitAddress1 ← bitAddress1 - ctx.wpl;
	    bitAddress2 ← bitAddress2 - ctx.wpl;
	    fillFudge1 ← fillFudge1 - 1;
	    fillFudge2 ← fillFudge2 - 1};
	  IF circleType[6] THEN {
	    bitAddress3 ← bitAddress3 - ctx.wpl; 
	    bitAddress4 ← bitAddress4 - ctx.wpl;
	    fillFudge3 ← fillFudge3 - 1;
	    fillFudge4 ← fillFudge4 - 1};
	  IF circleType[7] THEN {
	    bitAddress3 ← bitAddress3 + ctx.wpl;
	    bitAddress4 ← bitAddress4 + ctx.wpl;
	    fillFudge3 ← fillFudge3 + 1;
	    fillFudge4 ← fillFudge4 + 1};
          dashSum ← inc6 + widths[5]*10;
          dashCnt ← inc2/2;
          UNTIL bitIndex3 > bitIndex4 DO
            IF dashCnt IN [inc1..inc2) OR dashCnt IN [inc3..inc4)  -- dashes
              OR dashCnt IN [inc5..inc6) THEN {
              inL1: BOOLEAN = L[bitAddress1] IN [minBitAddress..maxBitAddress];
              inL2: BOOLEAN = L[bitAddress2] IN [minBitAddress..maxBitAddress];
              inL3: BOOLEAN = L[bitAddress3] IN [minBitAddress..maxBitAddress];
              inL4: BOOLEAN = L[bitAddress4] IN [minBitAddress..maxBitAddress];
              inC1: BOOLEAN = bitIndex1 IN [minBitIndex..maxBitIndex];
              inC2: BOOLEAN = bitIndex2 IN [minBitIndex..maxBitIndex];
              inC3: BOOLEAN = bitIndex3 IN [minBitIndex..maxBitIndex];
              inC4: BOOLEAN = bitIndex4 IN [minBitIndex..maxBitIndex];
              IF inL1 AND inC2 THEN -- sector 8
	        IF filled THEN {
	          index ← fillcnt2 + fillFudge1;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
		  fill.xs[index] ← bitIndex2}
                ELSE bitAddress1[bitIndex2] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress1[bitIndex2]);
              IF inL1 AND inC3 THEN -- sector 1
	        IF filled THEN {
		  index ← fillcnt2 + fillFudge1;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
		  fill.xs[index] ← bitIndex3}
                ELSE bitAddress1[bitIndex3] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress1[bitIndex3]);
              IF inL2 AND inC1 THEN -- sector 7
	        IF filled THEN {
		  index ← fillFudge2 - fillcnt1;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
		  fill.xs[index] ← bitIndex1}
                ELSE bitAddress2[bitIndex1] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress2[bitIndex1]);
              IF inL2 AND inC4 THEN -- sector 2
	        IF filled THEN {
		  index ← fillFudge2 - fillcnt1;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
		  fill.xs[index] ← bitIndex4}
                ELSE bitAddress2[bitIndex4] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress2[bitIndex4]);
              IF inL3 AND inC1 THEN  -- sector 6
	        IF filled THEN {
		  index ← fillFudge3 + fillcnt1;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
		  fill.xs[index] ← bitIndex1}
                ELSE bitAddress3[bitIndex1] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress3[bitIndex1]);
              IF inL3 AND inC4 THEN -- secter 3
	        IF filled THEN {
		  index ← fillFudge3 + fillcnt1;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
	          fill.xs[index] ← bitIndex4}
                ELSE bitAddress3[bitIndex4] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress3[bitIndex4]);
              IF inL4 AND inC2 THEN -- secter 5
	        IF filled THEN {
		  index ← fillFudge4 - fillcnt2;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
		  fill.xs[index] ← bitIndex2}
                ELSE bitAddress4[bitIndex2] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress4[bitIndex2]);
              IF inL4 AND inC3 THEN -- sector 4
	        IF filled THEN {
		  index ← fillFudge4 - fillcnt2;
		  IF index ~IN [0..fillLength) THEN DisplayOps.LogError[];
		  fill.xs[index] ← bitIndex3}
                ELSE bitAddress4[bitIndex3] ←
                  (SELECT paint FROM white => FALSE, black => TRUE,
                    ENDCASE => ~bitAddress4[bitIndex3])};
	    IF filled THEN  fillcnt1 ← fillcnt1 + 1;
            bitIndex2 ← bitIndex2 - 1;
            bitIndex3 ← bitIndex3 + 1;
            bitAddress2 ← bitAddress2 - ctx.wpl;
            bitAddress3 ← bitAddress3 + ctx.wpl;
            error ← error + twoxma;
            dashCnt ← (dashCnt + 10) MOD dashSum;
            IF ABS[newError ← error - twoymb] < ABS[error] THEN {
              IF filled THEN fillcnt2 ← fillcnt2 + 1;
              error ← newError;
              bitIndex1 ← bitIndex1 + 1;
              bitIndex4 ← bitIndex4 - 1;
              bitAddress1 ← bitAddress1 + ctx.wpl;
              bitAddress4 ← bitAddress4 - ctx.wpl;
              twoymb ← twoymb - 2;
              dashCnt ← (dashCnt + 4) MOD dashSum};
            twoxma ← twoxma + 2;
            ENDLOOP;
          ENDLOOP};
      ENDLOOP};

  END.