DIRECTORY Basics, Imager, ImagerBackdoor, ImagerBox, ImagerCache, ImagerDev, ImagerFont, ImagerFontPrivate, ImagerManhattan, ImagerMask, ImagerMaskCapture, ImagerPath, ImagerPixelMap, ImagerPrivate, ImagerRast USING [AndFlags, Data, DataRep, Flags, FontCache, FontTuner, NotFlags, OrFlags], ImagerScanConverter, ImagerState USING [ChangeFlags, notChanged, State, StateClip, StateClipRectangle, StateClipRectangleI, StateConcatT, StateCorrect, StateCorrectMask, StateCorrectSpace, StateDontCorrect, StateDoSave, StateGetClipper, StateGetColor, StateGetCP, StateGetFont, StateGetInt, StateGetReal, StateGetStrokeDashes, StateGetT, StateMaskUnderline, StateMove, StateRep, StateRotateT, StateScale2T, StateSetClipper, StateSetColor, StateSetCorrectMeasure, StateSetCorrectTolerance, StateSetFont, StateSetGray, StateSetInt, StateSetReal, StateSetSampledBlack, StateSetSampledColor, StateSetStrokeDashes, StateSetT, StateSetXY, StateSetXYRel, StateSpace, StateStartUnderline, StateTranslateT], NewImagerStroke USING [PathFromStroke], ImagerTransformation, Real, Rope USING [ROPE], Scaled, Vector2 USING [Div, Mul, VEC]; ImagerRastImpl: CEDAR PROGRAM IMPORTS Imager, ImagerBackdoor, ImagerBox, ImagerCache, ImagerFont, ImagerFontPrivate, ImagerManhattan, ImagerMask, ImagerMaskCapture, ImagerPath, ImagerRast, ImagerPixelMap, ImagerScanConverter, ImagerState, NewImagerStroke, ImagerTransformation, Real, Scaled, Vector2 EXPORTS Imager, ImagerBackdoor, ImagerFont, ImagerRast ~ BEGIN OPEN ImagerState, ImagerRast; Class: TYPE ~ ImagerPrivate.Class; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep State: TYPE ~ ImagerState.State; StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep ROPE: TYPE ~ Rope.ROPE; CharMask: TYPE ~ ImagerDev.CharMask; Clipper: TYPE ~ ImagerBackdoor.Clipper; Color: TYPE ~ Imager.Color; ColorOperator: TYPE ~ Imager.ColorOperator; Context: TYPE ~ Imager.Context; Device: TYPE ~ ImagerDev.Device; DeviceBox: TYPE ~ ImagerDev.DeviceBox; DevicePath: TYPE ~ ImagerScanConverter.DevicePath; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; Font: TYPE ~ ImagerFont.Font; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; PathProc: TYPE ~ ImagerPath.PathProc; PixelArray: TYPE ~ Imager.PixelArray; Rectangle: TYPE ~ ImagerTransformation.Rectangle; Transformation: TYPE ~ ImagerTransformation.Transformation; TransformationRep: TYPE ~ ImagerTransformation.TransformationRep; VEC: TYPE ~ Vector2.VEC; XChar: TYPE ~ ImagerFont.XChar; XStringProc: TYPE ~ ImagerFont.XStringProc; DeviceBoxFromDeviceRectangle: PROC [r: DeviceRectangle] RETURNS [DeviceBox] ~ INLINE { RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]]; }; DeviceRectangleFromDeviceBox: PROC [b: DeviceBox] RETURNS [DeviceRectangle] ~ INLINE { RETURN[[sMin: b.smin, fMin: b.fmin, sSize: b.smax-b.smin, fSize: b.fmax-b.fmin]]; }; Create: PUBLIC PROC [device: Device, pixelUnits: BOOL, fontCache: FontCache, rastWeight: REAL, fontTuner: FontTuner, class: Class] RETURNS [Context] ~ { data: Data ~ NEW[DataRep _ [device: device]]; state: State ~ NEW[StateRep _ []]; metersToSurface: VEC ~ device.surfaceUnitsPerInch.Div[Imager.metersPerInch]; data.rastWeight _ rastWeight; data.fontTuner _ fontTuner; data.viewToDevice _ ImagerTransformation.Scale[1]; data.clientToDevice _ ImagerTransformation.Scale[1]; data.charToDevice _ ImagerTransformation.Scale[1]; data.fontCache _ fontCache; DViewReset[data]; IF pixelUnits THEN state.T _ ImagerTransformation.Scale[1] ELSE state.T _ ImagerTransformation.Scale2[metersToSurface]; state.color _ Imager.black; IF class = NIL THEN class _ rasterClass; RETURN[NEW[Imager.ContextRep _ [class: rasterClass, state: state, data: data]]]; }; CreateClass: PUBLIC PROC [type: ATOM] RETURNS [Class] ~ { class: Class ~ NEW[ClassRep _ rasterClass^]; class.type _ type; RETURN[class]; }; ValidateClientToDevice: PROC [data: Data, state: State] ~ { data.clientToDevice.ApplyCat[state.T, data.viewToDevice]; data.valid.clientToDevice _ TRUE; }; ValidateCharToDevice: PROC [data: Data, state: State] ~ { IF NOT data.valid.clientToDevice THEN ValidateClientToDevice[data, state]; data.charToDevice^ _ data.clientToDevice^; data.charToDevice.ApplyTranslateTo[[0, 0]]; data.charToDevice.ApplyPreConcat[state.font.charToClient]; data.valid.charToDevice _ TRUE; }; FontImplRep: PUBLIC TYPE ~ ImagerFontPrivate.FontImplRep; -- export to to ImagerFont ValidateFontAtom: PROC [data: Data, state: State] ~ { fontImpl: REF FontImplRep ~ state.font.impl; IF NOT data.valid.charToDevice THEN ValidateCharToDevice[data, state]; data.fontAtom _ ImagerFontPrivate.MakeFontAtom[fontImpl.typeface.nameAtom, data.charToDevice]; data.valid.fontAtom _ TRUE; }; ValidateClientClipper: PROC [data: Data, state: State] ~ { ImagerManhattan.Destroy[data.clientClipMask]; data.clientClipMask _ ImagerManhattan.Copy[data.viewClipMask]; FOR each: Clipper _ state.clipper, each.rest UNTIL each=NIL DO path: PathProc ~ { ImagerPath.MapOutline[outline: each.first.outline, moveTo: moveTo, lineTo: lineTo, curveTo: curveTo, conicTo: conicTo, arcTo: arcTo] }; old, this, new: ManhattanPolygon; old _ data.clientClipMask; data.devicePath _ ImagerScanConverter.CreatePath[path: path, transformation: data.viewToDevice, clipBox: data.viewClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath, clipBox: data.viewClipBox, parityFill: each.first.parity]; IF each.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.valid.clientClipper _ TRUE; }; ValidateColor: PROC [data: Data, state: State] ~ { device: Device ~ data.device; device.class.SetColor[device, state.color, data.viewToDevice]; data.valid.deviceColor _ TRUE; }; ValidatePriority: PROC [data: Data, state: State] ~ { device: Device ~ data.device; device.class.SetPriority[device, state.np.priorityImportant#0]; data.valid.devicePriority _ TRUE; }; NoteStateChanges: PROC [data: Data, state: State] ~ { changed: ImagerState.ChangeFlags ~ state.changed; state.changed _ ImagerState.notChanged; IF changed.T THEN data.valid.clientToDevice _ data.valid.charToDevice _ data.valid.fontAtom _ FALSE; IF changed.color THEN data.valid.deviceColor _ FALSE; IF changed.priority THEN data.valid.devicePriority _ FALSE; IF changed.font THEN data.valid.charToDevice _ data.valid.fontAtom _ FALSE; IF changed.clipper THEN data.valid.clientClipper _ FALSE; }; ValidateIfNeeded: PROC [data: Data, state: State, needs: Flags] ~ { fix: Flags ~ AndFlags[needs, NotFlags[data.valid]]; IF fix.clientToDevice THEN ValidateClientToDevice[data, state]; IF fix.charToDevice THEN ValidateCharToDevice[data, state]; IF fix.deviceColor THEN ValidateColor[data, state]; IF fix.devicePriority THEN ValidatePriority[data, state]; IF fix.clientClipper THEN ValidateClientClipper[data, state]; IF fix.fontAtom THEN ValidateFontAtom[data, state]; IF AndFlags[data.valid, needs]#needs THEN ERROR; }; Validate: PROC [data: Data, state: State, needs: Flags] ~ INLINE { IF state.changed#ImagerState.notChanged THEN NoteStateChanges[data, state]; IF AndFlags[data.valid, needs]#needs THEN ValidateIfNeeded[data, state, needs]; }; Verify: PROC [data: Data, needs: Flags] ~ INLINE { IF AndFlags[data.valid, needs]#needs THEN ERROR; }; maskDevicePathNeeds: Flags ~ [clientClipper: TRUE, deviceColor: TRUE, devicePriority: TRUE]; MaskDevicePath: PROC [data: Data, parity: BOOL _ FALSE] ~ { device: Device ~ data.device; runs: PROC [run: PROC [sMin, fMin: INTEGER, fSize: NAT]] ~ { clientClipMask: ManhattanPolygon ~ data.clientClipMask; IF clientClipMask#NIL AND clientClipMask.rest=NIL THEN { ImagerScanConverter.ConvertToRuns[devicePath: data.devicePath, runProc: run, clipBox: clientClipMask.first, parityFill: parity]; } ELSE { rem: ManhattanPolygon _ clientClipMask; clip: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { WHILE rem#NIL AND rem.first.sMin+rem.first.sSize<=sMin DO rem _ rem.rest ENDLOOP; FOR l: LIST OF DeviceRectangle _ rem, l.rest UNTIL l=NIL OR l.first.sMin>sMin DO fMinRun: INTEGER ~ MAX[l.first.fMin, fMin]; fMaxRun: INTEGER ~ MIN[l.first.fMin+l.first.fSize, fMin+fSize]; IF fMaxRun > fMinRun THEN run[sMin, fMinRun, fMaxRun-fMinRun]; ENDLOOP; }; ImagerScanConverter.ConvertToRuns[devicePath: data.devicePath, runProc: clip, clipBox: data.clientClipBox, parityFill: parity]; }; }; rect: DeviceRectangle ~ ImagerScanConverter.BoundingBox[data.devicePath].Intersect[data.clientClipBox]; bounds: DeviceBox _ DeviceBoxFromDeviceRectangle[data.clientClipBox]; Verify[data, maskDevicePathNeeds]; -- caller should have validated these device.class.MaskRuns[device: device, bounds: bounds, runs: runs]; }; ClipBoxToMask: PROC [box: DeviceBox, mask: ManhattanPolygon, action: PROC[DeviceBox]] ~ { FOR list: LIST OF DeviceRectangle _ mask, list.rest UNTIL list=NIL DO c: DeviceBox ~ DeviceBoxFromDeviceRectangle[list.first]; IF box.sminc.smin AND box.fminc.fmin THEN action[[smin: MAX[box.smin, c.smin], smax: MIN[box.smax, c.smax], fmin: MAX[box.fmin, c.fmin], fmax: MIN[box.fmax, c.fmax]]]; ENDLOOP; }; maskNeeds: Flags ~ OrFlags[[clientToDevice: TRUE, clientClipper: TRUE], maskDevicePathNeeds]; RasterMaskFill: PROC [context: Context, path: PathProc, parity: BOOL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; Validate[data, state, maskNeeds]; data.devicePath _ ImagerScanConverter.CreatePath[path: path, transformation: data.clientToDevice, clipBox: data.clientClipBox, scratch: data.devicePath]; MaskDevicePath[data, parity]; }; }; specialRectangles: BOOL _ TRUE; RasterMaskRectangle: PROC [context: Context, r: Rectangle] ~ { path: PathProc ~ { moveTo[[r.x, r.y]]; lineTo[[r.x+r.w, r.y]]; lineTo[[r.x+r.w, r.y+r.h]]; lineTo[[r.x, r.y+r.h]]; }; state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; Validate[data, state, maskNeeds]; IF specialRectangles AND data.clientToDevice.form # 0 THEN { clientToDevice: Transformation ~ data.clientToDevice; clip: DeviceBox ~ DeviceBoxFromDeviceRectangle[data.clientClipBox]; p0: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x, r.y]]; p1: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x+r.w, r.y+r.h]]; s0: INT _ Real.Fix[MAX[MIN[p0.x+0.5, clip.smax], clip.smin]]; s1: INT _ Real.Fix[MAX[MIN[p1.x+0.5, clip.smax], clip.smin]]; f0: INT _ Real.Fix[MAX[MIN[p0.y+0.5, clip.fmax], clip.fmin]]; f1: INT _ Real.Fix[MAX[MIN[p1.y+0.5, clip.fmax], clip.fmin]]; IF s1box.smin AND fminbox.fmin THEN { device: Device ~ data.device; IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MaskBoxes[device: device, bounds: box, boxes: boxes]; }; } ELSE RasterMaskRectangle[context, [x: x, y: y, w: w, h: h]]; }; }; RasterMaskUnderline: PROC [context: Context, dy, h: REAL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { end: VEC ~ state.T.InverseTransform[state.p.cp]; -- current position (client space) start: VEC ~ [state.np.underlineStart, end.y-dy-h]; -- corner of underline (client space) underline: PROC ~ { Imager.SetXY[context, start]; Imager.Trans[context]; Imager.MaskRectangle[context, [0, 0, end.x-start.x, h]]; }; Imager.DoSaveAll[context, underline]; }; }; identity: Transformation ~ ImagerTransformation.Scale[1.0]; RasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; StrokePath: PathProc ~ { NewImagerStroke.PathFromStroke[path: path, width: state.np.strokeWidth, m: data.clientToDevice, moveTo: moveTo, lineTo: lineTo, conicTo: conicTo, curveTo: curveTo, closed: closed, end: state.np.strokeEnd, joint: state.np.strokeJoint]; }; Validate[data, state, maskNeeds]; data.devicePath _ ImagerScanConverter.CreatePath[path: StrokePath, transformation: identity, clipBox: data.clientClipBox, scratch: data.devicePath]; MaskDevicePath[data]; }; }; RasterMaskVector: PROC [context: Context, p1, p2: VEC] ~ { path: PathProc ~ { moveTo[p1]; lineTo[p2] }; RasterMaskStroke[context, path, FALSE]; }; RasterMaskPixel: PROC [context: Context, pa: PixelArray] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; Validate[data, state, maskNeeds]; ERROR; }; }; bitsPerWord: NAT ~ Basics.bitsPerWord; RasterMaskBits: PROC [context: Context, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT _ 0, sSize, fSize: NAT, tx, ty: INTEGER] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; Validate[data, state, maskNeeds]; IF data.clientToDevice.form=9 AND data.clientToDevice.integerTrans THEN { clientToDevice: Transformation ~ data.clientToDevice; smin: INT ~ INT[data.clientToDevice.tx]-ty; smax: INT ~ smin+sSize; fmin: INT ~ INT[data.clientToDevice.ty]+tx; fmax: INT ~ fmin+fSize; box: DeviceBox _ DeviceBoxFromDeviceRectangle[data.clientClipBox]; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.clientClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { device: Device ~ data.device; IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MaskBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine, ts: smin-sMin, tf: fmin-fMin, boxes: boxes, replace: FALSE]; }; } ELSE NULL; -- hard case }; }; showNeeds: Flags ~ OrFlags[[fontAtom: TRUE, charToDevice: TRUE], maskNeeds]; CharPartlyVisible: PROC [context: Context, font: Font, char: XChar] RETURNS [BOOL] ~ { bounds: Imager.Box ~ ImagerBox.BoxFromRect[ImagerBackdoor.GetBounds[context]]; bb: Imager.Box ~ ImagerBox.BoxFromExtents[ImagerFont.BoundingBox[font, char]]; common: Imager.Box ~ ImagerBox.IntersectBox[bounds, bb]; RETURN [((common.xmax > common.xmin) AND (common.ymax > common.ymin))] }; BasicShowChar: PROC [context: Context, font: Font, char: XChar, checkBB: BOOL, imaging: BOOL] ~ { data: Data ~ NARROW[context.data]; width: VEC _ font.Width[char]; IF font.Amplified[char] THEN { width _ width.Mul[ImagerBackdoor.GetReal[context, amplifySpace]]; }; Imager.Trans[context]; IF imaging AND (NOT checkBB OR CharPartlyVisible[context, font, char]) THEN { IF data.fontTuner = NIL THEN { ImagerFontPrivate.MaskChar[font, char, context]; } ELSE { op: PROC [ctx: Context] ~ {ImagerFontPrivate.MaskChar[font, char, ctx]}; data.fontTuner.proc[data.fontTuner, op, context]; }; }; Imager.SetXYRel[context, width]; SELECT font.Correction[char] FROM none => NULL; space => Imager.CorrectSpace[context, width]; mask => Imager.CorrectMask[context]; ENDCASE => ERROR; }; worryNat: NAT ~ NAT.LAST/2-1; worryReal: REAL _ worryNat; RasterShow: PROC [context: Context, string: XStringProc, xrel: BOOL] ~ { state: State ~ context.state; data: Data ~ NARROW[context.data]; device: Device ~ data.device; cache: ImagerCache.Ref ~ data.fontCache; font: Font ~ state.font; amplifySpace: REAL ~ state.np.amplifySpace; testAmplified: BOOL ~ amplifySpace#1.0; testCorrection: BOOL ~ state.np.correctPass#0; imaging: BOOL ~ state.np.noImage=0; basicCharAction: PROC [char: XChar] ~ {BasicShowChar[context, font, char, TRUE, imaging]}; scaledCPValid: BOOL _ FALSE; sCP: Scaled.Value; fCP: Scaled.Value; UpdateCP: PROC ~ { IF scaledCPValid THEN state.p.cp _ data.viewToDevice.InverseTransform[[sCP.Float, fCP.Float]]; }; TryScaledCP: PROC ~ { cp: VEC _ data.viewToDevice.Transform[state.p.cp]; IF ABS[cp.x] < worryReal AND ABS[cp.y] < worryReal THEN { sCP _ Scaled.FromReal[cp.x]; fCP _ Scaled.FromReal[cp.y]; scaledCPValid _ TRUE; } ELSE scaledCPValid _ FALSE; }; cachedCharAction: PROC [char: XChar] ~ { charMask: ImagerDev.CharMask _ ImagerCache.Fetch[cache, data.fontAtom, char]; IF charMask=NIL AND imaging AND CharPartlyVisible[context, font, char] THEN { operator: PROC [c: Context] ~ {BasicShowChar[c, font, char, FALSE, TRUE]}; m: Transformation _ data.clientToDevice.TranslateTo[[0, 0]]; charMask _ ImagerMaskCapture.Capture[operator, m, data.rastWeight]; IF charMask#NIL THEN { charMask.font _ data.fontAtom; charMask.char _ char; [] _ ImagerCache.Store[cache, charMask]; }; }; IF charMask=NIL OR NOT charMask.metricsValid OR NOT scaledCPValid THEN { UpdateCP[]; basicCharAction[char]; TryScaledCP[]; } ELSE { sCPOld: Scaled.Value ~ sCP; fCPOld: Scaled.Value ~ fCP; s: INTEGER ~ Scaled.Round[sCPOld]; f: INTEGER ~ Scaled.Round[fCPOld]; IF imaging AND charMask.sSizeBB#0 THEN { ShowCharacterMask[data: data, mask: charMask, s: s, f: f]; }; IF testCorrection OR (testAmplified AND charMask.amplified) THEN { width: VEC _ font.Width[char]; IF testAmplified AND font.Amplified[char] THEN { width _ width.Mul[amplifySpace]; }; UpdateCP[]; Imager.SetXYRel[context, width]; IF testCorrection THEN SELECT font.Correction[char] FROM none => NULL; space => Imager.CorrectSpace[context, width]; mask => Imager.CorrectMask[context]; ENDCASE => ERROR; TryScaledCP[]; } ELSE { sCPNew: Scaled.Value ~ (sCP _ sCPOld.PLUS[charMask.sWidth]); fCPNew: Scaled.Value ~ (fCP _ fCPOld.PLUS[charMask.fWidth]); IF sCPNew.integerPart NOT IN [-worryNat..worryNat] OR fCPNew.integerPart NOT IN [-worryNat..worryNat] THEN { UpdateCP[]; TryScaledCP[]; }; }; }; }; oddCharIndex: BOOL _ FALSE; xRelAction: PROC [char: XChar] ~ { IF oddCharIndex THEN { Imager.SetXRel[context, char.code-128]; } ELSE IF cache=NIL THEN basicCharAction[char] ELSE { TryScaledCP[]; cachedCharAction[char]; UpdateCP[]; }; oddCharIndex _ NOT oddCharIndex; }; saveAction: PROC ~ { Validate[data, state, showNeeds]; IF xrel THEN string[xRelAction] ELSE IF cache=NIL THEN string[basicCharAction] ELSE { TryScaledCP[]; string[cachedCharAction]; UpdateCP[]; }; }; Imager.DoSave[context, saveAction]; }; ShowCharacterMask: PROC [data: Data, mask: CharMask, s: INTEGER, f: INTEGER] ~ { sMin: INTEGER ~ s+mask.sMinBB; fMin: INTEGER ~ f+mask.fMinBB; device: Device ~ data.device; charBox: DeviceBox ~ [ smin: MAX[sMin, 0], fmin: MAX[fMin, 0], smax: MAX[sMin+INTEGER[mask.sSizeBB], 0], fmax: MAX[fMin+INTEGER[mask.fSizeBB], 0] ]; allVisible: BOOL _ FALSE; partlyVisible: BOOL _ TRUE; IF data.clientClipMask#NIL AND data.clientClipMask.rest=NIL THEN { dBB: DeviceBox ~ DeviceBoxFromDeviceRectangle[data.clientClipMask.first]; IF charBox.smin >= dBB.smin AND charBox.fmin >= dBB.fmin AND charBox.smax <= dBB.smax AND charBox.fmax <= dBB.fmax THEN { allVisible _ TRUE; } ELSE IF charBox.smax <= dBB.smin OR charBox.fmax <= dBB.fmin OR charBox.smin >= dBB.smax OR charBox.fmin > dBB.fmax THEN { partlyVisible _ FALSE; }; } ELSE IF charBox.smax = 0 OR charBox.fmax = 0 THEN partlyVisible _ FALSE ELSE IF device.class.MaskChar # NIL THEN { p: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[[charBox.smin, charBox.fmin, charBox.smax-charBox.smin, charBox.fmax-charBox.fmin]]; SELECT ImagerManhattan.IsVisible[p, data.clientClipMask] FROM invisible => partlyVisible _ FALSE; visible => allVisible _ TRUE; ENDCASE => NULL; ImagerManhattan.Destroy[p]; }; IF allVisible AND (device.class.MaskChar # NIL) THEN device.class.MaskChar[device, s, f, mask] ELSE IF partlyVisible THEN WITH mask SELECT FROM raster: REF ImagerDev.CharMaskRep.raster => { boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: charBox, mask: data.clientClipMask, action: action]; }; TRUSTED {device.class.MaskBits[device: device, srcBase: @raster[0], srcWordsPerLine: (raster.fSizeBB+(bitsPerWord-1))/bitsPerWord, ts: sMin, tf: fMin, boxes: boxes ]}; }; runs: REF ImagerDev.CharMaskRep.runs => { FOR p: ManhattanPolygon _ data.clientClipMask, p.rest UNTIL p=NIL DO Runs: PROC [run: ImagerDev.RunProc] ~ { ImagerMask.RunsFromCharMask[mask, run, s, f, p.first]; }; bounds: DeviceBox ~ DeviceBoxFromDeviceRectangle[p.first]; smin: CARDINAL ~ MAX[charBox.smin, bounds.smin]; smax: CARDINAL ~ MIN[charBox.smax, bounds.smax]; fmin: CARDINAL ~ MAX[charBox.fmin, bounds.fmin]; fmax: CARDINAL ~ MIN[charBox.fmax, bounds.fmax]; IF smin ERROR; }; RasterShowText: PROC [context: Context, text: REF READONLY TEXT, start, len: NAT, xrel: BOOL] ~ { string: XStringProc ~ { ImagerFont.MapText[text, start, len, charAction] }; RasterShow[context, string, xrel]; }; RasterGetBoundingRectangle: PUBLIC PROC [context: Context] RETURNS [Rectangle] ~ { state: State ~ context.state; data: Data ~ NARROW[context.data]; Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]]; RETURN[data.clientToDevice.InverseTransformRectangle[[ x: data.clientClipBox.sMin, y: data.clientClipBox.fMin, w: data.clientClipBox.sSize, h: data.clientClipBox.fSize ]]]; }; DViewReset: PROC [data: Data] ~ { data.viewToDevice^ _ data.device.surfaceToDevice^; data.viewClipBox _ DeviceRectangleFromDeviceBox[data.device.box]; ImagerManhattan.Destroy[data.viewClipMask]; data.viewClipMask _ ImagerManhattan.CreateFromBox[data.viewClipBox]; data.valid.clientToDevice _ FALSE; data.valid.charToDevice _ FALSE; data.valid.clientClipper _ FALSE; data.valid.deviceColor _ FALSE; }; DViewTranslate: PROC [data: Data, x, y: INTEGER] ~ { data.viewToDevice.ApplyPreTranslate[[x, y]]; data.valid.clientToDevice _ FALSE; data.valid.clientClipper _ FALSE; data.valid.deviceColor _ FALSE; }; DViewClipRectangleI: PROC [data: Data, x, y, w, h: INTEGER, exclude: BOOL] ~ { viewToDevice: Transformation ~ data.viewToDevice; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { old: ManhattanPolygon ~ data.viewClipMask; smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h]; smax: INTEGER ~ smin+ABS[h]; fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w]; fmax: INTEGER ~ fmin+ABS[w]; IF old#NIL AND old.rest=NIL AND NOT exclude THEN { csmin: INTEGER _ old.first.sMin; csmax: INTEGER _ csmin+old.first.sSize; cfmin: INTEGER _ old.first.fMin; cfmax: INTEGER _ cfmin+old.first.fSize; IF fmincfmin AND smax>csmin THEN { IF cfminfmax THEN cfmax _ fmax; IF csmax>smax THEN csmax _ smax; old.first.sMin _ csmin; old.first.sSize _ csmax-csmin; old.first.fMin _ cfmin; old.first.fSize _ cfmax-cfmin; data.viewClipBox _ old.first; } ELSE { ImagerManhattan.Destroy[old]; data.viewClipMask _ NIL; data.viewClipBox _ [0, 0, 0, 0]; }; } ELSE { this: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[[ sMin: smin, fMin: fmin, sSize: smax-smin, fSize: fmax-fmin]]; data.viewClipMask _ IF exclude THEN old.Difference[this] ELSE old.Intersection[this]; data.viewClipBox _ ImagerManhattan.BoundingBox[data.viewClipMask]; ImagerManhattan.Destroy[old]; ImagerManhattan.Destroy[this]; }; data.valid.clientClipper _ FALSE; } ELSE { path: PathProc ~ { moveTo[[x, y]]; lineTo[[x+w, y]]; lineTo[[x+w, y+h]]; lineTo[[x, y+h]]; }; DViewClip[data: data, path: path, parity: FALSE, exclude: exclude]; }; }; DViewClip: PROC [data: Data, path: PathProc, parity: BOOL, exclude: BOOL] ~ { old, this, new: ManhattanPolygon; old _ data.viewClipMask; data.devicePath _ ImagerScanConverter.CreatePath[path: path, transformation: data.viewToDevice, clipBox: data.viewClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath, clipBox: data.viewClipBox, parityFill: parity]; IF exclude THEN new _ old.Difference[this] ELSE new _ old.Intersection[this]; data.viewClipMask _ new; data.viewClipBox _ ImagerManhattan.BoundingBox[data.viewClipMask]; ImagerManhattan.Destroy[old]; ImagerManhattan.Destroy[this]; data.valid.clientClipper _ FALSE; }; ViewReset: PUBLIC PROC [context: Context] ~ { WITH context.data SELECT FROM data: Data => DViewReset[data]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewReset not implemented"]]; }; ViewTranslateI: PUBLIC PROC [context: Context, x, y: INTEGER] ~ { WITH context.data SELECT FROM data: Data => DViewTranslate[data, x, y]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewTranslateI not implemented"]]; }; ViewClip: PUBLIC PROC [context: Context, path: PathProc, parity: BOOL, exclude: BOOL] ~ { WITH context.data SELECT FROM data: Data => DViewClip[data: data, path: path, parity: parity, exclude: exclude]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewClip not implemented"]]; }; ViewClipRectangleI: PUBLIC PROC [context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ { WITH context.data SELECT FROM data: Data => DViewClipRectangleI[data: data, x: x, y: y, w: w, h: h, exclude: exclude]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewClipRectangleI not implemented"]]; }; ViewFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { state: State ~ context.state; IF state=NIL THEN ERROR Imager.Error[[$unimplemented, "ViewFromClient not implemented"]]; RETURN[state.T.Transform[p]]; }; ClientFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { state: State ~ context.state; IF state=NIL THEN ERROR Imager.Error[[$unimplemented, "ClientFromView not implemented"]]; RETURN[state.T.InverseTransform[p]]; }; DeviceFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => RETURN[data.viewToDevice.Transform[p]]; ENDCASE => ERROR Imager.Error[[$unimplemented, "DeviceFromView not implemented"]]; }; ViewFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => RETURN[data.viewToDevice.InverseTransform[p]]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewFromDevice not implemented"]]; }; DeviceFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => { Validate[data, context.state, [clientToDevice: TRUE]]; RETURN[data.clientToDevice.Transform[p]]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "DeviceFromClient not implemented"]]; }; ClientFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => { Validate[data, context.state, [clientToDevice: TRUE]]; RETURN[data.clientToDevice.InverseTransform[p]]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "ClientFromDevice not implemented"]]; }; MoveViewRectangle: PUBLIC PROC [context: Context, width, height, fromX, fromY, toX, toY: INTEGER] ~ { state: State ~ context.state; WITH context.data SELECT FROM data: Data => { Validate[data, state, maskNeeds]; DMoveViewRectangle[data: data, width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "MoveViewRectangle not implemented"]]; }; DMoveViewRectangle: PROC [data: Data, width, height, fromX, fromY, toX, toY: INTEGER] ~ { viewToDevice: Transformation ~ data.viewToDevice; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { smax: INT _ INT[viewToDevice.tx]-toY; smin: INT _ smax-height; fmin: INT _ INT[viewToDevice.ty]+toX; fmax: INT _ fmin+width; box: DeviceBox _ DeviceBoxFromDeviceRectangle[data.viewClipBox]; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.viewClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { device: Device ~ data.device; IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MoveBoxes[device: device, ts: fromY-toY, tf: toX-fromX, boxes: boxes]; }; } ELSE ERROR; -- hard case }; Visibility: TYPE ~ ImagerBackdoor.Visibility; TestViewRectangle: PUBLIC PROC [context: Imager.Context, x, y, w, h: INTEGER] RETURNS [Visibility] ~ { state: State ~ context.state; WITH context.data SELECT FROM data: Data => { Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]]; RETURN[DTestViewRectangle[data: data, x: x, y: y, w: w, h: h]]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "TestRectangleI not implemented"]]; }; DTestViewRectangle: PROC [data: Data, x, y, w, h: INTEGER] RETURNS [Visibility] ~ { viewToDevice: Transformation ~ data.viewToDevice; viewClipMask: ManhattanPolygon ~ data.viewClipMask; IF viewClipMask=NIL THEN RETURN[none]; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { clip: DeviceRectangle ~ data.viewClipBox; csmin: INTEGER ~ clip.sMin; csmax: INTEGER ~ csmin+clip.sSize; cfmin: INTEGER ~ clip.fMin; cfmax: INTEGER ~ cfmin+clip.fSize; smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h]; smax: INTEGER ~ smin+ABS[h]; fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w]; fmax: INTEGER ~ fmin+ABS[w]; IF fmin>=cfmax OR smin>=csmax OR fmax<=cfmin OR smax<=csmin THEN RETURN[none]; IF viewClipMask.rest#NIL THEN RETURN[part]; IF fmincfmax OR smax>csmax THEN RETURN[part]; RETURN[all]; } ELSE { RETURN[part]; }; }; rasterClass: Class ~ NEW[ClassRep _ [ type: $Raster, DoSave: ImagerState.StateDoSave, SetInt: ImagerState.StateSetInt, SetReal: ImagerState.StateSetReal, SetT: ImagerState.StateSetT, SetFont: ImagerState.StateSetFont, SetColor: ImagerState.StateSetColor, SetClipper: ImagerState.StateSetClipper, SetStrokeDashes: ImagerState.StateSetStrokeDashes, GetInt: ImagerState.StateGetInt, GetReal: ImagerState.StateGetReal, GetT: ImagerState.StateGetT, GetFont: ImagerState.StateGetFont, GetColor: ImagerState.StateGetColor, GetClipper: ImagerState.StateGetClipper, GetStrokeDashes: ImagerState.StateGetStrokeDashes, ConcatT: ImagerState.StateConcatT, Scale2T: ImagerState.StateScale2T, RotateT: ImagerState.StateRotateT, TranslateT: ImagerState.StateTranslateT, Move: ImagerState.StateMove, SetXY: ImagerState.StateSetXY, SetXYRel: ImagerState.StateSetXYRel, Show: RasterShow, ShowText: RasterShowText, StartUnderline: ImagerState.StateStartUnderline, MaskUnderline: ImagerState.StateMaskUnderline, CorrectMask: ImagerState.StateCorrectMask, CorrectSpace: ImagerState.StateCorrectSpace, Space: ImagerState.StateSpace, SetCorrectMeasure: ImagerState.StateSetCorrectMeasure, SetCorrectTolerance: ImagerState.StateSetCorrectTolerance, Correct: ImagerState.StateCorrect, DontCorrect: ImagerState.StateDontCorrect, SetGray: ImagerState.StateSetGray, SetSampledColor: ImagerState.StateSetSampledColor, SetSampledBlack: ImagerState.StateSetSampledBlack, MaskFill: RasterMaskFill, MaskStroke: RasterMaskStroke, MaskRectangle: RasterMaskRectangle, MaskRectangleI: RasterMaskRectangleI, MaskVector: RasterMaskVector, MaskPixel: RasterMaskPixel, MaskBits: RasterMaskBits, Clip: ImagerState.StateClip, ClipRectangle: ImagerState.StateClipRectangle, ClipRectangleI: ImagerState.StateClipRectangleI, GetCP: ImagerState.StateGetCP, GetBoundingRectangle: RasterGetBoundingRectangle, propList: NIL ]]; END. ΔImagerRastImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Michael Plass, May 19, 1985 2:27:44 pm PDT Doug Wyatt, May 10, 1985 10:56:11 am PDT USING [T, clipper, color, noImage, priorityImportant] USING [T, color, clipper, underlineStart, noImage, priorityImportant] USING [T, color, clipper, strokeWidth, strokeStyle, noImage, priorityImportant] USING [T, color, clipper, noImage, priorityImportant] paToDevice: Transformation _ NIL; pixelMask: ImagerMask.Mask _ NIL; paToDevice _ pa.m.Concat[data.clientToDevice]; pixelMask _ ImagerMask.FromPixelArray[pa, paToDevice]; ValidateDeviceState[data]; -- notify device if color changed data.device.ApplyMask[data.device, pixelMask, data.clientClipMask]; USING [T, font, color, clipper, cp, amplifySpace, noImage, priorityImportant] Note that translation doesn't invalidate charToDevice rect: Rectangle ~ viewToDevice.TransformRectangle[[x, y, w, h]]; -- test against viewClipBox -- Κ!p˜codešœ™Kšœ Οmœ7™BK™*Kšœ(™(—K˜šΟk ˜ Kšœ˜Kšœ˜Kšœ˜K˜ K˜ Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜K˜ K˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ žœK˜[Kšœ˜Kšœ žœ”˜₯Kšœžœ˜'Kšœ˜K˜Kšœžœžœ˜K˜Kšœžœ žœ˜—K˜KšΠblœžœž˜Kšžœ†˜Kšžœ/˜6šœžœžœ˜%K˜Kšœžœ˜"Kšœ žœžœΟc˜LK˜Kšœžœ˜ Kšœ žœžœ ˜JK˜šžœžœžœ˜K˜—Kšœ žœ˜$Kšœ žœ˜'Kšœžœ˜Kšœžœ˜+Kšœ žœ˜Kšœžœ˜ Kšœ žœ˜&Kšœ žœ"˜2Kšœžœ"˜7Kšœžœ˜Kšœžœ˜1Kšœ žœ˜%Kšœ žœ˜%Kšœ žœ"˜1Kšœžœ'˜;Kšœžœ*˜AKšžœžœ žœ˜Kšœžœ˜šœ žœ˜+K˜—šΟnœžœžœžœ˜VKšžœK˜QK˜K˜—š‘œžœžœžœ˜VKšžœK˜QK˜K˜—K˜š ‘œžœžœžœ$žœ&žœ˜˜Kšœ žœ˜-Kšœžœ˜"Kšœžœ8˜LKšœ˜Kšœ˜Kšœ2˜2Kšœ4˜4Kšœ2˜2Kšœ˜K˜Kšžœ žœžœ ˜:Kšžœžœ0˜˜>šžœ*žœžœž˜>Kšœœ˜œKšœ!˜!Kšœ˜Kšœ—˜—Kšœ˜Kšžœžœžœ˜XKšœ˜Kšœ˜Kšœ˜Kšžœ˜—KšœF˜FKšœžœ˜ K˜K˜—š‘ œžœ˜2K˜K˜>Kšœžœ˜K˜K˜—š‘œžœ˜5K˜K˜?Kšœžœ˜!K˜K˜—š‘œžœ˜5K˜1Kšœ'˜'Kšžœ žœMžœ˜dKšžœžœžœ˜5Kšžœžœžœ˜;Kšžœžœ1žœ˜KKšžœžœžœ˜9K˜K˜—š‘œžœ-˜CKšœ3˜3Kšžœžœ%˜?Kšžœžœ#˜;Kšžœžœ˜3Kšžœžœ˜9Kšžœžœ$˜=Kšžœžœ˜3Kšžœ#žœžœ˜0K˜K˜—š‘œžœ,žœ˜BKšžœ&žœ˜KKšžœ#žœ&˜OK˜K˜—š‘œžœžœ˜2Kšžœ#žœžœ˜0K˜K˜—K˜šœ-žœžœžœ˜\K˜—š‘œžœžœžœ˜;K˜š œžœžœžœ žœ˜Kšž˜—Kšœ˜—šœ>˜>Kšœ@˜@—K˜—K˜—Kšœg˜gKšœE˜EKšœ# %˜HKšœB˜BK˜—K˜š‘ œžœ2žœ˜Yš žœžœžœ#žœžœž˜EKšœ8˜8š žœžœžœžœž˜SKš œžœžœžœžœ˜~—Kšžœ˜—K˜K˜—šœ,žœžœ˜]K˜—š‘œžœ,žœ˜I˜Kšžœ0™5—šžœžœ˜Kšœ žœ˜"Kšœ!˜!Kšœ›˜›K˜K˜—K˜K˜—šœžœžœ˜K˜—š‘œžœ%˜>šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜šžœžœ˜Kšœ žœ˜"Kšœ!˜!šžœžœžœ˜˜EJšœžœF˜MJšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=Kšžœžœ žœ˜5Kšžœžœ žœ˜5šžœžœžœ˜J˜Jšœ:˜:šœžœ žœ˜(KšœC˜CKšœ˜—JšœB˜BJšœ˜—Jšœ˜—šžœ˜Jšœ›˜›K˜Jšœ˜—K˜—Kšœ˜K˜—š‘œžœ žœ˜FKšœ˜šžœžœ˜Kšœ žœ˜"Kšœ˜Kšœ!˜!Kšœ%˜%šžœžœžœ˜?Kšœžœžœžœ ˜9Kšœžœžœžœ ˜9KšœB˜Bšœžœ žœ˜(KšœC˜CKšœ˜—Kšžœ žœ žœ$˜AKšžœ žœ žœ$˜Aš žœžœžœžœžœ˜MK˜Jšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœB˜BK˜—K˜—Kšžœ8˜˜>Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜—Jšœ˜—šœžœ ˜)šžœ3žœžœž˜Dš‘œžœ˜'Jšœ6˜6Jšœ˜—Jšœ:˜:Jšœžœžœ˜0Jšœžœžœ˜0Jšœžœžœ˜0Jšœžœžœ˜0šžœ žœ žœ˜!Jšœl˜lJšœ˜—Jšžœ˜—Jšœ˜—Jšžœžœ˜—K˜K˜—š‘œžœžœžœžœžœžœ˜bKšœK˜KK˜"K˜K˜—K™š‘œžœžœžœ˜RK˜Kšœ žœ˜"Kšœ'žœžœ˜CKšžœ§˜­K˜K˜—K˜š‘ œžœ˜!Kšœ2˜2KšœA˜AKšœ+˜+KšœD˜DKšœžœ˜"Kšœžœ˜ Kšœžœ˜!Kšœžœ˜Kšœ˜K˜—š‘œžœžœ˜4K˜,Kšœžœ˜"Kšœžœ˜!Kšœžœ˜Kšœ5™5Kšœ˜K˜—š‘œžœžœ žœ˜NKšœ1˜1šžœžœžœ˜;Kšœ*˜*Kšœžœžœ ˜,Kšœžœžœ˜Kšœžœžœ ˜,Kšœžœžœ˜šžœžœžœ žœžœžœ žœ˜2Kšœžœ˜ Kšœžœ˜'Kšœžœ˜ Kšœžœ˜'š žœ žœ žœ žœ žœ˜AKšžœ žœ˜ Kšžœ žœ˜ Kšžœ žœ˜ Kšžœ žœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜K˜—šžœ˜Kšœ˜Kšœžœ˜K˜ K˜—K˜—šžœ˜šœ8˜8Kšœ=˜=—Kšœžœ žœžœ˜UKšœB˜BKšœ˜Kšœ˜K˜—Kšœžœ˜!K˜—šžœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ*žœ˜CK˜—Kšœ˜K˜—š‘ œžœ&žœ žœ˜MKšœ!˜!Kšœ˜Kšœ—˜—Kšœ‚˜‚Kšžœ žœžœ˜MKšœ˜KšœB˜BKšœ˜Kšœ˜Kšœžœ˜!Kšœ˜K˜—K˜š‘ œžœžœ˜-šžœžœž˜Kšœ˜Kšžœžœ=˜M—Kšœ˜K˜—š‘œžœžœžœ˜Ašžœžœž˜Kšœ)˜)KšžœžœB˜R—Kšœ˜K˜—š ‘œžœžœ,žœ žœ˜Yšžœžœž˜KšœR˜RKšžœžœ<˜L—Kšœ˜K˜—š ‘œžœžœ žœ žœ˜Zšžœžœž˜KšœX˜XKšžœžœF˜V—Kšœ˜K˜—š ‘œžœžœžœžœžœ˜HK˜KšžœžœžœžœB˜YKšžœ˜Kšœ˜K˜—š ‘œžœžœžœžœžœ˜HK˜KšžœžœžœžœB˜YKšžœ˜$Kšœ˜K˜—š ‘œžœžœžœžœžœ˜Hšžœžœž˜Kšœžœ!˜5KšžœžœB˜R—Kšœ˜K˜—š ‘œžœžœžœžœžœ˜Hšžœžœž˜Kšœžœ(˜Kšœžœžœžœ˜=Kšœ@˜@šœžœ žœ˜(KšœA˜AKšœ˜—š žœžœžœžœžœ˜MK˜Jšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœS˜SK˜—K˜—Kšžœžœ  ˜K˜K˜—Kšœ žœ˜-K˜š ‘œžœžœ'žœžœ˜gKšœ˜šžœžœž˜šœ˜Kšœ'žœžœ˜CKšžœ9˜?K˜—KšžœžœB˜R—K˜—K˜š‘œžœžœžœ˜SK˜1K˜3Kšžœžœžœžœ˜&šžœžœžœ˜;K˜)Kšœžœ ˜Kšœžœ˜"Kšœžœ ˜Kšœžœ˜"Kšœžœžœ ˜,Kšœžœžœ˜Kšœžœžœ ˜,Kšœžœžœ˜Kš žœ žœ žœ žœ žœžœ˜NKšžœžœžœžœ˜+Kš žœ žœ žœ žœ žœžœ˜JKšžœ˜ K˜—šžœ˜K™@Kš ™Kšžœ˜ K˜—K˜K˜—K˜šœžœ ˜%Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ"˜"Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ(˜(Kšœ2˜2Kšœ ˜ Kšœ"˜"Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ(˜(Kšœ2˜2Kšœ"˜"Kšœ"˜"Kšœ"˜"Kšœ(˜(Kšœ˜Kšœ˜Kšœ$˜$Kšœ˜Kšœ˜Kšœ0˜0Kšœ.˜.Kšœ*˜*Kšœ,˜,Kšœ˜Kšœ6˜6Kšœ:˜:Kšœ"˜"Kšœ*˜*Kšœ"˜"Kšœ2˜2Kšœ2˜2Kšœ˜Kšœ˜Kšœ#˜#Kšœ%˜%Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ.˜.Kšœ0˜0Kšœ˜Kšœ1˜1Kšœ ž˜ K˜K˜—K˜—Kšžœ˜—…—~œ£Π