<> <> <> <> <> <> 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; <> 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: BOOL _ TRUE] 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; <> 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 BOOL _ ALL[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: BOOL _ FALSE] ~ { proc: PROC ~ {action[pd.context]}; littleRectangle: PROC ~ { <> 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 { <> }; 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 }; <> 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: BOOL _ FALSE; 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 { <> 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 REF _ NEW[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] ~ { <> 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 _ LOOPHOLE[qq.lowbits]; } ELSE RETURN [quotient: n, remainder: 0]; }; Mod: PROC [n: INT, d: NAT] RETURNS [remainder: NAT] ~ { <> 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 INT _ NARROW[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.