ImagerPDImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Michael Plass, March 6, 1987 7:02:52 pm PST
Doug Wyatt, May 30, 1985 10:32:41 pm PDT
Dave Rumph, September 20, 1985 9:41:20 pm PDT
Tim Diebert: April 25, 1986 11:45:19 am PST
Eric Nickell February 18, 1986 6:49:26 pm PST
Stone, February 11, 1987 12:04:24 pm PST
Last edited by: Mik Lamming - May 1, 1987 11:13:22 am PDT
Maureen Stone, June 4, 1987 1:23:53 pm PDT
DIRECTORY
Atom, Basics, ColorRegistry, --FontTune,-- FunctionCache, Imager, ImagerBackdoor, ImagerBrick, ImagerMaskCache, ImagerColor, ImagerColorFns, ImagerDevice, ImagerPD, ImagerPDPrivate, ImagerSample, ImagerPixel, ImagerRaster, ImagerTransformation, ImagerPDPublic, PDFileFormat, PDFileWriter, PrincOps, PrincOpsUtils, RefTab, Rope, Vector2, SF, ImagerPrivate, ImagerState, ImagerPath;
ImagerPDImpl: CEDAR PROGRAM
IMPORTS Atom, Basics, --FontTune,-- FunctionCache, Imager, ImagerBackdoor, ImagerBrick, ImagerMaskCache, ImagerPDPrivate, ImagerSample, ImagerPixel, ImagerRaster, ImagerColor, ImagerState, ImagerTransformation, PDFileWriter, RefTab, Rope, SF
EXPORTS ImagerPD, Imager
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
VEC: TYPE ~ Imager.VEC;
PathProc: TYPE ~ ImagerPath.PathProc;
Context: TYPE ~ Imager.Context;
CharMask: TYPE ~ ImagerMaskCache.CharMask;
Toner: TYPE ~ ImagerPDPublic.Toner;
Toners: TYPE ~ LIST OF ImagerPDPublic.Toner;
UCR: TYPE ~ ImagerPDPublic.UCR;
PrinterType: TYPE ~ ImagerPD.PrinterType;
Transformation: TYPE ~ ImagerTransformation.Transformation;
DeviceCode: TYPE ~ PDFileFormat.DeviceCode;
PD: TYPE ~ REF PDRep;
PDRep: PUBLIC TYPE ~ ImagerPDPrivate.PDRep;
ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep;
StateRep: PUBLIC TYPE ~ ImagerState.StateRep;
DeviceData: TYPE ~ ImagerPDPrivate.DeviceData;
DeviceDataRep: TYPE ~ ImagerPDPrivate.DeviceDataRep;
deviceClass: ImagerDevice.DeviceClass ~ NEW[ImagerDevice.DeviceClassRep ← [
SetColor: ImagerPDPrivate.SetColor,
SetPriority: SetPriority,
MaskBoxes: MaskBoxes,
MaskBitmap: MaskBitmap,
MaskChar: MaskChar
]];
SampleMap: TYPE ~ ImagerSample.SampleMap;
Tile: TYPE ~ ImagerPD.Tile;
bitsPerWord: NAT ~ Basics.bitsPerWord;
TonerSetFromToners: PROC [toners: Toners] RETURNS [tonerSet: PACKED ARRAY PDFileFormat.Toner OF BOOL] ~ {
tonerSet ← ALL[FALSE];
FOR t: Toners ← toners, t.rest UNTIL t=NIL DO
pdt: PDFileFormat.Toner ~ VAL[ORD[t.first]];
IF tonerSet[pdt] THEN Imager.Error[[$specification, "Multiple use of same toner in PD Create"]];
tonerSet[pdt] ← TRUE;
ENDLOOP;
};
rastWeight: REAL ← 1.7;
fontCacheSize: NAT ← 8000;
defaultDotShape: REAL ← 0.45;
defaultScreenAngle: ARRAY PDFileFormat.Toner[black..yellow] OF REAL ← [
black: 90,
cyan: 75,
magenta: 105,
yellow: 45
];
GetDefaultBricks: PROC [deviceData: DeviceData, pixelsPerHalftoneDot: REAL] ~ {
FOR t: PDFileFormat.Toner IN PDFileFormat.Toner DO
IF deviceData.tonerSet[t] THEN {
angle: REAL ~ IF t IN[black..yellow] THEN defaultScreenAngle[t] ELSE 0;
brick: ImagerBrick.Brick ~ ImagerBrick.BrickFromDotScreen[pixelsPerDot: pixelsPerHalftoneDot, degrees: angle, shape: defaultDotShape];
deviceData.halftoneBrick[t] ← NEW[Tile ← brick];
};
ENDLOOP;
};
CreateFromParameters: PUBLIC PROC [name: ROPE, deviceCode: CARDINAL,
deviceType: ATOM,
sResolution, fResolution: CARDINAL, -- pixels per inch
imageSSize, imageFSize: CARDINAL, -- pixels
toners: Toners, leftovers: BOOL, bandSSize: NAT, maxLoadWords: INT, fontTuning: ROPE, tonerUniverse: Toners ← NIL, pixelsPerHalftoneDot: REAL, ucr: UCR ← []]
RETURNS [PD] ~ {
writer: PDFileWriter.PDState ~ PDFileWriter.Create[
fileName: name,
deviceCode: VAL[deviceCode],
sResolution: sResolution,
fResolution: fResolution,
imageSSize: imageSSize,
imageFSize: imageFSize,
bandSSize: bandSSize,
leftOverMode: leftovers,
maxLoadWords: maxLoadWords
];
deviceData: DeviceData ~ NEW [DeviceDataRep ← [
deviceType: deviceType,
writer: writer,
sSize: imageSSize,
fSize: imageFSize,
feed: FALSE,
strip: FALSE,
imageStarted: FALSE,
priorityImportant: FALSE,
tonerSet: TonerSetFromToners[toners],
tonerUniverseSet: TonerSetFromToners[IF tonerUniverse # NIL THEN tonerUniverse ELSE toners],
toner: black,
ucr: ucr,
maskTab: RefTab.Create[997],
colorKind: nil,
tileTable: NEW[ImagerPDPrivate.TileTableRep[17]],
colorTile: [0, NIL, 0],
colorClear: FALSE,
sampledSource: NIL,
paToDevice: ImagerTransformation.Scale[0],
halftoneBrick: ALL[NIL],
pixelsRGB: ImagerPixel.ObtainScratchPixels[3, 1],
pixelsCMYK: ImagerPixel.ObtainScratchPixels[4, 1],
interpolate: FALSE
]];
sInches: REAL ~ REAL[imageSSize]/REAL[sResolution];
fInches: REAL ~ REAL[imageFSize]/REAL[fResolution];
fontCacheID: ATOM ~ IF Rope.Size[fontTuning] = 0 THEN $PD ELSE Atom.MakeAtom[fontTuning];
fontTuner: ImagerDevice.FontTuner ← -- IF Rope.Size[fontTuning] # 0 THEN FontTune.CreateFontTuner[fontTuning] ELSE -- NIL;
deviceParm: ImagerDevice.DeviceParm ← NEW[ImagerDevice.DeviceParmRep ← [
class: deviceClass,
sSize: imageSSize,
fSize: imageFSize,
scanMode: IF sInches>fInches THEN [slow: down, fast: right] ELSE [slow: right, fast: up],
Call the larger dimension y; see the Interpress standard, section 4.3.1.
surfaceUnitsPerInch: [sResolution, fResolution],
surfaceUnitsPerPixel: 1,
fontTuner: fontTuner,
fontCache: ImagerMaskCache.GetNamedCache[fontCacheID, fontCacheSize],
rastWeight: rastWeight
]];
context: Context ~ ImagerRaster.Create[class: contextClass, deviceParm: deviceParm, data: deviceData, pixelUnits: FALSE];
pd: PD ~ NEW [PDRep ← [
context: context,
toners: toners,
deviceData: deviceData
]];
IF deviceType=$raven384 OR deviceType=$versatec OR deviceType=$bw400 THEN {
Stash the MaskStroke and the MaskVector Procs.
black: ImagerColor.Color ← ImagerColor.Find["Xerox/Research/Distinct/Black"];
white: ImagerColor.Color ← ImagerColor.Find["Xerox/Research/Distinct/White"];
oldClass: REF ClassRep ← context.class;  --crack the opaque type
newClass: REF ClassRep ← NEW[ClassRep ← oldClass^];
ImagerRasterMaskVector ← oldClass.MaskVector;
ImagerRasterMaskStroke ← oldClass.MaskStroke;
newClass.MaskStroke ← MyMaskStroke;
newClass.MaskVector ← MyMaskVector;
Imager.PutProp[context, $DistinctBlack, black];
Imager.PutProp[context, $DistinctWhite, white];
context.class ← newClass; --replace the class with our local subclass
};
IF pixelsPerHalftoneDot <= 0 THEN pixelsPerHalftoneDot ← 2;
GetDefaultBricks[deviceData, pixelsPerHalftoneDot];
RETURN[pd];
};
The great, dashed line hack. In each of these procedures, the call to SetColor will be noticed by the call to Validate in ImagerRaster. The color has to be set at the conclusion of the call in case the next masks are areas.
ImagerRasterMaskVector: PROC [context: Context, p1, p2: VEC] ← NIL;
ImagerRasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ← NIL;
DoDash: PROC[context: Context] RETURNS[BOOLEAN] = {
state: REF StateRep ← context.state;
WITH state.color SELECT FROM
color: ImagerColor.SpecialColor => IF color.type=$Distinct THEN {
nameData: ColorRegistry.Data ← NARROW[color.data];
IF nameData.id#$Black AND nameData.id#$White AND nameData.id#$Gray THEN {
widthInPixels: REAL ← ImagerTransformation.TransformVec[state.T, [state.np.strokeWidth, 0]].x;
IF widthInPixels < 10 THEN RETURN[TRUE]; --define 10 device pixels as ok for texture
}};
ENDCASE;
RETURN[FALSE];
};
MyMaskVector: PROC [context: Context, p1, p2: Imager.VEC] = {
IF DoDash[context] THEN {
black: ImagerColor.Color ← NARROW[Imager.GetProp[context, $DistinctBlack]];
white: ImagerColor.Color ← NARROW[Imager.GetProp[context, $DistinctWhite]];
path: ImagerPath.PathProc = {moveTo[p1]; lineTo[p2]};
color: ImagerColor.SpecialColor ← NARROW[ImagerState.StateGetColor[context]];
ImagerState.StateSetColor[context, white];
ImagerRasterMaskVector[context, p1, p2];
ImagerState.StateSetColor[context, black];
MaskDashedStroke[context, color, path, FALSE];
ImagerState.StateSetColor[context, color];
}
ELSE ImagerRasterMaskVector[context, p1, p2];
};
MyMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] = {
IF DoDash[context] THEN {
black: ImagerColor.Color ← NARROW[Imager.GetProp[context, $DistinctBlack]];
white: ImagerColor.Color ← NARROW[Imager.GetProp[context, $DistinctWhite]];
color: ImagerColor.SpecialColor ← NARROW[ImagerState.StateGetColor[context]];
ImagerState.StateSetColor[context, white];
ImagerRasterMaskStroke[context, path, closed];
ImagerState.StateSetColor[context, black];
MaskDashedStroke[context, color, path, closed];
ImagerState.StateSetColor[context, color];
}
ELSE ImagerRasterMaskStroke[context, path, closed];
};
yellowDash: ARRAY [0..3] OF REAL ← [0,2.5,0,2.5];
redDash: ARRAY [0..3] OF REAL ← [3,2,3,2];
greenDash: ARRAY [0..3] OF REAL ← [1,2,1,2];
blueDash: ARRAY [0..3] OF REAL ← [4,2,1,2];
MaskDashedStroke: PROC [context: Context, color: ImagerColor.SpecialColor, path: PathProc, closed: BOOL] = {
deviceData: DeviceData ~ NARROW[context.data];
unit: REALSELECT deviceData.deviceType FROM
$raven384, $bw400 => 10, $versatec => 5, ENDCASE => 1;
array: ARRAY [0..3] OF REAL;
pattern: PROC[i: NAT] RETURNS[REAL] = {
v: VEC ← ImagerTransformation.InverseTransformVec[state.T, [array[i]*unit, 0]];
RETURN[v.x]};
state: REF StateRep ← context.state;
end: INT ← state.np.strokeEnd;
joint: INT ← state.np.strokeJoint;
nameData: ColorRegistry.Data ← NARROW[color.data];
Imager.SetStrokeEnd[context, round];
Imager.SetStrokeJoint[context, round];
array ← SELECT nameData.id FROM
$Red => redDash, $Green => greenDash,
$Blue => blueDash, $Yellow => yellowDash,
ENDCASE => ERROR;
Imager.MaskDashedStroke[context, path, 4, pattern, 0,0];
state.np.strokeEnd ← end;
state.np.strokeJoint ← joint;
};
Close: PUBLIC PROC [pd: PD] ~ {
PDFileWriter.Close[pd.deviceData.writer];
[] ← FunctionCache.Obtain[x: FunctionCache.GlobalCache[], compare: FunctionCache.Any, limit: INT.LAST, clientID: pd.deviceData]; -- Flush our stuff from the cache.
};
StartImage: PROC [deviceData: DeviceData] ~ {
tonerSet: PACKED ARRAY PDFileFormat.Toner OF BOOLALL[FALSE];
tonerSet[deviceData.toner] ← TRUE;
PDFileWriter.StartImage[pdState: deviceData.writer, toners: tonerSet, feed: deviceData.feed, strip: deviceData.strip];
deviceData.imageStarted ← TRUE;
};
identity: Transformation ~ ImagerTransformation.Scale[1];
DoPage: PUBLIC PROC [pd: PD, action: PROC [context: Context], pixelUnits: BOOLFALSE] ~ {
proc: PROC ~ {action[pd.context]};
littleRectangle: PROC ~ {
PD files don't work very well with empty separations.
Imager.SetPriorityImportant[pd.context, TRUE];
Imager.SetGray[pd.context, 0];
ImagerBackdoor.SetT[pd.context, identity];
Imager.MaskRectangleI[pd.context, 0, 0, 1, 1];
};
pd.deviceData.feed ← TRUE;
FOR t: Toners ← pd.toners, t.rest UNTIL t=NIL DO
tonerCard: CARDINAL ~ ORD[t.first];
toner: PDFileFormat.Toner ~ VAL[tonerCard];
pd.deviceData.toner ← toner;
pd.deviceData.imageStarted ← FALSE;
pd.deviceData.strip ← t.rest=NIL;
StartImage[pd.deviceData];
Imager.SetGray[pd.context, 1];
Imager.DoSaveAll[pd.context, littleRectangle];
Imager.DoSaveAll[pd.context, proc];
IF pd.deviceData.strip AND NOT pd.deviceData.imageStarted THEN {
Were we more clever, we would not generate StartImage for empty separations.
};
IF pd.deviceData.imageStarted THEN {
PDFileWriter.EndPage[pd.deviceData.writer];
pd.deviceData.feed ← FALSE;
};
ENDLOOP;
};
SetPriority: PROC [context: Context, priorityImportant: BOOL] ~ {
deviceData: DeviceData ~ NARROW[context.data];
deviceData.priorityImportant ← priorityImportant;
[] ← PDFileWriter.SetPriorityImportant[deviceData.writer, priorityImportant];
};
MaskBox: PROC [context: Context, box: SF.Box] ~ {
deviceData: DeviceData ~ NARROW[context.data];
SELECT deviceData.colorKind FROM
nil => ERROR; -- color not initialized
constant => {
PDFileWriter.MaskRectangle[deviceData.writer, box.min.s, box.min.f, box.max.s-box.min.s, box.max.f-box.min.f];
};
sampled => {
size: SF.Vec ~ SF.Size[box];
DeliverLines: PROC[p: PROC [LONG POINTER]] ~ {
buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f];
bits: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[max: [1, size.f]]];
brick: ImagerPD.Tile ~ deviceData.halftoneBrick[deviceData.toner]^;
Action: SAFE PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ {
buffer.length ← pixels.length;
ImagerSample.GetTileSamples[tile: brick.sampleMap, initIndex: min, buffer: buffer, phase: brick.phase];
ImagerSample.Halftone[map: bits, min: [0, 0], sampleBuffer: pixels[0], thresholdBuffer: buffer, function: [null, complement]];
TRUSTED { p[ImagerSample.GetBase[bits].word] };
};
Boxes: SF.BoxGenerator ~ {boxAction[box]};
ImagerPixel.Resample[self: deviceData.sampledSource, m: deviceData.paToDevice, interpolate: deviceData.interpolate, boxes: Boxes, bounds: box, action: Action];
ImagerSample.ReleaseScratchSamples[buffer];
ImagerSample.ReleaseScratchMap[bits];
};
PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner,
sMin: box.min.s, fMin: box.min.f, sSize: size.s, fSize: size.f, deliverProc: DeliverLines,
tFlag: opaque];
};
sampledBlack => {
size: SF.Vec ~ SF.Size[box];
DeliverLines: PROC[p: PROC [LONG POINTER]] ~ {
bits: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[max: [1, size.f]]];
Action: PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ {
ImagerSample.PutSamples[map: bits, buffer: pixels[0]];
TRUSTED { p[ImagerSample.GetBase[bits].word] };
};
Boxes: SF.BoxGenerator ~ {boxAction[box]};
ImagerPixel.Resample[self: deviceData.sampledSource, m: deviceData.paToDevice, interpolate: deviceData.interpolate, boxes: Boxes, bounds: box, action: Action];
ImagerSample.ReleaseScratchMap[bits];
};
PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner,
sMin: box.min.s, fMin: box.min.f, sSize: size.s, fSize: size.f, deliverProc: DeliverLines,
tFlag: IF deviceData.colorClear THEN transparent ELSE opaque];
};
tile => {
tFlag: PDFileWriter.TFlag ~ IF deviceData.colorClear THEN transparent ELSE opaque;
deliverLines: PROC[p: PROC [LONG POINTER]] ~ TRUSTED {
FOR s: INTEGER IN [box.min.s..box.max.s) DO
line: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: [min: [s, box.min.f], max: [s+1, box.max.f]]];
ImagerSample.Tile[map: line, tile: deviceData.colorTile.sampleMap, phase: deviceData.colorTile.phase];
TRUSTED {p[ImagerSample.GetBase[line].word]};
ImagerSample.ReleaseScratchMap[line];
ENDLOOP;
};
PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner, sMin: box.min.s, fMin: box.min.f, sSize: box.max.s-box.min.s, fSize: box.max.f-box.min.f, deliverProc: deliverLines, tFlag: tFlag];
};
ENDCASE => ERROR; -- illegal case
};
useLoadForChars: BOOLTRUE;
GetLoadEntry: PROC [deviceData: DeviceData, mask: CharMask] RETURNS [REF INT] ~ {
refInt: REF INTNARROW[RefTab.Fetch[deviceData.maskTab, mask].val];
load: INT ← 0;
IF refInt#NIL THEN RETURN [refInt];
IF NOT useLoadForChars THEN RETURN [NIL];
WITH mask SELECT FROM
raster: REF ImagerMaskCache.CharMaskRep.raster => {
size: SF.Vec ~ SF.Size[raster.box];
loadWords: INT ~ SIZE[PDFileFormat.SampleArray] + Basics.LongMult[size.s, (size.f+bitsPerWord-1)/bitsPerWord];
IF loadWords > PDFileWriter.RemainingLoadSize[deviceData.writer] THEN RETURN[NIL]; -- load full
TRUSTED {load ← PDFileWriter.LoadContiguousSampleArray[pdState: deviceData.writer, sSize: size.s, fSize: size.f, bitsPtr: @(raster[0])]};
};
runs: REF ImagerMaskCache.CharMaskRep.runs => {
Runs: PROC [run: PROC [sMin, fMin: INTEGER, fSize: NAT]] ~ {
s: INTEGER ← 0;
sMax: INTEGER ~ SF.SizeS[runs.box];
FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO
r: ImagerMaskCache.Run ~ runs[i];
IF r.fSize # 0 THEN run[s, r.fMin, r.fSize];
IF r.lastRun THEN s ← s + 1;
ENDLOOP;
};
loadWords: INT ~ SIZE[PDFileFormat.RunGroup] + runs.nRuns*SIZE[PDFileFormat.Run];
IF loadWords > PDFileWriter.RemainingLoadSize[deviceData.writer] THEN RETURN[NIL];
load ← PDFileWriter.LoadRunGroup[deviceData.writer, Runs];
};
ENDCASE => ERROR;
refInt ← NEW[INT ← load];
[] ← RefTab.Store[deviceData.maskTab, mask, refInt];
RETURN [refInt];
};
MaskChar: PROC [context: Context, delta: SF.Vec, mask: CharMask] ~ {
deviceData: DeviceData ~ NARROW[context.data];
loadEntry: REF INT ← GetLoadEntry[deviceData, mask];
sMin: NAT ~ delta.s+mask.box.min.s;
fMin: NAT ~ delta.f+mask.box.min.f;
IF deviceData.colorKind#constant THEN ERROR;
IF loadEntry#NIL
THEN {
load: LONG CARDINAL ~ loadEntry^;
SELECT mask.rep FROM
raster => PDFileWriter.MaskSamplesRef[deviceData.writer, load, sMin, fMin];
runs => PDFileWriter.MaskRunGroupRef[deviceData.writer, load, sMin, fMin];
ENDCASE => ERROR;
}
ELSE WITH mask SELECT FROM
raster: REF ImagerMaskCache.CharMaskRep.raster => {
bitmap: ImagerSample.SampleMap ~ ImagerMaskCache.BitmapFromCharMask[raster];
MaskBitmap[context, bitmap, delta, SF.Displace[box: mask.box, t: delta], NIL];
};
runs: REF ImagerMaskCache.CharMaskRep.runs => {
Runs: PROC [run: PDFileWriter.CaptureRunProc] ~ {
s: INTEGER ← sMin;
sMax: INTEGER ~ delta.s+mask.box.max.s;
FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO
r: ImagerMaskCache.Run ~ runs[i];
IF r.fSize # 0 THEN run[s, r.fMin+fMin, r.fSize];
IF r.lastRun THEN s ← s + 1;
ENDLOOP;
};
PDFileWriter.MaskRunGroup[deviceData.writer, Runs];
};
ENDCASE => ERROR;
};
MaskBoxes: PROC [context: Context, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
deviceData: DeviceData ~ NARROW[context.data];
SELECT deviceData.colorKind FROM
nil => ERROR; -- color not initialized
constant => {
constantBox: PROC [box: SF.Box] ~ {
PDFileWriter.MaskRectangle[deviceData.writer, box.min.s, box.min.f, box.max.s-box.min.s, box.max.f-box.min.f];
};
boxes[constantBox];
};
ENDCASE => {
hardBox: PROC[box: SF.Box] ~ { MaskBox[context, box] };
boxes[hardBox];
};
};
MaskBitmap: PROC [context: Context, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
deviceData: DeviceData ~ NARROW[context.data];
SELECT deviceData.colorKind FROM
nil => ERROR; -- color not initialized
constant => {
constantBox: PROC [box: SF.Box] ~ {
deliverLines: PROC [p: PROC [LONG POINTER]] ~ TRUSTED {
FOR s: INTEGER IN [box.min.s..box.max.s) DO
line: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: [min: [s, box.min.f], max: [s+1, box.max.f]]];
ImagerSample.Transfer[dst: line, src: bitmap, delta: delta];
TRUSTED {p[ImagerSample.GetBase[line].word]};
ImagerSample.ReleaseScratchMap[line];
ENDLOOP;
};
PDFileWriter.MaskSamples[pdState: deviceData.writer, sMin: box.min.s, fMin: box.min.f, sSize: box.max.s-box.min.s, fSize: box.max.f-box.min.f, deliverProc: deliverLines];
};
IF boxes = NIL THEN constantBox[bounds] ELSE boxes[constantBox];
};
ENDCASE => ERROR; -- SetColor should not have allowed bitmaps
};
contextClass: ImagerPrivate.Class ~ ImagerRaster.CreateClass[type: $PD, deviceClass: deviceClass];
END.