ImagerDisplayClipperImpl.mesa
Created April 28, 1983
Last Edited by: Crow, August 31, 1983 6:14 pm
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;
Exported Definitions
NonRectangularClipper: PUBLIC SIGNAL ~ CODE;
NotImplementedYet: PUBLIC SIGNAL ~ CODE;
Client-called Procedures (exported to Imager)
Client clipping areas are first transformed by the client transform to fix them on the viewer
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: BOOLEANFALSE] ~ {
Modifies old clipping region by supplied outline. Exclude sets clipping outside path rather than inside.
displayContext: DisplayContext = NARROW[context.data];
displayContext.clientClipper ← ClipMask[
displayContext.clientClipper,
MaskFromPath[displayContext, outline],
exclude ];
ValidateCompositeClipper[displayContext];
};
ClipRectangle: PUBLIC PROC [context: Context, outline: Rectangle, exclude: BOOLEANFALSE] ~ {
Modifies old clipping region by supplied outline. Exclude sets clipping outside path rather than inside.
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 ];
};
};
Fast Track procedures
ClipIntRectangle: PUBLIC PROC [context: Context, outline: IntRectangle, exclude: BOOLEANFALSE] ~ {
Modifies old clipping region by supplied outline. Exclude sets clipping outside path rather than inside.
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]] ~ {
If area is all visible then execute callBack without clipping otherwise clip
displayContext: DisplayContext = NARROW[context.data];
IF TestIntRectangle[context, bounds] = visible THEN displayContext.noClip ← TRUE;
callBack[ context ];
displayContext.noClip ← FALSE;
};
Window Package Procedures (exported to Imager)
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 ]
]
];
};
};
Display Procedures (exported to ImagerDisplay)
SetViewerClipper: PUBLIC PROC [context: Context, outline: IntRectangle] ~ {
displayContext: DisplayContext = NARROW[context.data];
easy case assumed, set outline rectangle (in device space) to clip rectangle
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];
easy case assumed, clip outline rectangle (in device space) to clip rectangle
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];
};
Internal Procedures
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[[
Transforms (x, y) to (s. f)
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[[
Transforms (x, y) to (s. f)
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] ~ {
Assemble composite clipper, leave result in device space
OPEN displayContext;
clipper ←
ClipMask[
ClipMask[clientClipper, viewerClipper, FALSE],
deviceClipper,
FALSE
];
};
END.