<> <> <> DIRECTORY Real USING [FixI, Float, RoundLI], Imager USING [Context, Transformation, Clipper], ImagerBasic USING [Path, Pair, IntPair, IntRectangle, Rectangle, Visibility, Transformation], ImagerDisplay USING [ImagingSpace, ContextData, SetViewerTransform], ImagerTransform USING [Transform, TransformRectangle, TransformIntRectangle, Invert], ImagerMasks USING [Mask, Create, IsVisible, Difference, And, InlineBox], ImagerScanConverter USING [ConvertToRuns, DevicePath, CreatePath, DeviceRectangle]; ImagerDisplayClipperImpl: CEDAR PROGRAM IMPORTS ImagerTransform, Real, ImagerDisplay, ImagerMasks, ImagerScanConverter EXPORTS Imager, ImagerDisplay ~ BEGIN OPEN ImagerBasic; Context: TYPE ~ Imager.Context; Transformation: TYPE ~ Imager.Transformation; ImagingSpace: TYPE ~ ImagerDisplay.ImagingSpace; Mask: TYPE ~ ImagerMasks.Mask; DisplayContext: TYPE ~ ImagerDisplay.ContextData; <> NonRectangularClipper: PUBLIC SIGNAL ~ CODE; NotImplementedYet: PUBLIC SIGNAL ~ CODE; <> <> GetClipper: PUBLIC PROC [context: Context] RETURNS [Imager.Clipper] ~ { displayContext: DisplayContext = NARROW[context.data]; clipper: REF Mask _ NEW[ Mask _ displayContext.clientClipper ]; RETURN [ clipper ]; }; SetClipper: PUBLIC PROC [context: Context, clipper: Imager.Clipper] ~ { displayContext: DisplayContext = NARROW[context.data]; mask: REF Mask = NARROW[clipper]; displayContext.clientClipper _ mask^; ValidateCompositeClipper[displayContext]; }; ClipPath: PUBLIC PROC [context: Context, outline: Path, exclude: BOOLEAN _ FALSE] ~ { <> displayContext: DisplayContext = NARROW[context.data]; displayContext.clientClipper _ ClipMask[ displayContext.clientClipper, MaskFromPath[displayContext, outline], exclude ]; ValidateCompositeClipper[displayContext]; }; ClipRectangle: PUBLIC PROC [context: Context, outline: Rectangle, exclude: BOOLEAN _ FALSE] ~ { <> displayContext: DisplayContext = NARROW[context.data]; IF NOT EasyClipper[displayContext, exclude] THEN displayContext.clientClipper _ ClipMask[ displayContext.clientClipper, MaskFromRectangle[displayContext, outline], exclude ] ELSE { -- easy case, transform the outline rectangle and clip it to clip rectangle OPEN displayContext.clientClipper; sMax, fMax: INTEGER; outline _ ImagerTransform.TransformRectangle[outline, displayContext.transform]; fMin _ MAX[fMin, Real.FixI[outline.x]]; sMin _ MAX[sMin, Real.FixI[outline.y]]; fMax _ MIN[fMin + fSize, Real.FixI[outline.x + outline.w]]; sMax _ MIN[sMin + sSize, Real.FixI[outline.y + outline.h]]; fSize _ IF fMax > fMin THEN fMax - fMin + 1 ELSE 0; sSize _ IF sMax > sMin THEN sMax - sMin + 1 ELSE 0; }; ValidateCompositeClipper[displayContext]; }; TestRectangle: PUBLIC PROC [context: Context, area: Rectangle] RETURNS [Visibility] ~ { displayContext: DisplayContext = NARROW[context.data]; IF NOT EasyClipper[displayContext, FALSE] THEN RETURN[ ImagerMasks.IsVisible[ MaskFromRectangle[displayContext, area], displayContext.clipper ] ] ELSE { OPEN displayContext.clipper; -- easy case, real rectangle, clip rectangle area _ ImagerTransform.TransformRectangle[area, displayContext.transform]; IF (fMin + fSize <= Real.FixI[area.x]) OR (fMin >= Real.FixI[area.x + area.w]) OR (sMin + sSize <= Real.FixI[area.y]) OR (sMin >= Real.FixI[area.y + area.h]) THEN RETURN[ invisible ] ELSE IF (fMin + fSize <= Real.FixI[area.x + area.w]) AND (fMin >= Real.FixI[area.x]) AND (sMin + sSize <= Real.FixI[area.y + area.h]) AND (sMin >= Real.FixI[area.y]) THEN RETURN[ visible ] ELSE RETURN[ partlyVisible ]; }; }; <> ClipIntRectangle: PUBLIC PROC [context: Context, outline: IntRectangle, exclude: BOOLEAN _ FALSE] ~ { <> displayContext: DisplayContext = NARROW[context.data]; IF NOT EasyClipper[displayContext, exclude] THEN ClipRectangle[context, FloatRect[outline], exclude] ELSE { -- easy case, transform the outline rectangle and clip it to clip rectangle OPEN displayContext.clientClipper; sMax, fMax: INTEGER; outline _ ImagerTransform.TransformIntRectangle[outline,displayContext.clientTransform]; fMin _ MAX[fMin, outline.x]; sMin _ MAX[sMin, outline.y]; fMax _ MIN[fMin + fSize, outline.x + outline.w]; sMax _ MIN[sMin + sSize, outline.y + outline.h]; fSize _ IF fMax > fMin THEN fMax - fMin + 1 ELSE 0; sSize _ IF sMax > sMin THEN sMax - sMin + 1 ELSE 0; }; ValidateCompositeClipper[displayContext]; }; TestIntRectangle: PUBLIC PROC [context: Context, area: IntRectangle] RETURNS [Visibility] ~ { displayContext: DisplayContext = NARROW[context.data]; IF NOT EasyClipper[displayContext, FALSE] THEN RETURN[ TestRectangle[context, FloatRect[area]] ] ELSE { OPEN displayContext.clipper; -- easy case, int rectangle, clip rectangle area _ ImagerTransform.TransformIntRectangle[area, displayContext.clientTransform]; IF (fMin + fSize <= area.x) OR (fMin >= area.x + area.w) OR (sMin + sSize <= area.y) OR (sMin >= area.y + area.h) THEN RETURN[ invisible ] ELSE IF (fMin + fSize <= area.x + area.w) AND (fMin >= area.x) AND (sMin + sSize <= area.y + area.h) AND (sMin >= area.y) THEN RETURN[ visible ] ELSE RETURN[ partlyVisible ]; }; }; DoWithoutClipping: PUBLIC PROC [context: Context, bounds: IntRectangle, callBack: PROC[Context]] ~ { <> displayContext: DisplayContext = NARROW[context.data]; IF TestIntRectangle[context, bounds] = visible THEN displayContext.noClip _ TRUE; callBack[ context ]; displayContext.noClip _ FALSE; }; <> SetView: PUBLIC PROC [context: Context, box: IntRectangle, halftoneOrigin: IntPair _ [0, 0]] ~ { SetViewerClipper[context, box]; ImagerDisplay.SetViewerTransform[context, [1., 0., box.x, 0., 1., box.y, identity]]; }; ClipView: PUBLIC PROC [context: Context, box: IntRectangle, exclude: BOOLEAN] ~ { displayContext: DisplayContext = NARROW[context.data]; IF exclude = FALSE THEN ClipViewerClipper[context, box] ELSE displayContext.viewerClipper _ ClipMask[ displayContext.clientClipper, MaskFromRectangle[ displayContext, FloatRect[box] ], exclude ]; }; GetSurfaceBounds: PUBLIC PROC [context: Context] RETURNS [IntRectangle] ~ { displayContext: DisplayContext = NARROW[context.data]; { OPEN displayContext.deviceClipper; RETURN [ [fMin, sMin, fSize, sSize] ]; }; }; GetViewBounds: PUBLIC PROC [context: Context] RETURNS [IntRectangle] ~ { displayContext: DisplayContext = NARROW[context.data]; { OPEN displayContext.viewerClipper; RETURN [ ImagerTransform.TransformIntRectangle[ [fMin, sMin, fSize, sSize], ImagerTransform.Invert[ displayContext.deviceTransform ] ] ]; }; }; <> SetViewerClipper: PUBLIC PROC [context: Context, outline: IntRectangle] ~ { displayContext: DisplayContext = NARROW[context.data]; <> outline _ ImagerTransform.TransformIntRectangle[outline, displayContext.deviceTransform]; displayContext.viewerClipper _ [outline.y, outline.x, outline.h, outline.w, NIL]; ValidateCompositeClipper[displayContext]; }; ClipViewerClipper: PUBLIC PROC [context: Context, outline: IntRectangle] ~ { displayContext: DisplayContext = NARROW[context.data]; <> outline _ ImagerTransform.TransformIntRectangle[outline, displayContext.deviceTransform]; { OPEN displayContext.viewerClipper; sMax, fMax: INTEGER; fMin _ MAX[fMin, outline.x]; sMin _ MAX[sMin, outline.y]; fMax _ MIN[fMin + fSize, outline.x + outline.w]; sMax _ MIN[sMin + sSize, outline.y + outline.h]; fSize _ IF fMax > fMin THEN fMax - fMin + 1 ELSE 0; sSize _ IF sMax > sMin THEN sMax - sMin + 1 ELSE 0; }; ValidateCompositeClipper[displayContext]; }; SetDeviceClipper: PUBLIC PROC [context: Context, outline: IntRectangle] ~ { displayContext: DisplayContext = NARROW[context.data]; displayContext.deviceClipper _ [outline.y, outline.x, outline.h, outline.w, NIL]; ValidateCompositeClipper[displayContext]; }; <> FloatRect: PROC [rect: IntRectangle] RETURNS [Rectangle] ~ INLINE { RETURN[ [ Real.Float[rect.x], Real.Float[rect.y], Real.Float[rect.w], Real.Float[rect.h] ] ]; }; EasyClipper: PROC [displayContext: DisplayContext, exclude: BOOLEAN] RETURNS [BOOLEAN] ~ INLINE { RETURN [ IF displayContext.clientTransform.b # 0. -- Rotation or Skew? OR displayContext.clientTransform.d # 0. OR exclude -- Exclusion? OR displayContext.clientClipper.refRep # NIL THEN FALSE ELSE TRUE ]; }; ClipMask: PROC [clpr, new: Mask, exclude: BOOLEAN] RETURNS [Mask] ~ { IF clpr.refRep = NIL AND new.refRep = NIL AND exclude = FALSE THEN { fMax, sMax, fMin, sMin, fSize, sSize: INTEGER; fMin _ MAX[clpr.fMin, new.fMin]; sMin _ MAX[clpr.sMin, new.sMin]; fMax _ MIN[clpr.fMin + clpr.fSize, new.fMin + new.fSize]; sMax _ MIN[clpr.sMin + clpr.sSize, new.sMin + new.sSize]; fSize _ IF fMax > clpr.fMin THEN fMax - clpr.fMin ELSE 0; sSize _ IF sMax > clpr.sMin THEN sMax - clpr.sMin ELSE 0; RETURN [ [sMin, fMin, sSize, fSize, NIL] ]; } ELSE IF exclude THEN RETURN[ ImagerMasks.Difference[clpr, new] ] ELSE RETURN[ ImagerMasks.And[clpr, new] ]; }; BoxFromMask: PROC [mask: Mask] RETURNS [ ImagerScanConverter.DeviceRectangle ] ~ { RETURN [ [mask.sMin, mask.fMin, mask.sSize, mask.fSize] ]; }; MaskFromPath: PROC [displayContext: DisplayContext, area: Path] RETURNS [Mask] ~ { GenPath: PROC [move: PROC[s, f: REAL], line: PROC[s, f: REAL], curve: PROC[s1, f1, s2, f2, s3, f3: REAL]] = { m: ImagerBasic.Transformation _ displayContext.transform; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ <> m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; Xmove: PROC [p: Pair] ~ { pp: Pair ~ Xform[p]; move[pp.y, pp.x] }; Xline: PROC [p: Pair] ~ { pp: Pair ~ Xform[p]; line[pp.y, pp.x] }; Xcurve: PROC [p1, p2, p3: Pair] ~ { pp1: Pair ~ Xform[p1]; pp2: Pair ~ Xform[p2]; pp3: Pair ~ Xform[p3]; curve[pp1.x, pp1.y, pp2.x, pp2.y, pp3.x, pp3.y] }; area.generateProc[area, Xmove, Xline, Xcurve]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { RunFromScanConverter: PROC [sMin, fMin: INTEGER, fSize: NAT] = { run[s: sMin, fMin: fMin, fSize: fSize]; }; ImagerScanConverter.ConvertToRuns[ devicePath: devicePath, runProc: RunFromScanConverter, clipBox: BoxFromMask[displayContext.deviceClipper] ]; }; devicePath: ImagerScanConverter.DevicePath _ ImagerScanConverter.CreatePath[ pathProc: GenPath, clipBox: BoxFromMask[displayContext.deviceClipper] ]; RETURN[ ImagerMasks.Create[Runs] ]; }; MaskFromRectangle: PROC [displayContext: DisplayContext, area: Rectangle] RETURNS [Mask] = { GenPath: PROC [move: PROC[s, f: REAL], line: PROC[s, f: REAL], curve: PROC[s1, f1, s2, f2, s3, f3: REAL]] = { m: ImagerBasic.Transformation _ displayContext.transform; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ <> m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; p: Pair _ Xform[[area.x, area.y]]; move[p.y, p.x]; p _ Xform[[area.x + area.w, area.y]]; line[p.y, p.x]; p _ Xform[[area.x + area.w, area.y + area.h]]; line[p.y, p.x]; p _ Xform[[area.x, area.y + area.h]]; line[p.y, p.x]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { RunFromScanConverter: PROC [sMin, fMin: INTEGER, fSize: NAT] = { run[s: sMin, fMin: fMin, fSize: fSize]; }; ImagerScanConverter.ConvertToRuns[ devicePath: devicePath, runProc: RunFromScanConverter, clipBox: BoxFromMask[displayContext.deviceClipper] ]; }; devicePath: ImagerScanConverter.DevicePath; IF displayContext.transform.type # hard THEN { p1: Pair _ ImagerTransform.Transform[[area.x, area.y], displayContext.transform]; p2: Pair _ ImagerTransform.Transform[[area.x+area.w, area.y+area.h], displayContext.transform]; sMin: INTEGER _ Real.RoundLI[MAX[MIN[p1.x, p2.x], -LAST[INTEGER]/2]]; sMax: INTEGER _ Real.RoundLI[MIN[MAX[p1.x, p2.x], LAST[INTEGER]/2]]; fMin: INTEGER _ Real.RoundLI[MAX[MIN[p1.y, p2.y], -LAST[INTEGER]/2]]; fMax: INTEGER _ Real.RoundLI[MIN[MAX[p1.y, p2.y], LAST[INTEGER]/2]]; RETURN [ImagerMasks.InlineBox[sMin: sMin, fMin: fMin, sSize: sMax-sMin, fSize: fMax-fMin]]; }; devicePath _ ImagerScanConverter.CreatePath[ pathProc: GenPath, clipBox: BoxFromMask[displayContext.deviceClipper] ]; RETURN[ ImagerMasks.Create[Runs] ]; }; MaskFromIntRectangle: PROC [displayContext: DisplayContext, area: IntRectangle] RETURNS [Mask] ~ { RETURN[ MaskFromRectangle[ displayContext, FloatRect[area] ] ]; }; ValidateCompositeClipper: PROC [displayContext: DisplayContext] ~ { <> OPEN displayContext; clipper _ ClipMask[ ClipMask[clientClipper, viewerClipper, FALSE], deviceClipper, FALSE ]; }; END.