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. ImagerLFImpl.mesa Michael Plass, October 31, 1983 11:18 am Doug Wyatt, October 11, 1983 3:53 pm Now in context.state: currentColor: Color, currentPosition: Pair, -- in device space transformation: Transformation, surfaceToDevice: Transformation, Clipper info: View-to-device transformation info Scratch storage Cached color info The bitmap concatenates T with the current view-to-device transformation move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f)  note s comes first for ScanConvert! move: PROC [s, f: REAL], line: PROC [s, f: REAL], curve: PROC [s1, f1, s2, f2, s3, f3: REAL] Transforms (x, y) to (s, f)  note s comes first for ScanConvert! Because of an apparent bug in Real.FScale. Sorry, not yet. Ê!˜Jšœ™J™(J™$J˜šÏk ˜ Jšœ œ˜ Jšœœœ˜Jšœ˜Jšœ œä˜õJ˜ J˜Jšœ˜Jšœœ ˜$Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœœ ˜Jšœ˜Jšœœ˜)Jšœ œ˜'Jšœ˜—J˜Jšœœ˜Jšœç˜îJšœœœ ˜J˜šœ œœ˜JšœSœ˜\—J˜Jšœ˜J˜JšÏnœœœ7œ˜VJ˜Jšœœœ˜Jšœœ˜Jšœ œ˜*Jšœœ˜"Jšœœœ˜+Jšœœ˜ J˜Jšœ œ˜Jšœœ˜"J˜Jšœœœ ˜šœ œœ˜™Jšœ™JšœÏc™)Jšœœ™Jšœ ™ —Jšœ˜™ JšœŸ˜(Jšœœ˜Jšœœ˜JšœŸ˜&Jšœœ˜JšœŸ˜+—™"Jšœœ˜"Jšœœ˜$—™Jšœ+˜+J˜Jšœ˜—™Jšœ ˜ —™ Jšœ˜—Jšœ˜—J˜šœœœ˜:Jšœ&˜&Jšœ-˜-Jšœ(˜(Jšœ*˜*Jšœ*˜*Jšœ+˜+Jšœ+˜+Jšœ2˜2Jšœ4˜4J˜!Jšœ˜—Jšœ œœ'˜Hš žœœœœœ˜OJšœ/˜/šœœ˜4šœ˜Jšœœ˜!JšœC˜G—Jšœ˜—Jšœ˜J˜—šž œœœ˜HJ™=šœ˜ Jšœ$˜$Jšœ$˜$Jšœ#˜#Jšœ"˜"Jšœ!˜!Jšœ˜—Jšœ˜—J˜šžœœœ˜?šœ˜ Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜—Jšœ˜—J˜Jšœ œœ˜-J˜šž œœœ˜Cšœ˜ Jšœœ#˜EJšœœ"˜CJšœ˜—Jšœ˜J˜—šž œœ-˜>Jšœœ$˜HJšœœ$˜GJšœ˜J˜—JšœY˜YJ˜šžœœœ˜,Jšœ œ ˜Jšœ˜šœ'˜'JšœM˜M—Jšœ+˜+Jšœœ˜šœœ˜Jšœœ˜&Jšœœ/˜A—Jšœ!˜!Jšœ˜Jšœ˜J˜—Jšœœ˜Jšœ œ˜Jšœœ˜(Jšœœ ˜4J˜šžœœ˜"Jšœ œ˜"Jšœœ˜%Jšœ˜Jšœ.˜.JšœO˜OJšœO˜OJšœ9œ˜?šœ#˜#JšœH˜H—Jšœ9˜9Jšœ)˜)Jšœ ˜ Jšœ˜J˜—šžœœœœ ˜QJšœœ˜%Jšœœ˜ Jšœœ˜$Jšœ*˜*Jšœ+˜+Jšœ˜Jšœ˜Jšœ$˜$Jšœ#˜#Jšœ!˜!Jšœœœ˜#Jšœ˜J˜—šž œœ3œ œA˜”šœ œ˜šœœœ˜Jšœœ˜Jš œœœœœ˜9Jšœ˜—Jšœœ˜—Jšœ˜J˜—šžœœ5˜Išœœ˜$Jšœœœ˜Jšœœ˜—Jšœ˜J˜—šž œœœ˜2Jšœ œ˜"Jšœœ˜%Jšœ*˜*Jšœ'˜'Jšœ œ˜(Jšœ œ˜(Jšœœ˜*Jšœœ˜*šžœœ˜Jšœ(˜(Jšœ˜Jšœ˜Jšœ!˜!Jšœ!˜!Jšœ&œ'˜SJšœ˜Jšœ˜Jšœ˜—Jšœ(˜(Jšœœ˜J˜ Jšœ˜J˜—šžœœœ˜/Jšœ œ˜"Jšœœ˜%Jšœ7˜7Jšœ'˜'Jšœ œ˜(Jšœ œ˜(Jšœœ˜*Jšœœ˜*šžœœ˜Jšœ(˜(Jšœ˜Jšœ˜Jšœ!˜!Jšœ!˜!Jšœ"œ#˜KJšœ˜Jšœ˜Jšœ˜—Jšœ(˜(Jšœœ˜J˜ Jšœ˜J˜—Jšœ œœ ˜šœ œœ˜Jšœ œ˜J˜ Jšœ˜J˜—šœ œ˜J˜—šž œœœ˜9Jšœ œ˜"šœœ˜#Jšœœ ˜#Jšœ,˜,JšœT˜TJ˜Jšœ ˜Jšœ˜—Jšœœœ˜Jšœ˜J˜—šž œœ)˜9Jšœ œ˜"šœ œœ˜Jšœœ˜"Jšœœ˜ J˜Jšœ˜—šœ˜Jšœœ˜!Jšœ,˜,JšœL˜LJ˜Jšœ˜—Jšœœ˜#Jšœ˜J˜—šžœœœœ˜8Jšœœœ˜5Jšœœ!˜,J˜J˜—šžœœ œ˜2šœ˜ šœ ˜ Jšœœ˜Jšœ˜—šœ˜Jšœœ˜Jšœ˜Jšœ˜—Jšœœ˜—Jšœ"˜"J˜J˜—šž œœ4œ˜JJšœ˜Jšœ œ˜"Jšœœ˜%šœ%˜%Jšœœœ™Jšœœœ™Jšœœœ™*Jšœ˜Jšœ,˜,šžœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—Jšžœœ;˜GJšžœœ;˜Gšžœœ˜#JšœA˜AJšœ5˜5—JšžœœB˜Mšžœœœ˜(Jšœ+˜+Jšœ+˜+Jšœ˜Jšœ˜—Jšœ ˜ Jšœ0˜0Jšœ˜—šžœœ˜ Jšœœœ œ˜,Jšœœœ˜.Jšœ,˜,Jšœ˜—Jšœ^˜^Jšœ ˜ Jšœ˜J˜—šžœœ4œ˜LJšœ œ œ˜6Jšœ˜Jšœ œ˜"Jšœœ˜%šžœœ˜ Jšœœœ œ˜,Jšœœœ˜.Jšœ,˜,Jšœ˜—šœ4˜4Jšœ˜Jšœ˜Jšœ(˜(Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—Jšœ ˜ Jšœ˜J˜—šžœœ%œ˜TJšœ œ˜"Jšœœ˜%Jšœ,˜,šœ%˜%Jšœœœ™Jšœœœ™Jšœœœ™*Jšœ˜šžœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—Jšœ6˜6Jšœ?˜?JšœH˜HJšœ?˜?Jšœ˜—šžœœ˜ Jšœœœ œ˜,Jšœœœ˜.Jšœ,˜,Jšœ˜—šœœ˜Jšœ:˜:JšœH˜HJš œœœœœœ˜EJš œœœœœœ˜DJš œœœœœœ˜EJš œœœœœœ˜Dšœ˜Jšœ?˜?—Jšœ˜—Jšœ^˜^Jšœ ˜ Jšœ˜J˜—š žœœœœœ˜DJ™*Jšœœ ˜&Jšœœ˜Jšœ˜J˜—šžœœ,œ ˜VJšœ œ˜"Jšœœ˜%Jšœ,˜,Jšœ\˜\šžœœ˜ Jšœœœ œ˜,Jšœœœ˜.Jšœœ#˜<šžœœœ œ˜6JšœA˜AJšœH˜HJšœ/˜/Jšœ/˜/Jšœ8˜8Jšœ8˜8Jšœ œ˜Jšœœ˜JšœW˜Wšœ˜Jšœ˜Jšœœœ˜2Jšœœ+˜BJšœ˜Jšœœœ˜2Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜"Jšœ˜J˜—šžœœœ ˜6šœœœ˜(šœœ˜#JšœœH˜iJšœB˜FJšœ˜—Jšœ*˜.Jšœœ˜"Jšœ˜—Jšœ˜Jšœ˜J˜—šžœœ)œœ˜KJšœ œ˜"Jšœœ˜#šœœœ˜'Jšœœ˜!Jšœ$˜$Jšœ˜J˜Jšœ˜—šœœ œ˜šœœ˜#Jšœ1˜1J˜Jšœ˜—šœ˜Jšœ9˜9J˜J˜—Jšœ˜—šœ˜šœœ˜#Jšœ9˜9J˜Jšœœ˜"Jšœ˜—šœ˜Jšœ2˜2J˜J˜—Jšœ˜—Jšœ˜J˜—šž œœ4œ˜MJšœ<˜˜>—Jšœ œœ˜/Jšœ<˜˜>J˜J˜—Jšœ˜Jšœ˜Jšœ˜Jšœ;˜;Jšœ˜J˜—šžœœ0œ˜JJšœ œ˜"Jšœ4˜4Jšœœ˜#Jšœœ˜#šœ˜Jšœ œ"˜1Jšœ˜ —Jšœ˜J˜—šžœœ<˜VJšœ œ˜"Jšœ*˜*Jšœ"œ˜*Jšœœ˜Jšœv˜vJšœ\˜\Jšœd˜dJšœ˜—J˜šž œœ œœ˜QJšœ œ˜"Jšœ6˜6Jšœ)˜/Jšœ˜J˜—šžœœœ˜DJšœ œ˜"JšœP˜PJšœ…˜‹Jšœ˜J˜—šž œœœ˜AJšœ œ˜"JšœP˜PJšœ˜˜žJšœ˜J˜—š ž œœœœœœ˜IJšœœ˜)šœ˜šœ˜Jšœ œ˜Jšœ4˜;Jšœœ(œ!˜XJšœ6˜=Jšœ4˜;Jš œœ œœœ˜%Jšœ˜—Jšœœ'˜7—Jšœ˜J˜—šœ&œ˜EJšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ!˜!Jšœ!˜!Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ%˜%Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ)˜)Jšœ!˜!Jšœ/˜/Jšœ˜Jšœ)˜)Jšœ˜Jšœ˜Jšœ#˜#Jšœ%˜%Jšœ3˜3Jšœ˜Jšœ+˜+Jšœ-˜-Jšœ+˜+Jšœ9˜9Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ#˜#Jšœ+˜+Jšœ1˜1Jšœ˜Jšœ˜Jšœ'˜'Jšœ)˜)Jšœ3˜3Jšœ7˜7Jšœ˜Jšœ)˜)Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ+˜+Jšœ˜Jšœ#˜#Jšœ˜Jšœ˜Jšœ˜—J˜Jšœ-˜-J˜Jšœ˜—…—z2žU