<> <> <> <> <> <> <> <> <> <> DIRECTORY Atom, Basics, ColorRegistry, --FontTune,-- FunctionCache, Imager, ImagerBackdoor, ImagerBrick, ImagerMaskCache, ImagerColor, ImagerColorFns, ImagerDevice, ImagerPD, ImagerPDPrivate, ImagerSample, ImagerPixel, ImagerRaster, ImagerTransformation, ImagerPDPublic, PDFileFormat, PDFileWriter, PrincOps, PrincOpsUtils, RefTab, Rope, Vector2, SF, ImagerPrivate, ImagerState, ImagerPath; ImagerPDImpl: CEDAR PROGRAM IMPORTS Atom, Basics, --FontTune,-- FunctionCache, Imager, ImagerBackdoor, ImagerBrick, ImagerMaskCache, ImagerPDPrivate, ImagerSample, ImagerPixel, ImagerRaster, ImagerColor, ImagerState, ImagerTransformation, PDFileWriter, RefTab, Rope, SF EXPORTS ImagerPD, Imager ~ BEGIN ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Imager.VEC; PathProc: TYPE ~ ImagerPath.PathProc; Context: TYPE ~ Imager.Context; CharMask: TYPE ~ ImagerMaskCache.CharMask; Toner: TYPE ~ ImagerPDPublic.Toner; Toners: TYPE ~ LIST OF ImagerPDPublic.Toner; UCR: TYPE ~ ImagerPDPublic.UCR; PrinterType: TYPE ~ ImagerPD.PrinterType; Transformation: TYPE ~ ImagerTransformation.Transformation; DeviceCode: TYPE ~ PDFileFormat.DeviceCode; PD: TYPE ~ REF PDRep; PDRep: PUBLIC TYPE ~ ImagerPDPrivate.PDRep; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; StateRep: PUBLIC TYPE ~ ImagerState.StateRep; DeviceData: TYPE ~ ImagerPDPrivate.DeviceData; DeviceDataRep: TYPE ~ ImagerPDPrivate.DeviceDataRep; deviceClass: ImagerDevice.DeviceClass ~ NEW[ImagerDevice.DeviceClassRep _ [ SetColor: ImagerPDPrivate.SetColor, SetPriority: SetPriority, MaskBoxes: MaskBoxes, MaskBitmap: MaskBitmap, MaskChar: MaskChar ]]; SampleMap: TYPE ~ ImagerSample.SampleMap; Tile: TYPE ~ ImagerPD.Tile; bitsPerWord: NAT ~ Basics.bitsPerWord; TonerSetFromToners: PROC [toners: Toners] RETURNS [tonerSet: PACKED ARRAY PDFileFormat.Toner OF BOOL] ~ { tonerSet _ ALL[FALSE]; FOR t: Toners _ toners, t.rest UNTIL t=NIL DO pdt: PDFileFormat.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; defaultDotShape: REAL _ 0.45; defaultScreenAngle: ARRAY PDFileFormat.Toner[black..yellow] OF REAL _ [ black: 90, cyan: 75, magenta: 105, yellow: 45 ]; GetDefaultBricks: PROC [deviceData: DeviceData, pixelsPerHalftoneDot: REAL] ~ { FOR t: PDFileFormat.Toner IN PDFileFormat.Toner DO IF deviceData.tonerSet[t] THEN { angle: REAL ~ IF t IN[black..yellow] THEN defaultScreenAngle[t] ELSE 0; brick: ImagerBrick.Brick ~ ImagerBrick.BrickFromDotScreen[pixelsPerDot: pixelsPerHalftoneDot, degrees: angle, shape: defaultDotShape]; deviceData.halftoneBrick[t] _ NEW[Tile _ brick]; }; ENDLOOP; }; CreateFromParameters: PUBLIC PROC [name: ROPE, deviceCode: CARDINAL, deviceType: ATOM, 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, ucr: UCR _ []] 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 _ [ deviceType: deviceType, writer: writer, sSize: imageSSize, fSize: imageFSize, feed: FALSE, strip: FALSE, imageStarted: FALSE, priorityImportant: FALSE, tonerSet: TonerSetFromToners[toners], tonerUniverseSet: TonerSetFromToners[IF tonerUniverse # NIL THEN tonerUniverse ELSE toners], toner: black, ucr: ucr, maskTab: RefTab.Create[997], colorKind: nil, tileTable: NEW[ImagerPDPrivate.TileTableRep[17]], colorTile: [0, NIL, 0], colorClear: FALSE, sampledSource: NIL, paToDevice: ImagerTransformation.Scale[0], halftoneBrick: ALL[NIL], pixelsRGB: ImagerPixel.ObtainScratchPixels[3, 1], pixelsCMYK: ImagerPixel.ObtainScratchPixels[4, 1], interpolate: FALSE ]]; sInches: REAL ~ REAL[imageSSize]/REAL[sResolution]; fInches: REAL ~ REAL[imageFSize]/REAL[fResolution]; fontCacheID: ATOM ~ IF Rope.Size[fontTuning] = 0 THEN $PD ELSE Atom.MakeAtom[fontTuning]; fontTuner: ImagerDevice.FontTuner _ -- IF Rope.Size[fontTuning] # 0 THEN FontTune.CreateFontTuner[fontTuning] ELSE -- NIL; deviceParm: ImagerDevice.DeviceParm _ NEW[ImagerDevice.DeviceParmRep _ [ class: deviceClass, sSize: imageSSize, fSize: imageFSize, scanMode: IF sInches>fInches THEN [slow: down, fast: right] ELSE [slow: right, fast: up], <> surfaceUnitsPerInch: [sResolution, fResolution], surfaceUnitsPerPixel: 1, fontTuner: fontTuner, fontCache: ImagerMaskCache.GetNamedCache[fontCacheID, fontCacheSize], rastWeight: rastWeight ]]; context: Context ~ ImagerRaster.Create[class: contextClass, deviceParm: deviceParm, data: deviceData, pixelUnits: FALSE]; pd: PD ~ NEW [PDRep _ [ context: context, toners: toners, deviceData: deviceData ]]; IF deviceType=$raven384 OR deviceType=$versatec OR deviceType=$bw400 THEN { <> black: ImagerColor.Color _ ImagerColor.Find["Xerox/Research/Distinct/Black"]; white: ImagerColor.Color _ ImagerColor.Find["Xerox/Research/Distinct/White"]; oldClass: REF ClassRep _ context.class; --crack the opaque type newClass: REF ClassRep _ NEW[ClassRep _ oldClass^]; ImagerRasterMaskVector _ oldClass.MaskVector; ImagerRasterMaskStroke _ oldClass.MaskStroke; newClass.MaskStroke _ MyMaskStroke; newClass.MaskVector _ MyMaskVector; Imager.PutProp[context, $DistinctBlack, black]; Imager.PutProp[context, $DistinctWhite, white]; context.class _ newClass; --replace the class with our local subclass }; IF pixelsPerHalftoneDot <= 0 THEN pixelsPerHalftoneDot _ 2; GetDefaultBricks[deviceData, pixelsPerHalftoneDot]; RETURN[pd]; }; <> ImagerRasterMaskVector: PROC [context: Context, p1, p2: VEC] _ NIL; ImagerRasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] _ NIL; DoDash: PROC[context: Context] RETURNS[BOOLEAN] = { state: REF StateRep _ context.state; WITH state.color SELECT FROM color: ImagerColor.SpecialColor => IF color.type=$Distinct THEN { nameData: ColorRegistry.Data _ NARROW[color.data]; IF nameData.id#$Black AND nameData.id#$White AND nameData.id#$Gray THEN { widthInPixels: REAL _ ImagerTransformation.TransformVec[state.T, [state.np.strokeWidth, 0]].x; IF widthInPixels < 10 THEN RETURN[TRUE]; --define 10 device pixels as ok for texture }}; ENDCASE; RETURN[FALSE]; }; MyMaskVector: PROC [context: Context, p1, p2: Imager.VEC] = { IF DoDash[context] THEN { black: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctBlack]]; white: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctWhite]]; path: ImagerPath.PathProc = {moveTo[p1]; lineTo[p2]}; color: ImagerColor.SpecialColor _ NARROW[ImagerState.StateGetColor[context]]; ImagerState.StateSetColor[context, white]; ImagerRasterMaskVector[context, p1, p2]; ImagerState.StateSetColor[context, black]; MaskDashedStroke[context, color, path, FALSE]; ImagerState.StateSetColor[context, color]; } ELSE ImagerRasterMaskVector[context, p1, p2]; }; MyMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] = { IF DoDash[context] THEN { black: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctBlack]]; white: ImagerColor.Color _ NARROW[Imager.GetProp[context, $DistinctWhite]]; color: ImagerColor.SpecialColor _ NARROW[ImagerState.StateGetColor[context]]; ImagerState.StateSetColor[context, white]; ImagerRasterMaskStroke[context, path, closed]; ImagerState.StateSetColor[context, black]; MaskDashedStroke[context, color, path, closed]; ImagerState.StateSetColor[context, color]; } ELSE ImagerRasterMaskStroke[context, path, closed]; }; yellowDash: ARRAY [0..3] OF REAL _ [0,2.5,0,2.5]; redDash: ARRAY [0..3] OF REAL _ [3,2,3,2]; greenDash: ARRAY [0..3] OF REAL _ [1,2,1,2]; blueDash: ARRAY [0..3] OF REAL _ [4,2,1,2]; MaskDashedStroke: PROC [context: Context, color: ImagerColor.SpecialColor, path: PathProc, closed: BOOL] = { deviceData: DeviceData ~ NARROW[context.data]; unit: REAL _ SELECT deviceData.deviceType FROM $raven384, $bw400 => 10, $versatec => 5, ENDCASE => 1; array: ARRAY [0..3] OF REAL; pattern: PROC[i: NAT] RETURNS[REAL] = { v: VEC _ ImagerTransformation.InverseTransformVec[state.T, [array[i]*unit, 0]]; RETURN[v.x]}; state: REF StateRep _ context.state; end: INT _ state.np.strokeEnd; joint: INT _ state.np.strokeJoint; nameData: ColorRegistry.Data _ NARROW[color.data]; Imager.SetStrokeEnd[context, round]; Imager.SetStrokeJoint[context, round]; array _ SELECT nameData.id FROM $Red => redDash, $Green => greenDash, $Blue => blueDash, $Yellow => yellowDash, ENDCASE => ERROR; Imager.MaskDashedStroke[context, path, 4, pattern, 0,0]; state.np.strokeEnd _ end; state.np.strokeJoint _ joint; }; Close: PUBLIC PROC [pd: PD] ~ { PDFileWriter.Close[pd.deviceData.writer]; [] _ FunctionCache.Obtain[x: FunctionCache.GlobalCache[], compare: FunctionCache.Any, limit: INT.LAST, clientID: pd.deviceData]; -- Flush our stuff from the cache. }; 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.SetPriorityImportant[pd.context, TRUE]; 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 [context: Context, priorityImportant: BOOL] ~ { deviceData: DeviceData ~ NARROW[context.data]; deviceData.priorityImportant _ priorityImportant; [] _ PDFileWriter.SetPriorityImportant[deviceData.writer, priorityImportant]; }; MaskBox: PROC [context: Context, box: SF.Box] ~ { deviceData: DeviceData ~ NARROW[context.data]; SELECT deviceData.colorKind FROM nil => ERROR; -- color not initialized constant => { PDFileWriter.MaskRectangle[deviceData.writer, box.min.s, box.min.f, box.max.s-box.min.s, box.max.f-box.min.f]; }; sampled => { size: SF.Vec ~ SF.Size[box]; DeliverLines: PROC[p: PROC [LONG POINTER]] ~ { buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f]; bits: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[max: [1, size.f]]]; brick: ImagerPD.Tile ~ deviceData.halftoneBrick[deviceData.toner]^; Action: SAFE PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ { buffer.length _ pixels.length; ImagerSample.GetTileSamples[tile: brick.sampleMap, initIndex: min, buffer: buffer, phase: brick.phase]; ImagerSample.Halftone[map: bits, min: [0, 0], sampleBuffer: pixels[0], thresholdBuffer: buffer, function: [null, complement]]; TRUSTED { p[ImagerSample.GetBase[bits].word] }; }; Boxes: SF.BoxGenerator ~ {boxAction[box]}; ImagerPixel.Resample[self: deviceData.sampledSource, m: deviceData.paToDevice, interpolate: deviceData.interpolate, boxes: Boxes, bounds: box, action: Action]; ImagerSample.ReleaseScratchSamples[buffer]; ImagerSample.ReleaseScratchMap[bits]; }; PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner, sMin: box.min.s, fMin: box.min.f, sSize: size.s, fSize: size.f, deliverProc: DeliverLines, tFlag: opaque]; }; sampledBlack => { size: SF.Vec ~ SF.Size[box]; DeliverLines: PROC[p: PROC [LONG POINTER]] ~ { bits: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[max: [1, size.f]]]; Action: PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ { ImagerSample.PutSamples[map: bits, buffer: pixels[0]]; TRUSTED { p[ImagerSample.GetBase[bits].word] }; }; Boxes: SF.BoxGenerator ~ {boxAction[box]}; ImagerPixel.Resample[self: deviceData.sampledSource, m: deviceData.paToDevice, interpolate: deviceData.interpolate, boxes: Boxes, bounds: box, action: Action]; ImagerSample.ReleaseScratchMap[bits]; }; PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner, sMin: box.min.s, fMin: box.min.f, sSize: size.s, fSize: size.f, deliverProc: DeliverLines, tFlag: IF deviceData.colorClear THEN transparent ELSE opaque]; }; tile => { tFlag: PDFileWriter.TFlag ~ IF deviceData.colorClear THEN transparent ELSE opaque; deliverLines: PROC[p: PROC [LONG POINTER]] ~ TRUSTED { FOR s: INTEGER IN [box.min.s..box.max.s) DO line: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: [min: [s, box.min.f], max: [s+1, box.max.f]]]; ImagerSample.Tile[map: line, tile: deviceData.colorTile.sampleMap, phase: deviceData.colorTile.phase]; TRUSTED {p[ImagerSample.GetBase[line].word]}; ImagerSample.ReleaseScratchMap[line]; ENDLOOP; }; PDFileWriter.ColorSamples[pdState: deviceData.writer, toner: deviceData.toner, sMin: box.min.s, fMin: box.min.f, sSize: box.max.s-box.min.s, fSize: box.max.f-box.min.f, deliverProc: deliverLines, tFlag: tFlag]; }; ENDCASE => ERROR; -- illegal case }; useLoadForChars: BOOL _ TRUE; 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]; IF NOT useLoadForChars THEN RETURN [NIL]; WITH mask SELECT FROM raster: REF ImagerMaskCache.CharMaskRep.raster => { size: SF.Vec ~ SF.Size[raster.box]; loadWords: INT ~ SIZE[PDFileFormat.SampleArray] + Basics.LongMult[size.s, (size.f+bitsPerWord-1)/bitsPerWord]; IF loadWords > PDFileWriter.RemainingLoadSize[deviceData.writer] THEN RETURN[NIL]; -- load full TRUSTED {load _ PDFileWriter.LoadContiguousSampleArray[pdState: deviceData.writer, sSize: size.s, fSize: size.f, bitsPtr: @(raster[0])]}; }; runs: REF ImagerMaskCache.CharMaskRep.runs => { Runs: PROC [run: PROC [sMin, fMin: INTEGER, fSize: NAT]] ~ { s: INTEGER _ 0; sMax: INTEGER ~ SF.SizeS[runs.box]; FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO r: ImagerMaskCache.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 [context: Context, delta: SF.Vec, mask: CharMask] ~ { deviceData: DeviceData ~ NARROW[context.data]; loadEntry: REF INT _ GetLoadEntry[deviceData, mask]; sMin: NAT ~ delta.s+mask.box.min.s; fMin: NAT ~ delta.f+mask.box.min.f; IF deviceData.colorKind#constant THEN ERROR; IF 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 ImagerMaskCache.CharMaskRep.raster => { bitmap: ImagerSample.SampleMap ~ ImagerMaskCache.BitmapFromCharMask[raster]; MaskBitmap[context, bitmap, delta, SF.Displace[box: mask.box, t: delta], NIL]; }; runs: REF ImagerMaskCache.CharMaskRep.runs => { Runs: PROC [run: PDFileWriter.CaptureRunProc] ~ { s: INTEGER _ sMin; sMax: INTEGER ~ delta.s+mask.box.max.s; FOR i: NAT IN [0..runs.nRuns) WHILE s < sMax DO r: ImagerMaskCache.Run ~ runs[i]; IF r.fSize # 0 THEN run[s, r.fMin+fMin, r.fSize]; IF r.lastRun THEN s _ s + 1; ENDLOOP; }; PDFileWriter.MaskRunGroup[deviceData.writer, Runs]; }; ENDCASE => ERROR; }; MaskBoxes: PROC [context: Context, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { deviceData: DeviceData ~ NARROW[context.data]; SELECT deviceData.colorKind FROM nil => ERROR; -- color not initialized constant => { constantBox: PROC [box: SF.Box] ~ { PDFileWriter.MaskRectangle[deviceData.writer, box.min.s, box.min.f, box.max.s-box.min.s, box.max.f-box.min.f]; }; boxes[constantBox]; }; ENDCASE => { hardBox: PROC[box: SF.Box] ~ { MaskBox[context, box] }; boxes[hardBox]; }; }; MaskBitmap: PROC [context: Context, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { deviceData: DeviceData ~ NARROW[context.data]; SELECT deviceData.colorKind FROM nil => ERROR; -- color not initialized constant => { constantBox: PROC [box: SF.Box] ~ { deliverLines: PROC [p: PROC [LONG POINTER]] ~ TRUSTED { FOR s: INTEGER IN [box.min.s..box.max.s) DO line: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: [min: [s, box.min.f], max: [s+1, box.max.f]]]; ImagerSample.Transfer[dst: line, src: bitmap, delta: delta]; TRUSTED {p[ImagerSample.GetBase[line].word]}; ImagerSample.ReleaseScratchMap[line]; ENDLOOP; }; PDFileWriter.MaskSamples[pdState: deviceData.writer, sMin: box.min.s, fMin: box.min.f, sSize: box.max.s-box.min.s, fSize: box.max.f-box.min.f, deliverProc: deliverLines]; }; IF boxes = NIL THEN constantBox[bounds] ELSE boxes[constantBox]; }; ENDCASE => ERROR; -- SetColor should not have allowed bitmaps }; contextClass: ImagerPrivate.Class ~ ImagerRaster.CreateClass[type: $PD, deviceClass: deviceClass]; END.