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;
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:
BOOLEAN ←
FALSE] ~ {
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:
BOOLEAN ←
FALSE] ~ {
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:
BOOLEAN ←
FALSE] ~ {
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.