<<>> <> <> <> <> <> <> <<>> 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] = { <> RETURN [fractionProbes]; }; GetFractionMisses: PROC RETURNS [CARD] = { <> 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 { <> 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 { <> 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 <> 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 { <> }; 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 { <> 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. }; <> <> <> <> <> <> <> <> <> <<};>> <> <<};>> <<>> <> <<-- SetRGB may do color correction>> <> <> <> <> <> <> <> <> <> <> < 1.0 - rgb.R,>> < 1.0 - rgb.G,>> < 1.0 - rgb.B,>> < 0.0>> <<];>> <<};>> <<};>> <<>> SetGray: PROC [deviceColorData: ImagerPrintColor.DeviceColorData, f: REAL] ~ { <> 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 => { <> 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] => { <> }; ENDCASE => { <> 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 { <> 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; }; END. ComputeSource: PROC [deviceColorData: ImagerPrintColor.DeviceColorData, color: ImagerColor.SampledColor] RETURNS [ImagerPixel.PixelMap] ~ { pa: PixelArray ~ color.pa; colorOperator: ImagerColor.ColorOperator ~ color.colorOperator; <> <> <> <> < 4, -- CMYK>> < 3, -- CMY>> < 1; -- Intensity>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<}>> <> <> <> <> <> <> <<};>> <<}>> <> <> <> <> <> <<};>> <<};>> <> <> <> <> <> <<};>> <> <> <<};>> CHECKED { transform: ImagerColorPrivate.ColorTransform ~ GetColorTransform[deviceColorData, colorOperator]; pixelMap: PixelMap ~ ImagerColorPrivate.Translate[colorOperator: colorOperator, transform: transform, pa: pa]; RETURN [pixelMap]; }; };