ImagerRasterImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, November 1, 1985 9:31:41 am PST
Doug Wyatt, May 7, 1986 5:32:43 pm PDT
DIRECTORY
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],
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 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;
};
DViewTranslate: 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;
};
ViewReset: PUBLIC PROC [context: Context] ~ {
WITH context.data SELECT FROM
data: Data => DViewReset[data];
ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewReset not implemented"]];
};
ViewTranslateI: PUBLIC PROC [context: Context, x, y: INTEGER] ~ {
WITH context.data SELECT FROM
data: Data => DViewTranslate[data, x, y];
ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewTranslateI 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 => 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 => ERROR Imager.Error[[$unimplemented, "ViewClipRectangleI not implemented"]];
};
ViewFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
state: State ~ context.state;
IF state=NIL THEN ERROR Imager.Error[[$unimplemented, "ViewFromClient not implemented"]];
RETURN[state.T.Transform[p]];
};
ClientFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
state: State ~ context.state;
IF state=NIL THEN ERROR Imager.Error[[$unimplemented, "ClientFromView not implemented"]];
RETURN[state.T.InverseTransform[p]];
};
DeviceFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ {
WITH context.data SELECT FROM
data: Data => RETURN[data.viewToDevice.Transform[p]];
ENDCASE => 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 => 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 => 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 => ERROR Imager.Error[[$unimplemented, "ClientFromDevice not implemented"]];
};
MoveViewRectangle: PUBLIC PROC [context: Context,
width, height, fromX, fromY, toX, toY: INTEGER] ~ {
state: State ~ context.state;
WITH context.data SELECT FROM
data: Data => {
Validate[data, state, maskNeeds];
DMoveViewRectangle[data: data,
width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY];
};
ENDCASE => 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] ~ {
state: State ~ context.state;
WITH context.data SELECT FROM
data: Data => {
Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]];
RETURN[DTestViewRectangle[data: data, x: x, y: y, w: w, h: h]];
};
ENDCASE => 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.