ImagerPrintColorImpl.mesa
Copyright Ó 1987, 1990, 1991, 1992, 1993 by Xerox Corporation. All rights reserved.
Michael Plass, October 26, 1993 2:18 pm PDT
Dave Rumph, July 10, 1991 11:58 am PDT
Willie-s, July 23, 1991 10:49 am PDT
Russ Atkinson (RRA) November 11, 1992 6:03 pm PST
DIRECTORY Basics, FunctionCache, Imager, ImagerBox, ImagerBitmapContext, ImagerBrick, ImagerColor, ImagerColorPrivate, ImagerDeviceColor, ImagerPixel, ImagerPixelArray, ImagerPrintColor, ImagerPrintColorPrivate, ImagerSample, ImagerTransformation, PrintColor, SF, Real;
ImagerPrintColorImpl:
CEDAR PROGRAM
IMPORTS FunctionCache, Imager, ImagerBitmapContext, ImagerColor, ImagerColorPrivate, ImagerDeviceColor, ImagerPixelArray, ImagerSample, ImagerTransformation, SF, Real, ImagerPixel
EXPORTS ImagerPrintColor, ImagerPrintColorPrivate
~ BEGIN
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
Rectangle: TYPE ~ ImagerBox.Rectangle;
SampleMap: TYPE ~ ImagerSample.SampleMap;
Transformation: TYPE ~ ImagerTransformation.Transformation;
ColorOperator: TYPE ~ ImagerColor.ColorOperator;
ColorTransform: TYPE ~ ImagerColorPrivate.ColorTransform;
ColorPoint: TYPE ~ ImagerColorPrivate.ColorPoint;
PixelMap:
TYPE ~ ImagerPixel.PixelMap;
PrivateRep:
TYPE ~
RECORD [
scratchBitmapContext: Imager.Context ¬ NIL,
colorTransform: ImagerColorPrivate.ColorTransform ¬ NIL,
fractionCache: ARRAY [0..fractionMod] OF FractionEntry ¬ ALL[[]],
control: ImagerDeviceColor.DeviceColorControl ¬ NIL,
controlStamp: CARD ¬ 0
];
fractionMod: NAT = 256;
FractionEntry:
TYPE =
RECORD [
brick: ImagerBrick.Brick ¬ [0, NIL, 0],
bitmap: ImagerSample.RasterSampleMap ¬ NIL,
phase: INTEGER ¬ 0,
sample: CARDINAL ¬ 0];
kU: PrintColor.TonerUniverse ~ [black: TRUE];
cmyU: PrintColor.TonerUniverse ~ [cyan: TRUE, magenta: TRUE, yellow: TRUE];
cmykU: PrintColor.TonerUniverse ~ [black: TRUE, cyan: TRUE, magenta: TRUE, yellow: TRUE];
fractionProbes: CARD ¬ 0;
fractionMisses: CARD ¬ 0;
GetFractionProbes:
PROC
RETURNS [
CARD] = {
interp [ GetFractionProbes←P60 ]
RETURN [fractionProbes];
};
GetFractionMisses:
PROC
RETURNS [
CARD] = {
interp [ GetFractionMisses←P120 ]
RETURN [fractionMisses];
};
TileShape: TYPE ~ RECORD [box: SF.Box, phase: INTEGER];
bpw: NAT ~ BITS[WORD];
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 (bpw
MOD
NAT[size.s] = 0)
AND (bpw
MOD
NAT[size.f] = 0)
AND ((
NAT[phase]*(bpw/
NAT[size.s]))
MOD bpw = 0)
THEN {
Can turn this to a bpw by bpw brick.
RETURN [[box: [max: [bpw, bpw]], phase: 0]]
};
RETURN [[box: [max: size], phase: phase]];
};
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];
};
NewDeviceColorData:
PUBLIC
PROC [logicalDevice: PrintColor.LogicalDevice, halftoneProperties: PrintColor.HalftoneProperties, correction: PrintColor.ColorCorrection ¬
NIL, interpolate:
BOOL ¬
FALSE]
RETURNS [ImagerPrintColor.DeviceColorData] ~ {
deviceColorData: ImagerPrintColor.DeviceColorData ¬ NEW[ImagerPrintColor.DeviceColorDataRep];
deviceColorData.halftoneProperties ¬ halftoneProperties;
deviceColorData.correction ¬ correction;
deviceColorData.interpolate ¬ interpolate;
deviceColorData.case ¬ nil;
deviceColorData.pixelToDevice ¬ ImagerTransformation.Scale[1];
deviceColorData.bitmap ¬ NEW[ImagerPixel.PixelMapRep[1]];
deviceColorData.tonerUniverse ¬ ALL[FALSE];
FOR each:
LIST
OF PrintColor.HalftonePropertiesForSeparation ¬ halftoneProperties, each.rest
UNTIL each =
NIL
DO
deviceColorData.tonerUniverse[each.first.toner] ¬ TRUE;
ENDLOOP;
deviceColorData.private ¬ NEW[PrivateRep ¬ []];
RETURN [deviceColorData]
};
SetSeparation:
PUBLIC
PROC
[deviceColorData: ImagerPrintColor.DeviceColorData, toner: PrintColor.Toner] ~ {
old: ImagerBrick.Brick = deviceColorData.brick;
new: ImagerBrick.Brick ¬ [0, NIL, 0];
IF
NOT deviceColorData.tonerUniverse[toner]
THEN
ERROR Imager.Error[[code: $invalidToner, explanation: "Illegal toner (no halftone properties supplied)"]];
FOR each:
LIST
OF PrintColor.HalftonePropertiesForSeparation ¬ deviceColorData.halftoneProperties, each.rest
UNTIL each =
NIL
DO
IF each.first.toner = toner THEN {new ¬ each.first.brick; EXIT};
ENDLOOP;
IF new.sampleMap =
NIL
AND deviceColorData.halftoneProperties #
NIL
THEN {
This case might come up when trying to set a gray screen on a color device - we need to use something!
new ¬ deviceColorData.halftoneProperties.first.brick;
};
IF new.maxSample # old.maxSample
OR new.phase # old.phase
OR new.sampleMap =
NIL
OR old.sampleMap =
NIL
OR
NOT ImagerSample.Equal[new.sampleMap, old.sampleMap]
THEN
We ONLY change the brick if the new and old bricks differ in contents. This allows the fraction cache to work better. This assumes that the brick sample maps are readonly and persistent.
deviceColorData.brick ¬ new;
deviceColorData.toner ¬ toner;
};
SetDeviceColorControl:
PUBLIC
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, deviceColorControl: ImagerDeviceColor.DeviceColorControl] = {
private: REF PrivateRep = NARROW[deviceColorData.private];
private.control ¬ deviceColorControl;
private.controlStamp ¬ deviceColorControl.stamp - 1;
};
SetDeviceColorData:
PUBLIC
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, color: ImagerColor.Color, viewToDevice: Transformation] ~ {
deviceColorData.case ¬ nil;
deviceColorData.ink ¬ set;
deviceColorData.tileBits ¬ NIL;
deviceColorData.alpha ¬ 255;
deviceColorData.separation ¬ NIL;
deviceColorData.bitmap[0] ¬ NIL;
IF deviceColorData.sampleMapInUse #
NIL
THEN {
ImagerSample.ReleaseScratchMap[deviceColorData.sampleMapInUse];
deviceColorData.sampleMapInUse ¬ NIL;
};
deviceColorData.function ¬ [null, null];
DO
WITH color
SELECT
FROM
specialColor: ImagerColor.SpecialColor => {
SELECT specialColor.type
FROM
$PrintColor => {
data: PrintColor.PrintSpecialColorData ~
NARROW[specialColor.data];
IF deviceColorData.logicalDevice < data.size
THEN {
IF data[deviceColorData.logicalDevice] =
NIL
THEN {
ColorRegistry.??
};
IF data[deviceColorData.logicalDevice] #
NIL
THEN {
SetLogicalDeviceColor[deviceColorData, data[deviceColorData.logicalDevice]];
RETURN;
};
};
color ¬ specialColor.substitute; LOOP;
};
ENDCASE => {
IF specialColor.substitute =
NIL
THEN ERROR Imager.Error[[code: $unknownSpecialColor, explanation: "Unknown special color has no substitute value"]]
ELSE { color ¬ specialColor.substitute; LOOP }
};
};
opColor: ImagerColor.OpConstantColor => {
SetOpConstantColor[deviceColorData, opColor];
};
sampledColor: ImagerColor.SampledColor => {
ImagerTransformation.ApplyCat[deviceColorData.pixelToDevice, sampledColor.pa.m, sampledColor.um, viewToDevice];
SetSampledColor[deviceColorData, sampledColor];
};
sampledBlack: ImagerColor.SampledBlack => {
ImagerTransformation.ApplyCat[deviceColorData.pixelToDevice, sampledBlack.pa.m, sampledBlack.um, viewToDevice];
SetSampledBlack[deviceColorData, sampledBlack];
};
ENDCASE => ERROR;
EXIT;
ENDLOOP;
};
SetInk:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, ink: PrintColor.Ink] ~ {
deviceColorData.case ¬ constant;
deviceColorData.ink ¬ ink;
};
SetFraction:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, f:
REAL] ~ {
SELECT f
FROM
>= 0.9999 => SetInk[deviceColorData, set];
<= 0.0001 => SetInk[deviceColorData, remove];
ENDCASE => {
brick: ImagerBrick.Brick ~ deviceColorData.brick;
sample: CARDINAL ~ Real.Round[f*brick.maxSample];
SELECT sample
FROM
brick.maxSample => SetInk[deviceColorData, set];
0 => SetInk[deviceColorData, remove];
ENDCASE => {
private: REF PrivateRep = NARROW[deviceColorData.private];
mod: [0..fractionMod) = sample MOD fractionMod;
fractionProbes ¬ fractionProbes + 1;
IF private #
NIL
THEN
IF private.fractionCache[mod].brick = brick
AND private.fractionCache[mod].sample = sample
THEN {
We have a match!
SetTile[deviceColorData: deviceColorData,
tileBits: private.fractionCache[mod].bitmap,
tilePhase: private.fractionCache[mod].phase,
function: [null, null]];
RETURN;
};
{
shape: TileShape ~ GetGoodShape[ImagerSample.GetBox[brick.sampleMap], brick.phase];
b: ImagerSample.SampleBuffer = ImagerSample.ObtainScratchSamples[SF.SizeF[shape.box]];
bitmap: ImagerSample.RasterSampleMap = ImagerSample.NewSampleMap[shape.box];
ImagerSample.Clear[bitmap];
fractionMisses ¬ fractionMisses + 1;
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:
NAT
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;
SetTile[deviceColorData: deviceColorData, tileBits: bitmap, tilePhase: shape.phase, function: [null, null]];
IF private #
NIL
THEN
private.fractionCache[mod] ¬
[brick: brick, bitmap: bitmap, phase: shape.phase, sample: sample];
};
};
};
};
SetTile:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, tileBits: SampleMap, tilePhase:
NAT, function: ImagerSample.Function] ~ {
deviceColorData.case ¬ tile;
deviceColorData.tileBits ¬ tileBits;
deviceColorData.tilePhase ¬ tilePhase;
deviceColorData.function ¬ function;
};
SetLogicalDeviceColor:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, lData: PrintColor.LogicalDeviceColorData] ~ {
IF deviceColorData.logicalDevice # lData.logicalDevice THEN ERROR; -- bad color def
FOR e:
LIST
OF PrintColor.BehaviorForSeparation ¬ lData.behaviors, e.rest
UNTIL e =
NIL
DO
b: PrintColor.BehaviorForSeparation ~ e.first;
IF b.toner = deviceColorData.toner
THEN {
WITH b
SELECT
FROM
b: REF PrintColor.BehaviorForSeparationRep.constant => SetInk[deviceColorData, b.ink];
b: REF PrintColor.BehaviorForSeparationRep.fraction => SetFraction[deviceColorData, b.f];
b: REF PrintColor.BehaviorForSeparationRep.tile => SetTile[deviceColorData, b.tileBits, b.tilePhase, b.function];
ENDCASE => ERROR;
RETURN;
};
ENDLOOP;
SetInk[deviceColorData, remove]; -- no behavior was specified.
};
SetSubtractivePrimaries: PROC [deviceColorData: ImagerPrintColor.DeviceColorData, color: ImagerColor.OpConstantColor, output: ImagerColorPrivate.ColorOutput] ~ {
set primaries directly, no color correction
indexFromToner: ARRAY PrintColor.Toner[black..yellow] OF NAT ~ [cyan: 0, magenta: 1, yellow: 2, black: 3];
min: PrintColor.Toner ~ IF output.type = $CMYK THEN black ELSE cyan;
PixelIn: ImagerColorPrivate.TupleProc ~ { RETURN[color.pixel[i]] };
DoTuple: PROC [tupleOut: ImagerColorPrivate.TupleProc] ~ {
IF deviceColorData.toner IN [min..yellow]
THEN {SetFraction[deviceColorData, tupleOut[indexFromToner[deviceColorData.toner]]]}
ELSE {SetInk[deviceColorData, remove]};
};
ImagerColorPrivate.TupleFromPixel[color.colorOperator, output, PixelIn, DoTuple];
};
SetRGB: PROC [deviceColorData: ImagerPrintColor.DeviceColorData, rgb: ImagerColor.RGB] ~ {
-- SetRGB may do color correction
IF deviceColorData.correction # NIL
THEN {
pixelsRGB: ImagerPixel.PixelBuffer ~ ImagerPixel.ObtainScratchPixels[samplesPerPixel: 3, length: 1];
pixelsOut: ImagerPixel.PixelBuffer ~ ImagerPixel.ObtainScratchPixels[samplesPerPixel: IF deviceColorData.tonerUniverse[black] THEN 4 ELSE 3, length: 1];
pixelsRGB[0][0] ¬ Real.Round[rgb.R*255];
pixelsRGB[1][0] ¬ Real.Round[rgb.G*255];
pixelsRGB[2][0] ¬ Real.Round[rgb.B*255];
deviceColorData.correction.correctionProc[self: deviceColorData.correction, pixelsOut: pixelsOut, maxSampleOut: ALL[255], rgbIn: pixelsRGB];
SetFraction[deviceColorData,
SELECT deviceColorData.toner FROM
cyan => pixelsOut[0][0]/255.0,
magenta => pixelsOut[1][0]/255.0,
yellow => pixelsOut[2][0]/255.0,
black => pixelsOut[3][0]/255.0,
ENDCASE => 0.0
];
ImagerPixel.ReleaseScratchPixels[pixelsRGB];
ImagerPixel.ReleaseScratchPixels[pixelsOut];
}
ELSE {
SetFraction[deviceColorData,
SELECT deviceColorData.toner FROM
cyan => 1.0 - rgb.R,
magenta => 1.0 - rgb.G,
yellow => 1.0 - rgb.B,
ENDCASE => 0.0
];
};
};
SetGray:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, f:
REAL] ~ {
This requires a black toner to be present.
IF deviceColorData.tonerUniverse[black]
THEN {
IF deviceColorData.toner = black
THEN SetFraction[deviceColorData, f]
ELSE SetInk[deviceColorData, remove];
}
ELSE {
SetInk[deviceColorData, set];
SIGNAL Imager.Warning[[$illegalColor, "Unable to render gray on this device"]];
};
};
SetOpConstantColor:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, color: ImagerColor.OpConstantColor] ~ {
transform: ColorTransform ~ GetColorTransform[deviceColorData, color.colorOperator];
p: ColorPoint ¬ ImagerColorPrivate.ColorPointFromColor[color, transform];
SELECT p.dim
FROM
1 => { SetGray[deviceColorData, p[0]/255.0] };
4 => {
i:
NAT ~
SELECT deviceColorData.toner
FROM
cyan => 0,
magenta => 1,
yellow => 2,
black => 3,
ENDCASE => 3;
SetFraction[deviceColorData, p[i]/255.0]
};
ENDCASE => ERROR;
p ¬ ImagerColorPrivate.DestroyColorPoint[p];
};
SampledColorData: TYPE ~ REF SampledColorDataRep;
SampledColorDataRep:
TYPE ~
RECORD [
pa: PixelArray,
colorOperator: ImagerColor.ColorOperator,
fullColor: BOOL,
source: ImagerPixel.PixelMap
];
SetSampledColor:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, color: ImagerColor.SampledColor] ~ {
private: REF PrivateRep = NARROW[deviceColorData.private];
pa: PixelArray ~ color.pa;
cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[];
colorOperator: ImagerColor.ColorOperator ~ color.colorOperator;
tu: PrintColor.TonerUniverse ~ deviceColorData.tonerUniverse;
fullColor: BOOL ~ colorOperator.chromatic AND tu[cyan] AND tu[magenta] AND tu[yellow];
Compare: FunctionCache.CompareProc ~ {
WITH argument
SELECT
FROM
scd: SampledColorData => RETURN [scd.pa = pa AND scd.colorOperator = colorOperator AND scd.fullColor = fullColor];
ENDCASE => RETURN [FALSE]
};
toner: PrintColor.Toner ~ deviceColorData.toner;
index: NAT ~ IF fullColor THEN (SELECT toner FROM black => 3, cyan => 0, magenta => 1, yellow => 2, ENDCASE => 0) ELSE 0;
scd: SampledColorData ¬ NARROW[FunctionCache.Lookup[x: cache, compare: Compare, clientID: $PrintColor].value];
IF scd =
NIL
THEN {
pixelMap: ImagerPixel.PixelMap ~ ComputeSource[deviceColorData, color];
scd ¬ NEW [SampledColorDataRep ¬ [pa: pa, colorOperator: colorOperator, fullColor: fullColor, source: pixelMap]];
IF pa.immutable AND private.control.allIdentity THEN FunctionCache.Insert[x: cache, argument: scd, value: scd, size: PixelMapWords[pixelMap], clientID: $PrintColor];
FIXTHIS - if not pa.immutable (as for PS), this caching strategy gives pretty mediocre results for ImagerFourColorContext - the ComputeSource happens once per separaion, even though they are all done at (roughly) the same time!
};
IF index >= scd.source.samplesPerPixel
OR (
NOT fullColor
AND toner#black)
THEN {
FIXTHIS - overprint
SetInk[deviceColorData, remove];
}
ELSE {
deviceColorData.case ¬ sampledColor;
deviceColorData.separation ¬ ImagerPixel.MakePixelMap[scd.source[index]];
};
};
ComputeSource:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, color: ImagerColor.SampledColor]
RETURNS [ImagerPixel.PixelMap] ~ {
pa: PixelArray ~ color.pa;
colorOperator: ImagerColor.ColorOperator ~ color.colorOperator;
transform: ImagerColorPrivate.ColorTransform ~ GetColorTransform[deviceColorData, colorOperator];
pixelMap: PixelMap ~ ImagerColorPrivate.Translate[colorOperator: colorOperator, transform: transform, pa: pa];
RETURN [pixelMap];
};
colorSpaceCMYK: ImagerColorPrivate.ColorSpace ~ ImagerColorPrivate.DefineProcessSpace[ImagerColor.NewColorOperatorCMYK[255]];
allowY: LIST OF ImagerColorPrivate.ColorSpace ~ LIST[$Y];
allowColorOrY: LIST OF ImagerColorPrivate.ColorSpace ~ LIST[colorSpaceCMYK, $RGB, $Y];
GetColorTransform:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, colorOperator: ColorOperator]
RETURNS [ImagerColorPrivate.ColorTransform] ~ {
private: REF PrivateRep ~ NARROW[deviceColorData.private];
tu: PrintColor.TonerUniverse ~ deviceColorData.tonerUniverse;
fullColor: BOOL ~ tu[cyan] AND tu[magenta] AND tu[yellow] AND tu[black];
IF fullColor
THEN {
colorSpace: ImagerColorPrivate.ColorSpace ~ ImagerColorPrivate.ChooseColorSpace[colorOperator, allowColorOrY];
IF private.colorTransform =
NIL
OR colorSpace # private.colorTransform.domain
OR private.control.stamp # private.controlStamp
THEN {
rangeMax: ImagerColorPrivate.ColorPoint ~ ImagerColorPrivate.MakeColorPoint[4, 255];
ct: ImagerColorPrivate.ColorTransform ~ NEW[ImagerColorPrivate.ColorTransformRep ¬ [domain: colorSpace, rangeMax: rangeMax, proc: CMYKTransform, data: private.control]];
private.colorTransform ¬ ct;
private.controlStamp ¬ private.control.stamp;
};
}
ELSE {
colorSpace: ImagerColorPrivate.ColorSpace ~ ImagerColorPrivate.ChooseColorSpace[colorOperator, allowY];
IF private.colorTransform =
NIL
OR colorSpace # private.colorTransform.domain
OR private.control.stamp # private.controlStamp
THEN {
rangeMax: ImagerColorPrivate.ColorPoint ~ ImagerColorPrivate.MakeColorPoint[1, 255];
ct: ImagerColorPrivate.ColorTransform ~ NEW[ImagerColorPrivate.ColorTransformRep ¬ [domain: colorSpace, rangeMax: rangeMax, proc: KTransform, data: private.control]];
private.colorTransform ¬ ct;
private.controlStamp ¬ private.control.stamp;
};
};
RETURN [private.colorTransform]
};
CMYKTransform:
PROC [self: ColorTransform, in: ColorPoint, out: ColorPoint] ~ {
control: ImagerDeviceColor.DeviceColorControl = NARROW[self.data];
SELECT self.domain
FROM
$Y => {
ImagerDeviceColor.CMYKFromY[control, in[0], 255.0, out];
};
$RGB => {
ImagerDeviceColor.CMYKFromRGB[control, in[0], in[1], in[2], 255.0, out];
};
ENDCASE => {
ImagerDeviceColor.CMYKFromCMYK[control, in[0]/255.0, in[1]/255.0, in[2]/255.0, in[3]/255.0, 255.0, out];
};
};
KTransform:
PROC [self: ColorTransform, in: ColorPoint, out: ColorPoint] ~ {
control: ImagerDeviceColor.DeviceColorControl = NARROW[self.data];
SELECT self.domain
FROM
$Y => {
ImagerDeviceColor.YFromY[control, in[0], 255.0, out];
out[0] ¬ 255.0-out[0]; -- convert to fraction of ink
};
ENDCASE => ERROR;
};
SetSampledBlack:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, color: ImagerColor.SampledBlack] ~ {
pa: PixelArray ~ color.pa;
IF deviceColorData.tonerUniverse[black]
AND deviceColorData.toner # black
THEN {
deviceColorData.case ¬ constant;
deviceColorData.ink ¬ IF color.clear THEN nop ELSE remove;
RETURN
};
deviceColorData.function ¬ IF color.clear THEN [or, null] ELSE [null, null];
SELECT
TRUE
FROM
deviceColorData.pixelToDevice.form = 3
AND deviceColorData.pixelToDevice.integerTrans => {
This bitmap is already in the right orientation for the output raster; just use it!
min: SF.Vec ~ [s: deviceColorData.pixelToDevice.tx, f: deviceColorData.pixelToDevice.ty];
max: SF.Vec ~ [s: min.s + pa.sSize, f: min.f + pa.fSize];
sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box: [min: min, max: max]];
ImagerPixelArray.Transfer[pa: pa, dst: sampleMap, dstMin: min];
deviceColorData.case ¬ tile;
deviceColorData.tilePhase ¬ 0;
deviceColorData.tileBits ¬ sampleMap;
deviceColorData.sampleMapInUse ¬ sampleMap;
};
newStuff
AND TryTransformingBitmapIntoTile[deviceColorData, pa] => {
TryTransformingBitmapIntoTile already did the setup.
};
ENDCASE => {
The hard case - use the sampler
sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box: [max: [pa.sSize, pa.fSize]]];
ImagerPixelArray.Transfer[pa: pa, dst: sampleMap];
deviceColorData.case ¬ sampledBlack;
deviceColorData.bitmap[0] ¬ sampleMap;
deviceColorData.bitmap.box ¬ ImagerSample.GetBox[sampleMap];
deviceColorData.sampleMapInUse ¬ sampleMap;
};
};
ImagerPrintColorNewStuff:
PROC [new:
CARD]
RETURNS [old:
BOOL] ~ {
old ¬ newStuff;
newStuff ¬ new#0;
};
newStuff: BOOL ¬ TRUE; -- for testing
ImagerPrintColorTileTolerance:
PROC [int:
INT]
RETURNS [old:
INT] ~ {
old ¬ Real.Round[int];
tileTolerance ¬ int;
};
tileTolerance: REAL ¬ 2* 262144.0; -- tileTolerance is added to a small number (epsilon) and compared to tileTolerance to decide whether epsilon is close enough to zero. The default value allows a little more that 2% of a pixel of tolerance.
TryTransformingBitmapIntoTile:
PROC [deviceColorData: ImagerPrintColor.DeviceColorData, pa: PixelArray]
RETURNS [
BOOL] ~ {
m: Transformation ~ deviceColorData.pixelToDevice;
IF m.form#0
THEN {
sRect: Rectangle ~ [0, 0, pa.sSize, pa.fSize];
dRect: Rectangle ~ ImagerTransformation.TransformRectangle[m, sRect];
IF
MAX[
ABS[dRect.x],
ABS[dRect.y],
ABS[dRect.w],
ABS[dRect.h]] <= 16383
AND
ABS[dRect.w*dRect.h] <= 1048576.0
THEN {
sSize: NAT ~ Real.Round[dRect.w];
fSize: NAT ~ Real.Round[dRect.h];
IF (dRect.w-sSize)+tileTolerance = tileTolerance
AND (dRect.h-fSize)+tileTolerance = tileTolerance
THEN {
This bitmap is not quite right for the output raster, but we can make it so.
private: REF PrivateRep ~ NARROW[deviceColorData.private];
context: Imager.Context ¬ IF private.scratchBitmapContext = NIL THEN (private.scratchBitmapContext ¬ ImagerBitmapContext.Create[deviceSpaceSize: SF.maxVec, scanMode: [slow: right, fast: up], surfaceUnitsPerInch: [1,1], pixelUnits: TRUE, fontCacheName: $Bitmap]) ELSE private.scratchBitmapContext;
action:
PROC ~ {
pamInverse: Transformation ~ ImagerTransformation.Invert[pa.m];
Imager.TranslateT[context, [-dRect.x, -dRect.y]];
Imager.ConcatT[context, m];
Imager.ConcatT[context, pamInverse];
Imager.MaskPixel[context, pa];
ImagerTransformation.Destroy[pamInverse];
};
sampleMap: SampleMap ¬ ImagerSample.ObtainScratchMap[box: [max: [sSize, fSize]]];
tileMap: SampleMap ~ ImagerSample.Shift[sampleMap, [s: Real.Floor[dRect.x+0.5], f: Real.Floor[dRect.y+0.5]]];
ImagerSample.Clear[sampleMap];
ImagerBitmapContext.SetBitmap[context, sampleMap];
Imager.DoSave[context, action];
ImagerBitmapContext.SetBitmap[context, NIL];
TRUSTED {ImagerSample.ReleaseDescriptor[sampleMap]; sampleMap ¬ NIL};
SetTile[deviceColorData: deviceColorData, tileBits: tileMap, tilePhase: 0, function: deviceColorData.function];
deviceColorData.sampleMapInUse ¬ tileMap;
RETURN [TRUE];
};
};
};
RETURN [FALSE];
};
MaskBoxes:
PUBLIC
PROC [bitmap: SampleMap, deviceColorData: ImagerPrintColor.DeviceColorData, bounds:
SF.Box, boxes:
SF.BoxGenerator] ~ {
SELECT deviceColorData.case
FROM
constant => {
IF deviceColorData.ink # nop
THEN
ImagerSample.FillBoxes[map: bitmap, boxes: boxes, value: IF deviceColorData.ink = set THEN 1 ELSE 0];
};
tile => {
ImagerSample.TileBoxes[map: bitmap, boxes: boxes, tile: deviceColorData.tileBits, phase: deviceColorData.tilePhase, function: deviceColorData.function];
};
sampledColor =>
TRUSTED {
buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[bounds]];
Action:
SAFE
PROC [pixels: ImagerPixel.PixelBuffer, min:
SF.Vec] ~
TRUSTED {
buffer.length ¬ pixels.length;
ImagerSample.GetTileSamples[tile: deviceColorData.brick.sampleMap, initIndex: min, buffer: buffer, phase: deviceColorData.brick.phase];
ImagerSample.Halftone[map: bitmap, min: min, sampleBuffer: pixels[0], thresholdBuffer: buffer, function: [deviceColorData.function.dstFunc, IF deviceColorData.function.srcFunc = null THEN complement ELSE null]];
};
ImagerPixel.Resample[self: deviceColorData.separation, m: deviceColorData.pixelToDevice, interpolate: deviceColorData.interpolate, boxes: boxes, bounds: bounds, action: Action];
ImagerSample.ReleaseScratchSamples[buffer];
};
sampledBlack => {
Action:
PROC [pixels: ImagerPixel.PixelBuffer, min:
SF.Vec] ~ {
ImagerSample.PutSamples[map: bitmap, initIndex: min, buffer: pixels[0], start: 0, count: pixels.length, function: deviceColorData.function];
};
ImagerPixel.Resample[self: deviceColorData.bitmap, m: deviceColorData.pixelToDevice, interpolate: FALSE, boxes: boxes, bounds: bounds, action: Action];
};
ENDCASE => ERROR;
};