-- 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.