ImagerRasterImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Michael Plass, November 1, 1993 12:22 pm PST
Doug Wyatt, September 22, 1989 3:42:59 pm PDT
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];
ImagerRasterImpl: CEDAR PROGRAM
IMPORTS Basics, Char, Imager, ImagerDevice, ImagerDeviceInterchange, ImagerDeviceWorks, ImagerFont, ImagerFontWorks, ImagerManhattan, ImagerPenExtras, ImagerPixelArray, ImagerPrivate, ImagerSample, ImagerScanConverter, ImagerState, ImagerTransformation, ImagerTypeface, Real, RealFns, ImagerScaled, ImagerSwitches, SF, Vector2
EXPORTS ImagerRaster,
Imager, ImagerFont, ImagerState -- for opaque type access
~ BEGIN
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
ROPE: TYPE ~ Rope.ROPE;
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] };
};
DeviceInterchange
InterchangeState: TYPE ~ ImagerDeviceInterchange.InterchangeState;
GetInterchangeState: PUBLIC PROC [context: Imager.Context] RETURNS [iState: InterchangeState ¬ NIL] ~ {
state: State ~ context.state;
IF state # NIL THEN {
rasterData: RasterData ~ state.rasterData;
IF rasterData # NIL THEN {
Validate[state, [compositeClipper: TRUE, deviceColor: TRUE]];
iState ¬ ImagerDeviceInterchange.ObtainInterchangeState[];
iState.device ¬ rasterData.device;
iState.clientToView ¬ RasterGetTransformation[context: context, from: client, to: view];
iState.viewToSurface ¬ RasterGetTransformation[context: context, from: view, to: surface];
iState.surfaceToDevice ¬ RasterGetTransformation[context: context, from: surface, to: device];
iState.color ¬ state.color;
};
};
};
ContextFromInterchangeState: PUBLIC PROC [iState: InterchangeState, scratch: Imager.Context ¬ NIL] RETURNS [Imager.Context] ~ {
context: Context ~ IF scratch = NIL THEN NEW[Imager.ContextRep] ELSE scratch;
state: State ~ IF context.state = NIL THEN NEW[StateRep] ELSE context.state;
rasterData: RasterData ~ IF state.rasterData = NIL THEN NEW[RasterDataRep] ELSE state.rasterData;
charToDevice: Transformation ~ IF rasterData.charToDevice = NIL THEN ImagerTransformation.Scale[1] ELSE rasterData.charToDevice;
cp: ImagerDeviceVector.DVec ~ IF state.cp = NIL THEN NEW[ImagerDeviceVector.DVecRep] ELSE state.cp;
T: TYPE ~ Transformation; -- for brevity
Cat: PROC [a, b, recycle: T] RETURNS [T] ~ INLINE {
IF recycle # NIL THEN ImagerTransformation.Destroy[recycle];
RETURN [ImagerTransformation.Concat[a, b]];
};
surfaceToDevice: T ~ Cat[a: iState.surfaceToDevice, b: NIL, recycle: rasterData.surfaceToDevice];
viewToDevice: T ~ Cat[a: iState.viewToSurface, b: surfaceToDevice, recycle: state.viewToDevice];
clientToDevice: T ~ Cat[a: iState.clientToView, b: viewToDevice, recycle: state.clientToDevice];
viewClipper: DeviceClipper ¬ rasterData.viewClipper;
IF viewClipper = NIL THEN viewClipper ¬ NEW[ImagerDevice.DeviceClipperRep] ELSE ImagerManhattan.Destroy[viewClipper.clipMask];
viewClipper.clipMask ¬ ImagerManhattan.Copy[iState.device.worksState.clipper.clipMask];
viewClipper.clipBox ¬ iState.device.worksState.clipper.clipBox;
rasterData­ ¬ []; { -- work around Mimosa record-assignment bug:
rasterData.device ¬ iState.device;
rasterData.valid ¬ [];
rasterData.surfaceToDevice ¬ surfaceToDevice;
rasterData.viewClipper ¬ viewClipper;
rasterData.charToDevice ¬ charToDevice;
};
iState.device ¬ NIL;
state­ ¬ []; {
state.color ¬ iState.color;
state.clientToDevice ¬ clientToDevice;
state.viewToDevice ¬ viewToDevice;
state.cp ¬ cp;
state.rasterData ¬ rasterData;
};
iState.color ¬ NIL;
context­ ¬ [
class: rasterClassTemplate,
state: state,
data: NIL
];
ImagerDeviceInterchange.DestroyInterchangeState[iState];
ImagerState.StateSetXY[context, [0.0, 0.0]];
RETURN[context];
};
MaskFill
RasterMaskFill: PROC [context: Context, path: PathProc, oddWrap: BOOL] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
Validate[state, maskNeeds];
ImagerDeviceWorks.MaskFill[device: device, path: path, oddWrap: oddWrap, pathToDevice: state.clientToDevice];
};
};
MaskStroke
GetEnd: PROC [state: State] RETURNS [ImagerStroke.EndCode] ~ INLINE {
This folds the warn bit into the stroke-end encoding in the case of square ends
end: INT ¬ state.np.strokeEnd;
SELECT end FROM
ImagerStroke.buttEnd, ImagerStroke.roundEnd => {};
ENDCASE => end ¬ IF (state.np.warn#0) THEN ImagerStroke.warningSquareEnd ELSE ImagerStroke.squareEnd;
RETURN [end]
};
PenFromState: PROC [state: State] RETURNS [ImagerPen.Pen] ~ {
rasterData: RasterData ~ state.rasterData;
pen: ImagerPen.Pen ~ ImagerPenExtras.MakeThickenedTransformedCircle[
strokeWidth: state.np.strokeWidth,
m: state.clientToDevice,
thickening: [rasterData.device.parm.parameters.sThicken, rasterData.device.parm.parameters.fThicken],
minThickness: [rasterData.device.parm.parameters.sMinThickness, rasterData.device.parm.parameters.fMinThickness]];
RETURN [pen]
};
RasterMaskVector: PROC [context: Context, p1, p2: VEC] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
Validate[state, maskNeeds];
ImagerDeviceWorks.MaskVector[
device: device,
p1: p1,
p2: p2,
pointsToDevice: state.clientToDevice,
end: GetEnd[state],
pen: PenFromState[state]
];
};
};
RasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
Validate[state, maskNeeds];
ImagerDeviceWorks.MaskStroke[
device: device,
path: path,
closed: closed,
pathToDevice: state.clientToDevice,
end: GetEnd[state],
joint: state.np.strokeJoint,
miterLimit: state.np.miterLimit,
pen: PenFromState[state]
];
};
};
RasterMaskDashedStroke: PUBLIC PROC [context: Context, path: PathProc, patternLen: NAT, pattern: PROC [NAT] RETURNS [REAL], offset, length: REAL] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
Validate[state, maskNeeds];
ImagerDeviceWorks.MaskDashedStroke[
device: device,
path: path,
patternLen: patternLen,
pattern: pattern,
offset: offset,
length: length,
closed: FALSE,
pathToDevice: state.clientToDevice,
end: GetEnd[state],
joint: state.np.strokeJoint,
miterLimit: state.np.miterLimit,
pen: PenFromState[state]
];
};
};
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];
};
};
Clipping Region Support
GetContainingBox: PUBLIC PROC [context: Context, p: SF.Vec] RETURNS [SF.Box] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[state, [compositeClipper: TRUE]];
FOR each: LIST OF SF.Box ¬ rasterData.device.worksState.clipper.clipMask, each.rest UNTIL each = NIL DO
box: SF.Box ~ each.first;
IF SF.In[p, box] THEN RETURN [box];
IF box.min.s > p.s THEN EXIT;
ENDLOOP;
RETURN [[min: SF.maxVec, max: SF.minVec]];
};
GetClipper: PUBLIC PROC [context: Context] RETURNS [DeviceClipper] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[state, [compositeClipper: TRUE]];
RETURN [rasterData.device.worksState.clipper]
};
GetDeviceClipBox: PUBLIC PROC [context: Context] RETURNS [SF.Box] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
RETURN [rasterData.device.state.bounds]
};
SetDeviceClipBox: PUBLIC PROC [context: Context, clipBox: SF.Box] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
rasterData.device.state.bounds ¬ clipBox;
rasterData.valid.compositeClipper ¬ FALSE;
};
RasterGetBounds: PROC [context: Context] RETURNS [Rectangle] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[state, [compositeClipper: TRUE]];
{ box: SF.Box ~ rasterData.device.worksState.clipper.clipBox;
size: SF.Vec ~ SF.Size[box];
m: Transformation ~ state.clientToDevice;
r: Rectangle ~ m.InverseTransformRectangle[[x: box.min.s, y: box.min.f, w: size.s, h: size.f]];
RETURN[r];
};
};
RasterTestViewRectangle: PUBLIC PROC [context: Context, x, y, w, h: INTEGER] RETURNS [Visibility] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
viewToDevice: Transformation ~ state.viewToDevice;
viewClip: ManhattanPolygon ~ rasterData.viewClipper.clipMask;
IF viewClip = NIL THEN RETURN [none];
IF ImagerTransformation.EasyTransformation[viewToDevice] THEN {
box: SF.Box ~ ImagerTransformation.EasyTransformBox[m: viewToDevice, x: x, y: y, w: w, h: h, clip: SF.maxBox];
IF SF.Inside[box, viewClip.first] AND SF.Inside[box, rasterData.device.state.bounds] THEN RETURN [all];
IF SF.Disjoint[box, rasterData.viewClipper.clipBox] THEN RETURN [none];
};
Could distingush cases more carefully here, but we've got the most common ones done.
RETURN [part];
};
Double Buffering
fuzz: REAL ¬ 1.0/65535.0;
IntegerTranslation: PROC [m: Transformation] RETURNS [BOOL] ~ {
RETURN [ABS[m.c-m.tx] < fuzz AND ABS[m.f-m.ty] < fuzz]
};
RasterDoWithBuffer: PROC [context: Context, action: PROC, x, y, w, h: INTEGER, backgroundColor: Color] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
IF rasterData.device.parm.classHas[SwitchBuffer] THEN {
IF ImagerTransformation.EasyTransformation[state.clientToDevice] AND IntegerTranslation[state.clientToDevice] THEN {
oldDeviceClipBox: SF.Box ~ rasterData.device.state.bounds;
box: SF.Box ~ ImagerTransformation.EasyTransformBox[m: state.clientToDevice, x: x, y: y, w: w, h: h, clip: SF.Intersect[rasterData.viewClipper.clipBox, oldDeviceClipBox]];
fillAction: PROC ~ {
savedColor: Color ~ state.color;
Imager.SetColor[context, backgroundColor];
Imager.MaskRectangleI[context, x, y, w, h];
Imager.SetColor[context, savedColor];
action[];
};
IF SF.Empty[box] THEN RETURN;
rasterData.device.state.bounds ¬ box;
rasterData.valid.compositeClipper ¬ FALSE;
Validate[state, [compositeClipper: TRUE]];
IF rasterData.device.worksState.clipper.clipMask # NIL THEN {
rasterData.device.class.SwitchBuffer[device: rasterData.device, bounds: box, copy: backgroundColor=NIL OR state.np.noImage # 0 OR NOT SF.Inside[box, rasterData.device.worksState.clipper.clipMask.first], alt: TRUE];
Imager.DoSaveAll[context, IF backgroundColor=NIL THEN action ELSE fillAction !
UNWIND => {
SetDeviceClipBox[context, oldDeviceClipBox];
rasterData.device.class.SwitchBuffer[device: rasterData.device, bounds: box, copy: TRUE, alt: FALSE]}];
rasterData.device.class.SwitchBuffer[device: rasterData.device, bounds: box, copy: TRUE, alt: FALSE];
};
rasterData.device.state.bounds ¬ oldDeviceClipBox;
rasterData.valid.compositeClipper ¬ FALSE;
RETURN;
};
};
ImagerPrivate.DefaultDoWithBuffer[context: context, action: action, x: x, y: y, w: w, h: h, backgroundColor: backgroundColor];
};
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
]];
Class and Context Creation
CreateClass: PUBLIC PROC [type: ATOM] RETURNS [Class] ~ {
class: Class ~ NEW [ClassRep ¬ rasterClassTemplate­];
class.type ¬ type;
RETURN[class];
};
Create: PUBLIC PROC [class: Class, deviceClass: ImagerDevice.DeviceClass, deviceParm: ImagerDevice.DeviceParm, data: REF, pixelUnits: BOOL] RETURNS [Context] ~ {
device: ImagerDevice.Device ~ ImagerDevice.MakeDevice[class: deviceClass, parm: deviceParm, data: data];
rasterData: RasterData ~ NEW [RasterDataRep ¬ [
device: device,
valid: [],
surfaceToDevice: ImagerTransformation.XYToSF[device.parm.scanMode, device.parm.sSize, device.parm.fSize],
viewClipper: NEW[ImagerDevice.DeviceClipperRep],
charToDevice: ImagerTransformation.Scale[1]
]];
state: State ~ NEW[StateRep ¬ [
color: Imager.black,
clientToDevice: IF pixelUnits THEN ImagerTransformation.Scale[1] ELSE ImagerTransformation.Scale2[Vector2.Div[deviceParm.surfaceUnitsPerInch, Imager.metersPerInch]],
viewToDevice: ImagerTransformation.Scale[1], -- RasterViewReset will fix
cp: NEW[ImagerDeviceVector.DVecRep],
rasterData: rasterData
]];
context: Context ~ NEW [Imager.ContextRep ¬ [class: class, state: state, data: NIL]];
RasterViewReset[context];
RETURN[context];
};
MaskRectangle
RasterMaskRectangle: PROC [context: Context, r: Rectangle] ~ {
state: State ~ context.state;
Validate[state, maskNeeds];
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
ImagerDeviceWorks.MaskRectangle[
device: device,
rectangle: r,
rectangleToDevice: state.clientToDevice
];
};
};
RasterMaskRectangleI: PROC [context: Context, x, y, w, h: INTEGER] ~ {
state: State ~ context.state;
Validate[state, maskNeeds];
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
clientToDevice: Transformation ~ state.clientToDevice;
IF ImagerTransformation.EasyTransformation[clientToDevice]
THEN {
box: SF.Box ~ ImagerTransformation.EasyTransformBox[clientToDevice, x, y, w, h, rasterData.device.worksState.clipper.clipBox];
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.MaskBoxes[device: rasterData.device, bounds: box, boxes: boxes];
}
ELSE {
device: Device ~ rasterData.device;
device.works.MaskRectangle[
device: device,
rectangle: [x: x, y: y, w: w, h: h],
rectangleToDevice: clientToDevice
];
};
};
};
Show
BasicShowChar: PUBLIC PROC [context: Context, char: XChar] ~ {
state: State ~ context.state;
amplifySpace: REAL ~ state.np.amplifySpace;
proc: PROC ~ {
font: Font ~ state.font;
escapement: VEC ¬ ImagerFont.Escapement[font, char];
IF ImagerFont.Amplified[font, char] THEN {
escapement ¬ Vector2.Mul[escapement, amplifySpace];
};
Imager.Trans[context];
IF state.np.noImage = 0 THEN {
ImagerFontWorks.MaskChar[font, char, context]
};
Imager.SetXYRel[context, escapement];
SELECT ImagerFont.Correction[font, char] FROM
none => NULL;
space => Imager.CorrectSpace[context, escapement];
mask => Imager.CorrectMask[context];
ENDCASE => ERROR;
};
Imager.DoSave[context, proc];
};
FlushState: PUBLIC PROC [state: State] ~ {
rasterData: RasterData ~ state.rasterData;
IF rasterData.showValid THEN {
SELECT state.np.correctPass FROM
1 => state.p.correctSum ¬ VECFromSV[rasterData.correctSum];
ENDCASE => NULL;
rasterData.showValid ¬ FALSE;
};
};
VECFromSV: PROC [sv: ScaledVec] RETURNS [VEC] ~ INLINE {
RETURN [[x: ImagerScaled.Float[sv.s], y: ImagerScaled.Float[sv.f]]]
};
SVFromVEC: PROC [v: VEC] RETURNS [ScaledVec] ~ INLINE {
RETURN [[s: ImagerScaled.FromReal[v.x], f: ImagerScaled.FromReal[v.y]]]
};
GetFontAtom: PUBLIC PROC [context: Imager.Context] RETURNS [ImagerFont.Font] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[state, [fontInfo: TRUE]];
RETURN [rasterData.fontAtom]
};
TryFastState: PROC [state: State] ~ {
rasterData: RasterData ~ state.rasterData;
cp: ImagerDeviceVector.DVec ~ state.cp;
rasterData.easyMetrics ¬ IF state.np.amplifySpace = 1.0 THEN all ELSE ordinary;
rasterData.lastAmpMask ¬ NIL;
IF NOT cp.scaled THEN {
IF ABS[cp.fv.x] < worryReal AND ABS[cp.fv.y] < worryReal THEN {
cp.scaled ¬ TRUE;
cp.sv.s ¬ ImagerScaled.FromReal[cp.fv.x];
cp.sv.f ¬ ImagerScaled.FromReal[cp.fv.y];
};
};
rasterData.showValid ¬ state.cp.scaled;
IF rasterData.showValid AND state.np.correctPass = 1 THEN {
s: VEC ~ state.p.correctSum;
rasterData.easyMetrics ¬ none;
rasterData.showValid ¬ (ABS[s.x] < worryReal AND ABS[s.y] < worryReal);
IF rasterData.showValid THEN rasterData.correctSum ¬ SVFromVEC[s];
};
IF rasterData.showValid AND state.np.correctPass = 2 THEN {
s: VEC ~ state.p.correctMask;
rasterData.showValid ¬ (ABS[s.x] < worryReal AND ABS[s.y] < worryReal);
IF rasterData.showValid THEN {
rasterData.correctMask ¬ SVFromVEC[s];
rasterData.lastUnCorrectedSpace ¬ zeroScaledVec;
rasterData.lastCorrectedSpace ¬ zeroScaledVec;
rasterData.easyMetrics ¬ IF rasterData.correctMask = zeroScaledVec THEN ordinary ELSE none;
};
};
};
RasterShow: PUBLIC PROC [context: Context, string: XStringProc, xrel: BOOL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
device: Device ~ rasterData.device;
XRelString: XStringProc = {
[charAction: ImagerFont.XCharProc]
oddCharIndex: BOOL ¬ FALSE;
XRelAction: PROC [char: XChar] ~ {
IF oddCharIndex
THEN {
If anybody really wants to use xrel, this could be made a little faster.
FlushState[state];
Imager.SetXRel[context, INT[Char.Code[char]]-128];
TryFastState[state];
}
ELSE charAction[char];
oddCharIndex ¬ NOT oddCharIndex;
};
string[XRelAction];
};
HardChar: ImagerFont.XCharProc = {
[char: ImagerFont.XChar]
FlushState[state];
BasicShowChar[context, char];
TryFastState[state];
};
HardMetrics: PROC [m: CharMask] = {
esc: ScaledVec ¬ m.escapement;
IF NOT rasterData.showValid THEN GOTO Overflow;
IF m.flags.amplified THEN {
IF m # rasterData.lastAmpMask THEN {
amp: REAL ~ state.np.amplifySpace;
IF amp = 1.0
THEN { rasterData.lastAmp ¬ esc }
ELSE {
IF esc.s # ImagerScaled.zero THEN {
r: REAL ~ ImagerScaled.Float[esc.s]*amp;
IF ABS[r] < worryReal THEN esc.s ¬ ImagerScaled.FromReal[r] ELSE GOTO Overflow;
};
IF esc.f # ImagerScaled.zero THEN {
r: REAL ~ ImagerScaled.Float[esc.f]*amp;
IF ABS[r] < worryReal THEN esc.f ¬ ImagerScaled.FromReal[r] ELSE GOTO Overflow;
};
rasterData.lastAmp ¬ esc;
};
rasterData.lastAmpMask ¬ m;
};
esc ¬ rasterData.lastAmp;
};
SELECT state.np.correctPass FROM
1 => {
SELECT m.flags.correction FROM
mask => state.p.correctMaskCount ¬ state.p.correctMaskCount+1;
space => {
rasterData.correctSum.s ¬ ImagerScaled.PLUS[rasterData.correctSum.s, esc.s];
rasterData.correctSum.f ¬ ImagerScaled.PLUS[rasterData.correctSum.f, esc.f];
};
ENDCASE => NULL;
};
2 => {
SELECT m.flags.correction FROM
mask => {
IF state.p.correctMaskCount#0 THEN {
esc.s ¬ ImagerScaled.PLUS[esc.s, rasterData.correctMask.s];
esc.f ¬ ImagerScaled.PLUS[esc.f, rasterData.correctMask.f];
state.p.correctMaskCount ¬ state.p.correctMaskCount-1;
};
};
space => {
IF esc # rasterData.lastUnCorrectedSpace THEN {
cs: VEC ~ state.p.correctSpace;
e1: VEC ~ VECFromSV[esc];
e3: VEC ~ [x: cs.x*e1.x + e1.x, y: cs.y*e1.y + e1.y];
rasterData.lastCorrectedSpace ¬ SVFromVEC[e3];
rasterData.lastUnCorrectedSpace ¬ esc;
};
esc ¬ rasterData.lastCorrectedSpace
};
ENDCASE => NULL;
};
ENDCASE => NULL;
state.cp.sv.s ¬ ImagerScaled.PLUS[state.cp.sv.s, esc.s];
state.cp.sv.f ¬ ImagerScaled.PLUS[state.cp.sv.f, esc.f];
IF ABS[ImagerScaled.Floor[state.cp.sv.s]] >= worryNat OR ABS[ImagerScaled.Floor[state.cp.sv.f]] >= worryNat THEN FlushState[state];
EXITS Overflow => {
save: INT ~ state.np.noImage;
state.np.noImage ¬ 1;
FlushState[state];
BasicShowChar[context, m.char];
TryFastState[state];
state.np.noImage ¬ save;
};
};
Validate[state, showNeeds];
TryFastState[state];
ImagerDeviceWorks.Show[
device: device,
fontAtom: rasterData.fontAtom,
string: IF xrel THEN XRelString ELSE string,
cp: state.cp,
hardChar: HardChar,
hardMetrics: HardMetrics,
easyMetrics: rasterData.easyMetrics,
noImage: state.np.noImage#0
];
FlushState[state];
};
RasterShowBackward: PUBLIC PROC [context: Context, string: XStringProc] ~ {
state: State ~ context.state;
font: Font ~ state.font;
typeface: Typeface ~ font.typeface;
new: Font ~ ImagerTypeface.MakeFont[ImagerTypeface.ReverseMetrics[typeface], font.charToClient];
Imager.SetFont[context, new];
Imager.Show[context, string ! UNWIND => {Imager.SetFont[context, font]}];
Imager.SetFont[context, font];
};
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
]];
END.