<> <> <> <> DIRECTORY Basics USING [bitsPerWord], Imager USING [Box, Color, ColorOperator, Context, CorrectMask, CorrectSpace, DoSave, Error, PixelArray, SetXRel, SetXYRel, StateRep, Trans], ImagerBackdoor USING [Clipper, GetBounds, GetCP, GetReal], ImagerBox USING [BoxFromExtents, BoxFromRect, IntersectBox], ImagerCache USING [Fetch, Flush, Ref, Store], ImagerDevice USING [CharMask, CharMaskRep, Class, Device, DeviceBox, RunProc], ImagerFont USING [Amplified, BoundingBox, Correction, Font, MapText, Width, XChar, XStringProc], ImagerFontPrivate USING [FontImpl, FontImplRep, MaskChar], ImagerManhattan USING [CreateFromBox, Destroy, IsVisible, Polygon], ImagerMask USING [RunsFromCharMask], ImagerMaskCapture USING [Capture], ImagerPath USING [PathProc], ImagerPixelMap USING [DeviceRectangle], ImagerPrivate USING [Class, ClassRep], ImagerRaster USING [FontTuner], ImagerRasterPrivate USING [AndFlags, CharArrayIndex, charArraySize, ClipBoxToMask, Data, Flags, NoteStateChanges, ValidateIfNeeded], ImagerScanConverter USING [DevicePath], ImagerState USING [notChanged, State, StateRep], ImagerTransformation USING [InverseTransform, Rectangle, Transform, Transformation, TransformationRep, TranslateTo], Real USING [], Rope USING [ROPE], Scaled USING [Float, FromReal, PLUS, Round, Value], Vector2 USING [Mul, VEC]; ImagerRasterShowImpl: CEDAR PROGRAM IMPORTS Basics, Imager, ImagerBackdoor, ImagerBox, ImagerCache, ImagerFont, ImagerFontPrivate, ImagerManhattan, ImagerMask, ImagerMaskCapture, ImagerRasterPrivate, ImagerTransformation, Real, Scaled, Vector2 EXPORTS Imager, ImagerFont, ImagerRasterPrivate ~ BEGIN OPEN ImagerState, ImagerRasterPrivate; Class: TYPE ~ ImagerPrivate.Class; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep State: TYPE ~ ImagerState.State; StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep ROPE: TYPE ~ Rope.ROPE; CharMask: TYPE ~ ImagerDevice.CharMask; Clipper: TYPE ~ ImagerBackdoor.Clipper; Color: TYPE ~ Imager.Color; ColorOperator: TYPE ~ Imager.ColorOperator; Context: TYPE ~ Imager.Context; Device: TYPE ~ ImagerDevice.Device; DeviceBox: TYPE ~ ImagerDevice.DeviceBox; RunProc: TYPE ~ ImagerDevice.RunProc; DevicePath: TYPE ~ ImagerScanConverter.DevicePath; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; Font: TYPE ~ ImagerFont.Font; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; PathProc: TYPE ~ ImagerPath.PathProc; PixelArray: TYPE ~ Imager.PixelArray; Rectangle: TYPE ~ ImagerTransformation.Rectangle; Transformation: TYPE ~ ImagerTransformation.Transformation; TransformationRep: TYPE ~ ImagerTransformation.TransformationRep; VEC: TYPE ~ Vector2.VEC; XChar: TYPE ~ ImagerFont.XChar; XStringProc: TYPE ~ ImagerFont.XStringProc; FontImpl: TYPE ~ ImagerFontPrivate.FontImpl; FontImplRep: PUBLIC TYPE ~ ImagerFontPrivate.FontImplRep; -- export to to ImagerFont FontTuner: TYPE ~ ImagerRaster.FontTuner; bitsPerWord: NAT ~ Basics.bitsPerWord; DeviceBoxFromDeviceRectangle: PROC [r: DeviceRectangle] RETURNS [DeviceBox] ~ { RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]]; }; Validate: PROC [data: Data, state: State, needs: Flags] ~ INLINE { IF state.changed#ImagerState.notChanged THEN NoteStateChanges[data, state]; IF AndFlags[data.valid, needs]#needs THEN ValidateIfNeeded[data, state, needs]; }; showNeeds: Flags ~ [clientClipper: TRUE, deviceColor: TRUE, devicePriority: TRUE, clientToDevice: TRUE, fontInfo: TRUE]; CharPartlyVisible: PROC [context: Context, font: Font, char: XChar] RETURNS [BOOL] ~ { bounds: Imager.Box ~ ImagerBox.BoxFromRect[ImagerBackdoor.GetBounds[context]]; bb: Imager.Box ~ ImagerBox.BoxFromExtents[ImagerFont.BoundingBox[font, char]]; IF bb.xmin=bb.xmax THEN RETURN[TRUE] ELSE { cp: VEC ~ ImagerBackdoor.GetCP[context]; cbb: Imager.Box ~ [xmin: bb.xmin+cp.x, ymin: bb.ymin+cp.y, xmax: bb.xmax+cp.x, ymax: bb.ymax+cp.y]; common: Imager.Box ~ ImagerBox.IntersectBox[bounds, cbb]; RETURN [((common.xmax > common.xmin) AND (common.ymax > common.ymin))] }; }; BasicShowChar: PUBLIC PROC [context: Context, font: Font, char: XChar, checkBB: BOOL, imaging: BOOL, fontTuner: FontTuner _ NIL] ~ { width: VEC _ font.Width[char]; IF font.Amplified[char] THEN { width _ width.Mul[ImagerBackdoor.GetReal[context, amplifySpace]]; }; Imager.Trans[context]; IF imaging AND (NOT checkBB OR CharPartlyVisible[context, font, char]) THEN { IF fontTuner = NIL THEN { ImagerFontPrivate.MaskChar[font, char, context]; } ELSE { op: PROC [ctx: Context] ~ {ImagerFontPrivate.MaskChar[font, char, ctx]}; fontTuner.proc[fontTuner, op, context]; }; }; Imager.SetXYRel[context, width]; SELECT font.Correction[char] FROM none => NULL; space => Imager.CorrectSpace[context, width]; mask => Imager.CorrectMask[context]; ENDCASE => ERROR; }; worryNat: NAT ~ NAT.LAST/2-1; worryReal: REAL _ worryNat; RasterShow: PUBLIC PROC [context: Context, string: XStringProc, xrel: BOOL] ~ { state: State ~ context.state; data: Data ~ NARROW[context.data]; device: Device ~ data.device; cache: ImagerCache.Ref ~ data.fontCache; font: Font ~ state.font; amplifySpace: REAL ~ state.np.amplifySpace; testAmplified: BOOL ~ amplifySpace#1.0; testCorrection: BOOL ~ state.np.correctPass#0; imaging: BOOL ~ state.np.noImage=0; basicCharAction: PROC [char: XChar] ~ { BasicShowChar[context, font, char, TRUE, imaging, data.fontTuner]; }; scaledCPValid: BOOL _ FALSE; sCP: Scaled.Value; fCP: Scaled.Value; UpdateCP: PROC ~ { IF scaledCPValid THEN state.p.cp _ data.viewToDevice.InverseTransform[[sCP.Float, fCP.Float]]; }; TryScaledCP: PROC ~ { cp: VEC _ data.viewToDevice.Transform[state.p.cp]; IF ABS[cp.x] < worryReal AND ABS[cp.y] < worryReal THEN { sCP _ Scaled.FromReal[cp.x]; fCP _ Scaled.FromReal[cp.y]; scaledCPValid _ TRUE; } ELSE scaledCPValid _ FALSE; }; cachedCharAction: PROC [char: XChar] ~ { charIndex: CharArrayIndex ~ char.code MOD charArraySize; prevMask: CharMask ~ data.charArray[charIndex]; charMask: CharMask _ prevMask; Validate[data, state, showNeeds]; IF charMask#NIL AND charMask.font=data.fontAtom AND charMask.char=char THEN NULL ELSE charMask _ ImagerCache.Fetch[cache, data.fontAtom, char]; IF charMask=NIL AND imaging AND CharPartlyVisible[context, font, char] THEN { operator: PROC [c: Context] ~ {BasicShowChar[c, font, char, FALSE, TRUE, data.fontTuner]}; m: Transformation _ data.clientToDevice.TranslateTo[[0, 0]]; charMask _ ImagerMaskCapture.Capture[operator, m, data.rastWeight]; IF charMask#NIL THEN { charMask.font _ data.fontAtom; charMask.char _ char; UNTIL ImagerCache.Store[cache, charMask].ok DO ImagerCache.Flush[cache] ENDLOOP; }; }; IF charMask=NIL OR NOT charMask.metricsValid OR NOT scaledCPValid THEN { UpdateCP[]; basicCharAction[char]; TryScaledCP[]; } ELSE { sCPOld: Scaled.Value ~ sCP; fCPOld: Scaled.Value ~ fCP; s: INTEGER ~ Scaled.Round[sCPOld]; f: INTEGER ~ Scaled.Round[fCPOld]; IF imaging AND charMask.sSizeBB#0 THEN { ShowCharacterMask[data: data, mask: charMask, s: s, f: f]; }; IF testCorrection OR (testAmplified AND charMask.amplified) THEN { width: VEC _ font.Width[char]; IF testAmplified AND font.Amplified[char] THEN { width _ width.Mul[amplifySpace]; }; UpdateCP[]; Imager.SetXYRel[context, width]; IF testCorrection THEN SELECT font.Correction[char] FROM none => NULL; space => Imager.CorrectSpace[context, width]; mask => Imager.CorrectMask[context]; ENDCASE => ERROR; TryScaledCP[]; } ELSE { sCPNew: Scaled.Value ~ (sCP _ sCPOld.PLUS[charMask.sWidth]); fCPNew: Scaled.Value ~ (fCP _ fCPOld.PLUS[charMask.fWidth]); IF sCPNew.integerPart NOT IN [-worryNat..worryNat] OR fCPNew.integerPart NOT IN [-worryNat..worryNat] THEN { UpdateCP[]; TryScaledCP[]; }; }; IF charMask#prevMask THEN data.charArray[charIndex] _ charMask; }; }; oddCharIndex: BOOL _ FALSE; xRelAction: PROC [char: XChar] ~ { IF oddCharIndex THEN { Imager.SetXRel[context, char.code-128]; } ELSE IF cache=NIL THEN basicCharAction[char] ELSE { TryScaledCP[]; cachedCharAction[char]; UpdateCP[]; }; oddCharIndex _ NOT oddCharIndex; }; saveAction: PROC ~ { Validate[data, state, showNeeds]; IF xrel THEN string[xRelAction] ELSE IF cache=NIL THEN string[basicCharAction] ELSE { TryScaledCP[]; string[cachedCharAction]; UpdateCP[]; }; }; IF state.font=NIL THEN ERROR Imager.Error[[code: $noFont, explanation: "Failed to set font before calling Show."]]; Imager.DoSave[context, saveAction]; }; oops: CARDINAL _ 0; ShowCharacterMask: PUBLIC PROC [data: Data, mask: CharMask, s: INTEGER, f: INTEGER] ~ { sMin: INTEGER ~ s+mask.sMinBB; fMin: INTEGER ~ f+mask.fMinBB; device: Device ~ data.device; allVisible: BOOL _ FALSE; partlyVisible: BOOL _ TRUE; partlyClipped: BOOL _ FALSE; charBox: DeviceBox _ [ smin: LOOPHOLE[sMin], fmin: LOOPHOLE[fMin], smax: LOOPHOLE[sMin, CARDINAL]+mask.sSizeBB, fmax: LOOPHOLE[fMin, CARDINAL]+mask.fSizeBB ]; IF AndFlags[data.valid, showNeeds]#showNeeds THEN oops _ oops + 1; IF sMin < 0 OR fMin < 0 THEN { charBox _ [ smin: MAX[sMin, 0], fmin: MAX[fMin, 0], smax: MAX[sMin+INTEGER[mask.sSizeBB], 0], fmax: MAX[fMin+INTEGER[mask.fSizeBB], 0] ]; partlyClipped _ TRUE; }; IF data.clientClipBoxOnly THEN { dBB: DeviceBox ~ data.clientClipBox; IF NOT partlyClipped AND charBox.smin >= dBB.smin AND charBox.fmin >= dBB.fmin AND charBox.smax <= dBB.smax AND charBox.fmax <= dBB.fmax THEN { allVisible _ TRUE; } ELSE IF charBox.smax <= dBB.smin OR charBox.fmax <= dBB.fmin OR charBox.smin >= dBB.smax OR charBox.fmin > dBB.fmax THEN { partlyVisible _ FALSE; }; } ELSE IF charBox.smax = 0 OR charBox.fmax = 0 THEN partlyVisible _ FALSE ELSE IF device.class.MaskChar # NIL THEN { p: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[[sMin, fMin, mask.sSizeBB, mask.fSizeBB]]; SELECT ImagerManhattan.IsVisible[p, data.clientClipMask] FROM invisible => partlyVisible _ FALSE; visible => allVisible _ TRUE; ENDCASE => NULL; ImagerManhattan.Destroy[p]; }; IF allVisible AND (device.class.MaskChar # NIL) THEN device.class.MaskChar[device, s, f, mask] ELSE IF partlyVisible THEN WITH mask SELECT FROM raster: REF ImagerDevice.CharMaskRep.raster => { boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: charBox, mask: data.clientClipMask, action: action]; }; TRUSTED {device.class.MaskBits[device: device, srcBase: @raster[0], srcWordsPerLine: (raster.fSizeBB+(bitsPerWord-1))/bitsPerWord, ts: sMin, tf: fMin, boxes: boxes ]}; }; runs: REF ImagerDevice.CharMaskRep.runs => { FOR p: ManhattanPolygon _ data.clientClipMask, p.rest UNTIL p=NIL DO Runs: PROC [run: ImagerDevice.RunProc] ~ { ImagerMask.RunsFromCharMask[mask, run, s, f, p.first]; }; bounds: DeviceBox ~ DeviceBoxFromDeviceRectangle[p.first]; smin: CARDINAL ~ MAX[charBox.smin, bounds.smin]; smax: CARDINAL ~ MIN[charBox.smax, bounds.smax]; fmin: CARDINAL ~ MAX[charBox.fmin, bounds.fmin]; fmax: CARDINAL ~ MIN[charBox.fmax, bounds.fmax]; IF smin ERROR; }; RasterShowText: PUBLIC PROC [context: Context, text: REF READONLY TEXT, start, len: NAT, xrel: BOOL] ~ { class: Class ~ context.class; string: XStringProc ~ { ImagerFont.MapText[text, start, len, charAction] }; class.Show[context, string, xrel]; }; END.