DIRECTORY Font, Imager USING [Class, ClassRep, Context, ContextRep, Error, IntKey, RealKey], ImagerBasic USING [IntPair], ImagerColor USING [black, Color, ColorRep, MakeGray], ImagerDisplayPrivate, ImagerManhattan USING [BoundingBox, Copy, CreateFromBox, Destroy, Difference, Intersection, Polygon], ImagerMask USING [GenerateRuns, Mask], ImagerPath USING [Clipper, PathProc], ImagerPixelArray USING [PixelArray], ImagerPixelMap USING [DeviceRectangle, PixelMap], ImagerScanConverter USING [ConvertToManhattanPolygon, CreatePath, DevicePath], ImagerStroke USING [DevicePathFromStroke, StrokeStyle], ImagerTransformation USING [Concat, DRound, InverseTransform, PreMultiply, PreRotate, PreScale2, PreTranslate, Scale, SetTrans, Transform, Transformation, TransformationRep, TransformVec], ImagerUtils USING [ClipRectangleIViaClipRectangle, MaskUnderlineIViaMaskUnderline, MaskVectorIViaMaskStroke, MaskVectorViaMaskStroke, SpaceIViaSpace], Real USING [RoundI], RefText, Rope, Vector2 USING [Add, Div, InlineAdd, Length, Mul, Sub, VEC]; ImagerDisplayImpl: CEDAR PROGRAM IMPORTS Font, Imager, ImagerColor, ImagerManhattan, ImagerMask, ImagerDisplayPrivate, ImagerScanConverter, ImagerStroke, ImagerTransformation, ImagerUtils, Real, RefText, Rope, Vector2 ~ BEGIN Context: TYPE ~ Imager.Context; Display: TYPE ~ ImagerDisplayPrivate.Display; BYTE: TYPE ~ [0..377B]; CARD: TYPE ~ LONG CARDINAL; ROPE: TYPE ~ Rope.ROPE; VEC: TYPE ~ Vector2.VEC; IVEC: TYPE ~ ImagerBasic.IntPair; FONT: TYPE ~ Font.FONT; Transformation: TYPE ~ ImagerTransformation.Transformation; TransformationRep: TYPE ~ ImagerTransformation.TransformationRep; Color: TYPE ~ ImagerColor.Color; PixelArray: TYPE ~ ImagerPixelArray.PixelArray; PathProc: TYPE ~ ImagerPath.PathProc; Clipper: TYPE ~ ImagerPath.Clipper; PixelMap: TYPE ~ ImagerPixelMap.PixelMap; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; PersistentVariables: TYPE ~ RECORD[ cp: VEC _ [0, 0], -- current position correctMeasure: VEC _ [0, 0], -- target line measure for Correct correctMaskCount: INT _ 0, -- tally number of CorrectMask calls in pass 1 correctSum: VEC _ [0, 0], -- tally adjustable space from CorrectSpace calls in pass 1 correctMask: VEC _ [0, 0], -- amount of space added by each CorrectMask call in pass 2 correctSpace: VEC _ [0, 0] -- fraction of space added by each CorrectSpace call in pass 2 ]; NonPersistentVariables: TYPE ~ RECORD[ priorityImportant: INT _ 0, -- if nonzero, priority of masks is important mediumSize: VEC _ [0, 0], -- size of medium, in meters fieldMin: VEC _ [0, 0], -- minimum x and y of field, in meters fieldMax: VEC _ [0, 0], -- maximum x and y of field, in meters noImage: INT _ 0, -- if nonzero, don't image masks strokeWidth: REAL _ 0, -- stroke width strokeEnd: INT _ 0, -- stroke end treatment underlineStart: REAL _ 0, -- starting x position for underline amplifySpace: REAL _ 1.0, -- multiplies width of amplified characters correctPass: INT _ 0, -- which pass, during Correct correctShrink: REAL _ 0.5, -- allowable space shrink correctTolerance: VEC _ [0, 0] -- tolerable deviation from correctMeasure ]; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD[ p: PersistentVariables _ [], -- persistent variables np: NonPersistentVariables _ [], -- non-persistent variables, except T, font, color, clipper font: FONT _ NIL, -- current font ("showVec") color: Color _ NIL, -- current color clipper: Clipper _ NIL, -- current clipping outline clientToView: Transformation _ NIL, -- current client-to-view transformation ("T") clientToViewID: CARD _ 0, -- unique id for contents of clientToView transformation lastID: CARD _ 0, -- last value used for clientToViewID devicePath: ImagerScanConverter.DevicePath _ NIL, -- scratch storage for scan converter viewToDisplayValid: BOOL _ FALSE, viewClipperValid: BOOL _ FALSE, clientToDisplayValid: BOOL _ FALSE, clientClipperValid: BOOL _ FALSE, charToDisplayValid: BOOL _ FALSE, displayColorValid: BOOL _ FALSE, viewToDisplayEasy: BOOL _ FALSE, -- valid iff viewToDisplayValid clientToDisplayEasy: BOOL _ FALSE, -- valid iff clientToDisplayValid viewToDisplay: Transformation _ NIL, -- always valid clientToDisplay: Transformation _ NIL, -- valid iff clientToDisplayValid charToDisplay: Transformation _ NIL, -- valid iff charToDisplayValid viewOrigin: IVEC _ [0, 0], -- valid iff viewToDisplayValid AND viewToDisplayEasy clientOrigin: IVEC _ [0, 0], -- valid iff clientToDisplayValid AND clientToDisplayEasy viewClipMask: ManhattanPolygon _ NIL, -- display coords; always valid clientClipMask: ManhattanPolygon _ NIL, -- display coords; valid iff clientClipperValid viewClipBox: DeviceRectangle _ [0, 0, 0, 0], -- display coords; valid iff viewClipperValid clientClipBox: DeviceRectangle _ [0, 0, 0, 0], -- display coords; valid iff clientClipperValid display: Display _ NIL -- the particular display; its color setting is valid iff displayColorValid ]; Create: PROC[display: Display] RETURNS[Context] ~ { data: Data ~ NEW[DataRep _ []]; data.color _ ImagerColor.black; data.clientToView _ ImagerTransformation.Scale[1]; data.viewToDisplay _ ImagerTransformation.Scale[1]; data.clientToDisplay _ ImagerTransformation.Scale[1]; data.charToDisplay _ ImagerTransformation.Scale[1]; data.display _ display; ViewReset[data]; RETURN[NEW[Imager.ContextRep _ [class: displayClass, data: data, props: NIL]]]; }; GetOriginI: PROC[m: Transformation] RETURNS[origin: IVEC] ~ { origin.x _ Real.RoundI[MIN[MAX[m.c, INTEGER.FIRST], INTEGER.LAST]]; origin.y _ Real.RoundI[MIN[MAX[m.f, INTEGER.FIRST], INTEGER.LAST]]; }; ComputeViewToDisplay: PROC[data: Data] ~ { m: Transformation ~ data.viewToDisplay; origin: IVEC ~ GetOriginI[m]; data.viewOrigin _ origin; data.viewToDisplayEasy _ m.a=0 AND m.b=-1 AND m.c=origin.y -- s = (0) x + (-1) y + (origin.y) = origin.y-y AND m.d=1 AND m.e=0 AND m.f=origin.x; -- f = (1) x + (0) y + (origin.x) = origin.x+x data.viewToDisplayValid _ TRUE; }; ComputeClientToDisplay: PROC[data: Data] ~ { m: Transformation ~ data.clientToDisplay; origin: IVEC; ValidateViewToDisplay[data]; m^ _ data.viewToDisplay^; m.PreMultiply[data.clientToView]; origin _ GetOriginI[m]; data.clientOrigin _ origin; data.clientToDisplayEasy _ m.a=0 AND m.b=-1 AND m.c=origin.y -- s = (0) x + (-1) y + (origin.y) = origin.y-y AND m.d=1 AND m.e=0 AND m.f=origin.x; -- f = (1) x + (0) y + (origin.x) = origin.x+x data.clientToDisplayValid _ TRUE; }; ComputeCharToDisplay: PROC[data: Data] ~ { m: Transformation ~ data.charToDisplay; ValidateViewToDisplay[data]; m^ _ data.viewToDisplay^; m.PreMultiply[data.clientToView]; m.SetTrans[[0, 0]]; m.PreMultiply[data.font.charToClient]; data.charToDisplayValid _ TRUE; }; ComputeViewClipper: PROC[data: Data] ~ { data.viewClipBox _ ImagerManhattan.BoundingBox[data.viewClipMask]; data.viewClipperValid _ TRUE; }; ComputeClientClipper: PROC[data: Data] ~ { ImagerManhattan.Destroy[data.clientClipMask]; ValidateViewClipper[data]; data.clientClipMask _ ImagerManhattan.Copy[data.viewClipMask]; FOR list: Clipper _ data.clipper, list.rest UNTIL list=NIL DO old, this, new: ManhattanPolygon; old _ data.clientClipMask; data.devicePath _ ImagerScanConverter.CreatePath[ pathProc: list.first.outline.pathProc, pathData: list.first.outline.pathData, transformation: data.viewToDisplay, clipBox: data.viewClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[data.devicePath, data.clientClipBox]; IF list.first.exclude THEN new _ old.Difference[this] ELSE new _ old.Intersection[this]; data.clientClipMask _ new; ImagerManhattan.Destroy[old]; ImagerManhattan.Destroy[this]; ENDLOOP; data.clientClipBox _ ImagerManhattan.BoundingBox[data.clientClipMask]; data.clientClipperValid _ TRUE; }; SetDisplayColor: PROC[data: Data] ~ { data.display.SetColor[data.color, data.viewOrigin]; data.displayColorValid _ TRUE; }; ValidateViewToDisplay: PROC[data: Data] ~ INLINE { IF data.viewToDisplayValid THEN NULL ELSE ComputeViewToDisplay[data]; }; ValidateClientToDisplay: PROC[data: Data] ~ INLINE { IF data.clientToDisplayValid THEN NULL ELSE ComputeClientToDisplay[data]; }; ValidateCharToDisplay: PROC[data: Data] ~ INLINE { IF data.charToDisplayValid THEN NULL ELSE ComputeCharToDisplay[data]; }; ValidateViewClipper: PROC[data: Data] ~ INLINE { IF data.viewClipperValid THEN NULL ELSE ComputeViewClipper[data]; }; ValidateClientClipper: PROC[data: Data] ~ INLINE { IF data.clientClipperValid THEN NULL ELSE ComputeClientClipper[data]; }; ValidateDisplayColor: PROC[data: Data] ~ INLINE { IF data.displayColorValid THEN NULL ELSE SetDisplayColor[data]; }; DoSave: PROC[context: Context, body: PROC] ~ { data: Data ~ NARROW[context.data]; np: NonPersistentVariables ~ data.np; clientToViewID: CARD ~ data.clientToViewID; clientToViewRep: TransformationRep ~ data.clientToView^; font: FONT ~ data.font; color: Color ~ data.color; clipper: Clipper ~ data.clipper; Restore: PROC ~ { data.np _ np; IF clientToViewID#data.clientToViewID THEN { data.clientToView^ _ clientToViewRep; data.clientToViewID _ clientToViewID; data.clientToDisplayValid _ FALSE; data.charToDisplayValid _ FALSE; }; IF font#data.font THEN { data.font _ font; data.charToDisplayValid _ FALSE; }; IF color#data.color THEN { data.color _ color; data.displayColorValid _ FALSE; }; IF clipper#data.clipper THEN { data.clipper _ clipper; data.clientClipperValid _ FALSE; }; }; body[! UNWIND => Restore[]]; Restore[]; }; DoSaveAll: PROC[context: Context, body: PROC] ~ { data: Data ~ NARROW[context.data]; p: PersistentVariables ~ data.p; DoSave[context, body ! UNWIND => data.p _ p]; data.p _ p; }; ChangedClientToView: PROC[data: Data] ~ INLINE { data.clientToViewID _ data.lastID _ data.lastID+1; }; SetTransformation: PROC[context: Context, transformation: Transformation] ~ { data: Data ~ NARROW[context.data]; data.clientToView^ _ transformation^; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; data.charToDisplayValid _ FALSE; }; SetFont: PROC[context: Context, font: FONT] ~ { data: Data ~ NARROW[context.data]; IF font#data.font THEN { data.font _ font; data.charToDisplayValid _ FALSE; }; }; SetColor: PROC[context: Context, color: Color] ~ { data: Data ~ NARROW[context.data]; IF color#data.color THEN { data.color _ color; data.displayColorValid _ FALSE; }; }; SetClipper: PROC[context: Context, clipper: Clipper] ~ { data: Data ~ NARROW[context.data]; IF clipper#data.clipper THEN { data.clipper _ clipper; data.clientClipperValid _ FALSE; }; }; SetReal: PROC[context: Context, key: Imager.RealKey, value: REAL] ~ { data: Data ~ NARROW[context.data]; SELECT key FROM $DCScpx => data.p.cp.x _ value; $DCScpy => data.p.cp.y _ value; $correctMX => data.p.correctMeasure.x _ value; $correctMY => data.p.correctMeasure.y _ value; $mediumXSize => data.np.mediumSize.x _ value; $mediumYSize => data.np.mediumSize.y _ value; $fieldXMin => data.np.fieldMin.x _ value; $fieldYMin => data.np.fieldMin.y _ value; $fieldXMax => data.np.fieldMax.x _ value; $fieldYMax => data.np.fieldMax.y _ value; $strokeWidth => data.np.strokeWidth _ value; $underlineStart => data.np.underlineStart _ value; $amplifySpace => data.np.amplifySpace _ value; $correctShrink => data.np.correctShrink _ value; $correctTX => data.np.correctTolerance.x _ value; $correctTY => data.np.correctTolerance.y _ value; ENDCASE => ERROR Imager.Error[$Bug]; }; SetInt: PROC[context: Context, key: Imager.IntKey, value: INT] ~ { data: Data ~ NARROW[context.data]; SELECT key FROM $priorityImportant => data.np.priorityImportant _ value; $noImage => data.np.noImage _ value; $strokeEnd => data.np.strokeEnd _ value; $correctPass => data.np.correctPass _ value; ENDCASE => ERROR Imager.Error[$Bug]; }; GetTransformation: PROC[context: Context] RETURNS[Transformation] ~ { data: Data ~ NARROW[context.data]; RETURN[NEW[TransformationRep _ data.clientToView^]]; }; GetFont: PROC[context: Context] RETURNS[FONT] ~ { data: Data ~ NARROW[context.data]; RETURN[data.font]; }; GetColor: PROC[context: Context] RETURNS[Color] ~ { data: Data ~ NARROW[context.data]; RETURN[data.color]; }; GetClipper: PROC[context: Context] RETURNS[Clipper] ~ { data: Data ~ NARROW[context.data]; ERROR Imager.Error[$Unimplemented]; }; GetReal: PROC[context: Context, key: Imager.RealKey] RETURNS[REAL] ~ { data: Data ~ NARROW[context.data]; SELECT key FROM $DCScpx => RETURN[data.p.cp.x]; $DCScpy => RETURN[data.p.cp.y]; $correctMX => RETURN[data.p.correctMeasure.x]; $correctMY => RETURN[data.p.correctMeasure.y]; $mediumXSize => RETURN[data.np.mediumSize.x]; $mediumYSize => RETURN[data.np.mediumSize.y]; $fieldXMin => RETURN[data.np.fieldMin.x]; $fieldYMin => RETURN[data.np.fieldMin.y]; $fieldXMax => RETURN[data.np.fieldMax.x]; $fieldYMax => RETURN[data.np.fieldMax.y]; $strokeWidth => RETURN[data.np.strokeWidth]; $underlineStart => RETURN[data.np.underlineStart]; $amplifySpace => RETURN[data.np.amplifySpace]; $correctShrink => RETURN[data.np.correctShrink]; $correctTX => RETURN[data.np.correctTolerance.x]; $correctTY => RETURN[data.np.correctTolerance.y]; ENDCASE => ERROR Imager.Error[$Bug]; }; GetInt: PROC[context: Context, key: Imager.IntKey] RETURNS[INT] ~ { data: Data ~ NARROW[context.data]; SELECT key FROM $priorityImportant => RETURN[data.np.priorityImportant]; $noImage => RETURN[data.np.noImage]; $strokeEnd => RETURN[data.np.strokeEnd]; $correctPass => RETURN[data.np.correctPass]; ENDCASE => ERROR Imager.Error[$Bug]; }; GetCP: PROC[context: Context] RETURNS[VEC] ~ { data: Data ~ NARROW[context.data]; RETURN[data.clientToView.InverseTransform[data.p.cp]]; }; GetCPRounded: PROC[context: Context] RETURNS[VEC] ~ { data: Data ~ NARROW[context.data]; RETURN[data.clientToView.InverseTransform[ImagerTransformation.DRound[data.p.cp]]]; }; ConcatT: PROC[context: Context, m: Transformation] ~ { data: Data ~ NARROW[context.data]; data.clientToView.PreMultiply[m]; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; data.charToDisplayValid _ FALSE; }; Scale2T: PROC[context: Context, sx, sy: REAL] ~ { data: Data ~ NARROW[context.data]; data.clientToView.PreScale2[sx, sy]; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; data.charToDisplayValid _ FALSE; }; RotateT: PROC[context: Context, a: REAL] ~ { data: Data ~ NARROW[context.data]; data.clientToView.PreRotate[a]; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; data.charToDisplayValid _ FALSE; }; TranslateT: PROC[context: Context, x, y: REAL] ~ { data: Data ~ NARROW[context.data]; data.clientToView.PreTranslate[x, y]; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; }; Move: PROC[context: Context] ~ { data: Data ~ NARROW[context.data]; data.clientToView.SetTrans[data.p.cp]; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; }; Trans: PROC[context: Context] ~ { data: Data ~ NARROW[context.data]; data.clientToView.SetTrans[ImagerTransformation.DRound[data.p.cp]]; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; }; SetGray: PROC[context: Context, f: REAL] ~ { data: Data ~ NARROW[context.data]; data.color _ ImagerColor.MakeGray[f]; data.displayColorValid _ FALSE; }; SetSampledColor: PROC[context: Context, pa: PixelArray, m: Transformation, op: ATOM] ~ { data: Data ~ NARROW[context.data]; colorToView: Transformation ~ m.Concat[data.clientToView]; color: Color ~ NEW[ImagerColor.ColorRep[sampled] _ [sampled[ pa: pa, m: colorToView, transparent: FALSE, colorOperator: op]]]; data.color _ color; data.displayColorValid _ FALSE; }; StartUnderline: PROC[context: Context] ~ { data: Data ~ NARROW[context.data]; data.np.underlineStart _ data.clientToView.InverseTransform[data.p.cp].x; }; MaskUnderline: PROC[context: Context, dy, h: REAL] ~ { data: Data ~ NARROW[context.data]; end: VEC ~ data.clientToView.InverseTransform[data.p.cp]; -- current position (client space) start: VEC ~ [data.np.underlineStart, end.y-dy-h]; -- corner of underline (client space) underline: PROC ~ { data.clientToView.SetTrans[ImagerTransformation.DRound[start]]; ChangedClientToView[data]; data.clientToDisplayValid _ FALSE; MaskRectangle[context, 0, 0, end.x-start.x, h]; }; DoSave[context, underline]; }; SetXY: PROC[context: Context, p: VEC] ~ { data: Data ~ NARROW[context.data]; data.p.cp _ data.clientToView.Transform[p]; }; SetXYI: PROC[context: Context, x, y: INTEGER] ~ { data: Data ~ NARROW[context.data]; data.p.cp _ data.clientToView.Transform[[x, y]]; }; SetXYRel: PROC[context: Context, v: VEC] ~ { data: Data ~ NARROW[context.data]; data.p.cp _ data.p.cp.InlineAdd[data.clientToView.TransformVec[v]]; }; SetXYRelI: PROC[context: Context, x, y: INTEGER] ~ { data: Data ~ NARROW[context.data]; data.p.cp _ data.p.cp.InlineAdd[data.clientToView.TransformVec[[x, y]]]; }; ClipOutline: PROC[context: Context, pathProc: PathProc, pathData: REF, exclude: BOOL] ~ { ERROR Imager.Error[$Unimplemented]; }; ClipRectangle: PROC[context: Context, x, y, w, h: REAL, exclude: BOOL] ~ { ERROR Imager.Error[$Unimplemented]; }; CorrectMask: PROC[context: Context] ~ { data: Data ~ NARROW[context.data]; IF data.np.correctPass=0 THEN NULL ELSE { SELECT data.np.correctPass FROM 1 => data.p.correctMaskCount _ data.p.correctMaskCount+1; 2 => IF data.p.correctMaskCount#0 THEN { data.p.cp _ data.p.cp.InlineAdd[data.p.correctMask]; data.p.correctMaskCount _ data.p.correctMaskCount-1; }; ENDCASE; }; }; VMul: PROC[a, b: VEC] RETURNS[VEC] ~ INLINE { RETURN[[a.x*b.x, a.y*b.y]] }; CorrectSpace: PROC[context: Context, v: VEC] ~ { data: Data ~ NARROW[context.data]; IF data.np.correctPass=0 THEN NULL ELSE { s: VEC ~ data.clientToView.TransformVec[v]; SELECT data.np.correctPass FROM 1 => data.p.correctSum _ data.p.correctSum.InlineAdd[s]; 2 => data.p.cp _ data.p.cp.InlineAdd[VMul[s, data.p.correctSpace]]; ENDCASE; }; }; Space: PROC[context: Context, x: REAL] ~ { data: Data ~ NARROW[context.data]; s: VEC ~ data.clientToView.TransformVec[[x, 0]]; data.p.cp _ data.p.cp.InlineAdd[s]; SELECT data.np.correctPass FROM 0 => NULL; 1 => data.p.correctSum _ data.p.correctSum.InlineAdd[s]; 2 => data.p.cp _ data.p.cp.InlineAdd[VMul[s, data.p.correctSpace]]; ENDCASE; }; SetCorrectMeasure: PROC[context: Context, v: VEC] ~ { data: Data ~ NARROW[context.data]; data.p.correctMeasure _ data.clientToView.TransformVec[v]; }; SetCorrectTolerance: PROC[context: Context, v: VEC] ~ { data: Data ~ NARROW[context.data]; data.np.correctTolerance _ data.clientToView.TransformVec[v]; }; Correct: PROC[context: Context, body: PROC] ~ { data: Data ~ NARROW[context.data]; shrink: REAL ~ data.np.correctShrink; tolerance: VEC ~ data.np.correctTolerance; start, end, measure, target, correction: VEC; mask, space: VEC _ [0, 0]; data.p.correctMaskCount _ 0; data.p.correctSum _ [0, 0]; data.np.noImage _ 1; data.np.correctPass _ 1; start _ data.p.cp; -- starting position DoSave[context, body]; -- pass 1 end _ data.p.cp; -- ending position measure _ data.p.correctMeasure; -- desired measure (note: may be set by pass 1) target _ start.Add[measure]; -- target position correction _ target.Sub[end]; -- amount of correction needed (end + correction = target) IF correction.Length<=tolerance.Length THEN NULL -- close enough ELSE IF end.Sub[start].Length(shrink*data.p.correctSum.Length) THEN { mask _ correction.Add[data.p.correctSum.Mul[shrink]]; space _ correction.Sub[mask]; }; }; IF data.p.correctSum.x#0 THEN space.x _ space.x/data.p.correctSum.x ELSE IF space.x#0 THEN { mask.x _ mask.x+space.x; space.x _ 0 }; IF data.p.correctSum.y#0 THEN space.y _ space.y/data.p.correctSum.y ELSE IF space.y#0 THEN { mask.y _ mask.y+space.y; space.y _ 0 }; IF data.p.correctMaskCount#0 THEN { IF mask.x=0 AND mask.y=0 THEN data.p.correctMaskCount _ 0 ELSE IF data.p.correctMaskCount>1 THEN { data.p.correctMaskCount _ data.p.correctMaskCount-1; mask _ mask.Div[data.p.correctMaskCount]; }; }; data.p.correctMask _ mask; data.p.correctSpace _ space; data.np.noImage _ 0; data.np.correctPass _ 2; data.p.cp _ start; DoSave[context, body]; -- pass 2 end _ data.p.cp; -- ending position data.p.cp _ target; }; MaskDevicePath: PROC[data: Data, parity: BOOL _ FALSE] ~ { IF data.clientClipperValid THEN { Runs: PROC[run: PROC[sMin, fMin: INTEGER, fSize: NAT]] ~ { ImagerMask.GenerateRuns[ mask: data.devicePath, clipper: data.clientClipMask, runProc: run]; }; ValidateDisplayColor[data]; -- notify display if color changed data.display.MaskRuns[Runs]; } ELSE ERROR Imager.Error[$Bug]; -- clipper should have been validated }; MaskFill: PROC[context: Context, pathProc: PathProc, pathData: REF, parity: BOOL] ~ { data: Data ~ NARROW[context.data]; IF data.np.noImage=0 THEN { ValidateClientToDisplay[data]; -- validate data.clientToDisplay ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox data.devicePath _ ImagerScanConverter.CreatePath[ pathProc: pathProc, pathData: pathData, transformation: data.clientToDisplay, clipBox: data.clientClipBox, scratch: data.devicePath]; MaskDevicePath[data, parity]; }; }; MaskStroke: PROC[context: Context, pathProc: PathProc, pathData: REF, closed: BOOL] ~ { data: Data ~ NARROW[context.data]; IF data.np.noImage=0 THEN { style: ImagerStroke.StrokeStyle ~ SELECT data.np.strokeEnd FROM 0 => square, 1 => butt, 2 => round, ENDCASE => roundWithRoundJoints; ValidateClientToDisplay[data]; -- validate data.clientToDisplay ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox data.devicePath _ ImagerStroke.DevicePathFromStroke[ pathProc: pathProc, pathData: pathData, width: data.np.strokeWidth, style: style, closed: closed, clientToDevice: data.clientToDisplay, clipBox: data.clientClipBox, scratch: data.devicePath]; MaskDevicePath[data]; }; }; MaskRectangle: PROC[context: Context, x, y, w, h: REAL] ~ { data: Data ~ NARROW[context.data]; IF data.np.noImage=0 THEN { rectangle: PathProc ~ { moveTo[[x, y]]; lineTo[[x+w, y]]; lineTo[[x+w, y+h]]; lineTo[[x, y+h]]; }; ValidateClientToDisplay[data]; -- validate data.clientToDisplay ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox data.devicePath _ ImagerScanConverter.CreatePath[ pathProc: rectangle, pathData: NIL, transformation: data.clientToDisplay, clipBox: data.clientClipBox, scratch: data.devicePath]; MaskDevicePath[data]; }; }; MaskRectangleI: PROC[context: Context, x, y, w, h: INTEGER] ~ { data: Data ~ NARROW[context.data]; IF data.np.noImage=0 THEN { ValidateClientToDisplay[data]; -- validate data.clientToDisplay MaskRectangle[context, x, y, w, h]; }; }; MaskPixel: PROC[context: Context, pa: PixelArray] ~ { data: Data ~ NARROW[context.data]; IF data.np.noImage=0 THEN { paToDisplay: Transformation _ NIL; pixelMask: ImagerMask.Mask _ NIL; ValidateClientToDisplay[data]; -- validate data.clientToDisplay ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox ERROR; }; }; MaskBits: PROC[context: Context, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin, sSize, fSize: NAT, sOffset, fOffset: INTEGER _ 0] ~ { data: Data ~ NARROW[context.data]; IF data.np.noImage=0 THEN { pixelMask: ImagerMask.Mask _ NIL; ValidateClientToDisplay[data]; -- validate data.clientToDisplay ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox ERROR; }; }; ShowChar: PROC[context: Context, char: CHAR, charSet: BYTE _ 0] ~ { data: Data ~ NARROW[context.data]; font: FONT ~ data.font; code: Font.Char ~ charSet*256+ORD[char]; action: PROC ~ { width: VEC _ font.Width[code]; IF font.Amplified[code] THEN width _ width.Mul[data.np.amplifySpace]; Trans[context]; font.MaskChar[code, context]; SetXYRel[context, width]; SELECT font.Correction[code] FROM none => NULL; space => CorrectSpace[context, width]; mask => CorrectMask[context]; ENDCASE => ERROR Imager.Error[$Bug]; }; DoSave[context, action]; }; ShowRope: PROC[context: Context, rope: ROPE, start, len: INT, charSet: BYTE] ~ { action: Rope.ActionType ~ { ShowChar[context, c] }; [] _ Rope.Map[base: rope, start: start, len: len, action: action]; }; ShowText: PROC[context: Context, text: REF READONLY TEXT, start, len: NAT, charSet: BYTE] ~ { action: Rope.ActionType ~ { ShowChar[context, c] }; [] _ RefText.Map[s: text, start: start, len: len, action: action]; }; ViewReset: PROC[data: Data] ~ { data.viewToDisplay^ _ data.display.surfaceToDisplay^; data.viewToDisplayValid _ FALSE; data.clientToDisplayValid _ FALSE; data.charToDisplayValid _ FALSE; ImagerManhattan.Destroy[data.viewClipMask]; data.viewClipMask _ ImagerManhattan.CreateFromBox[data.display.clipBox]; data.viewClipperValid _ FALSE; data.clientClipperValid _ FALSE; }; ViewTranslate: PUBLIC PROC[context: Context, x, y: INTEGER] ~ { WITH context.data SELECT FROM data: Data => { data.viewToDisplay.PreTranslate[x, y]; data.viewToDisplayValid _ FALSE; data.clientToDisplayValid _ FALSE; data.clientClipperValid _ FALSE; }; ENDCASE => ERROR Imager.Error[$Unimplemented]; }; ViewClip: PUBLIC PROC[context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ { WITH context.data SELECT FROM data: Data => { old, this, new: ManhattanPolygon; old _ data.viewClipMask; ValidateViewToDisplay[data]; IF data.viewToDisplayEasy THEN { origin: IVEC ~ data.viewOrigin; xmin, xmax: INTEGER _ x; ymin, ymax: INTEGER _ y; IF w<0 THEN xmin _ xmax+w ELSE xmax _ xmin+w; IF h<0 THEN ymin _ ymax+h ELSE ymax _ ymin+h; this _ ImagerManhattan.CreateFromBox[[ sMin: origin.y-ymax, fMin: origin.x+xmin, sSize: ymax-ymin, fSize: xmax-xmin]]; } ELSE { rectangle: PathProc ~ { moveTo[[x, y]]; lineTo[[x+w, y]]; lineTo[[x+w, y+h]]; lineTo[[x, y+h]]; }; displayClipBox: DeviceRectangle ~ data.display.clipBox; data.devicePath _ ImagerScanConverter.CreatePath[ pathProc: rectangle, pathData: NIL, transformation: data.viewToDisplay, clipBox: displayClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[ devicePath: data.devicePath, clipBox: displayClipBox]; }; IF exclude THEN new _ old.Difference[this] ELSE new _ old.Intersection[this]; data.viewClipMask _ new; ImagerManhattan.Destroy[old]; ImagerManhattan.Destroy[this]; data.viewClipperValid _ FALSE; data.clientClipperValid _ FALSE; }; ENDCASE => ERROR Imager.Error[$Unimplemented]; }; displayClass: Imager.Class ~ NEW[Imager.ClassRep _ [ type: $Display, DoSave: DoSave, DoSaveAll: DoSaveAll, SetTransformation: SetTransformation, SetFont: SetFont, SetColor: SetColor, SetClipper: SetClipper, SetReal: SetReal, SetInt: SetInt, ConcatT: ConcatT, Scale2T: Scale2T, RotateT: RotateT, TranslateT: TranslateT, Move: Move, Trans: Trans, SetGray: SetGray, SetSampledColor: SetSampledColor, MaskFill: MaskFill, MaskStroke: MaskStroke, MaskRectangle: MaskRectangle, MaskRectangleI: MaskRectangleI, MaskVector: ImagerUtils.MaskVectorViaMaskStroke, MaskVectorI: ImagerUtils.MaskVectorIViaMaskStroke, StartUnderline: StartUnderline, MaskUnderline: MaskUnderline, MaskUnderlineI: ImagerUtils.MaskUnderlineIViaMaskUnderline, MaskPixel: MaskPixel, MaskBits: MaskBits, SetXY: SetXY, SetXYI: SetXYI, SetXYRel: SetXYRel, SetXYRelI: SetXYRelI, ShowChar: ShowChar, ShowRope: ShowRope, ShowText: ShowText, ClipOutline: ClipOutline, ClipRectangle: ClipRectangle, ClipRectangleI: ImagerUtils.ClipRectangleIViaClipRectangle, CorrectMask: CorrectMask, CorrectSpace: CorrectSpace, Space: Space, SpaceI: ImagerUtils.SpaceIViaSpace, SetCorrectMeasure: SetCorrectMeasure, SetCorrectTolerance: SetCorrectTolerance, Correct: Correct, GetTransformation: GetTransformation, GetFont: GetFont, GetColor: GetColor, GetClipper: GetClipper, GetReal: GetReal, GetInt: GetInt, GetCP: GetCP, GetCPRounded: GetCPRounded, props: NIL ]]; END. jImagerDisplayImpl.mesa Copyright c 1984 Xerox Corporation. All rights reserved. Michael Plass, March 19, 1984 5:16:59 pm PST Doug Wyatt, October 9, 1984 4:22:42 pm PDT EXPORTS ImagerDisplay CAUTION: this may clobber data.devicePath! multiplies vectors element-by-element IF target.Sub[end].Length>tolerance.Length THEN ERROR Imager.Error[$UnableToProperlyAdjustMaskPositions]; USING [T, color, clipper, noImage] USING [T, color, clipper, noImage, strokeWidth, strokeEnd] USING [T, color, clipper, noImage] USING [T, color, clipper, noImage] IF data.clientToDisplayEasy THEN { origin: IVEC ~ data.clientOrigin; xmin, xmax: INTEGER _ x; ymin, ymax: INTEGER _ y; mask: ManhattanPolygon; IF w<0 THEN xmin _ xmax+w ELSE xmax _ xmin+w; IF h<0 THEN ymin _ ymax+h ELSE ymax _ ymin+h; mask _ ImagerManhattan.CreateFromBox[[ sMin: origin.y-ymax, fMin: origin.x+xmin, sSize: ymax-ymin, fSize: xmax-xmin]]; ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox ValidateDisplayColor[data]; -- notify display if color changed data.display.ApplyMask[data.display, mask, data.clientClipMask]; } ELSE USING [T, color, clipper, noImage] paToDisplay _ pa.m.Concat[data.clientToDisplay]; pixelMask _ ImagerMask.FromPixelArray[pa, paToDisplay]; ValidateDisplayColor[data]; -- notify display if color changed data.display.ApplyMask[data.display, pixelMask, data.clientClipMask]; USING [T, color, clipper, noImage] pixelMap _ xxx; pixelMask _ ImagerMask.FromBitmap[pixelMap]; ValidateDisplayColor[data]; -- notify display if color changed data.display.ApplyMask[data.display, pixelMask, data.clientClipMask]; USING [T, font, color, clipper, noImage] StringState: TYPE ~ RECORD[mode: StringMode, offset: CARDINAL]; StringMode: TYPE ~ {run, esc, esc2, ext, ext2}; ShowText: PROC[context: Context, text: REF READONLY TEXT, start, len: NAT, charSet: BYTE] ~ { data: Data ~ NARROW[context.data]; USING [cp, T, font, color, clipper, noImage, amplifySpace] rem: NAT ~ text.length-start; offset: CARDINAL _ set; state: {run, esc, esc2, ext, ext2} _ run; deviceCP: VEC; Set: PROC[set: CARDINAL] ~ INLINE { IF set#offset THEN NewFontSet[offset _ set] }; Char: PROC[char: CHAR] ~ { info: CharInfo ~ GetCharInfo[char, offset]; width: VEC; IF data.np.noImage=0 THEN { data.display.ApplyMask[data.display, info.mask, data.clientClipMask]; }; width _ info.width; -- display coords IF info.amplified THEN width _ width.InlineMul[data.np.amplifySpace]; data.p.cp _ data.p.cp.InlineAdd[width]; IF data.np.correctPass#0 THEN SELECT info.correction FROM none => NULL; mask => { -- correct mask -- }; space => { -- correct space -- }; ENDCASE; }; Pass1: PROC[char: CHAR] ~ { info: CharInfo ~ GetCharInfo[char]; width: VEC _ info.width; -- in view coords! IF info.amplified THEN width _ width.InlineMul[data.np.amplifySpace]; data.p.cp _ data.p.cp.InlineAdd[width]; SELECT info.correction FROM none => NULL; mask => data.p.correctMaskCount _ data.p.correctMaskCount+1; space => data.p.correctSum _ data.p.correctSum.InlineAdd[width]; ENDCASE; }; ValidateCharToDisplay[data]; ValidateClientClipper[data]; -- validate data.clientClipMask and data.clientClipBox ValidateDisplayColor[data]; -- notify display if color changed FOR i: NAT IN[start..start+MIN[rem, len]) DO char: CHAR ~ text[i]; SELECT state FROM run => IF char='\377 THEN state _ esc ELSE Char[char]; esc => IF char='\377 THEN state _ esc2 ELSE { Set[ORD[char]]; state _ run }; esc2 => IF char='\000 THEN state _ ext ELSE ERROR Imager.Error[$InvalidString]; ext => IF char='\377 THEN state _ esc ELSE { Set[ORD[char]]; state _ ext2 }; ext2 => { Char[char]; state _ ext }; ENDCASE => ERROR; ENDLOOP; IF NOT(state=run OR state=ext) THEN ERROR Imager.Error[$InvalidString]; }; Note: a translation doesn't invalidate fontTransformation MoveSurfaceRectangle: PROC[context: Context, source: IntRectangle, dest: IntPair] ~ { WITH context.data SELECT FROM data: Data => { m: Transformation ~ SurfaceToDevice[data]; sMinDest, fMinDest, sMinSource, fMinSource, sSize, fSize: INTEGER; DoMove: PROC ~ { IF data.class.viewUnitsPerPixel # 1 THEN { sMinDest _ sMinDest / data.class.viewUnitsPerPixel; fMinDest _ fMinDest / data.class.viewUnitsPerPixel; sMinSource _ sMinSource / data.class.viewUnitsPerPixel; fMinSource _ fMinSource / data.class.viewUnitsPerPixel; sSize _ sSize / data.class.viewUnitsPerPixel; fSize _ fSize / data.class.viewUnitsPerPixel; }; FOR i: NAT IN [0..data.numberOfSeparations) DO data[i].Clip[[sMinDest, fMinDest, sSize, fSize]].Transfer[data[i].ShiftMap[sMinSource-sMinDest, fMinSource-fMinDest]]; ENDLOOP; }; [[sMinDest, fMinDest, sSize, fSize]] _ m.TransformIntRectangle[[dest.x, dest.y, source.w, source.h]]; [[sMinSource, fMinSource, sSize, fSize]] _ m.TransformIntRectangle[[source.x, source.y, source.w, source.h]]; data.class.DoUnderLock[data, DoMove, [MIN[sMinDest, sMinSource], MIN[fMinDest, fMinSource], sSize+ABS[sMinSource-sMinDest], fSize+ABS[fMinSource-fMinDest]]]; }; ENDCASE => ERROR Imager.Error[$Unimplemented]; }; TestRectangle: PROC [context: Context, x, y, w, h: REAL] RETURNS [visibility: Visibility] ~ { data: Data ~ NARROW[context.data]; data: Data ~ NARROW[context.data]; trans: TransformationRec ~ CompositeT[data, data]; manhattanPolygon: ImagerManhattan.Polygon _ NIL; IF specialCaseRectangles AND trans.a = 0.0 AND trans.e = 0.0 THEN { s0: INTEGER ~ Round[trans.b * y + trans.c]; s1: INTEGER ~ Round[trans.b * (y+h) + trans.c]; f0: INTEGER ~ Round[trans.d * x + trans.f]; f1: INTEGER ~ Round[trans.d * (x+w) + trans.f]; sMin: INTEGER ~ MIN[s0, s1]; sMax: INTEGER ~ MAX[s0, s1]; fMin: INTEGER ~ MIN[f0, f1]; fMax: INTEGER ~ MAX[f0, f1]; IF sMax<=sMin OR fMax<=fMin THEN RETURN [invisible]; manhattanPolygon _ ImagerManhattan.CreateFromBox[[sMin, fMin, sMax-sMin, fMax-fMin]]; } ELSE { PathMap: PathMapType ~ { move[[x, y]]; line[[x+w, y]]; line[[x+w, y+h]]; line[[x, y+h]]; }; bb: DeviceRectangle _ ImagerManhattan.BoundingBox[data.viewClipMask]; devicePath: DevicePath _ DevicePathFromPath[PathMap, NIL, CompositeT[data, context.data], bb]; manhattanPolygon _ ImagerScanConverter.ConvertToManhattanPolygon[devicePath, bb]; }; ValidateClientClipper[context, data]; visibility _ ImagerManhattan.IsVisible[manhattanPolygon, data.clientClipMask]; ImagerManhattan.Destroy[manhattanPolygon]; }; GetSurfaceBounds: PROC[context: Context] RETURNS[IntRectangle] ~ { WITH context.data SELECT FROM data: Data => RETURN[[0, 0, data.surfaceWidth, data.surfaceHeight]]; ENDCASE => ERROR Imager.Error[$Unimplemented]; }; Ê#µ˜šœ™Jšœ Ïmœ.™9J™,Jšœ*™*J˜—šÏk ˜ Jšœ˜Jšœžœ@˜LJšœ žœ ˜Jšœ žœ$˜5Jšœ˜JšœžœP˜eJšœ žœ˜&Jšœ žœ˜%Jšœžœ˜$Jšœžœ˜1Jšœžœ5˜NJšœ žœ%˜7Jšœžœ¢˜¼Jšœ žœ…˜–Jšœžœ ˜Jšœ˜Jšœ˜Jšœžœ)žœ˜;J˜—Jšœžœž˜ Jšžœ±˜¸Jšžœ™Jšœž˜J˜Jšœ žœ˜Jšœ žœ ˜-J˜Jšžœžœ ˜Jšžœžœžœžœ˜Jšžœžœžœ˜Jšžœžœ žœ˜Jšžœžœ˜!Jšžœžœžœ˜Jšœžœ'˜;Jšœžœ*˜AJšœžœ˜ Jšœ žœ˜/Jšœ žœ˜%Jšœ žœ˜#Jšœ žœ˜)Jšœžœ˜1šœžœ"˜7J˜—J˜šœžœžœ˜#Jšœžœ Ïc˜%Jšœžœ Ÿ"˜@JšœžœŸ.˜IJšœ žœ Ÿ;˜UJšœ žœ Ÿ;˜VJšœžœ Ÿ>˜YJ˜J˜—šœžœžœ˜&JšœžœŸ-˜IJšœ žœ Ÿ˜6Jšœ žœ Ÿ&˜>Jšœ žœ Ÿ&˜>Jšœ žœŸ ˜2Jšœ žœŸ˜&Jšœ žœŸ˜+JšœžœŸ$˜>JšœžœŸ+˜EJšœ žœŸ˜3JšœžœŸ˜4Jšœžœ Ÿ*˜IJ˜J˜—J˜Jšœžœžœ ˜šœ žœžœ˜JšœŸ˜4Jšœ!Ÿ;˜\JšœžœžœŸ˜-JšœžœŸ˜$JšœžœŸ˜3JšœžœŸ.˜RJšœžœŸ8˜RJšœžœŸ%˜7Jšœ-žœŸ%˜WJšœžœžœ˜!Jšœžœžœ˜Jšœžœžœ˜#Jšœžœžœ˜!Jšœžœžœ˜!Jšœžœžœ˜ JšœžœžœŸ˜@JšœžœžœŸ!˜DJšœ žœŸ˜4Jšœ"žœŸ!˜HJšœ žœŸ˜DJšœ žœ Ÿ5˜PJšœžœ Ÿ9˜VJšœ!žœŸ˜EJšœ#žœŸ/˜WJšœ-Ÿ-˜ZJšœ/Ÿ/˜^JšœžœŸK˜bJ˜J˜—J˜šÏnœžœžœ ˜3Jšœ žœ˜J˜Jšœ2˜2Jšœ3˜3Jšœ5˜5Jšœ3˜3J˜J˜Jšžœžœ>žœ˜OJ˜J˜—š  œžœžœ žœ˜=Jš œžœžœžœžœžœžœ˜CJš œžœžœžœžœžœžœ˜CJ˜J˜—š œžœ˜*J˜'Jšœžœ˜Jšœ˜šœ˜JšœžœžœŸ3˜UJšžœžœžœŸ2˜X—Jšœžœ˜J˜J˜—š œžœ˜,J˜)Jšœžœ˜ Jšœ˜J˜Jšœ!˜!Jšœ˜Jšœ˜šœ˜JšœžœžœŸ3˜UJšžœžœžœŸ2˜X—Jšœžœ˜!J˜J˜—š œžœ˜*Jšœ'˜'Jšœ˜Jšœ˜Jšœ!˜!Jšœ˜Jšœ&˜&Jšœžœ˜Jšœ˜J˜—š œžœ˜(JšœB˜BJšœžœ˜Jšœ˜J˜—š œžœ˜*J™*Jšœ-˜-Jšœ˜Jšœ>˜>šžœ)žœžœž˜=Jšœ!˜!Jšœ˜šœ1˜1JšœM˜MJšœ#˜#Jšœ5˜5—JšœZ˜ZJšžœžœžœ˜XJšœ˜Jšœ˜Jšœ˜Jšžœ˜—JšœF˜FJšœžœ˜J˜J˜—š œžœ˜%J˜3Jšœžœ˜J˜J˜—š œžœžœ˜2Jšžœžœžœžœ˜EJ˜J˜—š œžœžœ˜4Jšžœžœžœžœ˜IJ˜J˜—š œžœžœ˜2Jšžœžœžœžœ˜EJ˜J˜—š œžœžœ˜0Jšžœžœžœžœ˜AJ˜J˜—š œžœžœ˜2Jšžœžœžœžœ˜EJ˜J˜—š œžœžœ˜1Jšžœžœžœžœ˜?J˜J˜—J˜š œžœžœ˜.Jšœ žœ˜"Jšœ%˜%Jšœžœ˜+Jšœ8˜8Jšœžœ ˜Jšœ˜Jšœ ˜ š œžœ˜Jšœ ˜ šžœ$žœ˜,Jšœ%˜%Jšœ%˜%Jšœžœ˜"Jšœžœ˜ J˜—šžœžœ˜Jšœ˜Jšœžœ˜ Jšœ˜—šžœžœ˜Jšœ˜Jšœžœ˜Jšœ˜—šžœžœ˜Jšœ˜Jšœžœ˜ Jšœ˜—J˜—Jšœžœ˜J˜ J˜J˜—š  œžœžœ˜1Jšœ žœ˜"Jšœ ˜ Jšœžœ˜-Jšœ ˜ J˜J˜—š œžœžœ˜0Jšœ2˜2Jšœ˜J˜—š œžœ6˜MJšœ žœ˜"Jšœ%˜%Jšœ˜Jšœžœ˜"Jšœžœ˜ J˜J˜—š œžœžœ˜/Jšœ žœ˜"šžœžœ˜Jšœ˜Jšœžœ˜ J˜—J˜J˜—š œžœ$˜2Jšœ žœ˜"šžœžœ˜Jšœ˜Jšœžœ˜J˜—J˜J˜—š  œžœ(˜8Jšœ žœ˜"šžœžœ˜Jšœ˜Jšœžœ˜ J˜—J˜J˜—š œžœ/žœ˜EJšœ žœ˜"šžœž˜Jšœ˜Jšœ˜Jšœ.˜.Jšœ.˜.Jšœ-˜-Jšœ-˜-Jšœ)˜)Jšœ)˜)Jšœ)˜)Jšœ)˜)Jšœ,˜,Jšœ2˜2Jšœ.˜.Jšœ0˜0Jšœ1˜1Jšœ1˜1Jšžœžœ˜$—J˜J˜—š œžœ.žœ˜BJšœ žœ˜"šžœž˜Jšœ8˜8Jšœ$˜$Jšœ(˜(Jšœ,˜,Jšžœžœ˜$—J˜J˜—š œžœžœ˜EJšœ žœ˜"Jšžœžœ*˜4J˜J˜—š œžœžœžœ˜1Jšœ žœ˜"Jšžœ ˜J˜J˜—š œžœžœ ˜3Jšœ žœ˜"Jšžœ ˜J˜J˜—š  œžœžœ ˜7Jšœ žœ˜"Jšžœ˜#J˜J˜—š œžœ(žœžœ˜FJšœ žœ˜"šžœž˜Jšœ žœ˜Jšœ žœ˜Jšœžœ˜.Jšœžœ˜.Jšœžœ˜-Jšœžœ˜-Jšœžœ˜)Jšœžœ˜)Jšœžœ˜)Jšœžœ˜)Jšœžœ˜,Jšœžœ˜2Jšœžœ˜.Jšœžœ˜0Jšœžœ˜1Jšœžœ˜1Jšžœžœ˜$—J˜J˜—š œžœ'žœžœ˜CJšœ žœ˜"šžœž˜Jšœžœ˜8Jšœ žœ˜$Jšœžœ˜(Jšœžœ˜,Jšžœžœ˜$—J˜J˜—š œžœžœžœ˜.Jšœ žœ˜"Jšžœ0˜6J˜J˜—š  œžœžœžœ˜5Jšœ žœ˜"JšžœM˜SJ˜J˜—š œžœ)˜6Jšœ žœ˜"Jšœ!˜!Jšœ˜Jšœžœ˜"Jšœžœ˜ J˜J˜—š œžœžœ˜1Jšœ žœ˜"Jšœ$˜$Jšœ˜Jšœžœ˜"Jšœžœ˜ J˜J˜—š œžœžœ˜,Jšœ žœ˜"Jšœ˜Jšœ˜Jšœžœ˜"Jšœžœ˜ J˜J˜—š  œžœžœ˜2Jšœ žœ˜"Jšœ%˜%Jšœ˜Jšœžœ˜"J˜J˜—š œžœ˜ Jšœ žœ˜"Jšœ&˜&Jšœ˜Jšœžœ˜"J˜J˜—š œžœ˜!Jšœ žœ˜"JšœC˜CJšœ˜Jšœžœ˜"J˜J˜—š œžœžœ˜,Jšœ žœ˜"Jšœ%˜%Jšœžœ˜J˜J˜—š œžœ:žœ˜XJšœ žœ˜"Jšœ:˜:šœžœ*˜Jšœ˜J˜—JšžœžœŸ%˜DJ˜J˜—š œžœ1žœ žœ˜Ušœ žœ˜"Jšžœžœ™"—šžœžœ˜JšœŸ ˜?JšœŸ6˜Sšœ1˜1Jšœ'˜'Jšœ%˜%Jšœ7˜7—J˜J˜—J˜J˜—š  œžœ1žœ žœ˜Wšœ žœ˜"Jšžœžœ2™:—šžœžœ˜šœ"žœž˜?Jšœ$žœ˜D—JšœŸ ˜?JšœŸ6˜Sšœ4˜4Jšœ'˜'Jšœ9˜9Jšœ%˜%Jšœ7˜7—J˜J˜—J˜J˜—š  œžœžœ˜;šœ žœ˜"Jšžœžœ™"—šžœžœ˜šœ˜JšœG˜GJšœ˜—JšœŸ ˜?JšœŸ6˜Sšœ1˜1Jšœžœ˜#Jšœ%˜%Jšœ7˜7—J˜J˜—Jšœ˜J˜—š œžœžœ˜?šœ žœ˜"Jšžœžœ™"—šžœžœ˜JšœŸ ˜?šžœžœ™"Jšœžœ™!Jšœ žœ™Jšœ žœ™Jšœ™Jšžœžœžœ™-Jšžœžœžœ™-šœ&™&Jšœ)™)Jšœ%™%—JšœŸ6™SJšœŸ"™>Jšœ@™@J™—Jšž™Jšœ#˜#J˜—Jšœ˜J˜—š  œžœ&˜5šœ žœ˜"Jšžœžœ™"—šžœžœ˜Jšœžœ˜"Jšœžœ˜!JšœŸ ˜?JšœŸ6˜SJšžœ˜Jšœ0™0Jšœ7™7JšœŸ"™>JšœE™EJ˜—J˜J˜—š  œžœžœžœžœ˜GJšœžœžœ ˜Ašœ žœ˜"Jšžœžœ™"—šžœžœ˜Jšœžœ˜!JšœŸ ˜?JšœŸ6˜SJšžœ˜Jšœ™Jšœ,™,JšœŸ"™>JšœE™EJ˜—J˜J˜—š œžœžœ žœ ˜Cšœ žœ˜"Jšžœžœ ™(—Jšœžœ ˜Jšœžœ˜(šœžœ˜Jšœžœ˜Jšžœžœ)˜EJ˜Jšœ˜Jšœ˜šžœž˜!Jšœžœ˜ J˜&J˜Jšžœžœ˜$—J˜—J˜J˜J˜—š  œžœžœžœ žœ˜PJšœ3˜3J˜BJ˜J˜—š œžœžœžœžœžœ žœ˜]Jšœ3˜3J˜BJ˜J˜—Jšœ žœžœžœ™?Jšœ žœ™/J™š œžœžœžœžœžœ žœ™]šœ žœ™"Jšžœžœ.™:—Jšœžœ™Jšœžœ™J™)Jšœ žœ™Jš  œžœžœžœžœ žœ™Rš œžœžœ™J™+Jšœžœ™ šžœžœ™JšœE™EJ™—JšœŸ™%Jšžœžœ/™EJšœ'™'šžœžœžœž™9Jšœžœ™ Jšœ Ÿœ™Jšœ Ÿœ™!Jšžœ™—J™—š œžœžœ™J™#JšœžœŸ™+Jšžœžœ/™EJšœ'™'šžœž™Jšœžœ™ Jšœ<™š žœžœžœžœ ž™,Jšœžœ ™šžœž™Jšœžœ žœ žœ ™6Jš œžœ žœžœžœ™LJš œžœ žœ žœžœ™OJš œžœ žœ žœžœ™LJšœ$™$Jšžœžœ™—Jšžœ™—Jš žœžœ žœ žœžœ™GJ™J™—J˜š  œžœ˜J˜5Jšœžœ˜ Jšœžœ˜"Jšœžœ˜ Jšœ+˜+JšœH˜HJšœžœ˜Jšœžœ˜ Jšœ˜J˜—š  œžœžœžœ˜?šžœžœž˜šœ˜J˜&Jšœžœ˜ Jšœžœ˜"Jšœ9™9Jšœžœ˜ J˜—Jšžœžœ˜.—Jšœ˜J˜—š  œžœžœžœ žœ˜Ošžœžœž˜šœ˜Jšœ!˜!Jšœ˜Jšœ˜šžœžœ˜ Jšœžœ˜Jšœ žœ˜Jšœ žœ˜Jšžœžœžœ˜-Jšžœžœžœ˜-šœ&˜&Jšœ)˜)Jšœ%˜%—J˜—šžœ˜šœ˜JšœG˜GJšœ˜—J˜7šœ1˜1Jšœžœ˜#Jšœ#˜#Jšœ3˜3—šœ5˜5Jšœ6˜6—J˜—Jšžœ žœžœ˜MJšœ˜Jšœ˜Jšœ˜Jšœžœ˜Jšœžœ˜ J˜—Jšžœžœ˜.—Jšœ˜J˜—š œžœ;™Ušžœžœž™šœ™Jšœ*™*Jšœ:žœ™Bš œžœ™šžœ"žœ™*Jšœ3™3Jšœ3™3Jšœ7™7Jšœ7™7Jšœ-™-Jšœ-™-J™—šžœžœžœž™.Jšœv™vJšž™—J™—šœ&™&Jšœ>™>—šœ*™*JšœB™B—Jš œ&žœžœžœžœ™J™—Jšžœžœ™.—Jšœ™J™—š  œžœ žœžœ™]Jšœ žœ™"Jšœ žœ™"Jšœ2™2Jšœ,žœ™0šžœžœžœžœ™CJšœžœ ™+Jšœžœ$™/Jšœžœ ™+Jšœžœ$™/Jšœžœžœ ™Jšœžœžœ ™Jšœžœžœ ™Jšœžœžœ ™Jšžœ žœ žœžœ ™4JšœU™UJšœ™—šžœ™š œ™J™ J™J™J™Jšœ™—JšœE™EJšœ5žœ&™^JšœQ™QJšœ™—Jšœ%™%JšœN™NJšœ*™*Jšœ™J™—š œžœžœ™Bšžœžœž™Jšœžœ0™DJšžœžœ™.—Jšœ™J™—J˜šœžœ˜4Jšœ˜Jšœ˜Jšœ˜Jšœ%˜%Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ!˜!Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ0˜0Jšœ2˜2Jšœ˜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šžœ˜—…—l8©W