~
BEGIN
OPEN ImagerState, ImagerRast;
Class: TYPE ~ ImagerPrivate.Class;
ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep
State: TYPE ~ ImagerState.State;
StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep
CharMask: TYPE ~ ImagerDev.CharMask;
Clipper: TYPE ~ ImagerBackdoor.Clipper;
Color: TYPE ~ Imager.Color;
ColorOperator: TYPE ~ Imager.ColorOperator;
Context: TYPE ~ Imager.Context;
Device: TYPE ~ ImagerDev.Device;
DeviceBox: TYPE ~ ImagerDev.DeviceBox;
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;
DeviceBoxFromDeviceRectangle:
PROC [r: DeviceRectangle]
RETURNS [DeviceBox] ~
INLINE {
RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]];
};
DeviceRectangleFromDeviceBox:
PROC [b: DeviceBox]
RETURNS [DeviceRectangle] ~
INLINE {
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: FontCache, rastWeight:
REAL, fontTuner: 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.charToDevice ← ImagerTransformation.Scale[1];
data.fontCache ← fontCache;
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: rasterClass, 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;
};
ValidateCharToDevice:
PROC [data: Data, state: State] ~ {
IF NOT data.valid.clientToDevice THEN ValidateClientToDevice[data, state];
data.charToDevice^ ← data.clientToDevice^;
data.charToDevice.ApplyTranslateTo[[0, 0]];
data.charToDevice.ApplyPreConcat[state.font.charToClient];
data.valid.charToDevice ← TRUE;
};
FontImplRep:
PUBLIC
TYPE ~ ImagerFontPrivate.FontImplRep;
-- export to to ImagerFont
ValidateFontAtom:
PROC [data: Data, state: State] ~ {
fontImpl: REF FontImplRep ~ state.font.impl;
IF NOT data.valid.charToDevice THEN ValidateCharToDevice[data, state];
data.fontAtom ← ImagerFontPrivate.MakeFontAtom[fontImpl.typeface.nameAtom, data.charToDevice];
data.valid.fontAtom ← TRUE;
};
ValidateClientClipper:
PROC [data: Data, state: State] ~ {
ImagerManhattan.Destroy[data.clientClipMask];
data.clientClipMask ← ImagerManhattan.Copy[data.viewClipMask];
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] };
old, this, new: ManhattanPolygon;
old ← data.clientClipMask;
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];
IF each.first.exclude THEN new ← old.Difference[this] ELSE new ← old.Intersection[this];
data.clientClipMask ← new;
ImagerManhattan.Destroy[old];
ImagerManhattan.Destroy[this];
ENDLOOP;
data.clientClipBox ← ImagerManhattan.BoundingBox[data.clientClipMask];
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:
PROC [data: Data, state: State] ~ {
changed: ImagerState.ChangeFlags ~ state.changed;
state.changed ← ImagerState.notChanged;
IF changed.T THEN data.valid.clientToDevice ← data.valid.charToDevice ← data.valid.fontAtom ← FALSE;
IF changed.color THEN data.valid.deviceColor ← FALSE;
IF changed.priority THEN data.valid.devicePriority ← FALSE;
IF changed.font THEN data.valid.charToDevice ← data.valid.fontAtom ← FALSE;
IF changed.clipper THEN data.valid.clientClipper ← FALSE;
};
ValidateIfNeeded:
PROC [data: Data, state: State, needs: Flags] ~ {
fix: Flags ~ AndFlags[needs, NotFlags[data.valid]];
IF fix.clientToDevice THEN ValidateClientToDevice[data, state];
IF fix.charToDevice THEN ValidateCharToDevice[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.fontAtom THEN ValidateFontAtom[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;
};
maskDevicePathNeeds: Flags ~ [clientClipper:
TRUE, deviceColor:
TRUE, devicePriority:
TRUE];
MaskDevicePath:
PROC [data: Data, parity:
BOOL ←
FALSE] ~ {
device: Device ~ data.device;
runs:
PROC [run:
PROC [sMin, fMin:
INTEGER, fSize:
NAT]] ~ {
clientClipMask: ManhattanPolygon ~ data.clientClipMask;
IF clientClipMask#
NIL
AND clientClipMask.rest=
NIL
THEN {
ImagerScanConverter.ConvertToRuns[devicePath: data.devicePath,
runProc: run, clipBox: clientClipMask.first, parityFill: parity];
}
ELSE {
rem: ManhattanPolygon ← clientClipMask;
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: data.devicePath,
runProc: clip, clipBox: data.clientClipBox, parityFill: parity];
};
};
rect: DeviceRectangle ~ ImagerScanConverter.BoundingBox[data.devicePath].Intersect[data.clientClipBox];
bounds: DeviceBox ← DeviceBoxFromDeviceRectangle[data.clientClipBox];
Verify[data, maskDevicePathNeeds]; -- caller should have validated these
device.class.MaskRuns[device: device, bounds: bounds, runs: runs];
};
ClipBoxToMask:
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[[clientToDevice:
TRUE, clientClipper:
TRUE], maskDevicePathNeeds];
RasterMaskFill:
PROC [context: Context, path: PathProc, parity:
BOOL] ~ {
state: State ~ context.state;
USING [T, clipper, color, noImage, priorityImportant]
IF state.np.noImage=0
THEN {
data: Data ~ NARROW[context.data];
Validate[data, state, maskNeeds];
data.devicePath ← ImagerScanConverter.CreatePath[path: path,
transformation: data.clientToDevice, clipBox: data.clientClipBox,
scratch: data.devicePath];
MaskDevicePath[data, parity];
};
};
specialRectangles:
BOOL ←
TRUE;
RasterMaskRectangle:
PROC [context: Context, r: Rectangle] ~ {
path: 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]];
};
state: State ~ context.state;
IF state.np.noImage=0
THEN {
data: Data ~ NARROW[context.data];
Validate[data, state, maskNeeds];
IF specialRectangles
AND data.clientToDevice.form # 0
THEN {
clientToDevice: Transformation ~ data.clientToDevice;
clip: DeviceBox ~ DeviceBoxFromDeviceRectangle[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]];
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 {
device: Device ~ data.device;
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 {
data.devicePath ← ImagerScanConverter.CreatePath[path: path,
transformation: data.clientToDevice, clipBox: data.clientClipBox,
scratch: data.devicePath];
MaskDevicePath[data];
};
};
};
RasterMaskRectangleI:
PROC [context: Context, x, y, w, h:
INTEGER] ~ {
state: State ~ context.state;
IF state.np.noImage=0
THEN {
data: Data ~ NARROW[context.data];
clientToDevice: Transformation;
Validate[data, state, maskNeeds];
clientToDevice ← data.clientToDevice;
IF clientToDevice.form=9
AND clientToDevice.integerTrans
THEN {
smin: INT ← INT[clientToDevice.tx]-y; smax: INT ← smin-h;
fmin: INT ← INT[clientToDevice.ty]+x; fmax: INT ← fmin+w;
box: DeviceBox ← DeviceBoxFromDeviceRectangle[data.clientClipBox];
boxes:
PROC[action:
PROC[DeviceBox]] ~ {
ClipBoxToMask[box: box, mask: data.clientClipMask, action: action];
};
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 {
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.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];
};
};
identity: Transformation ~ ImagerTransformation.Scale[1.0];
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 ~ {
NewImagerStroke.PathFromStroke[path: path, width: state.np.strokeWidth, m: data.clientToDevice, moveTo: moveTo, lineTo: lineTo, conicTo: conicTo, curveTo: curveTo, closed: closed, end: state.np.strokeEnd, joint: state.np.strokeJoint];
};
Validate[data, state, maskNeeds];
data.devicePath ← ImagerScanConverter.CreatePath[path: StrokePath,
transformation: identity, clipBox: data.clientClipBox,
scratch: data.devicePath];
MaskDevicePath[data];
};
};
RasterMaskVector:
PROC [context: Context, p1, p2:
VEC] ~ {
path: PathProc ~ { moveTo[p1]; lineTo[p2] };
RasterMaskStroke[context, path, FALSE];
};
RasterMaskPixel:
PROC [context: Context, pa: PixelArray] ~ {
state: State ~ context.state;
USING [T, color, clipper, noImage, priorityImportant]
IF state.np.noImage=0
THEN {
data: Data ~ NARROW[context.data];
Validate[data, state, maskNeeds];
ERROR;
paToDevice: Transformation ← NIL;
pixelMask: ImagerMask.Mask ← NIL;
paToDevice ← pa.m.Concat[data.clientToDevice];
pixelMask ← ImagerMask.FromPixelArray[pa, paToDevice];
ValidateDeviceState[data]; -- notify device if color changed
data.device.ApplyMask[data.device, pixelMask, data.clientClipMask];
};
};
bitsPerWord:
NAT ~ Basics.bitsPerWord;
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];
Validate[data, state, maskNeeds];
IF data.clientToDevice.form=9
AND data.clientToDevice.integerTrans
THEN {
clientToDevice: Transformation ~ data.clientToDevice;
smin: INT ~ INT[data.clientToDevice.tx]-ty; smax: INT ~ smin+sSize;
fmin: INT ~ INT[data.clientToDevice.ty]+tx; fmax: INT ~ fmin+fSize;
box: DeviceBox ← DeviceBoxFromDeviceRectangle[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.MaskBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine,
ts: smin-sMin, tf: fmin-fMin, boxes: boxes, replace: FALSE];
};
}
ELSE NULL; -- hard case
};
};
showNeeds: Flags ~ OrFlags[[fontAtom:
TRUE, charToDevice:
TRUE], maskNeeds];
CharPartlyVisible:
PROC [context: Context, font: Font, char: XChar]
RETURNS [
BOOL] ~ {
bounds: Imager.Box ~ ImagerBox.BoxFromRect[ImagerBackdoor.GetBounds[context]];
bb: Imager.Box ~ ImagerBox.BoxFromExtents[ImagerFont.BoundingBox[font, char]];
common: Imager.Box ~ ImagerBox.IntersectBox[bounds, bb];
RETURN [((common.xmax > common.xmin) AND (common.ymax > common.ymin))]
};
BasicShowChar:
PROC [context: Context, font: Font, char: XChar, checkBB:
BOOL, imaging:
BOOL] ~ {
data: Data ~ NARROW[context.data];
width: VEC ← font.Width[char];
IF font.Amplified[char]
THEN {
width ← width.Mul[ImagerBackdoor.GetReal[context, amplifySpace]];
};
Imager.Trans[context];
IF imaging
AND (
NOT checkBB
OR CharPartlyVisible[context, font, char])
THEN {
IF data.fontTuner =
NIL
THEN {
ImagerFontPrivate.MaskChar[font, char, context];
}
ELSE {
op: PROC [ctx: Context] ~ {ImagerFontPrivate.MaskChar[font, char, ctx]};
data.fontTuner.proc[data.fontTuner, op, context];
};
};
Imager.SetXYRel[context, width];
SELECT font.Correction[char]
FROM
none => NULL;
space => Imager.CorrectSpace[context, width];
mask => Imager.CorrectMask[context];
ENDCASE => ERROR;
};
worryNat: NAT ~ NAT.LAST/2-1;
worryReal:
REAL ← worryNat;
RasterShow:
PROC [context: Context, string: XStringProc, xrel:
BOOL] ~ {
state: State ~ context.state;
USING [T, font, color, clipper, cp, amplifySpace, noImage, priorityImportant]
data: Data ~ NARROW[context.data];
device: Device ~ data.device;
cache: ImagerCache.Ref ~ data.fontCache;
font: Font ~ state.font;
amplifySpace: REAL ~ state.np.amplifySpace;
testAmplified: BOOL ~ amplifySpace#1.0;
testCorrection: BOOL ~ state.np.correctPass#0;
imaging: BOOL ~ state.np.noImage=0;
basicCharAction: PROC [char: XChar] ~ {BasicShowChar[context, font, char, TRUE, imaging]};
scaledCPValid: BOOL ← FALSE;
sCP: Scaled.Value;
fCP: Scaled.Value;
UpdateCP:
PROC ~ {
IF scaledCPValid THEN state.p.cp ← data.viewToDevice.InverseTransform[[sCP.Float, fCP.Float]];
};
TryScaledCP:
PROC ~ {
cp: VEC ← data.viewToDevice.Transform[state.p.cp];
IF
ABS[cp.x] < worryReal
AND
ABS[cp.y] < worryReal
THEN {
sCP ← Scaled.FromReal[cp.x];
fCP ← Scaled.FromReal[cp.y];
scaledCPValid ← TRUE;
}
ELSE scaledCPValid ← FALSE;
};
cachedCharAction:
PROC [char: XChar] ~ {
charMask: ImagerDev.CharMask ← ImagerCache.Fetch[cache, data.fontAtom, char];
IF charMask=
NIL
AND imaging
AND CharPartlyVisible[context, font, char]
THEN {
operator: PROC [c: Context] ~ {BasicShowChar[c, font, char, FALSE, TRUE]};
m: Transformation ← data.clientToDevice.TranslateTo[[0, 0]];
charMask ← ImagerMaskCapture.Capture[operator, m, data.rastWeight];
IF charMask#
NIL
THEN {
charMask.font ← data.fontAtom;
charMask.char ← char;
[] ← ImagerCache.Store[cache, charMask];
};
};
IF charMask=
NIL
OR
NOT charMask.metricsValid
OR
NOT scaledCPValid
THEN {
UpdateCP[];
basicCharAction[char];
TryScaledCP[];
}
ELSE {
sCPOld: Scaled.Value ~ sCP;
fCPOld: Scaled.Value ~ fCP;
s: INTEGER ~ Scaled.Round[sCPOld];
f: INTEGER ~ Scaled.Round[fCPOld];
IF imaging
AND charMask.sSizeBB#0
THEN {
ShowCharacterMask[data: data, mask: charMask, s: s, f: f];
};
IF testCorrection
OR (testAmplified
AND charMask.amplified)
THEN {
width: VEC ← font.Width[char];
IF testAmplified
AND font.Amplified[char]
THEN {
width ← width.Mul[amplifySpace];
};
UpdateCP[];
Imager.SetXYRel[context, width];
IF testCorrection
THEN
SELECT font.Correction[char]
FROM
none => NULL;
space => Imager.CorrectSpace[context, width];
mask => Imager.CorrectMask[context];
ENDCASE => ERROR;
TryScaledCP[];
}
ELSE {
sCPNew: Scaled.Value ~ (sCP ← sCPOld.PLUS[charMask.sWidth]);
fCPNew: Scaled.Value ~ (fCP ← fCPOld.PLUS[charMask.fWidth]);
IF sCPNew.integerPart NOT IN [-worryNat..worryNat]
OR fCPNew.integerPart
NOT
IN [-worryNat..worryNat]
THEN {
UpdateCP[];
TryScaledCP[];
};
};
};
};
oddCharIndex: BOOL ← FALSE;
xRelAction:
PROC [char: XChar] ~ {
IF oddCharIndex
THEN {
Imager.SetXRel[context, char.code-128];
}
ELSE IF cache=NIL THEN basicCharAction[char]
ELSE {
TryScaledCP[];
cachedCharAction[char];
UpdateCP[];
};
oddCharIndex ← NOT oddCharIndex;
};
saveAction:
PROC ~ {
Validate[data, state, showNeeds];
IF xrel THEN string[xRelAction]
ELSE IF cache=
NIL
THEN string[basicCharAction]
ELSE {
TryScaledCP[];
string[cachedCharAction];
UpdateCP[];
};
};
Imager.DoSave[context, saveAction];
};
ShowCharacterMask:
PROC [data: Data, mask: CharMask, s:
INTEGER, f:
INTEGER] ~ {
sMin: INTEGER ~ s+mask.sMinBB;
fMin: INTEGER ~ f+mask.fMinBB;
device: Device ~ data.device;
charBox: DeviceBox ~ [
smin: MAX[sMin, 0],
fmin: MAX[fMin, 0],
smax: MAX[sMin+INTEGER[mask.sSizeBB], 0],
fmax: MAX[fMin+INTEGER[mask.fSizeBB], 0]
];
allVisible: BOOL ← FALSE;
partlyVisible: BOOL ← TRUE;
IF data.clientClipMask#
NIL
AND data.clientClipMask.rest=
NIL
THEN {
dBB: DeviceBox ~ DeviceBoxFromDeviceRectangle[data.clientClipMask.first];
IF charBox.smin >= dBB.smin
AND charBox.fmin >= dBB.fmin
AND charBox.smax <= dBB.smax
AND charBox.fmax <= dBB.fmax
THEN {
allVisible ← TRUE;
}
ELSE
IF charBox.smax <= dBB.smin
OR charBox.fmax <= dBB.fmin
OR charBox.smin >= dBB.smax
OR charBox.fmin > dBB.fmax
THEN {
partlyVisible ← FALSE;
};
}
ELSE IF charBox.smax = 0 OR charBox.fmax = 0 THEN partlyVisible ← FALSE
ELSE
IF device.class.MaskChar #
NIL
THEN {
p: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[[charBox.smin, charBox.fmin, charBox.smax-charBox.smin, charBox.fmax-charBox.fmin]];
SELECT ImagerManhattan.IsVisible[p, data.clientClipMask]
FROM
invisible => partlyVisible ← FALSE;
visible => allVisible ← TRUE;
ENDCASE => NULL;
ImagerManhattan.Destroy[p];
};
IF allVisible AND (device.class.MaskChar # NIL) THEN device.class.MaskChar[device, s, f, mask]
ELSE
IF partlyVisible
THEN
WITH mask
SELECT
FROM
raster:
REF ImagerDev.CharMaskRep.raster => {
boxes:
PROC[action:
PROC[DeviceBox]] ~ {
ClipBoxToMask[box: charBox, mask: data.clientClipMask, action: action];
};
TRUSTED {device.class.MaskBits[device: device,
srcBase: @raster[0],
srcWordsPerLine: (raster.fSizeBB+(bitsPerWord-1))/bitsPerWord,
ts: sMin,
tf: fMin,
boxes: boxes
]};
};
runs:
REF ImagerDev.CharMaskRep.runs => {
FOR p: ManhattanPolygon ← data.clientClipMask, p.rest
UNTIL p=
NIL
DO
Runs:
PROC [run: ImagerDev.RunProc] ~ {
ImagerMask.RunsFromCharMask[mask, run, s, f, p.first];
};
bounds: DeviceBox ~ DeviceBoxFromDeviceRectangle[p.first];
smin: CARDINAL ~ MAX[charBox.smin, bounds.smin];
smax: CARDINAL ~ MIN[charBox.smax, bounds.smax];
fmin: CARDINAL ~ MAX[charBox.fmin, bounds.fmin];
fmax: CARDINAL ~ MIN[charBox.fmax, bounds.fmax];
IF smin<smax
AND fmin<fmax
THEN {
device.class.MaskRuns[device: device, bounds: [smin: smin, fmin: fmin, smax: smax, fmax: fmax], runs: Runs];
};
ENDLOOP;
};
ENDCASE => ERROR;
};
RasterShowText:
PROC [context: Context,
text:
REF
READONLY
TEXT, start, len:
NAT, xrel:
BOOL] ~ {
string: XStringProc ~ { ImagerFont.MapText[text, start, len, charAction] };
RasterShow[context, string, xrel];
};
RasterGetBoundingRectangle:
PUBLIC
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.clientClipBox.sMin, y: data.clientClipBox.fMin,
w: data.clientClipBox.sSize, h: data.clientClipBox.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.charToDevice ← FALSE;
data.valid.clientClipper ← 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];
smax: INTEGER ~ smin+ABS[h];
fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w];
fmax: INTEGER ~ fmin+ABS[w];
IF old#
NIL
AND old.rest=
NIL
AND
NOT exclude
THEN {
csmin: INTEGER ← old.first.sMin;
csmax: INTEGER ← csmin+old.first.sSize;
cfmin: INTEGER ← old.first.fMin;
cfmax: INTEGER ← cfmin+old.first.fSize;
IF fmin<cfmax
AND smin<csmax
AND fmax>cfmin
AND smax>csmin
THEN {
IF cfmin<fmin THEN cfmin ← fmin;
IF csmin<smin THEN csmin ← smin;
IF cfmax>fmax THEN cfmax ← fmax;
IF csmax>smax THEN csmax ← smax;
old.first.sMin ← csmin;
old.first.sSize ← csmax-csmin;
old.first.fMin ← cfmin;
old.first.fSize ← cfmax-cfmin;
data.viewClipBox ← old.first;
}
ELSE {
ImagerManhattan.Destroy[old];
data.viewClipMask ← NIL;
data.viewClipBox ← [0, 0, 0, 0];
};
}
ELSE {
this: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[[
sMin: smin, fMin: fmin, sSize: smax-smin, fSize: fmax-fmin]];
data.viewClipMask ← IF exclude THEN old.Difference[this] ELSE old.Intersection[this];
data.viewClipBox ← ImagerManhattan.BoundingBox[data.viewClipMask];
ImagerManhattan.Destroy[old];
ImagerManhattan.Destroy[this];
};
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];
};
};
DViewClip:
PROC [data: Data, path: PathProc, parity:
BOOL, exclude:
BOOL] ~ {
old, this, new: ManhattanPolygon;
old ← data.viewClipMask;
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];
IF exclude THEN new ← old.Difference[this] ELSE new ← old.Intersection[this];
data.viewClipMask ← new;
data.viewClipBox ← ImagerManhattan.BoundingBox[data.viewClipMask];
ImagerManhattan.Destroy[old];
ImagerManhattan.Destroy[this];
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: INT ← INT[viewToDevice.tx]-toY; smin: INT ← smax-height;
fmin: INT ← INT[viewToDevice.ty]+toX; fmax: INT ← fmin+width;
box: DeviceBox ← DeviceBoxFromDeviceRectangle[data.viewClipBox];
boxes:
PROC[action:
PROC[DeviceBox]] ~ {
ClipBoxToMask[box: box, mask: data.viewClipMask, action: action];
};
IF smin<box.smax
AND smax>box.smin
AND fmin<box.fmax
AND fmax>box.fmin
THEN {
device: Device ~ data.device;
IF box.smin<smin THEN box.smin ← smin; IF box.smax>smax THEN box.smax ← smax;
IF box.fmin<fmin THEN box.fmin ← fmin; IF box.fmax>fmax THEN box.fmax ← fmax;
device.class.MoveBoxes[device: device, ts: fromY-toY, tf: toX-fromX, boxes: boxes];
};
}
ELSE ERROR; -- hard case
};
Visibility: TYPE ~ ImagerBackdoor.Visibility;
TestViewRectangle:
PUBLIC
PROC [context: Imager.Context, x, y, w, h:
INTEGER]
RETURNS [Visibility] ~ {
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];
};
};
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,
SetStrokeDashes: ImagerState.StateSetStrokeDashes,
GetInt: ImagerState.StateGetInt,
GetReal: ImagerState.StateGetReal,
GetT: ImagerState.StateGetT,
GetFont: ImagerState.StateGetFont,
GetColor: ImagerState.StateGetColor,
GetClipper: ImagerState.StateGetClipper,
GetStrokeDashes: ImagerState.StateGetStrokeDashes,
ConcatT: ImagerState.StateConcatT,
Scale2T: ImagerState.StateScale2T,
RotateT: ImagerState.StateRotateT,
TranslateT: ImagerState.StateTranslateT,
Move: ImagerState.StateMove,
SetXY: ImagerState.StateSetXY,
SetXYRel: ImagerState.StateSetXYRel,
Show: RasterShow,
ShowText: 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,
MaskRectangle: RasterMaskRectangle,
MaskRectangleI: RasterMaskRectangleI,
MaskVector: RasterMaskVector,
MaskPixel: RasterMaskPixel,
MaskBits: RasterMaskBits,
Clip: ImagerState.StateClip,
ClipRectangle: ImagerState.StateClipRectangle,
ClipRectangleI: ImagerState.StateClipRectangleI,
GetCP: ImagerState.StateGetCP,
GetBoundingRectangle: RasterGetBoundingRectangle,
propList: NIL
]];