<> <> DIRECTORY Environment, Imager, ImagerBasic, ImagerBrick, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerPD, ImagerTransform, PDFileWriter, Real, Rope, RopeInline, Scaled, ScanConverter, UnifiedFonts; oldImagerPDImpl: CEDAR PROGRAM IMPORTS ImagerFontCache, ImagerBrick, ImagerHalftone, ImagerMasks, ImagerTransform, PDFileWriter, Real, Rope, RopeInline, Scaled, ScanConverter EXPORTS ImagerPD, Imager SHARES UnifiedFonts ~ BEGIN OPEN Imager; stats: RECORD [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT _ 0]; Stats: PROC RETURNS [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT] = { loadRunGroups _ stats.loadRunGroups; stats.loadRunGroups _ 0; loadRasters _ stats.loadRasters; stats.loadRasters _ 0; rasterChars _ stats.rasterChars; stats.rasterChars _ 0; runGroupChars _ stats.runGroupChars; stats.runGroupChars _ 0; clippedChars _ stats.clippedChars; stats.clippedChars _ 0; culledChars _ stats.culledChars; stats.culledChars _ 0; }; ROPE: TYPE ~ Rope.ROPE; FONT: TYPE ~ UnifiedFonts.FONT; DeviceCode: TYPE ~ PDFileWriter.DeviceCode; Mask: TYPE ~ ImagerMasks.Mask; Toner: TYPE ~ PDFileWriter.Toner; InkWell: TYPE ~ ARRAY [0..16) OF PACKED ARRAY [0..16) OF [0..1]; 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]; tryLeftoverMode: BOOLEAN _ TRUE; tryBandSize: NAT _ 16; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ currentColor: ImagerBasic.Color, sampledColor: ImagerBasic.SampledColor, currentFont: FONT, currentClipper: Mask, currentPosition: ImagerBasic.Pair, <> transformation: Transformation, viewToDevice: Transformation, pdState: PDFileWriter.PDState, devicePath: ScanConverter.DevicePath, grayTileRef: ARRAY Toner OF ARRAY [0..64) OF PDFileWriter.LoadReference, fontCache: ImagerFontCache.FontCache, lineBuffer: Mask, deviceBrick: ImagerHalftone.DeviceBrick ]; Create: PUBLIC PROC [fileName: ROPE, deviceCode: DeviceCode, copies: CARDINAL _ 1] RETURNS [context: Context] ~ { data: Data ~ NEW[DataRep]; context _ NEW[Imager.ContextRep]; context.data _ data; <> data.currentColor _ black; data.sampledColor _ NIL; data.currentFont _ NIL; data.devicePath _ ScanConverter.Allocate[]; 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; SELECT deviceCode FROM hornet => { scale: REAL ~ 384/72.0; data.currentClipper _ ImagerMasks.Box[sMin: 0, fMin: 0, sSize: 3264, fSize: 4224]; data.transformation _ data.viewToDevice _ [scale, 0, 0, 0, scale, 0, hard]; data.pdState _ PDFileWriter.Create[fileName: fileName, deviceCode: hornet, sResolution: 384, fResolution: 384, imageSSize: 3264, imageFSize: 4224, nColors: 1, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode]; data.deviceBrick _ ImagerHalftone.MakeDeviceBrick[ImagerBrick.BuildBrick[14, 45, ImagerHalftone.DotScreen], 255]; }; puffin => { scale: REAL ~ 384/72.0; data.currentClipper _ ImagerMasks.Box[sMin: 0, fMin: 0, sSize: 4224, fSize: 3264]; data.transformation _ data.viewToDevice _ [0, -scale, 4224, scale, 0, 0, hard]; data.pdState _ PDFileWriter.Create[fileName: fileName, deviceCode: puffin, sResolution: 384, fResolution: 384, imageSSize: 4224, imageFSize: 3264, nColors: 3, bandSSize: tryBandSize, leftOverMode: tryLeftoverMode, priorityImportant: tryLeftoverMode]; data.deviceBrick _ ImagerHalftone.MakeDeviceBrick[ImagerBrick.BuildBrick[9, 60, ImagerHalftone.LineScreen], 255]; }; ENDCASE => ERROR; data.lineBuffer _ ImagerMasks.NewBitmap[sMin: 0, fMin: 0, sSize: 1, fSize: data.pdState.GetBounds.fMax]; }; DoSaveAll: PUBLIC PROC [context: Context, action: PROC] ~ { data: Data _ NARROW[context.data]; cp: Pair _ data.currentPosition; DoSave[context, action]; data.currentPosition _ cp; }; DoSave: PUBLIC PROC [context: Context, action: PROC] ~ { data: Data _ NARROW[context.data]; color: Color _ data.currentColor; font: Font _ data.currentFont; clipper: Mask _ data.currentClipper; transformation: Transformation _ data.transformation; action[]; data.transformation _ transformation; data.currentClipper _ clipper; data.currentFont _ font; IF data.currentColor # color THEN SetColor[context, color]; }; Flush: PUBLIC PROC [context: Context] ~ { data: Data _ NARROW[context.data]; data.pdState.EndPage; }; Close: PUBLIC PROC [context: Context] ~ { data: Data _ NARROW[context.data]; data.pdState.EndPage; data.pdState.Close; }; TranslateT: PUBLIC PROC [context: Context, dx, dy: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreTranslate[dx, dy, data.transformation]; }; RotateT: PUBLIC PROC [context: Context, degrees: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreRotate[degrees, data.transformation]; }; ScaleT: PUBLIC PROC [context: Context, sx, sy: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreScale[sx, sy, data.transformation]; }; ConcatT: PUBLIC PROC [context: Context, transformation: Transformation] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.Concat[transformation, data.transformation]; }; IntTranslateT: PUBLIC PROC [context: Context, dx, dy: INTEGER] ~ {TranslateT[context, dx, dy]}; GetT: PUBLIC PROC [context: Context] RETURNS [Transformation] ~ { data: Data _ NARROW[context.data]; RETURN [ImagerTransform.Concat[data.transformation, ImagerTransform.Invert[data.viewToDevice]]] }; SetT: PUBLIC PROC [context: Context, transformation: Transformation] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.Concat[transformation, data.viewToDevice]; }; MaskFromPath: PROC [data: Data, outline: Path] RETURNS [mask: Mask] ~ { GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ <> m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; Xmove: PROC [p: Pair] ~ {move[Xform[p]]}; Xline: PROC [p: Pair] ~ {line[Xform[p]]}; Xcurve: PROC [p1, p2, p3: Pair] ~ {curve[Xform[p1], Xform[p2], Xform[p3]]}; outline.generateProc[outline, Xmove, Xline, Xcurve]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize]; }; data.devicePath.Reset; data.devicePath.PushPath[GenPath]; mask _ ImagerMasks.Create[Runs]; data.devicePath.Reset; }; MaskFromRectangle: PROC [data: Data, area: Rectangle] RETURNS [mask: Mask] ~ { GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ <> m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; move[Xform[[area.x, area.y]]]; line[Xform[[area.x + area.w, area.y]]]; line[Xform[[area.x + area.w, area.y + area.h]]]; line[Xform[[area.x, area.y + area.h]]]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize]; }; data.devicePath.Reset; data.devicePath.PushPath[GenPath]; mask _ ImagerMasks.Create[Runs]; data.devicePath.Reset; }; ClipPath: PUBLIC PROC [context: Context, outline: Path, exclude: BOOLEAN _ FALSE] ~ { data: Data _ NARROW[context.data]; BBoxPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { move[[data.currentClipper.FMin, data.currentClipper.SMin]]; line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin]]; line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin + data.currentClipper.SSize]]; line[[data.currentClipper.FMin, data.currentClipper.SMin + data.currentClipper.SSize]]; }; GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ <> m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; Xmove: PROC [p: Pair] ~ {move[Xform[p]]}; Xline: PROC [p: Pair] ~ {line[Xform[p]]}; Xcurve: PROC [p1, p2, p3: Pair] ~ {curve[Xform[p1], Xform[p2], Xform[p3]]}; outline.generateProc[outline, Xmove, Xline, Xcurve]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize]; }; data.devicePath.Reset; IF exclude THEN data.devicePath.PushPath[BBoxPath]; data.devicePath.PushPath[GenPath, exclude]; data.currentClipper _ ImagerMasks.Create[Runs].And[data.currentClipper]; data.devicePath.Reset; }; ClipRectangle: PUBLIC PROC [context: Context, outline: Rectangle, exclude: BOOLEAN _ FALSE] ~ { data: Data _ NARROW[context.data]; BBoxPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { move[[data.currentClipper.FMin, data.currentClipper.SMin]]; line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin]]; line[[data.currentClipper.FMin + data.currentClipper.FSize, data.currentClipper.SMin + data.currentClipper.SSize]]; line[[data.currentClipper.FMin, data.currentClipper.SMin + data.currentClipper.SSize]]; }; GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ <> m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; move[Xform[[outline.x, outline.y]]]; line[Xform[[outline.x + outline.w, outline.y]]]; line[Xform[[outline.x + outline.w, outline.y + outline.h]]]; line[Xform[[outline.x, outline.y + outline.h]]]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter, ymin: data.currentClipper.SMin, ymax: data.currentClipper.SMin+data.currentClipper.SSize]; }; data.devicePath.Reset; IF exclude THEN data.devicePath.PushPath[BBoxPath]; data.devicePath.PushPath[GenPath, exclude]; data.currentClipper _ ImagerMasks.Create[Runs].And[data.currentClipper]; data.devicePath.Reset; }; TestRectangle: PUBLIC PROC [context: Context, area: Rectangle] RETURNS [Visibility] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, area]; RETURN [mask.IsVisible[data.currentClipper]]; }; ClipIntRectangle: PUBLIC PROC [context: Context, outline: IntRectangle, exclude: BOOLEAN _ FALSE] ~ { ClipRectangle[context, [outline.x, outline.y, outline.w, outline.h], exclude]; }; TestIntRectangle: PUBLIC PROC [context: Context, area: IntRectangle] RETURNS [Visibility] ~ { RETURN [TestRectangle[context, [area.x, area.y, area.w, area.h]]]; }; DoWithoutClipping: PUBLIC PROC [context: Context, bounds: IntRectangle, callBack: PUBLIC PROC[Context]] ~ { callBack[context]; }; SetFont: PUBLIC PROC [context: Context, font: Font] ~ { data: Data _ NARROW[context.data]; data.currentFont _ font; }; GetFont: PUBLIC PROC [context: Context] RETURNS [font: Font] ~ { data: Data _ NARROW[context.data]; font _ data.currentFont; }; black: PUBLIC Color _ MakeGray[0]; white: PUBLIC Color _ MakeGray[1]; Card: PROC [real: REAL] RETURNS [card: CARDINAL] ~ { int: INT _ Real.RoundLI[real*LAST[CARDINAL]]; card _ MAX[MIN[int, LAST[CARDINAL]], 0]; }; InkWellGray: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] = { i, j: NAT _ 0; FOR i: NAT IN [0..16) DO inkWell[i] _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; ENDLOOP; WHILE gray>0 DO inkWell[i][j] _ 0; i _ i+1; IF i>=16 THEN {i_i-16; j _ j+1}; j _ j+3; IF j>=16 THEN j_j-16; gray_gray-1; ENDLOOP; }; InkWellGray45: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] = { i, j: INTEGER _ 0; FOR i: NAT IN [0..16) DO inkWell[i] _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; ENDLOOP; WHILE gray>0 DO inkWell[i][j] _ 0; i _ i+1; IF i>=16 THEN {i_i-16; j _ j+1}; j _ j-1; IF j<0 THEN j_j+16; gray_gray-1; ENDLOOP; }; InkWellGrayS: PROC [gray: [0..256]] RETURNS [inkWell: InkWell] = { i, j: INTEGER _ 0; FOR i: NAT IN [0..16) DO inkWell[i] _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; ENDLOOP; WHILE gray>0 DO inkWell[i][j] _ 0; i _ i+3; IF i>=16 THEN {i_i-16; j _ j-1}; IF j<0 THEN j_j+16; gray_gray-1; ENDLOOP; }; ConstructGray: PROC [data: Data, toner: Toner, index: [0..64)] = { value: [0..256] _ trc[index]; inkWell: InkWell _ SELECT toner FROM yellow => InkWellGrayS[value], magenta => InkWellGray45[value], ENDCASE => InkWellGray[value]; bitsPtr: LONG POINTER; loadReference: PDFileWriter.LoadReference; TRUSTED {bitsPtr _ @inkWell}; loadReference _ data.pdState.LoadContiguousColorTile[phase: 0, sMin: 0, fMin: 0, sSize: 16, fSize: 16, bitsPtr: bitsPtr]; data.grayTileRef[toner][index] _ loadReference; }; MakeGray: PUBLIC PROC [intensity: REAL] RETURNS [Color] ~ { RETURN [NEW[ImagerBasic.CIEColorRep _ [x: Card[0.3101], y: Card[0.3163], Y: Card[intensity]]]]; }; GetColor: PUBLIC PROC [context: Context] RETURNS [color: Color] ~ { data: Data _ NARROW[context.data]; color _ data.currentColor; }; SetColor: PUBLIC PROC [context: Context, color: Color] ~ { data: Data _ NARROW[context.data]; IF color = data.currentColor THEN RETURN; data.sampledColor _ NIL; data.currentColor _ color; SELECT color FROM black => { DoToner: PROC [toner: Toner] = {data.pdState.SetColorInk[toner]}; data.pdState.DoForEachToner[DoToner]; }; white => { DoToner: PROC [toner: Toner] = {data.pdState.SetColorClear[toner]}; data.pdState.DoForEachToner[DoToner]; }; ENDCASE => { WITH color SELECT FROM constantColor: ImagerBasic.CIEColor => { intensity: INT _ constantColor.Y; index: [0..64) _ MAX[MIN[intensity/(INT[LAST[CARDINAL]]/63), 63], 0]; DoToner: PROC [toner: Toner] = { IF data.grayTileRef[toner][index] = dummyLoadReference THEN ConstructGray[data, toner, index]; data.pdState.SetColorTile[toner, data.grayTileRef[toner][index]] }; data.pdState.DoForEachToner[DoToner]; }; sampledColor: ImagerBasic.SampledColor => { data.sampledColor _ sampledColor; }; ENDCASE => SetColor[context, black]; }; }; MaskStroke: PUBLIC PROC [context: Context, path: Path, width: REAL, strokeEnds: StrokeEnds] ~ { <<Not implemented yet>> }; ApplyMask: PROC [data: Data, mask: Mask] ~ { clipper: Mask _ data.currentClipper; IF data.sampledColor = NIL THEN { DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { Run: PROC [s, fMin: INTEGER, fSize: CARDINAL] = { SendRun[s, fMin, fSize]; }; mask.MapClippedRuns[clipper, Run]; }; data.pdState.MaskRunGroup[DeliverRuns]; stats.clippedChars _ stats.clippedChars + 1; } ELSE TRUSTED { transform: Transformation _ ImagerTransform.Concat[data.sampledColor.m, data.transformation]; linePointer: LONG POINTER _ NARROW[data.lineBuffer.refRep, REF ImagerMasks.MaskRep.bitmap].pointer; Line: PROC [proc: PROC [LONG POINTER]] ~ TRUSTED {proc[linePointer]}; SampledColorRun: PROC [s, fMin: INTEGER, fSize: CARDINAL] = TRUSTED { data.lineBuffer.sMin _ s; data.lineBuffer.fMin _ fMin; data.lineBuffer.sSize _ 1; data.lineBuffer.fSize _ fSize; ImagerHalftone.Halftone[data.lineBuffer, ImagerMasks.Box[sMin: s, sSize: 1, fMin: fMin, fSize: fSize], data.sampledColor.pa, transform, data.deviceBrick]; data.pdState.ColorSamples[black, s, fMin, 1, fSize, Line, opaque]; }; mask.MapClippedRuns[clipper, SampledColorRun]; }; }; MaskFill: PUBLIC PROC [context: Context, path: Path] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPath[data, path]; ApplyMask[data, mask]; }; MaskPixel: PUBLIC PROC [context: Context, pixelArray: ImagerBasic.PixelArray] ~ { <<Not implemented yet>> }; MoveTo: PUBLIC PROC [context: Context, p: IntPair] ~ { <<Not implemented yet>> }; DrawTo: PUBLIC PROC [context: Context, p: IntPair] ~ { <<Not implemented yet>> }; MaskThinStroke: PUBLIC PROC [context: Context, path: Path] ~ { <<Not implemented yet>> }; MaskIntRectangle: PUBLIC PROC [context: Context, area: IntRectangle] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, [area.x, area.y, area.w, area.h]]; ApplyMask[data, mask]; }; SetCP: PUBLIC PROC [context: Context, cp: Pair] ~ { data: Data _ NARROW[context.data]; data.currentPosition _ ImagerTransform.Transform[cp, data.transformation]; }; GetCP: PUBLIC PROC [context: Context] RETURNS [cp: Pair] ~ { data: Data _ NARROW[context.data]; cp _ ImagerTransform.InverseTransform[data.currentPosition, data.transformation]; }; SetIntCP: PUBLIC PROC [context: Context, cp: IntPair] ~ { SetCP[context, [cp.x, cp.y]]; }; GetIntCP: PUBLIC PROC [context: Context] RETURNS [cp: IntPair] ~ { realCP: Pair _ GetCP[context]; cp.x _ Real.RoundLI[realCP.x]; cp.y _ Real.RoundLI[realCP.y]; }; rasterToRunGroupStorageRatio: INT _ 1; CharRepresentation: TYPE = {null, runGroup, raster}; CharLoadInfo: TYPE = RECORD [loadRef: PDFileWriter.LoadReference, representation: CharRepresentation, sWidth, fWidth: Scaled.Value, mask: Mask, loadRepSize: INT]; LoadRunGroup: PROC [pdState: PDFileWriter.PDState, mask: Mask] RETURNS [loadReference: PDFileWriter.LoadReference] ~ { DeliverRuns: PROC [SendRun: PROC [sMin, fMin, fSize: CARDINAL]] = { Run: PROC [s, fMin: INTEGER, fSize: CARDINAL] = { SendRun[s-mask.SMin, fMin - mask.FMin, fSize]; }; mask.MapRuns[Run]; }; loadReference _ pdState.LoadRunGroup[DeliverRuns]; stats.loadRunGroups _ stats.loadRunGroups + 1; }; LoadBitmap: PROC [pdState: PDFileWriter.PDState, mask: Mask] RETURNS [loadReference: PDFileWriter.LoadReference] ~ { m: Mask _ mask.Bitmap; WITH m.refRep SELECT FROM b: REF ImagerMasks.MaskRep.bitmap => { loadReference _ pdState.LoadContiguousSampleArray[sSize: mask.sSize, fSize: mask.fSize, bitsPtr: b.pointer]; }; ENDCASE => ERROR; stats.loadRasters _ stats.loadRasters + 1; }; MaskAndRunCount: TYPE ~ RECORD [ mask: Mask, nRuns: INT ]; GetCharMask: PROC [font: FONT, transformation: Transformation, char: CHAR] RETURNS [ans: MaskAndRunCount] ~ { nRuns: INT _ 0; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { Run: PROC [s, fMin: INTEGER, fSize: NAT] ~ { run[s: s, fMin: fMin, fSize: fSize]; nRuns _ nRuns + 1; }; font.fontGraphicsClass.maskProc[font, transformation, char, Run]; }; nRuns _ 0; ans.mask _ ImagerMasks.Create[Runs]; ans.nRuns _ nRuns; }; MaskChars: PROC [context: Context, map: PROC[PROC[CHAR]]] ~ { data: Data _ NARROW[context.data]; fontCache: ImagerFontCache.FontCache _ data.fontCache; font: FONT _ data.currentFont; transform: Transformation _ ImagerTransform.Concat[font.actualTransformation, data.transformation]; fontCode: ImagerFontCache.FontCode _ ImagerFontCache.GetFontCode[transform.a, transform.b, transform.c, transform.d, transform.e, transform.f, font.graphicsKey]; clipper: Mask _ data.currentClipper; sCurrent: Scaled.Value _ Scaled.FromReal[data.currentPosition.x]; fCurrent: Scaled.Value _ Scaled.FromReal[data.currentPosition.y]; Load: PROC [char: CHAR] = { loadInfo: REF CharLoadInfo _ NEW[CharLoadInfo]; maskN: MaskAndRunCount _ GetCharMask[font, transform, char]; runGroupSize: INT _ maskN.nRuns * 2 + 2; rasterSize: INT _ maskN.mask.SSize * INT[(maskN.mask.FSize+15)/16] + 2; width: Pair _ ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], data.transformation]; loadInfo.loadRef _ dummyLoadReference; loadInfo.mask _ maskN.mask; loadInfo.representation _ SELECT TRUE FROM maskN.mask.SSize = 0 OR maskN.mask.FSize = 0 => null, maskN.mask.FSize > 32*Environment.bitsPerWord => runGroup, runGroupSize*rasterToRunGroupStorageRatio < rasterSize => runGroup, ENDCASE => raster; loadInfo.loadRepSize _ SELECT loadInfo.representation FROM runGroup => runGroupSize, raster => rasterSize, null => 0, ENDCASE => ERROR; loadInfo.sWidth _ Scaled.FromReal[width.x]; loadInfo.fWidth _ Scaled.FromReal[width.y]; fontCache.LoadCharData[fontCode, char, loadInfo]; }; DoChar: PROC [char: CHAR] = { sCP: INTEGER _ sCurrent.Round; fCP: INTEGER _ fCurrent.Round; loadInfo: REF CharLoadInfo _ LoadInfo[]; LoadInfo: PROC RETURNS [cli: REF CharLoadInfo] ~ INLINE { cli _ NARROW[ fontCache.GetCharData[fontCode, char ! ImagerFontCache.CacheMiss => {Load[char]; RETRY} ] ]; }; mask: Mask _ loadInfo.mask; bbox: Mask _ ImagerMasks.InlineBox[sMin: mask.SMin + sCP, fMin: mask.FMin + fCP, sSize: mask.SSize, fSize: mask.FSize]; visibility: Visibility _ bbox.IsVisible[clipper]; IF data.sampledColor # NIL AND visibility = visible THEN visibility _ partlyVisible; IF loadInfo.loadRef = dummyLoadReference AND visibility = visible THEN { IF data.pdState.RemainingLoadSize > loadInfo.loadRepSize + 5 THEN { loadInfo.loadRef _ SELECT loadInfo.representation FROM raster => LoadBitmap[data.pdState, loadInfo.mask], runGroup => LoadRunGroup[data.pdState, loadInfo.mask], ENDCASE => ERROR; } ELSE visibility _ partlyVisible; }; SELECT visibility FROM visible => { SELECT loadInfo.representation FROM raster => { IF loadInfo.loadRef = dummyLoadReference THEN ERROR; data.pdState.MaskSamplesRef[loadInfo.loadRef, bbox.SMin, bbox.FMin]; stats.rasterChars _ stats.rasterChars + 1; }; runGroup => { IF loadInfo.loadRef = dummyLoadReference THEN ERROR; data.pdState.MaskRunGroupRef[loadInfo.loadRef, bbox.SMin, bbox.FMin]; stats.runGroupChars _ stats.runGroupChars + 1; }; null => NULL; ENDCASE => ERROR; }; partlyVisible => { ApplyMask[data, mask.Shift[sCP, fCP]]; stats.clippedChars _ stats.clippedChars + 1; }; invisible => {stats.culledChars _ stats.culledChars + 1}; ENDCASE => ERROR; sCurrent _ sCurrent.PLUS[loadInfo.sWidth]; fCurrent _ fCurrent.PLUS[loadInfo.fWidth]; }; map[DoChar]; data.currentPosition _ [x: sCurrent.Float, y: fCurrent.Float]; }; MaskChar: PUBLIC PROC [context: Context, char: CHAR] ~ { MapOneChar: PROC [Char: PROC[CHAR]] = {Char[char]}; MaskChars[context, MapOneChar]; }; MaskCharacters: PUBLIC PROC [ context: Context, characters: REF, -- may be a Rope.ROPE or a REF TEXT, for non-cedar probably a LONG STRING start: INT _ 0, length: INT _ LAST[INT] ] ~ { WITH characters SELECT FROM rope: ROPE => { Map: PROC [Char: PROC[CHAR]] = { Action: PROC [c: CHAR] RETURNS [quit: BOOL _ FALSE] = {Char[c]}; [] _ rope.Map[start: start, len: length, action: Action]; }; MaskChars[context, Map] }; text: REF TEXT => { size: INT _ text.length; rem: INT _ RopeInline.NonNeg[size - RopeInline.NonNeg[start]]; Map: PROC [Char: PROC[CHAR]] = { FOR i: INT IN [start..start+length) DO Char[text[i]]; ENDLOOP; }; IF length > rem THEN length _ rem; MaskChars[context, Map]; }; ENDCASE => IF characters # NIL THEN ERROR; }; END.