ImagerDisplayImpl.mesa
Michael Plass, March 19, 1984 5:16:59 pm PST
DIRECTORY
Atom,
Basics,
Font,
Imager,
ImagerBasic,
ImagerBrick,
ImagerConic,
ImagerDefault,
ImagerFontCache,
ImagerDisplay,
ImagerManhattan,
ImagerPixelMaps,
ImagerPrivate,
ImagerScanConverter,
ImagerStroke,
ImagerTransform,
ImagerMasks,
Real,
RefText,
Scaled;
ImagerDisplayImpl: CEDAR MONITOR
IMPORTS Atom, Imager, ImagerConic, ImagerDefault, ImagerFontCache, ImagerManhattan, ImagerPixelMaps, ImagerScanConverter, ImagerStroke, ImagerTransform, ImagerMasks, Real, RefText, Scaled
EXPORTS ImagerDisplay
~ BEGIN OPEN ImagerDisplay;
StatsRecord: TYPE ~ RECORD [
loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT ← 0];
stats: StatsRecord ← [];
Stats: PROC RETURNS [StatsRecord] ~ { x: StatsRecord ~ stats; stats ← []; RETURN[x] };
FONT: TYPE ~ Font.FONT;
PixelMap: TYPE ~ ImagerPixelMaps.PixelMap;
Name: TYPE ~ ImagerPrivate.Name;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
TransformationRec: TYPE ~ ImagerTransform.TransformationRec;
DevicePath: TYPE ~ ImagerScanConverter.DevicePath;
ClientClipperItem: TYPE ~ ImagerBasic.ClientClipperItem;
IntPair: TYPE ~ ImagerBasic.IntPair;
IntRectangle: TYPE ~ ImagerBasic.IntRectangle;
Pair: TYPE ~ ImagerBasic.Pair;
PathMapType: TYPE ~ ImagerBasic.PathMapType;
PixelArray: TYPE ~ ImagerBasic.PixelArray;
StrokeEnd: TYPE ~ ImagerBasic.StrokeEnd;
Transformation: TYPE ~ ImagerBasic.Transformation;
Visibility: TYPE ~ ImagerBasic.Visibility;
Context: TYPE ~ Imager.Context;
State: TYPE ~ ImagerDefault.State;
Data: TYPE ~ ImagerDisplay.DisplayData;
CompositeT: PROC [data: Data, state: State] RETURNS [TransformationRec] ~ {
concatenates T with the current view-to-device transformation
t: TransformationRec ~ state.T.Contents;
IF data.rotate THEN RETURN [[
a: -t.d, d: t.a,
b: -t.e, e: t.b,
c: -t.f+data.surfaceHeight-data.viewOrigin.y,
f: t.c+data.viewOrigin.x
]]
ELSE RETURN [[
a: t.a, d: t.d,
b: t.b, e: t.e,
c: t.c-data.viewOrigin.x,
f: t.f+data.viewOrigin.y
]]
};
ViewToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ {
IF data.rotate THEN RETURN [[
a: 0, d: 1,
b: -1, e: 0,
c: data.surfaceHeight-data.viewOrigin.y,
f: data.viewOrigin.x
]]
ELSE RETURN [[
a: 1, d: 0,
b: 0, e: 1,
c: data.viewOrigin.x,
f: data.viewOrigin.y
]]
};
SurfaceToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ {
IF data.rotate THEN RETURN [[
a: 0, d: 1,
b: -1, e: 0,
c: data.surfaceHeight, f: 0
]]
ELSE RETURN [[
a: 1, d: 0,
b: 0, e: 1,
c: 0, f: 0
]]
};
DevicePair: TYPE ~ RECORD [s, f: Scaled.Value];
GetDeviceCP: PROC [data: Data, state: State] RETURNS [DevicePair] ~ {
IF data.rotate THEN RETURN [[
s: ScaledFromReal[-state.cpy].PLUS[Scaled.FromInt[data.surfaceHeight-data.viewOrigin.y]],
f: ScaledFromReal[state.cpx].PLUS[Scaled.FromInt[data.viewOrigin.x]]
]]
ELSE RETURN [[
s: ScaledFromReal[state.cpx].PLUS[Scaled.FromInt[data.viewOrigin.x]],
f: ScaledFromReal[state.cpy].PLUS[Scaled.FromInt[data.viewOrigin.y]]
]]
};
SetDeviceCP: PROC [data: Data, state: State, cp: DevicePair] ~ {
IF data.rotate THEN {
state.cpy ← -Scaled.Float[cp.s.MINUS[Scaled.FromInt[data.surfaceHeight-data.viewOrigin.y]]];
state.cpx ← Scaled.Float[cp.f.MINUS[Scaled.FromInt[data.viewOrigin.x]]];
}
ELSE {
state.cpx ← Scaled.Float[cp.s.MINUS[Scaled.FromInt[data.viewOrigin.x]]];
state.cpy ← Scaled.Float[cp.f.MINUS[Scaled.FromInt[data.viewOrigin.y]]];
}
};
displayRegistrationKey: ATOM ~ $ImagerDisplayClass;
CreateImagerClass: PUBLIC PROC [displayClass: DisplayClass] RETURNS [class: ImagerPrivate.Class] ~ {
Atom.PutProp[displayClass.displayType, displayRegistrationKey, displayClass];
class ← NEW[ImagerPrivate.ClassRep ← imagerClassTemplate^];
class.deviceType ← displayClass.displayType;
};
Init: PROC [context: Context, info: REF] ~ {
class: ImagerPrivate.Class ~ NARROW[context.class];
displayClass: DisplayClass ← NARROW[Atom.GetProp[class.deviceType, displayRegistrationKey]];
data: Data ~ displayClass.Create[displayClass, info];
context.data ← data;
ImagerDefault.InitState[context];
SetViewOrigin[context, [0, 0]];
SetViewBox[context, GetSurfaceBounds[context]];
Reset[context];
};
pixelsPerInch: REAL ~ 72.0;
mmPerInch: REAL ~ 25.4;
inchesPerMeter: REAL ~ 1000.0/mmPerInch;
pixelsPerMeter: REAL ← pixelsPerInch*inchesPerMeter;
Reset: PROC [context: Context] ~ {
data: Data ~ NARROW[context.data];
state: State ~ context.state;
ImagerDefault.Reset[context];
ImagerDefault.Scale2T[context, data.xRes * inchesPerMeter, data.yRes * inchesPerMeter];
state.fieldXMax ← state.mediumXSize ← data.surfaceWidth/(data.xRes * inchesPerMeter);
state.fieldYMax ← state.mediumYSize ← data.surfaceHeight/(data.yRes * inchesPerMeter);
data.compositeClipper ← NIL;
data.cachedColor ← NIL;
};
DevicePathFromPathMap: PROC [pathMap: PathMapType, pathData: REF, m: TransformationRec, clipBox: DeviceRectangle] RETURNS [DevicePath] ~ {
GenPath: ImagerScanConverter.PathProc
move: PROC [s, f: REAL],
line: PROC [s, f: REAL],
curve: PROC [s1, f1, s2, f2, s3, f3: REAL]
~ {
Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[
Transforms (x, y) to (s, f)
m.a * p.x + m.b * p.y + m.c,
m.d * p.x + m.e * p.y + m.f
]]};
Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp ← q };
Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp ← q };
Xcurve: PROC [p1, p2, p3: Pair] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3];
curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y];
lp ← q3
};
Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]};
Xconic: PROC [p1, p2: Pair, r: REAL] ~ {
q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2];
ImagerConic.ToCurves[lp, q1, q2, r, Curve];
lp ← q2;
};
lp: Pair;
pathMap[pathData, Xmove, Xline, Xcurve, Xconic];
};
RETURN [ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: clipBox]];
};
DevicePathFromStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL, clipBox: DeviceRectangle] RETURNS [devicePath: DevicePath] ~ {
data: Data ~ NARROW[context.data];
state: State ~ context.state;
devicePath ← ImagerStroke.DevicePathFromStroke[
pathMap: pathMap,
pathData: pathData,
clientToDevice: CompositeT[data, state].FromRec,
width: IF LOOPHOLE[strokeWidth, LONG CARDINAL] # LOOPHOLE[Imager.defaultStrokeWidth, LONG CARDINAL] THEN strokeWidth ELSE state.strokeWidth,
strokeEnd: IF strokeEnd # nil THEN strokeEnd ELSE SELECT state.strokeEnd FROM
0 => square,
1 => butt,
2 => round,
ENDCASE => nil,
closed: closed,
clipBox: ImagerManhattan.BoundingBox[data.viewClipper]
];
};
Floor: PROC [real: REAL] RETURNS [int: INT] ~ {
int ← Real.RoundLI[real];
WHILE int < real DO int ← int + 1 ENDLOOP;
WHILE int > real DO int ← int - 1 ENDLOOP;
};
Ceiling: PROC [real: REAL] RETURNS [int: INT] ~ {
int ← Real.RoundLI[real];
WHILE int > real DO int ← int - 1 ENDLOOP;
WHILE int < real DO int ← int + 1 ENDLOOP;
};
ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED {
Because of an apparent bug in Real.FScale.
i: INT ← Real.RoundLI[real * 65536.0];
RETURN[LOOPHOLE[i]]
};
FormCompositeClipper: PROC [context: Context] ~ {
data: Data ~ NARROW[context.data];
state: State ~ context.state;
mp: ManhattanPolygon ← data.compositeClipper;
IF mp = NIL OR data.clientClipper # state.clipper THEN {
clipBox: DeviceRectangle ← ImagerManhattan.BoundingBox[data.viewClipper];
ConcatClippers: PROC [l: LIST OF ClientClipperItem] ~ {
IF l#NIL THEN {
t1, t2: ManhattanPolygon;
ConcatClippers[l.rest];
t1 ← ImagerScanConverter.ConvertToManhattanPolygon[
devicePath: DevicePathFromPathMap[
pathMap: l.first.pathMap,
pathData: l.first.pathData,
m: ViewToDevice[data],
clipBox: clipBox
],
clipBox: clipBox
];
t2 ← mp;
mp ← IF l.first.exclude THEN mp.Difference[t1] ELSE mp.Intersection[t1];
ImagerManhattan.Destroy[t1];
ImagerManhattan.Destroy[t2];
};
};
ImagerManhattan.Destroy[data.compositeClipper];
data.clientClipper ← state.clipper;
mp ← ImagerManhattan.Copy[data.viewClipper];
ConcatClippers[data.clientClipper];
data.compositeClipper ← mp;
};
};
ValidateClipper: PROC [context: Context, data: Data] ~ INLINE {
IF data.compositeClipper = NIL OR data.clientClipper # context.state.clipper THEN FormCompositeClipper[context];
};
MaskStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL, strokeEnd: StrokeEnd] ~ {
data: Data ~ NARROW[context.data];
devicePath: DevicePath ← DevicePathFromStroke[context, pathMap, pathData, strokeWidth, strokeEnd, FALSE, ImagerManhattan.BoundingBox[data.viewClipper]];
ValidateClipper[context, data];
data.displayClass.ApplyMask[data, context.state.color, devicePath, 0, 0];
};
MaskStrokeClosed: PROC [context: Context, pathMap: PathMapType, pathData: REF,
strokeWidth: REAL] ~ {
data: Data ~ NARROW[context.data];
devicePath: DevicePath ← DevicePathFromStroke[context, pathMap, pathData, strokeWidth, nil, TRUE, ImagerManhattan.BoundingBox[data.viewClipper]];
ValidateClipper[context, data];
data.displayClass.ApplyMask[data, context.state.color, devicePath, 0, 0];
};
MaskFill: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ {
data: Data ~ NARROW[context.data];
devicePath: DevicePath ← DevicePathFromPathMap[pathMap, pathData, CompositeT[data, context.state], ImagerManhattan.BoundingBox[data.viewClipper]];
ValidateClipper[context, data];
data.displayClass.ApplyMask[data, context.state.color, devicePath, 0, 0];
};
specialCaseRectangles: BOOLEANTRUE;
Round: PROC [r: REAL] RETURNS [INTEGER] ~ {
IF r > LAST[INTEGER]/2 THEN RETURN [LAST[INTEGER]/2];
IF r < FIRST[INTEGER]/2 THEN RETURN [FIRST[INTEGER]/2];
RETURN [Real.RoundI[r]];
};
MaskRectangle: PROC [context: Context, x, y, w, h: REAL] ~ {
data: Data ~ NARROW[context.data];
trans: TransformationRec ~ CompositeT[data, context.state];
IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN {
s0: INTEGER ~ Round[trans.b * y + trans.c];
s1: INTEGER ~ Round[trans.b * (y+h) + trans.c];
f0: INTEGER ~ Round[trans.d * x + trans.f];
f1: INTEGER ~ Round[trans.d * (x+w) + trans.f];
sMin: INTEGER ~ MIN[s0, s1];
sMax: INTEGER ~ MAX[s0, s1];
fMin: INTEGER ~ MIN[f0, f1];
fMax: INTEGER ~ MAX[f0, f1];
t1: ManhattanPolygon;
IF sMax<=sMin OR fMax<=fMin THEN RETURN;
t1 ← ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]];
ValidateClipper[context, data];
data.displayClass.ApplyMask[data, context.state.color, t1, 0, 0];
ImagerManhattan.Destroy[t1];
}
ELSE {
PathMap: PathMapType ~ {
move[[x, y]];
line[[x+w, y]];
line[[x+w, y+h]];
line[[x, y+h]];
};
MaskFill[context, PathMap, NIL];
};
};
IntegerMaskRectangle: PROC [context: Context, x, y, w, h: INTEGER] ~ {
MaskRectangle[context, x, y, w, h];
};
MaskPixel: PROC [context: Context, pa: PixelArray] ~ {
data: Data ~ NARROW[context.data];
trans: Transformation ~ CompositeT[data, context.state].FromRec;
mask: REF ← ImagerMasks.FromPixelArray[pa, ImagerTransform.Concat[pa.m, trans]];
ValidateClipper[context, data];
data.displayClass.ApplyMask[data, context.state.color, mask, 0, 0];
mask ← NIL;
};
rasterToRunGroupStorageRatio: INT ← 1;
CharLoadInfo: TYPE ~ RECORD[sWidth, fWidth: Scaled.Value, mask: REF];
GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR]
RETURNS [ImagerManhattan.Polygon] ~ {
Runs: PROC [
run: PROC [sMin, fMin: INTEGER, fSize: NAT],
repeat: PROC [timesToRepeatScanline: NAT]] ~ {
Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
run[sMin: sMin, fMin: fMin, fSize: fSize];
};
font.fontGraphicsClass.maskProc[font, transformation, char, Run];
};
RETURN [ImagerManhattan.CreateFromRuns[Runs]]
};
FontCompositeTransform: PROC [context: Context, font: FONT] RETURNS [t: TransformationRec] ~ {
data: Data ~ NARROW[context.data];
state: State ~ context.state;
r: TransformationRec ← CompositeT[data, state];
r.c ← r.f ← 0;
t ← ImagerTransform.Concat[font.actualTransformation, r.FromRec].Contents;
};
fontCache: ImagerFontCache.FontCache ← ImagerFontCache.Create[];
LoadCharData: PROC [self: ImagerFontCache.FontObject, charCode: CARDINAL]
RETURNS [charData: REF, memoryCost: INT] ~ {
font: FONT ~ NARROW[self.fontAnyData];
char: CHAR ~ 0C+charCode; -- does this bounds check properly?
transform: Transformation ~ ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device
clientTransform: Transformation ~ font.actualTransformation.Invert.Concat[transform];
loadInfo: REF CharLoadInfo ← NEW[CharLoadInfo];
mask: ImagerManhattan.Polygon ← GetCharMask[font, transform, char];
nBoxes: INT ~ ImagerManhattan.CountBoxes[mask];
bb: DeviceRectangle ~ ImagerManhattan.BoundingBox[mask];
boxesSize: INT ← 7 * nBoxes;
rasterSize: INT ← bb.sSize * INT[(bb.fSize+15)/16] + 2;
width: Pair ← ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char] , clientTransform];
IF bb.fSize > 32*Basics.bitsPerWord OR boxesSize*rasterToRunGroupStorageRatio < rasterSize THEN loadInfo.mask ← mask
ELSE {
pixelMap: PixelMap ← ImagerPixelMaps.Create[0, bb];
ImagerPixelMaps.Clear[pixelMap];
FOR l: LIST OF DeviceRectangle ← mask, l.rest UNTIL l = NIL DO
ImagerPixelMaps.Fill[pixelMap, l.first, 1];
ENDLOOP;
loadInfo.mask ← ImagerMasks.FromBitmap[pixelMap];
ImagerManhattan.Destroy[mask]; mask ← NIL;
};
loadInfo.sWidth ← Scaled.FromReal[width.x];
loadInfo.fWidth ← Scaled.FromReal[width.y];
RETURN[loadInfo, 0];
};
ShowChar: PROC [context: Context, char: CHAR, font: FONT] ~ {
text: REF TEXT ← RefText.ObtainScratch[1];
text[0] ← char;
text.length ← 1;
ShowCharacters[context, text, font, 0, 1];
RefText.ReleaseScratch[text];
};
ShowCharacters: PROC [
context: Context,
characters: REF, -- may be a ROPE or a REF TEXT
font: FONT,
start: INT ← 0,
length: INTLAST[INT]
] ~ {
data: Data ~ NARROW[context.data];
TransformDeviceToViewVec: PROC [d: DevicePair] RETURNS [v: Pair] ~ {
v ← IF data.rotate THEN [x: Scaled.Float[d.f], y: -Scaled.Float[d.s]]
ELSE [x: Scaled.Float[d.s], y: Scaled.Float[d.f]]
};
state: State ~ context.state;
showVec: FONTIF font = NIL THEN NARROW[state.showVec] ELSE font;
transform: TransformationRec ← FontCompositeTransform[context, showVec];
fontCode: ImagerFontCache.FontCode ← ImagerFontCache.GetFontCode[[
CharDataProc: LoadCharData,
r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f,
fontScalarData: 0, -- unused
fontAnyData: showVec]];
noImage: BOOLEAN ~ state.noImage # 0;
ValidateClipper[context, data];
IF state.correctPass = 0 THEN {
DoChar: PROC [charCode: CARDINAL, charData: REF] ~ {
loadInfo: REF CharLoadInfo ~ NARROW[charData];
sDelta: Scaled.Value ← loadInfo.sWidth;
fDelta: Scaled.Value ← loadInfo.fWidth;
IF NOT noImage THEN
data.displayClass.ApplyMask[data, state.color, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]];
IF charCode = ORD[' ] AND state.amplifySpace # 1.0 THEN {
sDelta ← Scaled.FromReal[state.amplifySpace*Scaled.Float[sDelta]];
fDelta ← Scaled.FromReal[state.amplifySpace*Scaled.Float[fDelta]];
};
cp.s ← cp.s.PLUS[sDelta];
cp.f ← cp.f.PLUS[fDelta];
};
cp: DevicePair ← GetDeviceCP[data, state];
ImagerFontCache.GetStringData[DoChar, fontCache, fontCode, characters, start, length];
SetDeviceCP[data, state, cp];
}
ELSE {
CorrectingDoChar: PROC [charCode: CARDINAL, charData: REF] ~ {
loadInfo: REF CharLoadInfo ~ NARROW[charData];
delta: Pair ← TransformDeviceToViewVec[[loadInfo.sWidth, loadInfo.fWidth]];
IF NOT noImage THEN {
cp: DevicePair ← GetDeviceCP[data, state];
data.displayClass.ApplyMask[data, state.color, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]];
};
IF charCode = ORD[' ] THEN {
IF state.amplifySpace # 1.0 THEN {
delta.x ← delta.x * state.amplifySpace;
delta.y ← delta.y * state.amplifySpace;
};
ImagerDefault.CorrectSpaceView[context, delta];
}
ELSE {
ImagerDefault.CorrectMask[context];
};
state.cpx ← state.cpx + delta.x;
state.cpy ← state.cpy + delta.y;
};
ImagerFontCache.GetStringData[CorrectingDoChar, fontCache, fontCode, characters, start, length];
};
};
ManhattanPolygonFromSurfaceRectangle: PROC [context: Context, box: IntRectangle] RETURNS [LIST OF DeviceRectangle] ~ {
data: Data ~ NARROW[context.data];
IF data.rotate THEN {
s0: INTEGER ← data.surfaceHeight-box.y;
s1: INTEGER ← data.surfaceHeight-(box.y+box.h);
f0: INTEGER ← box.x;
f1: INTEGER ← box.x+box.w;
RETURN [ImagerManhattan.CreateFromBox[[MIN[s0, s1], MIN[f0, f1], ABS[s1-s0], ABS[f1-f0]]]];
}
ELSE RETURN [ImagerManhattan.CreateFromBox[[MIN[box.x, box.x+box.w], MIN[box.y, box.y+box.h], ABS[box.w], ABS[box.w]]]];
};
SetViewOrigin: PROC [context: Context, viewOrigin: IntPair] ~ {
data: Data ← NARROW[context.data];
data.viewOrigin ← viewOrigin;
};
GetViewOrigin: PROC [context: Context] RETURNS [viewOrigin: IntPair] ~ {
data: Data ~ NARROW[context.data];
RETURN[data.viewOrigin];
};
SetViewBox: PROC [context: Context, viewBox: IntRectangle] ~ {
data: Data ← NARROW[context.data];
surfaceBox: IntRectangle ← [
x: viewBox.x+data.viewOrigin.x,
y: viewBox.y+data.viewOrigin.y,
w: viewBox.w,
h: viewBox.h
];
mask: ImagerManhattan.Polygon ← ManhattanPolygonFromSurfaceRectangle[context, surfaceBox];
ImagerManhattan.Destroy[data.compositeClipper];
data.compositeClipper ← NIL;
ImagerManhattan.Destroy[data.viewClipper];
data.viewClipper ← mask;
};
GetViewBox: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ~ NARROW[context.data];
deviceBox: DeviceRectangle ← ImagerManhattan.BoundingBox[data.viewClipper];
surfaceBox: IntRectangle ~
IF data.rotate THEN [
x: deviceBox.fMin,
y: data.surfaceHeight-(deviceBox.sMin+deviceBox.sSize),
w: deviceBox.fSize,
h: deviceBox.sSize
]
ELSE [
x: deviceBox.sMin,
y: deviceBox.fMin,
w: deviceBox.sSize,
h: deviceBox.fSize
];
RETURN[[
x: surfaceBox.x-data.viewOrigin.x,
y: surfaceBox.y-data.viewOrigin.y,
w: surfaceBox.w,
h: surfaceBox.h
]];
};
ClipView: PROC [context: Context, clipBox: IntRectangle, exclude: BOOLEAN] ~ {
data: Data ← NARROW[context.data];
surfaceBox: IntRectangle ← [
x: clipBox.x+data.viewOrigin.x,
y: clipBox.y+data.viewOrigin.y,
w: clipBox.w,
h: clipBox.h
];
newBox: ImagerManhattan.Polygon ← ManhattanPolygonFromSurfaceRectangle[context, surfaceBox];
old: ImagerManhattan.Polygon ← data.viewClipper;
ImagerManhattan.Destroy[data.compositeClipper];
data.compositeClipper ← NIL;
data.viewClipper ←
IF exclude THEN ImagerManhattan.Difference[old, newBox]
ELSE ImagerManhattan.Intersection[old, newBox];
newBox.rest ← old;
ImagerManhattan.Destroy[newBox];
};
MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] ~ {
data: Data ~ NARROW[context.data];
m: Transformation ~ SurfaceToDevice[data].FromRec;
sMinDest, fMinDest, sMinSource, fMinSource, sSize, fSize: INTEGER;
DoMove: PROC ~ {
IF data.displayClass.viewUnitsPerPixel # 1 THEN {
sMinDest ← sMinDest / data.displayClass.viewUnitsPerPixel;
fMinDest ← fMinDest / data.displayClass.viewUnitsPerPixel;
sMinSource ← sMinSource / data.displayClass.viewUnitsPerPixel;
fMinSource ← fMinSource / data.displayClass.viewUnitsPerPixel;
sSize ← sSize / data.displayClass.viewUnitsPerPixel;
fSize ← fSize / data.displayClass.viewUnitsPerPixel;
};
FOR i: NAT IN [0..data.numberOfSeparations) DO
data[i].Clip[[sMinDest, fMinDest, sSize, fSize]].Transfer[data[i].ShiftMap[sMinSource-sMinDest, fMinSource-fMinDest]];
ENDLOOP;
};
[[sMinDest, fMinDest, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[[dest.x, dest.y, source.w, source.h], m];
[[sMinSource, fMinSource, sSize, fSize]] ← ImagerTransform.TransformIntRectangle[[source.x, source.y, source.w, source.h], m];
data.displayClass.DoUnderLock[data, DoMove, [MIN[sMinDest, sMinSource], MIN[fMinDest, fMinSource], sSize+ABS[sMinSource-sMinDest], fSize+ABS[fMinSource-fMinDest]]];
};
TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ {
data: Data ~ NARROW[context.data];
state: State ~ context.state;
trans: TransformationRec ~ CompositeT[data, state];
manhattanPolygon: ImagerManhattan.Polygon ← NIL;
IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN {
s0: INTEGER ~ Round[trans.b * y + trans.c];
s1: INTEGER ~ Round[trans.b * (y+h) + trans.c];
f0: INTEGER ~ Round[trans.d * x + trans.f];
f1: INTEGER ~ Round[trans.d * (x+w) + trans.f];
sMin: INTEGER ~ MIN[s0, s1];
sMax: INTEGER ~ MAX[s0, s1];
fMin: INTEGER ~ MIN[f0, f1];
fMax: INTEGER ~ MAX[f0, f1];
IF sMax<=sMin OR fMax<=fMin THEN RETURN [invisible];
manhattanPolygon ← ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]];
}
ELSE {
PathMap: PathMapType ~ {
move[[x, y]];
line[[x+w, y]];
line[[x+w, y+h]];
line[[x, y+h]];
};
bb: DeviceRectangle ← ImagerManhattan.BoundingBox[data.viewClipper];
devicePath: DevicePath ← DevicePathFromPathMap[PathMap, NIL, CompositeT[data, context.state], bb];
manhattanPolygon ← ImagerScanConverter.ConvertToManhattanPolygon[devicePath, bb];
};
ValidateClipper[context, data];
visibility ← ImagerManhattan.IsVisible[manhattanPolygon, data.compositeClipper];
ImagerManhattan.Destroy[manhattanPolygon];
};
GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ {
data: Data ~ NARROW[context.data];
RETURN [[0, 0, data.surfaceWidth, data.surfaceHeight]];
};
SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [REF] ~ {
SELECT op FROM
ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp];
};
imagerClassTemplate: ImagerPrivate.Class ← NEW [ImagerPrivate.ClassRep ← [
deviceType: NIL,
Init: Init,
ISet: ImagerDefault.ISet,
ISetReal: ImagerDefault.ISetReal,
ISetInt: ImagerDefault.ISetInt,
SetSampledColor: ImagerDefault.SetSampledColor,
SetSampledBlack: ImagerDefault.SetSampledBlack,
DoSave: ImagerDefault.DoSave,
DoSaveAll: ImagerDefault.DoSaveAll,
ConcatT: ImagerDefault.ConcatT,
TranslateT: ImagerDefault.TranslateT,
RotateT: ImagerDefault.RotateT,
ScaleT: ImagerDefault.ScaleT,
Scale2T: ImagerDefault.Scale2T,
Move: ImagerDefault.Move,
Trans: ImagerDefault.Trans,
SetXY: ImagerDefault.SetXY,
IntegerSetXY: ImagerDefault.IntegerSetXY,
SetXYRel: ImagerDefault.SetXYRel,
IntegerSetXYRel: ImagerDefault.IntegerSetXYRel,
GetCP: ImagerDefault.GetCP,
GetCPRounded: ImagerDefault.GetCPRounded,
MaskFill: MaskFill,
MaskStroke: MaskStroke,
MaskStrokeClosed: MaskStrokeClosed,
MaskVector: ImagerDefault.MaskVector,
IntegerMaskVector: ImagerDefault.IntegerMaskVector,
MaskRectangle: MaskRectangle,
IntegerMaskRectangle: IntegerMaskRectangle,
StartUnderline: ImagerDefault.StartUnderline,
MaskUnderline: ImagerDefault.MaskUnderline,
IntegerMaskUnderline: ImagerDefault.IntegerMaskUnderline,
MaskPixel: MaskPixel,
ClipOutline: ImagerDefault.ClipOutline,
ExcludeOutline: ImagerDefault.ExcludeOutline,
ClipRectangle: ImagerDefault.ClipRectangle,
ExcludeRectangle: ImagerDefault.ExcludeRectangle,
IntegerClipRectangle: ImagerDefault.IntegerClipRectangle,
IntegerExcludeRectangle: ImagerDefault.IntegerExcludeRectangle,
ShowChar: ShowChar,
ShowCharacters: ShowCharacters,
CorrectMask: ImagerDefault.CorrectMask,
CorrectSpace: ImagerDefault.CorrectSpace,
SetCorrectMeasure: ImagerDefault.SetCorrectMeasure,
SetCorrectTolerance: ImagerDefault.SetCorrectTolerance,
Space: ImagerDefault.Space,
IntegerSpace: ImagerDefault.IntegerSpace,
Correct: ImagerDefault.Correct,
Reset: Reset,
SetViewOrigin: SetViewOrigin,
GetViewOrigin: GetViewOrigin,
SetViewBox: SetViewBox,
GetViewBox: GetViewBox,
ClipView: ClipView,
MoveSurfaceRectangle: MoveSurfaceRectangle,
TestRectangle: TestRectangle,
GetSurfaceBounds: GetSurfaceBounds,
SpecialOp: SpecialOp
]];
END.