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
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:
BOOL ←
FALSE, 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:
BOOL ←
FALSE, 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:
BOOL ←
TRUE;
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: INT ← INT[clientToDevice.tx]-y; smax: INT ← smin-h;
fmin: INT ← INT[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: BOOL ← TRUE;
Advance:
PROC [patternOffset:
REAL, startAction, stopAction:
PROC ←
NIL] ~ {
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: BOOL ← FALSE;
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:
REAL ←
LAST[
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:
BOOL ←
TRUE;
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: INT ← INT[viewToDevice.tx]-toY; smin: INT ← smax-height;
fmin: INT ← INT[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.