ImagerPDColorImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Maureen Stone, June 11, 1987 6:28:23 pm PDT
DIRECTORY
Basics, Checksum, ColorRegistry, ColorRegistryProcs, --FontTune,-- FunctionCache, Imager, ImagerColor, ImagerColorFns, ImagerColorPrivate, ImagerDevice, ImagerPD, ImagerPDPrivate, ImagerPixelArray, ImagerSample, ImagerPixel, ImagerTransformation, ImagerPDPublic, PDFileFormat, PDFileWriter, PrincOps, PrincOpsUtils, Real, Rope, Vector2, SF, ImagerPrivate;
ImagerPDColorImpl: CEDAR PROGRAM
IMPORTS Checksum, ColorRegistryProcs, --FontTune,-- FunctionCache, Imager, ImagerColorPrivate, ImagerPixelArray, ImagerSample, ImagerPixel, ImagerTransformation, PDFileWriter, Real, SF
EXPORTS ImagerPDPrivate, ImagerPD
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
Context: TYPE ~ Imager.Context;
Toner: TYPE ~ ImagerPDPublic.Toner;
Toners: TYPE ~ LIST OF ImagerPDPublic.Toner;
PrinterType: TYPE ~ ImagerPD.PrinterType;
Color: TYPE ~ ImagerColor.Color;
Transformation: TYPE ~ ImagerTransformation.Transformation;
ConstantColor: TYPE ~ ImagerColor.ConstantColor;
SampledColor: TYPE ~ ImagerColor.SampledColor;
SampledBlack: TYPE ~ ImagerColor.SampledBlack;
ColorOperator: TYPE ~ ImagerColor.ColorOperator;
SampleMap: TYPE ~ ImagerSample.SampleMap;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
Tile: TYPE ~ ImagerPD.Tile;
TileEntry: TYPE ~ ImagerPDPrivate.TileEntry;
ColorKind: TYPE ~ ImagerPDPrivate.ColorKind;
PD: TYPE ~ REF PDRep;
PDRep: PUBLIC TYPE ~ ImagerPDPrivate.PDRep;
DeviceData: TYPE ~ ImagerPDPrivate.DeviceData;
DeviceDataRep: TYPE ~ ImagerPDPrivate.DeviceDataRep;
CMYK: TYPE ~ ImagerColorFns.CMYK;
RGB: TYPE ~ ImagerColor.RGB;
ColorPDBrick: TYPE ~ ColorRegistry.ColorPDBrick;
Sample: TYPE ~ ImagerSample.Sample;
SampleBuffer: TYPE ~ ImagerSample.SampleBuffer;
SetColor: PUBLIC PROC [context: Context, color: Color, viewToDevice: Transformation] RETURNS [ImagerDevice.AllowedMasks] ~ {
deviceData: DeviceData ~ NARROW[context.data];
writer: PDFileWriter.PDState ~ deviceData.writer;
setColorFromCMYK: PROC[val: CMYK] = {
SELECT deviceData.toner FROM
cyan => SetColorFromFraction[deviceData, val.C];
magenta => SetColorFromFraction[deviceData,val.M];
yellow => SetColorFromFraction[deviceData,val.Y];
black => SetColorFromFraction[deviceData,val.K];
ENDCASE => SetColorUniform[deviceData, noInk];
};
deviceData.colorKind ← nil;
deviceData.sampledSource ← NIL;
WITH color SELECT FROM
color: ImagerColor.SpecialColor => {
defaultColor: PROC ={
IF color.substitute # NIL
THEN { [] ← SetColor[context, color.substitute, viewToDevice] }
ELSE { ERROR Imager.Error[[code: $unknownSpecialColor, explanation: "Unknown special color has no substitute value"]] };
};
SELECT color.type FROM
$Distinct => { -- the distinct named colors
nameData: ColorRegistry.Data ← NARROW[color.data];
deviceIndex: NAT ← IndexFromAtom[deviceData.deviceType, $Distinct];
ColorRegistryProcs.InitPrinterColors[deviceType: deviceData.deviceType, colorType: $Distinct]; --won't do any work if already initialized
SELECT deviceData.deviceType FROM
$color400, $colorversatec => { --find CMYK
value: CMYKNARROW[ nameData.deviceValues[deviceIndex], REF CMYK]^;
setColorFromCMYK[value];
};
$bw400, $versatec, $raven384 => { --find a Tile
value: ImagerPD.Tile ← NARROW[nameData.deviceValues[deviceIndex], REF ImagerPD.Tile]^;
SetColorFromTile[deviceData, value, FALSE, TRUE]
};
ENDCASE => defaultColor[];
};
$ChipNDale => { -- the ChipNDale named colors
nameData: ColorRegistry.Data ← NARROW[color.data];
deviceIndex: NAT ← IndexFromAtom[deviceData.deviceType, $Distinct];
ColorRegistryProcs.InitPrinterColors[deviceType: deviceData.deviceType, colorType: $ChipNDale]; --won't do any work if already initialized
SELECT deviceData.deviceType FROM
$color400, $colorversatec => { --find LIST of ColorRegistry.ColorPDBrick
tiles: LIST OF REF ColorPDBrick ← NARROW[ nameData.deviceValues[deviceIndex]];
SetColorUniform[deviceData, off]; --have to have some color set
IF tiles#NIL THEN {
FOR l: LIST OF REF ColorPDBrick ← tiles, l.rest UNTIL l=NIL DO
IF l.first.toner=LOOPHOLE[deviceData.toner, ImagerPDPublic.Toner] THEN
SetColorFromTile[deviceData, l.first.tile, TRUE, TRUE];
ENDLOOP;
};
};
ENDCASE => defaultColor[];
};
ENDCASE => defaultColor[];
};
color: ImagerColor.OpConstantColor => {
Check here for different color operators
IF ImagerColorPrivate.SupportsOutput[color.colorOperator, $CMYK] THEN {
set primaries directly, no color correction
cmyk: CMYK;
pixelIn: ImagerColorPrivate.PixelProc ~ { RETURN[color.pixel[i]] };
tupleAction: PROC [tupleOut: ImagerColorPrivate.TupleProc] ~ {
cmyk ← [C: tupleOut[0], M: tupleOut[1], Y: tupleOut[2], K: tupleOut[3]];
};
ImagerColorPrivate.TupleFromPixel[color.colorOperator,
[type: $CMYK, samplesPerPixelOut: 4], pixelIn, tupleAction];
setColorFromCMYK[cmyk];
}
ELSE {
rgb: RGB ~ ImagerColorPrivate.RGBFromColor[color];
SetColorFromRGB may do color correction
SetColorFromRGB[deviceData, rgb, ImagerColorPrivate.IntensityFromColor[color]];
};
};
color: ImagerColor.SampledColor => {
pa: PixelArray ~ color.pa;
um: Transformation ~ color.um;
colorOperator: ColorOperator ~ color.colorOperator;
ImagerTransformation.ApplyCat[deviceData.paToDevice, pa.m, color.um, viewToDevice];
deviceData.sampledSource ← GetCachedSource[deviceData, color];
IF deviceData.sampledSource = NIL
THEN { SetColorUniform[deviceData, noInk] }
ELSE {
deviceData.colorKind ← sampled;
PDFileWriter.NewPriorityLevel[pdState: deviceData.writer, toner: deviceData.toner];
};
};
color: ImagerColor.SampledBlack => {
pa: PixelArray ~ color.pa;
um: Transformation ~ color.um;
SetColorFromSampledBlack[context, pa, color.clear];
PDFileWriter.NewPriorityLevel[pdState: deviceData.writer, toner: deviceData.toner];
};
ENDCASE => ERROR;
SELECT deviceData.colorKind FROM
constant => RETURN [[unorderedBoxes: TRUE, multipleCoverage: TRUE, regionFill: FALSE, bitmap: TRUE, rawBitmaps: FALSE, runGroupChar: TRUE, rasterChar: TRUE]];
ENDCASE => RETURN [[unorderedBoxes: FALSE, multipleCoverage: FALSE, regionFill: FALSE, bitmap: FALSE, rawBitmaps: FALSE, runGroupChar: FALSE, rasterChar: FALSE]];
};
The color name scheme requires each device to have an index understood by it an
ColorRegistryImpl. This is the cnIndex in the DeviceRec.
Any of the devices not in this list will end up using the color's substitute value
nDevices: NAT = 5;
deviceList: ARRAY [0..nDevices) OF DeviceRec ← [ --devices we have implemented names for
[8,$color400],
[9,$colorversatec],
[10,$raven384],
[11,$bw400],
[12,$versatec]];
DeviceRec: TYPE = RECORD[cnIndex: NAT, type: ATOM];
lastPair: DeviceRec ← [0, NIL]; --cheap cache
IndexFromAtom: PROC [deviceType: ATOM, colorType: ATOM] RETURNS [cnIndex: NAT] ~ {
FOR i: NAT IN [0..nDevices) DO
IF deviceList[i].type = deviceType THEN {
cnIndex ← deviceList[i].cnIndex;
lastPair ← deviceList[i];
RETURN[cnIndex]};
ENDLOOP;
ERROR;
};
SetHalftoneProperties: PUBLIC PROC [pd: PD, toner: ImagerPD.Toner, brick: Tile] ~ {
IF brick.maxSample#BYTE.LAST THEN ERROR; -- restrict this for now.
pd.deviceData.halftoneBrick[VAL[ORD[toner]]] ← NEW[Tile ← brick];
};
SetColorCorrection: PUBLIC PROC [pd: PD, correctionProc: ImagerPD.CorrectionProc, data: REF] = {
Sets the color correction proc, which will be called once per pixel for each sampled color, and once for each constant color.
pd.deviceData.correctionProc ← correctionProc;
pd.deviceData.colorCorrectionData ← data;
[] ← FunctionCache.Obtain[x: FunctionCache.GlobalCache[], compare: FunctionCache.Any, limit: INT.LAST, clientID: pd.deviceData]; -- Flush our stuff from the cache.
};
TileShape: TYPE ~ RECORD [box: SF.Box, phase: INTEGER];
GetGoodShape: PROC [box: SF.Box, phase: INTEGER] RETURNS [TileShape] ~ {
size: SF.Vec ~ SF.Size[box];
IF size.s <= 0 OR size.f <= 0 THEN ERROR;
WHILE phase < 0 DO phase ← phase + size.f ENDLOOP;
IF (16 MOD NAT[size.s] = 0) AND (16 MOD NAT[size.f] = 0) AND ((NAT[phase]*(16/NAT[size.s])) MOD 16 = 0) THEN {
Can turn this to a 16 by 16 brick.
RETURN [[box: [max: [16, 16]], phase: 0]]
};
RETURN [[box: [max: size], phase: phase]];
};
CreateTile: PUBLIC PROC [maxSample: CARDINAL, sampleMap: SampleMap, phase: INTEGER ← 0, copy: BOOLTRUE] RETURNS [Tile] ~ {
boxIn: SF.Box ~ ImagerSample.GetBox[sampleMap];
shape: TileShape ~ GetGoodShape[boxIn, phase];
raster: ImagerSample.RasterSampleMap ← WITH sampleMap SELECT FROM r: ImagerSample.RasterSampleMap => r ENDCASE => NIL;
IF copy OR raster=NIL OR shape#[boxIn, phase] THEN {
raster ← ImagerSample.NewSampleMap[box: shape.box, bitsPerSample: ImagerSample.GetBitsPerSample[sampleMap]];
ImagerSample.Clear[raster];
ImagerSample.Tile[map: raster, tile: sampleMap, phase: phase];
};
RETURN [[maxSample: maxSample, sampleMap: raster, phase: shape.phase]]
};
CopyTile: PROC [tile: Tile] RETURNS [Tile] ~ {
RETURN [CreateTile[tile.maxSample, tile.sampleMap, tile.phase, TRUE]];
};
TileHash: PROC [tile: Tile] RETURNS [hash: CARDINAL] ~ TRUSTED {
box: SF.Box ~ ImagerSample.GetBox[tile.sampleMap];
bps: NAT ~ ImagerSample.GetBitsPerSample[tile.sampleMap];
r: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: box, bitsPerSample: bps, bitsPerLine: SF.SizeF[box]*bps];
unsafeBlock: Basics.UnsafeBlock ~ ImagerSample.GetUnsafeBlock[r];
zero: [0..0] ~ unsafeBlock.startIndex;
p: LONG POINTER ← unsafeBlock.base;
count: CARDCARD[unsafeBlock.count+Basics.bytesPerWord]/Basics.bytesPerWord;
ImagerSample.Clear[r];
ImagerSample.Transfer[dst: r, src: tile.sampleMap];
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;
ImagerSample.ReleaseScratchMap[r]
};
TileEqual: PROC [a, b: Tile] RETURNS [BOOL] ~ {
IF a.maxSample # b.maxSample THEN RETURN [FALSE];
IF a.phase # b.phase THEN RETURN [FALSE];
RETURN [ImagerSample.Equal[a.sampleMap, b.sampleMap]];
};
PutTileIntoLoad: PROC [writer: PDFileWriter.PDState, tile: Tile] RETURNS [CARD] ~ TRUSTED {
box: SF.Box ~ ImagerSample.GetBox[tile.sampleMap];
base: LONG POINTER ~ ImagerSample.GetBase[tile.sampleMap].word;
bitsPerLine: NAT ~ ImagerSample.GetBitsPerLine[tile.sampleMap];
IF bitsPerLine MOD 16 # 0 OR (bitsPerLine-SF.SizeF[box]) NOT IN [0..15) THEN ERROR;
RETURN [PDFileWriter.LoadContiguousColorTile[pdState: writer, phase: tile.phase, sMin: box.min.s, fMin: box.min.f, sSize: SF.SizeS[box], fSize: SF.SizeF[box], bitsPtr: base]];
};
TileLoadWords: PROC [tile: Tile] RETURNS [INT] ~ {
RETURN [SIZE[PDFileFormat.Tile] + ImagerSample.WordsForMap[ImagerSample.GetSize[tile.sampleMap]]];
};
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;
}
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 => {
IF deviceData.priorityImportant
THEN PDFileWriter.SetColorClear[deviceData.writer, deviceData.toner]
ELSE PDFileWriter.SetColorOff[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 => {
brick: Tile ~ deviceData.halftoneBrick[deviceData.toner]^;
shape: TileShape ~ GetGoodShape[ImagerSample.GetBox[brick.sampleMap], brick.phase];
bitmap: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[shape.box];
b: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[shape.box]];
sample: CARDINAL ~ Real.InlineRoundC[f*brick.maxSample];
ImagerSample.Clear[bitmap];
FOR s: INTEGER IN [shape.box.min.s..shape.box.max.s) DO
ImagerSample.GetTileSamples[tile: brick.sampleMap, phase: brick.phase, initIndex: [s, shape.box.min.f], buffer: b];
FOR j: INTEGER IN [0..b.length) DO
b[j] ← IF sample > b[j] THEN 1 ELSE 0;
ENDLOOP;
ImagerSample.PutSamples[map: bitmap, initIndex: [s, shape.box.min.f], buffer: b];
ENDLOOP;
SetColorFromTile[deviceData: deviceData, tile: [maxSample: 1, sampleMap: bitmap, phase: shape.phase], clear: FALSE, copy: TRUE];
ImagerSample.ReleaseScratchSamples[b];
ImagerSample.ReleaseScratchMap[bitmap];
};
};
brickFraction: REAL ← 0.5; --for experimenting
SetColorFromBrick: PROC [deviceData: DeviceData, brick: Tile] ~ {
shape: TileShape ~ GetGoodShape[ImagerSample.GetBox[brick.sampleMap], brick.phase];
bitmap: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[shape.box];
b: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[shape.box]];
sample: CARDINAL ~ Real.InlineRoundC[brickFraction*brick.maxSample];
ImagerSample.Clear[bitmap];
FOR s: INTEGER IN [shape.box.min.s..shape.box.max.s) DO
ImagerSample.GetTileSamples[tile: brick.sampleMap, phase: brick.phase, initIndex: [s, shape.box.min.f], buffer: b];
FOR j: INTEGER IN [0..b.length) DO
b[j] ← IF sample > b[j] THEN 1 ELSE 0;
ENDLOOP;
ImagerSample.PutSamples[map: bitmap, initIndex: [s, shape.box.min.f], buffer: b];
ENDLOOP;
SetColorFromTile[deviceData: deviceData, tile: [maxSample: 1, sampleMap: bitmap, phase: shape.phase], clear: FALSE, copy: TRUE];
ImagerSample.ReleaseScratchSamples[b];
ImagerSample.ReleaseScratchMap[bitmap];
};
SetColorFromRGB: PROC [deviceData: DeviceData, rgb: RGB, Y: REAL] ~ {
IF deviceData.tonerUniverseSet[cyan] AND deviceData.tonerUniverseSet[magenta] AND deviceData.tonerUniverseSet[yellow] THEN {
IF deviceData.correctionProc # NIL THEN {
deviceData.pixelsRGB[0][0] ← Real.InlineRoundC[rgb.R*255];
deviceData.pixelsRGB[1][0] ← Real.InlineRoundC[rgb.G*255];
deviceData.pixelsRGB[2][0] ← Real.InlineRoundC[rgb.B*255];
deviceData.correctionProc[
deviceData.pixelsRGB, deviceData.pixelsCMYK,
deviceData.colorCorrectionData, LOOPHOLE[deviceData.toner] --toner is actually ignored. Will return all 4 separations, so we could be more efficient here if we wanted to implement a cache.
];
SELECT deviceData.toner FROM
cyan => SetColorFromFraction[deviceData, deviceData.pixelsCMYK[0][0]/255.0];
magenta => SetColorFromFraction[deviceData,deviceData.pixelsCMYK[1][0]/255.0];
yellow => SetColorFromFraction[deviceData,deviceData.pixelsCMYK[2][0]/255.0];
black => SetColorFromFraction[deviceData,deviceData.pixelsCMYK[3][0]/255.0];
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;
SetColorFromSampledBlack: PROC [context: Context, pa: PixelArray, clear: BOOL] ~ {
deviceData: DeviceData ~ NARROW[context.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: ImagerPixel.PixelMap ← NARROW[FunctionCache.Lookup[cache, compare, deviceData].value];
IF scd = NIL THEN TRUSTED {
sampleMap: ImagerSample.RasterSampleMap ~ ImagerSample.NewSampleMap[box: [max: [pa.sSize, pa.fSize]]];
ImagerSample.Clear[sampleMap];
ImagerPixelArray.Transfer[pa: pa, dst: sampleMap];
scd ← ImagerPixel.MakePixelMap[sampleMap];
IF pa.immutable THEN FunctionCache.Insert[cache, pa, scd, PixelMapWords[scd], deviceData];
};
deviceData.sampledSource ← scd;
IF deviceData.paToDevice.form = 3 AND deviceData.paToDevice.integerTrans
THEN {
SetColorFromTile[deviceData, CreateTile[1, ImagerSample.Shift[scd[0], [deviceData.paToDevice.tx, deviceData.paToDevice.ty]], 0], clear, FALSE];
}
ELSE {
deviceData.colorKind ← sampledBlack;
deviceData.colorClear ← clear;
PDFileWriter.SetColorInk[deviceData.writer, deviceData.toner];
};
};
};
SampledColorData: TYPE ~ REF SampledColorDataRep;
SampledColorDataRep: TYPE ~ RECORD [
pa: PixelArray,
colorOperator: ColorOperator,
source: ImagerPixel.PixelMap
];
PixelMapWords: PROC [pixelMap: ImagerPixel.PixelMap] RETURNS [INT] ~ {
words: INT ← 0;
FOR i: NAT IN [0..pixelMap.samplesPerPixel) DO
words ← words + ImagerSample.WordsForMap[size: ImagerSample.GetSize[pixelMap[i]], bitsPerSample: ImagerSample.GetBitsPerSample[pixelMap[i]]]
ENDLOOP;
RETURN [words];
};
GetCachedSource: PROC [deviceData: DeviceData, sampledColor: ImagerColor.SampledColor] RETURNS [ImagerPixel.PixelMap] ~ {
cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[];
pa: ImagerPixelArray.PixelArray ~ sampledColor.pa;
colorOperator: ImagerColor.ColorOperator ~ sampledColor.colorOperator;
Compare: FunctionCache.CompareProc ~ {
WITH argument SELECT FROM
scd: SampledColorData => RETURN [scd.pa = pa AND scd.colorOperator = colorOperator];
ENDCASE => RETURN [FALSE]
};
scd: SampledColorData ← NARROW[FunctionCache.Lookup[cache, Compare, deviceData].value];
fullColor: BOOL ~ colorOperator.chromatic AND deviceData.tonerUniverseSet[cyan] AND deviceData.tonerUniverseSet[magenta] AND deviceData.tonerUniverseSet[yellow];
index: NAT ~ IF fullColor THEN (SELECT deviceData.toner FROM black => 3, cyan => 0, magenta => 1, yellow => 2, ENDCASE => 0) ELSE 0;
IF scd = NIL THEN {
pixelMap: ImagerPixel.PixelMap ~ ComputeSource[deviceData, sampledColor];
scd ← NEW [SampledColorDataRep ← [pa: pa, colorOperator: colorOperator, source: pixelMap]];
IF pa.immutable THEN FunctionCache.Insert[x: cache, argument: scd, value: scd, size: PixelMapWords[pixelMap], clientID: deviceData];
};
IF index >= scd.source.samplesPerPixel THEN RETURN [NIL] ELSE RETURN [ImagerPixel.MakePixelMap[scd.source[index]]];
};
ComputeSource: PROC [deviceData: DeviceData, sampledColor: ImagerColor.SampledColor] RETURNS [ImagerPixel.PixelMap] ~ {
pa: ImagerPixelArray.PixelArray ~ sampledColor.pa;
colorOperator: ImagerColor.ColorOperator ~ sampledColor.colorOperator;
fullColor: BOOL ~ colorOperator.chromatic AND deviceData.tonerUniverseSet[cyan] AND deviceData.tonerUniverseSet[magenta] AND deviceData.tonerUniverseSet[yellow];
cmyk: BOOL ~ ImagerColorPrivate.SupportsOutput[colorOperator, $CMYK];
size: SF.Vec ~ [s: NAT[pa.sSize], f: NAT[pa.fSize]];
samplesPerPixelOut: NAT ~
SELECT TRUE FROM
fullColor AND deviceData.tonerUniverseSet[black] AND (deviceData.correctionProc#NIL OR cmyk) => 4, -- CMYK
fullColor => 3, -- CMY
ENDCASE => 1; -- Intensity
type: ATOM ~ IF samplesPerPixelOut = 4 AND cmyk THEN $CMYK ELSE IF samplesPerPixelOut = 1 THEN $Y ELSE IF ImagerColorPrivate.SupportsOutput[colorOperator, $CMY] THEN $CMY ELSE $RGB;
maxIn: ImagerPixel.PixelProc ~ { RETURN[pa.MaxSampleValue[i]] };
maxOut: ImagerPixel.PixelProc ~ { RETURN[BYTE.LAST] };
pixelMap: ImagerPixel.PixelMap ~ ImagerPixel.NewPixelMap[samplesPerPixel: samplesPerPixelOut, box: [max: size], maxSample: maxOut];
translateAction: PROC [translate: ImagerColorPrivate.TranslateProc] ~ {
pixelsIn: ImagerPixel.PixelBuffer ~ ImagerPixel.ObtainScratchPixels[pa.samplesPerPixel, size.f];
pixelsRGB: ImagerPixel.PixelBuffer ~ ImagerPixel.ObtainScratchPixels[3, size.f];
pixelsOut: ImagerPixel.PixelBuffer ~ ImagerPixel.ObtainScratchPixels[samplesPerPixelOut, size.f];
FOR i: NAT IN [0..samplesPerPixelOut) DO
ImagerSample.ClearSamples[pixelsOut[i]]
ENDLOOP;
FOR s: NAT IN [0..size.s) DO
pa.GetPixels[s: s, f: 0, pixels: pixelsIn];
SELECT type FROM
$CMYK, $CMY => translate[pixelsIn: pixelsIn, pixelsOut: pixelsOut];
$RGB => {
Correction: ImagerPD.CorrectionProc ~ deviceData.correctionProc;
correctionData: REF ~ deviceData.colorCorrectionData;
translate[pixelsIn: pixelsIn, pixelsOut: pixelsRGB];
IF Correction # NIL THEN
Computed the corrected pixels for all four separations
Correction[pixelsRGB, pixelsOut, correctionData, black] --toner field is ignored
ELSE TRUSTED {
FOR i: NAT IN [0..3) DO
a: LONG POINTER TO Basics.RawWords ~ ImagerSample.PointerToSamples[buffer: pixelsRGB[i], start: 0, count: size.f];
b: LONG POINTER TO Basics.RawWords ~ ImagerSample.PointerToSamples[buffer: pixelsOut[i], start: 0, count: size.f];
FOR j: NAT IN [0..size.f) DO b[j] ← BYTE.LAST-a[j] ENDLOOP; -- convert to fraction of ink
ENDLOOP;
};
};
$Y => {
buf: LONG POINTER TO Basics.RawWords ~ ImagerSample.PointerToSamples[buffer: pixelsOut[0], start: 0, count: size.f];
translate[pixelsIn: pixelsIn, pixelsOut: pixelsOut];
TRUSTED {
FOR i: NAT IN [0..size.f) DO buf[i] ← BYTE.LAST-buf[i] ENDLOOP; -- convert to fraction of ink
};
};
ENDCASE => ERROR;
pixelMap.PutPixels[initIndex: [s, 0], pixels: pixelsOut, count: size.f];
ENDLOOP;
ImagerPixel.ReleaseScratchPixels[pixelsOut];
ImagerPixel.ReleaseScratchPixels[pixelsIn];
};
ImagerColorPrivate.TranslatePixels[colorOperator: colorOperator, output: [type: type, samplesPerPixelOut: samplesPerPixelOut], maxIn: maxIn, maxOut: maxOut, translateAction: translateAction];
RETURN [pixelMap];
};
END.