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.
���P��ImagerLFImpl.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šœ
œ˜"Jšœ˜Jšœ˜—šžœœ%˜3Jšœ
œ˜"Jšœœœ˜)Jšœ˜Jšœœœœ˜<šœ˜šœ	œœœ˜"Jšœ	œœ˜CJš	œ
œœœœ˜AJšœ˜Jšœœœœœ	œœœœ˜XJšœœœœ˜5Jšœ˜JšœZ˜ZJšœ˜JšœZ˜ZJšœ˜Jšœ[˜[Jšœ˜Jšœ[˜[Jšœ˜—šœ(˜(Jšœœ˜!Jšœœœ˜3Jšœ,˜,šœ˜Jšœ˜Jšœ$˜$Jšœ˜Jšœ*˜*Jšœ˜Jšœ˜—Jšœ˜—Jšœ*œ˜/šœ	œ˜šœ	˜Jšœœ˜
Jšœ%˜,—Jšœ˜—Jšœ%˜,—Jšœ˜—šž
œœ'œ"œ˜iJšœ
œ˜"Jšœ3˜3Jšœ•˜•Jšœ˜Jšœ˜—šž	œœ˜,Jšœ5˜5Jšœœœ˜%šœ˜JšœW˜WJšœj˜jJšœ\˜\šœ˜šœœ˜"šœ(˜(Jšœ=˜=Jšœ˜—šœ	œœ˜Jšœ=˜=Jšœ˜—šœ+˜+JšœV˜VJšœW˜WJšœ˜—Jšœœ˜———Jšœ˜—šžœœ#˜1Jšœ
œ˜"Jšœ&˜&Jšœ˜Jšœ˜—šž	œœ:˜JJšœ
œ˜"Jšœ2˜2Jšœ˜Jšœ˜—Jšœ,œ˜KJšœœœ˜Tšžœœœœœ
œ-˜yJšœœœ˜Jšœ
œ˜"J˜!šœ"œ
œœœœœ˜…Jšœœ˜"Jšœ*œ˜2Jšœ`˜`Jšœp˜pJšœ!˜!Jšœ ˜ Jšœ$˜$Jš	œ\œFœœœ˜ßJšœ˜—šœ˜Jšœœœœ˜:JšŸ˜Jšœ˜—Jšœ˜—Jšœœœ˜!šžœœ ˜1Jšœ™Jšœ˜—šžœœ#˜7Jšœ™Jšœ˜—šžœœ#˜7Jšœ™Jšœ˜—šž
œœ'˜;Jšœ
œ˜"Jšœ+˜+Jšœ˜Jšœ˜—šžœœ*˜AJšœ
œ˜"JšœG˜GJšœ˜Jšœ˜—šžœœ!˜,Jšœ
œ˜"JšœJ˜JJšœ˜—šžœœœ˜5Jšœ
œ˜"JšœQ˜QJšœ˜—šžœœ$˜2Jšœ˜Jšœ˜—šžœœœ˜;Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœœ˜&Jšœœœ9œ˜Yšœœœ˜ J˜Jšœ˜
Jšœ˜—š
žœœœ(œœ˜mJšœœ˜šžœœœ
œ	œœœ˜ašžœœœ	œ˜,Jšœ$˜$Jšœ˜Jšœ˜—JšœA˜AJšœ˜—Jšœ
˜
Jšœ$˜$Jšœ˜Jšœ˜—šžœœ!œœ˜bJšœ˜J˜Jšœ9˜9Jšœ˜—Jšœ@˜@šž	œœ'œœœœ˜VJšœ
œ˜"Jšœ?˜?Jšœ¡˜¡Jšœ'˜'Jšœ@˜@Jšœ@˜@šžœœœ˜Jšœ
œœ˜/Jšœ<˜<Jšœœ˜(Jšœœœ˜GJšœt˜tJšœ˜šœœ˜Jšœœ4˜KJšœ.˜0Jšœ`˜`JšœN˜U—Jšœ+˜+Jšœ+˜+Jšœ1˜1Jšœ˜—šžœœœ˜Jšœœ˜Jšœœ˜Jšœ
œ˜(š
žœœœœœ˜9šœœ˜
šœ$˜$Jšœ,œ˜2Jšœ˜—J˜—Jšœ˜—Jšœ/˜/Jšœœ˜*Jšœœ˜*Jšœ˜—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šœœŸ˜4JšœœœŸ˜Nš	œœœœŸ˜cJšœ*˜*JšœE˜EJ˜—J˜—Jšœ\œ˜cJšœbœ˜iJšœ$˜$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˜�Jšœ2˜2J˜�Jšœ˜—�…—����€ˆ��£B��