DIRECTORY Basics, Font, Imager, ImagerBasic, ImagerBrick, ImagerConic, ImagerDefault, ImagerFontCache, ImagerHalftone, ImagerManhattan, ImagerMasks, ImagerPD, ImagerPDExtras, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, PDFileWriter, Real, RefText, Rope, Scaled ; ImagerPDImpl: CEDAR PROGRAM IMPORTS Imager, ImagerConic, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, PDFileWriter, Real, RefText, Scaled, ImagerDefault, ImagerManhattan EXPORTS ImagerPD, ImagerPDExtras SHARES ImagerTransform ~ BEGIN OPEN ImagerBasic; PDFileDescription: TYPE ~ ImagerPD.PDFileDescription; PDFileDescriptionRep: TYPE ~ ImagerPD.PDFileDescriptionRep; FONT: TYPE ~ Font.FONT; ROPE: TYPE ~ Font.ROPE; DeviceCode: TYPE ~ PDFileWriter.DeviceCode; Mask: TYPE ~ ImagerMasks.Mask; Toner: TYPE ~ PDFileWriter.Toner; TransformationRec: TYPE ~ ImagerTransform.TransformationRec; PixelMap: TYPE ~ ImagerPixelMaps.PixelMap; Tile: TYPE ~ ImagerPixelMaps.Tile; SpecialColor: TYPE ~ REF ColorRep[special]; Name: TYPE ~ ImagerPrivate.Name; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; Context: TYPE ~ Imager.Context; State: TYPE ~ ImagerDefault.State; Raven: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, raven, 300, 300, 2550, 3300, 1, 16, 60000, TRUE, 1 ]]] }; Hornet: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, hornet, 384, 384, 3264, 4224, 1, 16, 60000, TRUE, 1 ]]] }; Gnat: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, gnat, 384, 384, 4224, 5376, 1, 16, 60000, TRUE, 1 ]]] }; PlateMaker: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, mig, 880, 880, 9680, 7480, 1, 16, 60000, TRUE, 1 ]]] }; ReticleMaker: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, reticleMaker, 880, 880, 8800, 8800, 1, 16, 60000, TRUE, 1 ]]] }; Puffin: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, puffin, 384, 384, 4224, 3264, 3, 16, 60000, TRUE, 1 ]]] }; ColorVersatec: PUBLIC PROC [fileName: ROPE] RETURNS [PDFileDescription] ~ { RETURN [NEW[PDFileDescriptionRep _ [ fileName, VAL[7], 200, 200, 20000, 8000, 4, 64, 100000, FALSE, 1 ]]] }; StatsRecord: TYPE = RECORD[ loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT _ 0]; stats: StatsRecord _ []; Stats: PROC RETURNS[StatsRecord] = { x: StatsRecord = stats; stats _ []; RETURN[x] }; trc: ARRAY [0..64) OF [0..256] _ InitTRC[]; InitTRC: PROC RETURNS [trc: ARRAY [0..64) OF [0..256]] ~ { FOR i: [0..64) IN [0..64) DO trc[i] _ Real.RoundLI[i/63.0*256.0]; ENDLOOP; }; dummyLoadReference: PDFileWriter.LoadReference ~ LAST[PDFileWriter.LoadReference]; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ viewClipper: ManhattanPolygon, -- in device coords clientClipper: ClientClipper, -- for noticing client clipper changes compositeClipper: ManhattanPolygon, -- in device coords; invalid if NIL or clientClipper#state.clipper xScale, yScale: REAL, viewOrigin: IntPair, surfaceToDevice: Transformation, surfaceBounds: DeviceRectangle, devicePath: ImagerScanConverter.DevicePath, cachedColor: Color _ NIL, grayTileRef: ARRAY Toner OF ARRAY [0..64) OF PDFileWriter.LoadReference, stipples: LIST OF StippleRep _ NIL, cachedT: Transformation _ NIL, compositeT: Transformation _ NIL, fontCache: ImagerFontCache.FontCache, pdState: PDFileWriter.PDState, firstToner: NAT _ 0, lastToner: NAT _ 0, currentToner: NAT _ 1, -- set to SUCC[lastToner] if no toner was explicitly selected. leftoverMode: BOOLEAN, pdStatePriorityImportant: INT _ 0 ]; CurrentToner: PROC [data: Data] RETURNS [toner: Toner] ~ INLINE { RETURN [IF data.currentToner > data.lastToner THEN black ELSE VAL[data.currentToner]] }; StippleRep: TYPE ~ RECORD [ loadReference: PDFileWriter.LoadReference, stipple: PixelMap ]; Round: PROC [r: REAL] RETURNS [INTEGER] ~ { IF r > LAST[INTEGER] THEN RETURN [LAST[INTEGER]]; IF r < FIRST[INTEGER] THEN RETURN [FIRST[INTEGER]]; RETURN [Real.RoundI[r]]; }; CompositeT: PROC [data: Data, state: State] RETURNS [Transformation] ~ { pre: Transformation _ state.T; post: Transformation _ data.surfaceToDevice; t: Transformation _ IF data.compositeT = NIL THEN data.compositeT _ NEW[ImagerTransform.TransformationRep] ELSE data.compositeT; IF data.cachedT # pre THEN { prec: REAL _ pre.c + data.viewOrigin.x; pref: REAL _ pre.f + data.viewOrigin.y; t^ _ [ a: pre.a*post.a + pre.d*post.b, d: pre.a*post.d + pre.d*post.e, b: pre.b*post.a + pre.e*post.b, e: pre.b*post.d + pre.e*post.e, c: prec*post.a + pref*post.b + post.c, f: prec*post.d + pref*post.e + post.f ]; data.cachedT _ pre; }; RETURN [t]; }; Init: PROC [context: Context, info: REF] ~ { data: Data ~ NEW[DataRep]; desc: PDFileDescription ~ NARROW[info]; sScale: REAL ~ desc.sResolution/0.0254; fScale: REAL ~ desc.fResolution/0.0254; context.data _ data; data.surfaceBounds _ [sMin: 0, fMin: 0, sSize: desc.imageSSize, fSize: desc.imageFSize]; IF desc.imageFSize >= desc.imageSSize THEN { data.surfaceToDevice _ ImagerTransform.Create[1, 0, 0, 0, 1, 0]; data.xScale _ sScale; data.yScale _ fScale; } ELSE { data.surfaceToDevice _ ImagerTransform.Create[0, -1, desc.imageSSize, 1, 0, 0]; data.xScale _ fScale; data.yScale _ sScale; }; SetViewOrigin[context, [0, 0]]; SetViewBox[context, GetSurfaceBounds[context]]; data.pdState _ PDFileWriter.Create[fileName: desc.fileName, deviceCode: desc.deviceCode, sResolution: desc.sResolution, fResolution: desc.fResolution, imageSSize: desc.imageSSize, imageFSize: desc.imageFSize, bandSSize: desc.bandSSize, copies: desc.copies, leftOverMode: desc.leftovers]; data.leftoverMode _ desc.leftovers; data.fontCache _ ImagerFontCache.Create[]; FOR toner: Toner IN Toner DO FOR i: [0..64) IN [0..64) DO data.grayTileRef[toner][i] _ dummyLoadReference ENDLOOP ENDLOOP; data.firstToner _ IF desc.nColors = 3 THEN 1 ELSE 0; data.lastToner _ data.firstToner + desc.nColors - 1; data.currentToner _ data.lastToner + 1; ImagerDefault.InitState[context]; Reset[context]; }; Reset: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; state: State ~ NARROW[context.state]; surfaceSize: Pair ~ ImagerTransform.InverseTransformVec[ [data.surfaceBounds.sSize, data.surfaceBounds.fSize], data.surfaceToDevice ]; ImagerDefault.Reset[context]; state.T _ ImagerTransform.Scale2[data.xScale, data.yScale]; state.fieldXMax _ state.mediumXSize _ surfaceSize.x/data.xScale; state.fieldYMax _ state.mediumYSize _ surfaceSize.y/data.yScale; data.compositeClipper _ NIL; data.cachedColor _ NIL; }; DevicePathFromViewPath: PROC [context: Context, pathMap: PathMapType, pathData: REF] RETURNS [ImagerScanConverter.DevicePath] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; GenPath: ImagerScanConverter.PathProc ~ { Xform: PROC [p: Pair] RETURNS [Pair] ~ { p.y _ p.y + data.viewOrigin.y; p.x _ p.x + data.viewOrigin.x; RETURN[ImagerTransform.Transform[p, data.surfaceToDevice]]; }; Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp _ q }; Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp _ q }; Xcurve: PROC [p1, p2, p3: Pair] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3]; curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp _ q3 }; Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]}; Xconic: PROC [p1, p2: Pair, r: REAL] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; ImagerConic.ToCurves[lp, q1, q2, r, Curve]; lp _ q2; }; lp: Pair; pathMap[pathData, Xmove, Xline, Xcurve, Xconic]; }; RETURN [ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.surfaceBounds]]; }; DevicePathFromPath: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; GenPath: ImagerScanConverter.PathProc ~ { m: TransformationRec ~ CompositeT[data, state].Contents; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.a * p.x + m.b * p.y + m.c, m.d * p.x + m.e * p.y + m.f ]]}; Xmove: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; move[q.x, q.y]; lp _ q }; Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp _ q }; Xcurve: PROC [p1, p2, p3: Pair] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; q3: Pair ~ Xform[p3]; curve[q1.x, q1.y, q2.x, q2.y, q3.x, q3.y]; lp _ q3 }; Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]}; Xconic: PROC [p1, p2: Pair, r: REAL] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; ImagerConic.ToCurves[lp, q1, q2, r, Curve]; lp _ q2; }; lp: Pair; pathMap[pathData, Xmove, Xline, Xcurve, Xconic]; }; data.devicePath _ ImagerScanConverter.CreatePath[pathProc: GenPath, clipBox: data.surfaceBounds, scratch: data.devicePath]; }; DevicePathFromStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; data.devicePath _ ImagerStroke.DevicePathFromStroke[ pathMap: pathMap, pathData: pathData, clientToDevice: CompositeT[data, state], width: IF LOOPHOLE[strokeWidth, LONG CARDINAL] # LOOPHOLE[Imager.defaultStrokeWidth, LONG CARDINAL] THEN strokeWidth ELSE state.strokeWidth, strokeEnd: IF strokeEnd # nil THEN strokeEnd ELSE SELECT state.strokeEnd FROM 0 => square, 1 => butt, 2 => round, ENDCASE => nil, closed: closed, clipBox: data.surfaceBounds, scratch: data.devicePath ]; }; CompositeClipper: PROC [context: Context] RETURNS [ManhattanPolygon] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; mp: ManhattanPolygon _ data.compositeClipper; IF mp = NIL OR data.clientClipper # state.clipper THEN { ConcatClippers: PROC [l: LIST OF ClientClipperItem] ~ { IF l#NIL THEN { t1, t2: ManhattanPolygon; ConcatClippers[l.rest]; t1 _ ImagerScanConverter.ConvertToManhattanPolygon[DevicePathFromViewPath[context: context, pathMap: l.first.pathMap, pathData: l.first.pathData], data.surfaceBounds]; t2 _ mp; mp _ IF l.first.exclude THEN mp.Difference[t1] ELSE mp.Intersection[t1]; ImagerManhattan.Destroy[t1]; ImagerManhattan.Destroy[t2]; }; }; ImagerManhattan.Destroy[data.compositeClipper]; data.clientClipper _ state.clipper; mp _ ImagerManhattan.Copy[data.viewClipper]; ConcatClippers[data.clientClipper]; data.compositeClipper _ mp; }; RETURN [mp] }; MakeTileSamples: PROC [xSize, ySize: NAT, sample: NAT] RETURNS [tileSamples: PixelArray] ~ { tileSamples _ NEW[PixelArrayRep]; tileSamples.m _ ImagerTransform.Rotate[0]; tileSamples.xPixels _ xSize; tileSamples.yPixels _ ySize; tileSamples.maxSampleValue _ 255; tileSamples.samplesPerPixel _ 1; tileSamples.get _ ConstantGet; tileSamples.data _ NEW[NAT _ sample]; }; ConstantGet: PROC [self: PixelArray, buffer: PixelBuffer, nSamples: NAT, layer: INT, xStart, yStart: Scaled.Value, xDelta, yDelta: Scaled.Value] ~ { WITH self.data SELECT FROM n: REF NAT => { value: NAT _ n^; FOR i: NAT IN [0..nSamples) DO buffer[i] _ value ENDLOOP; }; ENDCASE => ERROR; }; deviceBrickDefault: ImagerHalftone.DeviceBrick _ ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, 255]; ConstructTile: PROC [data: Data, toner: Toner, index: [0..64), customBrick: ImagerBrick.Brick _ NIL] ~ { deviceBrick: ImagerHalftone.DeviceBrick _ IF customBrick # NIL THEN ImagerHalftone.MakeDeviceBrick[customBrick, 255] ELSE ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, 255]; pixelMap: ImagerPixelMaps.PixelMap _ ImagerPixelMaps.Create[0, [0, 0, deviceBrick.sPeriod, deviceBrick.fPeriod]]; loadReference: PDFileWriter.LoadReference; Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ { FOR s: INTEGER IN [0..deviceBrick.sPeriod) DO run[s, 0, deviceBrick.fPeriod]; ENDLOOP; }; ImagerHalftone.Halftone[ dest: pixelMap, runs: Runs, source: MakeTileSamples[16, 16, trc[index]], transformation: ImagerTransform.Rotate[0], deviceBrick: deviceBrick ]; loadReference _ data.pdState.LoadContiguousColorTile[phase: deviceBrick.phase, sMin: 0, fMin: 0, sSize: deviceBrick.sPeriod, fSize: deviceBrick.fPeriod, bitsPtr: pixelMap.refRep.pointer]; data.grayTileRef[toner][index] _ loadReference; }; maxColorTileArea: REAL _ 4*4096; CurrentColor: PROC [context: Context] RETURNS [color: Color] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; SetConstantColor: PROC [intensity: REAL] ~ { index: [0..64) _ Real.RoundI[MAX[MIN[intensity*63, 63], 0]]; DoToner: PROC [toner: Toner] = { IF index = 0 THEN data.pdState.SetColorInk[toner] ELSE IF index = 63 THEN data.pdState.SetColorClear[toner] ELSE { IF data.grayTileRef[toner][index] = dummyLoadReference THEN ConstructTile[data, toner, index, NARROW[Imager.GetProp[context, $CustomBrick]]]; data.pdState.SetColorTile[toner, data.grayTileRef[toner][index]] }; }; data.pdState.DoForEachToner[DoToner]; }; NoteColorChange: PROC ~ { DoToner: PROC [toner: Toner] = {data.pdState.SetColorClear[toner]}; data.pdState.DoForEachToner[DoToner]; }; IF data.pdStatePriorityImportant # state.priorityImportant THEN { [] _ data.pdState.SetPriorityImportant[state.priorityImportant#0]; data.pdStatePriorityImportant _ state.priorityImportant; }; IF data.cachedColor = state.color THEN RETURN [data.cachedColor]; color _ data.cachedColor _ state.color; IF color = Imager.black THEN { DoToner: PROC [toner: Toner] ~ {data.pdState.SetColorInk[toner]}; data.pdState.DoForEachToner[DoToner]; } ELSE IF color = Imager.white THEN { DoToner: PROC [toner: Toner] ~ {data.pdState.SetColorClear[toner]}; data.pdState.DoForEachToner[DoToner]; } ELSE WITH color SELECT FROM constantColor: ConstantColor => { SetConstantColor[constantColor.Y/65536.0]; }; specialColor: SpecialColor => { WITH specialColor.ref SELECT FROM stipple: REF CARDINAL => { oneBits: NAT _ 0; FOR v: CARDINAL _ stipple^, v/2 UNTIL v=0 DO oneBits _ oneBits + v MOD 2 ENDLOOP; SetConstantColor[(16-oneBits)/16.0]; }; ENDCASE => ERROR Imager.Error[$UnknownSpecialColor]; }; sampledColor: SampledColor => { NoteColorChange[]; IF sampledColor.colorOperator = $SampledBlack THEN { transform: Transformation _ ImagerTransform.Concat[ImagerTransform.Concat[ImagerTransform.Concat[ sampledColor.pa.m, sampledColor.m], ImagerTransform.Translate[data.viewOrigin.x, data.viewOrigin.y]], data.surfaceToDevice]; tileRect: Rectangle _ ImagerTransform.TransformRectangle[[0, 0, sampledColor.pa.xPixels, sampledColor.pa.yPixels], transform]; transformRec: TransformationRec _ ImagerTransform.Contents[transform]; easyTransform: BOOLEAN ~ (transformRec.a = 0 AND transformRec.e = 0) OR (transformRec.b = 0 AND transformRec.d = 0); tileArea: REAL _ tileRect.w*tileRect.h; IF easyTransform AND IsInteger[tileRect.w] AND IsInteger[tileRect.h] AND tileArea <= maxColorTileArea AND data.pdState.RemainingLoadSize > Real.RoundLI[tileArea/16] + 3500 THEN { mask: Mask _ ImagerMasks.FromPixelArray[sampledColor.pa, transform]; bb: DeviceRectangle _ ImagerMasks.BoundingBox[mask]; pixels: PixelMap _ ImagerMasks.ToPixelMap[mask, ImagerMasks.FromRectangle[bb]]; tFlag: PDFileWriter.TFlag _ IF sampledColor.transparent THEN transparent ELSE opaque; color _ Imager.black; FOR s: LIST OF StippleRep _ data.stipples, s.rest UNTIL s = NIL DO IF s.first.stipple.Equal[pixels] THEN { data.pdState.SetColorTile[CurrentToner[data], s.first.loadReference, tFlag]; pixels.refRep.pointer _ NIL; pixels.refRep.ref _ NIL; pixels.refRep _ NIL; RETURN; }; ENDLOOP; data.stipples _ CONS[[ loadReference: data.pdState.LoadContiguousColorTile[ phase: 0, sMin: pixels.sMin+pixels.sOrigin, fMin: pixels.fMin+pixels.fOrigin, sSize: pixels.sSize, fSize: pixels.fSize, bitsPtr: pixels.refRep.pointer ], stipple: pixels], data.stipples]; data.pdState.SetColorTile[CurrentToner[data], data.stipples.first.loadReference, tFlag]; RETURN; }; data.pdState.SetColorInk[CurrentToner[data]]; }; }; ENDCASE => state.color _ Imager.black; }; IsInteger: PROC [r: REAL] RETURNS [BOOLEAN] ~ { IF ABS[r] > 32000 THEN RETURN [FALSE]; RETURN [Real.RoundLI[r] = (r+1000.0)-1000.0]; }; ApplyMask: PROC [context: Context, mask: REF, sTranslate, fTranslate: INTEGER _ 0] ~ { state: State ~ NARROW[context.state]; IF state.noImage = 0 THEN { data: Data ~ NARROW[context.data]; color: Color ~ CurrentColor[context]; clipper: ManhattanPolygon _ CompositeClipper[context]; WITH color SELECT FROM sampledColor: SampledColor => TRUSTED { lineBuffer: ImagerPixelMaps.PixelMap _ ImagerPixelMaps.Create[0, [0, 0, 1, data.surfaceBounds.fSize]]; customBrick: ImagerBrick.Brick _ NARROW[Imager.GetProp[context, $CustomBrick]]; brick: ImagerHalftone.DeviceBrick _ IF customBrick # NIL THEN ImagerHalftone.MakeDeviceBrick[customBrick, sampledColor.pa.maxSampleValue] ELSE ImagerHalftone.MakeSquareBrick[16, 3, 2, 1, 0.5, sampledColor.pa.maxSampleValue]; transform: Transformation _ ImagerTransform.Concat[ImagerTransform.Concat[ImagerTransform.Concat[ sampledColor.pa.m, sampledColor.m], ImagerTransform.Translate[data.viewOrigin.x, data.viewOrigin.y]], data.surfaceToDevice]; linePointer: LONG POINTER _ lineBuffer.refRep.pointer; Line: PROC [proc: PROC [LONG POINTER]] ~ TRUSTED {proc[linePointer]}; SampledColorRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ TRUSTED { run[sMin, fMin, fSize]; }; lineBuffer.sOrigin _ sMin; lineBuffer.fOrigin _ fMin; lineBuffer.fSize _ fSize; ImagerHalftone.Halftone[lineBuffer, Runs, sampledColor.pa, transform, brick, invertOutput]; PDFileWriter.ColorSamples[ pdState: data.pdState, toner: CurrentToner[data], sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize, deliverProc: Line, tFlag: IF sampledColor.transparent THEN transparent ELSE opaque ]; }; invertOutput: BOOLEAN _ FALSE; SELECT sampledColor.colorOperator FROM $SampledBlack => invertOutput _ TRUE; $Intensity => NULL; ENDCASE => Imager.Error[$UnknownColorModel]; ImagerMasks.GenerateRuns[mask, clipper, SampledColorRun, sTranslate, fTranslate]; }; ENDCASE => { DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] ~ { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { SendRun[sMin, fMin, fSize]; }; ImagerMasks.GenerateRuns[mask, clipper, Run, sTranslate, fTranslate]; }; PDFileWriter.MaskRunGroup[data.pdState, DeliverRuns]; }; }; }; MaskStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd] ~ { data: Data ~ NARROW[context.data]; DevicePathFromStroke[context, pathMap, pathData, strokeWidth, strokeEnd, FALSE]; ApplyMask[context, data.devicePath]; }; MaskStrokeClosed: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL] = { data: Data ~ NARROW[context.data]; DevicePathFromStroke[context, pathMap, pathData, strokeWidth, nil, TRUE]; ApplyMask[context, data.devicePath]; }; MaskFill: PROC[context: Context, pathMap: PathMapType, pathData: REF] ~ { data: Data ~ NARROW[context.data]; DevicePathFromPath[context, pathMap, pathData]; ApplyMask[context, data.devicePath]; }; MaskPixel: PROC [context: Context, pa: PixelArray] ~ { data: Data _ NARROW[context.data]; state: State ~ NARROW[context.state]; trans: Transformation ~ CompositeT[data, state]; mask: REF _ ImagerMasks.FromPixelArray[pa, ImagerTransform.Concat[pa.m, trans]]; ApplyMask[context, mask]; mask _ NIL; }; specialCaseRectangles: BOOLEAN _ TRUE; MaskRectangle: PROC [context: Context, x, y, w, h: REAL] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; compositeT: Transformation ~ CompositeT[data, state]; IF specialCaseRectangles AND Easy[compositeT] THEN { clipper: ManhattanPolygon ~ CompositeClipper[context]; color: Color ~ CurrentColor[context]; IF clipper# NIL AND clipper.rest = NIL AND color.tag = constant THEN { drect: ImagerPixelMaps.DeviceRectangle _ DeviceRectangleFromEasyBox[compositeT, x, y, w, h]; drect _ ImagerPixelMaps.Intersect[drect, clipper.first]; IF drect.sSize # 0 THEN PDFileWriter.MaskRectangle[data.pdState, drect.sMin, drect.fMin, drect.sSize, drect.fSize]; } ELSE { t1: ManhattanPolygon _ ManhattanPolygonFromEasyBox[compositeT, x, y, w, h]; clipped: ManhattanPolygon _ t1.Intersection[clipper]; IF clipped = NIL THEN NULL ELSE IF clipped.rest = NIL AND color.tag = constant THEN { PDFileWriter.MaskRectangle[data.pdState, clipped.first.sMin, clipped.first.fMin, clipped.first.sSize, clipped.first.fSize]; } ELSE ApplyMask[context, t1, 0, 0]; ImagerManhattan.Destroy[t1]; ImagerManhattan.Destroy[clipped]; }; } ELSE { PathMap: PathMapType ~ { move[[x, y]]; line[[x+w, y]]; line[[x+w, y+h]]; line[[x, y+h]]; }; MaskFill[context, PathMap, NIL]; }; }; IntegerMaskRectangle: PROC[context: Context, x, y, w, h: INTEGER] = { MaskRectangle[context, x, y, w, h]; }; rasterToRunGroupStorageRatio: INT _ 1; CharRepresentation: TYPE ~ {null, runGroup, raster}; CharLoadInfo: TYPE ~ RECORD[ sWidth, fWidth: Scaled.Value, loadRef: PDFileWriter.LoadReference, representation: CharRepresentation, mask: Mask, loadRepSize: INT ]; GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR] RETURNS [ImagerManhattan.Polygon] ~ { Runs: PROC [ run: PROC [sMin, fMin: INTEGER, fSize: NAT], repeat: PROC [timesToRepeatScanline: NAT]] ~ { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { run[sMin: sMin, fMin: fMin, fSize: fSize]; }; font.fontGraphicsClass.maskProc[font, transformation, char, Run]; }; RETURN [ImagerManhattan.CreateFromRuns[Runs]] }; FontCompositeTransform: PROC [context: Context, font: FONT] RETURNS [t: Transformation] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; r: TransformationRec _ CompositeT[data, state].Contents; r.c _ r.f _ 0; t _ ImagerTransform.Concat[font.actualTransformation, r.FromRec]; }; bigRectangle: LIST OF DeviceRectangle ~ LIST[[0, 0, LAST[NAT]/2, LAST[NAT]/2]]; LoadRunGroup: PROC [pdState: PDFileWriter.PDState, mask: Mask] RETURNS [loadReference: PDFileWriter.LoadReference] ~ { DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {SendRun[sMin, fMin, fSize]}; ImagerMasks.GenerateRuns[mask, bigRectangle, Run, -bb.sMin, -bb.fMin]; }; bb: DeviceRectangle _ ImagerMasks.BoundingBox[mask]; loadReference _ pdState.LoadRunGroup[DeliverRuns]; stats.loadRunGroups _ stats.loadRunGroups + 1; }; LoadBitmap: PROC [pdState: PDFileWriter.PDState, mask: Mask] RETURNS [loadReference: PDFileWriter.LoadReference] ~ { WITH mask SELECT FROM b: REF ImagerPixelMaps.PixelMap => { loadReference _ pdState.LoadContiguousSampleArray[sSize: b^.sSize, fSize: b^.fSize, bitsPtr: b^.refRep.pointer]; }; ENDCASE => ERROR; stats.loadRasters _ stats.loadRasters + 1; }; LoadCharData: PROC[self: ImagerFontCache.FontObject, charCode: CARDINAL] RETURNS [charData: REF, memoryCost: INT] ~ { font: FONT ~ NARROW[self.fontAnyData]; char: CHAR ~ VAL[charCode]; transform: Transformation ~ ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device clientTransform: Transformation ~ ImagerTransform.Concat[ ImagerTransform.Invert[font.actualTransformation], transform]; loadInfo: REF CharLoadInfo ~ NEW[CharLoadInfo]; mask: ImagerManhattan.Polygon ~ GetCharMask[font, transform, char]; nRuns: INT ~ ImagerManhattan.CountRuns[mask]; bb: DeviceRectangle ~ ImagerManhattan.BoundingBox[mask]; runGroupSize: INT _ 2 * nRuns; rasterSize: INT _ bb.sSize * INT[(bb.fSize+15)/16] + 2; width: Pair _ ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], clientTransform]; loadInfo.loadRef _ dummyLoadReference; loadInfo.mask _ mask; SELECT TRUE FROM bb.sSize = 0 OR bb.fSize = 0 => { loadInfo.representation _ null; loadInfo.loadRepSize _ 0; }; bb.fSize > 32*Basics.bitsPerWord OR runGroupSize*rasterToRunGroupStorageRatio < rasterSize => { loadInfo.representation _ runGroup; loadInfo.loadRepSize _ runGroupSize; }; ENDCASE => { loadInfo.representation _ raster; loadInfo.loadRepSize _ rasterSize; loadInfo.mask _ ImagerMasks.FromBitmap[ImagerMasks.ToPixelMap[mask, ImagerMasks.FromRectangle[bb]]]; ImagerManhattan.Destroy[mask]; }; loadInfo.sWidth _ Scaled.FromReal[width.x]; loadInfo.fWidth _ Scaled.FromReal[width.y]; RETURN [loadInfo, 0]; }; ShowChar: PROC [context: Context, char: CHAR, font: FONT] ~ { text: REF TEXT _ RefText.ObtainScratch[1]; text.length _ 1; text[0] _ char; ShowCharacters[context, text, font, 0, 1]; RefText.ReleaseScratch[text]; }; loadSpaceReservedForColorTiles: INT _ 3500; biggishInt: REAL _ INT.LAST/2; ShowCharacters: PROC [ context: Context, characters: REF, -- may be a ROPE or a REF TEXT font: FONT, start: INT _ 0, length: INT _ LAST[INT] ] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; showVec: FONT _ IF font = NIL THEN NARROW[state.showVec] ELSE font; surfaceToDevice: TransformationRec _ data.surfaceToDevice.Contents; TransformDeviceToViewVec: PROC [s, f: Scaled.Value] RETURNS [v: Pair] ~ { v _ IF surfaceToDevice.a = 0 THEN [x: Scaled.Float[f], y: -Scaled.Float[s]] ELSE [x: Scaled.Float[s], y: Scaled.Float[f]] }; TransformViewToDevice: PROC [v: Pair] RETURNS [Pair] ~ {RETURN [ IF surfaceToDevice.a = 0 THEN [-(v.y+data.viewOrigin.y) + surfaceToDevice.c, (v.x+data.viewOrigin.x) + surfaceToDevice.f] ELSE [(v.x+data.viewOrigin.x) + surfaceToDevice.c, (v.y+data.viewOrigin.y) + surfaceToDevice.f] ]}; clipper: ManhattanPolygon _ CompositeClipper[context]; transform: TransformationRec ~ FontCompositeTransform[context, showVec].Contents; fontCode: ImagerFontCache.FontCode _ ImagerFontCache.GetFontCode[[ CharDataProc: LoadCharData, r0: transform.a, r1: transform.b, r2: transform.c, r3: transform.d, r4: transform.e, r5: transform.f, fontScalarData: 0, -- unused fontAnyData: showVec]]; bbScratch: LIST OF DeviceRectangle _ LIST[[0,0,0,0]]; noImage: BOOLEAN ~ state.noImage # 0; currentColor: Color ~ CurrentColor[context]; DoChar: PROC[charCode: CARDINAL, charData: REF] = { loadInfo: REF CharLoadInfo ~ NARROW[charData]; delta: Pair _ TransformDeviceToViewVec[loadInfo.sWidth, loadInfo.fWidth]; cp: Pair _ TransformViewToDevice[[state.cpx, state.cpy]]; bbScratch.first _ ImagerMasks.BoundingBox[loadInfo.mask]; IF bbScratch.first.sSize > 0 AND NOT noImage AND ABS[cp.x] <= biggishInt AND ABS[cp.y] < biggishInt THEN { sCP: INT _ Real.RoundLI[cp.x]; fCP: INT _ Real.RoundLI[cp.y]; sMinTranslated: INT _ bbScratch.first.sMin + sCP; fMinTranslated: INT _ bbScratch.first.fMin + fCP; visibility: Visibility; IF ABS[sMinTranslated] <= INTEGER.LAST AND ABS[fMinTranslated] <= INTEGER.LAST THEN { bbScratch.first.sMin _ sMinTranslated; bbScratch.first.fMin _ fMinTranslated; visibility _ ImagerManhattan.IsVisible[bbScratch, clipper]; } ELSE { visibility _ invisible; }; IF visibility = visible THEN WITH currentColor SELECT FROM sampled: SampledColor => visibility _ partlyVisible; ENDCASE => NULL; IF loadInfo.loadRef = dummyLoadReference AND visibility = visible THEN { IF data.pdState.RemainingLoadSize > loadInfo.loadRepSize + loadSpaceReservedForColorTiles THEN { SELECT loadInfo.representation FROM raster => loadInfo.loadRef _ LoadBitmap[data.pdState, loadInfo.mask]; runGroup => loadInfo.loadRef _ LoadRunGroup[data.pdState, loadInfo.mask]; ENDCASE; } ELSE visibility _ partlyVisible; }; SELECT visibility FROM visible => { SELECT loadInfo.representation FROM raster => { IF loadInfo.loadRef = dummyLoadReference THEN ERROR; data.pdState.MaskSamplesRef[loadInfo.loadRef, bbScratch.first.sMin, bbScratch.first.fMin]; stats.rasterChars _ stats.rasterChars + 1; }; runGroup => { IF loadInfo.loadRef = dummyLoadReference THEN ERROR; data.pdState.MaskRunGroupRef[loadInfo.loadRef, bbScratch.first.sMin, bbScratch.first.fMin]; stats.runGroupChars _ stats.runGroupChars + 1; }; null => NULL; ENDCASE => ERROR; }; partlyVisible => { ApplyMask[context, loadInfo.mask, sCP, fCP]; stats.clippedChars _ stats.clippedChars + 1; }; invisible => {stats.culledChars _ stats.culledChars + 1}; ENDCASE => ERROR; }; IF charCode = ORD[' ] THEN { IF state.amplifySpace # 1.0 THEN { delta.x _ delta.x * state.amplifySpace; delta.y _ delta.y * state.amplifySpace; }; ImagerDefault.CorrectSpaceView[context, delta]; } ELSE { ImagerDefault.CorrectMask[context]; }; state.cpx _ state.cpx + delta.x; state.cpy _ state.cpy + delta.y; }; ImagerFontCache.GetStringData[DoChar, data.fontCache, fontCode, characters, start, length]; }; ManhattanPolygonFromSurfaceRectangle: PROC [context: Context, box: IntRectangle] RETURNS [LIST OF DeviceRectangle] ~ { data: Data ~ NARROW[context.data]; deviceBox: IntRectangle _ ImagerTransform.TransformIntRectangle[[x: box.x, y: box.y, w: box.w, h: box.h], data.surfaceToDevice]; RETURN [ImagerManhattan.CreateFromBox[[deviceBox.x, deviceBox.y, deviceBox.w, deviceBox.h]]]; }; SetViewOrigin: PROC [context: Context, viewOrigin: IntPair] ~ { data: Data _ NARROW[context.data]; data.viewOrigin _ viewOrigin; data.cachedT _ NIL; }; GetViewOrigin: PROC [context: Context] RETURNS [viewOrigin: IntPair] ~ { data: Data ~ NARROW[context.data]; RETURN[data.viewOrigin]; }; SetViewBox: PROC [context: Context, viewBox: IntRectangle] ~ { data: Data _ NARROW[context.data]; surfaceBox: IntRectangle _ [ x: viewBox.x+data.viewOrigin.x, y: viewBox.y+data.viewOrigin.y, w: viewBox.w, h: viewBox.h ]; mask: ImagerManhattan.Polygon _ ManhattanPolygonFromSurfaceRectangle[context, surfaceBox]; ImagerManhattan.Destroy[data.compositeClipper]; data.compositeClipper _ NIL; ImagerManhattan.Destroy[data.viewClipper]; data.viewClipper _ mask; }; GetViewBox: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data ~ NARROW[context.data]; deviceBox: DeviceRectangle _ ImagerManhattan.BoundingBox[data.viewClipper]; deviceToSurface: Transformation _ ImagerTransform.Invert[data.surfaceToDevice]; surfaceBox: IntRectangle _ ImagerTransform.TransformIntRectangle[[deviceBox.sMin, deviceBox.fMin, deviceBox.sSize, deviceBox.fSize], deviceToSurface]; RETURN[[ x: surfaceBox.x-data.viewOrigin.x, y: surfaceBox.y-data.viewOrigin.y, w: surfaceBox.w, h: surfaceBox.h ]]; }; ClipView: PROC [context: Context, clipBox: IntRectangle, exclude: BOOLEAN] ~ { data: Data _ NARROW[context.data]; surfaceBox: IntRectangle _ [ x: clipBox.x+data.viewOrigin.x, y: clipBox.y+data.viewOrigin.y, w: clipBox.w, h: clipBox.h ]; newBox: ImagerManhattan.Polygon _ ManhattanPolygonFromSurfaceRectangle[context, surfaceBox]; old: ImagerManhattan.Polygon _ data.viewClipper; ImagerManhattan.Destroy[data.compositeClipper]; data.compositeClipper _ NIL; data.viewClipper _ IF exclude THEN ImagerManhattan.Difference[old, newBox] ELSE ImagerManhattan.Intersection[old, newBox]; newBox.rest _ old; ImagerManhattan.Destroy[newBox]; }; MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] ~ { ERROR Imager.Error[$NotImplementedForThisDevice]; }; Zero: PROC [real: REAL] RETURNS [BOOLEAN] ~ INLINE {RETURN [real=0.0]}; Easy: PROC [trans: Transformation] RETURNS [BOOLEAN] ~ { RETURN [(Zero[trans.Contents.b] AND Zero[trans.Contents.d]) OR (Zero[trans.Contents.a] AND Zero[trans.Contents.e])] }; ManhattanPolygonFromEasyBox: PROC [compositeT: Transformation, x, y, w, h: REAL] RETURNS [manhattanPolygon: ImagerManhattan.Polygon] ~ { manhattanPolygon _ ImagerManhattan.CreateFromBox[DeviceRectangleFromEasyBox[compositeT, x, y, w, h]]; }; DeviceRectangleFromEasyBox: PROC [compositeT: Transformation, x, y, w, h: REAL] RETURNS [deviceRectangle: DeviceRectangle] ~ { trans: TransformationRec ~ compositeT.Contents; x1: REAL ~ x+w; y1: REAL ~ y+h; s0: INTEGER ~ Round[trans.a * x + trans.b * y + trans.c]; s1: INTEGER ~ Round[trans.a * x1 + trans.b * y1 + trans.c]; f0: INTEGER ~ Round[trans.d * x + trans.e * y + trans.f]; f1: INTEGER ~ Round[trans.d * x1 + trans.e * y1 + trans.f]; sMin: INTEGER ~ MIN[s0, s1]; sMax: INTEGER ~ MAX[s0, s1]; fMin: INTEGER ~ MIN[f0, f1]; fMax: INTEGER ~ MAX[f0, f1]; deviceRectangle _ [sMin, fMin, sMax-sMin, fMax-fMin]; }; TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; compositeT: Transformation ~ CompositeT[data, state]; manhattanPolygon: ImagerManhattan.Polygon _ NIL; IF specialCaseRectangles AND Easy[compositeT] THEN manhattanPolygon _ ManhattanPolygonFromEasyBox[compositeT, x, y, w, h] ELSE { PathMap: PathMapType ~ { move[[x, y]]; line[[x+w, y]]; line[[x+w, y+h]]; line[[x, y+h]]; }; DevicePathFromPath[context, PathMap, NIL]; manhattanPolygon _ ImagerScanConverter.ConvertToManhattanPolygon[data.devicePath, data.surfaceBounds]; }; visibility _ ImagerManhattan.IsVisible[manhattanPolygon, CompositeClipper[context]]; ImagerManhattan.Destroy[manhattanPolygon]; }; GetSurfaceBounds: PROC[context: Context] RETURNS[IntRectangle] = { data: Data ~ NARROW[context.data]; b: DeviceRectangle ~ data.surfaceBounds; deviceToSurface: Transformation ~ ImagerTransform.Invert[data.surfaceToDevice]; RETURN [ImagerTransform.TransformIntRectangle[[b.sMin, b.fMin, b.sSize, b.fSize], deviceToSurface]] }; NewToner: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; toners: PDFileWriter.TonerSet _ ALL[FALSE]; IF data.currentToner = data.lastToner THEN Imager.Error[$TooManyToners]; IF data.currentToner > data.lastToner THEN data.currentToner _ data.firstToner ELSE { data.currentToner _ SUCC[data.currentToner]; data.pdState.EndPage; }; toners[VAL[data.currentToner]] _ TRUE; data.pdState.StartImage[toners: toners, feed: data.currentToner = data.firstToner, strip: data.currentToner = data.lastToner]; data.cachedColor _ NIL; }; NewPage: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; WHILE data.currentToner < data.lastToner DO NewToner[context] ENDLOOP; data.pdState.EndPage; Reset[context]; data.pdStatePriorityImportant _ 0; data.currentToner _ SUCC[data.lastToner]; }; Close: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; WHILE data.currentToner < data.lastToner DO NewToner[context] ENDLOOP; data.pdState.EndPage; data.pdState.Close; }; SpecialOp: PROC[context: Context, op: ATOM, data: REF] RETURNS [REF _ NIL] = { SELECT op FROM $NewPage => NewPage[context]; $NewToner => NewToner[context]; $Close => Close[context]; ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp]; }; PDClass: ImagerPrivate.Class _ NEW [ImagerPrivate.ClassRep _ [ deviceType: $PD, Init: Init, ISet: ImagerDefault.ISet, ISetReal: ImagerDefault.ISetReal, ISetInt: ImagerDefault.ISetInt, SetSampledColor: ImagerDefault.SetSampledColor, SetSampledBlack: ImagerDefault.SetSampledBlack, DoSave: ImagerDefault.DoSave, DoSaveAll: ImagerDefault.DoSaveAll, ConcatT: ImagerDefault.ConcatT, TranslateT: ImagerDefault.TranslateT, RotateT: ImagerDefault.RotateT, ScaleT: ImagerDefault.ScaleT, Scale2T: ImagerDefault.Scale2T, Move: ImagerDefault.Move, Trans: ImagerDefault.Trans, SetXY: ImagerDefault.SetXY, IntegerSetXY: ImagerDefault.IntegerSetXY, SetXYRel: ImagerDefault.SetXYRel, IntegerSetXYRel: ImagerDefault.IntegerSetXYRel, GetCP: ImagerDefault.GetCP, GetCPRounded: ImagerDefault.GetCPRounded, MaskFill: MaskFill, MaskStroke: MaskStroke, MaskStrokeClosed: MaskStrokeClosed, MaskVector: ImagerDefault.MaskVector, IntegerMaskVector: ImagerDefault.IntegerMaskVector, MaskRectangle: MaskRectangle, IntegerMaskRectangle: IntegerMaskRectangle, StartUnderline: ImagerDefault.StartUnderline, MaskUnderline: ImagerDefault.MaskUnderline, IntegerMaskUnderline: ImagerDefault.IntegerMaskUnderline, MaskPixel: MaskPixel, ClipOutline: ImagerDefault.ClipOutline, ExcludeOutline: ImagerDefault.ExcludeOutline, ClipRectangle: ImagerDefault.ClipRectangle, ExcludeRectangle: ImagerDefault.ExcludeRectangle, IntegerClipRectangle: ImagerDefault.IntegerClipRectangle, IntegerExcludeRectangle: ImagerDefault.IntegerExcludeRectangle, ShowChar: ShowChar, ShowCharacters: ShowCharacters, CorrectMask: ImagerDefault.CorrectMask, CorrectSpace: ImagerDefault.CorrectSpace, SetCorrectMeasure: ImagerDefault.SetCorrectMeasure, SetCorrectTolerance: ImagerDefault.SetCorrectTolerance, Space: ImagerDefault.Space, IntegerSpace: ImagerDefault.IntegerSpace, Correct: ImagerDefault.Correct, Reset: Reset, SetViewOrigin: SetViewOrigin, GetViewOrigin: GetViewOrigin, SetViewBox: SetViewBox, GetViewBox: GetViewBox, ClipView: ClipView, MoveSurfaceRectangle: MoveSurfaceRectangle, TestRectangle: TestRectangle, GetSurfaceBounds: GetSurfaceBounds, SpecialOp: SpecialOp ]]; ImagerPrivate.RegisterDevice[PDClass]; END. rImagerPDImpl.mesa Copyright (C) 1983, 1984, 1985 Xerox Corporation. All rights reserved. Michael Plass, February 4, 1985 5:17:56 pm PST Procedures exported to ImagerPD These are surely wrong. Clipper info: Initial Client-to-view info View-to-surface info Surface-to-device info Scratch storage Cached color info Cached transformation info The font cache Output info concatenates T with the current view-to-device transformation Cheats and treats data.compositeT as mutable data.lineBuffer _ ImagerPixelMaps.Create[0, [0, 0, 1, data.surfaceBounds.fSize]]; move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f) move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f) The screen angle really needs to depend on the toner. This crock is needed to get the right priorities into the result in case of leftovers. [t.a*(v.x+data.viewOrigin.x) + t.b*(v.y+data.viewOrigin.y) + t.c, t.d*(v.x+data.viewOrigin.x) + t.e*(v.y+data.viewOrigin.y) + t.f], where t=data.surfaceToDevice. a=e=0, b=-1, d=1 a=e=1, b=d=0 Ê&ð˜Jšœ™JšœG™G™.J˜—šÏk ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ J˜ J˜ J˜Jšœ˜Jšœ˜J˜Jšœ ˜ J˜ J˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—JšÏn œœ˜JšœÛ˜âJšœ˜ Jšœ˜šœœœ ˜J˜—Jšœœ˜5Jšœœ!˜;Jšœœœ˜Jšœœœ˜Jšœ œ˜+Jšœœ˜Jšœœ˜!šœœ%˜˜VJšœœ˜Jšœœ˜!—Jšœ˜J˜—šž œœœœ˜AJš œœ$œœœ˜UJšœ˜J˜—šœ œœ˜Jšœ*˜*J˜Jšœ˜J˜—š žœœœœœ˜+Jšœœœœœœœ˜1Jšœœœœœœœ˜3Jšœ˜Jšœ˜J˜—šž œœœ˜HJ™=Jšœ,™,Jšœ˜Jšœ,˜,Jš œœœœœ$œ˜€šœœ˜Jšœœ˜'Jšœœ˜'˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ&˜&Jšœ%˜%J˜—Jšœ˜Jšœ˜—Jšœ˜ Jšœ˜J˜—šžœœœ˜,Jšœ œ ˜Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœ˜JšœX˜Xšœ$œ˜,Jšœ@˜@Jšœ˜Jšœ˜Jšœ˜—šœ˜JšœO˜OJšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ/˜/JšœŸ˜ŸJšœ#˜#Jšœ*˜*Jšœœœœ œ œ1œœ˜zJšœQ™QJšœœœœ˜4Jšœ4˜4Jšœ'˜'Jšœ!˜!Jšœ˜Jšœ˜J˜—šžœœ˜"Jšœ œ˜"Jšœœ˜%šœ8˜8Jšœ5˜5Jšœ˜J˜—Jšœ˜Jšœ;˜;Jšœ@˜@Jšœ@˜@Jšœœ˜Jšœœ˜Jšœ˜J˜—šžœœ4œœ%˜Jšœ œ˜"Jšœœ˜%šœ%˜%Jšœœœ™Jšœœœ™Jšœœœ™*Jšœ˜šžœœ œ ˜(Jšœ™Jšœ˜Jšœ˜Jšœ5˜;Jšœ˜—Jšžœœ<˜GJšžœœ<˜Gšžœœ˜#JšœA˜AJšœ5˜5—JšžœœB˜Mšžœœœ˜(Jšœ+˜+Jšœ+˜+Jšœ˜Jšœ˜—Jšœ ˜ Jšœ0˜0Jšœ˜—JšœR˜XJšœ˜J˜—šžœœ4œ˜TJšœ œ˜"Jšœœ˜%šžœ˜%Jšœœœ™Jšœœœ™Jšœœœ™*Jšœ˜Jšœ8˜8šžœœ œ œ˜0Jšœ™Jšœ˜Jšœ˜Jšœ˜—Jšžœœ;˜GJšžœœ;˜Gšžœœ˜#JšœA˜AJšœ5˜5—JšžœœB˜Mšžœœœ˜(Jšœ+˜+Jšœ+˜+Jšœ˜Jšœ˜—Jšœ ˜ Jšœ0˜0Jšœ˜—Jšœ{˜{Jšœ˜J˜—šžœœ4œ˜RJšœ œ œ˜:Jšœ œ˜"Jšœœ˜%šœ4˜4Jšœ˜Jšœ˜Jšœ(˜(Jšœœœœœœœœœ œ˜Œš œ œœ œœ˜MJ˜ J˜ J˜ Jšœ˜—Jšœ˜Jšœ˜Jšœ˜J˜—Jšœ˜J˜—šžœœœ˜HJšœ œ˜"Jšœœ˜%Jšœ-˜-šœœœ$œ˜8šžœœœœ˜7šœœœ˜Jšœ˜Jšœ˜Jšœ§˜§Jšœ˜Jšœœœœ˜HJšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ/˜/Jšœ#˜#Jšœ,˜,Jšœ#˜#Jšœ˜Jšœ˜—Jšœ˜ Jšœ˜J˜—š žœœœ œœ˜\Jšœœ˜!Jšœ*˜*Jšœ˜Jšœ˜Jšœ!˜!Jšœ ˜ Jšœ˜Jšœœœ ˜%Jšœ˜J˜—šž œœ3œ œA˜”šœ œ˜šœœœ˜Jšœœ˜Jš œœœœœ˜9Jšœ˜—Jšœœ˜—Jšœ˜J˜—šœg˜gJ˜—šž œœMœ˜hšœ*˜*Jšœœ1˜JJšœ7˜;J™5—Jšœq˜qJšœ*˜*š žœœœ œ œ˜;šœœœ˜-Jšœ˜Jš˜—Jšœ˜—šœ˜Jšœ˜Jšœ ˜ Jšœ,˜,Jšœ*˜*Jšœ˜Jšœ˜—Jšœ»˜»Jšœ/˜/Jšœ˜J˜—šœœ ˜ J˜—šž œœœ˜@Jšœ œ˜"Jšœœ˜%šžœœ œ˜,Jšœœœ˜<šžœœ˜ Jšœ œ ˜1Jšœœ œ"˜9šœ˜Jšœ5œ#œ)˜Jšœ@˜@Jšœ˜—Jšœ˜—Jšœ%˜%J˜—šžœœ˜Jšžœœ6˜CJ™VJšœ%˜%Jšœ˜—šœ9œ˜AJšœB˜BJšœ8˜8Jšœ˜—Jšœ œœ˜AJšœ'˜'šœœ˜Jšžœœ4˜AJšœ%˜%Jšœ˜—šœœœ˜#Jšžœœ6˜CJšœ%˜%Jšœ˜—šœœ ˜šœ!˜!Jšœ*˜*Jšœ˜—šœ˜šœœ˜!šœ œœ˜Jšœ œ˜Jš œœœœœœ˜QJšœ$˜$J˜—Jšœœ$˜4—J˜—šœ˜J˜šœ,œ˜4šœ˜šœE˜EJšœ#˜#JšœA˜AJšœ˜——Jšœ~˜~JšœF˜FJš œœœœœ˜tJšœ œ˜'Jšœ˜Jšœ˜Jšœ˜Jšœ˜ JšœB˜Ešœ˜JšœD˜DJšœ4˜4JšœO˜OJšœœœ œ˜UJšœ˜š œœœ$œœ˜Bšœœ˜'JšœL˜LJšœœ˜Jšœœ˜Jšœœ˜Jšœ˜Jšœ˜—Jš˜—šœœ˜šœ4˜4Jšœ ˜ Jšœ!˜!Jšœ!˜!Jšœ)˜)Jšœ˜Jšœ˜—Jšœ!˜!—JšœX˜XJšœ˜Jšœ˜—Jšœ-˜-Jšœ˜—J˜—Jšœ˜&—Jšœ˜J˜—š ž œœœœœ˜/Jš œœ œœœ˜&Jšœ'˜-Jšœ˜J˜—šž œœœœ ˜VJšœœ˜%šœœ˜Jšœ œ˜"Jšœ%˜%Jšœ6˜6šœœ˜šœœ˜'Jšœf˜fJšœ!œ(˜Ošœ#˜#šœ˜JšœK˜K—š˜JšœQ˜Q——šœ˜šœE˜EJšœ#˜#JšœA˜AJšœ˜——Jšœ œœ˜6Jš žœœœœœœ˜Eš žœœœ œœ˜Cš žœœœ œ œœ˜CJšœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœ˜Jšœ[˜[šœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜Jšœœœ œ˜?Jšœ˜—Jšœ˜—Jšœœœ˜šœ˜&Jšœ œ˜%Jšœœ˜Jšœ%˜,—JšœQ˜QJšœ˜—šœ˜ šž œœ œœ˜Cšžœœœ œ˜/Jšœ˜Jšœ˜—JšœE˜EJ˜—Jšœ5˜5Jšœ˜——Jšœ˜—Jšœ˜J˜—šž œœ4œ˜HJšœ œ˜,Jšœ œ˜"JšœIœ˜PJšœ$˜$Jšœ˜J˜—šžœœ4œ˜NJšœ œ˜Jšœ œ˜"JšœCœ˜IJšœ$˜$Jšœ˜J˜—šžœœ3œ˜IJšœ œ˜"Jšœ/˜/Jšœ$˜$Jšœ˜J˜—šž œœ&˜6Jšœ œ˜"Jšœœ˜%Jšœ0˜0JšœœG˜PJšœ˜Jšœœ˜ Jšœ˜J˜—šœœœ˜&J˜—šž œœ œ˜˜>—Jšœ œœ˜/JšœC˜CJšœœ#˜-Jšœ8˜8Jšœœ ˜Jšœ œ œ˜7Jšœp˜pJšœ&˜&Jšœ˜šœœ˜šœ œ˜!Jšœ˜Jšœ˜Jšœ˜—Jšœ!˜#šœ;˜;Jšœ#˜#Jšœœ˜$Jšœ˜—šœ˜ Jšœ!˜!Jšœœ˜"Jšœd˜dJšœ˜Jšœ˜——Jšœ+˜+Jšœ+˜+Jšœ˜Jšœ˜J˜—šžœœœœ˜=Jšœœœ˜*Jšœ˜Jšœ˜Jšœ*˜*Jšœ˜Jšœ˜J˜—šœ œ˜+J˜—šœ œœœ˜J˜—šžœœ˜Jšœ˜Jšœ œŸ˜/Jšœœ˜ Jšœœ˜Jšœœœœ˜Jšœ˜Jšœ œ˜"Jšœœ˜%Jš œ œœœœœœ˜CJšœC˜Cšžœœœ˜Išœœ˜Jšœ*˜.Jšœ)˜-—Jšœ˜—šžœœ œ œ˜@Jšœ¡™¡šœ˜šœ˜J™Jšœ[˜[—š˜J™ JšœZ˜Z——Jšœ˜—Jšœ6˜6JšœQ˜QšœB˜BJšœ˜Jšœe˜eJšœŸ ˜Jšœ˜—Jšœ œœœ ˜5Jšœ œ˜%Jšœ,˜,šžœœ œ œ˜3Jšœ œœ ˜.JšœI˜IJšœ9˜9Jšœ9˜9šœœœ œœœœœ˜jJšœœ˜Jšœœ˜Jšœœ˜1Jšœœ˜1Jšœ˜šœœœœœœœœœ˜UJšœ&˜&Jšœ&˜&Jšœ;˜;Jšœ˜—šœ˜Jšœ˜J˜—š œœœœ˜:Jšœ4˜4Jšœœ˜—šœ'œœ˜HšœXœ˜`šœ˜#JšœE˜EJšœI˜IJšœ˜—Jšœ˜—Jšœ˜ Jšœ˜—šœ ˜˜ šœ˜#šœ ˜ Jšœ'œœ˜4JšœZ˜ZJ˜*J˜—šœ ˜ Jšœ'œœ˜4Jšœ[˜[J˜.J˜—Jšœœ˜ Jšœœ˜—J˜—šœ˜Jšœ,˜,J˜,J˜—Jšœ9˜9Jšœœ˜—J˜—šœ œœ˜šœœ˜"Jšœ'˜'Jšœ'˜'J˜—Jšœ/˜/J˜—šœ˜Jšœ#˜#Jšœ˜—Jšœ ˜ Jšœ ˜ J˜—Jšœ[˜[Jšœ˜J˜—š ž$œœ'œœœ˜vJšœ œ˜"Jšœ€˜€JšœW˜]Jšœ˜J˜—šž œœ,˜?Jšœ œ˜"Jšœ˜Jšœœ˜Jšœ˜J˜—šž œœœ˜HJšœ œ˜"Jšœ˜Jšœ˜J˜—šž œœ.˜>Jšœ œ˜"šœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜—JšœZ˜ZJšœ/˜/Jšœœ˜Jšœ*˜*Jšœ˜Jšœ˜J˜—šž œœœ˜>Jšœ œ˜"JšœK˜KJšœO˜OJšœ–˜–šœ˜Jšœ"˜"Jšœ"˜"Jšœ˜Jšœ˜Jšœ˜—Jšœ˜J˜—šžœœ4œ˜NJšœ œ˜"šœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜—Jšœ\˜\Jšœ0˜0Jšœ/˜/Jšœœ˜šœ˜Jšœ œ(˜7Jšœ+˜/—Jšœ˜Jšœ ˜ Jšœ˜J˜—šžœœ<˜VJšœ,˜1Jšœ˜J˜—šžœœœœœœœ ˜GJ˜—šžœœœœ˜8Jšœœœœ˜sJšœ˜J˜—šžœœ*œœ0˜ˆJšœe˜eJšœ˜J˜—šžœœ*œœ'˜~Jšœ/˜/Jšœœ˜Jšœœ˜Jšœœ.˜9Jšœœ0˜;Jšœœ.˜9Jšœœ0˜;Jšœœœ ˜Jšœœœ ˜Jšœœœ ˜Jšœœœ ˜Jšœ5˜5Jšœ˜J˜—šž œœ œœ˜]Jšœ œ˜"Jšœœ˜%Jšœ5˜5Jšœ,œ˜0šœœ˜2JšœF˜F—šœ˜šžœ˜J˜ J˜J˜J˜Jšœ˜—Jšœ%œ˜*Jšœf˜fJšœ˜—JšœT˜TJšœ*˜*Jšœ˜J˜—šžœœœ˜BJšœ œ˜"Jšœ(˜(JšœO˜OJšœ]˜cJšœ˜J˜—šžœœ˜%Jšœ œ˜"Jšœ œœ˜+Jšœ$œ˜HJšœ$œ$˜Nšœ˜Jšœœ˜,Jšœ˜Jšœ˜—Jšœœœ˜&Jšœ~˜~Jšœœ˜Jšœ˜J˜—šžœœ˜$Jšœ œ˜"Jšœ$œœ˜FJšœ˜Jšœ˜Jšœ"˜"Jšœœ˜)Jšœ˜J˜—šžœœ˜"Jšœ œ˜"Jšœ$œœ˜FJšœ˜Jšœ˜Jšœ˜J˜—šž œœœœœœœ˜Nšœ˜J˜Jšœ˜J˜Jšœœ'˜7—Jšœ˜J˜—šœœ˜>Jšœ˜Jšœ ˜ Jšœ˜Jšœ!˜!Jšœ˜Jšœ/˜/Jšœ/˜/Jšœ˜Jšœ#˜#Jšœ˜Jšœ%˜%Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ)˜)Jšœ!˜!Jšœ/˜/Jšœ˜Jšœ)˜)Jšœ˜Jšœ˜Jšœ#˜#Jšœ%˜%Jšœ3˜3Jšœ˜Jšœ+˜+Jšœ-˜-Jšœ+˜+Jšœ9˜9Jšœ˜Jšœ'˜'Jšœ-˜-Jšœ+˜+Jšœ1˜1Jšœ9˜9Jšœ?˜?Jšœ˜Jšœ˜Jšœ'˜'Jšœ)˜)Jšœ3˜3Jšœ7˜7Jšœ˜Jšœ)˜)Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ+˜+Jšœ˜Jšœ#˜#Jšœ˜Jšœ˜J˜—šœ&˜&J˜—Jšœ˜—…—Ž€¹â