<> <> <> DIRECTORY Basics, Font, Imager, ImagerBasic, ImagerBrick, ImagerConic, ImagerDefault, ImagerFontCache, ImagerFrameBuffer, ImagerHalftone, ImagerLF, ImagerManhattan, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, ImagerMasks, Real, Rope, RefText, Scaled; ImagerLFImpl: CEDAR MONITOR IMPORTS Imager, ImagerConic, ImagerDefault, ImagerFontCache, ImagerFrameBuffer, ImagerHalftone, ImagerManhattan, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, ImagerMasks, Real, Rope, RefText, Scaled ~ BEGIN OPEN ImagerBasic; StatsRecord: TYPE ~ RECORD [ loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT _ 0]; stats: StatsRecord _ []; Stats: PROC RETURNS [StatsRecord] ~ { x: StatsRecord ~ stats; stats _ []; RETURN[x] }; FONT: TYPE ~ Font.FONT; PixelMap: TYPE ~ ImagerPixelMaps.PixelMap; Tile: TYPE ~ ImagerPixelMaps.Tile; SpecialColor: TYPE ~ REF ColorRep[special]; Name: TYPE ~ ImagerPrivate.Name; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; TransformationRec: TYPE ~ ImagerTransform.TransformationRec; Context: TYPE ~ Imager.Context; State: TYPE ~ ImagerDefault.State; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ ImagerLF.DataRep; SurfaceOriginS: PROC [data: Data] RETURNS [s: INTEGER] ~ INLINE { RETURN [data.canvas.sSize] }; CompositeT: PROC [data: Data, state: State] RETURNS [TransformationRec] ~ { <> t: TransformationRec ~ state.T.Contents; RETURN [[ a: -t.d, d: t.a, b: -t.e, e: t.b, c: -t.f+SurfaceOriginS[data]-data.viewOrigin.y, f: t.c+data.viewOrigin.x ]] }; ViewToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ { RETURN [[ a: 0, d: 1, b: -1, e: 0, c: SurfaceOriginS[data]-data.viewOrigin.y, f: data.viewOrigin.x ]] }; SurfaceToDevice: PROC [data: Data] RETURNS [TransformationRec] ~ { RETURN [[ a: 0, d: 1, b: -1, e: 0, c: SurfaceOriginS[data], f: 0 ]] }; DevicePair: TYPE ~ RECORD [s, f: Scaled.Value]; TransformDeviceToViewVec: PROC [d: DevicePair] RETURNS [v: Pair] ~ { v _ [x: Scaled.Float[d.f], y: -Scaled.Float[d.s]]; }; GetDeviceCP: PROC [data: Data, state: State] RETURNS [DevicePair] ~ { RETURN [[ s: ScaledFromReal[-state.cpy].PLUS[Scaled.FromInt[SurfaceOriginS[data]-data.viewOrigin.y]], f: ScaledFromReal[state.cpx].PLUS[Scaled.FromInt[data.viewOrigin.x]] ]] }; SetDeviceCP: PROC [data: Data, state: State, cp: DevicePair] ~ { state.cpy _ -Scaled.Float[cp.s.MINUS[Scaled.FromInt[SurfaceOriginS[data]-data.viewOrigin.y]]]; state.cpx _ Scaled.Float[cp.f.MINUS[Scaled.FromInt[data.viewOrigin.x]]]; }; Init: PROC [context: Context, info: REF] ~ { tileStorage: PixelMap ~ ImagerPixelMaps.Create[lgBitsPerPixel: 0, bounds: [0, 0, 16, 16]]; data: Data ~ NEW[DataRep]; context.data _ data; data.tile _ [sOrigin: 0, fOrigin: 0, sSize: 16, fSize: 16, phase: 0, refRep: tileStorage.refRep]; data.tileSamples _ MakeTileSamples[16, 16]; data.devicePath _ NIL; WITH info SELECT FROM pm: REF PixelMap => data.canvas _ pm^; ENDCASE => TRUSTED {data.canvas _ ImagerFrameBuffer.LFDisplay[]}; ImagerDefault.InitState[context]; SetViewOrigin[context, [0, 0]]; SetViewBox[context, GetSurfaceBounds[context]]; Reset[context]; }; pixelsPerInch: REAL ~ 72.0; mmPerInch: REAL ~ 25.4; inchesPerMeter: REAL ~ 1000.0/mmPerInch; pixelsPerMeter: REAL _ pixelsPerInch*inchesPerMeter; Reset: PROC [context: Context] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; ImagerDefault.Reset[context]; ImagerDefault.ScaleT[context, pixelsPerMeter]; state.fieldXMax _ state.mediumXSize _ data.canvas.fSize/pixelsPerMeter; state.fieldYMax _ state.mediumYSize _ data.canvas.sSize/pixelsPerMeter; data.compositeClipper _ NIL; data.cachedColor _ NIL; }; MakeTileSamples: PROC [xSize, ySize: NAT] RETURNS [tileSamples: SampledColor] ~ { tileSamples _ NEW[ColorRep[sampled]]; tileSamples.transparent _ FALSE; tileSamples.pa _ NEW[PixelArrayRep]; tileSamples.m _ tileSamples.pa.m _ ImagerTransform.Rotate[0]; tileSamples.colorOperator _ $Intensity; tileSamples.pa.xPixels _ xSize; tileSamples.pa.yPixels _ ySize; tileSamples.pa.maxSampleValue _ 255; tileSamples.pa.samplesPerPixel _ 1; tileSamples.pa.get _ ConstantGet; tileSamples.pa.data _ NEW[NAT _ 0]; }; 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; }; SetTileSamples: PROC [tileSamples: SampledColor, intensity: [0..255]] ~ { WITH tileSamples.pa.data SELECT FROM n: REF NAT => n^ _ intensity; ENDCASE => ERROR; }; 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] ~ {RETURN[[ <> -p.y + SurfaceOriginS[data]-data.viewOrigin.y, p.x + data.viewOrigin.x ]]}; 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.canvas.BoundedWindow]]; }; 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]; 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.canvas.BoundedWindow, 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].FromRec, 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.canvas.BoundedWindow, scratch: data.devicePath ]; }; Floor: PROC [real: REAL] RETURNS [int: INT] ~ { int _ Real.RoundLI[real]; WHILE int < real DO int _ int + 1 ENDLOOP; WHILE int > real DO int _ int - 1 ENDLOOP; }; Ceiling: PROC [real: REAL] RETURNS [int: INT] ~ { int _ Real.RoundLI[real]; WHILE int > real DO int _ int - 1 ENDLOOP; WHILE int < real DO int _ int + 1 ENDLOOP; }; ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED { <> i: INT _ Real.RoundLI[real * 65536.0]; RETURN[LOOPHOLE[i]] }; 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.canvas.sOrigin+data.canvas.sMin, data.canvas.fOrigin+data.canvas.fMin, data.canvas.sSize, data.canvas.fSize]]; 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] }; lfBrick: ImagerHalftone.DeviceBrick _ ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255]; CurrentColor: PROC [context: Context] RETURNS [color: Color] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ { FOR s: INTEGER IN [data.tile.sOrigin..data.tile.sOrigin + data.tile.sSize) DO run[s, data.tile.fOrigin, data.tile.fSize]; ENDLOOP; }; IF data.cachedColor = state.color THEN RETURN [data.cachedColor]; color _ data.cachedColor _ state.color; IF color = Imager.black OR color = Imager.white THEN RETURN; WITH color SELECT FROM constantColor: ConstantColor => { intensity: INT _ constantColor.Y; intensity _ (intensity + 127)/(LAST[CARDINAL]/255); SetTileSamples[data.tileSamples, intensity]; ImagerHalftone.Halftone[ dest: [sOrigin: data.tile.sOrigin, fOrigin: data.tile.fOrigin, sMin: 0, fMin: 0, sSize: data.tile.sSize, fSize: data.tile.fSize, refRep: data.tile.refRep], runs: Runs, source: data.tileSamples.pa, transformation: ImagerTransform.Rotate[0], deviceBrick: lfBrick ]; }; specialColor: SpecialColor => { WITH specialColor.ref SELECT FROM stipple: REF CARDINAL => data.tile _ ImagerPixelMaps.TileFromStipple[stipple: stipple^, scratch: data.tile.refRep]; atom: ATOM => { SELECT atom FROM $XOR => NULL; ENDCASE => ERROR Imager.Error[$UnknownSpecialColor]; }; ENDCASE => ERROR Imager.Error[$UnknownSpecialColor]; }; sampledColor: SampledColor => NULL; ENDCASE => state.color _ Imager.black; }; 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]; SELECT color FROM Imager.black => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 1, [null, null], sTranslate, fTranslate]; Imager.white => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 0, [null, null], sTranslate, fTranslate]; Imager.XOR => ImagerMasks.ApplyConstant[mask, clipper, data.canvas, 1, [xor, null], sTranslate, fTranslate]; ENDCASE => { WITH color SELECT FROM sampledColor: SampledColor => { Runs: PROC [run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ { ImagerMasks.GenerateRuns[mask, clipper, run, sTranslate, fTranslate]; }; transform: Transformation _ sampledColor.pa.m.Concat[sampledColor.m].Concat[ViewToDevice[data].FromRec]; invertOutput: BOOLEAN _ FALSE; customBrick: ImagerBrick.Brick _ NARROW[Imager.GetProp[context, $CustomBrick]]; deviceBrick: ImagerHalftone.DeviceBrick; IF customBrick # NIL THEN deviceBrick _ ImagerHalftone.MakeDeviceBrick[customBrick, sampledColor.pa.maxSampleValue] ELSE { IF data.deviceBrickMaxSampleValue # sampledColor.pa.maxSampleValue THEN { data.deviceBrickMaxSampleValue _ sampledColor.pa.maxSampleValue; data.deviceBrick _ ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, sampledColor.pa.maxSampleValue]; }; deviceBrick _ data.deviceBrick; }; SELECT sampledColor.colorOperator FROM $SampledBlack => invertOutput _ TRUE; $Intensity => NULL; ENDCASE => Imager.Error[$UnknownColorModel]; ImagerHalftone.Halftone[data.canvas, Runs, sampledColor.pa, transform, deviceBrick, invertOutput, sampledColor.transparent]; }; ENDCASE => { -- constant (other than black or white) or stipple tile: ImagerPixelMaps.Tile _ data.tile; tile.sOrigin _ tile.sOrigin + SurfaceOriginS[data]-data.viewOrigin.y; tile.fOrigin _ tile.fOrigin + data.viewOrigin.x; ImagerMasks.ApplyTile[mask, clipper, data.canvas, tile, [null, null], sTranslate, fTranslate]; }; }; }; }; 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]; }; specialCaseRectangles: BOOLEAN _ TRUE; MaskRectangle: PROC [context: Context, x, y, w, h: REAL] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; trans: TransformationRec ~ CompositeT[data, state]; IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN { s0: REAL ~ trans.b * y + trans.c; s1: REAL ~ trans.b * (y+h) + trans.c; f0: REAL ~ trans.d * x + trans.f; f1: REAL ~ trans.d * (x+w) + trans.f; sMin: INTEGER ~ Floor[MAX[MIN[s0, s1], -LAST[INTEGER]/2]]; sMax: INTEGER ~ Ceiling[MIN[MAX[s0, s1], LAST[INTEGER]/2]]; fMin: INTEGER ~ Floor[MAX[MIN[f0, f1], -LAST[INTEGER]/2]]; fMax: INTEGER ~ Ceiling[MIN[MAX[f0, f1], LAST[INTEGER]/2]]; t1: ManhattanPolygon; IF sMax<=sMin OR fMax<=fMin THEN RETURN; t1 _ ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]]; ApplyMask[context, t1, 0, 0]; ImagerManhattan.Destroy[t1]; } 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]; }; MaskPixel: PROC [context: Context, pa: PixelArray] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; trans: Transformation ~ CompositeT[data, state].FromRec; mask: REF _ ImagerMasks.FromPixelArray[pa, ImagerTransform.Concat[pa.m, trans]]; ApplyMask[context, mask, 0, 0]; mask _ NIL; }; rasterToRunGroupStorageRatio: INT _ 1; CharLoadInfo: TYPE ~ RECORD[sWidth, fWidth: Scaled.Value, mask: REF]; 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: TransformationRec] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; r: TransformationRec _ CompositeT[data, state]; r.c _ r.f _ 0; t _ ImagerTransform.Concat[font.actualTransformation, r.FromRec].Contents; }; fontCache: ImagerFontCache.FontCache _ ImagerFontCache.Create[]; LoadCharData: PROC [self: ImagerFontCache.FontObject, charCode: CARDINAL] RETURNS [charData: REF, memoryCost: INT] ~ { font: FONT ~ NARROW[self.fontAnyData]; char: CHAR ~ 0C+charCode; -- does this bounds check properly? transform: Transformation ~ ImagerTransform.Create[self.r0, self.r1, self.r2, self.r3, self.r4, self.r5]; -- character (to client) to device clientTransform: Transformation ~ font.actualTransformation.Invert.Concat[transform]; loadInfo: REF CharLoadInfo _ NEW[CharLoadInfo]; mask: ImagerManhattan.Polygon _ GetCharMask[font, transform, char]; nBoxes: INT ~ ImagerManhattan.CountBoxes[mask]; bb: DeviceRectangle ~ ImagerManhattan.BoundingBox[mask]; boxesSize: INT _ 7 * nBoxes; rasterSize: INT _ bb.sSize * INT[(bb.fSize+15)/16] + 2; width: Pair _ ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char] , clientTransform]; IF bb.fSize > 32*Basics.bitsPerWord OR boxesSize*rasterToRunGroupStorageRatio < rasterSize THEN loadInfo.mask _ mask ELSE { pixelMap: PixelMap _ ImagerPixelMaps.Create[0, bb]; ImagerPixelMaps.Clear[pixelMap]; FOR l: LIST OF DeviceRectangle _ mask, l.rest UNTIL l = NIL DO ImagerPixelMaps.Fill[pixelMap, l.first, 1]; ENDLOOP; loadInfo.mask _ ImagerMasks.FromBitmap[pixelMap]; ImagerManhattan.Destroy[mask]; mask _ NIL; }; 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[0] _ char; text.length _ 1; ShowCharacters[context, text, font, 0, 1]; RefText.ReleaseScratch[text]; }; 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; transform: TransformationRec _ FontCompositeTransform[context, showVec]; 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]]; noImage: BOOLEAN ~ state.noImage # 0; residual: INT; TooBigForFixed: ERROR ~ CODE; WITH characters SELECT FROM rope: Rope.ROPE => residual _ Rope.Size[rope]; text: REF TEXT => residual _ MIN[text.length, text.maxLength]; ENDCASE => Imager.Error[$MustBeRopeOrRefText]; start _ MIN[residual, MAX[0,start]]; length _ MIN[residual-start, MAX[length, 0]]; residual _ length; WHILE residual > 0 DO {ENABLE TooBigForFixed => GOTO TryAgain; IF state.correctPass = 0 AND ABS[state.cpx] < NAT.LAST AND ABS[state.cpy] < NAT.LAST THEN { DoChar: PROC [charCode: CARDINAL, charData: REF] ~ { loadInfo: REF CharLoadInfo ~ NARROW[charData]; sDelta: Scaled.Value _ loadInfo.sWidth; fDelta: Scaled.Value _ loadInfo.fWidth; IF ABS[cp.s.Floor] >= NAT.LAST OR ABS[cp.s.Floor] >= NAT.LAST THEN {SetDeviceCP[data, state, cp]; ERROR TooBigForFixed}; IF NOT noImage THEN ApplyMask[context, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]]; IF charCode = ORD[' ] AND state.amplifySpace # 1.0 THEN { sDelta _ Scaled.FromReal[state.amplifySpace*Scaled.Float[sDelta]]; fDelta _ Scaled.FromReal[state.amplifySpace*Scaled.Float[fDelta]]; }; cp.s _ cp.s.PLUS[sDelta]; cp.f _ cp.f.PLUS[fDelta]; residual _ residual - 1; }; cp: DevicePair _ GetDeviceCP[data, state]; ImagerFontCache.GetStringData[DoChar, fontCache, fontCode, characters, start, length]; SetDeviceCP[data, state, cp]; } ELSE { CorrectingDoChar: PROC [charCode: CARDINAL, charData: REF] ~ { loadInfo: REF CharLoadInfo ~ NARROW[charData]; delta: Pair _ TransformDeviceToViewVec[[loadInfo.sWidth, loadInfo.fWidth]]; IF NOT noImage THEN { cp: DevicePair _ GetDeviceCP[data, state]; ApplyMask[context, loadInfo.mask, Scaled.Round[cp.s], Scaled.Round[cp.f]]; }; 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; residual _ residual - 1; }; ImagerFontCache.GetStringData[CorrectingDoChar, fontCache, fontCode, characters, start, length]; }; EXITS TryAgain => {start _ start + (length-residual); length _ residual}; }; ENDLOOP; }; ManhattanPolygonFromSurfaceRectangle: PROC [context: Context, box: IntRectangle] RETURNS [LIST OF DeviceRectangle] ~ { data: Data ~ NARROW[context.data]; s0: INTEGER _ SurfaceOriginS[data]-box.y; s1: INTEGER _ SurfaceOriginS[data]-(box.y+box.h); f0: INTEGER _ box.x; f1: INTEGER _ box.x+box.w; RETURN [ImagerManhattan.CreateFromBox[[MIN[s0, s1], MIN[f0, f1], ABS[s1-s0], ABS[f1-f0]]]]; }; SetViewOrigin: PROC [context: Context, viewOrigin: IntPair] ~ { data: Data _ NARROW[context.data]; data.viewOrigin _ viewOrigin; }; 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]; surfaceBox: IntRectangle _ [ x: deviceBox.fMin, y: SurfaceOriginS[data]-(deviceBox.sMin+deviceBox.sSize), w: deviceBox.fSize, h: deviceBox.sSize ]; 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] ~ { data: Data ~ NARROW[context.data]; m: Transformation ~ SurfaceToDevice[data].FromRec; sMinDest, fMinDest, sSize, fSize: INTEGER; shift: Pair _ ImagerTransform.TransformVec[[dest.x-source.x, dest.y-source.y], m]; [[sMinDest, fMinDest, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[[dest.x, dest.y, source.x, source.h], m]; data.canvas.Clip[[sMinDest, fMinDest, sSize, fSize]].Transfer[data.canvas.ShiftMap[Real.RoundI[shift.x], Real.RoundI[shift.y]]]; }; TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; trans: TransformationRec ~ CompositeT[data, state]; manhattanPolygon: ImagerManhattan.Polygon _ NIL; IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN { s0: REAL ~ trans.b * y + trans.c; s1: REAL ~ trans.b * (y+h) + trans.c; f0: REAL ~ trans.d * x + trans.f; f1: REAL ~ trans.d * (x+w) + trans.f; sMin: INTEGER ~ Floor[MAX[MIN[s0, s1], -LAST[INTEGER]/2]]; sMax: INTEGER ~ Ceiling[MIN[MAX[s0, s1], LAST[INTEGER]/2]]; fMin: INTEGER ~ Floor[MAX[MIN[f0, f1], -LAST[INTEGER]/2]]; fMax: INTEGER ~ Ceiling[MIN[MAX[f0, f1], LAST[INTEGER]/2]]; IF sMax<=sMin OR fMax<=fMin THEN RETURN [invisible]; manhattanPolygon _ ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]]; } 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.canvas.BoundedWindow]; }; visibility _ ImagerManhattan.IsVisible[manhattanPolygon, CompositeClipper[context]]; ImagerManhattan.Destroy[manhattanPolygon]; }; GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data ~ NARROW[context.data]; deviceToSurface: Transformation ~ ImagerTransform.Invert[SurfaceToDevice[data].FromRec]; RETURN [ImagerTransform.TransformIntRectangle[[data.canvas.sMin, data.canvas.fMin, data.canvas.sSize, data.canvas.fSize], deviceToSurface]] }; SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [REF] ~ { SELECT op FROM ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp]; }; LFDisplayClass: ImagerPrivate.Class _ NEW [ImagerPrivate.ClassRep _ [ deviceType: $LFDisplay, 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[LFDisplayClass]; END.