ImagerPDImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Michael Plass, September 18, 1985 4:05:51 pm PDT
Doug Wyatt, May 30, 1985 10:32:41 pm PDT
Dave Rumph, September 20, 1985 9:41:20 pm PDT
Tim Diebert: October 11, 1985 2:15:19 pm PDT
DIRECTORY
Atom USING [MakeAtom],
Basics,
Checksum,
FunctionCache,
Imager,
ImagerBackdoor USING [SetT],
ImagerBrick,
ImagerCache USING [GetNamedCache],
ImagerColor,
ImagerColorDefs,
ImagerColorOperator,
ImagerColorPrivate,
ImagerDevice,
ImagerMask,
ImagerPD,
ImagerPDPrivate,
ImagerPixelArrayDefs,
ImagerPixelMap,
ImagerPixelArray,
ImagerRaster USING [Create],
ImagerSample,
ImagerTransformation,
PDFileFormat,
PDFileWriter,
PrincOps,
PrincOpsUtils,
Real,
RefTab,
Rope USING [ROPE, Size],
Vector2;
ImagerPDImpl: CEDAR PROGRAM
IMPORTS Atom, Basics, Checksum, FunctionCache, Imager, ImagerBackdoor, ImagerBrick, ImagerCache, ImagerColor, ImagerMask, ImagerColorOperator, ImagerRaster, ImagerPixelMap, ImagerSample, ImagerTransformation, PDFileWriter, Real, RefTab, Rope, ImagerPixelArray, PrincOpsUtils
EXPORTS ImagerPD, ImagerColorDefs
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
Context: TYPE ~ Imager.Context;
CharMask: TYPE ~ ImagerDevice.CharMask;
Toners: TYPE ~ LIST OF ImagerPD.Toner;
PrinterType: TYPE ~ ImagerPD.PrinterType;
ColorSeparationProc: TYPE ~ ImagerPD.ColorSeparationProc;
SpecialSeparationProc: TYPE ~ ImagerPD.SpecialSeparationProc;
Color: TYPE ~ ImagerColorDefs.Color;
ConstantColor: TYPE ~ ImagerColorDefs.ConstantColor;
SampledColor: TYPE ~ ImagerColorDefs.SampledColor;
ColorOperator: TYPE ~ ImagerColorDefs.ColorOperator;
Device: TYPE ~ ImagerDevice.Device;
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
PixelArray: TYPE ~ ImagerPixelArrayDefs.PixelArray;
Tile: TYPE ~ ImagerPixelMap.Tile;
Transformation: TYPE ~ ImagerTransformation.Transformation;
DeviceCode: TYPE ~ PDFileFormat.DeviceCode;
Sample: TYPE ~ ImagerSample.Sample;
SampleBuffer: TYPE ~ ImagerSample.SampleBuffer;
UnsafeSamples: TYPE ~ ImagerSample.UnsafeSamples;
VEC: TYPE ~ Vector2.VEC;
ConstantColorImpl: TYPE ~ REF ConstantColorImplRep;
ConstantColorImplRep: PUBLIC TYPE ~ ImagerColorPrivate.ConstantColorImplRep;
export to ImagerColorDefs.ConstantColorImplRep
PD: TYPE ~ REF PDRep;
PDRep: PUBLIC TYPE ~ ImagerPDPrivate.PDRep;
ColorKind: TYPE ~ ImagerPDPrivate.ColorKind;
DeviceData: TYPE ~ ImagerPDPrivate.DeviceData;
DeviceDataRep: TYPE ~ ImagerPDPrivate.DeviceDataRep;
TileEntry: TYPE ~ ImagerPDPrivate.TileEntry;
CreateTile: PUBLIC PROC [pixelMap: PixelMap, phase: INTEGER ← 0, copy: BOOLTRUE] RETURNS [tile: Tile] ~ {
IF copy OR pixelMap.sMin#0 OR pixelMap.fMin#0 THEN pixelMap ← ImagerPixelMap.Copy[pixelMap];
tile ← [sOrigin: pixelMap.sOrigin, fOrigin: pixelMap.fOrigin, sSize: pixelMap.sSize, fSize: pixelMap.fSize, phase: Mod[phase, pixelMap.fSize], refRep: pixelMap.refRep];
WHILE tile.sOrigin < 0 DO
tile.sOrigin ← tile.sOrigin + tile.sSize;
tile.fOrigin ← tile.fOrigin + tile.phase;
ENDLOOP;
WHILE tile.fOrigin < 0 DO
tile.fOrigin ← tile.fOrigin + tile.fSize;
ENDLOOP;
};
CopyTile: PROC [tile: Tile] RETURNS [Tile] ~ {
RETURN [CreateTile[[tile.sOrigin, tile.fOrigin, 0, 0, tile.sSize, tile.fSize, tile.refRep], tile.phase, TRUE]];
};
CreateFromPrinterType: PUBLIC PROC [name: ROPE, printerType: PrinterType, toners: Toners ← NIL, tonerUniverse: Toners ← NIL, pixelsPerHalftoneDot: REAL] RETURNS [PD] ~ {
SELECT printerType FROM
raven300 => RETURN[CreateFromParameters[name: name,
deviceCode: DeviceCode[raven].ORD,
sResolution: 300, fResolution: 300,
imageSSize: 300*8+300/2, imageFSize: 300*11, -- 8.5 by 11 inches
toners: IF toners # NIL THEN toners ELSE LIST[black], leftovers: TRUE, bandSSize: 16, maxLoadWords: 60000, fontTuning: NIL, tonerUniverse: tonerUniverse, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
raven384 => RETURN[CreateFromParameters[name: name,
deviceCode: DeviceCode[hornet].ORD,
sResolution: 384, fResolution: 384,
imageSSize: 384*8+384/2, imageFSize: 384*11, -- 8.5 by 11 inches
toners: IF toners # NIL THEN toners ELSE LIST[black], leftovers: TRUE, bandSSize: 16, maxLoadWords: 60000, fontTuning: NIL, tonerUniverse: tonerUniverse, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
plateMaker => RETURN[CreateFromParameters[name: name,
deviceCode: DeviceCode[mig].ORD,
sResolution: 1200, fResolution: 1200,
imageSSize: 13800, imageFSize: 11400, -- 11.5 by 9.5 inches
toners: IF toners # NIL THEN toners ELSE LIST[black], leftovers: TRUE, bandSSize: 16, maxLoadWords: 60000, fontTuning: NIL, tonerUniverse: tonerUniverse, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
c150 => RETURN[CreateFromParameters[name: name,
deviceCode: PrinterType[c150].ORD,
sResolution: 120, fResolution: 120,
imageSSize: 1320, imageFSize: 1020,
toners: LIST[black, cyan, magenta, yellow], leftovers: FALSE, bandSSize: 48, maxLoadWords: 60000, fontTuning: NIL, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
color400 => RETURN[CreateFromParameters[name: name,
deviceCode: PrinterType[color400].ORD,
sResolution: 400, fResolution: 400,
imageSSize: 5500, imageFSize: 4096,
toners: LIST[yellow, magenta, cyan, black], leftovers: FALSE, bandSSize: 50, maxLoadWords: 100000, fontTuning: NIL, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
puffin => RETURN[CreateFromParameters[name: name,
deviceCode: DeviceCode[puffin].ORD,
sResolution: 384, fResolution: 384,
imageSSize: 384*11, imageFSize: 384*8+384/2, -- 11 by 8.5 inches
toners: IF toners # NIL THEN toners ELSE LIST[cyan, magenta, yellow], leftovers: TRUE, bandSSize: 16, maxLoadWords: 60000, fontTuning: NIL, tonerUniverse: tonerUniverse, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
versatec => RETURN[CreateFromParameters[name: name,
deviceCode: DeviceCode[colorVersatec].ORD,
sResolution: 200, fResolution: 200,
imageSSize: 200*163, imageFSize: 200*40, -- 163 by 40 inches
toners: IF toners # NIL THEN toners ELSE LIST[black], leftovers: FALSE, bandSSize: 64, maxLoadWords: 100000, fontTuning: NIL, tonerUniverse: tonerUniverse, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
colorVersatec => RETURN[CreateFromParameters[name: name,
deviceCode: DeviceCode[colorVersatec].ORD,
sResolution: 200, fResolution: 200,
imageSSize: 200*163, imageFSize: 200*40, -- 163 by 40 inches
toners: LIST[black, cyan, magenta, yellow], leftovers: FALSE, bandSSize: 64, maxLoadWords: 100000, fontTuning: NIL, pixelsPerHalftoneDot: pixelsPerHalftoneDot]];
ENDCASE => ERROR Imager.Error[[$unimplemented, "Unknown printer type."]];
};
CreateDevice: PROC [deviceData: DeviceData, sSize, fSize, sPixelsPerInch, fPixelsPerInch: CARDINAL] RETURNS [Device] ~ {
sInches: REAL ~ REAL[sSize]/REAL[sPixelsPerInch];
fInches: REAL ~ REAL[fSize]/REAL[fPixelsPerInch];
surfaceToDevice: Transformation ~ ImagerTransformation.Scale[1];
xPixelsPerInch: CARDINAL ← sPixelsPerInch;
yPixelsPerInch: CARDINAL ← fPixelsPerInch;
Call the larger dimension y; see the Interpress standard, section 4.3.1.
IF sInches>fInches THEN { -- s (y) is top-to-bottom, f (x) is left-to-right
surfaceToDevice.ApplyPreTranslate[[sSize, 0]];
surfaceToDevice.ApplyPreRotate[90];
xPixelsPerInch ← fPixelsPerInch; yPixelsPerInch ← sPixelsPerInch;
}
ELSE NULL; -- s (x) is left-to-right, f (y) is bottom-to-top
RETURN[NEW[ImagerDevice.DeviceRep ← [
class: deviceClass,
box: [smin: 0, fmin: 0, smax: sSize, fmax: fSize],
surfaceToDevice: surfaceToDevice,
surfaceUnitsPerInch: [xPixelsPerInch, yPixelsPerInch],
surfaceUnitsPerPixel: 1,
data: deviceData
]]];
};
deviceClass: ImagerDevice.Class ~ NEW[ImagerDevice.ClassRep ← [
type: $PD,
SetColor: SetColor,
SetPriority: SetPriority,
SetHalftone: SetHalftone,
MaskRuns: MaskRuns,
MaskBoxes: MaskBoxes,
MaskBits: MaskBits,
MaskChar: MaskChar
]];
TonerSetFromToners: PROC [toners: Toners] RETURNS [tonerSet: PACKED ARRAY PDFileWriter.Toner OF BOOL] ~ {
tonerSet ← ALL[FALSE];
FOR t: Toners ← toners, t.rest UNTIL t=NIL DO
pdt: PDFileWriter.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;
nilTile: Tile ~ [0,0,0,0,0,NIL];
nilPixelMap: PixelMap ~ [0,0,0,0,0,0,NIL];
TileFromBrick: PROC [brick: ImagerBrick.Brick] RETURNS [tile: Tile] ~ {
pm: PixelMap ← ImagerPixelMap.Create[4, [0, 0, brick.sSize, brick.fSize]];
max: CARDINAL ~ Basics.BITSHIFT[1, Basics.BITSHIFT[1, pmLgBitsPerSample]] - 1;
FOR s: NAT IN[0..brick.sSize) DO
FOR f: NAT IN[0..brick.fSize) DO
r: REAL ~ ImagerBrick.GetElement[brick, s, f];
sample: Sample ~ Real.Round[r*max];
pm.PutPixel[s, f, sample];
ENDLOOP;
ENDLOOP;
tile ← CreateTile[pm, brick.phase, FALSE];
};
defaultScreenAngle: ARRAY PDFileWriter.Toner[black..yellow] OF REAL ← [
black: 45,
cyan: 75,
magenta: 105,
yellow: 90
];
GetDefaultBricks: PROC [deviceData: DeviceData, pixelsPerHalftoneDot: REAL] ~ {
FOR t: PDFileWriter.Toner IN PDFileWriter.Toner DO
IF deviceData.tonerSet[t] THEN {
angle: REAL ~ IF t IN[black..yellow] THEN defaultScreenAngle[t] ELSE 0;
brick: ImagerBrick.Brick ~ ImagerBrick.NewBrick[freq: pixelsPerHalftoneDot, angle: angle, filter: ImagerBrick.DotScreen];
deviceData.halftoneBrick[t] ← NEW[Tile ← TileFromBrick[brick]];
};
ENDLOOP;
};
CreateFromParameters: PUBLIC PROC [name: ROPE, deviceCode: CARDINAL,
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]
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 ← [
writer: writer,
sSize: imageSSize,
fSize: imageFSize,
feed: FALSE,
strip: FALSE,
imageStarted: FALSE,
tonerSet: TonerSetFromToners[toners],
tonerUniverseSet: TonerSetFromToners[IF tonerUniverse # NIL THEN tonerUniverse ELSE toners],
toner: black,
maskTab: RefTab.Create[997],
colorKind: nil,
tileTable: NEW[ImagerPDPrivate.TileTableRep[17]],
colorTile: nilTile,
colorClear: FALSE,
sampledSource: nilPixelMap,
paToDevice: ImagerTransformation.Scale[0],
scratchPM: NIL,
sampler: NEW[ImagerSample.SamplerRep ← []],
bitBuffer: ImagerSample.NewBuffer[1, (imageFSize+bitsPerWord-1)/bitsPerWord + 1],
sampleBuffer: ImagerSample.NewBuffer[1, imageFSize],
brickBuffer: ImagerSample.NewBuffer[1, MAX[imageFSize, 100]],
halftoneBrick: ALL[NIL]
]];
device: Device ~ CreateDevice[deviceData: deviceData, sSize: imageSSize, fSize: imageFSize, sPixelsPerInch: sResolution, fPixelsPerInch: fResolution];
fontCacheID: ATOM ~ IF Rope.Size[fontTuning] = 0 THEN $PD ELSE Atom.MakeAtom[fontTuning];
context: Context ~ ImagerRaster.Create[device: device, pixelUnits: FALSE, fontCache: ImagerCache.GetNamedCache[fontCacheID, fontCacheSize], rastWeight: rastWeight];
pd: PD ~ NEW [PDRep ← [
context: context,
toners: toners,
deviceData: deviceData
]];
GetDefaultBricks[deviceData, pixelsPerHalftoneDot];
RETURN[pd];
};
Close: PUBLIC PROC [pd: PD] ~ {
PDFileWriter.Close[pd.deviceData.writer];
};
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.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 [device: Device, priorityImportant: BOOL] ~ {
deviceData: DeviceData ~ NARROW[device.data];
[] ← PDFileWriter.SetPriorityImportant[deviceData.writer, priorityImportant];
};
SetHalftone: PROC[device: Device, halftone: ImagerDevice.HalftoneParameters] ~ {
deviceData: DeviceData ~ NARROW[device.data];
};
Check: PROC [x: INTEGER, max: NAT] RETURNS[NAT] ~
TRUSTED MACHINE CODE { PrincOps.zINC; PrincOps.zBNDCK };
IF x IN[0..max] THEN RETURN[x] ELSE ERROR RuntimeError.BoundsFault
TileHash: PROC [tile: Tile] RETURNS [hash: CARDINAL] ~ TRUSTED {
p: LONG POINTER ← tile.refRep.pointer;
count: LONG CARDINAL ← Basics.LongMult[tile.refRep.rast, tile.sSize];
IF tile.refRep.lgBitsPerPixel # 0 OR tile.refRep.rast # NAT[tile.fSize+bitsPerWord-1]/bitsPerWord THEN ERROR;
hash ← 0;
UNTIL count = 0 DO
nWords: CARDINAL ~ MIN[count, NAT.LAST];
hash ← Checksum.ComputeChecksum[cs: hash, nWords: nWords, p: p];
p ← p + nWords;
count ← count - nWords;
ENDLOOP;
};
TileEqual: PROC [a, b: Tile] RETURNS [BOOL] ~ {
IF a.sOrigin = b.sOrigin
AND a.fOrigin = b.fOrigin
AND a.sSize = b.sSize
AND a.fSize = b.fSize
AND a.phase = b.phase
AND a.refRep.lgBitsPerPixel = b.refRep.lgBitsPerPixel
AND a.refRep.rast = b.refRep.rast
THEN TRUSTED {
ap: LONG POINTER TO WORD ← a.refRep.pointer;
bp: LONG POINTER TO WORD ← b.refRep.pointer;
FOR i: LONG CARDINAL IN [0..Basics.LongMult[a.refRep.rast, a.refRep.lines]) DO
IF (ap+i)^ # (bp+i)^ THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE];
}
ELSE RETURN [FALSE];
};
bitsPerWord: NAT ~ Basics.bitsPerWord;
PutTileIntoLoad: PROC [writer: PDFileWriter.PDState, tile: Tile] RETURNS [LONG CARDINAL] ~ TRUSTED {
IF tile.refRep.lgBitsPerPixel # 0 OR NAT[tile.refRep.rast] # (tile.fSize+bitsPerWord-1)/bitsPerWord THEN ERROR;
RETURN [PDFileWriter.LoadContiguousColorTile[pdState: writer, phase: tile.phase, sMin: tile.sOrigin, fMin: tile.fOrigin, sSize: tile.sSize, fSize: tile.fSize, bitsPtr: tile.refRep.pointer]];
};
TileLoadWords: PROC [tile: Tile] RETURNS [INT] ~ {
RETURN [SIZE[PDFileFormat.Tile] + Basics.LongMult[tile.refRep.rast, tile.sSize]];
};
SetColorFromTile: PROC [deviceData: DeviceData, tile: Tile, clear: BOOL, copy: BOOL] ~ {
hash: CARDINAL ~ TileHash[tile];
bucket: NAT ~ hash MOD deviceData.tileTable.mod;
loadIndex: LONG CARDINAL ← 0;
found: BOOLFALSE;
FOR t: LIST OF TileEntry ← deviceData.tileTable[bucket], t.rest UNTIL (found OR t = NIL) DO
IF t.first.hash = hash AND TileEqual[tile, t.first.tile] THEN {
found ← TRUE;
loadIndex ← t.first.loadIndex;
};
ENDLOOP;
IF NOT found AND TileLoadWords[tile] <= PDFileWriter.RemainingLoadSize[deviceData.writer] THEN {
loadIndex ← PutTileIntoLoad[deviceData.writer, tile];
deviceData.tileTable[bucket] ← CONS[[loadIndex, hash, IF copy THEN CopyTile[tile] ELSE tile], deviceData.tileTable[bucket]];
found ← TRUE;
};
IF found THEN {
PDFileWriter.SetColorTile[pdState: deviceData.writer, toner: deviceData.toner, tileRef: loadIndex, tFlag: (IF clear THEN transparent ELSE opaque)];
deviceData.colorKind ← constant;
deviceData.colorTile.refRep ← NIL;
}
ELSE {
deviceData.colorTile ← (IF copy THEN CopyTile[tile] ELSE tile);
deviceData.colorClear ← clear;
deviceData.colorKind ← tile;
IF clear THEN PDFileWriter.SetColorInk[deviceData.writer, deviceData.toner]
ELSE PDFileWriter.NewPriorityLevel[pdState: deviceData.writer, toner: deviceData.toner];
};
};
UniformColor: TYPE ~ {ink, noInk, off};
SetColorUniform: PROC [deviceData: DeviceData, u: UniformColor] ~ {
SELECT u FROM
ink => PDFileWriter.SetColorInk[deviceData.writer, deviceData.toner];
noInk => PDFileWriter.SetColorClear[deviceData.writer, deviceData.toner];
off => PDFileWriter.SetColorOff[deviceData.writer, deviceData.toner];
ENDCASE => NULL;
deviceData.colorKind ← constant;
};
SetColorFromFraction: PROC [deviceData: DeviceData, f: REAL] ~ {
SELECT f FROM
>= 0.9999 => { -- all toner
SetColorUniform[deviceData, ink]
};
<= 0.0001 => { -- no toner
SetColorUniform[deviceData, noInk]
};
ENDCASE => {
tile: REF Tile ~ deviceData.halftoneBrick[deviceData.toner];
pm: PixelMap ← deviceData.scratchPM.Reshape[0, [tile.sOrigin, tile.fOrigin, tile.sSize, tile.fSize]];
brickBuffer: SampleBuffer ~ deviceData.brickBuffer;
max: CARDINAL ~ Basics.BITSHIFT[1, Basics.BITSHIFT[1, pmLgBitsPerSample]] - 1;
samp: CARDINAL ← Real.Round[(1.0-f)*max];
ImagerPixelMap.Clear[pm];
FOR s: NAT IN [0..tile.sSize) DO
GetBrickSamples[tile: tile, s: s+tile.sOrigin, f: tile.fOrigin, buffer: brickBuffer, bi: 0, bj: 0, count: tile.fSize];
FOR j: NAT IN [0..tile.fSize) DO
brickBuffer[j] ← samp-brickBuffer[j];
ENDLOOP;
TRUSTED {
samples: UnsafeSamples ~ brickBuffer.GetPointer[0, 0, pm.fSize];
ImagerSample.UnsafePutFSign[samples: samples, count: pm.fSize, s: s, f: 0, base: pm.refRep.pointer, wordsPerLine: pm.refRep.rast];
};
ENDLOOP;
SetColorFromTile[deviceData: deviceData, tile: CreateTile[pixelMap: pm, phase: tile.phase, copy: FALSE], clear: FALSE, copy: TRUE];
deviceData.scratchPM ← pm.refRep;
};
};
UCR: TYPE ~ RECORD [
blackGamma: REAL,
blackThreshold: REAL,
removedFraction: REAL
];
ucr: UCR ← [
blackGamma: 0.7,
blackThreshold: 0.1,
removedFraction: 0.0
];
SetColorFromRGB: PROC [deviceData: DeviceData, rgb: ImagerColor.RGB, Y: REAL] ~ {
IF deviceData.tonerUniverseSet[cyan] AND deviceData.tonerUniverseSet[magenta] AND deviceData.tonerUniverseSet[yellow] THEN {
minCMY: REAL ← 1.0 - MAX[MAX[rgb.R, rgb.G], rgb.B];
IF deviceData.tonerUniverseSet[black] AND minCMY >= ucr.blackThreshold THEN {
Do Undercolor removal (UCR)
SELECT deviceData.toner FROM
cyan => SetColorFromFraction[deviceData, (1.0 - rgb.R) - ((minCMY - ucr.blackThreshold) * ucr.removedFraction)];
magenta => SetColorFromFraction[deviceData, (1.0 - rgb.G) - ((minCMY - ucr.blackThreshold) * ucr.removedFraction)];
yellow => SetColorFromFraction[deviceData, (1.0 - rgb.B) - ((minCMY - ucr.blackThreshold) * ucr.removedFraction)];
black => SetColorFromFraction[deviceData, (minCMY - ucr.blackThreshold) * ucr.blackGamma];
ENDCASE => SetColorUniform[deviceData, noInk];
}
ELSE SELECT deviceData.toner FROM
cyan => SetColorFromFraction[deviceData, 1.0 - rgb.R];
magenta => SetColorFromFraction[deviceData, 1.0 - rgb.G];
yellow => SetColorFromFraction[deviceData, 1.0 - rgb.B];
ENDCASE => SetColorUniform[deviceData, noInk];
}
ELSE IF deviceData.tonerUniverseSet[black] THEN {
SetColorFromFraction[deviceData, 1.0 - Y];
}
ELSE ERROR;
};
UnimplementedColor: PUBLIC SIGNAL [color: Color] ~ CODE;
FloorMod4: PROC [r: REAL] RETURNS [NAT] ~ {
WHILE r<0 DO r ← r + 4.0 ENDLOOP;
WHILE r>=1024.0 DO r ← r - 1024.0 ENDLOOP;
RETURN [Real.Fix[r] MOD 4];
};
SetColor: PROC [device: Device, color: Color, viewToDevice: Transformation] ~ {
deviceData: DeviceData ~ NARROW[device.data];
writer: PDFileWriter.PDState ~ deviceData.writer;
deviceData.colorKind ← nil;
WITH color SELECT FROM
color: ConstantColor => {
impl: ConstantColorImpl ~ color.impl;
WITH impl: impl SELECT FROM
stipple => {
word: CARDINAL ~ impl.word;
clear: BOOL ~ impl.function = paint;
pm: PixelMap ← deviceData.scratchPM.Reshape[0, [0, 0, 4, 4]];
w: CARDINAL ← word;
SELECT impl.function FROM
replace, paint => NULL;
ENDCASE => SIGNAL UnimplementedColor[color];
FOR y: NAT DECREASING IN [0..4) DO
FOR x: NAT IN [0..4) DO
bit: [0..1] ← w/(CARDINAL[256]*128);
sf: VEC ~ ImagerTransformation.Transform[viewToDevice, [x+0.5, y+0.5]];
s: NAT ~ FloorMod4[sf.x];
f: NAT ~ FloorMod4[sf.y];
pm.PutPixel[s, f, w/(CARDINAL[256]*128)];
w ← (w - bit*(CARDINAL[256]*128))*2;
ENDLOOP;
ENDLOOP;
SetColorFromTile[deviceData, ImagerPixelMap.CreateTile[pm, 0], clear, FALSE];
deviceData.scratchPM ← pm.refRep;
};
gray => {
IF deviceData.tonerSet[black] THEN {
SetColorFromFraction[deviceData, IF deviceData.toner = black THEN impl.f ELSE 0.0];
}
ELSE SetColorFromRGB[deviceData, [impl.Y, impl.Y, impl.Y], impl.Y];
};
rgb => {
SetColorFromRGB[deviceData, impl.val, impl.Y];
};
cie => {
SetColorFromRGB[deviceData, ImagerColor.RGBFromCIE[impl.val], impl.Y];
};
ENDCASE => ERROR;
};
color: SampledColor => {
pa: PixelArray ~ color.pa;
um: Transformation ~ color.um;
colorOperator: ColorOperator ~ color.colorOperator;
fullColor: BOOL ~ deviceData.tonerUniverseSet[cyan] AND deviceData.tonerUniverseSet[magenta] AND deviceData.tonerUniverseSet[yellow];
ImagerTransformation.ApplyCat[deviceData.paToDevice, pa.m, color.um, viewToDevice];
SELECT ImagerColorOperator.GetColorOperatorClass[colorOperator] FROM
$SampledBlack => SetColorFromSampledBlack[device, pa, FALSE];
$SampledBlackClear => SetColorFromSampledBlack[device, pa, TRUE];
$GrayLinear, $GrayDensity, $GrayVisual => {
IF deviceData.tonerUniverseSet[black] AND deviceData.toner # black THEN {
SetColorUniform[deviceData, noInk];
}
ELSE SetColorFromPixelArray[device, pa, colorOperator, $Intensity];
};
ENDCASE => {
IF fullColor THEN {
component: ATOM ~ SELECT deviceData.toner FROM
cyan => $Red,
magenta => $Green,
yellow => $Blue,
ENDCASE => NIL;
IF component=NIL THEN {
SetColorUniform[deviceData, noInk];
}
ELSE SetColorFromPixelArray[device, pa, colorOperator, component];
}
ELSE {
SetColorFromPixelArray[device, pa, colorOperator, $Intensity];
};
};
};
ENDCASE => ERROR;
};
me: REF TEXT ~ "PD"; -- a globally unique REF for use as a clientID in the global cache.
SetColorFromSampledBlack: PROC [device: Device, pa: PixelArray, clear: BOOL] ~ {
deviceData: DeviceData ~ NARROW[device.data];
IF deviceData.tonerUniverseSet[black] AND deviceData.toner # black THEN {
SetColorUniform[deviceData, IF clear THEN off ELSE noInk];
}
ELSE {
cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[];
compare: FunctionCache.CompareProc ~ {RETURN [argument=pa]};
scd: REF PixelMap ← NARROW[FunctionCache.Lookup[cache, compare, me].value];
IF scd = NIL THEN TRUSTED {
source: ImagerPixelMap.PixelMap ~ ImagerPixelMap.Create[
lgBitsPerPixel: 0, bounds: [sMin: 0, fMin: 0, sSize: pa.sSize, fSize: pa.fSize]];
ImagerPixelMap.Clear[source];
ImagerPixelArray.UnsafeGetBits[pa: pa, i: 0, s: 0, f: 0, dst: [word: source.refRep.pointer, bit: 0], dstBpl: source.refRep.rast*bitsPerWord, width: source.fSize, height: source.sSize];
scd ← NEW[PixelMap ← source];
FunctionCache.Insert[cache, pa, scd, source.refRep.words, me];
};
deviceData.sampledSource ← scd^;
IF deviceData.paToDevice.form = 3 AND deviceData.paToDevice.integerTrans THEN {
SetColorFromTile[deviceData, CreateTile[scd^.ShiftMap[deviceData.paToDevice.tx, deviceData.paToDevice.ty], 0], clear, FALSE];
}
ELSE {
deviceData.colorKind ← sampledBlack;
deviceData.colorClear ← clear;
PDFileWriter.SetColorInk[deviceData.writer, deviceData.toner];
};
};
};
pmLgBitsPerSample: NAT ~ 3; -- for sampled color source
SetColorFromPixelArray: PROC [device: Device, pa: PixelArray, colorOperator: ColorOperator, component: ATOM] ~ {
deviceData: DeviceData ~ NARROW[device.data];
cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[];
compare: FunctionCache.CompareProc ~ {
WITH argument SELECT FROM
a: REF ARRAY [0..1] OF REF => RETURN [a[0]=pa AND a[1]=component];
ENDCASE => RETURN [FALSE];
};
scd: REF PixelMap ← NARROW[FunctionCache.Lookup[cache, compare, me].value];
IF scd = NIL THEN TRUSTED {
bitsPerSample: NAT ~ Basics.BITSHIFT[1, pmLgBitsPerSample];
maxSample: Sample ~ Basics.BITSHIFT[1, bitsPerSample]-1;
sSize: NAT ~ pa.sSize;
fSize: NAT ~ pa.fSize;
maxIn: Sample ~ ImagerPixelArray.MaxSampleValue[pa, 0];
pm: ImagerPixelMap.PixelMap ~ ImagerPixelMap.Create[
lgBitsPerPixel: pmLgBitsPerSample,
bounds: [sMin: 0, fMin: 0, sSize: sSize, fSize: fSize]];
pixels: SampleBuffer ~ ImagerSample.NewBuffer[pa.samplesPerPixel, fSize];
buffer: SampleBuffer ~ ImagerSample.NewBuffer[1, fSize];
mapper: ImagerColorOperator.Mapper ~ ImagerColorOperator.NewMapper[
op: colorOperator, component: component, maxIn: maxIn, maxOut: maxSample];
samples: UnsafeSamples ~ buffer.GetPointer[0, 0, fSize];
pmBase: LONG POINTER ~ pm.refRep.pointer;
pmWordsPerLine: NAT ~ pm.refRep.rast;
a: REF ARRAY [0..1] OF REFNEW[ARRAY [0..1] OF REF ← [pa, component]];
FOR s: NAT IN[0..sSize) DO
ImagerPixelArray.GetPixels[pa: pa, s: s, f: 0, buffer: pixels, count: fSize];
ImagerColorOperator.MapPixels[mapper: mapper, pixels: pixels,
buffer: buffer, count: fSize];
TRUSTED { ImagerSample.UnsafePutF[samples: samples, count: fSize,
base: pmBase, wordsPerLine: pmWordsPerLine,
bitsPerSample: bitsPerSample, s: s, f: 0] };
ENDLOOP;
scd ← NEW[ImagerPixelMap.PixelMap ← pm];
FunctionCache.Insert[cache, pa, scd, pm.refRep.words, me];
};
deviceData.sampledSource ← scd^;
deviceData.colorKind ← sampled;
PDFileWriter.NewPriorityLevel[pdState: deviceData.writer, toner: deviceData.toner];
};
DeviceBox: TYPE ~ ImagerDevice.DeviceBox;
DivMod: PROC [n: INTEGER, d: NAT] RETURNS [quotient: INTEGER, remainder: NAT] ~ {
Number-theoretic: 0 <= remainder < d, n = quotient*d + remainder
IF d#1 THEN {
nn: Basics.LongNumber ← [li[n]];
qq: Basics.LongNumber ← [lc[0]];
IF nn.li < 0 THEN {nn.highbits ← nn.highbits + d; -- qq.highbits ← CARDINAL.LAST --};
[quotient: qq.lowbits, remainder: remainder] ← Basics.LongDivMod[nn.lc, d];
quotient ← qq.li;
quotient ← LOOPHOLE[qq.lowbits];
}
ELSE RETURN [quotient: n, remainder: 0];
};
Mod: PROC [n: INT, d: NAT] RETURNS [remainder: NAT] ~ {
Number-theoretic: 0 <= remainder < d
IF d#1 THEN {
nn: Basics.LongNumber ← [li[n]];
WHILE nn.li < 0 DO nn.highbits ← nn.highbits + d ENDLOOP;
WHILE nn.highbits >= d DO nn.highbits ← nn.highbits - d ENDLOOP;
RETURN [Basics.LongDivMod[nn.lc, d].remainder];
}
ELSE RETURN [remainder: 0];
};
Mul: PROC [a: INTEGER, b: NAT] RETURNS [INT] ~ {
IF a >= 0 THEN RETURN [Basics.LongMult[a, b]]
ELSE RETURN [-INT[Basics.LongMult[-a, b]]];
};
cd: REF PixelMap ← NIL;
GetBrickSamples: PROC [tile: REF Tile, s, f: INTEGER, buffer: SampleBuffer, bi, bj, count: NAT] ~ TRUSTED {
samples: UnsafeSamples ~ buffer.GetPointer[bi, bj, count];
refRep: REF ImagerPixelMap.PixelMapRep ~ tile.refRep;
base: LONG POINTER ~ refRep.pointer;
wordsPerLine: NAT ~ refRep.rast;
bitsPerSample: ImagerSample.BitsPerSample ~ Basics.BITSHIFT[1, refRep.lgBitsPerPixel];
fSizeTile: NAT ~ tile.fSize;
initCount: NAT ~ MIN[fSizeTile, count];
b: INTEGER;
s0, f0, c0: NAT;
[quotient: b, remainder: s0] ← DivMod[s-tile.sOrigin, tile.sSize];
f0 ← Mod[f-tile.fOrigin-Mul[b, tile.phase], tile.fSize];
c0 ← MIN[initCount, tile.fSize-f0];
ImagerSample.UnsafeGetF[samples: samples, count: c0, s: s0, f: f0, base: base, wordsPerLine: wordsPerLine, bitsPerSample: bitsPerSample];
IF c0 < initCount THEN {
ImagerSample.UnsafeGetF[samples: samples+c0, count: initCount-c0, s: s0, f: 0, base: base, wordsPerLine: wordsPerLine, bitsPerSample: bitsPerSample];
};
IF count > fSizeTile THEN {
PrincOpsUtils.LongCopy[from: samples, nwords: count-fSizeTile, to: samples+fSizeTile];
};
IF cd # NIL THEN {
ImagerSample.UnsafePutF[samples: ImagerSample.GetPointer[buffer, 0, 0, cd.fSize], count: cd.fSize, s: Mod[s-cd.sOrigin, cd.sSize], f: 0, base: cd.refRep.pointer, wordsPerLine: cd.refRep.rast, bitsPerSample: Basics.BITSHIFT[1, cd.refRep.lgBitsPerPixel]];
};
};
MaskRuns: PROC[device: Device, bounds: DeviceBox, runs: PROC[ImagerDevice.RunProc]] ~ {
deviceData: DeviceData ~ NARROW[device.data];
SELECT deviceData.colorKind FROM
nil => ERROR; -- color not initialized
constant => {
PDFileWriter.MaskRunGroup[deviceData.writer, runs];
};
sampled, sampledBlack => TRUSTED {
binary: BOOL ~ deviceData.colorKind=sampledBlack;
tFlag: PDFileWriter.TFlag ~ IF binary AND deviceData.colorClear THEN transparent ELSE opaque;
sampler: ImagerSample.Sampler ~ deviceData.sampler;
sampleBuffer: SampleBuffer ~ deviceData.sampleBuffer;
brickBuffer: SampleBuffer ~ deviceData.brickBuffer;
bitBuffer: SampleBuffer ~ deviceData.bitBuffer;
bitBufferSize: NAT ~ bounds.fmax-bounds.fmin;
bitBufferPointer: LONG POINTER ~ bitBuffer.GetPointer[0, 0, (bitBufferSize+bitsPerWord-1)/bitsPerWord];
deliverLine: PROC[p: PROC [LONG POINTER]] ~ TRUSTED {p[bitBufferPointer]};
tile: REF Tile ~ deviceData.halftoneBrick[deviceData.toner];
pdSampledRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED {
ImagerSample.GetPointSamples[sampler: sampler, s: sMin, f: fMin,
buffer: sampleBuffer, bi: 0, bj: 0, count: fSize];
GetBrickSamples[tile: tile, s: sMin, f: fMin, buffer: brickBuffer, bi: 0, bj: 0, count: fSize];
ImagerSample.SubSamples[samples: brickBuffer, si: 0, sj: 0,
buffer: sampleBuffer, bi: 0, bj: 0, count: fSize];
TRUSTED {
samples: UnsafeSamples ~ sampleBuffer.GetPointer[0, 0, fSize];
bitBuffer[fSize/bitsPerWord] ← 0;
ImagerSample.UnsafePutFSign[samples: samples, count: Check[fSize, bitBufferSize],
s: 0, f: 0, base: bitBufferPointer, wordsPerLine: 0];
PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner,
sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize, deliverProc: deliverLine,
tFlag: tFlag];
};
};
pdSampledBlackRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED {
ImagerSample.GetPointSamples[sampler: sampler, s: sMin, f: fMin,
buffer: sampleBuffer, bi: 0, bj: 0, count: fSize];
TRUSTED {
samples: UnsafeSamples ~ sampleBuffer.GetPointer[0, 0, fSize];
bitBuffer[fSize/bitsPerWord] ← 0;
ImagerSample.UnsafePutF[samples: samples, count: Check[fSize, bitBufferSize],
s: 0, f: 0, base: bitBufferPointer, wordsPerLine: 0, bitsPerSample: 1];
PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner,
sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize, deliverProc: deliverLine,
tFlag: tFlag];
};
};
t: ImagerPixelMap.PixelMap ~ deviceData.sampledSource;
sampler.base ← t.refRep.pointer;
sampler.wordsPerLine ← t.refRep.rast;
sampler.bitsPerSample ← Basics.BITSHIFT[1, t.refRep.lgBitsPerPixel];
sampler.sMin ← t.sMin;
sampler.fMin ← t.fMin;
sampler.sSize ← t.sSize;
sampler.fSize ← t.fSize;
ImagerSample.SetSamplerIncrements[sampler, deviceData.paToDevice];
ImagerSample.SetSamplerPosition[sampler: sampler, m: deviceData.paToDevice, s: bounds.smin, f: bounds.fmin];
IF binary THEN runs[pdSampledBlackRun] ELSE runs[pdSampledRun];
};
tile => {
tFlag: PDFileWriter.TFlag ~ IF deviceData.colorClear THEN transparent ELSE opaque;
scratch: REF ImagerPixelMap.PixelMapRep ← deviceData.scratchPM;
pdTileRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
pm: PixelMap ~ ImagerPixelMap.Reshape[scratch, 0, [sMin, fMin, 1, fSize]];
deliverLine: PROC[p: PROC [LONG POINTER]] ~ TRUSTED {p[pm.refRep.pointer]};
ImagerPixelMap.TransferTile[pm, deviceData.colorTile];
PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner,
sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize, deliverProc: deliverLine,
tFlag: tFlag];
scratch ← pm.refRep;
};
runs[pdTileRun];
deviceData.scratchPM ← scratch;
};
ENDCASE => ERROR; -- illegal case
};
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];
WITH mask SELECT FROM
raster: REF ImagerDevice.CharMaskRep.raster => {
loadWords: INT ~ SIZE[PDFileFormat.SampleArray] + Basics.LongMult[raster.sSizeBB, (raster.fSizeBB+bitsPerWord-1)/bitsPerWord];
IF loadWords > PDFileWriter.RemainingLoadSize[deviceData.writer] THEN RETURN[NIL];
TRUSTED {load ← PDFileWriter.LoadContiguousSampleArray[pdState: deviceData.writer, sSize: raster.sSizeBB, fSize: raster.fSizeBB, bitsPtr: @(raster[0])]};
};
runs: REF ImagerDevice.CharMaskRep.runs => {
Runs: PROC [run: PROC [sMin, fMin: INTEGER, fSize: NAT]] ~ {
s: INTEGER ← 0;
sMax: INTEGER ~ runs.sSizeBB;
FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO
r: ImagerDevice.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 [device: Device, s, f: INTEGER, mask: CharMask] ~ {
deviceData: DeviceData ~ NARROW[device.data];
loadEntry: REF INT ← GetLoadEntry[deviceData, mask];
sMin: NAT ~ s+mask.sMinBB;
fMin: NAT ~ f+mask.fMinBB;
IF deviceData.colorKind=constant AND 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 ImagerDevice.CharMaskRep.raster => {
boxes: PROC[action: PROC[DeviceBox]] ~ {action[[smin: sMin, fmin: fMin, smax: sMin+mask.sSizeBB, fmax: fMin+mask.fSizeBB]]};
TRUSTED {MaskBits[device: device,
srcBase: @raster[0],
srcWordsPerLine: (raster.fSizeBB+(bitsPerWord-1))/bitsPerWord,
ts: sMin,
tf: fMin,
boxes: boxes
]};
};
runs: REF ImagerDevice.CharMaskRep.runs => {
Runs: PROC [run: ImagerDevice.RunProc] ~ {
s: INTEGER ← sMin;
sMax: INTEGER ~ sMin+runs.sSizeBB;
FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO
r: ImagerDevice.Run ~ runs[i];
IF r.fSize # 0 THEN run[s, r.fMin+fMin, r.fSize];
IF r.lastRun THEN s ← s + 1;
ENDLOOP;
};
MaskRuns[device: device, bounds: [smin: sMin, fmin: fMin, smax: sMin+mask.sSizeBB, fmax: fMin+mask.fSizeBB], runs: Runs];
};
ENDCASE => ERROR;
};
MaskBoxes: PROC[device: Device, bounds: DeviceBox, boxes: PROC[ImagerDevice.BoxProc]] ~ {
deviceData: DeviceData ~ NARROW[device.data];
SELECT deviceData.colorKind FROM
nil => ERROR; -- color not initialized
constant => {
constantBox: PROC[box: DeviceBox] ~ {
PDFileWriter.MaskRectangle[deviceData.writer, box.smin, box.fmin, box.smax-box.smin, box.fmax-box.fmin];
};
boxes[constantBox];
};
ENDCASE => {
hardBox: PROC[box: DeviceBox] ~ {
runs: PROC[run: ImagerDevice.RunProc] ~ { ImagerMask.RunsFromBox[box: box, run: run] };
MaskRuns[device: device, bounds: box, runs: runs];
};
boxes[hardBox];
};
};
MaskBits: PROC[device: Device, srcBase: LONG POINTER, srcWordsPerLine: NAT,
ts, tf: INTEGER, boxes: PROC[ImagerDevice.BoxProc]] ~ {
deviceData: DeviceData ~ NARROW[device.data];
hardMaskBitsBox: PROC[box: DeviceBox] ~ {
hardMaskBitsBoxAction: PROC ~ {
runs: PROC[run: ImagerDevice.RunProc] ~ { ImagerMask.RunsFromBits[
base: srcBase, wordsPerLine: srcWordsPerLine,
sBits: box.smin-ts, fBits: box.fmin-tf, sRuns: box.smin, fRuns: box.fmin,
sSize: box.smax-box.smin, fSize: box.fmax-box.fmin, run: run];
};
MaskRuns[device: device, bounds: box, runs: runs];
};
};
SELECT deviceData.colorKind FROM
nil => ERROR; -- color not initialized
constant => {
boxAction: PROC[box: DeviceBox] ~ {
Line: PROC[p: PROC [LONG POINTER]] ~ TRUSTED {
a: LONG POINTER ← srcBase;
FOR c: CARDINAL IN [box.smin..box.smax) DO
p[a];
a ← a + srcWordsPerLine;
ENDLOOP;
};
IF INTEGER[box.smin]#ts OR INTEGER[box.fmin]#tf THEN hardMaskBitsBox[box]
ELSE PDFileWriter.MaskSamples[
pdState: deviceData.writer,
sMin: ts,
fMin: tf,
sSize: box.smax-box.smin,
fSize: box.fmax-box.fmin,
deliverProc: Line
];
};
boxes[boxAction];
};
ENDCASE => boxes[hardMaskBitsBox];
};
END.