DIRECTORY
Basics USING [BITAND, BITNOT, BITOR],
Char,
Imager USING [black, ClassRep, Color, ColorOperator, Context, ContextRep, CorrectMask, CorrectSpace, DoSave, DoSaveAll, Error, MaskRectangleI, metersPerInch, Object, SetColor, SetFont, SetXRel, SetXYRel, Show, Trans],
ImagerBackdoor USING [Clipper, CoordSys, Visibility],
ImagerBox USING [Rectangle],
ImagerBrick,
ImagerColor,
ImagerDevice,
ImagerDeviceInterchange,
ImagerDeviceVector USING [DVec, DVecRep, ScaledVec],
ImagerDeviceWorks,
ImagerFont USING [Amplified, Correction, Escapement, Font, Typeface, TypefaceRep, XChar, XCharProc, XStringProc],
ImagerFontWorks USING [MaskChar],
ImagerManhattan USING [BoundingBox, ClipBoxToMask, Copy, CreateFromBox, Destroy, DestructiveClip, DestructiveDifference, DestructiveIntersection, Polygon],
ImagerMaskCache USING [CharMask, CharMaskRep],
ImagerPath USING [PathProc],
ImagerPen USING [Pen],
ImagerPenExtras USING [MakeThickenedTransformedCircle],
ImagerPixel USING [PixelMap],
ImagerPixelArray USING [MaxSampleValue, PixelArray],
ImagerPrivate USING [Class, ClassRep, DefaultDiscardBuffer, DefaultDoIfVisible, DefaultDoWithBuffer, DefaultDrawBitmap, DefaultDrawObject, DefaultDrawPixels, DefaultRestoreBuffer, DefaultSaveBuffer, DefaultShowAndFixedXRel, DefaultShowText, DefaultTransform],
ImagerRaster USING [],
ImagerRasterPublic USING [Interceptor],
ImagerSample USING [Clip, EdgeAction, GetBox, RawDescriptor, ReleaseDescriptor, SampleMap],
ImagerScanConverter,
ImagerState USING [ChangeFlags, notChanged, RasterData, RasterDataRep, State, StateClip, StateClipRectangle, StateClipRectangleI, StateConcatT, StateCorrect, StateCorrectMask, StateCorrectSpace, StateDontCorrect, StateGetClipper, StateGetColor, StateGetCP, StateGetFont, StateGetInt, StateGetReal, StateGetT, StateMaskUnderline, StateMove, StateRep, StateRestore, StateRotateT, StateSave, StateScale2T, StateSetClipper, StateSetColor, StateSetCorrectMeasure, StateSetCorrectTolerance, StateSetFont, StateSetGray, StateSetInt, StateSetReal, StateSetSampledBlack, StateSetSampledColor, StateSetT, StateSetXY, StateSetXYRel, StateSpace, StateStartUnderline, StateTranslateT],
ImagerStroke USING [EndCode, warningSquareEnd, squareEnd, buttEnd, roundEnd],
ImagerSwitches USING [Define, Value],
ImagerTransformation USING [ApplyCat, ApplyInvert, ApplyPreConcat, ApplyPreScale, ApplyPreTranslate, ApplyPostScale2, ApplyPostTranslate, ApplySFToXY, ApplyTranslateTo, Concat, Copy, Destroy, EasyTransformation, EasyTransformBox, Equal, InverseTransformRectangle, Scale, Scale2, ScanMode, Transformation, Transform, TransformRectangle, XYToSF],
ImagerTypeface USING [MakeFont, ReverseMetrics, Typeface, TypefaceRep],
Real USING [Round],
RealFns USING [AlmostEqual],
Rope USING [ROPE],
ImagerScaled USING [Float, Floor, FromReal, PLUS, zero],
SF USING [Add, Box, BoxGenerator, Disjoint, Displace, Empty, In, Inside, Intersect, Max, maxBox, maxVec, Min, minVec, Nonempty, Size, Sub, Vec],
Vector2 USING [Div, Mul, VEC];
Types and Constants
Class: TYPE ~ ImagerPrivate.Class;
ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep
Typeface: TYPE ~ ImagerTypeface.Typeface;
TypefaceRep:
PUBLIC
TYPE ~ ImagerTypeface.TypefaceRep;
-- export to ImagerFont.TypefaceRep
State: TYPE ~ ImagerState.State;
StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep
ChangeFlags: TYPE ~ ImagerState.ChangeFlags;
CharMask: TYPE ~ ImagerMaskCache.CharMask;
CharMaskRep: TYPE ~ ImagerMaskCache.CharMaskRep;
Clipper: TYPE ~ ImagerBackdoor.Clipper;
Color: TYPE ~ Imager.Color;
ColorOperator: TYPE ~ Imager.ColorOperator;
Context: TYPE ~ Imager.Context;
CoordSys: TYPE ~ ImagerBackdoor.CoordSys;
DeviceClass: TYPE ~ ImagerDevice.DeviceClass;
Device: TYPE ~ ImagerDevice.Device;
DeviceClipper: TYPE ~ ImagerDevice.DeviceClipper;
DevicePath: TYPE ~ ImagerScanConverter.DevicePath;
Font: TYPE ~ ImagerFont.Font;
Interceptor: TYPE ~ ImagerRasterPublic.Interceptor;
IntVec: TYPE ~ RECORD [s, f: INT];
ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon;
Object: TYPE ~ Imager.Object;
PathProc: TYPE ~ ImagerPath.PathProc;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
PixelMap: TYPE ~ ImagerPixel.PixelMap;
RawDescriptor: TYPE ~ ImagerSample.RawDescriptor;
Rectangle: TYPE ~ ImagerBox.Rectangle;
SampleMap: TYPE ~ ImagerSample.SampleMap;
ScanMode: TYPE ~ ImagerTransformation.ScanMode;
ScaledVec: TYPE ~ ImagerDeviceVector.ScaledVec;
Transformation: TYPE ~ ImagerTransformation.Transformation;
VEC: TYPE ~ Vector2.VEC;
Visibility: TYPE ~ ImagerBackdoor.Visibility;
XChar: TYPE ~ ImagerFont.XChar;
XStringProc:
TYPE ~ ImagerFont.XStringProc;
notChanged: ChangeFlags ~ ImagerState.notChanged;
zeroScaledVec: ScaledVec ~ [ImagerScaled.zero, ImagerScaled.zero];
Flags:
TYPE ~
MACHINE DEPENDENT
RECORD [
compositeClipper: BOOL ¬ FALSE, -- device.worksState.clipper is valid
fontInfo: BOOL ¬ FALSE, -- charToDevice, fontAtom valid
deviceColor: BOOL ¬ FALSE, -- color last set by SetColor[device, ...] is valid
devicePriority: BOOL ¬ FALSE, -- priority last set by SetPriority[device, ...] is valid
unused: PACKED ARRAY [0..BITS[CARDINAL]-4) OF [0..1] ¬ ALL[0]
];
identity: Transformation = ImagerTransformation.Scale[1.0];
maskNeeds: Flags = [compositeClipper: TRUE, deviceColor: TRUE, devicePriority: TRUE];
showNeeds: Flags = [compositeClipper:
TRUE, fontInfo:
TRUE, deviceColor:
TRUE, devicePriority:
TRUE];
RasterData: TYPE = REF RasterDataRep;
RasterDataRep:
PUBLIC
TYPE =
RECORD [
-- Export to ImagerState.RasterDataRep
device: Device,
valid: Flags ¬ [],
surfaceToDevice: Transformation ¬ NIL, -- always valid
viewClipper: DeviceClipper ¬ NIL, -- device.state.bounds device coords; always valid
compositeClipper: DeviceClipper ¬ NIL, -- device coords; valid iff valid.compositeClipper - use device.worksState.clipper instead
fontAtom: ImagerFont.Font ¬ NIL, -- canonicalized ref used as part of key for font cache
showValid: BOOL ¬ FALSE, -- says whether any of the following is valid
easyMetrics: ImagerDevice.EasyMetrics ¬ none,
correctSum: ScaledVec ¬ zeroScaledVec, -- used only in correct pass 1
correctMask: ScaledVec ¬ zeroScaledVec, -- used only in correct pass 2
lastAmpMask: ImagerMaskCache.CharMask ¬ NIL, -- validates lastAmp
lastAmp: ScaledVec ¬ zeroScaledVec, -- amplified space width
lastUnCorrectedSpace: ScaledVec ¬ zeroScaledVec, -- validates lastCorrectedSpace
lastCorrectedSpace: ScaledVec ¬ zeroScaledVec, -- used only in correct pass 2
lastCharToClient: Transformation ¬ NIL, -- used to validate charToDevice
charToDevice: Transformation ¬ NIL,
colorViewToDeviceScratch: Transformation -- see GetColorViewToDevice
];
Device State Maintainence
OrFlags:
PROC [f1, f2: Flags]
RETURNS [Flags] ~
INLINE {
RETURN[LOOPHOLE[Basics.BITOR[LOOPHOLE[f1], LOOPHOLE[f2]]]];
};
AndFlags:
PROC [f1, f2: Flags]
RETURNS [Flags] ~
INLINE {
RETURN[LOOPHOLE[Basics.BITAND[LOOPHOLE[f1], LOOPHOLE[f2]]]];
};
NotFlags:
PROC [f: Flags]
RETURNS [Flags] ~
INLINE {
RETURN[LOOPHOLE[Basics.BITNOT[LOOPHOLE[f]]]];
};
NoteStateChanges:
PROC [state: State] ~ {
rasterData: RasterData ~ state.rasterData;
changed: ImagerState.ChangeFlags ~ state.changed;
state.changed ¬ ImagerState.notChanged;
IF changed.T THEN { rasterData.valid.fontInfo ¬ FALSE; rasterData.lastCharToClient ¬ NIL };
IF changed.color THEN rasterData.valid.deviceColor ¬ FALSE;
IF changed.priority THEN rasterData.valid.devicePriority ¬ FALSE;
IF changed.clipper THEN rasterData.valid.compositeClipper ¬ FALSE;
IF changed.font THEN rasterData.valid.fontInfo ¬ FALSE;
};
aSwitch: CHAR = 'a;
a:
CHAR[aSwitch..aSwitch] = ImagerSwitches.Define[switch: aSwitch, name: $adjustsampledblack, doc: "Sampled blacks may be rescaled by a factor this close to unity, to improve rendered appearance and/or performance.", defaultValue:
NEW[
REAL ¬ 0.0001]];
GetColorViewToDevice:
PROC [state: State]
RETURNS [Transformation] = {
WITH state.color
SELECT
FROM
color: ImagerColor.SampledBlack => {
r:
REAL =
WITH ImagerSwitches.Value[aSwitch]
SELECT
FROM
rr: REF REAL => rr
ENDCASE => 0.0;
IF r
IN (0.0..1.0]
THEN {
rasterData: RasterData ~ state.rasterData;
m: Transformation ¬ rasterData.colorViewToDeviceScratch;
xScale, yScale: REAL ¬ 1.0;
origin: Vector2.VEC;
IF m = NIL THEN m ¬ rasterData.colorViewToDeviceScratch ¬ ImagerTransformation.Scale[1];
ImagerTransformation.ApplyCat[m, color.pa.m, color.um, state.viewToDevice];
origin ¬ ImagerTransformation.Transform[m, [0, 0]];
SELECT m.form
FROM
1 => { xScale ¬ ABS[m.a]; yScale ¬ ABS[m.e] };
2 => { xScale ¬ ABS[m.b]; yScale ¬ ABS[m.d] };
ENDCASE => RETURN [state.viewToDevice];
m ¬ state.viewToDevice;
ImagerTransformation.ApplyPostTranslate[m, [-origin.x, -origin.y]];
ImagerTransformation.ApplyPostScale2[m, [ScaleSnap[xScale, r], ScaleSnap[yScale, r]]];
ImagerTransformation.ApplyPostTranslate[m, origin];
RETURN [m]
};
};
ENDCASE;
RETURN [state.viewToDevice];
};
ScaleSnap:
PROC [s:
REAL, r:
REAL]
RETURNS [v:
REAL] = {
Returns a number v within r of unity. If possible, s*v is a small integer.
IF s > 0.5
THEN {
min: REAL = 1.0-r;
max: REAL = 1.0+r;
f: REAL = 1.0/s;
kf: REAL ¬ f;
THROUGH [1..4]
DO
IF kf IN [min..max] THEN RETURN [kf];
kf ¬ kf + f;
ENDLOOP;
};
RETURN [1.0]
};
ValidateIfNeeded:
PROC [state: State, needs: Flags] ~ {
rasterData: RasterData ~ state.rasterData;
ValidateFontInfo:
PROC ~
INLINE {
font: Font ~ state.font;
IF font =
NIL
THEN ERROR Imager.Error[[code: $noFont, explanation: "Failed to set font before calling Show."]]
ELSE {
typeface: Typeface ~ font.typeface;
charToDevice: Transformation ~ rasterData.charToDevice;
IF rasterData.lastCharToClient =
NIL
OR
NOT ImagerTransformation.Equal[rasterData.lastCharToClient, font.charToClient]
THEN {
charToDevice ¬ state.clientToDevice;
charToDevice.ApplyTranslateTo[[0, 0]];
charToDevice.ApplyPreConcat[font.charToClient];
rasterData.lastCharToClient ¬ font.charToClient;
};
rasterData.fontAtom ¬ ImagerTypeface.MakeFont[typeface, charToDevice];
rasterData.valid.fontInfo ¬ TRUE;
};
};
ValidateColor:
PROC ~
INLINE {
rasterData.device.class.SetColor[rasterData.device, state.color, GetColorViewToDevice[state]];
rasterData.valid.deviceColor ¬ TRUE;
};
ValidatePriority:
PROC ~
INLINE {
IF rasterData.device.parm.classHas[SetPriority]
THEN rasterData.device.class.SetPriority[rasterData.device, state.np.priorityImportant#0];
rasterData.valid.devicePriority ¬ TRUE;
};
ValidateClientClipper:
PROC ~
INLINE {
ImagerDeviceWorks.Clip[device: rasterData.device, viewClipper: rasterData.viewClipper, clipperToDevice: state.viewToDevice, clientClipper: state.clipper];
rasterData.valid.compositeClipper ¬ TRUE;
};
fix: Flags ~ AndFlags[needs, NotFlags[rasterData.valid]];
IF fix.deviceColor THEN ValidateColor[];
IF fix.devicePriority THEN ValidatePriority[];
IF fix.compositeClipper THEN ValidateClientClipper[];
IF fix.fontInfo THEN ValidateFontInfo[];
IF AndFlags[rasterData.valid, needs]#needs THEN ERROR;
};
Validate:
PROC [state: State, needs: Flags] ~ {
rasterData: RasterData ~ state.rasterData;
IF state.changed#ImagerState.notChanged THEN NoteStateChanges[state];
IF AndFlags[rasterData.valid, needs]#needs THEN ValidateIfNeeded[state, needs];
};
Verify:
PROC [rasterData: RasterData, needs: Flags] ~
INLINE {
IF AndFlags[rasterData.valid, needs]#needs THEN ERROR;
};
GetDevice:
PUBLIC
PROC [context: Context]
RETURNS [Device ¬
NIL] ~ {
state: State ~ context.state;
IF state #
NIL
THEN {
rasterData: RasterData ~ state.rasterData;
IF rasterData #
NIL
THEN {
IF rasterData.device.class = modifiedDeviceClass
THEN {
iData: InterceptData ~ NARROW[rasterData.device.data];
RETURN [iData.device];
};
RETURN [rasterData.device]
};
};
};
DoWithDevice:
PUBLIC
PROC [context: Context, bounds:
SF.Box, action:
PROC [device: Device, clipper: DeviceClipper]] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[state, maskNeeds];
IF rasterData.device.class = modifiedDeviceClass
THEN {
iData: InterceptData ~ NARROW[rasterData.device.data];
proc:
PROC ~ {
iData.device.worksState.clipper^ ¬ rasterData.device.worksState.clipper^;
action[device: iData.device, clipper: iData.device.worksState.clipper];
iData.device.worksState.clipper^ ¬ [clipBox: [[0,0], [0,0]], clipMask: NIL];
};
interceptor: Interceptor ~ iData.interceptor;
interceptor.intercept[interceptor, bounds, proc, FALSE];
}
ELSE { action[device: rasterData.device, clipper: rasterData.device.worksState.clipper] };
};
MaskPixel
RasterMaskBitmap:
PROC [context: Context, bitmap: SampleMap, referencePoint:
SF.Vec, scanMode: ScanMode, position:
VEC] ~ {
state: State ~ context.state;
Validate[state, maskNeeds];
IF state.np.noImage=0
THEN {
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
m: Transformation ¬ ImagerTransformation.Copy[state.clientToDevice];
ImagerTransformation.ApplyPreTranslate[m, position];
ImagerTransformation.ApplySFToXY[m, scanMode, 0, 0];
ImagerTransformation.ApplyPreTranslate[m, [-referencePoint.s, -referencePoint.f]];
ImagerDeviceWorks.MaskBitmap[
device: device,
bitmap: bitmap,
bitsToDevice: m
];
ImagerTransformation.Destroy[m];
};
};
GetSampleMapClippedDisplacedBox:
PROC [map: SampleMap, clip:
SF.Box, m: Transformation]
RETURNS [
SF.Box] = {
box: SF.Box = ImagerSample.GetBox[map];
ts: INT = Real.Round[m.c];
tf: INT = Real.Round[m.f];
clipped: SF.Box = ClippedBounds[clipBox: clip, p0: [box.min.s + ts, box.min.f + tf], p1: [box.max.s + ts, box.max.f + tf]];
IF m.form # 3 THEN ERROR;
RETURN[clipped]
};
ClippedBounds:
PROC [clipBox:
SF.Box, p0, p1: IntVec]
RETURNS [
SF.Box] = {
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.min.s];
p0.f ¬ MAX[p0.f, clipBox.min.f];
p1.s ¬ MIN[p1.s, clipBox.max.s];
p1.f ¬ MIN[p1.f, clipBox.max.f];
IF p0.s < p1.s AND p0.f < p1.f
THEN RETURN[[min: [INTEGER[p0.s], INTEGER[p0.f]], max: [INTEGER[p1.s], INTEGER[p1.f]]]]
ELSE RETURN[[min: clipBox.min, max: clipBox.min]];
};
RasterDrawBitmap:
PROC [context: Context, bitmap: SampleMap, referencePoint:
SF.Vec, scanMode: ScanMode, position:
VEC] ~ {
state: State ~ context.state;
Validate[state, maskNeeds];
IF state.np.noImage=0
THEN {
rasterData: RasterData ~ state.rasterData;
m: Transformation ¬ ImagerTransformation.Copy[state.clientToDevice];
ImagerTransformation.ApplyPreTranslate[m, position];
ImagerTransformation.ApplySFToXY[m, scanMode, 0, 0];
ImagerTransformation.ApplyPreTranslate[m, [-referencePoint.s, -referencePoint.f]];
IF rasterData.device.parm.classHas[DrawBitmap]
AND m.form=3
AND
ABS[m.c] < bigTranslate
AND
ABS[m.f] < bigTranslate
THEN {
box: SF.Box ~ GetSampleMapClippedDisplacedBox[bitmap, rasterData.device.worksState.clipper.clipBox, m];
Boxes: PROC [action: PROC [SF.Box]] ~ { ImagerManhattan.ClipBoxToMask[box: box, mask: rasterData.device.worksState.clipper.clipMask, action: action] };
IF SF.Nonempty[box] THEN rasterData.device.class.DrawBitmap[device: rasterData.device, bitmap: bitmap, delta: [s: Real.Round[m.c], f: Real.Round[m.f]], bounds: box, boxes: Boxes];
}
ELSE ImagerPrivate.DefaultDrawBitmap[context, bitmap, referencePoint, scanMode, position];
ImagerTransformation.Destroy[m];
};
};
RasterMaskPixel:
PROC [context: Context, pa: PixelArray] ~ {
state: State ~ context.state;
IF pa.samplesPerPixel#1 THEN ERROR Imager.Error[[code: $illegalPixelMask, explanation: "MaskPixel argument must have one sample per pixel."]];
IF ImagerPixelArray.MaxSampleValue[pa, 0]#1 THEN ERROR Imager.Error[[code: $illegalPixelMask, explanation: "MaskPixel argument must have one bit per sample."]];
Validate[state, maskNeeds];
IF state.np.noImage=0
AND pa.sSize > 0
AND pa.fSize > 0
THEN
TRUSTED {
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
ImagerDeviceWorks.MaskPixelArray[device, pa, state.clientToDevice];
};
};
View
RasterViewReset:
PROC [context: Context] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
T: Transformation ~ ImagerState.StateGetT[context];
state.viewToDevice ¬ rasterData.surfaceToDevice;
IF rasterData.device.parm.surfaceUnitsPerPixel # 1
THEN
ImagerTransformation.ApplyPreScale[m: state.viewToDevice, s: rasterData.device.parm.surfaceUnitsPerPixel];
rasterData.viewClipper.clipBox ¬ [
max: [rasterData.device.parm.sSize, rasterData.device.parm.fSize]];
ImagerManhattan.Destroy[rasterData.viewClipper.clipMask];
rasterData.viewClipper.clipMask ¬ ImagerManhattan.CreateFromBox[rasterData.viewClipper.clipBox];
rasterData.valid.compositeClipper ¬ FALSE;
rasterData.valid.fontInfo ¬ FALSE;
rasterData.valid.deviceColor ¬ FALSE;
ImagerState.StateSetT[context, T];
ImagerTransformation.Destroy[T];
ImagerState.StateSetXY[context, [0.0, 0.0]];
};
RasterViewTranslateI:
PROC [context: Context, x, y:
INTEGER] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
T: Transformation ~ ImagerState.StateGetT[context];
ImagerTransformation.ApplyPreTranslate[state.viewToDevice, [x, y]];
ImagerState.StateSetT[context, T];
ImagerTransformation.Destroy[T];
rasterData.valid.compositeClipper ¬ FALSE;
rasterData.valid.deviceColor ¬ FALSE;
Note that translation doesn't invalidate charToDevice
};
RasterViewClipRectangleI:
PROC [context: Context, x, y, w, h:
INTEGER, exclude:
BOOL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
viewToDevice: Transformation ~ state.viewToDevice;
IF ImagerTransformation.EasyTransformation[viewToDevice]
THEN {
old: ManhattanPolygon ~ rasterData.viewClipper.clipMask;
box: SF.Box ~ ImagerTransformation.EasyTransformBox[m: viewToDevice, x: x, y: y, w: w, h: h, clip: rasterData.viewClipper.clipBox];
v: ManhattanPolygon ¬ rasterData.viewClipper.clipMask;
IF exclude
THEN { t: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[box]; v ¬ ImagerManhattan.DestructiveDifference[v, t]; ImagerManhattan.Destroy[t] }
ELSE v ¬ ImagerManhattan.DestructiveClip[v, box];
rasterData.viewClipper.clipMask ¬ v;
rasterData.viewClipper.clipBox ¬ ImagerManhattan.BoundingBox[v];
rasterData.valid.compositeClipper ¬ FALSE;
}
ELSE {
path: PathProc ~ {moveTo[[x,y]]; lineTo[[x+w,y]]; lineTo[[x+w,y+h]]; lineTo[[x,y+h]]};
RasterViewClip[context: context, path: path, oddWrap: FALSE, exclude: exclude];
};
};
RasterGetTransformation:
PROC [context: Context, from, to: CoordSys]
RETURNS [Transformation] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
m: Transformation ¬ NIL;
SELECT to
FROM
client => m ¬ ImagerTransformation.Copy[state.clientToDevice];
view => m ¬ ImagerTransformation.Copy[state.viewToDevice];
surface => m ¬ ImagerTransformation.Copy[rasterData.surfaceToDevice];
device => m ¬ ImagerTransformation.Scale[1.0];
ENDCASE => ERROR;
ImagerTransformation.ApplyInvert[m];
SELECT from
FROM
client => ImagerTransformation.ApplyPreConcat[m, state.clientToDevice];
view => ImagerTransformation.ApplyPreConcat[m, state.viewToDevice];
surface => ImagerTransformation.ApplyPreConcat[m, rasterData.surfaceToDevice];
device => NULL;
ENDCASE => ERROR;
RETURN [m]
};
RasterViewClip:
PROC [context: Context, path: PathProc, oddWrap:
BOOL, exclude:
BOOL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
this: ManhattanPolygon;
Combine:
PROC [a, b: ManhattanPolygon]
RETURNS [ManhattanPolygon] ¬ (
IF exclude
THEN ImagerManhattan.DestructiveDifference
ELSE ImagerManhattan.DestructiveIntersection
);
devicePath: ImagerScanConverter.DevicePath = ImagerScanConverter.Create[];
ImagerScanConverter.SetPath[devicePath: devicePath, path: path,
transformation: state.viewToDevice, clipBox: rasterData.viewClipper.clipBox];
this ¬ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: devicePath,
clipBox: rasterData.viewClipper.clipBox, oddWrap: oddWrap];
rasterData.viewClipper.clipMask ¬ Combine[rasterData.viewClipper.clipMask, this];
ImagerManhattan.Destroy[this];
ImagerScanConverter.Destroy[devicePath];
rasterData.viewClipper.clipBox ¬ ImagerManhattan.BoundingBox[rasterData.viewClipper.clipMask];
rasterData.valid.compositeClipper ¬ FALSE;
};
worryNat: NAT = LAST[NAT15] / 2 - 1;
worryReal: REAL ¬ worryNat;
bigTranslate: REAL ¬ REAL[LAST[INT]/2];
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, ir, -18] THEN RETURN[TRUE];
};
RETURN[FALSE];
};
RETURN[Is[m.a]
AND Is[m.b]
AND
ABS[m.c] <= bigTranslate
AND Is[m.d]
AND Is[m.e]
AND ABS[m.f] <= bigTranslate];
};
RasterMoveViewRectangle:
PROC [context: Context, width, height, fromX, fromY, toX, toY:
INTEGER] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
viewToDevice: Transformation ~ state.viewToDevice;
IF state.np.noImage # 0 THEN RETURN;
IF ImagerTransformation.EasyTransformation[viewToDevice]
AND rasterData.device.parm.classHas[MoveBox]
THEN {
clipBox: SF.Box ~ SF.Intersect[rasterData.viewClipper.clipBox, rasterData.device.state.bounds];
srcBox: SF.Box ~ ImagerTransformation.EasyTransformBox[m: viewToDevice, x: fromX, y: fromY, w: width, h: height, clip: clipBox];
dstBox: SF.Box ~ ImagerTransformation.EasyTransformBox[m: viewToDevice, x: toX, y: toY, w: width, h: height, clip: clipBox];
size: SF.Vec ~ SF.Size[srcBox];
IF
SF.Size[dstBox] = size
THEN {
rasterData.device.class.MoveBox[device: rasterData.device, dstMin: dstBox.min, srcMin: srcBox.min, size: size];
RETURN;
};
};
IF viewToDevice.form # 0
AND IsAllInteger[viewToDevice]
AND rasterData.device.parm.classHas[MoveBox]
THEN {
This case is here for the sake of the antialiasing contexts, which have multiple device pixels per view pixel.
srcRect: Rectangle ~ ImagerTransformation.TransformRectangle[m: viewToDevice, r: [x: fromX, y: fromY, w: width, h: height]];
dstRect: Rectangle ~ ImagerTransformation.TransformRectangle[m: viewToDevice, r: [x: toX, y: toY, w: width, h: height]];
clipBox: SF.Box ~ SF.Intersect[rasterData.viewClipper.clipBox, rasterData.device.state.bounds];
srcBox: SF.Box ~ ImagerTransformation.EasyTransformBox[m: identity, x: Real.Round[srcRect.x], y: Real.Round[srcRect.y], w: Real.Round[srcRect.w], h: Real.Round[srcRect.h], clip: clipBox];
dstBox: SF.Box ~ ImagerTransformation.EasyTransformBox[m: identity, x: Real.Round[dstRect.x], y: Real.Round[dstRect.y], w: Real.Round[dstRect.w], h: Real.Round[dstRect.h], clip: clipBox];
size: SF.Vec ~ SF.Size[srcBox];
IF
SF.Size[dstBox] = size
THEN {
rasterData.device.class.MoveBox[device: rasterData.device, dstMin: dstBox.min, srcMin: srcBox.min, size: size];
RETURN;
};
};
ERROR Imager.Error[[$unimplemented, "MoveViewRectangle not implemented for this case"]];
};
RasterGetBufferColorOperator:
PROC [context: Context]
RETURNS [ColorOperator ¬
NIL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
IF device.class.GetBufferColorOperator#
NIL
THEN {
RETURN [device.class.GetBufferColorOperator[device]]
};
};
RasterAccessBuffer:
PROC [context: Context, action:
PROC [pixelMap: PixelMap], path: PathProc, oddWrap:
BOOL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
outer:
PROC [bounds:
SF.Box, boxGenerator:
SF.BoxGenerator] ~ {
inner:
PROC [pixelMap: PixelMap] ~ {
pixelMapBox: SF.Box ~ pixelMap.box;
boxAction:
PROC [box:
SF.Box] ~ {
IF box = pixelMapBox
THEN action[pixelMap]
ELSE {
a: ARRAY [0..5) OF SampleMap;
pixelMap.box ¬ box;
FOR i:
NAT
IN [0..pixelMap.samplesPerPixel)
DO
a[i] ¬ pixelMap[i];
pixelMap[i] ¬ ImagerSample.Clip[a[i], box];
ENDLOOP;
action[pixelMap];
FOR i:
NAT
IN [0..pixelMap.samplesPerPixel)
DO
TRUSTED {ImagerSample.ReleaseDescriptor[pixelMap[i]]};
pixelMap[i] ¬ a[i];
ENDLOOP;
pixelMap.box ¬ pixelMapBox;
};
};
boxGenerator[boxAction];
};
rasterData.device.class.AccessBuffer[rasterData.device, bounds, inner];
};
IF state.np.noImage # 0 OR NOT rasterData.device.parm.classHas[AccessBuffer] THEN RETURN;
Validate[state, maskNeeds];
ImagerDeviceWorks.BoxesFromPath[
action: outer,
path: path,
oddWrap: oddWrap,
pathToDevice: state.clientToDevice,
clipper: rasterData.device.worksState.clipper
];
};
Raster Context Class Template
rasterClassTemplate: Class ~
NEW [ClassRep ¬ [
type: $RasterTemplate,
Save: ImagerState.StateSave,
Restore: ImagerState.StateRestore,
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: ImagerState.StateGetClipper,
ConcatT: ImagerState.StateConcatT,
Scale2T: ImagerState.StateScale2T,
RotateT: ImagerState.StateRotateT,
TranslateT: ImagerState.StateTranslateT,
Move: ImagerState.StateMove,
SetXY: ImagerState.StateSetXY,
SetXYRel: ImagerState.StateSetXYRel,
GetCP: ImagerState.StateGetCP,
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,
Clip: ImagerState.StateClip,
ClipRectangle: ImagerState.StateClipRectangle,
ClipRectangleI: ImagerState.StateClipRectangleI,
Show: RasterShow,
MaskFill: RasterMaskFill,
MaskRectangle: RasterMaskRectangle,
MaskStroke: RasterMaskStroke,
MaskPixel: RasterMaskPixel,
ShowAndFixedXRel: ImagerPrivate.DefaultShowAndFixedXRel,
ShowBackward: RasterShowBackward,
ShowText: ImagerPrivate.DefaultShowText,
MaskRectangleI: RasterMaskRectangleI,
MaskVector: RasterMaskVector,
MaskDashedStroke: RasterMaskDashedStroke,
MaskBitmap: RasterMaskBitmap,
DrawBitmap: RasterDrawBitmap,
DrawPixels: ImagerPrivate.DefaultDrawPixels,
DoIfVisible: ImagerPrivate.DefaultDoIfVisible,
DoWithBuffer: RasterDoWithBuffer,
DrawObject: ImagerPrivate.DefaultDrawObject,
GetBounds: RasterGetBounds,
ViewReset: RasterViewReset,
ViewTranslateI: RasterViewTranslateI,
ViewClip: RasterViewClip,
ViewClipRectangleI: RasterViewClipRectangleI,
Transform: ImagerPrivate.DefaultTransform,
GetTransformation: RasterGetTransformation,
MoveViewRectangle: RasterMoveViewRectangle,
TestViewRectangle: RasterTestViewRectangle,
GetBufferColorOperator: RasterGetBufferColorOperator,
AccessBuffer: RasterAccessBuffer,
SaveBuffer: ImagerPrivate.DefaultSaveBuffer,
RestoreBuffer: ImagerPrivate.DefaultRestoreBuffer,
DiscardBuffer: ImagerPrivate.DefaultDiscardBuffer,
propList: NIL
]];
Intercepting Raster Accesses
InterceptData: TYPE ~ REF InterceptDataRep;
InterceptDataRep:
TYPE ~
RECORD [
buffered: BOOL,
interceptor: Interceptor,
device: Device
];
ModifySetColor:
PROC [device: Device, color: Color, viewToDevice: Transformation] ~ {
iData: InterceptData ~ NARROW[device.data];
iData.device.class.SetColor[iData.device, color, viewToDevice];
};
ModifySetPriority:
PROC [device: Device, priorityImportant:
BOOL] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.device.class.SetPriority = NIL THEN RETURN;
iData.device.class.SetPriority[iData.device, priorityImportant];
};
ModifySetHalftoneProperties:
PROC [device: Device, halftoneProperties: ImagerBrick.HalftoneProperties] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.device.class.SetHalftoneProperties = NIL THEN RETURN;
iData.device.class.SetHalftoneProperties[iData.device, halftoneProperties];
};
ModifyMaskBoxes:
PROC [device: Device, bounds:
SF.Box, boxes:
SF.BoxGenerator] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.MaskBoxes[iData.device, bounds, boxes] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ {iData.device.class.MaskBoxes[iData.device, bounds, boxes]};
interceptor.intercept[interceptor, bounds, action, FALSE];
};
};
ModifyMaskRegion:
PROC [device: Device, bounds:
SF.Box, edgeGenerator:
PROC [ImagerSample.EdgeAction]] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.MaskRegion[iData.device, bounds, edgeGenerator] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ {iData.device.class.MaskRegion[iData.device, bounds, edgeGenerator]};
interceptor.intercept[interceptor, bounds, action, FALSE];
};
};
ModifyMaskBitmap:
PROC [device: Device, bitmap: SampleMap, delta:
SF.Vec, bounds:
SF.Box, boxes:
SF.BoxGenerator] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.MaskBitmap[iData.device, bitmap, delta, bounds, boxes] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ {iData.device.class.MaskBitmap[iData.device, bitmap, delta, bounds, boxes]};
interceptor.intercept[interceptor, bounds, action, FALSE];
};
};
ModifyMaskPixelArray:
PROC [device: Device, bitmap: PixelArray, clientToDevice: Transformation, bounds:
SF.Box, boxes:
SF.BoxGenerator] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.MaskPixelArray[iData.device, bitmap, clientToDevice, bounds, boxes] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ {iData.device.class.MaskPixelArray[iData.device, bitmap, clientToDevice, bounds, boxes]};
interceptor.intercept[interceptor, bounds, action, FALSE];
};
};
ModifyMaskRawBitmaps:
PROC [device: Device, list:
LIST
OF ImagerSample.RawDescriptor] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.MaskRawBitmaps[iData.device, list] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ { iData.device.class.MaskRawBitmaps[iData.device, list] };
bounds: SF.Box ¬ [min: SF.maxVec, max: SF.minVec];
FOR tail:
LIST
OF ImagerSample.RawDescriptor ¬ list, tail.rest
UNTIL tail =
NIL
DO
bounds.min ¬ SF.Min[bounds.min, tail.first.box.min];
bounds.max ¬ SF.Max[bounds.max, tail.first.box.max];
ENDLOOP;
IF SF.Nonempty[bounds] THEN interceptor.intercept[interceptor, bounds, action, FALSE];
};
};
ModifyDrawBitmap:
PROC [device: Device, bitmap: SampleMap, delta:
SF.Vec, bounds:
SF.Box, boxes:
SF.BoxGenerator] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.DrawBitmap[iData.device, bitmap, delta, bounds, boxes] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ {iData.device.class.DrawBitmap[iData.device, bitmap, delta, bounds, boxes]};
interceptor.intercept[interceptor, bounds, action, FALSE];
};
};
ModifyMaskChar:
PROC [device: Device, delta:
SF.Vec, mask: CharMask] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.MaskChar[iData.device, delta, mask] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ {iData.device.class.MaskChar[iData.device, delta, mask]};
interceptor.intercept[interceptor, SF.Displace[mask.box, delta], action, FALSE];
};
};
ModifyMoveBox:
PROC [device: Device, dstMin, srcMin, size:
SF.Vec] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.MoveBox[iData.device, dstMin, srcMin, size] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
action: PROC ~ {iData.device.class.MoveBox[iData.device, dstMin, srcMin, size]};
min: SF.Vec ~ SF.Min[dstMin, srcMin];
max: SF.Vec ~ SF.Add[dstMin, srcMin].Sub[min].Add[size];
interceptor.intercept[interceptor, [min: min, max: max], action, FALSE];
};
};
ModifySwitchBuffer:
PROC [device: Device, bounds:
SF.Box, copy:
BOOL, alt:
BOOL] ~ {
iData: InterceptData ~ NARROW[device.data];
interceptor: Interceptor ~ iData.interceptor;
proc: PROC ~ { iData.device.class.SwitchBuffer[iData.device, bounds, copy, alt] };
IF copy THEN interceptor.intercept[interceptor, bounds, proc, alt] ELSE proc[];
iData.buffered ¬ alt;
};
ModifyGetBufferColorOperator:
PROC [device: Device]
RETURNS [ColorOperator] ~ {
iData: InterceptData ~ NARROW[device.data];
dev: Device ~ iData.device;
IF dev.class.GetBufferColorOperator = NIL THEN RETURN [NIL];
RETURN [dev.class.GetBufferColorOperator[dev]]
};
ModifyAccessBuffer:
PROC [device: Device, box:
SF.Box, action:
PROC [pixelMap: PixelMap]] ~ {
iData: InterceptData ~ NARROW[device.data];
IF iData.buffered
THEN { iData.device.class.AccessBuffer[iData.device, box, action] }
ELSE {
interceptor: Interceptor ~ iData.interceptor;
proc: PROC ~ {iData.device.class.AccessBuffer[iData.device, box, action]};
interceptor.intercept[interceptor, box, proc, FALSE];
};
};
InterceptorInEffect:
PUBLIC
PROC [context: Context]
RETURNS [
BOOL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
RETURN [rasterData.device.class = modifiedDeviceClass]
};
SetInterceptor:
PUBLIC
PROC [context: Context, interceptor: Interceptor]
RETURNS [old: Interceptor ¬
NIL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
IF rasterData.device.class = modifiedDeviceClass
THEN {
Take out old interceptor.
iData: InterceptData ~ NARROW[rasterData.device.data];
old ¬ iData.interceptor;
rasterData.device ¬ iData.device;
};
IF interceptor #
NIL
THEN {
iData: InterceptData ~ NEW[InterceptDataRep ¬ [buffered: FALSE, interceptor: interceptor, device: rasterData.device]];
rasterData.device ¬ ImagerDevice.MakeDevice[
class: modifiedDeviceClass,
parm: rasterData.device.parm,
state: rasterData.device.state,
data: iData
];
};
};
modifiedDeviceClass: DeviceClass ~
NEW [ImagerDevice.DeviceClassRep ¬ [
SetColor: ModifySetColor,
SetPriority: ModifySetPriority,
SetHalftoneProperties: ModifySetHalftoneProperties,
MaskBoxes: ModifyMaskBoxes,
MaskRegion: ModifyMaskRegion,
MaskBitmap: ModifyMaskBitmap,
MaskPixelArray: ModifyMaskPixelArray,
MaskRawBitmaps: ModifyMaskRawBitmaps,
DrawBitmap: ModifyDrawBitmap,
MaskChar: ModifyMaskChar,
MoveBox: ModifyMoveBox,
SwitchBuffer: ModifySwitchBuffer,
GetBufferColorOperator: ModifyGetBufferColorOperator,
AccessBuffer: ModifyAccessBuffer
]];