-- Copyright (C) 1983, 1984  by Xerox Corporation. All rights reserved. 
-- DisplayImplE.mesa - last edited by 
-- S. Schiller       7-Mar-83 13:55:16
-- Rick             22-Nov-83 16:49:41
-- Bruce	     6-Feb-83 21:13:09
-- Daniels	     7-Jun-84 14:58:00

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

DisplayImplE: MONITOR LOCKS WindowOps.lock
  IMPORTS DisplayOps, SpecialDisplay, WindowOps
  EXPORTS Display, 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;

  Ellipse: PUBLIC ENTRY PROC [
    window: Handle, center: Window.Place, xRadius, yRadius: INTEGER,
    bounds: Window.BoxHandle ← NIL] = {
    ENABLE UNWIND => NULL;
    absBounds: WindowOps.ScreenBox =
      IF bounds = NIL THEN WindowOps.Bounds[window]
      ELSE WindowOps.AbsoluteBoxHandle[window, bounds];
    -- switch to screen coords
    absCenter: Window.Place = DisplayOps.AbsPlace[window, center];
    IF ~window.inTree THEN RETURN;
    FOR r: WindowOps.RecList ← WindowOps.DisplayList[window], r.link UNTIL r = NIL
      DO
      IF DisplayOps.Intersect[r, absBounds] THEN {
        wpl: CARDINAL = SpecialDisplay.defaultContext.wpl;
        error: LONG INTEGER ← 0;
        xerror, yerror, xyerror: LONG INTEGER;
        a, b, dx, dy, dxdx, dydy: LONG INTEGER;
        xRight: INTEGER ← absCenter.x;
        xLeft: INTEGER ← absCenter.x;
        yLow: WindowOps.ScanLine ← WindowOps.bitmapAddress +
	  WindowOps.SpecialTimesWpl[INTEGER[absCenter.y-yRadius]];
        yHigh: WindowOps.ScanLine ← WindowOps.bitmapAddress +
	  WindowOps.SpecialTimesWpl[INTEGER[absCenter.y + yRadius]];
        safetyCount: INTEGER ← 0;
        IF absCenter.x - xRadius < r.box.left OR absCenter.x + xRadius >= r.box.right
          OR absCenter.y + yRadius >= r.box.bottom
          OR absCenter.y - yRadius < r.box.top OR DisplayOps.HasUnder[r] THEN {
          SpEllipseInternal[
            r, FALSE, window, center, xRadius, yRadius, bounds,
            SpecialDisplay.solid, Display.paintGrayFlags];
          RETURN};
        a ← LONG[yRadius]*LONG[yRadius];
        dxdx ← 2*a;
        b ← LONG[xRadius]*LONG[xRadius];
        dydy ← 2*b;
        dx ← a;
        dy ← b*(1 - 2*yRadius);
        -- Inner Loop
        UNTIL yLow >= yHigh OR safetyCount > xRadius DO
          yLow[xRight] ← TRUE;  -- set bit
          yHigh[xRight] ← TRUE;
          yLow[xLeft] ← TRUE;
          yHigh[xLeft] ← TRUE;
          xerror ← error + dx;
          xyerror ← xerror + dy;
          safetyCount ← safetyCount + 1;
          IF ABS[xyerror] < ABS[xerror] THEN {
            IF ABS[error + dy] < ABS[xyerror] THEN {
              error ← error + dy;
              yLow ← yLow + wpl;
              yHigh ← yHigh - wpl;
              dy ← dy + dydy;
              -- Y varies more quickly now
              -- stay in this loop until done
              UNTIL yLow >= yHigh DO
                yLow[xRight] ← TRUE;
                yHigh[xRight] ← TRUE;
                yLow[xLeft] ← TRUE;
                yHigh[xLeft] ← TRUE;
                yerror ← error + dy;
                xyerror ← yerror + dx;
                IF ABS[xyerror] < ABS[yerror] THEN {
                  error ← xyerror;
                  yLow ← yLow + wpl;
                  yHigh ← yHigh - wpl;
                  xRight ← xRight + 1;
                  xLeft ← xLeft - 1;
                  safetyCount ← safetyCount + 1;
                  dx ← dx + dxdx;
                  dy ← dy + dydy}
                ELSE {
                  error ← yerror;
                  yLow ← yLow + wpl;
                  yHigh ← yHigh - wpl;
                  dy ← dy + dydy};
                ENDLOOP;
              EXIT};
            error ← xyerror;
            yLow ← yLow + wpl;
            yHigh ← yHigh - wpl;
            xRight ← xRight + 1;
            xLeft ← xLeft - 1;
            dx ← dx + dxdx;
            dy ← dy + dydy}
          ELSE {
            error ← xerror;
            xRight ← xRight + 1;
            xLeft ← xLeft - 1;
            dx ← dx + dxdx};
          ENDLOOP;
        yLow[xRight] ← TRUE;
        yLow[xLeft] ← TRUE;
        -- don't need to set yHigh cause its equal to yLow now
        -- loop below fills out long skinny ellipses
        UNTIL safetyCount >= xRadius DO
          xRight ← xRight + 1;
          xLeft ← xLeft - 1;
          yLow[xLeft] ← TRUE;
          yLow[xRight] ← TRUE;
          safetyCount ← safetyCount + 1
          ENDLOOP};
      ENDLOOP};

  SpEllipse: PUBLIC ENTRY PROC [
    window: Handle, center: Window.Place, xRadius, yRadius: INTEGER,
    bounds: Window.BoxHandle, dashes: LineStyle, flags: BitBlt.BitBltFlags,
    context: Special ← SpecialDisplay.defaultContext] = {
    ENABLE UNWIND => NULL;
    filled: BOOLEAN = context.alloc # NIL;
    firstRec: WindowOps.RecList = DisplayOps.FillList[window, filled];
    SpEllipseInternal[
      firstRec, filled, window, center, xRadius, yRadius, bounds, dashes, flags,
      context]};

  SpEllipseInternal: INTERNAL PROC [
    firstRec: WindowOps.RecList, filled: BOOLEAN, window: Handle,
    center: Window.Place, xRadius, yRadius: INTEGER, bounds: Window.BoxHandle,
    dashes: LineStyle, flags: BitBlt.BitBltFlags,
    context: Special ← SpecialDisplay.defaultContext] = {
    absBounds: WindowOps.ScreenBox =
      IF bounds = NIL THEN WindowOps.Bounds[window]
      ELSE WindowOps.AbsoluteBoxHandle[window, bounds];
    -- switch to screen coords
    absCenter: Window.Place = DisplayOps.AbsPlace[window, center];
    IF ~window.inTree THEN RETURN;
    FOR r: WindowOps.RecList ← firstRec, r.link UNTIL r = NIL DO
      IF DisplayOps.Intersect[r, absBounds] THEN {
        ctx: Special = WindowOps.GetContext[r, context];
        error: LONG INTEGER ← 0;
        xerror, yerror, xyerror: LONG INTEGER;
        a, b, dx, dy, dxdx, dydy: LONG INTEGER;
        xRight: INTEGER ← absCenter.x;
        xLeft: INTEGER ← absCenter.x;
        yHighSL: WindowOps.ScanLine ← ctx.bmAddress +
	  WindowOps.SpecialTimesWpl[INTEGER[absCenter.y - yRadius], ctx];
        yLowSL: WindowOps.ScanLine ← ctx.bmAddress +
	  WindowOps.SpecialTimesWpl[INTEGER[absCenter.y + yRadius], ctx];
        yHigh: INTEGER ← absCenter.y - yRadius;
        yLow: INTEGER ← absCenter.y + yRadius;
        xTravel: INTEGER ← 0;
        start1: CARDINAL = 0;
        dOrtho: CARDINAL = 5;
        dDiag: CARDINAL = 7;
        stop1: CARDINAL = dashes.widths[0]*dOrtho;
        start2: CARDINAL = stop1 + dashes.widths[1]*dOrtho;
        stop2: CARDINAL = start2 + dashes.widths[2]*dOrtho;
        start3: CARDINAL = stop2 + dashes.widths[3]*dOrtho;
        stop3: CARDINAL = start3 + dashes.widths[4]*dOrtho;
        dashSum: CARDINAL = stop3 + dashes.widths[5]*dOrtho;
        dashCnt: CARDINAL ← 0;
        thickness: CARDINAL = dashes.thickness;
        left: INTEGER = r.box.left;
        right: INTEGER = r.box.right;
        top: INTEGER = r.box.top;
        bottom: INTEGER = r.box.bottom;
        yMin, yLen: INTEGER;  -- used for filing
        fillLeft, fillRight: FillHandle;
        PaintDot: PROC [x, y: INTEGER, ySL: WindowOps.ScanLine] = INLINE {
          IF x IN [left..right) AND y IN [top..bottom) THEN
            ySL[x] ←
              (SELECT DisplayOps.Shade[flags] FROM
                 white => FALSE,
                 black => TRUE,
                 ENDCASE => NOT ySL[x])};
        FillLine: PROC [xL, xR, ybiz: INTEGER] = INLINE {
          IF ybiz IN [top..bottom) THEN {
            xL ← MAX[xL, left];
            xR ← MIN[xR, right];
            ybiz ← ybiz - yMin;
            fillLeft.xs[ybiz] ← xL;
            fillRight.xs[ybiz] ← xR}};
        ShallowEllipsePart: PROC[] = {
          UNTIL yHighSL >= yLowSL DO
            IF NOT filled THEN {
              IF
                (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2)
                  OR dashCnt IN [start3..stop3)) THEN {
                PaintDot[xRight, yHigh, yHighSL];
                PaintDot[xRight, yLow, yLowSL];
                PaintDot[xLeft, yHigh, yHighSL];
                PaintDot[xLeft, yLow, yLowSL];
                IF thickness > 1 THEN {
		  quotiet: INTEGER = thickness/2;
		  remainder: INTEGER = thickness MOD 2;
		  FOR i: INTEGER IN [1..quotiet] DO
                    xl: INTEGER = xLeft + i;
                    xr: INTEGER = xRight - i;
                    PaintDot[xr, yHigh, yHighSL];
                    PaintDot[xr, yLow, yLowSL];
                    PaintDot[xl, yHigh, yHighSL];
                    PaintDot[xl, yLow, yLowSL];
                    IF i < quotiet OR remainder = 1 THEN {
                      xl: INTEGER = xLeft - i;
                      xr: INTEGER = xRight + i;
                      PaintDot[xr, yHigh, yHighSL];
                      PaintDot[xr, yLow, yLowSL];
                      PaintDot[xl, yHigh, yHighSL];
                      PaintDot[xl, yLow, yLowSL]};
		    ENDLOOP}}}
            ELSE {FillLine[xLeft, xRight, yLow]; FillLine[xLeft, xRight, yHigh]};
            yerror ← error + dy;
            xyerror ← yerror + dx;
            IF ABS[xyerror] < ABS[yerror] THEN {
              error ← xyerror;
              yHighSL ← yHighSL + ctx.wpl;
              yLowSL ← yLowSL - ctx.wpl;
              yHigh ← yHigh + 1;
              yLow ← yLow - 1;
              xRight ← xRight + 1;
              xLeft ← xLeft - 1;
              xTravel ← xTravel + 1;
              dx ← dx + dxdx;
              dy ← dy + dydy;
              dashCnt ← dashCnt + dDiag}
            ELSE {
              error ← yerror;
              yHighSL ← yHighSL + ctx.wpl;
              yLowSL ← yLowSL - ctx.wpl;
              yHigh ← yHigh + 1;
              yLow ← yLow - 1;
              dy ← dy + dydy;
              dashCnt ← dashCnt + dOrtho};
            UNTIL dashCnt < dashSum DO dashCnt ← dashCnt - dashSum ENDLOOP
            ENDLOOP;
          IF NOT filled THEN {
            IF
              (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2)
                OR dashCnt IN [start3..stop3)) THEN {
              PaintDot[xRight, yHigh, yHighSL];
              PaintDot[xLeft, yHigh, yHighSL];
              IF thickness > 1 THEN {
	        quotiet: INTEGER = thickness/2;
		remainder: INTEGER = thickness MOD 2;
	        FOR i: INTEGER IN [1..quotiet] DO
                  xl: INTEGER = xLeft + i;
                  xr: INTEGER = xRight - i;
                  PaintDot[xr, yHigh, yHighSL];
                  PaintDot[xl, yHigh, yHighSL];
                  IF i < quotiet OR remainder = 1 THEN {
                    xl: INTEGER = xLeft - i;
                    xr: INTEGER = xRight + i;
                    PaintDot[xr, yHigh, yHighSL];
                    PaintDot[xl, yHigh, yHighSL]}
		  ENDLOOP}}}
          ELSE {FillLine[xLeft, xRight, yLow]; FillLine[xLeft, xRight, yHigh]}};
        -- start of code
        IF filled THEN {
          yMin ← MAX[r.box.top, absCenter.y - yRadius];
          yLen ← MIN[r.box.bottom, absCenter.y + yRadius] - yMin + 2;
          fillLeft ← ctx.alloc[window, yMin, yLen];
          fillRight ← ctx.alloc[window, yMin, yLen]};
        a ← LONG[yRadius]*LONG[yRadius];
        dxdx ← 2*a;
        b ← LONG[xRadius]*LONG[xRadius];
        dydy ← 2*b;
        dx ← a;
        dy ← b*(1 - 2*yRadius);
        -- right up
        UNTIL yHighSL >= yLowSL OR xTravel > xRadius DO
          IF NOT filled THEN {
            IF
              (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2)
                OR dashCnt IN [start3..stop3)) THEN {
              PaintDot[xRight, yLow, yLowSL];  -- set bit
              PaintDot[xRight, yHigh, yHighSL];
              PaintDot[xLeft, yLow, yLowSL];
              PaintDot[xLeft, yHigh, yHighSL];
              IF thickness > 1 THEN {
	        quotiet: INTEGER = thickness/2;
		remainder: INTEGER = thickness MOD 2;
	        FOR i: INTEGER IN [1..quotiet] DO
                  yl: INTEGER = yLow - i;
                  yh: INTEGER = yHigh + i;
                  yLSL: WindowOps.ScanLine = yLowSL - i*ctx.wpl;
                  yHSL: WindowOps.ScanLine = yHighSL + i*ctx.wpl;
                  PaintDot[xRight, yl, yLSL];  -- set inside bit
                  PaintDot[xRight, yh, yHSL];
                  PaintDot[xLeft, yl, yLSL];
                  PaintDot[xLeft, yh, yHSL];
                  IF i < quotiet OR remainder = 1 THEN {
                    yl: INTEGER = yLow + i;
                    yh: INTEGER = yHigh - i;
                    yLSL: WindowOps.ScanLine = yLowSL + i*ctx.wpl;
                    yHSL: WindowOps.ScanLine = yHighSL - i*ctx.wpl;
                    PaintDot[xRight, yl, yLSL];  -- set inside bit
                    PaintDot[xRight, yh, yHSL];
                    PaintDot[xLeft, yl, yLSL];
                    PaintDot[xLeft, yh, yHSL]}
		  ENDLOOP}}}
          ELSE {FillLine[xLeft, xRight, yLow]; FillLine[xLeft, xRight, yHigh]};
          xerror ← error + dx;
          xyerror ← xerror + dy;
          xTravel ← xTravel + 1;
          IF ABS[xyerror] < ABS[xerror] THEN {
            IF ABS[error + dy] < ABS[xyerror] THEN {
              error ← error + dy;
              yHighSL ← yHighSL + ctx.wpl;
              yLowSL ← yLowSL - ctx.wpl;
              yLow ← yLow - 1;
              yHigh ← yHigh + 1;
              dy ← dy + dydy;
              -- Y varies more quickly now
              ShallowEllipsePart[];
              EXIT};
            error ← xyerror;
            yHighSL ← yHighSL + ctx.wpl;
            yLowSL ← yLowSL - ctx.wpl;
            yLow ← yLow - 1;
            yHigh ← yHigh + 1;
            xRight ← xRight + 1;
            xLeft ← xLeft - 1;
            dx ← dx + dxdx;
            dy ← dy + dydy;
            dashCnt ← dashCnt + dDiag}
          ELSE {
            error ← xerror;
            xRight ← xRight + 1;
            xLeft ← xLeft - 1;
            dx ← dx + dxdx;
            dashCnt ← dashCnt + dOrtho};
          UNTIL dashCnt < dashSum DO dashCnt ← dashCnt - dashSum ENDLOOP;
          REPEAT
            FINISHED => {
              IF NOT filled THEN {
                IF
                  (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2)
                    OR dashCnt IN [start3..stop3)) THEN {
                  PaintDot[xLeft, yHigh, yHighSL];
                  PaintDot[xRight, yHigh, yHighSL];
                  IF thickness > 1 THEN {
		    quotiet: INTEGER = thickness/2;
		    remainder: INTEGER = thickness MOD 2;
		    FOR i: INTEGER IN [1..quotiet] DO
                      yh: INTEGER = yHigh + i;
                      yHSL: WindowOps.ScanLine = yHighSL + i*ctx.wpl;
                      PaintDot[xRight, yh, yHSL];
                      PaintDot[xLeft, yh, yHSL];
                      IF i < quotiet OR remainder = 1 THEN {
                        yh: INTEGER = yHigh - i;
                        yHSL: WindowOps.ScanLine = yHighSL - i*ctx.wpl;
                        PaintDot[xRight, yh, yHSL];
                        PaintDot[xLeft, yh, yHSL]}
		      ENDLOOP}}}
              ELSE {FillLine[xLeft, xRight, yLow]; FillLine[xLeft, xRight, yHigh]}}
          ENDLOOP;
        -- don't need to set yLowSL cause its equal to yLowSL now
        -- loop below fills out long skinny ellipses
        UNTIL xTravel >= xRadius DO
          IF NOT filled THEN {
            IF
              (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2)
                OR dashCnt IN [start3..stop3)) THEN {
              PaintDot[xLeft, yHigh, yHighSL];
              PaintDot[xRight, yHigh, yHighSL];
              IF thickness > 1 THEN {
	        quotiet: INTEGER = thickness/2;
	        remainder: INTEGER = thickness MOD 2;
	        FOR i: INTEGER IN [1..quotiet] DO
                  yh: INTEGER = yHigh + i;
                  yHSL: WindowOps.ScanLine = yHighSL + i*ctx.wpl;
                  PaintDot[xRight, yh, yHSL];
                  PaintDot[xLeft, yh, yHSL];
                  IF i < quotiet OR remainder = 1 THEN {
                    yh: INTEGER = yHigh - i;
                    yHSL: WindowOps.ScanLine = yHighSL - i*ctx.wpl;
                    PaintDot[xRight, yh, yHSL];
                    PaintDot[xLeft, yh, yHSL]}
		  ENDLOOP}}}
          ELSE {
            diff: INTEGER = xRadius - xTravel;
            FillLine[xRight + diff, xLeft - diff, yHigh];
            xTravel ← xRadius};
          xRight ← xRight + 1;
          xLeft ← xLeft - 1;
          xTravel ← xTravel + 1;
          dashCnt ← dashCnt + dOrtho;
          UNTIL dashCnt < dashSum DO dashCnt ← dashCnt - dashSum ENDLOOP;
          ENDLOOP};
      ENDLOOP};


  END.