DIRECTORY BitBlt, Environment, Imager, ImagerBasic, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerRegistration, ImagerStroke, ImagerTransform, Inline, Real, Rope, RopeInline, Scaled, ScanConverter, TerminalMultiplex, UnifiedFonts, UserTerminal; ImagerLFImpl: CEDAR MONITOR IMPORTS BitBlt, Imager, ImagerFontCache, ImagerHalftone, ImagerMasks, ImagerRegistration, ImagerStroke, ImagerTransform, Inline, Real, Rope, RopeInline, Scaled, ScanConverter, TerminalMultiplex, UserTerminal SHARES UnifiedFonts ~ BEGIN stats: RECORD [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT _ 0]; Stats: PROC RETURNS [loadRunGroups, loadRasters, runGroupChars, rasterChars, clippedChars, culledChars: INT] = { loadRunGroups _ stats.loadRunGroups; stats.loadRunGroups _ 0; loadRasters _ stats.loadRasters; stats.loadRasters _ 0; rasterChars _ stats.rasterChars; stats.rasterChars _ 0; runGroupChars _ stats.runGroupChars; stats.runGroupChars _ 0; clippedChars _ stats.clippedChars; stats.clippedChars _ 0; culledChars _ stats.culledChars; stats.culledChars _ 0; }; ROPE: TYPE ~ Rope.ROPE; FONT: TYPE ~ UnifiedFonts.FONT; Mask: TYPE ~ ImagerMasks.Mask; Rectangle: TYPE ~ Imager.Rectangle; IntRectangle: TYPE ~ Imager.IntRectangle; Pair: TYPE ~ Imager.Pair; IntPair: TYPE ~ Imager.IntPair; Context: TYPE ~ Imager.Context; Transformation: TYPE ~ Imager.Transformation; Clipper: TYPE ~ Imager.Clipper; Visibility: TYPE ~ Imager.Visibility; Path: TYPE ~ Imager.Path; StrokeEnds: TYPE ~ Imager.StrokeEnds; Color: TYPE ~ Imager.Color; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ currentColor: Color, clientClipper: Mask, -- in device coords clientClipperPresent: BOOLEAN, clientClipperExclude: BOOLEAN, compositeClipperValid: BOOLEAN, doSaveCount: [0..1000) _ 0, compositeClipper: Mask, -- in device coords viewClipper: Mask, -- in device coords sOriginView, fOriginView: INTEGER, sOriginBrick, fOriginBrick: INTEGER, currentPosition: ImagerBasic.Pair, transformation: Transformation, viewToDevice: Transformation, surfaceToDevice: Transformation, devicePath: ScanConverter.DevicePath, lineBuffer: Mask, tile: Mask, tileSamples: ImagerBasic.SampledColor, phase: INTEGER, canvas: Mask ]; LFBrick: ImagerHalftone.DeviceBrick _ ImagerHalftone.MakeSquareBrick[4, 3, 2, 1, 1, 255]; Init: PROC [context: Context, name: ROPE, bounds: IntRectangle] ~ TRUSTED { data: Data ~ NEW[DataRep]; context.data _ data; data.tile _ ImagerMasks.NewBitmap[0, 0, 16, 16]; data.tileSamples _ MakeTileSamples[16, 16]; data.phase _ 0; data.devicePath _ ScanConverter.Allocate[]; data.canvas _ ImagerMasks.LFDisplay[]; data.transformation _ data.viewToDevice _ data.surfaceToDevice _ ImagerTransform.Concat[[1,0,0,0,1,0,hard], ImagerTransform.Create[0, -1, data.canvas.SSize, 1, 0, 0]]; data.clientClipperPresent _ data.compositeClipperValid _ FALSE; data.currentPosition _ [0, 0]; data.viewClipper _ ImagerMasks.Box[sMin: 0, fMin: 0, sSize: data.canvas.SSize, fSize: data.canvas.FSize]; [[data.sOriginView, data.fOriginView]] _ [[data.sOriginBrick, data.fOriginBrick]] _ ImagerTransform.IntTransform[[0,0], data.surfaceToDevice]; SetColor[context, Imager.black]; IF bounds # Imager.nullBounds THEN { sMin, fMin, sSize, fSize: INTEGER; [[sMin, fMin, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[bounds, data.transformation]; data.canvas _ ImagerMasks.NewBitmap[sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize]; }; }; Reset: PROC [context: Context] ~ TRUSTED { data: Data _ NARROW[context.data]; context.data _ data; data.phase _ 0; data.devicePath _ ScanConverter.Allocate[]; data.canvas _ ImagerMasks.LFDisplay[]; data.transformation _ data.viewToDevice _ data.surfaceToDevice _ ImagerTransform.Concat[[1,0,0,0,1,0,hard], ImagerTransform.Create[0, -1, data.canvas.SSize, 1, 0, 0]]; data.clientClipperPresent _ data.compositeClipperValid _ FALSE; data.currentPosition _ [0, 0]; data.viewClipper _ ImagerMasks.Box[sMin: 0, fMin: 0, sSize: data.canvas.SSize, fSize: data.canvas.FSize]; [[data.sOriginView, data.fOriginView]] _ [[data.sOriginBrick, data.fOriginBrick]] _ ImagerTransform.IntTransform[[0,0], data.surfaceToDevice]; SetColor[context, Imager.black]; }; MakeTileSamples: PROC [xSize, ySize: NAT] RETURNS [tileSamples: ImagerBasic.SampledColor] ~ { tileSamples _ NEW[ImagerBasic.SampledColorRep]; tileSamples.transparent _ FALSE; tileSamples.pa _ NEW[ImagerBasic.PixelArrayRep]; tileSamples.m _ ImagerTransform.Rotate[0]; tileSamples.colorMap _ $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]; }; SetTileSamples: PROC [tileSamples: ImagerBasic.SampledColor, intensity: [0..255]] ~ { WITH tileSamples.pa.data SELECT FROM n: REF NAT => n^ _ intensity; ENDCASE => ERROR; }; ConstantGet: PROC [self: ImagerBasic.PixelArray, buffer: ImagerBasic.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; }; DoSaveAll: PROC [context: Context, action: PROC] ~ { data: Data _ NARROW[context.data]; cp: Pair _ data.currentPosition; DoSave[context, action]; data.currentPosition _ cp; }; DoSave: PROC [context: Context, action: PROC] ~ { data: Data _ NARROW[context.data]; color: Color _ data.currentColor; clipper: Clipper _ GetClipper[context]; transformation: Transformation _ data.transformation; sOriginView: INTEGER _ data.sOriginView; fOriginView: INTEGER _ data.fOriginView; sOriginBrick: INTEGER _ data.sOriginBrick; fOriginBrick: INTEGER _ data.fOriginBrick; data.doSaveCount _ data.doSaveCount + 1; action[]; data.doSaveCount _ data.doSaveCount - 1; data.sOriginView _ sOriginView; data.fOriginView _ fOriginView; data.sOriginBrick _ sOriginBrick; data.fOriginBrick _ fOriginBrick; data.transformation _ transformation; context.SetClipper[clipper]; IF data.currentColor # color THEN SetColor[context, color]; }; Flush: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; }; Close: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; }; TranslateT: PROC [context: Context, dx, dy: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreTranslate[dx, dy, data.transformation]; }; RotateT: PROC [context: Context, degrees: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreRotate[degrees, data.transformation]; }; ScaleT: PROC [context: Context, sx, sy: REAL] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.PreScale[sx, sy, data.transformation]; }; ConcatT: PROC [context: Context, transformation: Transformation] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.Concat[transformation, data.transformation]; }; IntTranslateT: PROC [context: Context, dx, dy: INTEGER] ~ {TranslateT[context, dx, dy]}; IntScaleT: PROC [context: Context, sx, sy: INTEGER] ~ { data: Data _ NARROW[context.data]; IF sx=1 AND sy=-1 THEN { data.transformation _ ImagerTransform.Concat[[1, 0, 0, 0, -1, 0, mirrorY], data.transformation] } ELSE data.transformation _ ImagerTransform.PreScale[sx, sy, data.transformation]; }; ClipperRep: TYPE ~ RECORD [ exclude: BOOLEAN, mask: Mask ]; GetClipper: PROC [context: Context] RETURNS [Clipper] ~ { data: Data _ NARROW[context.data]; IF data.clientClipperPresent THEN { clipper: REF ClipperRep _ 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 { clipperRef: REF ClipperRep _ NARROW[clipper]; data.clientClipperPresent _ TRUE; data.clientClipperExclude _ clipperRef.exclude; data.clientClipper _ clipperRef.mask.Shift[data.sOriginView, data.fOriginView]; ccCount _ ccCount + 1; }; data.compositeClipperValid _ FALSE; }; GetT: PROC [context: Context] RETURNS [Transformation] ~ { data: Data _ NARROW[context.data]; RETURN [ImagerTransform.Concat[data.transformation, ImagerTransform.Invert[data.viewToDevice]]] }; SetT: PROC [context: Context, transformation: Transformation] ~ { data: Data _ NARROW[context.data]; data.transformation _ ImagerTransform.Concat[transformation, data.viewToDevice]; }; MaskFromPath: PROC [data: Data, outline: Path] RETURNS [mask: Mask] ~ { GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; Xmove: PROC [p: Pair] ~ {move[Xform[p]]}; Xline: PROC [p: Pair] ~ {line[Xform[p]]}; Xcurve: PROC [p1, p2, p3: Pair] ~ {curve[Xform[p1], Xform[p2], Xform[p3]]}; outline.generateProc[outline, Xmove, Xline, Xcurve]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter]; }; data.devicePath.Reset; data.devicePath.PushPath[GenPath]; mask _ ImagerMasks.Create[Runs]; data.devicePath.Reset; }; MaskFromRectangle: PROC [data: Data, area: Rectangle] RETURNS [mask: Mask] ~ { GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = { m: ImagerBasic.Transformation _ data.transformation; Xform: PROC [p: Pair] RETURNS [Pair] ~ {RETURN[[ m.d * p.x + m.e * p.y + m.f, m.a * p.x + m.b * p.y + m.c ]]}; move[Xform[[area.x, area.y]]]; line[Xform[[area.x + area.w, area.y]]]; line[Xform[[area.x + area.w, area.y + area.h]]]; line[Xform[[area.x, area.y + area.h]]]; }; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = { IF h # 1 THEN ERROR; run[s: y, fMin: x, fSize: w]; }; data.devicePath.ScanConvert[proc: BoxFromScanConverter]; }; IF data.transformation.type # hard THEN { p1: Pair _ ImagerTransform.Transform[[area.x, area.y], data.transformation]; p2: Pair _ ImagerTransform.Transform[[area.x+area.w, area.y+area.h], data.transformation]; 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.Reset; data.devicePath.PushPath[GenPath]; mask _ ImagerMasks.Create[Runs]; data.devicePath.Reset; }; ScaledFromReal: PROC [real: REAL] RETURNS [Scaled.Value] ~ TRUSTED { i: INT _ Real.RoundLI[real * 65536.0]; RETURN[LOOPHOLE[i]] }; MaskFromPixelArray: PROC [data: Data, pixelArray: ImagerBasic.PixelArray] RETURNS [Mask] ~ { destArea: Mask _ MaskFromRectangle[data, [0, 0, pixelArray.xPixels, pixelArray.yPixels]]; Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { buffer: ImagerBasic.PixelBuffer _ NEW[ImagerBasic.PixelBufferRep[destArea.FSize+1]]; DoRun: PROC [s, fMin: INTEGER, fSize: CARDINAL] ~ { nextPixel: ImagerBasic.Pair _ ImagerTransform.InverseTransformVec[[0, 1], data.transformation]; start: ImagerBasic.Pair _ ImagerTransform.InverseTransform[[0.5+s, 0.5+fMin], data.transformation]; 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[s, 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] }; ccCount: INT _ 0; ClipMask: PROC [data: Data, mask: Mask, exclude: BOOLEAN] ~ { 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; }; }; }; ClipPath: PROC [context: Context, outline: Path, exclude: BOOLEAN _ FALSE] ~ { data: Data _ NARROW[context.data]; ClipMask[data, MaskFromPath[data, outline], exclude]; }; ClipRectangle: PROC [context: Context, outline: Rectangle, exclude: BOOLEAN _ FALSE] ~ { data: Data _ NARROW[context.data]; ClipMask[data, MaskFromRectangle[data, outline], exclude]; }; TestRectangle: PROC [context: Context, area: Rectangle] RETURNS [Visibility] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, area]; RETURN [mask.IsVisible[CompositeClipper[data]]]; }; ClipIntRectangle: PROC [context: Context, outline: IntRectangle, exclude: BOOLEAN _ FALSE] ~ { ClipRectangle[context, [outline.x, outline.y, outline.w, outline.h], exclude]; }; TestIntRectangle: PROC [context: Context, area: IntRectangle] RETURNS [Visibility] ~ { RETURN [TestRectangle[context, [area.x, area.y, area.w, area.h]]]; }; DoWithoutClipping: PROC [context: Context, bounds: IntRectangle, callBack: PROC[Context]] ~ { callBack[context]; }; GetColor: PROC [context: Context] RETURNS [color: Color] ~ { data: Data _ NARROW[context.data]; color _ data.currentColor; }; SetColor: PROC [context: Context, color: Color] ~ { data: Data _ NARROW[context.data]; IF color = data.currentColor THEN RETURN; data.currentColor _ color; IF color = Imager.black OR color = Imager.white THEN RETURN; WITH color SELECT FROM stipple: REF CARDINAL => TRUSTED { maskRep: REF ImagerMasks.MaskRep.bitmap _ NARROW[data.tile.refRep]; stippleRows: PACKED ARRAY [0..4) OF [0..16) _ LOOPHOLE[stipple^]; stippleRow: [0..16); dest: LONG POINTER TO ARRAY [0..16) OF PACKED ARRAY [0..4) OF [0..16) _ maskRep.pointer; IF maskRep.rast # 1 OR maskRep.lines # 16 THEN ERROR; stippleRow _ stippleRows[0]; dest[0] _ dest[4] _ dest[8] _ dest[12] _ [stippleRow, stippleRow, stippleRow, stippleRow]; stippleRow _ stippleRows[1]; dest[1] _ dest[5] _ dest[9] _ dest[13] _ [stippleRow, stippleRow, stippleRow, stippleRow]; stippleRow _ stippleRows[2]; dest[2] _ dest[6] _ dest[10] _ dest[14] _ [stippleRow, stippleRow, stippleRow, stippleRow]; stippleRow _ stippleRows[3]; dest[3] _ dest[7] _ dest[11] _ dest[15] _ [stippleRow, stippleRow, stippleRow, stippleRow]; }; constantColor: ImagerBasic.CIEColor => { intensity: INT _ constantColor.Y; intensity _ (intensity + 127)/(LAST[CARDINAL]/255); SetTileSamples[data.tileSamples, intensity]; ImagerHalftone.Halftone[ dest: data.tile, mask: ImagerMasks.Box[0, 0, 16, 16], source: data.tileSamples.pa, transformation: ImagerTransform.Rotate[0], deviceBrick: LFBrick ]; }; sampledColor: ImagerBasic.SampledColor => NULL; special: ATOM => { SELECT special FROM $XOR => NULL; ENDCASE => data.currentColor _ Imager.black; }; ENDCASE => data.currentColor _ Imager.black; }; MaskStroke: PROC [context: Context, path: Path, width: REAL, strokeEnds: StrokeEnds, closed: BOOLEAN] ~ { data: Data _ NARROW[context.data]; clipper: ImagerMasks.Mask _ CompositeClipper[data]; mask: ImagerMasks.Mask _ ImagerStroke.MaskFromStroke[path, data.transformation, width, strokeEnds, closed, clipper.sMin, clipper.sMin+clipper.sSize]; ApplyMask[data, mask]; }; ApplyMask: PROC [data: Data, mask: Mask] ~ { clippedMask: Mask _ mask.And[CompositeClipper[data]]; IF clippedMask.sSize = 0 THEN RETURN; SELECT data.currentColor FROM Imager.black => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase]; Imager.white => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase, [and, complement]]; $XOR => data.canvas.MaskConstant[clippedMask, ImagerMasks.inkTile, data.phase, [xor, null]]; ENDCASE => WITH data.currentColor SELECT FROM constantColor: ImagerBasic.CIEColor => { data.canvas.MaskConstant[clippedMask, data.tile, data.phase]; }; stipple: REF CARDINAL => { data.canvas.MaskConstant[clippedMask, data.tile, data.phase]; }; sampledColor: ImagerBasic.SampledColor => { transform: Transformation _ ImagerTransform.Concat[sampledColor.m, data.viewToDevice]; ImagerHalftone.Halftone[data.canvas, clippedMask, sampledColor.pa, transform, LFBrick]; }; ENDCASE => ERROR; }; MaskFill: PROC [context: Context, path: Path] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPath[data, path]; ApplyMask[data, mask]; }; MaskPixel: PROC [context: Context, pixelArray: ImagerBasic.PixelArray] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromPixelArray[data, pixelArray]; ApplyMask[data, mask]; }; scratchPixelArray: ImagerBasic.PixelArray _ NEW[ImagerBasic.PixelArrayRep]; scratchBitmapDesc: REF ImagerMasks.MaskRep.bitmap _ NEW[ImagerMasks.MaskRep.bitmap]; MaskBits: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, tile: IntRectangle, area: IntRectangle] ~ { ENABLE UNWIND => NULL; data: Data _ NARROW[context.data]; color: Color _ data.currentColor; IF data.transformation.type = rot90 AND tile.w = 16 AND raster = 1 AND tile.h <= 16 AND (color = Imager.black OR color = $XOR) THEN { sMin, fMin, sSize, fSize: INTEGER; sMinTile, fMinTile, sSizeTile, fSizeTile: INTEGER; [[sMin, fMin, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[area, data.transformation]; [[sMinTile, fMinTile, sSizeTile, fSizeTile]] _ ImagerTransform.TransformIntRectangle[tile, data.transformation]; scratchBitmapDesc.pointer _ base; scratchBitmapDesc.rast _ raster; scratchBitmapDesc.lines _ sSizeTile; ImagerMasks.MaskConstant[data.canvas, CompositeClipper[data].And[[sMin, fMin, sSize, fSize, NIL]], [sMinTile, fMinTile, sSizeTile, fSizeTile, scratchBitmapDesc], 0, IF color = Imager.black THEN [or, null] ELSE [xor, null]]; } ELSE { IF firstBadMaskBits THEN {firstBadMaskBits _ FALSE; ERROR} -- sorry, not yet. }; }; firstBadMaskBits: BOOLEAN _ TRUE; MaskSegment: PROC [context: Context, p: Pair] ~ { }; MaskIntSegment: PROC [context: Context, p: IntPair] ~ { }; MaskThinStroke: PROC [context: Context, path: Path] ~ { }; MaskRectangle: PROC [context: Context, area: Rectangle] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, area]; ApplyMask[data, mask]; }; MaskIntRectangle: PROC [context: Context, area: IntRectangle] ~ { data: Data _ NARROW[context.data]; mask: Mask _ MaskFromRectangle[data, [area.x, area.y, area.w, area.h]]; ApplyMask[data, mask]; }; SetCP: PROC [context: Context, cp: Pair] ~ { data: Data _ NARROW[context.data]; data.currentPosition _ ImagerTransform.Transform[cp, data.transformation]; }; GetCP: PROC [context: Context] RETURNS [cp: Pair] ~ { data: Data _ NARROW[context.data]; cp _ ImagerTransform.InverseTransform[data.currentPosition, data.transformation]; }; SetIntCP: PROC [context: Context, cp: IntPair] ~ { SetCP[context, [cp.x, cp.y]]; }; GetIntCP: PROC [context: Context] RETURNS [cp: IntPair] ~ { realCP: Pair _ GetCP[context]; cp.x _ Real.RoundLI[realCP.x]; cp.y _ Real.RoundLI[realCP.y]; }; rasterToRunGroupStorageRatio: INT _ 1; 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[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ { Run: PROC [s, fMin: INTEGER, fSize: NAT] ~ { run[s: s, fMin: fMin, fSize: fSize]; nRuns _ nRuns + 1; }; font.fontGraphicsClass.maskProc[font, transformation, char, Run]; }; nRuns _ 0; ans.mask _ ImagerMasks.Create[Runs]; ans.nRuns _ nRuns; }; FontCompositeTransform: PROC [data: Data, font: UnifiedFonts.FONT] RETURNS [t: Transformation] ~ { t _ data.transformation; t.c _ t.f _ 0; t _ ImagerTransform.Concat[font.actualTransformation, t]; }; fontCache: ImagerFontCache.FontCache _ ImagerFontCache.Create[]; MaskChars: PROC [context: Context, font: UnifiedFonts.FONT, map: PROC[PROC[CHAR]]] ~ { data: Data _ NARROW[context.data]; transform: Transformation _ FontCompositeTransform[data, font]; fontCode: ImagerFontCache.FontCode _ ImagerFontCache.GetFontCode[transform.a, transform.b, transform.c, transform.d, transform.e, transform.f, font.graphicsKey]; clipper: Mask _ CompositeClipper[data]; sCurrent: Scaled.Value _ ScaledFromReal[data.currentPosition.x]; fCurrent: Scaled.Value _ ScaledFromReal[data.currentPosition.y]; Load: PROC [char: CHAR] = { loadInfo: REF CharLoadInfo _ NEW[CharLoadInfo]; maskN: MaskAndRunCount _ GetCharMask[font, transform, char]; runGroupSize: INT _ maskN.nRuns * 2 + 2; rasterSize: INT _ maskN.mask.SSize * INT[(maskN.mask.FSize+15)/16] + 2; width: Pair _ ImagerTransform.TransformVec[font.fontGraphicsClass.widthVectorProc[font, char], data.transformation]; loadInfo.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]; fontCache.LoadCharData[fontCode, char, loadInfo]; }; DoChar: PROC [char: CHAR] = { sCP: INTEGER _ sCurrent.Round; fCP: INTEGER _ fCurrent.Round; loadInfo: REF CharLoadInfo _ LoadInfo[]; LoadInfo: PROC RETURNS [cli: REF CharLoadInfo] ~ INLINE { cli _ NARROW[ fontCache.GetCharData[fontCode, char ! ImagerFontCache.CacheMiss => {Load[char]; RETRY} ] ]; }; ApplyMask[data, loadInfo.mask.Shift[sCP, fCP]]; sCurrent _ sCurrent.PLUS[loadInfo.sWidth]; fCurrent _ fCurrent.PLUS[loadInfo.fWidth]; }; map[DoChar]; data.currentPosition _ [x: sCurrent.Float, y: fCurrent.Float]; }; MaskChar: PROC [context: Context, font: UnifiedFonts.FONT, char: CHAR] ~ { MapOneChar: PROC [Char: PROC[CHAR]] = {Char[char]}; MaskChars[context, font, MapOneChar]; }; MaskCharacters: PROC [ context: Context, font: UnifiedFonts.FONT, characters: REF, -- may be a Rope.ROPE or a REF TEXT start: INT _ 0, length: INT _ LAST[INT] ] ~ { WITH characters SELECT FROM rope: ROPE => { Map: PROC [Char: PROC[CHAR]] = { Action: PROC [c: CHAR] RETURNS [quit: BOOL _ FALSE] = {Char[c]}; [] _ rope.Map[start: start, len: length, action: Action]; }; MaskChars[context, font, Map] }; text: REF TEXT => { size: INT _ text.length; rem: INT _ RopeInline.NonNeg[size - RopeInline.NonNeg[start]]; Map: PROC [Char: PROC[CHAR]] = { FOR i: INT IN [start..start+length) DO Char[text[i]]; ENDLOOP; }; IF length > rem THEN length _ rem; MaskChars[context, font, Map]; }; ENDCASE => IF characters # NIL THEN ERROR; }; TransferBuffer: PROC [context, source: Context] ~ { data: Data _ NARROW[context.data]; WITH source.data SELECT FROM sourceData: Data => { ImagerMasks.MaskSampled[data.canvas, CompositeClipper[data], sourceData.canvas.InlineShift[data.sOriginView-sourceData.sOriginView, data.fOriginView-sourceData.fOriginView]]; }; ENDCASE => ERROR Imager.IncompatibleContexts; }; SetColorInvert: PROC [context: Context] ~ { data: Data _ NARROW[context.data]; data.currentColor _ $XOR; }; DrawBitmap: ENTRY PROC [context: Context, base: LONG POINTER, raster: CARDINAL, area: IntRectangle] ~ { ENABLE UNWIND => NULL; data: Data _ NARROW[context.data]; IF data.transformation.type = rot90 THEN { sMin, fMin, sSize, fSize: INTEGER; [[sMin, fMin, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[area, data.transformation]; scratchBitmapDesc.pointer _ base; scratchBitmapDesc.rast _ raster; scratchBitmapDesc.lines _ sSize; data.canvas.MaskSampled[CompositeClipper[data], [sMin, fMin, sSize, fSize, scratchBitmapDesc]]; } ELSE { IF firstBadDrawBitmap THEN {firstBadDrawBitmap _ FALSE; ERROR} }; }; firstBadDrawBitmap: BOOLEAN _ TRUE; SpecialOp: PROC [context: Context, op: ATOM, data: REF] RETURNS [implemented: BOOLEAN] ~ { clipperData: Data _ NARROW[context.data]; implemented _ TRUE; SELECT op FROM $OtherScreen => { swapped: BOOLEAN; TRUSTED {swapped _ TerminalMultiplex.SelectTerminal[swap]}; TRUSTED {IF UserTerminal.GetState[] = disconnected THEN [] _ UserTerminal.SetState[on]}; clipperData.canvas _ ImagerMasks.LFDisplay[]; TRUSTED {swapped _ TerminalMultiplex.SelectTerminal[swap]}; implemented _ swapped }; ENDCASE => implemented _ FALSE; }; MaskFromSurfaceRectangle: PROC [data: Data, box: IntRectangle] RETURNS [Mask] ~ { deviceBox: IntRectangle _ ImagerTransform.TransformIntRectangle[box, data.surfaceToDevice]; 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[data, box]; deviceOrigin: IntPair _ ImagerTransform.IntTransform[[box.x, box.y], data.surfaceToDevice]; sOriginView: INTEGER _ deviceOrigin.x; fOriginView: INTEGER _ deviceOrigin.y; sShift: INTEGER _ sOriginView-data.sOriginView; fShift: INTEGER _ fOriginView-data.fOriginView; newHalftoneOrigin: IntPair _ ImagerTransform.IntTransform[halftoneOrigin, data.surfaceToDevice]; 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.currentPosition.x _ data.currentPosition.x + sShift; data.currentPosition.y _ data.currentPosition.y + fShift; data.transformation.c _ data.transformation.c + sShift; data.transformation.f _ data.transformation.f + fShift; data.viewToDevice.c _ data.viewToDevice.c + sShift; data.viewToDevice.f _ data.viewToDevice.f + fShift; 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[data, box]; data.compositeClipperValid _ FALSE; data.compositeClipper.refRep _ NIL; data.viewClipper _ IF exclude THEN data.viewClipper.Difference[mask] ELSE data.viewClipper.And[mask]; }; GetSurfaceBounds: PROC [context: Context] RETURNS [IntRectangle] ~ { data: Data _ NARROW[context.data]; deviceToSurface: Transformation _ ImagerTransform.Invert[data.surfaceToDevice]; 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[data.surfaceToDevice]; RETURN [ImagerTransform.TransformIntRectangle[[data.viewClipper.sMin, data.viewClipper.fMin, data.viewClipper.sSize, data.viewClipper.fSize], deviceToSurface]] }; MoveSurfaceRectangle: PROC [context: Context, source: IntRectangle, dest: IntPair] RETURNS [BOOLEAN] ~ { data: Data _ NARROW[context.data]; bitmap: REF ImagerMasks.MaskRep.bitmap; sSizeCanvas, fSizeCanvas: NAT; sTranslate, fTranslate: INTEGER; sMinSource, fMinSource, sMinDest, fMinDest: INTEGER; sSize, fSize: INTEGER; WITH data.canvas.refRep SELECT FROM b: REF ImagerMasks.MaskRep.bitmap => bitmap _ b; ENDCASE => ERROR; sSizeCanvas _ MIN[data.canvas.sSize, bitmap.lines]; fSizeCanvas _ MIN[data.canvas.fSize, bitmap.rast*Environment.bitsPerWord]; [[sTranslate, fTranslate]] _ ImagerTransform.TransformIntVec[[dest.x-source.x, dest.y-source.y], data.surfaceToDevice]; [[sMinSource, fMinSource, sSize, fSize]] _ ImagerTransform.TransformIntRectangle[source, data.surfaceToDevice]; sMinSource _ sMinSource - data.canvas.sMin; fMinSource _ fMinSource - data.canvas.fMin; sMinDest _ sMinSource + sTranslate; fMinDest _ fMinSource + fTranslate; IF sMinDest < 0 THEN { d: INTEGER _ - sMinDest; sSize _ sSize - d; sMinSource _ sMinSource + d; sMinDest _ sMinDest + d; }; IF sMinSource < 0 THEN { d: INTEGER _ - sMinSource; sSize _ sSize - d; sMinSource _ sMinSource + d; sMinDest _ sMinDest + d; }; IF fMinDest < 0 THEN { d: INTEGER _ - fMinDest; fSize _ fSize - d; fMinSource _ fMinSource + d; fMinDest _ fMinDest + d; }; IF fMinSource < 0 THEN { d: INTEGER _ - fMinSource; fSize _ fSize - d; fMinSource _ fMinSource + d; fMinDest _ fMinDest + d; }; IF sMinDest + sSize > sSizeCanvas THEN { sSize _ sSizeCanvas - sMinDest }; IF sMinSource + sSize > sSizeCanvas THEN { sSize _ sSizeCanvas - sMinSource }; IF fMinDest + fSize > fSizeCanvas THEN { fSize _ fSizeCanvas - fMinDest }; IF fMinSource + fSize > data.canvas.fMin + fSizeCanvas THEN { fSize _ fSizeCanvas - fMinSource }; IF sSize > 0 AND fSize > 0 THEN TRUSTED { Blt[bitmap.pointer, bitmap.rast, sMinSource, fMinSource, sMinDest, fMinDest, sSize, fSize] }; RETURN [TRUE]; }; Blt: UNSAFE PROC [dbase: LONG POINTER, drast, sMinSource, fMinSource, sMinDest, fMinDest, sSize, fSize: NAT] ~ UNCHECKED INLINE { bpl: INTEGER _ drast * Environment.bitsPerWord; bbspace: BitBlt.BBTableSpace; bb: BitBlt.BitBltTablePtr _ BitBlt.AlignedBBTable[@bbspace]; bb^ _ [dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]], width: 0, height: 0, flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]]; IF (fMinSource+fSize)>fMinDest AND (fMinDest+fSize)>fMinSource AND (sMinSource+sSize)>sMinDest AND (sMinDest+sSize)>sMinSource THEN { bb.flags.disjoint _ FALSE; -- the rectangles overlap IF sMinDest=sMinSource THEN bb.flags.disjointItems _ FALSE; -- so do the items IF sMinDest>sMinSource OR (sMinDest=sMinSource AND fMinDest>fMinSource) THEN { -- reverse direction bb.flags.direction _ backward; bpl _ -bpl; sMinSource _ sMinSource + (sSize-1); sMinDest _ sMinDest + (sSize-1); }; }; bb.dst.word _ dbase + Inline.LongMult[sMinDest, drast] + fMinDest/16; bb.dst.bit _ fMinDest MOD 16; bb.src.word _ dbase + Inline.LongMult[sMinSource, drast] + fMinSource/16; bb.src.bit _ fMinSource MOD 16; bb.dstBpl _ bb.srcDesc.srcBpl _ bpl; bb.width _ fSize; bb.height _ sSize; BitBlt.BITBLT[bb]; }; LFDisplayClass: REF Imager.ClassRep _ NEW [Imager.ClassRep _ [ deviceType: $LFDisplay, Init: Init, DoSaveAll: DoSaveAll, DoSave: DoSave, Flush: Flush, Close: Close, TranslateT: TranslateT, RotateT: RotateT, ScaleT: ScaleT, ConcatT: ConcatT, GetT: GetT, SetT: SetT, IntTranslateT: IntTranslateT, IntScaleT: IntScaleT, GetClipper: GetClipper, SetClipper: SetClipper, ClipPath: ClipPath, ClipRectangle: ClipRectangle, TestRectangle: TestRectangle, ClipIntRectangle: ClipIntRectangle, TestIntRectangle: TestIntRectangle, DoWithoutClipping: DoWithoutClipping, SetColor: SetColor, GetColor: GetColor, MaskStroke: MaskStroke, MaskFill: MaskFill, MaskPixel: MaskPixel, MaskBits: MaskBits, MaskSegment: MaskSegment, MaskIntSegment: MaskIntSegment, MaskThinStroke: MaskThinStroke, MaskRectangle: MaskRectangle, MaskIntRectangle: MaskIntRectangle, SetCP: SetCP, GetCP: GetCP, SetIntCP: SetIntCP, GetIntCP: GetIntCP, MaskChar: MaskChar, MaskCharacters: MaskCharacters, TransferBuffer: TransferBuffer, Reset: Reset, SetView: SetView, ClipView: ClipView, GetSurfaceBounds: GetSurfaceBounds, GetViewBounds: GetViewBounds, MoveSurfaceRectangle: MoveSurfaceRectangle, SetColorInvert: SetColorInvert, DrawBitmap: DrawBitmap, SpecialOp: SpecialOp ]]; ImagerRegistration.RegisterDevice[LFDisplayClass]; END. PImagerLFImpl.mesa Michael Plass, August 4, 1983 11:55 am in device space Transforms (x, y) to (f, s)  note f comes first for ScanConvert! Transforms (x, y) to (f, s)  note f comes first for ScanConvert! Because of an apparent bug in Real.FScale. Not implemented yet Not implemented yet Not implemented yet Sorry, not yet. Ê!j˜Jšœ™J™&J˜JšÏk œñ˜úJ˜šœœ˜JšœÈ˜ÏJšœ ˜—Jšœ˜JšœœUœ˜kšÏnœœœUœ˜pJšœ=˜=Jšœ7˜7Jšœ7˜7Jšœ=˜=Jšœ:˜:Jšœ7˜7Jšœ˜—Jšœœœ˜Jšœœœ˜Jšœœ˜Jšœ œ˜#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šœY˜Yšžœœœœ˜KJšœ œ ˜Jšœ˜Jšœ0˜0Jšœ+˜+Jšœ˜Jšœ+˜+Jšœ&˜&Jšœ§˜§Jšœ9œ˜?Jšœ˜Jšœi˜iJšœŽ˜ŽJšœ ˜ šœœ˜$Jšœœ˜"Jšœb˜bJšœX˜XJšœ˜—Jšœ˜—šžœœœ˜*Jšœ œ˜"Jšœ˜Jšœ˜Jšœ+˜+Jšœ&˜&Jšœ§˜§Jšœ9œ˜?Jšœ˜Jšœi˜iJšœŽ˜ŽJšœ ˜ Jšœ˜—šžœœœœ,˜]Jšœœ˜/Jšœœ˜ Jšœœ˜0Jšœ*˜*Jšœ&˜&Jšœ˜Jšœ˜Jšœ$˜$Jšœ#˜#Jšœ!˜!Jšœœœ˜#Jšœ˜—šžœœA˜Ušœœ˜$Jšœœœ˜Jšœœ˜—Jšœ˜—šž œœKœ œA˜¬šœ œ˜šœœœ˜Jšœœ˜Jš œœœœœ˜9Jšœ˜—Jšœœ˜—Jšœ˜—šž œœœ˜4Jšœ œ˜"Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜—šžœœœ˜1Jšœ œ˜"Jšœ!˜!Jšœ'˜'Jšœ5˜5Jšœ œ˜(Jšœ œ˜(Jšœœ˜*Jšœœ˜*Jšœ(˜(Jšœ ˜ Jšœ(˜(Jšœ˜Jšœ˜Jšœ!˜!Jšœ!˜!Jšœ%˜%Jšœ˜Jšœœ˜;Jšœ˜—šžœœ˜"Jšœ œ˜"J˜Jšœ˜—šžœœ˜"Jšœ œ˜"Jšœ˜—šž œœœ˜5Jšœ œ˜"JšœP˜PJšœ˜—šžœœœ˜3Jšœ œ˜"JšœN˜NJšœ˜—šžœœœ˜1Jšœ œ˜"JšœL˜LJšœ˜—šžœœ7˜DJšœ œ˜"JšœR˜RJšœ˜—Jšž œœœ"˜Xšž œœœ˜7Jšœ œ˜"šœœœ˜Jšœ_˜_Jšœ˜—JšœM˜QJšœ˜—šœ œœ˜Jšœ œ˜J˜ Jšœ˜—šž œœœ˜9Jšœ œ˜"šœœ˜#Jšœ œœ ˜*Jšœ,˜,JšœT˜TJ˜Jšœ ˜Jšœ˜—Jšœœœ˜Jšœ˜—šž œœ)˜9Jšœ œ˜"šœ œœ˜Jšœœ˜"Jšœœ˜ J˜Jšœ˜—šœ˜Jšœ œœ ˜-Jšœœ˜!Jšœ/˜/JšœO˜OJ˜Jšœ˜—Jšœœ˜#Jšœ˜—šžœœœ˜:Jšœ œ˜"JšœY˜_Jšœ˜—šžœœ7˜AJšœ œ˜"JšœP˜PJšœ˜—šž œœœ˜Gš žœœœœœ˜UJšœ4˜4šžœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—Jšžœœ˜)Jšžœœ˜)Jšžœœ>˜KJšœ4˜4Jšœ˜—šžœœœ œ œ œœ˜ašžœœœ˜4Jšœœœ˜Jšœ˜Jšœ˜—Jšœ8˜8Jšœ˜—Jšœ˜Jšœ"˜"Jšœ ˜ Jšœ˜Jšœ˜—šžœœœ˜Nš žœœœœœ˜UJšœ4˜4šžœœ œ œ˜0JšœA™AJšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ'˜'Jšœ0˜0Jšœ'˜'Jšœ˜—šžœœœ œ œ œœ˜ašžœœœ˜4Jšœœœ˜Jšœ˜Jšœ˜—Jšœ8˜8Jšœ˜—šœ!œ˜)JšœL˜LJšœZ˜ZJš œœœœœœ˜EJš œœœœœœ˜DJš œœœœœœ˜EJš œœœœœœ˜DJšœU˜[Jšœ˜—Jšœ˜Jšœ"˜"Jšœ ˜ Jšœ˜Jšœ˜—š žœœœœœ˜DJ™*Jšœœ ˜&Jšœœ˜Jšœ˜—šžœœ2œ ˜\JšœY˜Yšžœœœ œ œ œœ˜aJšœ"œ/˜Tšžœœ œ œ˜3Jšœ_˜_Jšœc˜cJšœ/˜/Jšœ/˜/Jšœ8˜8Jšœ8˜8Jšœ œ˜Jšœœ˜JšœW˜Wšœ˜Jšœ˜Jšœœœ˜2Jšœœ(˜?Jšœ˜Jšœœœ˜2Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—Jšœ˜"Jšœ˜—šžœœœ ˜6šœœœ˜(šœœ˜#JšœœH˜iJšœB˜FJšœ˜—Jšœ*˜.Jšœœ˜"Jšœ˜—Jšœ˜Jšœ˜—Jšœ œ˜šžœœ#œ˜=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šœ˜—šžœœ,œœ˜NJšœ œ˜"Jšœ5˜5J˜—šž œœ1œœ˜XJšœ œ˜"Jšœ:˜:Jšœ˜—šž œœ%œ˜PJšœ œ˜"J˜+Jšœ*˜0Jšœ˜—šžœœ4œœ˜^JšœN˜NJšœ˜—šžœœ(œ˜VJšœ<˜BJšœ˜—šžœœ4œ˜]Jšœ˜Jšœ˜—šžœœœ˜˜>Jšœ˜—šžœœ'œœ˜JJšž œœœœ˜3Jšœ%˜%Jšœ˜—šžœœ˜Jšœ˜Jšœœ˜Jšœ œŸ#˜4Jšœœ˜Jšœœœœ˜šœ˜šœ œ˜šœœ˜š žœœžœœœ˜ Jš žœœœœœœ˜@Jšœ9˜9J˜—Jšœ˜Jšœ˜—šœœœ˜Jšœœ˜Jšœœ6˜>š žœœžœœœ˜ šœœœ˜&Jšœ˜Jšœ˜—Jšœ˜—Jšœœ˜"Jšœ˜Jšœ˜—Jš œœœœœ˜*—Jšœ˜——šžœœ˜3Jšœ œ˜"šœ œ˜šœ˜Jšœ®˜®Jšœ˜—Jšœœ˜-—Jšœ˜—šžœœ˜+Jšœ œ˜"Jšœ˜Jšœ˜—š ž œœœœœ œ˜gJšœœœ˜Jšœ œ˜"šœ"œ˜*Jšœœ˜"Jšœ`˜`Jšœ!˜!Jšœ ˜ Jšœ ˜ Jšœ_˜_Jšœ˜—šœ˜šœœœœ˜>J™—Jšœ˜—Jšœ˜—Jšœœœ˜#š ž œœœœœœ˜ZJšœœ˜)Jšœœ˜šœ˜šœ˜Jšœ œ˜Jšœ4˜;Jšœœ(œ!˜XJšœ-˜-Jšœ4˜;Jšœ˜Jšœ˜—Jšœœ˜—Jšœ˜—šžœœ!œ ˜QJšœ[˜[JšœM˜SJšœ˜—šžœœL˜YJšœ œ˜"Jšœ1˜1Jšœ[˜[Jšœ œ˜&Jšœ œ˜&Jšœœ ˜/Jšœœ ˜/Jšœ`˜`JšœœœŸ/˜SJš œœ œ œ<œœ˜„Jšœœ˜#Jšœœ˜#šœœ˜#Jšœ>˜>J˜J˜—Jšœ˜Jšœ9˜9Jšœ9˜9Jšœ7˜7Jšœ7˜7Jšœ3˜3Jšœ3˜3Jšœ˜Jšœ˜Jšœ;˜;Jšœ˜—šžœœ0œ˜JJšœ œ˜"Jšœ1˜1Jšœœ˜#Jšœœ˜#šœ˜Jšœ œ"˜1Jšœ˜ —Jšœ˜—šžœœœ˜DJšœ œ˜"JšœO˜OJšœ…˜‹Jšœ˜—šž œœœ˜AJšœ œ˜"JšœO˜OJšœ™˜ŸJšœ˜—šžœœ9œœ˜hJšœ œ˜"Jšœœ˜'Jšœœ˜Jšœœ˜ Jšœ,œ˜4Jšœœ˜šœœ˜#Jšœœ*˜0Jšœœ˜—Jšœœ"˜3Jšœœ9˜JJšœw˜wJšœo˜oJšœ+˜+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šœ˜—šœ œ˜(Jšœ˜Jšœ˜—šœ5œ˜=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šœ˜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šœ˜—J˜Jšœ2˜2J˜Jšœ˜—…—€ˆ£B