<> <> <> <> <<>> DIRECTORY Imager, ImagerBackdoor, ImagerColor, ImagerPath, ImagerTransformation, IO, Pipal, PipalInt, PipalPaint, PipalReal, Rope, TerminalIO; PipalPaintImpl: CEDAR MONITOR LOCKS queue USING queue: Queue IMPORTS Imager, ImagerBackdoor, ImagerColor, ImagerTransformation, IO, Pipal, PipalInt, PipalReal, Rope, TerminalIO EXPORTS PipalPaint = BEGIN OPEN PipalPaint; <> debugPrintEnqueued: BOOL _ FALSE; -- When TRUE, prints all enqueued requests debugPrintDequeued: BOOL _ FALSE; -- When TRUE, prints all dequeued requests debugOptimize: BOOL _ TRUE; debugConservativeClip: BOOL _ TRUE; debugTestForEmpty: BOOL _ FALSE; debugClipArea: BOOL _ FALSE; -- set to TRUE to see the area redisplayed! debugContext: Imager.Context _ NIL; <> paintMethod: PUBLIC Pipal.Method _ Pipal.RegisterMethod["Paint"]; Paint: PUBLIC PaintProc = { data: REF _ Pipal.ObjectMethod[object, paintMethod]; debugContext _ context; IF data=NIL THEN PaintFromEnumerate[object, context] ELSE (NARROW [data, REF PaintProc]^)[object, context]; }; PaintFromEnumerate: PaintProc = { EachChild: PipalReal.EachChildProc = { TransformAndPaint: PROC = { Imager.ConcatT[context, transformation]; Paint[child, context]; }; Imager.DoSave[context, TransformAndPaint]; }; transformation: PipalReal.Transformation _ PipalReal.CreateTransformation[]; [] _ PipalReal.Enumerate[object, EachChild, transformation]; PipalReal.DestroyTransformation[transformation]; }; <> paintMethodProp: PUBLIC ATOM _ $PaintMethodAnnotation; CreatePaintMethodAnnotation: PUBLIC PROC [child: Pipal.Object, paint: PaintProc] RETURNS [annotation: Pipal.Annotation] = { annotation _ Pipal.CreateAnnotation[child, paintMethodProp, NEW [PaintProc _ paint]]; }; PaintAnnotation: PaintProc = { annotation: Pipal.Annotation _ NARROW [object]; paint: PaintProc _ IF annotation.key=paintMethodProp THEN (NARROW [annotation.value, REF PaintProc]^) ELSE Paint; paint[annotation.child, context]; }; <> Rectangle: TYPE = PipalReal.Rectangle; Rectangles: TYPE = LIST OF Rectangle; enumerateAreaMethod: PUBLIC Pipal.Method _ Pipal.RegisterMethod["EnumerateArea"]; EnumerateArea: PUBLIC EnumerateAreaProc = { data: REF _ Pipal.ObjectMethod[area, enumerateAreaMethod]; quit _ (IF data=NIL THEN AreaFromEnumerate ELSE (NARROW [data, REF EnumerateAreaProc]^))[area, each, transformation]; }; AreaFromEnumerate: EnumerateAreaProc = { EachChild: PipalReal.EachChildProc = {quit _ EnumerateArea[child, each, transformation]}; quit _ PipalReal.Enumerate[area, EachChild, transformation]; }; emptyArea: PUBLIC Area = Pipal.void; fullArea: PUBLIC Area = CreateExplicitArea[PipalReal.fullRectangle]; -- anything would do ... IsEmptyArea: PUBLIC PROC [area: Area] RETURNS [empty: BOOL] = { transformation: PipalReal.Transformation _ PipalReal.CreateTransformation[]; empty _ NOT EnumerateArea[area, PipalReal.AlwaysQuit, transformation]; PipalReal.DestroyTransformation[transformation]; }; AreaToRope: PUBLIC PROC [area: Area] RETURNS [rope: Pipal.ROPE] = { transformation: PipalReal.Transformation _ PipalReal.CreateTransformation[]; EachRect: PipalReal.RectangleProc = { rope _ Rope.Cat[rope, PipalReal.RectangleToRope[rect], " "]; }; rope _ "{"; [] _ EnumerateArea[area, EachRect, transformation]; PipalReal.DestroyTransformation[transformation]; RETURN [Rope.Cat[rope, "}"]]; }; <> ExplicitArea: TYPE = REF ExplicitAreaRec; ExplicitAreaRec: TYPE = RECORD [rectangle: Rectangle]; explicitAreaClass: PRIVATE Pipal.Class _ Pipal.RegisterClass[name: "ExplicitArea", type: CODE [ExplicitAreaRec]]; AreaExplicitArea: EnumerateAreaProc = { ea: ExplicitArea _ NARROW [area]; quit _ each[ea.rectangle]; }; BBoxExplicitArea: PipalReal.BBoxProc = { ea: ExplicitArea _ NARROW [object]; bbox _ PipalReal.TransformRectangle[transformation, ea.rectangle]; }; CreateExplicitArea: PUBLIC PROC [rectangle: PipalReal.Rectangle] RETURNS [area: Area] = { area _ NEW [ExplicitAreaRec _ [rectangle]]; }; ConsRect: PROC [rect: Rectangle, rects: Rectangles] RETURNS [Rectangles] = { RETURN [IF PipalReal.IsDegeneratedRectangle[rect] THEN rects ELSE CONS [rect, rects]]; }; CollectRects: PROC [area: Area, transformation: PipalReal.Transformation] RETURNS [rects: Rectangles _ NIL] = { InsertEach: PipalReal.RectangleProc = {rects _ ConsRect[rect, rects]}; [] _ EnumerateArea[area, InsertEach, transformation]; }; IntersectRects: PROC [rects1, rects2: Rectangles] RETURNS [rects: Rectangles _ NIL] = { FOR list1: Rectangles _ rects1, list1.rest WHILE list1#NIL DO rect1: Rectangle _ list1.first; FOR list2: Rectangles _ rects2, list2.rest WHILE list2#NIL DO rects _ ConsRect[PipalReal.IntersectBox[rect1, list2.first], rects]; ENDLOOP; ENDLOOP; }; DecomposeRects: PROC [r, clip: Rectangle] RETURNS [union: Rectangles _ NIL] = { InsertEach: PipalReal.RectangleProc = {union _ CONS [rect, union]}; [] _ PipalReal.DecomposeRect[r: r, clip: clip, outside: InsertEach]; }; ComplementRects: PROC [rects1, rects2: Rectangles] RETURNS [rects: Rectangles _ NIL] = { FOR list1: Rectangles _ rects1, list1.rest WHILE list1#NIL DO rect1: Rectangle _ list1.first; inter: Rectangles _ LIST [rect1]; FOR list2: Rectangles _ rects2, list2.rest WHILE list2#NIL DO inter _ IntersectRects[inter, DecomposeRects[r: list2.first, clip: rect1]]; ENDLOOP; FOR list2: Rectangles _ inter, list2.rest WHILE list2#NIL DO rects _ ConsRect[list2.first, rects]; ENDLOOP; ENDLOOP; }; IntersectionArea: TYPE = REF IntersectionAreaRec; IntersectionAreaRec: TYPE = RECORD [sub: BOOL, area1, area2: Area]; intersectionAreaClass: PRIVATE Pipal.Class _ Pipal.RegisterClass[name: "IntersectionArea", type: CODE [IntersectionAreaRec]]; AreaIntersectionArea: EnumerateAreaProc = { ia: IntersectionArea = NARROW [area]; subRects1, subRects2: Rectangles; subRects1 _ CollectRects[ia.area1, transformation]; subRects2 _ CollectRects[ia.area2, transformation]; FOR list: Rectangles _ (IF ia.sub THEN ComplementRects ELSE IntersectRects)[subRects1, subRects2], list.rest WHILE list#NIL DO IF each[list.first] THEN RETURN [TRUE]; ENDLOOP; }; BBoxIntersectionArea: PipalReal.BBoxProc = { ia: IntersectionArea = NARROW [object]; bbox _ PipalReal.BBox[ia.area1, transformation]; IF NOT ia.sub THEN bbox _ PipalReal.BoundingBox[PipalReal.BBox[ia.area2, transformation], bbox]; }; IntersectArea: PUBLIC PROC [area1, area2: Area] RETURNS [area: Area] = { area _ NEW [IntersectionAreaRec _ [FALSE, area1, area2]]; }; SubArea: PUBLIC PROC [area1, area2: Area] RETURNS [area: Area] = { area _ NEW [IntersectionAreaRec _ [TRUE, area1, area2]]; }; ChildishArea: TYPE = REF ChildishAreaRec; ChildishAreaRec: TYPE = RECORD [child: Pipal.Object, option: AreaOption]; childishAreaClass: PRIVATE Pipal.Class _ Pipal.RegisterClass[name: "ChildishArea", type: CODE [ChildishAreaRec]]; ApplyOnEdges: PROC [rect: Rectangle, each: PipalReal.RectangleProc] RETURNS [quit: BOOL _ FALSE] = { IF each[[[rect.base.x, rect.base.y], [rect.size.x, 0]]] THEN RETURN [TRUE]; IF each[[[rect.base.x, rect.base.y], [0, rect.size.y]]] THEN RETURN [TRUE]; IF each[[[rect.base.x+rect.size.x, rect.base.y], [0, rect.size.y]]] THEN RETURN [TRUE]; IF each[[[rect.base.x, rect.base.y+rect.size.y], [rect.size.x, 0]]] THEN RETURN [TRUE]; }; AreaChildishArea: EnumerateAreaProc = { ca: ChildishArea _ NARROW [area]; bbox: Rectangle = PipalReal.BBox[ca.child, transformation]; ExtendedEach: PipalReal.RectangleProc = {quit _ each[PipalReal.Extend[rect, 1.0]]}; <> EachChildEdge: PipalReal.RectangleProc ~ {quit _ ApplyOnEdges[rect, ExtendedEach]}; SELECT ca.option FROM bboxChild => quit _ ExtendedEach[bbox]; edgesChild => quit _ ApplyOnEdges[bbox, ExtendedEach]; edgesChildren => quit _ EnumerateArea[ca.child, EachChildEdge, transformation]; ENDCASE => ERROR; }; BBoxChildishArea: PipalReal.BBoxProc = { ca: ChildishArea = NARROW [object]; bbox _ PipalReal.Extend[PipalReal.BBox[ca.child, transformation], 1.0]; }; ChildArea: PUBLIC PROC [child: Pipal.Object, option: AreaOption] RETURNS [area: Area] = { area _ NEW [ChildishAreaRec _ [child, option]]; }; PaintAreaOutlineAnnotation: PaintProc = { PaintAreaOutline[context, object]; }; CreatePaintAreaOutline: PUBLIC PROC [area: Area] RETURNS [annotation: Pipal.Annotation] = { annotation _ CreatePaintMethodAnnotation[area, PaintAreaOutlineAnnotation]; }; <> nullRequest: Request = [none, emptyArea]; -- all what is important here is that referencing/copying it does not cause a memory fault. RequestToRope: PUBLIC PROC [request: Request] RETURNS [Pipal.ROPE] = { RETURN [Rope.Cat[ "Request { ", SELECT request.type FROM none => "none", clearArea => Rope.Cat["clearArea ", AreaToRope[request.area]], paintArea => Rope.Cat["paintArea ", Pipal.DescribeToRope[request.data], " ", AreaToRope[request.area]], clearAndPaint => Rope.Cat["clearAndPaint ", Pipal.DescribeToRope[request.data], " ", AreaToRope[request.area]], scale => Rope.Cat["scale ", PipalReal.TransformationToRope[NARROW [request.data]]], other => "other", ENDCASE => ERROR, " }" ]]; }; <<>> Mod: PROC [index: NAT, max: NAT] RETURNS [NAT] = INLINE { RETURN [IF index requests[index] _ ClipRequest[requests[index], area]; scale => RETURN; none, other => {}; ENDCASE => ERROR; ENDLOOP; }; Enqueue: PUBLIC ENTRY PROC [queue: Queue, request: Request, reverse: BOOL _ FALSE] = { ENABLE UNWIND => NULL; new: NAT; max: NAT _ queue.requests.max; IF queue.size>=max THEN { requests: REF Requests _ NEW [Requests[max*2]]; FOR i: NAT IN [0 .. max) DO requests[i] _ queue.requests[i] ENDLOOP; FOR i: NAT IN [max .. max*2) DO requests[i] _ nullRequest ENDLOOP; queue.requests _ requests; max _ max*2; }; new _ Mod[queue.start+(IF reverse THEN max-1 ELSE queue.size), max]; IF debugPrintEnqueued THEN TerminalIO.PutF["Enqueued: %g\n", IO.rope[RequestToRope[request]]]; queue.requests[new] _ request; queue.size _ queue.size+1; IF reverse THEN queue.start _ new; IF NOT debugOptimize THEN RETURN; SELECT request.type FROM clearArea, clearAndPaint => IF NOT reverse THEN OptimizeClear[queue, request.area, queue.start, queue.size-1]; ENDCASE => {}; }; Dequeue: PUBLIC ENTRY PROC [queue: Queue] RETURNS [empty: BOOL, request: Request] = { ENABLE UNWIND => NULL; Decr: PROC = { queue.start _ Mod[queue.start+1, queue.requests.max]; queue.size _ queue.size-1; }; SkipNone: PROC = { WHILE queue.size#0 AND queue.requests[queue.start].type=none DO Decr[] ENDLOOP; }; SkipNone[]; IF queue.size=0 THEN RETURN [TRUE, nullRequest]; request _ queue.requests[queue.start]; Decr[]; empty _ FALSE; <> SkipNone[]; WHILE queue.size#0 DO next: Request _ queue.requests[queue.start]; IF request.type#clearAndPaint OR next.type#clearAndPaint OR request.data#next.data THEN EXIT; -- hack, of course! request.area _ Pipal.CreateOverlay[LIST [request.area, next.area]]; Decr[]; SkipNone[]; ENDLOOP; IF debugPrintDequeued THEN TerminalIO.PutF["Dequeued: %g\n", IO.rope[RequestToRope[request]]]; }; <> PaintIntTranslation: PaintProc = { translation: PipalInt.Translation _ NARROW [object]; Imager.TranslateT[context, PipalReal.IntToRealVector[translation.vector]]; Paint[translation.child, context]; Imager.TranslateT[context, PipalReal.IntToRealVector[PipalInt.Neg[translation.vector]]]; }; <> PaintOverlay: PaintProc = { transformation: PipalReal.Transformation _ PipalReal.CreateTransformation[]; -- changed the day PaintProcs have a transformation! overlay: Pipal.Overlay _ NARROW [object]; bounds: Imager.Rectangle _ ImagerBackdoor.GetBounds[context ! Imager.Error => GOTO PaintAnyway]; clip: Rectangle _ [[bounds.x, bounds.y], [bounds.w, bounds.h]]; FOR i: NAT DECREASING IN [0..overlay.size) DO IF PipalReal.DoRectanglesIntersect[clip, PipalReal.BBox[overlay[i], transformation]] THEN Paint[overlay[i], context]; ENDLOOP; PipalReal.DestroyTransformation[transformation]; EXITS PaintAnyway => PaintFromEnumerate[object, context]; }; <> RectanglePath: PROC [r: Rectangle, moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc] = { moveTo[r.base]; lineTo[[r.base.x+r.size.x, r.base.y]]; lineTo[[r.base.x+r.size.x, r.base.y+r.size.y]]; lineTo[[r.base.x, r.base.y+r.size.y]]; }; AreaPath: PROC [area: Area, moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc] = { transformation: PipalReal.Transformation _ PipalReal.CreateTransformation[]; EachRect: PipalReal.RectangleProc = {RectanglePath[rect, moveTo, lineTo]}; [] _ EnumerateArea[area, EachRect, transformation]; PipalReal.DestroyTransformation[transformation]; }; white: Imager.ConstantColor = ImagerColor.Find["Xerox/Research/ChipNDale/CD/InitialColor"]; ClearArea: PUBLIC PROC [context: Imager.Context, area: Area] = { Path: Imager.PathProc ~ {AreaPath[area, moveTo, lineTo]}; ClearAreaInternal: PROC = { SetColor[context, IF debugClipArea THEN Imager.MakeGray[0.25] ELSE white]; Imager.MaskFill[context: context, path: Path, oddWrap: FALSE]; }; Imager.DoSave[context, ClearAreaInternal]; }; ClipAndPaint: PUBLIC PROC [context: Imager.Context, object: Pipal.Object, clipArea: Area] = { Path: Imager.PathProc ~ {AreaPath[clipArea, moveTo, lineTo]}; ClipAndPaintInternal: PROC = { Imager.Clip[context: context, path: Path, oddWrap: FALSE]; Paint[object, context]; }; Imager.DoSave[context, ClipAndPaintInternal]; }; ClearClipAndPaint: PUBLIC PROC [context: Imager.Context, object: Pipal.Object, clipArea: Area] = { Path: Imager.PathProc ~ {AreaPath[clipArea, moveTo, lineTo]}; ClearClipAndPaintInternal: PROC = { SetColor[context, IF debugClipArea THEN Imager.MakeGray[0.25] ELSE white]; Imager.MaskFill[context: context, path: Path, oddWrap: FALSE]; Imager.Clip[context: context, path: Path, oddWrap: FALSE]; Paint[object, context]; }; Imager.DoSave[context, ClearClipAndPaintInternal]; }; PaintOutline: PUBLIC PROC [context: Imager.Context, r: Rectangle, color: Color _ Imager.black] = { SetColor[context, color]; Imager.MaskVector[context, [r.base.x, r.base.y], [r.base.x+r.size.x, r.base.y]]; Imager.MaskVector[context, [r.base.x, r.base.y], [r.base.x, r.base.y+r.size.y]]; Imager.MaskVector[context, [r.base.x+r.size.x, r.base.y], [r.base.x+r.size.x, r.base.y+r.size.y]]; Imager.MaskVector[context, [r.base.x, r.base.y+r.size.y], [r.base.x+r.size.x, r.base.y+r.size.y]]; }; PaintAreaOutline: PUBLIC PROC [context: Imager.Context, area: Area, color: Color _ Imager.black] = { transformation: PipalReal.Transformation _ PipalReal.CreateTransformation[]; Each: PipalReal.RectangleProc ~ {PaintOutline[context, rect, color]}; [] _ EnumerateArea[area, Each, transformation]; PipalReal.DestroyTransformation[transformation]; }; PaintArea: PUBLIC PROC [context: Imager.Context, area: Area, color: Color _ Imager.black] = { transformation: PipalReal.Transformation _ PipalReal.CreateTransformation[]; EachRect: PipalReal.RectangleProc ~ { Imager.MaskRectangle[context, [rect.base.x, rect.base.y, rect.size.x, rect.size.y]]; }; SetColor[context, color]; [] _ EnumerateArea[area, EachRect, transformation]; PipalReal.DestroyTransformation[transformation]; }; PaintText: PUBLIC PROC [context: Imager.Context, contents: Pipal.ROPE, font: Imager.Font, color: Color _ Imager.black] = { Imager.SetFont[context, font]; Imager.SetColor[context, color]; Imager.SetXY[context, [0, 0]]; Imager.ShowRope[context, contents]; }; debugFlipText: BOOL _ FALSE; -- will be true if the flipping did not work! PaintFlippedText: PUBLIC PROC [context: Imager.Context, contents: Pipal.ROPE, font: Imager.Font, bbox: PipalInt.Rectangle, color: Color _ Imager.black] = { vec: Imager.VEC _ ImagerBackdoor.TransformVec[context, [1, 1], client, surface ! Imager.Error => GOTO JustPaint]; tt: Imager.Transformation _ PipalReal.IntToRealTransformation[[ [IF vec.x<0 THEN bbox.size.x+bbox.base.x+bbox.base.x ELSE 0, IF vec.y<0 THEN bbox.size.y+bbox.base.y+bbox.base.y ELSE 0], SELECT TRUE FROM vec.y<0 AND vec.x<0 => rotate180, vec.y<0 => rotate180X, vec.x<0 => mirrorX, ENDCASE => identity ]]; IF ImagerBackdoor.GetFont[context ! Imager.Error => GOTO JustPaint]#font THEN Imager.SetFont[context, font ! Imager.Error => GOTO JustPaint]; SetColor[context, color]; Imager.ConcatT[context, tt]; vec _ ImagerBackdoor.TransformVec[context, [1, 1], client, surface]; debugFlipText _ debugFlipText OR vec.x<0 OR vec.y<0; Imager.SetXY[context, [0, 0]]; Imager.ShowRope[context, contents]; Imager.ConcatT[context, ImagerTransformation.Invert[tt]]; PipalReal.DestroyTransformation[tt]; EXITS JustPaint => PaintText[context, contents, font, color]; }; invertingBlack: PUBLIC Imager.Color _ ImagerBackdoor.MakeStipple[0FFFFH, TRUE]; invertingLightGray: PUBLIC Imager.Color _ ImagerBackdoor.MakeStipple[0208H, TRUE]; invertingDarkGray: PUBLIC Imager.Color _ ImagerBackdoor.MakeStipple[0A5A5H, TRUE]; GetBounds: PUBLIC PROC [context: Imager.Context] RETURNS [bounds: PipalReal.Rectangle] = { ibounds: Imager.Rectangle _ ImagerBackdoor.GetBounds[context ! Imager.Error => GOTO ReturnUniverse]; bounds _ [[ibounds.x, ibounds.y], [ibounds.w, ibounds.h]]; EXITS ReturnUniverse => bounds _ PipalReal.fullRectangle; }; SetColor: PUBLIC PROC [context: Imager.Context, color: Color] = { IF ImagerBackdoor.GetColor[context ! Imager.Error => GOTO SetAnyway]#color THEN Imager.SetColor[context, color]; -- the exception is needed, because ImagerBackdoor.GetColor might not be implemented (e.g. in an interpress context!) EXITS SetAnyway => Imager.SetColor[context, color]; }; <> Pipal.PutClassMethod[explicitAreaClass, enumerateAreaMethod, NEW [EnumerateAreaProc _ AreaExplicitArea]]; Pipal.PutClassMethod[explicitAreaClass, PipalReal.bboxMethod, NEW [PipalReal.BBoxProc _ BBoxExplicitArea]]; Pipal.PutClassMethod[intersectionAreaClass, enumerateAreaMethod, NEW [EnumerateAreaProc _ AreaIntersectionArea]]; Pipal.PutClassMethod[intersectionAreaClass, PipalReal.bboxMethod, NEW [PipalReal.BBoxProc _ BBoxIntersectionArea]]; Pipal.PutClassMethod[childishAreaClass, enumerateAreaMethod, NEW [EnumerateAreaProc _ AreaChildishArea]]; Pipal.PutClassMethod[childishAreaClass, PipalReal.bboxMethod, NEW [PipalReal.BBoxProc _ BBoxChildishArea]]; Pipal.PutClassMethod[Pipal.annotationClass, paintMethod, NEW [PaintProc _ PaintAnnotation]]; Pipal.PutClassMethod[PipalInt.translationClass, paintMethod, NEW [PaintProc _ PaintIntTranslation]]; -- speed up Pipal.PutClassMethod[Pipal.overlayClass, paintMethod, NEW [PaintProc _ PaintOverlay]]; -- speed up END.