<> <> <> DIRECTORY Environment USING [bitsPerWord], Font USING [FONT], Imager, ImagerBasic USING [Pair, IntPair, Rectangle, IntRectangle, DeviceRectangle, TransformType, Transformation, Visibility, PathMapType, StrokeEnd, Color, ColorRep, ConstantColor, SampledColor, PixelArray, PixelArrayRep, PixelBuffer, PixelBufferRep], ImagerConic, ImagerDefault, ImagerFontCache, ImagerFrameBuffer USING [LFDisplay], ImagerHalftone, ImagerMasks, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, Real USING [RoundLI], Scaled, TerminalMultiplex USING [SelectTerminal], UserTerminal USING [GetState, SetState] ; ImagerLFImpl: CEDAR MONITOR IMPORTS Imager, ImagerConic, ImagerDefault, ImagerFontCache, ImagerFrameBuffer, ImagerHalftone, ImagerMasks, ImagerPixelMaps, ImagerPrivate, ImagerScanConverter, ImagerStroke, ImagerTransform, Real, Scaled, TerminalMultiplex, UserTerminal ~ 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; Mask: TYPE ~ ImagerMasks.Mask; PixelMap: TYPE ~ ImagerPixelMaps.PixelMap; Tile: TYPE ~ ImagerPixelMaps.Tile; SpecialColor: TYPE ~ REF ColorRep[special]; Name: TYPE ~ ImagerPrivate.Name; Context: TYPE ~ Imager.Context; State: TYPE ~ ImagerDefault.State; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ <> <> <> <> <> doSaveCount: [0..1000) _ 0, <> clientClipper: Mask, -- in device coords clientClipperPresent: BOOLEAN, clientClipperExclude: BOOLEAN, viewClipper: Mask, -- in device coords compositeClipperValid: BOOLEAN, compositeClipper: Mask, -- in device coords <> sOriginView, fOriginView: INTEGER, sOriginBrick, fOriginBrick: INTEGER, <> devicePath: ImagerScanConverter.DevicePath, lineBuffer: Mask, tileSamples: SampledColor, <> tile: Tile, <> canvas: PixelMap ]; transformOfType: ARRAY TransformType OF Transformation ~ [ none : [ 0, 0, 0, 0, 0, 0, none], identity : [ 1, 0, 0, 0, 1, 0, identity], rot90 : [ 0, -1, 0, 1, 0, 0, rot90], rot180 : [-1, 0, 0, 0, -1, 0, rot180], rot270 : [ 0, 1, 0, -1, 0, 0, rot270], mirrorX : [-1, 0, 0, 0, 1, 0, mirrorX], mirrorY : [ 1, 0, 0, 0, -1, 0, mirrorY], mirror45Deg : [ 0, 1, 0, 1, 0, 0, mirror45Deg], mirror135Deg : [ 0, -1, 0, -1, 0, 0, mirror135Deg], hard : [3, 1, 4, 1, 5, 9, hard] ]; rot90Type: ARRAY TransformType OF TransformType ~ PrecomputeRot90Type[]; PrecomputeRot90Type: PROC RETURNS [t: ARRAY TransformType OF TransformType] ~ { rot90: Transformation ~ transformOfType[rot90]; FOR transformType: TransformType IN TransformType DO t[transformType] _ IF transformType = none THEN none ELSE ImagerTransform.Concat[transformOfType[transformType], rot90].type ENDLOOP; }; CompositeT: PROC [data: Data, state: State] RETURNS [Transformation] ~ { <> RETURN [[ a: -state.nps.T.d, d: state.nps.T.a, b: -state.nps.T.e, e: state.nps.T.b, c: -state.nps.T.f+data.sOriginView, f: state.nps.T.c+data.fOriginView, type: rot90Type[state.nps.T.type] ]] }; SurfaceToDevice: PROC [data: Data] RETURNS [Transformation] ~ { RETURN [[ a: 0, d: 1, b: -1, e: 0, c: data.canvas.sSize, f: 0, type: rot90 ]] }; DeviceCP: TYPE ~ RECORD [s, f: Scaled.Value]; GetDeviceCP: PROC [data: Data, state: State] RETURNS [DeviceCP] ~ { RETURN [[ s: ScaledFromReal[-state.cpy].PLUS[Scaled.FromInt[data.sOriginView]], f: ScaledFromReal[state.cpx].PLUS[Scaled.FromInt[data.fOriginView]] ]] }; SetDeviceCP: PROC [data: Data, state: State, cp: DeviceCP] ~ { state.cpy _ -Scaled.Float[cp.s.MINUS[Scaled.FromInt[data.sOriginView]]]; state.cpx _ Scaled.Float[cp.f.MINUS[Scaled.FromInt[data.fOriginView]]]; }; LFBrick: ImagerHalftone.DeviceBrick _ ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255]; Init: PROC [context: Context, info: REF] ~ { data: Data ~ NEW[DataRep]; context.data _ data; data.tile _ ImagerPixelMaps.CreateTile[ pixelMap: ImagerPixelMaps.Create[lgBitsPerPixel: 0, bounds: [0, 0, 16, 16]]]; 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]; 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.nps.fieldXMax _ state.nps.mediumXSize _ data.canvas.fSize/pixelsPerMeter; state.nps.fieldYMax _ state.nps.mediumYSize _ data.canvas.sSize/pixelsPerMeter; data.clientClipperPresent _ data.compositeClipperValid _ FALSE; data.viewClipper _ ImagerMasks.Box[ [sMin: 0, fMin: 0, sSize: data.canvas.sSize, fSize: data.canvas.fSize]]; data.sOriginView _ data.sOriginBrick _ data.canvas.sSize; data.fOriginView _ data.fOriginBrick _ 0; SetColor[context, Imager.black]; }; MakeTileSamples: PROC [xSize, ySize: NAT] RETURNS [tileSamples: SampledColor] ~ { tileSamples _ NEW[ColorRep[sampled]]; tileSamples.transparent _ FALSE; tileSamples.pa _ NEW[PixelArrayRep]; tileSamples.m _ ImagerTransform.Rotate[0]; tileSamples.colorOperator _ $Intensity8bpp; 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; }; DoSaveAll: PROC [context: Context, body: PROC] ~ { data: Data _ NARROW[context.data]; state: State _ NARROW[context.state]; stateRep: ImagerDefault.StateRep _ state^; clipper: Clipper _ GetClipper[context]; sOriginView: INTEGER _ data.sOriginView; fOriginView: INTEGER _ data.fOriginView; sOriginBrick: INTEGER _ data.sOriginBrick; fOriginBrick: INTEGER _ data.fOriginBrick; Restore: PROC ~ { data.doSaveCount _ data.doSaveCount - 1; data.sOriginView _ sOriginView; data.fOriginView _ fOriginView; data.sOriginBrick _ sOriginBrick; data.fOriginBrick _ fOriginBrick; IF stateRep.nps.color # state.nps.color THEN SetColor[context, stateRep.nps.color]; SetClipper[context, clipper]; state^ _ stateRep; }; data.doSaveCount _ data.doSaveCount + 1; body[! UNWIND => Restore[]]; Restore[]; }; DoSave: PROC [context: Context, body: PROC] ~ { data: Data _ NARROW[context.data]; state: State _ NARROW[context.state]; stateNPS: ImagerDefault.NonPersistentState _ state.nps; clipper: Clipper _ GetClipper[context]; sOriginView: INTEGER _ data.sOriginView; fOriginView: INTEGER _ data.fOriginView; sOriginBrick: INTEGER _ data.sOriginBrick; fOriginBrick: INTEGER _ data.fOriginBrick; Restore: PROC ~ { data.doSaveCount _ data.doSaveCount - 1; data.sOriginView _ sOriginView; data.fOriginView _ fOriginView; data.sOriginBrick _ sOriginBrick; data.fOriginBrick _ fOriginBrick; IF stateNPS.color # state.nps.color THEN SetColor[context, stateNPS.color]; SetClipper[context, clipper]; state.nps _ stateNPS; }; data.doSaveCount _ data.doSaveCount + 1; body[! UNWIND => Restore[]]; Restore[]; }; Clipper: TYPE ~ REF ClipperRep; ClipperRep: TYPE ~ RECORD [ exclude: BOOLEAN, mask: Mask ]; ccCount: INT _ 0; GetClipper: PROC [context: Context] RETURNS [Clipper] ~ { data: Data ~ NARROW[context.data]; IF data.clientClipperPresent THEN { clipper: Clipper ~ NEW[ClipperRep]; clipper.exclude _ data.clientClipperExclude; clipper.mask _ data.clientClipper.InlineShift[-data.sOriginView, -data.fOriginView]; ccCount _ ccCount + 1; RETURN [clipper] } ELSE RETURN [NIL] }; SetClipper: PROC [context: Context, clipper: Clipper] ~ { data: Data ~ NARROW[context.data]; IF clipper = NIL THEN { data.clientClipperPresent _ FALSE; data.clientClipper.refRep _ NIL; ccCount _ ccCount + 1; } ELSE { data.clientClipperPresent _ TRUE; data.clientClipperExclude _ clipper.exclude; data.clientClipper _ clipper.mask.Shift[data.sOriginView, data.fOriginView]; ccCount _ ccCount + 1; }; data.compositeClipperValid _ FALSE; }; IGet: PROC [context: Context, n: Name] RETURNS [REF] ~ { IF n = $clipOutline THEN RETURN [GetClipper[context]] ELSE RETURN [ImagerDefault.IGet[context, n]] }; ISet: PROC [context: Context, n: Name, x: REF] ~ { SELECT n FROM $color => { SetColor[context, NARROW[x]]; }; $clipOutline => { SetClipper[context, NARROW[x]]; RETURN; }; ENDCASE => NULL; ImagerDefault.ISet[context, n, x]; }; MaskFromPath: PROC [context: Context, pathMap: PathMapType, pathData: REF] RETURNS [mask: Mask] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; GenPath: ImagerScanConverter.PathProc <> <> <> ~ { m: Transformation ~ 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 _ p }; Xline: PROC [p: Pair] ~ { q: Pair ~ Xform[p]; line[q.x, q.y]; lp _ p }; 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 _ p3 }; Curve: PROC [p1, p2, p3: Pair] ~ {curve[p1.x, p1.y, p1.x, p1.y, p1.x, p1.y]}; Xconic: PROC [p1, p2: Pair, r: REAL] ~ { q1: Pair ~ Xform[p1]; q2: Pair ~ Xform[p2]; ImagerConic.ToCurves[lp, q1, q2, r, Curve]; lp _ p2; }; lp: Pair; pathMap[pathData, Xmove, Xline, Xcurve, Xconic]; }; Runs: PROC [ run: PROC [sMin, fMin: INTEGER, fSize: NAT], repeat: PROC [timesToRepeatScanline: NAT]] ~ { data.devicePath.ConvertToRuns[runProc: run]; }; data.devicePath _ ImagerScanConverter.CreatePath[pathProc: GenPath, scratch: data.devicePath]; mask _ ImagerMasks.Create[Runs]; }; MaskFromStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd, closed: BOOL] RETURNS [mask: Mask] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; Runs: PROC [ run: PROC [sMin, fMin: INTEGER, fSize: NAT], repeat: PROC [timesToRepeatScanline: NAT]] ~ { data.devicePath.ConvertToRuns[runProc: run]; }; data.devicePath _ ImagerStroke.DevicePathFromStroke[ pathMap: pathMap, pathData: pathData, clientToDevice: CompositeT[data, state], width: strokeWidth, strokeEnd: strokeEnd, closed: FALSE, scratch: data.devicePath ]; mask _ ImagerMasks.Create[Runs]; }; MaskFromRectangle: PROC [context: Context, area: Rectangle] RETURNS [mask: Mask] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; m: Transformation ~ CompositeT[data, state]; GenPath: ImagerScanConverter.PathProc <> <> <> ~ { 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 ]]}; { q: Pair ~ Xform[[area.x, area.y]]; move[q.x, q.y] }; { q: Pair ~ Xform[[area.x + area.w, area.y]]; line[q.x, q.y] }; { q: Pair ~ Xform[[area.x + area.w, area.y + area.h]]; line[q.x, q.y] }; { q: Pair ~ Xform[[area.x, area.y + area.h]]; line[q.x, q.y] }; }; Runs: PROC [ run: PROC [sMin, fMin: INTEGER, fSize: NAT], repeat: PROC [timesToRepeatScanline: NAT]] ~ { data.devicePath.ConvertToRuns[runProc: run]; }; IF m.type # hard THEN { p1: Pair _ ImagerTransform.Transform[[area.x, area.y], m]; p2: Pair _ ImagerTransform.Transform[[area.x+area.w, area.y+area.h], m]; sMin: INTEGER _ Real.RoundLI[MAX[MIN[p1.x, p2.x], -LAST[INTEGER]/2]]; sMax: INTEGER _ Real.RoundLI[MIN[MAX[p1.x, p2.x], LAST[INTEGER]/2]]; fMin: INTEGER _ Real.RoundLI[MAX[MIN[p1.y, p2.y], -LAST[INTEGER]/2]]; fMax: INTEGER _ Real.RoundLI[MIN[MAX[p1.y, p2.y], LAST[INTEGER]/2]]; RETURN [ImagerMasks.InlineBox[ [sMin: sMin, fMin: fMin, sSize: sMax-sMin, fSize: fMax-fMin]]]; }; data.devicePath _ ImagerScanConverter.CreatePath[pathProc: GenPath, scratch: data.devicePath]; mask _ ImagerMasks.Create[Runs]; }; ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED { <> i: INT _ Real.RoundLI[real * 65536.0]; RETURN[LOOPHOLE[i]] }; MaskFromPixelArray: PROC [context: Context, pixelArray: PixelArray] RETURNS [Mask] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; m: Transformation ~ CompositeT[data, state]; destArea: Mask _ MaskFromRectangle[context, [0, 0, pixelArray.xPixels, pixelArray.yPixels]]; Runs: PROC [ run: PROC [sMin, fMin: INTEGER, fSize: NAT], repeat: PROC [timesToRepeatScanline: NAT]] ~ { buffer: PixelBuffer _ NEW[PixelBufferRep[destArea.fSize+1]]; DoRun: PROC [sMin, fMin: INTEGER, fSize: CARDINAL] ~ { nextPixel: Pair _ ImagerTransform.InverseTransformVec[[0, 1], m]; start: Pair _ ImagerTransform.InverseTransform[[0.5+sMin, 0.5+fMin], m]; xStart: Scaled.Value _ ScaledFromReal[start.x]; yStart: Scaled.Value _ ScaledFromReal[start.y]; xDeltaPixel: Scaled.Value _ ScaledFromReal[nextPixel.x]; yDeltaPixel: Scaled.Value _ ScaledFromReal[nextPixel.y]; runStart: NAT _ 0; fRel: NAT _ 0; pixelArray.get[pixelArray, buffer, fSize, 0, xStart, yStart, xDeltaPixel, yDeltaPixel]; WHILE fRel < fSize DO buffer[fSize] _ 0; WHILE buffer[fRel] # 0 DO fRel _ fRel + 1 ENDLOOP; IF fRel > runStart THEN {run[sMin, fMin + fRel, fRel - runStart]}; buffer[fSize] _ 1; WHILE buffer[fRel] = 0 DO fRel _ fRel + 1 ENDLOOP; runStart _ fRel; ENDLOOP; }; destArea.MapRuns[DoRun]; }; RETURN [ImagerMasks.Create[Runs]]; }; CompositeClipper: PROC [data: Data] RETURNS [Mask] ~ { IF NOT data.compositeClipperValid THEN { IF data.clientClipperPresent THEN { IF data.clientClipperExclude THEN data.compositeClipper _ data.viewClipper.Difference[data.clientClipper] ELSE data.compositeClipper _ data.viewClipper.And[data.clientClipper]; } ELSE data.compositeClipper _ data.viewClipper; data.compositeClipperValid _ TRUE; }; RETURN [data.compositeClipper] }; ClipMask: PROC [context: Context, mask: Mask, exclude: BOOLEAN _ FALSE] ~ { data: Data ~ NARROW[context.data]; data.compositeClipperValid _ FALSE; IF NOT data.clientClipperPresent THEN { data.clientClipperPresent _ TRUE; data.clientClipperExclude _ exclude; data.clientClipper _ mask; ccCount _ ccCount + 1; } ELSE IF exclude THEN { IF data.clientClipperExclude THEN { data.clientClipper _ data.clientClipper.Or[mask]; ccCount _ ccCount + 1; } ELSE { data.clientClipper _ data.clientClipper.Difference[mask]; ccCount _ ccCount + 1; }; } ELSE { IF data.clientClipperExclude THEN { data.clientClipper _ mask.Difference[data.clientClipper]; ccCount _ ccCount + 1; data.clientClipperExclude _ FALSE; } ELSE { data.clientClipper _ data.clientClipper.And[mask]; ccCount _ ccCount + 1; }; }; }; ClipOutline: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ { ClipMask[context, MaskFromPath[context, pathMap, pathData]]; }; ExcludeOutline: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ { ClipMask[context, MaskFromPath[context, pathMap, pathData], TRUE]; }; ClipRectangle: PROC [context: Context, x, y, w, h: REAL] ~ { ClipMask[context, MaskFromRectangle[context, [x, y, w, h]]]; }; ExcludeRectangle: PROC [context: Context, x, y, w, h: REAL] ~ { ClipMask[context, MaskFromRectangle[context, [x, y, w, h]], TRUE]; }; IntegerClipRectangle: PROC [context: Context, x, y, w, h: INTEGER] ~ { ClipMask[context, MaskFromRectangle[context, [x, y, w, h]], TRUE]; }; IntegerExcludeRectangle: PROC [context: Context, x, y, w, h: INTEGER] ~ { ClipMask[context, MaskFromRectangle[context, [x, y, w, h]], TRUE]; }; SetColor: PROC [context: Context, color: Color] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; IF color = state.nps.color THEN RETURN; state.nps.color _ 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], mask: ImagerMasks.Box[[0, 0, 16, 16]], 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.nps.color _ Imager.black; }; MaskStroke: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL, strokeEnd: StrokeEnd] ~ { data: Data ~ NARROW[context.data]; mask: ImagerMasks.Mask _ MaskFromStroke[context, pathMap, pathData, strokeWidth, strokeEnd, FALSE]; ApplyMask[context, mask]; }; MaskStrokeClosed: PROC [context: Context, pathMap: PathMapType, pathData: REF, strokeWidth: REAL] ~ { data: Data ~ NARROW[context.data]; mask: ImagerMasks.Mask _ MaskFromStroke[context, pathMap, pathData, strokeWidth, nil, TRUE]; ApplyMask[context, mask]; }; blackTile: Tile _ ImagerPixelMaps.TileFromConstant[pixelValue: 1, lgBitsPerPixel: 0]; whiteTile: Tile _ ImagerPixelMaps.TileFromConstant[pixelValue: 0, lgBitsPerPixel: 0]; ApplyMask: PROC [context: Context, mask: Mask] ~ { state: State ~ NARROW[context.state]; IF state.nps.noImage = 0 THEN { data: Data ~ NARROW[context.data]; color: Color ~ NARROW[state.nps.color]; clippedMask: Mask _ mask.And[CompositeClipper[data]]; IF clippedMask.sSize = 0 THEN RETURN; SELECT color FROM Imager.black => ImagerMasks.MaskTile[data.canvas, clippedMask, blackTile]; Imager.white => ImagerMasks.MaskTile[data.canvas, clippedMask, whiteTile]; Imager.XOR => ImagerMasks.MaskTile[data.canvas, clippedMask, blackTile, [xor, null]]; ENDCASE => WITH color SELECT FROM sampledColor: SampledColor => { transform: Transformation _ ImagerTransform.Concat[sampledColor.m, SurfaceToDevice[data]]; transform.c _ transform.c + data.sOriginBrick; transform.f _ transform.f + data.fOriginBrick; ImagerHalftone.Halftone[data.canvas, clippedMask, sampledColor.pa, transform, LFBrick]; }; ENDCASE => { -- constant (other than black or white) or stipple tile: ImagerPixelMaps.Tile _ data.tile; tile.sOrigin _ tile.sOrigin + data.sOriginBrick; tile.fOrigin _ tile.fOrigin + data.fOriginBrick; ImagerMasks.MaskTile[data.canvas, clippedMask, tile]; }; }; }; MaskFill: PROC [context: Context, pathMap: PathMapType, pathData: REF] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPath[context, pathMap, pathData]; ApplyMask[context, mask]; }; MaskPixel: PROC [context: Context, pa: PixelArray] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPixelArray[context, pa]; ApplyMask[context, mask]; }; scratchPixelArray: PixelArray _ NEW[PixelArrayRep]; scratchBitmapDesc: REF ImagerPixelMaps.PixelMapRep _ NEW[ImagerPixelMaps.PixelMapRep]; MaskBits: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, tile: IntRectangle, area: IntRectangle] ~ { ENABLE UNWIND => NULL; data: Data _ NARROW[context.data]; state: State ~ NARROW[context.state]; m: Transformation ~ CompositeT[data, state]; color: Color ~ NARROW[state.nps.color]; IF m.type = rot90 AND tile.w = 16 AND raster = 1 AND tile.h <= 16 AND (color = Imager.black OR color = Imager.XOR) THEN { sMin, fMin, sSize, fSize: INTEGER; sMinTile, fMinTile, sSizeTile, fSizeTile: INTEGER; [[sMin, fMin, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[area, m]; [[sMinTile, fMinTile, sSizeTile, fSizeTile]] _ ImagerTransform.TransformIntRectangle[tile, m]; scratchBitmapDesc.pointer _ base; scratchBitmapDesc.rast _ raster; scratchBitmapDesc.lines _ sSizeTile; ImagerMasks.MaskTile[data.canvas, CompositeClipper[data].And[[sMin, fMin, sSize, fSize, NIL]], [sMinTile, fMinTile, sSizeTile, fSizeTile, 0, scratchBitmapDesc], IF color = Imager.black THEN [or, null] ELSE [xor, null]]; } ELSE { IF firstBadMaskBits THEN {firstBadMaskBits _ FALSE; ERROR Imager.Error[$NotImplemented]} -- sorry, not yet. }; }; firstBadMaskBits: BOOLEAN _ TRUE; MaskRectangle: PROC [context: Context, x, y, w, h: REAL] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[context, [x, y, w, h]]; ApplyMask[context, mask]; }; IntegerMaskRectangle: PROC [context: Context, x, y, w, h: INTEGER] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[context, [x, y, w, h]]; ApplyMask[context, mask]; }; rasterToRunGroupStorageRatio: INT _ 1; CharLoadInfo: TYPE ~ RECORD[sWidth, fWidth: Scaled.Value, mask: Mask, loadRepSize: INT]; 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 [sMin, fMin: INTEGER, fSize: NAT], repeat: PROC [timesToRepeatScanline: NAT]] ~ { Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { run[sMin: sMin, fMin: fMin, fSize: fSize]; nRuns _ nRuns + 1; }; font.fontGraphicsClass.maskProc[font, transformation, char, Run]; }; nRuns _ 0; ans.mask _ ImagerMasks.Create[Runs]; ans.nRuns _ nRuns; }; FontCompositeTransform: PROC [context: Context, font: FONT] RETURNS [t: Transformation] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; t _ CompositeT[data, state]; t.c _ t.f _ 0; t _ ImagerTransform.Concat[font.actualTransformation, t]; }; 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 ~ ImagerTransform.Concat[ ImagerTransform.Invert[font.actualTransformation], transform]; 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], clientTransform]; loadInfo.mask _ maskN.mask; SELECT TRUE FROM maskN.mask.sSize = 0 OR maskN.mask.fSize = 0 => {loadInfo.loadRepSize _ 0}; maskN.mask.fSize > 32*Environment.bitsPerWord OR runGroupSize*rasterToRunGroupStorageRatio < rasterSize => {loadInfo.loadRepSize _ runGroupSize}; ENDCASE => {loadInfo.loadRepSize _ rasterSize; loadInfo.mask _ loadInfo.mask.Bitmap}; loadInfo.sWidth _ Scaled.FromReal[width.x]; loadInfo.fWidth _ Scaled.FromReal[width.y]; RETURN[loadInfo, 0]; }; ShowChar: PROC [context: Context, char: CHAR, font: FONT] ~ { data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; transform: Transformation _ FontCompositeTransform[context, font]; 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: font]]; cp: DeviceCP _ GetDeviceCP[data, state]; loadInfo: REF CharLoadInfo ~ NARROW[ ImagerFontCache.GetCharData[fontCache, fontCode, char-0C]]; ApplyMask[context, loadInfo.mask.Shift[Scaled.Round[cp.s], Scaled.Round[cp.f]]]; cp.s _ cp.s.PLUS[loadInfo.sWidth]; cp.f _ cp.f.PLUS[loadInfo.fWidth]; SetDeviceCP[data, state, cp]; }; 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]; transform: Transformation _ FontCompositeTransform[context, font]; 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: font]]; cp: DeviceCP _ GetDeviceCP[data, state]; DoChar: PROC [charCode: CARDINAL, charData: REF] ~ { loadInfo: REF CharLoadInfo ~ NARROW[charData]; ApplyMask[context, loadInfo.mask.Shift[Scaled.Round[cp.s], Scaled.Round[cp.f]]]; cp.s _ cp.s.PLUS[loadInfo.sWidth]; cp.f _ cp.f.PLUS[loadInfo.fWidth]; }; ImagerFontCache.GetStringData[DoChar, fontCache, fontCode, characters, start, length]; SetDeviceCP[data, state, cp]; }; firstBadDrawBitmap: BOOLEAN _ TRUE; DrawBitmap: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, area: IntRectangle] ~ { ENABLE UNWIND => NULL; data: Data ~ NARROW[context.data]; state: State ~ NARROW[context.state]; m: Transformation ~ CompositeT[data, state]; IF m.type = rot90 THEN { sMin, fMin, sSize, fSize: INTEGER; [[sMin, fMin, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[area, m]; scratchBitmapDesc.pointer _ base; scratchBitmapDesc.rast _ raster; scratchBitmapDesc.lines _ sSize; ImagerMasks.MaskTile[data.canvas, ImagerMasks.Box[[sMin, fMin, sSize, fSize]].And[CompositeClipper[data]], [sMin, fMin, sSize, fSize, 0, scratchBitmapDesc]]; } ELSE { IF firstBadDrawBitmap THEN {firstBadDrawBitmap _ FALSE; ERROR Imager.Error[$NotImplemented]} <> }; }; MaskFromSurfaceRectangle: PROC [context: Context, box: IntRectangle] RETURNS [Mask] ~ { data: Data ~ NARROW[context.data]; deviceBox: IntRectangle _ ImagerTransform.TransformIntRectangle[box, SurfaceToDevice[data]]; RETURN [ImagerMasks.InlineBox[[deviceBox.x, deviceBox.y, deviceBox.w, deviceBox.h]]]; }; SetView: PROC [context: Context, box: IntRectangle, halftoneOrigin: IntPair _ [0, 0]] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromSurfaceRectangle[context, box]; sOriginView: INTEGER _ data.canvas.sSize-box.y; fOriginView: INTEGER _ box.x; sShift: INTEGER _ sOriginView-data.sOriginView; fShift: INTEGER _ fOriginView-data.fOriginView; newHalftoneOrigin: IntPair _ ImagerTransform.IntTransform[halftoneOrigin, SurfaceToDevice[data]]; IF data.doSaveCount # 0 THEN ERROR; -- Can't change a view with DoSave in progress. IF data.viewClipper = mask AND sShift = 0 AND fShift = 0 AND newHalftoneOrigin = [data.sOriginBrick, data.fOriginBrick] THEN RETURN; data.compositeClipperValid _ FALSE; data.compositeClipper.refRep _ NIL; IF data.clientClipperPresent THEN { data.clientClipper _ data.clientClipper.Shift[sShift, fShift]; ccCount _ ccCount + 1; }; data.viewClipper _ mask; data.sOriginView _ sOriginView; data.fOriginView _ fOriginView; [data.sOriginBrick, data.fOriginBrick] _ newHalftoneOrigin; }; ClipView: PROC [context: Context, box: IntRectangle, exclude: BOOLEAN] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromSurfaceRectangle[context, box]; data.compositeClipperValid _ FALSE; data.compositeClipper.refRep _ NIL; data.viewClipper _ IF exclude THEN data.viewClipper.Difference[mask] ELSE data.viewClipper.And[mask]; }; MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] ~ { data: Data ~ NARROW[context.data]; m: Transformation ~ SurfaceToDevice[data]; sMinDest, fMinDest, sSize, fSize: INTEGER; sShift, fShift: INTEGER; [[sMinDest, fMinDest, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[[dest.x, dest.y, source.x, source.h], m]; [[sShift, fShift]] _ ImagerTransform.TransformIntVec[[dest.x-source.x, dest.y-source.y], m]; data.canvas.Clip[[sMinDest, fMinDest, sSize, fSize]].Transfer[data.canvas.ShiftMap[sShift, fShift]]; }; TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [Visibility] ~ { data: Data ~ NARROW[context.data]; mask: Mask ~ MaskFromRectangle[context, [x, y, w, h]]; RETURN[mask.IsVisible[CompositeClipper[data]]]; }; GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data ~ NARROW[context.data]; deviceToSurface: Transformation ~ ImagerTransform.Invert[SurfaceToDevice[data]]; RETURN [ImagerTransform.TransformIntRectangle[[data.canvas.sMin, data.canvas.fMin, data.canvas.sSize, data.canvas.fSize], deviceToSurface]] }; GetViewBounds: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data ~ NARROW[context.data]; deviceToSurface: Transformation ~ ImagerTransform.Invert[SurfaceToDevice[data]]; RETURN[ImagerTransform.TransformIntRectangle[[data.viewClipper.sMin, data.viewClipper.fMin, data.viewClipper.sSize, data.viewClipper.fSize], deviceToSurface]] }; SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [REF] ~ { clipperData: Data ~ NARROW[context.data]; SELECT op FROM $OtherScreen => { swapped: BOOLEAN; TRUSTED {swapped _ TerminalMultiplex.SelectTerminal[swap]}; TRUSTED {IF UserTerminal.GetState[] = disconnected THEN [] _ UserTerminal.SetState[on]}; TRUSTED {clipperData.canvas _ ImagerFrameBuffer.LFDisplay[]}; TRUSTED {swapped _ TerminalMultiplex.SelectTerminal[swap]}; RETURN [IF swapped THEN $OK ELSE NIL] }; ENDCASE => ERROR Imager.Error[$UnimplementedSpecialOp]; }; LFDisplayClass: ImagerPrivate.Class _ NEW [ImagerPrivate.ClassRep _ [ deviceType: $LFDisplay, Init: Init, IGet: IGet, ISet: ISet, IGetReal: ImagerDefault.IGetReal, ISetReal: ImagerDefault.ISetReal, IGetInt: ImagerDefault.IGetInt, ISetInt: ImagerDefault.ISetInt, DoSave: DoSave, DoSaveAll: 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: ClipOutline, ExcludeOutline: ExcludeOutline, ClipRectangle: ClipRectangle, ExcludeRectangle: ExcludeRectangle, IntegerClipRectangle: IntegerClipRectangle, IntegerExcludeRectangle: 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, SetView: SetView, ClipView: ClipView, DrawBitmap: DrawBitmap, MaskBits: MaskBits, MoveSurfaceRectangle: MoveSurfaceRectangle, TestRectangle: TestRectangle, GetSurfaceBounds: GetSurfaceBounds, GetViewBounds: GetViewBounds, SpecialOp: SpecialOp ]]; ImagerPrivate.RegisterDevice[LFDisplayClass]; END.