ImagerRasterImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, November 1, 1985 9:31:41 am PST
Doug Wyatt, September 29, 1986 11:57:15 am PDT
DIRECTORY
Atom USING [GetPropFromList],
Basics USING [bitsPerWord, LongMult],
Imager USING [black, Box, ClassRep, Color, ColorOperator, Context, ContextRep, DoSave, DoSaveAll, Error, MaskBits, MaskRectangle, MaskRectangleI, metersPerInch, PixelArray, SetColor, SetXY, StateRep, Trans, white],
ImagerBackdoor USING [Clipper, Visibility],
ImagerBackdoorPrivate USING [Class],
ImagerBox USING [BoxFromRect],
ImagerCache USING [Ref],
ImagerDevice USING [CharMask, Class, Device, DeviceBox, RunProc],
ImagerFont USING [Font, FontImplRep, XChar, XStringProc],
ImagerFontPrivate USING [FontImpl, FontImplRep, MakeFontAtom],
ImagerManhattan USING [BoundingBox, Copy, CreateFromBox, Destroy, Polygon],
ImagerManhattanExtras USING [DestructiveClip, DestructiveDifference, DestructiveIntersection],
ImagerMask USING [RunsFromBits],
ImagerPath USING [ArcToConics, ConicToCurves, MapOutline, PathProc],
ImagerPixelArray USING [MaxSampleValue, PixelArray, UnsafeGetBits],
ImagerPixelMap USING [DeviceRectangle, Intersect],
ImagerPrivate USING [Class, ClassRep],
ImagerRaster USING [FontTuner],
ImagerRasterPrivate USING [AndFlags, CharArrayRep, Data, DataRep, Flags, NotFlags, OrFlags, RasterShow, RasterShowText],
ImagerSample USING [GetPointSamples, ObtainScratchBuffer, ReleaseScratchBuffer, Sample, SampleBuffer, Sampler, SamplerRep, SetSamplerIncrements, SetSamplerPosition],
ImagerScanConverter USING [BoundingBox, ConvertToManhattanPolygon, ConvertToRuns, CreatePath, DevicePath],
ImagerState USING [ChangeFlags, notChanged, State, StateClip, StateClipRectangle, StateClipRectangleI, StateConcatT, StateCorrect, StateCorrectMask, StateCorrectSpace, StateDontCorrect, StateDoSave, StateGetClipper, StateGetColor, StateGetCP, StateGetFont, StateGetInt, StateGetReal, StateGetT, StateMaskUnderline, StateMove, StateRep, StateRotateT, StateScale2T, StateSetClipper, StateSetColor, StateSetCorrectMeasure, StateSetCorrectTolerance, StateSetFont, StateSetGray, StateSetInt, StateSetReal, StateSetSampledBlack, StateSetSampledColor, StateSetT, StateSetXY, StateSetXYRel, StateSpace, StateStartUnderline, StateTranslateT],
ImagerStroke USING [PathFromStroke],
ImagerTransformation USING [ApplyCat, ApplyPostConcat, ApplyPostTranslate, ApplyPreConcat, ApplyPreTranslate, ApplyTranslateTo, InverseTransform, InverseTransformRectangle, Rectangle, Rotate, Scale, Scale2, Transform, Transformation, TransformationRep],
Process USING [CheckForAbort],
Real USING [Fix, FScale, LargestNumber, Round],
RealFns USING [AlmostEqual, SqRt],
Rope USING [ROPE],
Vector2 USING [Add, Div, Length, Mul, Square, Sub, VEC],
VM USING [AddressForPageNumber, Free, Interval, PagesForWords, SimpleAllocate];
ImagerRasterImpl: CEDAR PROGRAM
IMPORTS Atom, Basics, Imager, ImagerBox, ImagerFontPrivate, ImagerManhattan, ImagerManhattanExtras, ImagerMask, ImagerPath, ImagerPixelArray, ImagerPixelMap, ImagerRasterPrivate, ImagerSample, ImagerScanConverter, ImagerState, ImagerStroke, ImagerTransformation, Process, Real, RealFns, Vector2, VM
EXPORTS Imager, ImagerBackdoor, ImagerFont, ImagerRaster, ImagerRasterPrivate
~ BEGIN OPEN ImagerState, ImagerRasterPrivate;
Class: TYPE ~ ImagerPrivate.Class;
ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep
State: TYPE ~ ImagerState.State;
StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep
ROPE: TYPE ~ Rope.ROPE;
CharMask: TYPE ~ ImagerDevice.CharMask;
Clipper: TYPE ~ ImagerBackdoor.Clipper;
Color: TYPE ~ Imager.Color;
ColorOperator: TYPE ~ Imager.ColorOperator;
Context: TYPE ~ Imager.Context;
Device: TYPE ~ ImagerDevice.Device;
DeviceBox: TYPE ~ ImagerDevice.DeviceBox;
RunProc: TYPE ~ ImagerDevice.RunProc;
DevicePath: TYPE ~ ImagerScanConverter.DevicePath;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
Font: TYPE ~ ImagerFont.Font;
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
PathProc: TYPE ~ ImagerPath.PathProc;
PixelArray: TYPE ~ Imager.PixelArray;
Rectangle: TYPE ~ ImagerTransformation.Rectangle;
Transformation: TYPE ~ ImagerTransformation.Transformation;
TransformationRep: TYPE ~ ImagerTransformation.TransformationRep;
VEC: TYPE ~ Vector2.VEC;
XChar: TYPE ~ ImagerFont.XChar;
XStringProc: TYPE ~ ImagerFont.XStringProc;
FontImpl: TYPE ~ ImagerFontPrivate.FontImpl;
FontImplRep: PUBLIC TYPE ~ ImagerFontPrivate.FontImplRep; -- export to to ImagerFont
DeviceBoxFromDeviceRectangle: PROC [r: DeviceRectangle] RETURNS [DeviceBox] ~ {
RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]];
};
DeviceRectangleFromDeviceBox: PROC [b: DeviceBox] RETURNS [DeviceRectangle] ~ {
RETURN[[sMin: b.smin, fMin: b.fmin, sSize: b.smax-b.smin, fSize: b.fmax-b.fmin]];
};
Create: PUBLIC PROC [device: Device, pixelUnits: BOOL,
fontCache: ImagerCache.Ref, rastWeight: REAL, fontTuner: ImagerRaster.FontTuner, class: Class
] RETURNS [Context] ~ {
data: Data ~ NEW[DataRep ← [device: device]];
state: State ~ NEW[StateRep ← []];
metersToSurface: VEC ~ device.surfaceUnitsPerInch.Div[Imager.metersPerInch];
data.rastWeight ← rastWeight;
data.fontTuner ← fontTuner;
data.viewToDevice ← ImagerTransformation.Scale[1];
data.clientToDevice ← ImagerTransformation.Scale[1];
data.maskToDevice ← ImagerTransformation.Scale[1];
data.charToDevice ← ImagerTransformation.Scale[1];
data.fontCache ← fontCache;
data.charArray ← NEW[CharArrayRep ← ALL[NIL]];
DViewReset[data];
IF pixelUnits THEN state.T ← ImagerTransformation.Scale[1]
ELSE state.T ← ImagerTransformation.Scale2[metersToSurface];
state.color ← Imager.black;
IF class=NIL THEN class ← rasterClass;
RETURN[NEW[Imager.ContextRep ← [class: class, state: state, data: data]]];
};
CreateClass: PUBLIC PROC [type: ATOM] RETURNS [Class] ~ {
class: Class ~ NEW[ClassRep ← rasterClass^];
class.type ← type;
RETURN[class];
};
ValidateClientToDevice: PROC [data: Data, state: State] ~ {
data.clientToDevice.ApplyCat[state.T, data.viewToDevice];
data.valid.clientToDevice ← TRUE;
};
ValidateFontInfo: PROC [data: Data, state: State] ~ {
font: Font ~ state.font;
IF font = NIL THEN ERROR Imager.Error[[code: $noFont, explanation: "Failed to set font before calling Show."]]
ELSE {
impl: FontImpl ~ font.impl;
IF NOT data.valid.clientToDevice THEN ValidateClientToDevice[data, state];
data.charToDevice^ ← data.clientToDevice^;
data.charToDevice.ApplyTranslateTo[[0, 0]];
data.charToDevice.ApplyPreConcat[font.charToClient];
data.fontAtom ← ImagerFontPrivate.MakeFontAtom[impl.typeface, data.charToDevice];
data.valid.fontInfo ← TRUE;
};
};
ValidateClientClipper: PROC [data: Data, state: State] ~ {
cc: ImagerManhattan.Polygon ← NIL;
ImagerManhattan.Destroy[data.clientClipMask];
cc ← ImagerManhattan.Copy[data.viewClipMask];
IF data.specialClipPresent THEN {
cc ← ImagerManhattanExtras.DestructiveClip[cc, data.specialClipRect];
};
FOR each: Clipper ← state.clipper, each.rest UNTIL each=NIL DO
path: PathProc ~ { ImagerPath.MapOutline[outline: each.first.outline,
moveTo: moveTo, lineTo: lineTo, curveTo: curveTo,
conicTo: conicTo, arcTo: arcTo] };
this: ManhattanPolygon;
data.devicePath ← ImagerScanConverter.CreatePath[path: path,
transformation: data.viewToDevice, clipBox: data.viewClipBox,
scratch: data.devicePath];
this ← ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath,
clipBox: data.viewClipBox, parityFill: each.first.parity];
cc ← CombineManhattan[cc, this, each.first.exclude];
ImagerManhattan.Destroy[this];
ENDLOOP;
data.clientClipMask ← cc;
data.clientClipRect ← ImagerManhattan.BoundingBox[cc];
data.clientClipBox ← DeviceBoxFromDeviceRectangle[data.clientClipRect];
data.clientClipBoxOnly ← (cc=NIL OR cc.rest=NIL);
data.valid.clientClipper ← TRUE;
};
ValidateColor: PROC [data: Data, state: State] ~ {
device: Device ~ data.device;
device.class.SetColor[device, state.color, data.viewToDevice];
data.valid.deviceColor ← TRUE;
};
ValidatePriority: PROC [data: Data, state: State] ~ {
device: Device ~ data.device;
device.class.SetPriority[device, state.np.priorityImportant#0];
data.valid.devicePriority ← TRUE;
};
NoteStateChanges: PUBLIC PROC [data: Data, state: State] ~ {
changed: ImagerState.ChangeFlags ~ state.changed;
state.changed ← ImagerState.notChanged;
IF changed.T THEN data.valid.clientToDevice ← data.valid.fontInfo ← FALSE;
IF changed.color THEN data.valid.deviceColor ← FALSE;
IF changed.priority THEN data.valid.devicePriority ← FALSE;
IF changed.clipper THEN data.valid.clientClipper ← FALSE;
IF changed.font THEN data.valid.fontInfo ← FALSE;
};
ValidateIfNeeded: PUBLIC PROC [data: Data, state: State, needs: Flags] ~ {
fix: Flags ~ AndFlags[needs, NotFlags[data.valid]];
IF fix.clientToDevice THEN ValidateClientToDevice[data, state];
IF fix.deviceColor THEN ValidateColor[data, state];
IF fix.devicePriority THEN ValidatePriority[data, state];
IF fix.clientClipper THEN ValidateClientClipper[data, state];
IF fix.fontInfo THEN ValidateFontInfo[data, state];
IF AndFlags[data.valid, needs]#needs THEN ERROR;
};
Validate: PROC [data: Data, state: State, needs: Flags] ~ INLINE {
IF state.changed#ImagerState.notChanged THEN NoteStateChanges[data, state];
IF AndFlags[data.valid, needs]#needs THEN ValidateIfNeeded[data, state, needs];
};
Verify: PROC [data: Data, needs: Flags] ~ INLINE {
IF AndFlags[data.valid, needs]#needs THEN ERROR;
};
runsFromPathNeeds: Flags ~ [clientClipper: TRUE];
RunsFromPath: PROC [data: Data, path: PathProc,
parity: BOOLFALSE, transformation: Transformation ← NIL,
action: PROC [bounds: DeviceBox, runs: PROC[RunProc]]
] ~ {
clipBox: DeviceRectangle ~ data.clientClipRect;
clipMask: ManhattanPolygon ~ data.clientClipMask;
devicePath: DevicePath ~ ImagerScanConverter.CreatePath[path: path,
transformation: transformation, clipBox: clipBox, scratch: data.devicePath];
rect: DeviceRectangle ~ ImagerScanConverter.BoundingBox[devicePath];
bounds: DeviceBox ~ DeviceBoxFromDeviceRectangle[rect.Intersect[clipBox]];
runs: PROC [run: RunProc] ~ {
IF clipMask#NIL AND clipMask.rest=NIL THEN {
ImagerScanConverter.ConvertToRuns[devicePath: devicePath,
runProc: run, clipBox: clipMask.first, parityFill: parity];
}
ELSE {
rem: ManhattanPolygon ← clipMask;
clip: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
WHILE rem#NIL AND rem.first.sMin+rem.first.sSize<=sMin DO rem ← rem.rest ENDLOOP;
FOR l: LIST OF DeviceRectangle ← rem, l.rest UNTIL l=NIL OR l.first.sMin>sMin DO
fMinRun: INTEGER ~ MAX[l.first.fMin, fMin];
fMaxRun: INTEGER ~ MIN[l.first.fMin+l.first.fSize, fMin+fSize];
IF fMaxRun > fMinRun THEN run[sMin, fMinRun, fMaxRun-fMinRun];
ENDLOOP;
};
ImagerScanConverter.ConvertToRuns[devicePath: devicePath,
runProc: clip, clipBox: clipBox, parityFill: parity];
};
};
Verify[data, runsFromPathNeeds];
Process.CheckForAbort[];
action[bounds, runs];
data.devicePath ← devicePath;
};
maskRunsNeeds: Flags ~ OrFlags[runsFromPathNeeds, [deviceColor: TRUE, devicePriority: TRUE]];
MaskRuns: PROC [data: Data, path: PathProc,
parity: BOOLFALSE, transformation: Transformation ← NIL] ~ {
maskRunsAction: PROC [bounds: DeviceBox, runs: PROC[RunProc]] ~ {
device: Device ~ data.device;
device.class.MaskRuns[device: device, bounds: bounds, runs: runs];
};
Verify[data, maskRunsNeeds];
RunsFromPath[data: data, path: path, parity: parity,
transformation: transformation, action: maskRunsAction];
};
ClipBoxToMask: PUBLIC PROC [box: DeviceBox, mask: ManhattanPolygon, action: PROC[DeviceBox]] ~ {
FOR list: LIST OF DeviceRectangle ← mask, list.rest UNTIL list=NIL DO
c: DeviceBox ~ DeviceBoxFromDeviceRectangle[list.first];
IF box.smin<c.smax AND box.smax>c.smin AND box.fmin<c.fmax AND box.fmax>c.fmin THEN
action[[smin: MAX[box.smin, c.smin], smax: MIN[box.smax, c.smax],
fmin: MAX[box.fmin, c.fmin], fmax: MIN[box.fmax, c.fmax]]];
ENDLOOP;
};
maskNeeds: Flags ~ OrFlags[maskRunsNeeds, [clientToDevice: TRUE]];
RasterMaskFill: PROC [context: Context, path: PathProc, oddWrap: BOOL] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
data: Data ~ NARROW[context.data];
Validate[data, state, maskNeeds];
MaskRuns[data: data, path: path, parity: oddWrap, transformation: data.clientToDevice];
};
};
specialRectangles: BOOLTRUE;
RasterMaskRectangle: PROC [context: Context, r: Rectangle] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
data: Data ~ NARROW[context.data];
device: Device ~ data.device;
Validate[data, state, maskNeeds];
IF specialRectangles AND data.clientToDevice.form # 0 AND device.class.MaskBoxes#NIL THEN {
clientToDevice: Transformation ~ data.clientToDevice;
clip: DeviceBox ~ data.clientClipBox;
p0: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x, r.y]];
p1: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x+r.w, r.y+r.h]];
s0: INT ← Real.Fix[MAX[MIN[p0.x+0.5, clip.smax], clip.smin]];
s1: INT ← Real.Fix[MAX[MIN[p1.x+0.5, clip.smax], clip.smin]];
f0: INT ← Real.Fix[MAX[MIN[p0.y+0.5, clip.fmax], clip.fmin]];
f1: INT ← Real.Fix[MAX[MIN[p1.y+0.5, clip.fmax], clip.fmin]];
Process.CheckForAbort[];
IF s1<s0 THEN { temp: INT ~ s0; s0 ← s1; s1 ← temp };
IF f1<f0 THEN { temp: INT ~ f0; f0 ← f1; f1 ← temp };
IF s0<s1 AND f0<f1 THEN {
box: DeviceBox ~ [smin: s0, fmin: f0, smax: s1, fmax: f1];
boxes: PROC[action: PROC[DeviceBox]] ~ {
ClipBoxToMask[box: box, mask: data.clientClipMask, action: action];
};
device.class.MaskBoxes[device: device, bounds: box, boxes: boxes];
};
}
ELSE {
rectanglePath: PathProc ~ {
moveTo[[r.x, r.y]];
lineTo[[r.x+r.w, r.y]];
lineTo[[r.x+r.w, r.y+r.h]];
lineTo[[r.x, r.y+r.h]];
};
MaskRuns[data: data, path: rectanglePath, transformation: data.clientToDevice];
};
};
};
RasterMaskRectangleI: PROC [context: Context, x, y, w, h: INTEGER] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
data: Data ~ NARROW[context.data];
device: Device ~ data.device;
clientToDevice: Transformation;
Validate[data, state, maskNeeds];
clientToDevice ← data.clientToDevice;
IF clientToDevice.form=9 AND clientToDevice.integerTrans AND device.class.MaskBoxes#NIL THEN {
smin: INTINT[clientToDevice.tx]-y; smax: INT ← smin-h;
fmin: INTINT[clientToDevice.ty]+x; fmax: INT ← fmin+w;
box: DeviceBox ← data.clientClipBox;
boxes: PROC[action: PROC[DeviceBox]] ~ {
ClipBoxToMask[box: box, mask: data.clientClipMask, action: action];
};
Process.CheckForAbort[];
IF smax<smin THEN { temp: INT ~ smin; smin ← smax; smax ← temp };
IF fmax<fmin THEN { temp: INT ~ fmin; fmin ← fmax; fmax ← temp };
IF smin<box.smax AND smax>box.smin AND fmin<box.fmax AND fmax>box.fmin THEN {
IF box.smin<smin THEN box.smin ← smin; IF box.smax>smax THEN box.smax ← smax;
IF box.fmin<fmin THEN box.fmin ← fmin; IF box.fmax>fmax THEN box.fmax ← fmax;
device.class.MaskBoxes[device: device, bounds: box, boxes: boxes];
};
}
ELSE RasterMaskRectangle[context, [x: x, y: y, w: w, h: h]];
};
};
RasterMaskUnderline: PROC [context: Context, dy, h: REAL] ~ {
state: State ~ context.state;
USING [T, color, clipper, underlineStart, noImage, priorityImportant]
IF state.np.noImage=0 THEN {
end: VEC ~ state.T.InverseTransform[state.p.cp]; -- current position (client space)
start: VEC ~ [state.np.underlineStart, end.y-dy-h]; -- corner of underline (client space)
underline: PROC ~ {
Imager.SetXY[context, start];
Imager.Trans[context];
Imager.MaskRectangle[context, [0, 0, end.x-start.x, h]];
};
Imager.DoSaveAll[context, underline];
};
};
RasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ~ {
state: State ~ context.state;
USING [T, color, clipper, strokeWidth, strokeStyle, noImage, priorityImportant]
IF state.np.noImage=0 THEN {
data: Data ~ NARROW[context.data];
strokePath: PathProc ~ {
ImagerStroke.PathFromStroke[path: path, closed: closed, m: data.clientToDevice,
width: state.np.strokeWidth, end: state.np.strokeEnd, joint: state.np.strokeJoint,
moveTo: moveTo, lineTo: lineTo, conicTo: conicTo, curveTo: curveTo];
};
Validate[data, state, maskNeeds];
MaskRuns[data: data, path: strokePath];
};
};
RasterMaskVector: PROC [context: Context, p1, p2: VEC] ~ {
vectorPath: PathProc ~ { moveTo[p1]; lineTo[p2] };
RasterMaskStroke[context: context, path: vectorPath, closed: FALSE];
};
RasterMaskDashedStroke: PROC [context: Context, path: PathProc,
patternLen: NAT, pattern: PROC [i: NAT] RETURNS [REAL], offset, length: REAL] ~ {
Bezier: TYPE ~ ARRAY [0..4) OF VEC;
RealPlusOrMinus: TYPE ~ RECORD [value: REAL, error: REAL];
nPts: NAT ← 6;
AverageSpeed: PROC [p: Bezier] RETURNS [RealPlusOrMinus] ~ {
Computes an approximation to the average speed of the point moving along the curve as a function of its parameter, along with estimated error bounds. Since the curve is parameterized from zero to one, its average speed is numerically equal to its arc length.
nReal: REAL ~ nPts;
min: REAL ← Real.LargestNumber;
max: REAL ← 0;
delta: ARRAY [0..3) OF VEC ~ [Vector2.Sub[p[1], p[0]], Vector2.Sub[p[2], p[1]], Vector2.Sub[p[3], p[2]]];
The deltas form a Bezier description of the velocity profile. We want its average magnitude.
FOR i: NAT IN [0..nPts] DO
t: REAL ~ i/nReal;
s: REAL ~ 1.0-t;
d01: VEC ← Vector2.Add[Vector2.Mul[delta[0], t], Vector2.Mul[delta[1], s]];
d12: VEC ← Vector2.Add[Vector2.Mul[delta[1], t], Vector2.Mul[delta[2], s]];
d012: VEC ← Vector2.Add[Vector2.Mul[d01, t], Vector2.Mul[d12, s]];
sqr: REAL ~ Vector2.Square[d012];
IF sqr > max THEN max ← sqr;
IF sqr < min THEN min ← sqr;
ENDLOOP;
max ← RealFns.SqRt[max];
min ← RealFns.SqRt[min];
RETURN [[(max+min)*1.5, (max-min)*1.5]]
};
sigBits: NAT ← 5;
DivideUntilSmooth: PROC [p: Bezier, piece: PROC [p: Bezier, speed: REAL], fullScale: REAL] ~ {
a: RealPlusOrMinus ← AverageSpeed[p];
UNTIL Real.FScale[a.error, sigBits] <= fullScale DO
Mid: PROC [a,b: VEC] RETURNS [VEC] ~ INLINE {RETURN [[Real.FScale[a.x+b.x, -1], Real.FScale[a.y+b.y, -1]]]};
p01: VEC ~ Mid[p[0],p[1]];
p12: VEC ~ Mid[p[1],p[2]];
p23: VEC ~ Mid[p[2],p[3]];
p012: VEC ~ Mid[p01,p12];
p123: VEC ~ Mid[p12,p23];
p0123: VEC ~ Mid[p012,p123];
DivideUntilSmooth[[p[0], p01, p012, p0123], piece, fullScale];
p ← [p0123, p123, p23, p[3]];
a ← AverageSpeed[p];
ENDLOOP;
piece[p, a.value];
};
SubDivide: PROC [b: Bezier, t: REAL, hi: BOOL] RETURNS [Bezier] ~ {
s: REAL ~ 1-t;
Interpolate: PROC [a, b: VEC] RETURNS [VEC] ~ INLINE {
RETURN [[a.x*s+b.x*t, a.y*s+b.y*t]]
};
q1 : VEC ~ Interpolate[b[0], b[1]];
q2: VEC ~ Interpolate[b[1], b[2]];
q3: VEC ~ Interpolate[b[2], b[3]];
qp1: VEC ~ Interpolate[q1, q2];
qp2: VEC ~ Interpolate[q2, q3];
q: VEC ~ Interpolate[qp1, qp2];
RETURN [IF hi THEN [q, qp2, q3, b[3]] ELSE [b[0], q1, qp1, q]]
};
SubPiece: PROC [b: Bezier, t0, t1: REAL] RETURNS [Bezier] ~ {
IF t1 # 1.0 THEN {
b ← SubDivide[b, t1, FALSE];
t0 ← t0/t1;
t1 ← 1;
};
IF t0 # 0.0 THEN b ← SubDivide[b, t0, TRUE];
RETURN [b];
};
MeasurePath: PROC [path: PathProc] RETURNS [sum: REAL ← 0.0] ~ {
lp: VEC ← [0,0];
move: PROC [p0: VEC] ~ {
lp ← p0;
};
line: PROC [p1: VEC] ~ {
sum ← sum + Vector2.Length[Vector2.Sub[p1, lp]];
lp ← p1;
};
piece: PROC [p: Bezier, speed: REAL] ~ {
sum ← sum + speed;
};
curve: PROC [p1, p2, p3: VEC] ~ TRUSTED {
p: Bezier ~ [lp, p1, p2, p3];
fullScale: REAL ~ AverageSpeed[p].value;
IF fullScale > 0.0 THEN DivideUntilSmooth[p, piece, fullScale];
lp ← p3;
};
conic: PROC [p1, p2: VEC, r: REAL] ~ {
ImagerPath.ConicToCurves[lp, p1, p2, r, curve]
};
arc: PROC [p1, p2: VEC] ~ {ImagerPath.ArcToConics[lp, p1, p2, conic]};
path[moveTo: move, lineTo: line, curveTo: curve, conicTo: conic, arcTo: arc];
};
pathUnits: REAL ~ MeasurePath[path];
stretch: REAL ~ IF length<=0.0 OR pathUnits=0.0 THEN 1.0 ELSE pathUnits/length;
lp: VEC ← [0,0];
index: INT ← 0; -- index of currently active pattern element.
residual: REAL ← pattern[0]*stretch; -- remaining part of current pattern element, in master units.
used: REAL ← 0.0; -- amount of pattern used, in master units.
on: BOOLTRUE;
Advance: PROC [patternOffset: REAL, startAction, stopAction: PROCNIL] ~ {
UNTIL used >= patternOffset DO
IF residual > 0.0
THEN {
Still have part of the current piece to use up.
IF used+residual <= patternOffset
THEN {used ← used+residual; residual ← 0.0}
ELSE {residual ← used+residual - patternOffset; used ← patternOffset};
}
ELSE {
The current piece is all used up; go on to the next.
IF on AND stopAction#NIL THEN stopAction[];
index ← index + 1; IF index = patternLen THEN index ← 0;
residual ← pattern[index]*stretch;
on ← NOT on;
IF on AND startAction#NIL THEN startAction[];
};
ENDLOOP;
};
dashedPath: PathProc ~ {
move: PROC [p0: VEC] ~ {lp ← p0; IF on THEN moveTo[lp]};
line: PROC [p1: VEC] ~ {
delta: VEC ~ Vector2.Sub[p1, lp];
d: REAL ~ Vector2.Length[delta];
segmentStart: REAL ~ used;
segmentEnd: REAL ~ used + d;
start: PROC ~ {
s: REAL ← (used-segmentStart)/d;
moveTo[Vector2.Add[lp, Vector2.Mul[delta, s]]];
};
stop: PROC ~ {
s: REAL ← (used-segmentStart)/d;
lineTo[Vector2.Add[lp, Vector2.Mul[delta, s]]];
};
Advance[segmentEnd, start, stop];
IF on THEN lineTo[p1];
lp ← p1;
};
curve: PROC [p1, p2, p3: VEC] ~ {
piece: PROC [p: Bezier, speed: REAL] ~ {
segmentStart: REAL ~ used;
segmentEnd: REAL ~ used + speed;
dashStartParam: REAL ← 0;
needMove: BOOLFALSE;
start: PROC ~ {
dashStartParam ← (used-segmentStart)/speed;
needMove ← TRUE;
};
stop: PROC ~ {
dashEndParam: REAL ← (used-segmentStart)/speed;
b: Bezier ← SubPiece[p, dashStartParam, dashEndParam];
IF needMove THEN moveTo[b[0]];
curveTo[b[1], b[2], b[3]];
needMove ← FALSE;
};
Advance[segmentEnd, start, stop];
IF on THEN stop[];
};
p: Bezier ~ [lp, p1, p2, p3];
fullScale: REAL ~ AverageSpeed[p].value;
IF fullScale > 0.0 THEN DivideUntilSmooth[p, piece, fullScale];
lp ← p3;
};
conic: PROC [p1, p2: VEC, r: REAL] ~ {
ImagerPath.ConicToCurves[lp, p1, p2, r, curve]
};
arc: PROC [p1, p2: VEC] ~ {ImagerPath.ArcToConics[lp, p1, p2, conic]};
path[moveTo: move, lineTo: line, curveTo: curve, conicTo: conic, arcTo: arc];
};
Advance[offset*stretch];
RasterMaskStroke[context, dashedPath, FALSE];
};
bitsPerWord: NAT ~ Basics.bitsPerWord;
SMul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ INLINE {RETURN [IF a#0 THEN Smul[a,b] ELSE 0]};
Smul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ {
Overflow cannot happen.
HighBit: PROC [a: INTEGER] RETURNS [[0..1]] ~ INLINE {
RETURN [LOOPHOLE[a, PACKED ARRAY [0..Basics.bitsPerWord) OF [0..1]][0]];
};
Card: PROC [i: INTEGER] RETURNS [NAT] ~ INLINE {RETURN [LOOPHOLE[i]]};
SELECT HighBit[a]*2+HighBit[b] FROM
0 => RETURN [Basics.LongMult[Card[a], Card[b]]];
1 => RETURN [-Basics.LongMult[Card[a], Card[-b]]];
2 => RETURN [-Basics.LongMult[Card[-a], Card[b]]];
3 => RETURN [Basics.LongMult[Card[-a], Card[-b]]];
ENDCASE => ERROR;
};
worryReal: REALLAST[NAT]*0.5;
IsAllInteger: PROC [m: Transformation] RETURNS [BOOL] ~ {
Is: PROC [r: REAL] RETURNS [BOOL] ~ {
IF r IN [-worryReal..worryReal] THEN {
ir: INT ~ Real.Round[r];
IF r = ir THEN RETURN [TRUE];
IF RealFns.AlmostEqual[r, Real.Round[r], -18] THEN RETURN [TRUE];
};
RETURN [FALSE];
};
RETURN [Is[m.a] AND Is[m.b] AND Is[m.c] AND Is[m.d] AND Is[m.e] AND Is[m.f]];
};
Ceiling: PROC [r: REAL] RETURNS [i: INT] ~ {
i ← Real.Round[r];
IF i < r THEN i ← i + 1;
};
SF: TYPE ~ RECORD [s, f: INT];
ClippedBounds: PROC [clipBox: DeviceBox, p0, p1: SF] RETURNS [DeviceBox] ~ {
Computes bounding box of p0 and p1, clipped against clipBox.
IF p0.s > p1.s THEN {t: INT ← p0.s; p0.s ← p1.s; p1.s ← t};
IF p0.f > p1.f THEN {t: INT ← p0.f; p0.f ← p1.f; p1.f ← t};
p0.s ← MAX[p0.s, clipBox.smin];
p0.f ← MAX[p0.f, clipBox.fmin];
p1.s ← MIN[p1.s, clipBox.smax];
p1.f ← MIN[p1.f, clipBox.fmax];
IF p0.s < p1.s AND p0.f < p1.f THEN RETURN [[smin: p0.s, fmin: p0.f, smax: p1.s, fmax: p1.f]]
ELSE RETURN [[smin: clipBox.smin, fmin: clipBox.fmin, smax: clipBox.smin, fmax: clipBox.fmin]];
};
specialMaskBits: BOOLTRUE;
MaskSampledBits: PROC [data: Data, base: LONG POINTER, wordsPerLine: NAT,
sMin, fMin: NAT, sSize, fSize: NAT, maskToDevice: Transformation, constant: BOOL] ~ {
device: Device ~ data.device;
SELECT TRUE FROM
maskToDevice.form=3 AND maskToDevice.integerTrans AND device.class.MaskBits#NIL => {
smin: INT ~ maskToDevice.tx; smax: INT ~ smin+sSize;
fmin: INT ~ maskToDevice.ty; fmax: INT ~ fmin+fSize;
box: DeviceBox ← data.clientClipBox;
boxes: PROC[action: PROC[DeviceBox]] ~ {
ClipBoxToMask[box: box, mask: data.clientClipMask, action: action];
};
IF smin<box.smax AND smax>box.smin AND fmin<box.fmax AND fmax>box.fmin THEN {
IF box.smin<smin THEN box.smin ← smin; IF box.smax>smax THEN box.smax ← smax;
IF box.fmin<fmin THEN box.fmin ← fmin; IF box.fmax>fmax THEN box.fmax ← fmax;
device.class.MaskBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine,
ts: smin-sMin, tf: fmin-fMin, boxes: boxes];
};
};
specialMaskBits AND maskToDevice.form#0 AND IsAllInteger[maskToDevice] AND constant AND device.class.MaskBoxes#NIL => {
a: INTEGER ~ Real.Round[maskToDevice.a];
b: INTEGER ~ Real.Round[maskToDevice.b];
c: INTEGER ~ maskToDevice.tx;
d: INTEGER ~ Real.Round[maskToDevice.d];
e: INTEGER ~ Real.Round[maskToDevice.e];
f: INTEGER ~ maskToDevice.ty;
SF: TYPE ~ RECORD [s, f: INT];
Map: PROC [x, y: INTEGER] RETURNS [SF] ~ {
RETURN [[SMul[a, x] + SMul[b, y] + c, SMul[d, x] + SMul[e, y] + f]];
};
box: DeviceBox ~ ClippedBounds[data.clientClipBox, [c, f], Map[sSize, fSize]];
boxes: PROC[action: PROC[DeviceBox]] ~ {
myAction: PROC[clipBox: DeviceBox] ~ {
run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
clippedBox: DeviceBox ~ ClippedBounds[clipBox, Map[sMin, fMin], Map[sMin+1, fMin+fSize]];
IF clippedBox.smin # clippedBox.smax THEN action[clippedBox];
};
sb: Imager.Box ~ ImagerBox.BoxFromRect[
ImagerTransformation.InverseTransformRectangle[maskToDevice, [
x: clipBox.smin,
y: clipBox.fmin,
w: clipBox.smax-clipBox.smin,
h: clipBox.fmax-clipBox.fmin
]]
];
srcBox: DeviceBox ~ ClippedBounds[
clipBox: [smin: 0, fmin: 0, smax: sSize, fmax: fSize],
p0: [Real.Fix[sb.xmin], Real.Fix[sb.ymin]],
p1: [Ceiling[sb.xmax], Ceiling[sb.ymax]]
];
ImagerMask.RunsFromBits[base: base, wordsPerLine: wordsPerLine, sBits: srcBox.smin+sMin, fBits: srcBox.fmin+fMin, sSize: srcBox.smax-srcBox.smin, fSize: srcBox.fmax-srcBox.fmin, sRuns: srcBox.smin, fRuns: srcBox.fmin, run: run];
};
ClipBoxToMask[box: box, mask: data.clientClipMask, action: myAction];
};
device.class.MaskBoxes[device: device, bounds: box, boxes: boxes];
};
ENDCASE => HardMaskSampledBits[data, base, wordsPerLine, sMin, fMin, sSize, fSize, maskToDevice];
};
HardMaskSampledBits: PROC [data: Data, base: LONG POINTER, wordsPerLine: NAT,
sMin, fMin: NAT, sSize, fSize: NAT, maskToDevice: Transformation] ~ {
sampler: ImagerSample.Sampler ~ NEW[ImagerSample.SamplerRep ← []];
maskBitsAction: PROC [bounds: DeviceBox, runs: PROC [RunProc]] ~ {
device: Device ~ data.device;
buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchBuffer[
iSize: 1, jSize: bounds.fmax-bounds.fmin];
maskBitsRuns: PROC [run: RunProc] ~ {
maskBitsRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
state: {off, on} ← off;
f0: NAT ← 0;
ImagerSample.GetPointSamples[sampler: sampler, s: sMin, f: fMin,
buffer: buffer, bi: 0, bj: 0, count: fSize];
FOR f: NAT IN[0..fSize) DO
x: ImagerSample.Sample ~ buffer[f];
SELECT state FROM
off => IF x#0 THEN { f0 ← f; state ← on };
on => IF x=0 THEN { run[sMin: sMin, fMin: fMin+f0, fSize: f-f0]; state ← off };
ENDCASE => ERROR;
ENDLOOP;
IF state=on THEN run[sMin: sMin, fMin: fMin+f0, fSize: fSize-f0];
};
runs[maskBitsRun];
};
ImagerSample.SetSamplerPosition[sampler: sampler,
m: maskToDevice, s: bounds.smin, f: bounds.fmin];
device.class.MaskRuns[device: device, bounds: bounds, runs: maskBitsRuns];
ImagerSample.ReleaseScratchBuffer[buffer];
};
maskBoundary: PathProc ~ {
moveTo[[0, 0]]; lineTo[[sSize, 0]]; lineTo[[sSize, fSize]]; lineTo[[0, fSize]];
};
sampler.base ← base;
sampler.wordsPerLine ← wordsPerLine;
sampler.bitsPerSample ← 1;
sampler.sMin ← sMin;
sampler.fMin ← fMin;
sampler.sSize ← sSize;
sampler.fSize ← fSize;
ImagerSample.SetSamplerIncrements[sampler, maskToDevice];
RunsFromPath[data: data, path: maskBoundary,
transformation: maskToDevice, action: maskBitsAction];
};
bitsToClient: Transformation ~ ImagerTransformation.Rotate[-90];
RasterMaskBits: PROC [context: Context, base: LONG POINTER, wordsPerLine: NAT,
sMin, fMin: NAT ← 0, sSize, fSize: NAT, tx, ty: INTEGER] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
data: Data ~ NARROW[context.data];
maskToDevice: Transformation ~ data.maskToDevice;
Validate[data, state, maskNeeds];
maskToDevice^ ← bitsToClient^;
maskToDevice.ApplyPostTranslate[[tx, ty]];
maskToDevice.ApplyPostConcat[data.clientToDevice];
MaskSampledBits[data: data, base: base, wordsPerLine: wordsPerLine,
sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize, maskToDevice: maskToDevice, constant: state.color.tag = constant];
};
};
RasterDrawBits: PROC [context: Context, base: LONG POINTER, wordsPerLine: NAT,
sMin, fMin: NAT ← 0, sSize, fSize: NAT, tx, ty: INTEGER] ~ {
drawBitsAction: PROC ~ {
Imager.SetColor[context: context, color: Imager.white];
Imager.MaskRectangleI[context: context, x: tx, y: ty, w: fSize, h: -sSize];
Imager.SetColor[context: context, color: Imager.black];
Imager.MaskBits[context: context, base: base, wordsPerLine: wordsPerLine,
sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize, tx: tx, ty: ty];
};
state: State ~ context.state;
IF state.np.noImage=0 THEN {
data: Data ~ NARROW[context.data];
maskToDevice: Transformation ~ data.maskToDevice;
Validate[data, state, maskNeeds];
maskToDevice^ ← bitsToClient^;
maskToDevice.ApplyPostTranslate[[tx, ty]];
maskToDevice.ApplyPostConcat[data.clientToDevice];
IF maskToDevice.form=3 AND maskToDevice.integerTrans AND data.device.class.DrawBits # NIL THEN {
smin: INT ~ maskToDevice.tx; smax: INT ~ smin+sSize;
fmin: INT ~ maskToDevice.ty; fmax: INT ~ fmin+fSize;
box: DeviceBox ← data.clientClipBox;
boxes: PROC[action: PROC[DeviceBox]] ~ {
ClipBoxToMask[box: box, mask: data.clientClipMask, action: action];
};
IF smin<box.smax AND smax>box.smin AND fmin<box.fmax AND fmax>box.fmin THEN {
device: Device ~ data.device;
IF box.smin<smin THEN box.smin ← smin; IF box.smax>smax THEN box.smax ← smax;
IF box.fmin<fmin THEN box.fmin ← fmin; IF box.fmax>fmax THEN box.fmax ← fmax;
device.class.DrawBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine,
ts: smin-sMin, tf: fmin-fMin, boxes: boxes];
};
}
ELSE Imager.DoSave[context, drawBitsAction];
};
};
RasterMaskPixel: PROC [context: Context, pa: PixelArray] ~ {
state: State ~ context.state;
IF pa.samplesPerPixel=1 THEN NULL
ELSE ERROR Imager.Error[[code: $illegalPixelMask,
explanation: "MaskPixel argument must have one sample per pixel."]];
IF ImagerPixelArray.MaxSampleValue[pa, 0]=1 THEN NULL
ELSE ERROR Imager.Error[[code: $illegalPixelMask,
explanation: "MaskPixel argument must have one bit per sample."]];
IF state.np.noImage=0 THEN TRUSTED {
rast: NAT ~ (pa.fSize+bitsPerWord)/bitsPerWord;
words: INT ~ Basics.LongMult[pa.sSize, rast];
vm: VM.Interval ~ VM.SimpleAllocate[VM.PagesForWords[words]];
pointer: LONG POINTER ~ VM.AddressForPageNumber[vm.page];
DoMask: UNSAFE PROC ~ {
data: Data ~ NARROW[context.data];
maskToDevice: Transformation ~ data.maskToDevice;
Validate[data, state, maskNeeds];
maskToDevice.ApplyCat[pa.m, data.clientToDevice];
ImagerPixelArray.UnsafeGetBits[pa: pa, i: 0, s: 0, f: 0,
dst: [word: pointer, bit: 0], dstBpl: rast*bitsPerWord,
width: pa.fSize, height: pa.sSize];
MaskSampledBits[data: data,
base: pointer, wordsPerLine: rast,
sMin: 0, fMin: 0, sSize: pa.sSize, fSize: pa.fSize,
maskToDevice: maskToDevice, constant: state.color.tag = constant];
};
DoMask[ ! UNWIND => VM.Free[vm]];
VM.Free[vm];
};
};
RasterGetBounds: PROC [context: Context] RETURNS [Rectangle] ~ {
state: State ~ context.state;
data: Data ~ NARROW[context.data];
Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]];
RETURN[data.clientToDevice.InverseTransformRectangle[[
x: data.clientClipRect.sMin, y: data.clientClipRect.fMin,
w: data.clientClipRect.sSize, h: data.clientClipRect.fSize
]]];
};
DViewReset: PROC [data: Data] ~ {
data.viewToDevice^ ← data.device.surfaceToDevice^;
data.viewClipBox ← DeviceRectangleFromDeviceBox[data.device.box];
ImagerManhattan.Destroy[data.viewClipMask];
data.viewClipMask ← ImagerManhattan.CreateFromBox[data.viewClipBox];
data.valid.clientToDevice ← FALSE;
data.valid.clientClipper ← FALSE;
data.valid.fontInfo ← FALSE;
data.valid.deviceColor ← FALSE;
};
DViewTranslateI: PROC [data: Data, x, y: INTEGER] ~ {
data.viewToDevice.ApplyPreTranslate[[x, y]];
data.valid.clientToDevice ← FALSE;
data.valid.clientClipper ← FALSE;
data.valid.deviceColor ← FALSE;
Note that translation doesn't invalidate charToDevice
};
DViewClipRectangleI: PROC [data: Data, x, y, w, h: INTEGER, exclude: BOOL] ~ {
viewToDevice: Transformation ~ data.viewToDevice;
IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN {
old: ManhattanPolygon ~ data.viewClipMask;
smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h];
fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w];
rect: DeviceRectangle ~ [sMin: smin, fMin: fmin, sSize: ABS[h], fSize: ABS[w]];
IF exclude THEN {
t: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[rect];
data.viewClipMask ← ImagerManhattanExtras.DestructiveDifference[data.viewClipMask, t];
ImagerManhattan.Destroy[t];
}
ELSE data.viewClipMask ← ImagerManhattanExtras.DestructiveClip[data.viewClipMask, rect];
data.viewClipBox ← ImagerManhattan.BoundingBox[data.viewClipMask];
data.valid.clientClipper ← FALSE;
}
ELSE {
path: PathProc ~ {
moveTo[[x, y]];
lineTo[[x+w, y]];
lineTo[[x+w, y+h]];
lineTo[[x, y+h]];
};
DViewClip[data: data, path: path, parity: FALSE, exclude: exclude];
};
};
CombineManhattan: PROC [a, b: ManhattanPolygon, exclude: BOOL] RETURNS [ManhattanPolygon] ~ {
IF exclude THEN RETURN [ImagerManhattanExtras.DestructiveDifference[a, b]]
ELSE RETURN [ImagerManhattanExtras.DestructiveIntersection[a, b]];
};
DViewClip: PROC [data: Data, path: PathProc, parity: BOOL, exclude: BOOL] ~ {
this: ManhattanPolygon;
data.devicePath ← ImagerScanConverter.CreatePath[path: path,
transformation: data.viewToDevice, clipBox: data.viewClipBox,
scratch: data.devicePath];
this ← ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath,
clipBox: data.viewClipBox, parityFill: parity];
data.viewClipMask ← CombineManhattan[data.viewClipMask, this, exclude];
ImagerManhattan.Destroy[this];
data.viewClipBox ← ImagerManhattan.BoundingBox[data.viewClipMask];
data.valid.clientClipper ← FALSE;
};
GetViewerClass: PROC [context: Context] RETURNS [ImagerBackdoorPrivate.Class] ~ {
class: Class ~ context.class;
val: REF ~ Atom.GetPropFromList[class.propList, $viewOperations];
WITH val SELECT FROM
x: ImagerBackdoorPrivate.Class => RETURN[x];
ENDCASE => RETURN[NIL];
};
ViewReset: PUBLIC PROC [context: Context] ~ {
WITH context.data SELECT FROM
data: Data => DViewReset[data];
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
viewerClass.ViewReset[context]
ELSE
ERROR Imager.Error[[$unimplemented, "ViewReset not implemented"]];
};
};
ViewTranslateI: PUBLIC PROC [context: Context, x, y: INTEGER] ~ {
WITH context.data SELECT FROM
data: Data => DViewTranslateI[data, x, y];
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
viewerClass.ViewTranslateI[context, x, y]
ELSE
ERROR Imager.Error[[$unimplemented, "ViewTranslate not implemented"]];
};
};
ViewClip: PUBLIC PROC [context: Context, path: PathProc, parity: BOOL, exclude: BOOL] ~ {
WITH context.data SELECT FROM
data: Data => DViewClip[data: data, path: path, parity: parity, exclude: exclude];
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
viewerClass.ViewClip[context: context, path: path, parity: parity, exclude: exclude]
ELSE
ERROR Imager.Error[[$unimplemented, "ViewClip not implemented"]];
};
};
ViewClipRectangleI: PUBLIC PROC [context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ {
WITH context.data SELECT FROM
data: Data => DViewClipRectangleI[data: data, x: x, y: y, w: w, h: h, exclude: exclude];
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
viewerClass.ViewClipRectangleI[context: context, x: x, y: y, w: w, h: h, exclude: exclude]
ELSE
ERROR Imager.Error[[$unimplemented, "ViewClipRectangleI not implemented"]];
};
};
ViewFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
state: State ~ context.state;
IF state#NIL THEN RETURN[state.T.Transform[p]]
ELSE {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
RETURN[viewerClass.ViewFromClient[context: context, p: p]]
};
ERROR Imager.Error[[$unimplemented, "ViewFromClient not implemented"]];
};
ClientFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
state: State ~ context.state;
IF state#NIL THEN RETURN[state.T.InverseTransform[p]]
ELSE {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
RETURN[viewerClass.ClientFromView[context: context, p: p]]
};
ERROR Imager.Error[[$unimplemented, "ClientFromView not implemented"]];
};
DeviceFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
WITH context.data SELECT FROM
data: Data => RETURN[data.viewToDevice.Transform[p]];
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
RETURN[viewerClass.DeviceFromView[context: context, p: p]]
ELSE
ERROR Imager.Error[[$unimplemented, "DeviceFromView not implemented"]];
};
};
ViewFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
WITH context.data SELECT FROM
data: Data => RETURN[data.viewToDevice.InverseTransform[p]];
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
RETURN[viewerClass.ViewFromDevice[context: context, p: p]]
ELSE
ERROR Imager.Error[[$unimplemented, "ViewFromDevice not implemented"]];
};
};
DeviceFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
WITH context.data SELECT FROM
data: Data => {
Validate[data, context.state, [clientToDevice: TRUE]];
RETURN[data.clientToDevice.Transform[p]];
};
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
RETURN[viewerClass.DeviceFromClient[context: context, p: p]]
ELSE
ERROR Imager.Error[[$unimplemented, "DeviceFromClient not implemented"]];
};
};
ClientFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
WITH context.data SELECT FROM
data: Data => {
Validate[data, context.state, [clientToDevice: TRUE]];
RETURN[data.clientToDevice.InverseTransform[p]];
};
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
RETURN[viewerClass.ClientFromDevice[context: context, p: p]]
ELSE
ERROR Imager.Error[[$unimplemented, "ClientFromDevice not implemented"]];
};
};
MoveViewRectangle: PUBLIC PROC [context: Context,
width, height, fromX, fromY, toX, toY: INTEGER] ~ {
WITH context.data SELECT FROM
data: Data => {
state: State ~ context.state;
Validate[data, state, maskNeeds];
DMoveViewRectangle[data: data,
width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY];
};
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
viewerClass.MoveViewRectangle[context: context, width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY]
ELSE
ERROR Imager.Error[[$unimplemented, "MoveViewRectangle not implemented"]];
};
};
DMoveViewRectangle: PROC [data: Data,
width, height, fromX, fromY, toX, toY: INTEGER] ~ {
viewToDevice: Transformation ~ data.viewToDevice;
IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN {
smax: INTINT[viewToDevice.tx]-toY; smin: INT ← smax-height;
fmin: INTINT[viewToDevice.ty]+toX; fmax: INT ← fmin+width;
box: DeviceBox ← DeviceBoxFromDeviceRectangle[data.viewClipBox];
boxes: PROC[action: PROC[DeviceBox]] ~ {
ClipBoxToMask[box: box, mask: data.viewClipMask, action: action];
};
IF smin<box.smax AND smax>box.smin AND fmin<box.fmax AND fmax>box.fmin THEN {
device: Device ~ data.device;
IF box.smin<smin THEN box.smin ← smin; IF box.smax>smax THEN box.smax ← smax;
IF box.fmin<fmin THEN box.fmin ← fmin; IF box.fmax>fmax THEN box.fmax ← fmax;
device.class.MoveBoxes[device: device, ts: fromY-toY, tf: toX-fromX, boxes: boxes];
};
}
ELSE ERROR; -- hard case
};
Visibility: TYPE ~ ImagerBackdoor.Visibility;
TestViewRectangle: PUBLIC PROC [context: Imager.Context, x, y, w, h: INTEGER]
RETURNS
[Visibility] ~ {
WITH context.data SELECT FROM
data: Data => {
state: State ~ context.state;
Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]];
RETURN[DTestViewRectangle[data: data, x: x, y: y, w: w, h: h]];
};
ENDCASE => {
viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context];
IF viewerClass#NIL THEN
RETURN[viewerClass.TestViewRectangle[context: context, x: x, y: y, w: w, h: h]]
ELSE
ERROR Imager.Error[[$unimplemented, "TestRectangleI not implemented"]];
};
};
DTestViewRectangle: PROC [data: Data, x, y, w, h: INTEGER] RETURNS [Visibility] ~ {
viewToDevice: Transformation ~ data.viewToDevice;
viewClipMask: ManhattanPolygon ~ data.viewClipMask;
IF viewClipMask=NIL THEN RETURN[none];
IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN {
clip: DeviceRectangle ~ data.viewClipBox;
csmin: INTEGER ~ clip.sMin;
csmax: INTEGER ~ csmin+clip.sSize;
cfmin: INTEGER ~ clip.fMin;
cfmax: INTEGER ~ cfmin+clip.fSize;
smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h];
smax: INTEGER ~ smin+ABS[h];
fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w];
fmax: INTEGER ~ fmin+ABS[w];
IF fmin>=cfmax OR smin>=csmax OR fmax<=cfmin OR smax<=csmin THEN RETURN[none];
IF viewClipMask.rest#NIL THEN RETURN[part];
IF fmin<cfmin OR smin<csmin OR fmax>cfmax OR smax>csmax THEN RETURN[part];
RETURN[all];
}
ELSE {
rect: Rectangle ~ viewToDevice.TransformRectangle[[x, y, w, h]];
-- test against viewClipBox --
RETURN[part];
};
};
RasterGetClipper: PUBLIC PROC[context: Context] RETURNS[clipper: Clipper] ~ {
data: Data ~ NARROW[context.data];
IF data.specialClipPresent THEN {
ERROR Imager.Error[[$unimplemented, "GetClipper during ImagerOps.DoWithBuffer"]];
};
RETURN [ImagerState.StateGetClipper[context]];
};
rasterClass: Class ~ NEW[ClassRep ← [
type: $Raster,
DoSave: ImagerState.StateDoSave,
SetInt: ImagerState.StateSetInt,
SetReal: ImagerState.StateSetReal,
SetT: ImagerState.StateSetT,
SetFont: ImagerState.StateSetFont,
SetColor: ImagerState.StateSetColor,
SetClipper: ImagerState.StateSetClipper,
GetInt: ImagerState.StateGetInt,
GetReal: ImagerState.StateGetReal,
GetT: ImagerState.StateGetT,
GetFont: ImagerState.StateGetFont,
GetColor: ImagerState.StateGetColor,
GetClipper: RasterGetClipper,
ConcatT: ImagerState.StateConcatT,
Scale2T: ImagerState.StateScale2T,
RotateT: ImagerState.StateRotateT,
TranslateT: ImagerState.StateTranslateT,
Move: ImagerState.StateMove,
SetXY: ImagerState.StateSetXY,
SetXYRel: ImagerState.StateSetXYRel,
Show: ImagerRasterPrivate.RasterShow,
ShowText: ImagerRasterPrivate.RasterShowText,
StartUnderline: ImagerState.StateStartUnderline,
MaskUnderline: ImagerState.StateMaskUnderline,
CorrectMask: ImagerState.StateCorrectMask,
CorrectSpace: ImagerState.StateCorrectSpace,
Space: ImagerState.StateSpace,
SetCorrectMeasure: ImagerState.StateSetCorrectMeasure,
SetCorrectTolerance: ImagerState.StateSetCorrectTolerance,
Correct: ImagerState.StateCorrect,
DontCorrect: ImagerState.StateDontCorrect,
SetGray: ImagerState.StateSetGray,
SetSampledColor: ImagerState.StateSetSampledColor,
SetSampledBlack: ImagerState.StateSetSampledBlack,
MaskFill: RasterMaskFill,
MaskStroke: RasterMaskStroke,
MaskDashedStroke: RasterMaskDashedStroke,
MaskRectangle: RasterMaskRectangle,
MaskRectangleI: RasterMaskRectangleI,
MaskVector: RasterMaskVector,
MaskPixel: RasterMaskPixel,
MaskBits: RasterMaskBits,
DrawBits: RasterDrawBits,
Clip: ImagerState.StateClip,
ClipRectangle: ImagerState.StateClipRectangle,
ClipRectangleI: ImagerState.StateClipRectangleI,
GetCP: ImagerState.StateGetCP,
GetBoundingRectangle: RasterGetBounds,
propList: NIL
]];
END.