IIRasterImpl.mesa
Copyright Ó 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Michael Plass, December 10, 1986 4:23:28 pm PST
Doug Wyatt, August 4, 1986 11:08:19 am PDT
DIRECTORY
Basics USING [BITAND, BITNOT, BITOR, bitsPerWord, CARD, LongMult],
II,
IIBackdoor,
IIBox USING [Box, BoxFromExtents, BoxFromRectangle, Rectangle, RectangleFromBox],
IIDevice USING [AllowedMasks, DeviceClass, DeviceClassRep, DeviceParm],
IIFont USING [Font, FontBoundingBox, Typeface, TypefaceRep, XChar, XStringProc],
IIManhattan,
IIMaskCache USING [BitmapFromCharMask, BoxesFromCharMask, CharMask, CharMaskRep, MaskCache],
IIPath USING [MapOutline, PathProc],
IIPixel USING [MakePixelMap, PixelBuffer, PixelMap, Resample],
IIPixelArray USING [MaxSampleValue, maxVec, PixelArray, Transfer],
IIPrivate,
IIRaster USING [Interceptor],
IIRasterShow,
RefText,
IISample,
IIScanConverter USING [BoundingBox, ConvertToBoxes, ConvertToManhattanPolygon, Create, CreatePath, DevicePath, GenerateEdges, maxBox, Monotone, SetPath],
IIState USING [ChangeFlags, notChanged, RasterData, RasterDataRep, State, StateClip, StateClipRectangle, StateClipRectangleI, StateConcatT, StateCorrect, StateCorrectMask, StateCorrectSpace, StateDontCorrect, StateDoSave, StateGetClipper, StateGetColor, StateGetCP, StateGetFont, StateGetInt, StateGetReal, StateGetT, StateMaskUnderline, StateMove, StateRep, StateRotateT, StateScale2T, StateSetClipper, StateSetColor, StateSetCorrectMeasure, StateSetCorrectTolerance, StateSetFont, StateSetGray, StateSetInt, StateSetReal, StateSetSampledBlack, StateSetSampledColor, StateSetT, StateSetXY, StateSetXYRel, StateSpace, StateStartUnderline, StateTranslateT],
IIStroke USING [PathFromStroke, PathFromVector],
IITransformation,
IITypeface USING [MakeFont, Typeface, TypefaceRep],
Process USING [CheckForAbort],
Real USING [Fix, Round],
RealFns USING [AlmostEqual],
Rope USING [ROPE],
Scaled USING [Floor, FromReal, Round, Value],
SF,
Vector2 USING [Div, VEC];
IIRasterImpl: CEDAR PROGRAM
IMPORTS Basics, II, IIBackdoor, IIBox, IIFont, IIManhattan, IIMaskCache, IIPath, IIPixel, IIPixelArray, IIPrivate, IIRasterShow, IISample, IIScanConverter, IIState, IIStroke, IITransformation, IITypeface, Process, Real, RealFns, RefText, Scaled, SF, Vector2
EXPORTS II, IIFont, IIRaster, IIRasterShow, IIState
~ BEGIN
Types and Constants
Class: TYPE ~ IIPrivate.Class;
ClassRep: PUBLIC TYPE ~ IIPrivate.ClassRep; -- export to II.ClassRep
Typeface: TYPE ~ IITypeface.Typeface;
TypefaceRep: PUBLIC TYPE ~ IITypeface.TypefaceRep; -- export to IIFont.TypefaceRep
State: TYPE ~ IIState.State;
StateRep: PUBLIC TYPE ~ IIState.StateRep; -- export to II.StateRep
ROPE: TYPE ~ Rope.ROPE;
ChangeFlags: TYPE ~ IIState.ChangeFlags;
notChanged: ChangeFlags ~ IIState.notChanged;
CharMask: TYPE ~ IIMaskCache.CharMask;
CharMaskRep: TYPE ~ IIMaskCache.CharMaskRep;
Clipper: TYPE ~ IIBackdoor.Clipper;
Color: TYPE ~ II.Color;
ColorOperator: TYPE ~ II.ColorOperator;
Context: TYPE ~ II.Context;
CoordSys: TYPE ~ IIBackdoor.CoordSys;
DeviceClass: TYPE ~ IIDevice.DeviceClass;
DeviceParm: TYPE ~ IIDevice.DeviceParm;
DevicePath: TYPE ~ IIScanConverter.DevicePath;
Font: TYPE ~ IIFont.Font;
Interceptor: TYPE ~ IIRaster.Interceptor;
IntVec: TYPE ~ RECORD [s, f: INT];
ManhattanPolygon: TYPE ~ IIManhattan.Polygon;
nullBox: SF.Box ~ [min: [0, 0], max: [0, 0]];
Object: TYPE ~ II.Object;
PathProc: TYPE ~ IIPath.PathProc;
PixelArray: TYPE ~ IIPixelArray.PixelArray;
RawArray: TYPE ~ IISample.RawArray;
RawDescriptor: TYPE ~ IISample.RawDescriptor;
Rectangle: TYPE ~ IIBox.Rectangle;
SampleMap: TYPE ~ IISample.SampleMap;
ScanMode: TYPE ~ IITransformation.ScanMode;
ShowData: TYPE ~ IIRasterShow.ShowData;
ShowDataRep: TYPE ~ IIRasterShow.ShowDataRep;
Transformation: TYPE ~ IITransformation.Transformation;
Visibility: TYPE ~ IIBackdoor.Visibility;
VEC: TYPE ~ Vector2.VEC;
XChar: TYPE ~ IIFont.XChar;
bitsPerWord: NAT ~ Basics.bitsPerWord;
rawArraySize: NAT ~ IISample.rawArraySize;
Flags: TYPE ~ RECORD [
clientToDevice: BOOLFALSE, -- clientToDevice is valid
compositeClipper: BOOLFALSE, -- compositeClip and compositeBox are valid
fontInfo: BOOLFALSE, -- charToDevice and fontAtom are valid
fontBB: BOOLFALSE, -- fontBB is valid
deviceColor: BOOLFALSE, -- color last set by SetColor[device, ...] is valid
devicePriority: BOOLFALSE, -- priority last set by SetPriority[device, ...] is valid
unused: [0..1777B] ← 0
];
RasterData: TYPE ~ REF RasterDataRep;
RasterDataRep: PUBLIC TYPE ~ RECORD [ -- Export to IIState.RasterDataRep
deviceParm: DeviceParm ← NIL, -- parameters for the particular raster device
deviceClass: DeviceClass ← NIL, -- the device class, perhaps layered to call modify proc
interceptor: IIRaster.Interceptor ← NIL,
devicePath: DevicePath ← NIL, -- scratch storage for scan converter
valid: Flags ← [],
allow: IIDevice.AllowedMasks ← [FALSE, FALSE, FALSE, FALSE, FALSE, FALSE],
deviceClipBox: SF.Box ← nullBox, -- always valid
surfaceToDevice: Transformation ← NIL, -- always valid
viewToDevice: Transformation ← NIL, -- always valid
viewClip: ManhattanPolygon ← NIL, -- deviceClipBox device coords; always valid
viewBox: SF.Box ← nullBox, -- bb of viewClip; device coords; always valid
clientToDevice: Transformation ← NIL, -- valid iff valid.clientToDevice
compositeClip: ManhattanPolygon ← NIL, -- device coords; valid iff valid.compositeClipper
compositeBox: SF.Box ← nullBox, -- device coords; valid iff valid.compositeClipper
fontAtom: IIFont.Font ← NIL, -- valid iff valid.fontInfo
charToDevice: Transformation ← NIL, -- valid iff valid.fontInfo
showData: ShowData ← NIL
];
worryNat: NAT ~ LAST[NAT]/2-1;
worryReal: REAL ← worryNat;
Class and Context Creation
CreateClass: PUBLIC PROC [type: ATOM, deviceClass: DeviceClass] RETURNS [Class] ~ {
class: Class ~ NEW [ClassRep ← rasterClassTemplate^];
class.type ← type;
IF deviceClass.DrawBitmap = NIL THEN class.DrawBitmap ← NIL;
RETURN[class];
};
Create: PUBLIC PROC [class: Class, deviceParm: DeviceParm, data: REF, pixelUnits: BOOL] RETURNS [Context] ~ {
rasterData: RasterData ~ NEW [RasterDataRep ← [
deviceParm: deviceParm,
deviceClass: deviceParm.class,
interceptor: NIL,
devicePath: IIScanConverter.Create[],
valid: [],
deviceClipBox: [max: [deviceParm.sSize, deviceParm.fSize]],
surfaceToDevice: IITransformation.XYToSF[deviceParm.scanMode, deviceParm.sSize, deviceParm.fSize],
viewToDevice: IITransformation.Scale[1],
viewClip: NIL,
viewBox: nullBox,
clientToDevice: IITransformation.Scale[1],
compositeClip: NIL,
compositeBox: nullBox,
fontAtom: NIL,
charToDevice: IITransformation.Scale[1],
showData: NEW[ShowDataRep]
]];
state: State ~ NEW[StateRep ← [rasterData: rasterData]];
metersToSurface: VEC ~ Vector2.Div[deviceParm.surfaceUnitsPerInch, II.metersPerInch];
context: Context ~ NEW [II.ContextRep ← [class: class, state: state, data: data]];
state.T ← IF pixelUnits
THEN IITransformation.Scale[1]
ELSE IITransformation.Scale2[metersToSurface];
state.color ← II.black;
RasterViewReset[context];
rasterData.showData.viewToDevice ← rasterData.viewToDevice;
rasterData.showData.fontCache ← deviceParm.fontCache;
rasterData.showData.deviceParm ← deviceParm;
RETURN[context];
};
Device State Maintainence
ValidateClientToDevice: PROC [state: State] ~ {
rasterData: RasterData ~ state.rasterData;
rasterData.clientToDevice.ApplyCat[state.T, rasterData.viewToDevice];
rasterData.valid.clientToDevice ← TRUE;
};
ValidateFontInfo: PROC [context: Context] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
font: Font ~ state.font;
IF font = NIL
THEN ERROR II.Error[[code: $noFont, explanation: "Failed to set font before calling Show."]]
ELSE {
typeface: Typeface ~ font.typeface;
IF NOT rasterData.valid.clientToDevice THEN ValidateClientToDevice[state];
rasterData.charToDevice^ ← rasterData.clientToDevice^;
rasterData.charToDevice.ApplyTranslateTo[[0, 0]];
rasterData.charToDevice.ApplyPreConcat[font.charToClient];
rasterData.fontAtom ← IITypeface.MakeFont[typeface, rasterData.charToDevice];
rasterData.valid.fontInfo ← TRUE;
};
};
ValidateFontBB: PROC [context: Context] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
font: Font ~ state.font;
m: Transformation;
Verify[rasterData, [clientToDevice: TRUE]];
m ← IITransformation.Copy[rasterData.clientToDevice];
IITransformation.ApplyTranslateTo[m, [0, 0]];
rasterData.showData.fontBB ← IIBox.BoxFromRectangle[IITransformation.TransformRectangle[m, IIBox.RectangleFromBox[IIBox.BoxFromExtents[IIFont.FontBoundingBox[state.font]]]]];
rasterData.valid.fontBB ← TRUE;
IITransformation.Destroy[m];
};
ValidateClientClipper: PROC [context: Context] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
cc: IIManhattan.Polygon ← NIL;
IIManhattan.Destroy[rasterData.compositeClip];
cc ← IIManhattan.Copy[rasterData.viewClip];
cc ← IIManhattan.DestructiveClip[cc, rasterData.deviceClipBox];
FOR each: Clipper ← state.clipper, each.rest UNTIL each=NIL DO
Combine: PROC [a, b: ManhattanPolygon] RETURNS [ManhattanPolygon] ← (
IF each.first.exclude
THEN IIManhattan.DestructiveDifference
ELSE IIManhattan.DestructiveIntersection
);
path: PathProc ~ { IIPath.MapOutline[outline: each.first.outline,
moveTo: moveTo, lineTo: lineTo, curveTo: curveTo,
conicTo: conicTo, arcTo: arcTo] };
devicePath: DevicePath ← IIScanConverter.CreatePath[path: path, transformation: rasterData.viewToDevice, clipBox: rasterData.viewBox, scratch: rasterData.devicePath];
this: ManhattanPolygon ← IIScanConverter.ConvertToManhattanPolygon[devicePath: devicePath, clipBox: rasterData.viewBox, oddWrap: each.first.oddWrap];
cc ← Combine[cc, this];
IIManhattan.Destroy[this];
ENDLOOP;
rasterData.compositeClip ← cc;
rasterData.compositeBox ← IIManhattan.BoundingBox[cc];
rasterData.valid.compositeClipper ← TRUE;
};
ValidateColor: PROC [context: Context] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
rasterData.allow ← rasterData.deviceClass.SetColor[context, state.color, rasterData.viewToDevice];
rasterData.valid.deviceColor ← TRUE;
};
ValidatePriority: PROC [context: Context] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
IF rasterData.deviceParm.class.SetPriority # NIL
THEN rasterData.deviceClass.SetPriority[context, state.np.priorityImportant#0];
rasterData.valid.devicePriority ← TRUE;
};
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: PUBLIC PROC [context: Context] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
changed: IIState.ChangeFlags ~ state.changed;
state.changed ← IIState.notChanged;
IF changed.T THEN rasterData.valid.clientToDevice ← rasterData.valid.fontInfo ← rasterData.valid.fontBB ← FALSE;
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 ← rasterData.valid.fontBB ← FALSE;
};
ValidateIfNeeded: PUBLIC PROC [context: Context, needs: Flags] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
fix: Flags ~ AndFlags[needs, NotFlags[rasterData.valid]];
IF fix.clientToDevice THEN ValidateClientToDevice[state];
IF fix.deviceColor THEN ValidateColor[context];
IF fix.devicePriority THEN ValidatePriority[context];
IF fix.compositeClipper THEN ValidateClientClipper[context];
IF fix.fontInfo THEN ValidateFontInfo[context];
IF fix.fontBB THEN ValidateFontBB[context];
IF AndFlags[rasterData.valid, needs]#needs THEN ERROR;
};
Validate: PROC [context: Context, needs: Flags] ~ INLINE {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
IF state.changed#IIState.notChanged THEN NoteStateChanges[context];
IF AndFlags[rasterData.valid, needs]#needs THEN ValidateIfNeeded[context, needs];
};
Verify: PROC [rasterData: RasterData, needs: Flags] ~ INLINE {
IF AndFlags[rasterData.valid, needs]#needs THEN ERROR;
};
GetDeviceParm: PUBLIC PROC [context: Context] RETURNS [DeviceParm] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
RETURN [rasterData.deviceParm]
};
ValidateState: PUBLIC PROC [context: Context] RETURNS [IIDevice.AllowedMasks] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[context, [deviceColor: TRUE, devicePriority: TRUE]];
RETURN [rasterData.allow];
};
GetShowData: PUBLIC PROC [context: Context] RETURNS [ShowData] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
showData: ShowData ~ rasterData.showData;
Validate[context, [deviceColor: TRUE, devicePriority: TRUE, fontInfo: TRUE, compositeClipper: TRUE, fontBB: TRUE]];
showData.fontAtom ← rasterData.fontAtom;
showData.clipBounds ← rasterData.compositeBox;
showData.deviceClass ← rasterData.deviceClass;
showData.allow ← rasterData.allow;
RETURN [showData]
};
Easy Transformations
SMul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ INLINE {RETURN [IF a#0 THEN Smul[a,b] ELSE 0]};
Smul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ {
Overflow cannot happen.
HighBit: PROC [a: INTEGER] RETURNS [[0..1]] ~ INLINE {
RETURN [LOOPHOLE[a, PACKED ARRAY [0..Basics.bitsPerWord) OF [0..1]][0]];
};
Card: PROC [i: INTEGER] RETURNS [NAT] ~ INLINE {RETURN [LOOPHOLE[i]]};
SELECT HighBit[a]*2+HighBit[b] FROM
0 => RETURN [Basics.LongMult[Card[a], Card[b]]];
1 => RETURN [-Basics.LongMult[Card[a], Card[-b]]];
2 => RETURN [-Basics.LongMult[Card[-a], Card[b]]];
3 => RETURN [Basics.LongMult[Card[-a], Card[-b]]];
ENDCASE => ERROR;
};
IsAllInteger: PROC [m: Transformation] RETURNS [BOOL] ~ {
Is: PROC [r: REAL] RETURNS [BOOL] ~ {
IF r IN [-worryReal..worryReal] THEN {
ir: INT ~ Real.Round[r];
IF r = ir THEN RETURN [TRUE];
IF RealFns.AlmostEqual[r, Real.Round[r], -18] THEN RETURN [TRUE];
};
RETURN [FALSE];
};
RETURN [Is[m.a] AND Is[m.b] AND Is[m.c] AND Is[m.d] AND Is[m.e] AND Is[m.f]];
};
Floor: PROC [r: REAL] RETURNS [i: INT] ~ INLINE {
i ← Real.Round[r];
IF i > r THEN i ← i - 1;
};
Ceiling: PROC [r: REAL] RETURNS [i: INT] ~ INLINE {
i ← Real.Round[r];
IF i < r THEN i ← i + 1;
};
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: [p0.s, p0.f], max: [p1.s, p1.f]]]
ELSE RETURN [[min: clipBox.min, max: clipBox.min]];
};
Mask Support
MaskDeviceBoxes: PUBLIC PROC [context: Context, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[context, maskNeeds];
IF rasterData.compositeClip # NIL THEN {
IF SF.Inside[bounds, rasterData.compositeClip.first] -- AND rasterData.allow.unorderedBoxes --
THEN {rasterData.deviceClass.MaskBoxes[context: context, bounds: bounds, boxes: boxes]}
ELSE {
manhattan: IIManhattan.Polygon ~ IIManhattan.DestructiveIntersection[IIManhattan.CreateFromBoxes[boxes], rasterData.compositeClip];
IF manhattan # NIL THEN {
ManhattanBoxes: SF.BoxGenerator ~ {
FOR each: LIST OF SF.Box ← manhattan, each.rest UNTIL each=NIL DO
boxAction[each.first];
ENDLOOP;
};
rasterData.deviceClass.MaskBoxes[context: context, bounds: IIManhattan.BoundingBox[manhattan], boxes: ManhattanBoxes];
IIManhattan.Destroy[manhattan];
};
};
};
};
boxesFromPathNeeds: Flags ~ [compositeClipper: TRUE];
BoxesFromPath: PROC [rasterData: RasterData, path: PathProc,
oddWrap: BOOLFALSE, transformation: Transformation ← NIL,
action: PROC [bounds: SF.Box, boxGenerator: SF.BoxGenerator]
] ~ {
clipBox: SF.Box ~ rasterData.compositeBox;
clipMask: ManhattanPolygon ~ rasterData.compositeClip;
devicePath: DevicePath ~ IIScanConverter.CreatePath[path: path,
transformation: transformation, clipBox: clipBox, scratch: rasterData.devicePath];
pathBox: SF.Box ~ IIScanConverter.BoundingBox[devicePath];
bounds: SF.Box ~ SF.Intersect[pathBox, clipBox];
runs: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] ~ {
IF clipMask#NIL AND clipMask.rest=NIL THEN {
IIScanConverter.ConvertToBoxes[devicePath: devicePath, oddWrap: oddWrap,
clipBox: clipMask.first, boxAction: boxAction];
}
ELSE {
rem: ManhattanPolygon ← clipMask;
clipBoxAction: --SF.BoxAction-- PROC [box: SF.Box] ~ {
WHILE rem#NIL AND rem.first.max.s<=box.min.s DO rem ← rem.rest ENDLOOP;
FOR l: LIST OF SF.Box ← rem, l.rest UNTIL l=NIL OR l.first.min.s>=box.max.s DO
clipped: SF.Box ~ SF.Intersect[box, l.first];
IF SF.Nonempty[clipped] THEN boxAction[clipped];
ENDLOOP;
};
IIScanConverter.ConvertToBoxes[devicePath: devicePath, oddWrap: oddWrap,
clipBox: clipBox, boxAction: clipBoxAction];
};
};
Verify[rasterData, boxesFromPathNeeds];
Process.CheckForAbort[];
action[bounds, runs];
};
maskBoxesNeeds: Flags ~ OrFlags[boxesFromPathNeeds, [deviceColor: TRUE, devicePriority: TRUE]];
MaskFillViaBoxes: PROC [context: Context, path: PathProc,
oddWrap: BOOLFALSE, transformation: Transformation ← NIL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
maskBoxesAction: PROC [bounds: SF.Box, boxGenerator: SF.BoxGenerator] ~ {
rasterData.deviceClass.MaskBoxes[context: context, bounds: bounds, boxes: boxGenerator];
};
Verify[rasterData, maskBoxesNeeds];
BoxesFromPath[rasterData: rasterData, path: path, oddWrap: oddWrap,
transformation: transformation, action: maskBoxesAction];
};
ClipBoxToMask: PUBLIC PROC [box: SF.Box, mask: ManhattanPolygon, action: PROC [SF.Box]] ~ {
FOR list: LIST OF SF.Box ← mask, list.rest UNTIL list=NIL DO
c: SF.Box ~ SF.Intersect[box, list.first];
IF SF.Nonempty[c] THEN action[c];
ENDLOOP;
};
MaskFill
maskNeeds: Flags ~ OrFlags[maskBoxesNeeds, [clientToDevice: TRUE]];
RasterMaskFill: PROC [context: Context, path: PathProc, oddWrap: BOOL] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
Validate[context, maskNeeds];
MaskFillViaBoxes[context: context, path: path, oddWrap: oddWrap, transformation: rasterData.clientToDevice];
};
};
MaskRectangle
RasterMaskRectangle: PROC [context: Context, r: Rectangle] ~ {
state: State ~ context.state;
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
deviceClass: DeviceClass ~ rasterData.deviceClass;
Validate[context, maskNeeds];
IF rasterData.clientToDevice.form # 0 AND (rasterData.allow.unorderedBoxes OR rasterData.compositeClip = NIL OR rasterData.compositeClip.rest = NIL) THEN {
clientToDevice: Transformation ~ rasterData.clientToDevice;
clip: SF.Box ~ rasterData.compositeBox;
p0: VEC ~ IITransformation.Transform[clientToDevice, [r.x, r.y]];
p1: VEC ~ IITransformation.Transform[clientToDevice, [r.x+r.w, r.y+r.h]];
s0: INT ← Real.Fix[MAX[MIN[p0.x+0.5, clip.max.s], clip.min.s]];
s1: INT ← Real.Fix[MAX[MIN[p1.x+0.5, clip.max.s], clip.min.s]];
f0: INT ← Real.Fix[MAX[MIN[p0.y+0.5, clip.max.f], clip.min.f]];
f1: INT ← Real.Fix[MAX[MIN[p1.y+0.5, clip.max.f], clip.min.f]];
Process.CheckForAbort[];
IF s1<s0 THEN { temp: INT ~ s0; s0 ← s1; s1 ← temp };
IF f1<f0 THEN { temp: INT ~ f0; f0 ← f1; f1 ← temp };
IF s0<s1 AND f0<f1 THEN {
box: SF.Box ~ [min: [s0, f0], max: [s1, f1]];
boxes: PROC [action: PROC[SF.Box]] ~ {
ClipBoxToMask[box: box, mask: rasterData.compositeClip, action: action];
};
deviceClass.MaskBoxes[context: context, bounds: box, boxes: boxes];
};
}
ELSE {
rectanglePath: PathProc ~ {
moveTo[[r.x, r.y]];
lineTo[[r.x+r.w, r.y]];
lineTo[[r.x+r.w, r.y+r.h]];
lineTo[[r.x, r.y+r.h]];
};
MaskFillViaBoxes[context: context, path: rectanglePath, transformation: rasterData.clientToDevice];
};
};
};
RasterMaskRectangleI: PROC [context: Context, x, y, w, h: INTEGER] ~ {
state: State ~ context.state;
Validate[context, maskNeeds];
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
clientToDevice: Transformation ~ rasterData.clientToDevice;
IF IITransformation.EasyTransformation[clientToDevice] AND (rasterData.allow.unorderedBoxes OR rasterData.compositeClip = NIL OR rasterData.compositeClip.rest = NIL) THEN {
box: SF.Box ~ IITransformation.EasyTransformBox[clientToDevice, x, y, w, h, rasterData.compositeBox];
boxes: PROC [action: PROC[SF.Box]] ~ {
ClipBoxToMask[box: box, mask: rasterData.compositeClip, action: action];
};
rasterData.deviceClass.MaskBoxes[context: context, bounds: box, boxes: boxes];
}
ELSE RasterMaskRectangle[context, [x: x, y: y, w: w, h: h]];
};
};
MaskStroke
RasterMaskVector: PROC [context: Context, p1, p2: VEC] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[context, maskNeeds];
IF state.np.noImage=0 AND rasterData.compositeClip # NIL THEN {
strokePath: PathProc ~ {
IIStroke.PathFromVector[p0: p1, p1: p2, m: rasterData.clientToDevice, width: state.np.strokeWidth, end: state.np.strokeEnd, moveTo: moveTo, lineTo: lineTo];
};
IF rasterData.compositeClip.rest = NIL AND rasterData.allow.regionFill THEN {
devicePath: DevicePath ~ IIScanConverter.CreatePath[path: strokePath, clipBox: rasterData.compositeBox, scratch: rasterData.devicePath];
bounds: SF.Box ~ IIScanConverter.BoundingBox[devicePath];
IF IIScanConverter.Monotone[devicePath] THEN {
edgeGenerator: PROC [edgeAction: IISample.EdgeAction] ~ {
IIScanConverter.GenerateEdges[devicePath, edgeAction];
};
Process.CheckForAbort[];
IF NOT SF.Empty[bounds] THEN rasterData.deviceClass.MaskRegion[context, bounds, edgeGenerator];
RETURN;
};
};
MaskFillViaBoxes[context: context, path: strokePath];
};
};
RasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ~ {
state: State ~ context.state;
USING [T, color, clipper, strokeWidth, strokeStyle, noImage, priorityImportant]
rasterData: RasterData ~ state.rasterData;
Validate[context, maskNeeds];
IF state.np.noImage=0 AND rasterData.compositeClip # NIL THEN {
strokePath: PathProc ~ {
IIStroke.PathFromStroke[path: path, closed: closed, m: rasterData.clientToDevice,
width: state.np.strokeWidth, end: state.np.strokeEnd, joint: state.np.strokeJoint,
moveTo: moveTo, lineTo: lineTo, conicTo: conicTo, box: [xmin: rasterData.compositeBox.min.s, ymin: rasterData.compositeBox.min.f, xmax: rasterData.compositeBox.max.s, ymax: rasterData.compositeBox.max.f]];
};
IF rasterData.allow.unorderedBoxes AND rasterData.compositeClip.rest = NIL THEN {
...
IIScanConverter.ObjectsFromPath[strokePath, rasterData.compositeBox, object, rasterData.devicePath];
RETURN;
};
MaskFillViaBoxes[context: context, path: strokePath];
};
};
MaskPixel
MaskSampledBits: PROC [context: Context, bitmap: SampleMap, maskToDevice: Transformation] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
deviceClass: DeviceClass ~ rasterData.deviceClass;
SELECT TRUE FROM
maskToDevice.form=3 AND maskToDevice.integerTrans AND rasterData.allow.bitmap => {
delta: SF.Vec ~ [s: maskToDevice.tx, f: maskToDevice.ty];
box: SF.Box ~ SF.Intersect[rasterData.compositeBox, SF.Displace[IISample.GetBox[bitmap], delta]];
boxes: PROC [action: PROC[SF.Box]]
~ { ClipBoxToMask[box: box, mask: rasterData.compositeClip, action: action] };
deviceClass.MaskBitmap[context: context, bitmap: bitmap, delta: delta, bounds: box, boxes: boxes];
};
maskToDevice.form#0 AND rasterData.allow.unorderedBoxes AND IsAllInteger[maskToDevice] => {
a: INTEGER ~ Real.Round[maskToDevice.a];
b: INTEGER ~ Real.Round[maskToDevice.b];
c: INTEGER ~ maskToDevice.tx;
d: INTEGER ~ Real.Round[maskToDevice.d];
e: INTEGER ~ Real.Round[maskToDevice.e];
f: INTEGER ~ maskToDevice.ty;
Map: PROC [p: SF.Vec] RETURNS [IntVec]
~ {RETURN [[SMul[a, p.s] + SMul[b, p.f] + c, SMul[d, p.s] + SMul[e, p.f] + f]]};
srcBox: SF.Box ~ IISample.GetBox[bitmap]; -- Overall bounds, in source bitmap coordinates;
dstBox: SF.Box ~ ClippedBounds[rasterData.compositeBox, Map[srcBox.min], Map[srcBox.max]]; -- Overall bounds, in device coordinates;
boxGenerator: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] ~ {
FOR each: LIST OF SF.Box ← rasterData.compositeClip, each.rest UNTIL each = NIL DO
Come here once for each box in the clipping region.
visibleBox: SF.Box ~ SF.Intersect[dstBox, each.first];
IF SF.Nonempty[visibleBox] THEN {
Come here once for each box in the clipping region that intersects the bitmap area.
srcVisibleRealBox: IIBox.Box ~ IIBox.BoxFromRectangle[
IITransformation.InverseTransformRectangle[maskToDevice, [
x: visibleBox.min.s, y: visibleBox.min.f,
w: visibleBox.max.s-visibleBox.min.s, h: visibleBox.max.f-visibleBox.min.f]]
];
srcVisibleBox: SF.Box ~ ClippedBounds[clipBox: srcBox,
p0: [Floor[srcVisibleRealBox.xmin], Floor[srcVisibleRealBox.ymin]],
p1: [Ceiling[srcVisibleRealBox.xmax], Ceiling[srcVisibleRealBox.ymax]]
];
processPartiallyVisibleBox: --SF.BoxAction-- PROC [box: SF.Box] ~ {
This box indicates a region of 1-bits in the source bitmap coordinates;
transform to device coordinates, clip it and pass it to the device.
clippedBox: SF.Box ~ ClippedBounds[visibleBox, Map[box.min], Map[box.max]];
IF SF.Nonempty[clippedBox] THEN boxAction[clippedBox];
};
visibleBitmap: SampleMap ~ IISample.Clip[bitmap, srcVisibleBox];
IISample.BoxesFromBitmap[map: visibleBitmap, boxAction: processPartiallyVisibleBox];
TRUSTED {IISample.ReleaseDescriptor[visibleBitmap]};
};
ENDLOOP;
};
rasterData.deviceClass.MaskBoxes[context: context, bounds: dstBox, boxes: boxGenerator];
};
rasterData.allow.unorderedBoxes AND IITransformation.SingularValues[maskToDevice].y > 1.0 => FastHardMaskSampledBits[context, bitmap, maskToDevice];
ENDCASE => HardMaskSampledBits[context, bitmap, maskToDevice];
};
FastHardMaskSampledBits: PROC [context: Context, bitmap: SampleMap, maskToDevice: Transformation] ~ {
deviceToView: Transformation ~ IIBackdoor.GetTransformation[context: context, from: device, to: view];
maskToView: Transformation ~ IITransformation.Concat[maskToDevice, deviceToView];
proc: PROC ~ {
box: SF.Box ← IISample.GetBox[bitmap];
clip: IIBox.Box;
IIBackdoor.SetT[context, maskToView];
clip ← IIBox.BoxFromRectangle[IIBackdoor.GetBounds[context]];
IF box.min.s < clip.xmin THEN box.min.s ← Floor[clip.xmin];
IF box.min.f < clip.ymin THEN box.min.f ← Floor[clip.ymin];
IF box.max.s > clip.xmax THEN box.max.s ← Ceiling[clip.xmax];
IF box.max.f > clip.ymax THEN box.max.f ← Ceiling[clip.ymax];
IF SF.Nonempty[box] THEN {
bytes: NAT ~ (box.max.f-box.min.f+7)/8;
textBuf: REF TEXT ~ RefText.ObtainScratch[nChars: bytes];
String: II.XStringProc ~ {
FOR i: NAT IN [0..bytes) DO
charAction[[set: 0, code: ORD[textBuf[i]]]];
ENDLOOP;
};
textBufAsSampleMap: SampleMap ← NIL;
TRUSTED {
pointer: LONG POINTER ~ LOOPHOLE[textBuf, LONG POINTER] + SIZE[TEXT[0]];
textBufAsSampleMap ← IISample.ObtainUnsafeDescriptor[size: [s: 1, f: box.max.f-box.min.f], bitsPerSample: 1, bitsPerLine: bytes*8, base: [word: pointer, bit: 0], ref: textBuf, words: SIZE[TEXT[textBuf.maxLength]]-SIZE[TEXT[0]]];
};
IISample.Clear[textBufAsSampleMap];
II.SetFont[context, IIRasterShow.BitFont[]];
FOR s: INTEGER IN [box.min.s..box.max.s) DO
IISample.BasicTransfer[dst: textBufAsSampleMap, src: bitmap, srcMin: [s, box.min.f], size: [1, box.max.f-box.min.f]];
II.SetXY[context, [s, box.min.f]];
II.Show[context, String];
ENDLOOP;
TRUSTED { IISample.ReleaseDescriptor[textBufAsSampleMap] };
RefText.ReleaseScratch[textBuf];
};
};
II.DoSaveAll[context, proc];
IITransformation.Destroy[deviceToView];
IITransformation.Destroy[maskToView];
};
HardMaskSampledBits: PROC [context: Context, bitmap: SampleMap, maskToDevice: Transformation] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
srcBox: SF.Box ~ IISample.GetBox[bitmap];
maskBoundary: PathProc ~ {
x0: REAL ~ srcBox.min.s; y0: REAL ~ srcBox.min.f;
x1: REAL ~ srcBox.max.s; y1: REAL ~ srcBox.max.f;
moveTo[[x0, y0]]; lineTo[[x1, y0]]; lineTo[[x1, y1]]; lineTo[[x0, y1]];
};
Nest1: PROC [bounds: SF.Box, boxGenerator: SF.BoxGenerator] ~ {
Nest2: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] ~ {
pixelMap: IIPixel.PixelMap ~ IIPixel.MakePixelMap[bitmap];
Nest3: --IIPixel.ResampleAction-- PROC [pixels: IIPixel.PixelBuffer, min: SF.Vec] ~ {
count: NAT ~ pixels.length;
buffer: IISample.SampleBuffer ~ pixels[0];
state: {off, on} ← off;
f0: NAT ← 0;
FOR f: NAT IN [0..count) DO
x: IISample.Sample ~ buffer[f];
SELECT state FROM
off => IF x#0 THEN { f0 ← f; state ← on };
on => IF x=0 THEN {
boxAction[[min: [min.s, min.f+f0], max: [min.s+1, min.f+f]]];
state ← off
};
ENDCASE => ERROR;
ENDLOOP;
IF state=on THEN boxAction[[min: [min.s, min.f+f0], max: [min.s+1, min.f+count]]];
};
IIPixel.Resample[self: pixelMap, m: maskToDevice, interpolate: FALSE, boxes: boxGenerator, bounds: bounds, action: Nest3];
};
rasterData.deviceClass.MaskBoxes[context: context, bounds: bounds, boxes: Nest2];
};
BoxesFromPath[rasterData: rasterData, path: maskBoundary, transformation: maskToDevice, action: Nest1];
};
RasterMaskBitmap: PROC [context: Context, bitmap: SampleMap, referencePoint: SF.Vec,
scanMode: ScanMode, position: VEC] ~ {
state: State ~ context.state;
Validate[context, maskNeeds];
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
m: Transformation ← IITransformation.Copy[rasterData.clientToDevice];
IITransformation.ApplyPreTranslate[m, position];
IITransformation.ApplySFToXY[m, scanMode, 0, 0];
IITransformation.ApplyPreTranslate[m, [-referencePoint.s, -referencePoint.f]];
MaskSampledBits[context: context, bitmap: bitmap, maskToDevice: m];
IITransformation.Destroy[m];
};
};
RasterDrawBitmap: PROC [context: Context, bitmap: SampleMap, referencePoint: SF.Vec,
scanMode: ScanMode, position: VEC] ~ {
state: State ~ context.state;
Validate[context, maskNeeds];
IF state.np.noImage=0 THEN {
rasterData: RasterData ~ state.rasterData;
deviceClass: DeviceClass ~ rasterData.deviceClass;
m: Transformation ← IITransformation.Copy[rasterData.clientToDevice];
IITransformation.ApplyPreTranslate[m, position];
IITransformation.ApplySFToXY[m, scanMode, 0, 0];
IITransformation.ApplyPreTranslate[m, [-referencePoint.s, -referencePoint.f]];
IF m.form=3 AND m.integerTrans AND rasterData.deviceParm.class.DrawBitmap # NIL THEN {
delta: SF.Vec ~ [s: m.tx, f: m.ty];
box: SF.Box ~ SF.Intersect[rasterData.compositeBox, SF.Displace[IISample.GetBox[bitmap], delta]];
boxes: PROC [action: PROC [SF.Box]]
~ { ClipBoxToMask[box: box, mask: rasterData.compositeClip, action: action] };
deviceClass.DrawBitmap[context: context, bitmap: bitmap, delta: delta, bounds: box, boxes: boxes];
}
ELSE IIPrivate.DefaultDrawBitmap[context, bitmap, referencePoint, scanMode, position];
IITransformation.Destroy[m];
};
};
bitsPerChunk: INT ← 100000;
RasterMaskPixel: PROC [context: Context, pa: PixelArray] ~ {
state: State ~ context.state;
IF pa.samplesPerPixel#1 THEN ERROR II.Error[[code: $illegalPixelMask,
explanation: "MaskPixel argument must have one sample per pixel."]];
IF IIPixelArray.MaxSampleValue[pa, 0]#1 THEN ERROR II.Error[[code: $illegalPixelMask,
explanation: "MaskPixel argument must have one bit per sample."]];
Validate[context, maskNeeds];
IF state.np.noImage=0 THEN TRUSTED {
rasterData: RasterData ~ state.rasterData;
sBufSize: NAT ~ MIN[pa.sSize, MAX[((bitsPerChunk+pa.fSize-1)/pa.fSize+15)/16, 1]*16];
scratch: IISample.SampleMap ~ IISample.ObtainScratchMap[box: [max: [sBufSize, pa.fSize]]];
bitmap: IISample.SampleMap ← scratch;
maskToDevice: Transformation ← IITransformation.Concat[pa.m, rasterData.clientToDevice];
FOR s: INT ← 0, s+sBufSize WHILE s < pa.sSize DO
IF pa.sSize-s < sBufSize THEN bitmap ← IISample.Clip[scratch, [max: [s: pa.sSize-s, f: pa.fSize]]];
IIPixelArray.Transfer[self: pa, dst: bitmap, s: s];
MaskSampledBits[context: context, bitmap: bitmap, maskToDevice: maskToDevice];
IITransformation.ApplyPreTranslate[maskToDevice, [sBufSize, 0]];
ENDLOOP;
IF bitmap # scratch THEN TRUSTED { IISample.ReleaseDescriptor[bitmap] };
IISample.ReleaseScratchMap[scratch];
IITransformation.Destroy[maskToDevice];
};
};
Show
MaskCharMask: PUBLIC PROC [context: Context, charMask: CharMask] RETURNS [ok: BOOLFALSE] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
cp: VEC ← rasterData.viewToDevice.Transform[state.p.cp];
Verify[rasterData, [compositeClipper: TRUE]];
IF rasterData.compositeClip = NIL THEN RETURN [ok: TRUE];
IF charMask#NIL AND ABS[cp.x] < worryReal AND ABS[cp.y] < worryReal THEN {
sCP: Scaled.Value ~ Scaled.FromReal[cp.x];
fCP: Scaled.Value ~ Scaled.FromReal[cp.y];
s: INTEGER ~ Scaled.Round[sCP];
f: INTEGER ~ Scaled.Round[fCP];
box: SF.Box ~ SF.Displace[charMask.box, [s, f]];
dstBox: SF.Box ~ SF.Intersect[box, rasterData.compositeBox];
IF SF.Empty[dstBox] THEN RETURN [ok: TRUE];
IF charMask.rep = culled OR charMask.rep = maskNotCacheable THEN RETURN [ok: FALSE]; -- only the metrics were in cache
IF SF.Inside[box, rasterData.compositeClip.first]
AND ((charMask.rep = raster AND rasterData.allow.rasterChar)
OR (charMask.rep = runs AND rasterData.allow.runGroupChar)) THEN {
All visible: go for it
rasterData.deviceClass.MaskChar[context: context, delta: [s, f], mask: charMask];
RETURN [ok: TRUE];
};
Partly visible: clip it
IF charMask.rep = raster AND rasterData.allow.bitmap
THEN {
bitmap: SampleMap ~ IIMaskCache.BitmapFromCharMask[charMask];
boxes: PROC [action: PROC[SF.Box]] ~ { ClipBoxToMask[box: dstBox, mask: rasterData.compositeClip, action: action] };
rasterData.deviceClass.MaskBitmap[context: context, bitmap: bitmap, delta: [s, f], bounds: dstBox, boxes: boxes];
TRUSTED { IISample.ReleaseDescriptor[bitmap] };
}
ELSE {
boxGenerator: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] ~ {
FOR each: LIST OF SF.Box ← rasterData.compositeClip, each.rest UNTIL each = NIL DO
IIMaskCache.BoxesFromCharMask[charMask: charMask, boxAction: boxAction, delta: [s, f], clip: each.first];
ENDLOOP;
};
rasterData.deviceClass.MaskBoxes[context: context, bounds: dstBox, boxes: boxGenerator];
};
RETURN [ok: TRUE];
};
};
Clipping Region Support
GetContainingBox: PUBLIC PROC [context: Context, p: SF.Vec] RETURNS [SF.Box] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[context, [compositeClipper: TRUE]];
FOR each: LIST OF SF.Box ← rasterData.compositeClip, each.rest UNTIL each = NIL DO
box: SF.Box ~ each.first;
IF each.rest = NIL OR SF.In[p, box] THEN RETURN [box];
ENDLOOP;
RETURN [[]];
};
GetDeviceClipBox: PUBLIC PROC [context: Context] RETURNS [SF.Box] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
RETURN [rasterData.deviceClipBox]
};
SetDeviceClipBox: PUBLIC PROC [context: Context, clipBox: SF.Box] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
rasterData.deviceClipBox ← clipBox;
rasterData.valid.compositeClipper ← FALSE;
};
RasterGetBounds: PROC [context: Context] RETURNS [Rectangle] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
Validate[context, [clientToDevice: TRUE, compositeClipper: TRUE]];
{ box: SF.Box ~ rasterData.compositeBox;
size: SF.Vec ~ SF.Size[box];
m: Transformation ~ rasterData.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 ~ rasterData.viewToDevice;
viewClip: ManhattanPolygon ~ rasterData.viewClip;
IF viewClip = NIL THEN RETURN [none];
IF IITransformation.EasyTransformation[viewToDevice] THEN {
box: SF.Box ~ IITransformation.EasyTransformBox[m: viewToDevice, x: x, y: y, w: w, h: h, clip: SF.maxBox];
IF SF.Inside[box, SF.Intersect[viewClip.first, rasterData.deviceClipBox]] THEN RETURN [all];
IF SF.Empty[SF.Intersect[box, rasterData.viewBox]] THEN RETURN [none];
};
Could distingush cases more carefully here, but we've got the most common ones done.
RETURN [part];
};
Double Buffering
RasterDoWithBuffer: PROC [context: Context, action: PROC, x, y, w, h: INTEGER, backgroundColor: Color] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
deviceClass: DeviceClass ~ rasterData.deviceClass;
IF rasterData.deviceParm.class.DoBuffered # NIL THEN {
Validate[context, [clientToDevice: TRUE]];
IF IITransformation.EasyTransformation[rasterData.clientToDevice] THEN {
oldDeviceClipBox: SF.Box ~ rasterData.deviceClipBox;
box: SF.Box ~ IITransformation.EasyTransformBox[m: rasterData.clientToDevice, x: x, y: y, w: w, h: h, clip: SF.Intersect[rasterData.viewBox, oldDeviceClipBox]];
fillAction: PROC ~ {
savedColor: Color ~ state.color;
II.SetColor[context, backgroundColor];
II.MaskRectangleI[context, x, y, w, h];
II.SetColor[context, savedColor];
action[];
};
saveAction: PROC ~ {
II.DoSaveAll[context, IF backgroundColor=NIL THEN action ELSE fillAction];
};
rasterData.deviceClipBox ← box;
rasterData.valid.compositeClipper ← FALSE;
Validate[context, [compositeClipper: TRUE]];
IF rasterData.compositeClip # NIL THEN deviceClass.DoBuffered[
context: context,
bounds: box,
copy: backgroundColor=NIL OR NOT SF.Inside[box, rasterData.compositeClip.first],
action: saveAction !
UNWIND => SetDeviceClipBox[context, oldDeviceClipBox]];
rasterData.deviceClipBox ← oldDeviceClipBox;
rasterData.valid.compositeClipper ← FALSE;
RETURN;
};
};
IIPrivate.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;
rasterData.viewToDevice^ ← rasterData.surfaceToDevice^;
rasterData.viewBox ← [max: [rasterData.deviceParm.sSize, rasterData.deviceParm.fSize]];
IIManhattan.Destroy[rasterData.viewClip];
rasterData.viewClip ← IIManhattan.CreateFromBox[rasterData.viewBox];
rasterData.valid.clientToDevice ← FALSE;
rasterData.valid.compositeClipper ← FALSE;
rasterData.valid.fontInfo ← FALSE;
rasterData.valid.deviceColor ← FALSE;
};
RasterViewTranslateI: PROC [context: Context, x, y: INTEGER] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
rasterData.viewToDevice.ApplyPreTranslate[[x, y]];
rasterData.valid.clientToDevice ← FALSE;
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 ~ rasterData.viewToDevice;
IF IITransformation.EasyTransformation[viewToDevice]
THEN {
old: ManhattanPolygon ~ rasterData.viewClip;
box: SF.Box ~ IITransformation.EasyTransformBox[m: viewToDevice, x: x, y: y, w: w, h: h, clip: rasterData.viewBox];
v: ManhattanPolygon ← rasterData.viewClip;
IF exclude THEN { t: ManhattanPolygon ~ IIManhattan.CreateFromBox[box]; v ← IIManhattan.DestructiveDifference[v, t]; IIManhattan.Destroy[t] }
ELSE v ← IIManhattan.DestructiveClip[v, box];
rasterData.viewClip ← v;
rasterData.viewBox ← IIManhattan.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;
Validate[context, [clientToDevice: TRUE]];
SELECT to FROM
client => m ← IITransformation.Copy[rasterData.clientToDevice];
view => m ← IITransformation.Copy[rasterData.viewToDevice];
surface => m ← IITransformation.Copy[rasterData.surfaceToDevice];
device => m ← IITransformation.Scale[1.0];
ENDCASE => ERROR;
IITransformation.ApplyInvert[m];
SELECT from FROM
client => IITransformation.ApplyPreConcat[m, rasterData.clientToDevice];
view => IITransformation.ApplyPreConcat[m, rasterData.viewToDevice];
surface => IITransformation.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 IIManhattan.DestructiveDifference
ELSE IIManhattan.DestructiveIntersection
);
IIScanConverter.SetPath[devicePath: rasterData.devicePath, path: path,
transformation: rasterData.viewToDevice, clipBox: rasterData.viewBox];
this ← IIScanConverter.ConvertToManhattanPolygon[devicePath: rasterData.devicePath,
clipBox: rasterData.viewBox, oddWrap: oddWrap];
rasterData.viewClip ← Combine[rasterData.viewClip, this];
IIManhattan.Destroy[this];
rasterData.viewBox ← IIManhattan.BoundingBox[rasterData.viewClip];
rasterData.valid.compositeClipper ← FALSE;
};
RasterMoveViewRectangle: PROC [context: Context, width, height, fromX, fromY, toX, toY: INTEGER] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
deviceClass: DeviceClass ~ rasterData.deviceClass;
viewToDevice: Transformation ~ rasterData.viewToDevice;
IF IITransformation.EasyTransformation[viewToDevice] AND rasterData.deviceParm.class.MoveBox # NIL THEN {
clipBox: SF.Box ~ SF.Intersect[rasterData.viewBox, rasterData.deviceClipBox];
srcBox: SF.Box ~ IITransformation.EasyTransformBox[m: viewToDevice, x: fromX, y: fromY, w: width, h: height, clip: clipBox];
dstBox: SF.Box ~ IITransformation.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 {
deviceClass.MoveBox[context: context, dstMin: dstBox.min, srcMin: srcBox.min, size: size];
RETURN;
};
};
ERROR II.Error[[$unimplemented, "MoveViewRectangle not implemented for this case"]];
};
Intercepting Raster Accesses
ModifySetColor: PROC [context: Context, color: Color, viewToDevice: Transformation] RETURNS [IIDevice.AllowedMasks]~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
RETURN [rasterData.deviceParm.class.SetColor[context, color, viewToDevice]];
};
ModifySetPriority: PROC [context: Context, priorityImportant: BOOL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
rasterData.deviceParm.class.SetPriority[context, priorityImportant];
};
ModifyMaskBoxes: PROC [context: Context, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
action: PROC ~ {rasterData.deviceClass.MaskBoxes[context, bounds, boxes]};
ModifyRaster[context, bounds, action];
};
ModifyMaskRegion: PROC [context: Context, bounds: SF.Box, edgeGenerator: PROC [IISample.EdgeAction]] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
action: PROC ~ {rasterData.deviceClass.MaskRegion[context, bounds, edgeGenerator]};
ModifyRaster[context, bounds, action];
};
ModifyMaskBitmap: PROC [context: Context, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
action: PROC ~ {rasterData.deviceClass.MaskBitmap[context, bitmap, delta, bounds, boxes]};
ModifyRaster[context, bounds, action];
};
ModifyMaskRawBitmaps: PROC [context: Context, n: [0..rawArraySize], a: POINTER TO RawArray] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
action: PROC ~ {rasterData.deviceClass.MaskRawBitmaps[context, n, a]};
bounds: SF.Box ← [min: SF.maxVec, max: SF.minVec];
TRUSTED {
p: POINTER TO RawDescriptor ← @(a[0]);
FOR i: NAT IN [0..n) DO
bounds.min ← SF.Min[bounds.min, p^.box.min];
bounds.min ← SF.Max[bounds.max, p^.box.max];
p ← p + SIZE[RawDescriptor];
ENDLOOP;
};
IF SF.Nonempty[bounds] THEN ModifyRaster[context, bounds, action];
};
ModifyDrawBitmap: PROC [context: Context, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
action: PROC ~ {rasterData.deviceClass.DrawBitmap[context, bitmap, delta, bounds, boxes]};
ModifyRaster[context, bounds, action];
};
ModifyMaskChar: PROC [context: Context, delta: SF.Vec, mask: CharMask] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
action: PROC ~ {rasterData.deviceClass.MaskChar[context, delta, mask]};
ModifyRaster[context, SF.Displace[mask.box, delta], action];
};
ModifyMoveBox: PROC [context: Context, dstMin, srcMin, size: SF.Vec] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
action: PROC ~ {rasterData.deviceClass.MoveBox[context, dstMin, srcMin, size]};
min: SF.Vec ~ SF.Min[dstMin, srcMin];
max: SF.Vec ~ SF.Add[dstMin, srcMin].Sub[min].Add[size];
ModifyRaster[context, [min: min, max: max], action];
};
ModifyDoBuffered: PROC [context: Context, bounds: SF.Box, copy: BOOL, action: PROC] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
proc: PROC ~ {rasterData.deviceClass.DoBuffered[context, bounds, copy, action]};
ModifyRaster[context, bounds, proc];
};
ModifyRaster: PUBLIC PROC [context: Context, box: SF.Box, action: PROC] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
interceptor: Interceptor ~ rasterData.interceptor;
parmClass: DeviceClass ~ rasterData.deviceParm.class;
IF rasterData.deviceClass # modifiedDeviceClass THEN ERROR; -- should not have called
IF interceptor.intercept = NIL THEN ERROR;
rasterData.deviceClass ← rasterData.deviceParm.class;
interceptor.intercept[interceptor, box, action, context !
UNWIND => rasterData.deviceClass ← modifiedDeviceClass
];
rasterData.deviceClass ← modifiedDeviceClass;
};
InterceptorInEffect: PUBLIC PROC [context: Context] RETURNS [BOOL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
RETURN [rasterData.deviceClass = modifiedDeviceClass]
};
SetInterceptor: PUBLIC PROC [context: Context, interceptor: Interceptor] RETURNS [old: Interceptor ← NIL] ~ {
state: State ~ context.state;
rasterData: RasterData ~ state.rasterData;
old ← rasterData.interceptor;
rasterData.interceptor ← interceptor;
rasterData.deviceClass ← IF interceptor = NIL THEN rasterData.deviceParm.class ELSE modifiedDeviceClass;
};
modifiedDeviceClass: DeviceClass ~ NEW [IIDevice.DeviceClassRep ← [
SetColor: ModifySetColor,
SetPriority: ModifySetPriority,
MaskBoxes: ModifyMaskBoxes,
MaskRegion: ModifyMaskRegion,
MaskBitmap: ModifyMaskBitmap,
MaskRawBitmaps: ModifyMaskRawBitmaps,
DrawBitmap: ModifyDrawBitmap,
MaskChar: ModifyMaskChar,
MoveBox: ModifyMoveBox,
DoBuffered: ModifyDoBuffered
]];
Raster Context Class Template
rasterClassTemplate: Class ~ NEW [ClassRep ← [
type: $RasterTemplate,
DoSave: IIState.StateDoSave,
SetInt: IIState.StateSetInt,
SetReal: IIState.StateSetReal,
SetT: IIState.StateSetT,
SetFont: IIState.StateSetFont,
SetColor: IIState.StateSetColor,
SetClipper: IIState.StateSetClipper,
GetInt: IIState.StateGetInt,
GetReal: IIState.StateGetReal,
GetT: IIState.StateGetT,
GetFont: IIState.StateGetFont,
GetColor: IIState.StateGetColor,
GetClipper: IIState.StateGetClipper,
ConcatT: IIState.StateConcatT,
Scale2T: IIState.StateScale2T,
RotateT: IIState.StateRotateT,
TranslateT: IIState.StateTranslateT,
Move: IIState.StateMove,
SetXY: IIState.StateSetXY,
SetXYRel: IIState.StateSetXYRel,
GetCP: IIState.StateGetCP,
StartUnderline: IIState.StateStartUnderline,
MaskUnderline: IIState.StateMaskUnderline,
CorrectMask: IIState.StateCorrectMask,
CorrectSpace: IIState.StateCorrectSpace,
Space: IIState.StateSpace,
SetCorrectMeasure: IIState.StateSetCorrectMeasure,
SetCorrectTolerance: IIState.StateSetCorrectTolerance,
Correct: IIState.StateCorrect,
DontCorrect: IIState.StateDontCorrect,
SetGray: IIState.StateSetGray,
SetSampledColor: IIState.StateSetSampledColor,
SetSampledBlack: IIState.StateSetSampledBlack,
Clip: IIState.StateClip,
ClipRectangle: IIState.StateClipRectangle,
ClipRectangleI: IIState.StateClipRectangleI,
Show: IIRasterShow.RasterShow,
MaskFill: RasterMaskFill,
MaskRectangle: RasterMaskRectangle,
MaskStroke: RasterMaskStroke,
MaskPixel: RasterMaskPixel,
ShowAndFixedXRel: IIPrivate.DefaultShowAndFixedXRel,
ShowText: IIPrivate.DefaultShowText,
MaskRectangleI: RasterMaskRectangleI,
MaskVector: RasterMaskVector,
MaskDashedStroke: IIPrivate.DefaultMaskDashedStroke,
MaskBitmap: RasterMaskBitmap,
DrawBitmap: RasterDrawBitmap,
DrawPixels: IIPrivate.DefaultDrawPixels,
DoIfVisible: IIPrivate.DefaultDoIfVisible,
DoWithBuffer: RasterDoWithBuffer,
DrawObject: IIPrivate.DefaultDrawObject,
GetBounds: RasterGetBounds,
ViewReset: RasterViewReset,
ViewTranslateI: RasterViewTranslateI,
ViewClip: RasterViewClip,
ViewClipRectangleI: RasterViewClipRectangleI,
Transform: IIPrivate.DefaultTransform,
GetTransformation: RasterGetTransformation,
MoveViewRectangle: RasterMoveViewRectangle,
TestViewRectangle: RasterTestViewRectangle,
propList: NIL
]];
END.