DIRECTORY Basics, BlockRotate, Char, ImagerBox USING [Box, BoxFromRectangle, Extents, Rectangle], ImagerClipper, ImagerDevice, ImagerDeviceInterchange USING [InterchangeStateRep], ImagerDeviceProcs USING [], ImagerDeviceVector USING [DVec, DVecRep], ImagerDeviceWorks USING [], ImagerError USING [Error], ImagerFont USING [CorrectionType, Font, Modify, nullXChar, XChar, XCharProc, XStringProc], ImagerFontWorks, ImagerManhattan, ImagerMaskCache USING [BitmapFromCharMask, BoxesFromCharMask, CharFlags, CharMask, CharMaskRep, Fetch, GetParameters, MaskCache, ObtainSmallCache, Parameters, ParametersRep, ReleaseSmallCache, SmallCache, Store], ImagerPath, ImagerPen, ImagerPixel, ImagerPixelArray, ImagerSample, ImagerScanConverter, ImagerScanConverterExtras, ImagerStroke USING [buttEnd, ConvolvePenWithPath, Dashes, EndCode, JointCode, MapBitPath, PathFromStroke, PathFromVector, roundEnd, SignalSquareEndWithNoDirection, warningSquareEnd], ImagerSwitches, ImagerSys USING [SMul], ImagerTransformation, ImagerTypeface USING [MakeFont, Typeface, TypefaceClass, TypefaceClassRep, TypefaceRep], Prop USING [PropList], Real USING [Ceiling, Floor, Round], RealInline, RealFns USING [AlmostEqual], RefText USING [ObtainScratch, ReleaseScratch], ImagerScaled USING [Float, Floor, FromReal, IntRep, PLUS, Round, Value], SF, SFInline USING [Intersect, Nonempty], Vector2 USING [InlineSub, Length, VEC]; ImagerDeviceWorksImpl: CEDAR MONITOR IMPORTS Basics, BlockRotate, Char, ImagerBox, ImagerError, ImagerFont, ImagerFontWorks, ImagerManhattan, ImagerMaskCache, ImagerPath, ImagerPixel, ImagerPixelArray, ImagerSample, ImagerScanConverter, ImagerScanConverterExtras, ImagerStroke, ImagerSwitches, ImagerSys, ImagerTransformation, ImagerTypeface, Real, RealFns, RealInline, RefText, ImagerScaled, SF, SFInline, Vector2 EXPORTS ImagerDevice, ImagerDeviceProcs, ImagerDeviceWorks, ImagerDeviceInterchange ~ BEGIN Device: TYPE = ImagerDevice.Device; DeviceClipper: TYPE = ImagerDevice.DeviceClipper; DeviceParm: TYPE = ImagerDevice.DeviceParm; DevicePath: TYPE = ImagerScanConverter.DevicePath; EasyMetrics: TYPE = ImagerDevice.EasyMetrics; Extents: TYPE = ImagerBox.Extents; Font: TYPE = ImagerFont.Font; ManhattanPolygon: TYPE = ImagerManhattan.Polygon; RasterMask: TYPE = REF ImagerMaskCache.CharMaskRep.raster; PathProc: TYPE = ImagerPath.PathProc; PixelArray: TYPE = ImagerPixelArray.PixelArray; SampleMap: TYPE = ImagerSample.SampleMap; Transformation: TYPE = ImagerTransformation.Transformation; Typeface: TYPE = ImagerTypeface.Typeface; VEC: TYPE = Vector2.VEC; XChar: TYPE = ImagerFont.XChar; XStringProc: TYPE = ImagerFont.XStringProc; bitsPerWord: NAT = BITS[WORD]; nullXChar: XChar = ImagerFont.nullXChar; ordinaryMetrics: ImagerMaskCache.CharFlags ~ ImagerDevice.ordinaryMetrics; worryNat: NAT = LAST[NAT15] / 2 - 1; worryReal: REAL ¬ worryNat; scanFatSwitch: CHAR['s..'s] ~ ImagerSwitches.Define['s, $scanfat, "Use fat-mode scan conversion", NIL]; CreatePath: PROC [path: PathProc, transformation: Transformation, clipBox: SF.Box] RETURNS [DevicePath] ~ { devicePath: DevicePath ~ ImagerScanConverter.Create[]; ImagerScanConverterExtras.SetScanConversionMode[devicePath, ORD[ImagerSwitches.BoolValue[scanFatSwitch]]]; ImagerScanConverter.SetPath[devicePath, path, transformation, clipBox]; RETURN [devicePath] }; WhatClassHas: PROC [class: ImagerDevice.DeviceClass] RETURNS [ImagerDevice.ClassHas] = INLINE { RETURN [[ SetPriority: class.SetPriority # NIL, DrawBitmap: class.DrawBitmap # NIL, MoveBox: class.MoveBox # NIL, SwitchBuffer: class.SwitchBuffer # NIL, AccessBuffer: class.AccessBuffer # NIL ]] }; MakeDeviceParm: PUBLIC PROC [class: ImagerDevice.DeviceClass, sSize, fSize: NAT, scanMode: ImagerTransformation.ScanMode, surfaceUnitsPerInch: Vector2.VEC, surfaceUnitsPerPixel: NAT ¬ 1, fontCache: ImagerMaskCache.MaskCache ¬ NIL, parameters: ImagerMaskCache.Parameters ¬ NIL, propList: Prop.PropList ¬ NIL] RETURNS [DeviceParm] = { deviceParm: DeviceParm ~ NEW [ImagerDevice.DeviceParmRepr ¬ [ classHas: WhatClassHas[class], sSize: sSize, fSize: fSize, scanMode: scanMode, surfaceUnitsPerInch: surfaceUnitsPerInch, surfaceUnitsPerPixel: surfaceUnitsPerPixel, fontCache: fontCache, parameters: IF parameters # NIL THEN parameters ELSE IF fontCache # NIL THEN ImagerMaskCache.GetParameters[fontCache] ELSE NEW[ImagerMaskCache.ParametersRep ¬ []], propList: propList ]]; RETURN [deviceParm] }; BoxesFromPath: PUBLIC PROC [action: PROC [bounds: SF.Box, boxGenerator: SF.BoxGenerator], path: PathProc, oddWrap: BOOL, pathToDevice: Transformation, clipper: DeviceClipper] = { devicePath: DevicePath = CreatePath[path: path, transformation: pathToDevice, clipBox: clipper.clipBox]; pathBox: SF.Box = ImagerScanConverter.BoundingBox[devicePath]; bounds: SF.Box = SF.Intersect[pathBox, clipper.clipBox]; Runs: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] = { rem: ManhattanPolygon ¬ clipper.clipMask; IF rem#NIL AND rem.rest=NIL THEN { ImagerScanConverter.ConvertToBoxes[devicePath: devicePath, oddWrap: oddWrap, clipBox: clipper.clipMask.first, boxAction: boxAction]; } ELSE { ClipBoxAction: --SF.BoxAction-- PROC [box: SF.Box] = { WHILE rem#NIL AND rem.first.max.s<=box.min.s DO rem ¬ rem.rest ENDLOOP; FOR l: LIST OF SF.Box ¬ rem, l.rest UNTIL l=NIL OR l.first.min.s>=box.max.s DO clipped: SF.Box = SF.Intersect[box, l.first]; IF SF.Nonempty[clipped] THEN boxAction[clipped]; ENDLOOP; }; ImagerScanConverter.ConvertToBoxes[devicePath: devicePath, oddWrap: oddWrap, clipBox: bounds, boxAction: ClipBoxAction]; }; }; IF SF.Nonempty[bounds] THEN action[bounds, Runs]; ImagerScanConverter.Destroy[devicePath]; }; StandardClip: PUBLIC PROC [device: Device, viewClipper: DeviceClipper, clipperToDevice: Transformation, clientClipper: ImagerClipper.Clipper] = { cc: ImagerManhattan.Polygon ¬ NIL; clipper: DeviceClipper ¬ device.worksState.clipper; IF clipper = NIL THEN device.worksState.clipper ¬ clipper ¬ NEW[ImagerDevice.DeviceClipperRep]; ImagerManhattan.Destroy[clipper.clipMask]; cc ¬ ImagerManhattan.Copy[viewClipper.clipMask]; cc ¬ ImagerManhattan.DestructiveClip[cc, device.state.bounds]; FOR each: ImagerClipper.Clipper ¬ clientClipper, each.rest UNTIL each=NIL DO Combine: PROC [a, b: ManhattanPolygon] RETURNS [ManhattanPolygon] ¬ ( IF each.first.exclude THEN ImagerManhattan.DestructiveDifference ELSE ImagerManhattan.DestructiveIntersection ); path: PathProc ~ { ImagerPath.MapOutline[outline: each.first.outline, moveTo: moveTo, lineTo: lineTo, curveTo: curveTo, conicTo: conicTo, arcTo: arcTo] }; devicePath: DevicePath ¬ CreatePath[path: path, transformation: clipperToDevice, clipBox: viewClipper.clipBox]; this: ManhattanPolygon ¬ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: devicePath, clipBox: viewClipper.clipBox, oddWrap: each.first.oddWrap]; cc ¬ Combine[cc, this]; ImagerManhattan.Destroy[this]; ImagerScanConverter.Destroy[devicePath]; ENDLOOP; clipper.clipMask ¬ cc; clipper.clipBox ¬ ImagerManhattan.BoundingBox[cc]; device.worksState.clipperToDevice ¬ clipperToDevice; device.worksState.clientClipper ¬ clientClipper; }; StandardMaskFill: PUBLIC PROC [device: Device, path: PathProc, oddWrap: BOOL, pathToDevice: Transformation] = { clipper: DeviceClipper = device.worksState.clipper; devicePath: DevicePath = CreatePath[path: path, transformation: pathToDevice, clipBox: clipper.clipBox]; FillDevicePath[device, devicePath, oddWrap]; ImagerScanConverter.Destroy[devicePath]; }; FillDevicePath: PROC [device: Device, devicePath: DevicePath, oddWrap: BOOL ¬ FALSE] ~ { clipper: DeviceClipper ~ device.worksState.clipper; pathBox: SF.Box = ImagerScanConverter.BoundingBox[devicePath]; bounds: SF.Box = SFInline.Intersect[pathBox, clipper.clipBox]; IF SFInline.Nonempty[bounds] THEN { IF device.state.allow.regionFill AND ImagerScanConverter.Monotone[devicePath] AND SF.Inside[inner: bounds, outer: clipper.clipMask.first] THEN { GenerateEdges: PROC [edgeAction: ImagerSample.EdgeAction] = { ImagerScanConverter.GenerateEdges[devicePath: devicePath, edgeAction: edgeAction]; }; device.class.MaskRegion[device: device, bounds: bounds, edgeGenerator: GenerateEdges]; } ELSE { GenerateBoxes: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] = { rem: ManhattanPolygon ¬ clipper.clipMask; IF rem#NIL AND rem.rest=NIL THEN { ImagerScanConverter.ConvertToBoxes[devicePath: devicePath, oddWrap: oddWrap, clipBox: clipper.clipMask.first, boxAction: boxAction]; } ELSE { ClipBoxAction: --SF.BoxAction-- PROC [box: SF.Box] = { WHILE rem#NIL AND rem.first.max.s<=box.min.s DO rem ¬ rem.rest ENDLOOP; FOR l: LIST OF SF.Box ¬ rem, l.rest UNTIL l=NIL OR l.first.min.s>=box.max.s DO clipped: SF.Box = SFInline.Intersect[box, l.first]; IF SFInline.Nonempty[clipped] THEN boxAction[clipped]; ENDLOOP; }; ImagerScanConverter.ConvertToBoxes[devicePath: devicePath, oddWrap: oddWrap, clipBox: bounds, boxAction: ClipBoxAction]; }; }; device.class.MaskBoxes[device: device, bounds: bounds, boxes: GenerateBoxes]; }; }; }; ClippedRound: PROC [r: REAL, min, max: INTEGER] RETURNS [i: INTEGER] = { r ¬ r + 0.5; IF r < min THEN r ¬ min; IF r > max THEN r ¬ max; RETURN [Real.Floor[r]]; }; StandardMaskRectangle: PUBLIC PROC [device: Device, rectangle: ImagerBox.Rectangle, rectangleToDevice: Transformation] = { clipper: DeviceClipper ~ device.worksState.clipper; IF rectangleToDevice.form # 0 AND NOT ImagerSwitches.BoolValue[scanFatSwitch] THEN { p0: VEC = ImagerTransformation.Transform[rectangleToDevice, [rectangle.x, rectangle.y]]; p1: VEC = ImagerTransformation.Transform[rectangleToDevice, [rectangle.x + rectangle.w, rectangle.y + rectangle.h]]; s0: INTEGER ¬ ClippedRound[p0.x, clipper.clipBox.min.s, clipper.clipBox.max.s]; s1: INTEGER ¬ ClippedRound[p1.x, clipper.clipBox.min.s, clipper.clipBox.max.s]; f0: INTEGER ¬ ClippedRound[p0.y, clipper.clipBox.min.f, clipper.clipBox.max.f]; f1: INTEGER ¬ ClippedRound[p1.y, clipper.clipBox.min.f, clipper.clipBox.max.f]; IF s1 < s0 THEN {temp: INTEGER = s0; s0 ¬ s1; s1 ¬ temp}; IF f1 < f0 THEN {temp: INTEGER = f0; f0 ¬ f1; f1 ¬ temp}; IF s0 < s1 AND f0 < f1 THEN { box: SF.Box = [min: [s0, f0], max: [s1, f1]]; Boxes: PROC [action: PROC [SF.Box]] = { ImagerManhattan.ClipBoxToMask[box: box, mask: clipper.clipMask, action: action]; }; device.class.MaskBoxes[device: device, bounds: box, boxes: Boxes]; }; } ELSE { RectanglePath: PathProc = { moveTo[[rectangle.x, rectangle.y]]; lineTo[[rectangle.x + rectangle.w, rectangle.y]]; lineTo[[rectangle.x + rectangle.w, rectangle.y + rectangle.h]]; lineTo[[rectangle.x, rectangle.y + rectangle.h]]; }; device.works.MaskFill[device: device, path: RectanglePath, oddWrap: FALSE, pathToDevice: rectangleToDevice]; }; }; StandardMaskStroke: PUBLIC PROC [device: Device, path: PathProc, closed: BOOL, pathToDevice: Transformation, end: ImagerStroke.EndCode, joint: ImagerStroke.JointCode, miterLimit: REAL, pen: ImagerPen.Pen] = { clipper: DeviceClipper ~ device.worksState.clipper; devicePath: DevicePath ¬ NIL; T: PROC [p: VEC] RETURNS [ImagerScanConverter.Pair] ~ INLINE { RETURN [[s: p.x, f: p.y]] }; MoveTo: ImagerPath.MoveToProc ~ { -- [p: VEC] ImagerScanConverter.MoveTo[devicePath, T[p]]; }; LineTo: ImagerPath.LineToProc ~ { -- PROC [p1: VEC] ImagerScanConverter.LineTo[devicePath, T[p1]]; }; ConicTo: ImagerPath.ConicToProc ~ { -- PROC [p1, p2: VEC, r: REAL] ImagerScanConverter.ParTo[devicePath, T[p1], T[p2]]; }; StrokePath: ImagerPath.PathProc ~ { -- PROC [moveTo: MoveToProc, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc] StrokeMoveTo: ImagerPath.MoveToProc ~ { -- [p: VEC] moveTo[p]; -- This comes first to flush out any previous trajectory. We rely on the the fact that all ImagerStroke.PathFromStroke does with this point is save it away - it can't do more until it has a direction. IF devicePath = NIL THEN { devicePath ¬ ImagerScanConverter.Create[] } ELSE { FillDevicePath[device, devicePath] }; << ImagerScanConverter.SetScanConversionMode[devicePath, 0]; >> ImagerScanConverter.SetBounds[devicePath, clipper.clipBox]; }; path[moveTo: StrokeMoveTo, lineTo: lineTo, curveTo: curveTo, conicTo: conicTo, arcTo: arcTo]; }; ImagerStroke.PathFromStroke[path: StrokePath, closed: closed, m: pathToDevice, pen: pen, end: end, joint: joint, moveTo: MoveTo, lineTo: LineTo, conicTo: ConicTo, box: [xmin: clipper.clipBox.min.s, ymin: clipper.clipBox.min.f, xmax: clipper.clipBox.max.s, ymax: clipper.clipBox.max.f], miterLimit: miterLimit]; IF devicePath # NIL THEN { FillDevicePath[device, devicePath]; ImagerScanConverter.Destroy[devicePath]; }; }; BailOut: ERROR = CODE; HardCurve: ImagerPath.CurveToProc = { ERROR BailOut }; HardConic: ImagerPath.ConicToProc = { ERROR BailOut }; HardArc: ImagerPath.ArcToProc = { ERROR BailOut }; SimplePath: TYPE = RECORD [ valid: BOOL ¬ FALSE, p0: VEC ¬ [0,0], p1: VEC ¬ [0,0] ]; CheckSimplePath: PROC [path: PathProc] RETURNS [SimplePath] = { result: SimplePath ¬ []; state: NAT ¬ 0; EasyMove: ImagerPath.MoveToProc = { IF state = 0 THEN { result.p0 ¬ p; state ¬ 1 } ELSE ERROR BailOut }; EasyLine: ImagerPath.LineToProc = { IF state = 1 THEN { result.p1 ¬ p1; state ¬ 2 } ELSE ERROR BailOut }; path[moveTo: EasyMove, lineTo: EasyLine, curveTo: HardCurve, conicTo: HardConic, arcTo: HardArc ! BailOut => { state ¬ 3; CONTINUE }]; result.valid ¬ state=2; RETURN [result] }; newDash: BOOL ¬ TRUE; NewDash: PROC [c: CARD] RETURNS [old: BOOL] = { old ¬ newDash; newDash ¬ c#0 }; StandardMaskDashedStroke: PUBLIC PROC [device: Device, path: PathProc, patternLen: NAT, pattern: PROC [NAT] RETURNS [REAL], offset: REAL, length: REAL, closed: BOOL, pathToDevice: Transformation, end: INT, joint: INT, miterLimit: REAL, pen: ImagerPen.Pen] ~ { clipper: DeviceClipper ~ device.worksState.clipper; DashedPath: ImagerPath.PathProc ~ { ImagerStroke.Dashes[path: path, patternLen: patternLen, pattern: pattern, offset: offset, length: length, moveTo: moveTo, lineTo: lineTo, conicTo: conicTo, curveTo: curveTo] }; IF clipper.clipMask = NIL THEN RETURN; IF newDash AND NOT closed AND patternLen > 0 AND pattern[0] > 1.0E-3 AND offset >= 0.0 THEN { v: SimplePath = CheckSimplePath[path]; IF v.valid THEN { IF length < 1.0E-10 THEN length ¬ Vector2.Length[Vector2.InlineSub[v.p0, v.p1]]; IF length >= 1.0E-10 THEN SELECT TRUE FROM device.state.allow.unorderedBoxes AND end # ImagerStroke.roundEnd AND pathToDevice.form # 0 AND (v.p0.x = v.p1.x OR v.p0.y = v.p1.y) => { vd0: VEC = ImagerTransformation.Transform[m: pathToDevice, v: v.p0]; vd1: VEC = ImagerTransformation.Transform[m: pathToDevice, v: v.p1]; sConstant: BOOL ~ vd0.x = vd1.x; fConstant: BOOL ~ vd0.y = vd1.y; d1: VEC ¬ vd0; d2: VEC ¬ vd1; IF d1.x > d2.x THEN {t: REAL ¬ d1.x; d1.x ¬ d2.x; d2.x ¬ t}; IF d1.y > d2.y THEN {t: REAL ¬ d1.y; d1.y ¬ d2.y; d2.y ¬ t}; { square: BOOL ~ (end # ImagerStroke.buttEnd); ds: REAL ~ IF square OR sConstant THEN pen.bounds.x ELSE 0.0; df: REAL ~ IF square OR fConstant THEN pen.bounds.y ELSE 0.0; s0: INTEGER ~ ClippedRound[d1.x-ds, clipper.clipBox.min.s, clipper.clipBox.max.s]; s1: INTEGER ~ ClippedRound[d2.x+ds, clipper.clipBox.min.s, clipper.clipBox.max.s]; f0: INTEGER ~ ClippedRound[d1.y-df, clipper.clipBox.min.f, clipper.clipBox.max.f]; f1: INTEGER ~ ClippedRound[d2.y+df, clipper.clipBox.min.f, clipper.clipBox.max.f]; IF s0 < s1 AND f0 < f1 THEN { Blend: PROC [u0, u1, d, start, stop: REAL, min, max: INTEGER] RETURNS [ARRAY [0..1] OF INTEGER] = { v0: REAL ¬ (u0*(length-start) + u1*(start))/length; v1: REAL ¬ (u0*(length-stop) + u1*(stop))/length; IF v0 > v1 THEN {t: REAL ¬ v0; v0 ¬ v1; v1 ¬ t}; RETURN [[ ClippedRound[v0-d, min, max], ClippedRound[v1+d, min, max] ]] }; Boxes: PROC [action: PROC[SF.Box]] ~ { SConstantDash: PROC [start, stop: REAL] = { f: ARRAY [0..1] OF INTEGER = Blend[vd0.y, vd1.y, df, start, stop, clipper.clipBox.min.f, clipper.clipBox.max.f]; ImagerManhattan.ClipBoxToMask[box: [min: [s0, f[0]], max: [s1, f[1]]], mask: clipper.clipMask, action: action]; }; FConstantDash: PROC [start, stop: REAL] = { s: ARRAY [0..1] OF INTEGER = Blend[vd0.x, vd1.x, ds, start, stop, clipper.clipBox.min.s, clipper.clipBox.max.s]; ImagerManhattan.ClipBoxToMask[box: [min: [s[0], f0], max: [s[1], f1]], mask: clipper.clipMask, action: action]; }; MapDashSegments[patternLen, pattern, offset, length, IF sConstant THEN SConstantDash ELSE FConstantDash]; }; device.class.MaskBoxes[device: device, bounds: [min: [s0, f0], max: [s1, f1]], boxes: Boxes]; RETURN; }; }; }; ENDCASE => { EachDash: PROC [start, stop: REAL] = { Blend: PROC [r: REAL] RETURNS [VEC] = { s: REAL = length-r; RETURN [[(v.p0.x*s+v.p1.x*r)/length, (v.p0.y*s+v.p1.y*r)/length]] }; StandardMaskVector[ device: device, p1: Blend[start], p2: Blend[stop], pointsToDevice: pathToDevice, end: end, pen: pen ]; }; MapDashSegments[patternLen, pattern, offset, length, EachDash]; RETURN; }; }; }; device.works.MaskStroke[device: device, path: DashedPath, closed: FALSE, pathToDevice: pathToDevice, end: end, joint: joint, miterLimit: miterLimit, pen: pen]; }; MapDashSegments: PROC [patternLen: NAT, pattern: PROC [NAT] RETURNS [REAL], offset: REAL, length: REAL, action: PROC [start, stop: REAL]] = { prev: REAL ¬ -1.0; -- To make sure we make progress p: REAL ¬ 0.0; -- End of current dash or space q: REAL ¬ 0.0; -- Start of current dash or space k: NAT ¬ 0; -- Index into pattern state: [0..1] ¬ 0; UNTIL p > offset DO pk: REAL = pattern[k]; IF pk < 0.0 THEN GOTO BadPattern; p ¬ p + pk; k ¬ k + 1; IF k = patternLen THEN { k ¬ 0; IF p <= prev THEN GOTO NoProgress; prev ¬ p }; state ¬ 1-state; ENDLOOP; p ¬ p - offset; prev ¬ -1.0; UNTIL q >= length DO pk: REAL = pattern[k]; IF pk < 0.0 THEN GOTO BadPattern; IF state = 1 THEN { action[q, MIN[p, length]] }; q ¬ p; p ¬ p + pk; k ¬ k + 1; IF k = patternLen THEN { k ¬ 0; IF p <= prev THEN GOTO NoProgress; prev ¬ p }; state ¬ 1-state; ENDLOOP; EXITS BadPattern => { ERROR ImagerError.Error[[$illegalArguments, "Negative element in dash pattern"]]; }; NoProgress => { }; }; StandardMaskVector: PUBLIC PROC [device: Device, p1, p2: VEC, pointsToDevice: Transformation, end: ImagerStroke.EndCode, pen: ImagerPen.Pen] = { clipper: DeviceClipper ~ device.worksState.clipper; IF clipper.clipMask # NIL THEN { IF end # ImagerStroke.roundEnd AND pointsToDevice.form # 0 AND (p1.x = p2.x OR p1.y = p2.y) THEN { d1: VEC ¬ ImagerTransformation.Transform[m: pointsToDevice, v: p1]; d2: VEC ¬ ImagerTransformation.Transform[m: pointsToDevice, v: p2]; sConstant: BOOL ~ d1.x = d2.x; fConstant: BOOL ~ d1.y = d2.y; IF d1.x > d2.x THEN {t: REAL ¬ d1.x; d1.x ¬ d2.x; d2.x ¬ t}; IF d1.y > d2.y THEN {t: REAL ¬ d1.y; d1.y ¬ d2.y; d2.y ¬ t}; IF sConstant AND fConstant THEN { IF end = ImagerStroke.warningSquareEnd THEN { ImagerStroke.SignalSquareEndWithNoDirection[]; }; RETURN; }; BEGIN square: BOOL ~ (end # ImagerStroke.buttEnd); ds: REAL ~ IF square OR sConstant THEN pen.bounds.x ELSE 0.0; df: REAL ~ IF square OR fConstant THEN pen.bounds.y ELSE 0.0; s0: INTEGER ~ ClippedRound[d1.x-ds, clipper.clipBox.min.s, clipper.clipBox.max.s]; s1: INTEGER ~ ClippedRound[d2.x+ds, clipper.clipBox.min.s, clipper.clipBox.max.s]; f0: INTEGER ~ ClippedRound[d1.y-df, clipper.clipBox.min.f, clipper.clipBox.max.f]; f1: INTEGER ~ ClippedRound[d2.y+df, clipper.clipBox.min.f, clipper.clipBox.max.f]; IF s0 < s1 AND f0 < f1 THEN { box: SF.Box ~ [min: [s0, f0], max: [s1, f1]]; Boxes: PROC [action: PROC[SF.Box]] ~ { ImagerManhattan.ClipBoxToMask[box: box, mask: clipper.clipMask, action: action]; }; device.class.MaskBoxes[device: device, bounds: box, boxes: Boxes]; }; END; } ELSE { StrokePath: PathProc ~ { ImagerStroke.PathFromVector[p0: p1, p1: p2, m: pointsToDevice, pen: pen, end: end, moveTo: moveTo, lineTo: lineTo]; }; device.works.MaskFill[device: device, path: StrokePath, oddWrap: FALSE, pathToDevice: NIL]; }; }; }; bigTranslate: REAL ¬ REAL[LAST[INT]/2]; IsAllInteger: PUBLIC PROC [m: Transformation] RETURNS [BOOL] = { Is: PROC [r: REAL] RETURNS [BOOL] = { IF r IN [-worryReal..worryReal] THEN { ir: INT = Real.Round[r]; IF r = ir THEN RETURN[TRUE]; IF RealFns.AlmostEqual[r, ir, -18] THEN RETURN[TRUE]; }; RETURN[FALSE]; }; RETURN[Is[m.a] AND Is[m.b] AND ABS[m.c] <= bigTranslate AND Is[m.d] AND Is[m.e] AND ABS[m.f] <= bigTranslate]; }; GetSampleMapClippedTransformedBox: PROC [map: SampleMap, clip: SF.Box, m: Transformation] RETURNS [SF.Box] = { box: SF.Box = ImagerSample.GetBox[map]; IF m.form = 3 THEN { ts: INT = Real.Round[m.c]; tf: INT = Real.Round[m.f]; clipped: SF.Box = ClippedBounds[clipBox: clip, p0: [box.min.s + ts, box.min.f + tf], p1: [box.max.s + ts, box.max.f + tf]]; RETURN[clipped] } ELSE { p0: VEC ~ ImagerTransformation.Transform[m, [box.min.s, box.min.f]]; p1: VEC ~ ImagerTransformation.Transform[m, [box.max.s, box.max.f]]; clipped: SF.Box = ClippedBounds[clipBox: clip, p0: [Real.Round[p0.x], Real.Round[p0.y]], p1: [Real.Round[p1.x], Real.Round[p1.y]]]; RETURN[clipped] }; }; IntVec: TYPE = RECORD [s, f: INT]; ClippedBounds: PROC [clipBox: SF.Box, p0, p1: IntVec] RETURNS [SF.Box] = { IF p0.s > p1.s THEN {t: INT ¬ p0.s; p0.s ¬ p1.s; p1.s ¬ t}; IF p0.f > p1.f THEN {t: INT ¬ p0.f; p0.f ¬ p1.f; p1.f ¬ t}; p0.s ¬ MAX[p0.s, clipBox.min.s]; p0.f ¬ MAX[p0.f, clipBox.min.f]; p1.s ¬ MIN[p1.s, clipBox.max.s]; p1.f ¬ MIN[p1.f, clipBox.max.f]; IF p0.s < p1.s AND p0.f < p1.f THEN RETURN[[min: [INTEGER[p0.s], INTEGER[p0.f]], max: [INTEGER[p1.s], INTEGER[p1.f]]]] ELSE RETURN[[min: clipBox.min, max: clipBox.min]]; }; DMaskBitmap: PUBLIC -- ImagerDeviceProcs -- PROC [device: Device, bitmap: ImagerSample.SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] = { EachBox: PROC [box: SF.Box] = { visibleBitmap: SampleMap ~ ImagerSample.ReIndex[map: bitmap, delta: delta, box: box]; visibleBounds: SF.Box ~ ImagerSample.GetBox[visibleBitmap]; LittleBoxes: SF.BoxGenerator = { ImagerSample.BoxesFromBitmap[map: visibleBitmap, boxAction: boxAction]; }; IF SF.Nonempty[visibleBounds] THEN device.class.MaskBoxes[device: device, bounds: visibleBounds, boxes: LittleBoxes]; TRUSTED { ImagerSample.ReleaseDescriptor[visibleBitmap] }; }; boxes[EachBox]; }; ResampleCellProc: TYPE ~ UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [nz: CARD]; MaskBitmapHelp: UNSAFE PROC [device: Device, bitmap: SampleMap, bitsToDevice: Transformation, sSizeSrc, fSizeSrc: NAT, resampleCell: ResampleCellProc] = UNCHECKED { vDst: VEC ~ ImagerTransformation.TransformVec[bitsToDevice, [sSizeSrc, fSizeSrc]]; sDeltaDst: INT ~ RealInline.MCRound[vDst.x]; fDeltaDst: INT ~ RealInline.MCRound[vDst.y]; sSizeDst: NAT ~ ABS[sDeltaDst]; fSizeDst: NAT ~ ABS[fDeltaDst]; check: [0..0] ~ Basics.BITOR[fSizeSrc, fSizeDst] MOD BITS[WORD]; clipper: DeviceClipper ~ device.worksState.clipper; clip: ImagerManhattan.Polygon ¬ ImagerManhattan.DestructiveIntersection[ ImagerManhattan.CreateFromBox[ GetSampleMapClippedTransformedBox[bitmap, clipper.clipBox, bitsToDevice] ], clipper.clipMask ]; IF clip # NIL THEN { deltaSrcForSlowDst: VEC ~ ImagerTransformation.InverseTransformVec[bitsToDevice, [sSizeDst, 0]]; deltaSrcForFastDst: VEC ~ ImagerTransformation.InverseTransformVec[bitsToDevice, [0, fSizeDst]]; sDeltaSrcForSlowDst: INT ~ RealInline.MCRound[deltaSrcForSlowDst.x]; fDeltaSrcForSlowDst: INT ~ RealInline.MCRound[deltaSrcForSlowDst.y]; sDeltaSrcForFastDst: INT ~ RealInline.MCRound[deltaSrcForFastDst.x]; fDeltaSrcForFastDst: INT ~ RealInline.MCRound[deltaSrcForFastDst.y]; Boxes: SAFE PROC [action: PROC [SF.Box]] = CHECKED { FOR tail: LIST OF SF.Box ¬ clip, tail.rest UNTIL tail = NIL DO action[tail.first]; ENDLOOP; }; box: SF.Box ~ ImagerManhattan.BoundingBox[clip]; sSize: NAT ~ box.max.s-box.min.s; fSize: NAT ~ box.max.f-box.min.f; sSizePadded: NAT ~ ((sSize+(sSizeDst-1))/sSizeDst)*sSizeDst; fSizePadded: NAT ~ ((fSize+(fSizeDst-1))/fSizeDst)*fSizeDst; buffer: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[ [min: box.min, max: [box.min.s+sSizePadded, box.max.f+fSizePadded]] ]; sMinSrcOriented: INT ~ MIN[sDeltaSrcForSlowDst, sDeltaSrcForFastDst]; fMinSrcOriented: INT ~ MIN[fDeltaSrcForSlowDst, fDeltaSrcForFastDst]; srcOriented: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[min: [sMinSrcOriented, fMinSrcOriented], max: [s: sMinSrcOriented+sSizeSrc, f: fMinSrcOriented+fSizeSrc]]]; srcOrientedPtr: POINTER TO WORD ~ ImagerSample.GetBase[srcOriented].word; dstPtr0: POINTER TO WORD ¬ ImagerSample.GetBase[buffer].word; fUnitsDst: CARD ~ fSizeDst/BITS[UNIT]; dstupl: CARD ~ ImagerSample.GetBitsPerLine[buffer]/BITS[UNIT]; srcOrientedUpl: CARD ¬ fSizeSrc/BITS[UNIT]; p0: VEC ~ ImagerTransformation.InverseTransform[bitsToDevice, [box.min.s, box.min.f]]; sSrc0: INTEGER ¬ Real.Round[p0.x]; fSrc0: INTEGER ¬ Real.Round[p0.y]; this: ImagerManhattan.Polygon ¬ NIL; live, lastLive: ImagerManhattan.Polygon ¬ NIL; goodBase: POINTER ¬ NIL; -- Try to pull directly from buffer when possible goodSrcBox: SF.Box ¬ []; -- denotes legal values for [sSrc, fSrc] for direct access goodUpl: CARDINAL ¬ 0; -- units per source line for direct access WITH bitmap SELECT FROM raster: ImagerSample.RasterSampleMap => { bpl: CARDINAL ¬ raster.GetBitsPerLine; IF bpl MOD BITS[WORD] = 0 THEN { goodUpl ¬ bpl / BITS[UNIT]; goodBase ¬ raster.GetBase.word; goodSrcBox ¬ raster.GetBox; goodSrcBox.min.s ¬ goodSrcBox.min.s-sMinSrcOriented; goodSrcBox.min.f ¬ goodSrcBox.min.f-fMinSrcOriented-raster.GetBase.bit; goodSrcBox.max.s ¬ goodSrcBox.max.s-(sMinSrcOriented+sSizeSrc); goodSrcBox.max.f ¬ goodSrcBox.max.f-(fMinSrcOriented+fSizeSrc); }; }; ENDCASE; FOR s: INTEGER ¬ box.min.s, s + sSizeDst UNTIL s >= box.max.s DO dstPtr: POINTER TO WORD ¬ dstPtr0 + (s-box.min.s)*dstupl; fLiveMin: INTEGER ¬ INTEGER.LAST; fLiveMax: INTEGER ¬ INTEGER.FIRST; sSrc: INTEGER ¬ sSrc0; fSrc: INTEGER ¬ fSrc0; FOR f: INTEGER ¬ box.min.f, f + fSizeDst UNTIL f >= box.max.f DO nz: WORD ¬ 0; fSrcOffset: CARDINAL; IF goodBase # NIL AND sSrc IN [goodSrcBox.min.s..goodSrcBox.max.s] AND fSrc IN [goodSrcBox.min.f..goodSrcBox.max.f] AND (fSrcOffset ¬ LOOPHOLE[fSrc-goodSrcBox.min.f, CARDINAL]) MOD BITS[WORD] = 0 THEN { src: POINTER ~ goodBase + LOOPHOLE[sSrc-goodSrcBox.min.s, CARDINAL]*goodUpl + fSrcOffset/BITS[UNIT]; nz ¬ resampleCell[src: src, srcupl: goodUpl, dst: dstPtr, dstupl: dstupl]; } ELSE { ImagerSample.Clear[srcOriented]; ImagerSample.Transfer[dst: srcOriented, src: bitmap, delta: [-sSrc, -fSrc]]; nz ¬ resampleCell[src: srcOrientedPtr, srcupl: srcOrientedUpl, dst: dstPtr, dstupl: dstupl]; }; IF nz # 0 THEN { IF f < fLiveMin THEN fLiveMin ¬ f ELSE IF f > fLiveMax THEN { new: ImagerManhattan.Polygon ~ ImagerManhattan.CreateFromBox[[[s, fLiveMin], [s+sSizeDst, fLiveMax]]]; IF lastLive = NIL THEN live ¬ lastLive ¬ new ELSE lastLive ¬ lastLive.rest ¬ new; fLiveMin ¬ fLiveMax ¬ f; }; IF f+fSizeDst > fLiveMax THEN fLiveMax ¬ f+fSizeDst; }; dstPtr ¬ dstPtr + fUnitsDst; sSrc ¬ sSrc + sDeltaSrcForFastDst; fSrc ¬ fSrc + fDeltaSrcForFastDst; ENDLOOP; sSrc0 ¬ sSrc0 + sDeltaSrcForSlowDst; fSrc0 ¬ fSrc0 + fDeltaSrcForSlowDst; IF fLiveMin < fLiveMax THEN { new: ImagerManhattan.Polygon ~ ImagerManhattan.CreateFromBox[[[s, fLiveMin], [s+sSizeDst, fLiveMax]]]; IF lastLive = NIL THEN live ¬ lastLive ¬ new ELSE lastLive ¬ lastLive.rest ¬ new; }; ENDLOOP; clip ¬ ImagerManhattan.DestructiveIntersection[clip, live]; (IF device.state.allow.bitmap THEN device.class.MaskBitmap ELSE DMaskBitmap)[ device: device, bitmap: buffer, delta: [0, 0], bounds: box, boxes: Boxes ]; ImagerSample.ReleaseScratchMap[buffer]; ImagerSample.ReleaseScratchMap[srcOriented]; ImagerManhattan.Destroy[clip]; ImagerManhattan.Destroy[live]; ImagerManhattan.Destroy[this]; }; }; SpecialMaskBitmapProc: TYPE ~ PROC [device: Device, bitmap: SampleMap, bitsToDevice: Transformation] RETURNS [BOOL ¬ FALSE]; SMB: TYPE ~ REF SpecialMaskBitmapRep; SpecialMaskBitmapRep: TYPE ~ RECORD [ templateT: Transformation, specialMaskBitmap: SpecialMaskBitmapProc ]; maxValidForm: NAT = 10; -- see ImagerTransformationImpl.CheckForm smbRegistry: ARRAY [0..maxValidForm] OF LIST OF SMB ¬ ALL[NIL]; RegisterSpecialMaskBitmap: ENTRY PROC [scale: REAL, rotation: REAL, proc: SpecialMaskBitmapProc] RETURNS [SMB] ~ { m: Transformation ~ ImagerTransformation.Scale[scale].PostRotate[rotation]; form: [0..maxValidForm] ~ m.form; smb: SMB ~ NEW[SpecialMaskBitmapRep ¬ [m, proc]]; smbRegistry[form] ¬ CONS[smb, smbRegistry[form]]; RETURN [smb] }; BlockRotate180: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { nz: WORD ¬ 0; d: POINTER TO WORD32 ¬ dst + 32 * dstupl; s: POINTER TO WORD32 ¬ src; m8: WORD32 ¬ 00FF00FFH; m4: WORD32 ¬ 0F0F0F0FH; m2: WORD32 ¬ 33333333H; m1: WORD32 ¬ 55555555H; w: WORD32; THROUGH [0..32) DO d ¬ d - dstupl; w ¬ s^; nz ¬ Basics.BITOR[nz, w]; w ¬ w/2**16 + w*2**16; w ¬ Basics.BITAND[m8, w/2**8] + Basics.BITAND[Basics.BITNOT[m8], w*2**8]; w ¬ Basics.BITAND[m4, w/2**4] + Basics.BITAND[Basics.BITNOT[m4], w*2**4]; w ¬ Basics.BITAND[m2, w/2**2] + Basics.BITAND[Basics.BITNOT[m2], w*2**2]; w ¬ Basics.BITAND[m1, w/2**1] + Basics.BITAND[Basics.BITNOT[m1], w*2**1]; d­ ¬ w; s ¬ s + srcupl; ENDLOOP; RETURN [nz] }; mb1to1: SMB ~ RegisterSpecialMaskBitmap[1.0, 0, MB1to1]; MB1to1: SpecialMaskBitmapProc ~ { clipper: DeviceClipper ~ device.worksState.clipper; box: SF.Box = GetSampleMapClippedTransformedBox[bitmap, clipper.clipBox, bitsToDevice]; Boxes: PROC [action: PROC [SF.Box]] = { ImagerManhattan.ClipBoxToMask[box: box, mask: clipper.clipMask, action: action] }; IF SF.Nonempty[box] THEN { (IF device.state.allow.bitmap THEN device.class.MaskBitmap ELSE DMaskBitmap)[ device: device, bitmap: bitmap, delta: [s: Real.Round[bitsToDevice.c], f: Real.Round[bitsToDevice.f]], bounds: box, boxes: Boxes]; }; RETURN [TRUE] }; mb1to1L: SMB ~ RegisterSpecialMaskBitmap[1.0, 90, MB1to1L]; MB1to1L: SpecialMaskBitmapProc ~ TRUSTED { RotateLeft: UNSAFE PROC [src: POINTER TO WORD, srcPitch: INT, dst: POINTER TO WORD, dstPitch: INT] RETURNS [WORD] ~ BlockRotate.RotateLeft; MaskBitmapHelp[device, bitmap, bitsToDevice, 32, 32, LOOPHOLE[RotateLeft]]; RETURN [TRUE] }; mb1to1R: SMB ~ RegisterSpecialMaskBitmap[1.0, -90, MB1to1R]; MB1to1R: SpecialMaskBitmapProc ~ TRUSTED { RotateRight: UNSAFE PROC [src: POINTER TO WORD, srcPitch: INT, dst: POINTER TO WORD, dstPitch: INT] RETURNS [WORD] ~ BlockRotate.RotateRight; MaskBitmapHelp[device, bitmap, bitsToDevice, 32, 32, LOOPHOLE[RotateRight]]; RETURN [TRUE] }; mb1to1I: SMB ~ RegisterSpecialMaskBitmap[1.0, 180, MB1to1I]; MB1to1I: SpecialMaskBitmapProc ~ TRUSTED { MaskBitmapHelp[device, bitmap, bitsToDevice, 32, 32, BlockRotate180]; RETURN [TRUE] }; resampleByte2to3: PACKED ARRAY BYTE OF [0..2**12) ¬ ALL[0]; initByte2to3: BOOL ~ InitByte2to3[]; InitByte2to3: PROC RETURNS [BOOL] ~ { b: ARRAY [0..4) OF [0..8) ~ [0, 3, 6, 7]; FOR i: [0..4) IN [0..4) DO FOR j: [0..4) IN [0..4) DO FOR k: [0..4) IN [0..4) DO FOR l: [0..4) IN [0..4) DO resampleByte2to3[(((i*4+j)*4+k)*4+l)] ¬ (((b[i]*8+b[j])*8+b[k])*8+b[l]); ENDLOOP; ENDLOOP; ENDLOOP; ENDLOOP; RETURN [TRUE] }; ResampleCell2to3: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { x: POINTER TO PACKED ARRAY BYTE OF [0..2**12) ¬ @resampleByte2to3; srcP: POINTER TO PACKED ARRAY [0..2) OF PACKED ARRAY[0..4) OF BYTE ¬ LOOPHOLE[src]; dstP: POINTER TO PACKED ARRAY [0..3) OF Basics.FWORD ¬ LOOPHOLE[dst]; src0: PACKED ARRAY[0..4) OF BYTE ¬ srcP[0]; src1: PACKED ARRAY[0..4) OF BYTE ¬ srcP[1]; dst00, dst01, dst02: CARD32; dst20, dst21, dst22: CARD32; nz: CARD32 ¬ Basics.BITOR[LOOPHOLE[src0], LOOPHOLE[src1]]; IF nz = 0 THEN { dst00 ¬ dst01 ¬ dst02 ¬ 0 } ELSE { dst00 ¬ x[src0[0]]*(2**20) + x[src0[1]]*(2**8) + (dst01 ¬ x[src0[2]])/(2**4); dst01 ¬ dst01*(2**28) + x[src0[3]]*(2**16) + x[src1[0]]*(2**4) + (dst02 ¬ x[src1[1]])/(2**8); dst02 ¬ dst02*(2**24) + x[src1[2]]*(2**12) + x[src1[3]]; }; dstP[0] ¬ Basics.FFromCard32[dst00]; dstP[1] ¬ Basics.FFromCard32[dst01]; dstP[2] ¬ Basics.FFromCard32[dst02]; srcP ¬ srcP + srcupl; src0 ¬ srcP[0]; src1 ¬ srcP[1]; dst20 ¬ Basics.BITOR[LOOPHOLE[src0], LOOPHOLE[src1]]; nz ¬ Basics.BITOR[nz, dst20]; IF dst20 = 0 THEN { dst20 ¬ dst21 ¬ dst22 ¬ 0 } ELSE { dst20 ¬ x[src0[0]]*(2**20) + x[src0[1]]*(2**8) + (dst21 ¬ x[src0[2]])/(2**4); dst21 ¬ dst21*(2**28) + x[src0[3]]*(2**16) + x[src1[0]]*(2**4) + (dst22 ¬ x[src1[1]])/(2**8); dst22 ¬ dst22*(2**24) + x[src1[2]]*(2**12) + x[src1[3]]; }; dstP ¬ dstP + dstupl; dstP[0] ¬ Basics.FFromCard32[Basics.BITOR[dst00, dst20]]; dstP[1] ¬ Basics.FFromCard32[Basics.BITOR[dst01, dst21]]; dstP[2] ¬ Basics.FFromCard32[Basics.BITOR[dst02, dst22]]; dstP ¬ dstP + dstupl; dstP[0] ¬ Basics.FFromCard32[dst20]; dstP[1] ¬ Basics.FFromCard32[dst21]; dstP[2] ¬ Basics.FFromCard32[dst22]; RETURN [nz] }; mb2to3: SMB ~ RegisterSpecialMaskBitmap[1.5, 0, FaxyMaskBitmap]; FaxyMaskBitmap: SpecialMaskBitmapProc ~ { srcPtr: POINTER ¬ NIL; srcupl: NAT ¬ 0; WITH bitmap SELECT FROM bitmap: ImagerSample.RasterSampleMap => { base: ImagerSample.BitAddress ~ ImagerSample.GetBase[bitmap]; bpl: NAT ~ ImagerSample.GetBitsPerLine[bitmap]; IF (base.bit = 0 AND bpl MOD BITS[WORD] = 0) THEN { srcPtr ¬ LOOPHOLE[base.word]; srcupl ¬ bpl/BITS[UNIT]; }; }; ENDCASE; TRUSTED { clipper: DeviceClipper ~ device.worksState.clipper; tmpBitsToDevice: Transformation ~ ImagerTransformation.Translate[[bitsToDevice.c, bitsToDevice.f]]; srcBox: SF.Box ¬ ImagerSample.GetBox[bitmap]; srcSize: SF.Vec ¬ SF.Size[srcBox]; dstMin: SF.Vec ¬ [srcBox.min.s*3/2, srcBox.min.f*3/2]; dstSize: SF.Vec ¬ [ s: MakeMultiple[(srcSize.s*3+1)/2, 3], f: MakeMultiple[(srcSize.f*3+1)/2, 32*3]]; tmpBox: SF.Box ~ [min: dstMin, max: [dstMin.s+dstSize.s, dstMin.f+dstSize.f]]; tmp: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[tmpBox]; dstPtr: POINTER ¬ ImagerSample.GetBase[tmp].word; dstupl: CARD ~ ImagerSample.GetBitsPerLine[tmp]/BITS[UNIT]; buf: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[[max: [2, 2*32]]]; nz: CARD ¬ 0; FOR s: INTEGER ¬ 0, s + 2 UNTIL s >= srcSize.s DO srcOffset: CARD ¬ 0; dstOffset: CARD ¬ 0; FOR f: INTEGER ¬ 0, f+64 UNTIL f >= srcSize.f DO IF srcPtr = NIL OR (s+2) > srcSize.s OR (f+64) > srcSize.f THEN { ImagerSample.Clear[buf]; ImagerSample.Transfer[dst: buf, src: bitmap, delta: [-(srcBox.min.s+s), -(srcBox.min.f+f)]]; nz ¬ Basics.BITOR[nz, ResampleCell2to3[ src: LOOPHOLE[buf.GetBase.word], srcupl: SIZE[PACKED ARRAY [0..8) OF BYTE], dst: dstPtr+dstOffset, dstupl: dstupl ]]; } ELSE { nz ¬ Basics.BITOR[nz, ResampleCell2to3[ src: srcPtr+srcOffset, srcupl: srcupl, dst: dstPtr+dstOffset, dstupl: dstupl ]]; }; srcOffset ¬ srcOffset + SIZE[PACKED ARRAY [0..8) OF BYTE]; dstOffset ¬ dstOffset + SIZE[PACKED ARRAY [0..12) OF BYTE]; ENDLOOP; IF srcPtr # NIL THEN srcPtr ¬ srcPtr + 2*srcupl; dstPtr ¬ dstPtr + 3*dstupl; ENDLOOP; IF nz # 0 THEN { IF NOT MB1to1[device, tmp, tmpBitsToDevice] THEN ERROR; }; ImagerSample.ReleaseScratchMap[buf]; ImagerSample.ReleaseScratchMap[tmp]; ImagerTransformation.Destroy[tmpBitsToDevice]; RETURN[TRUE]; }; }; mb2to3L: SMB ~ RegisterSpecialMaskBitmap[1.5, 90, MB2to3L]; MB2to3L: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE ~ PACKED ARRAY [0..2*BITS[WORD]) OF [0..1]; buf: ARRAY [0..BITS[WORD]) OF Line; RRCell2to3: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; bufupl: CARD ~ UNITS[Line]; dstupl3: CARD ~ 3*dstupl; nz0: CARD ¬ BlockRotate.RotateLeft[src: src, srcPitch: srcupl, dst: bp, dstPitch: UNITS[Line]]; nz1: CARD ¬ BlockRotate.RotateLeft[src: src + srcupl*BITS[WORD], srcPitch: srcupl, dst: bp + UNITS[WORD], dstPitch: UNITS[Line]]; IF Basics.BITOR[nz0, nz1] = 0 THEN RETURN [0]; THROUGH [0..BITS[WORD]/2) DO [] ¬ ResampleCell2to3[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 2*UNITS[Line]; dst ¬ dst + dstupl3; ENDLOOP; RETURN [1] }; MaskBitmapHelp[device, bitmap, bitsToDevice, 2*BITS[WORD], BITS[WORD], RRCell2to3]; RETURN [TRUE] }; mb2to3R: SMB ~ RegisterSpecialMaskBitmap[1.5, -90, MB2to3R]; MB2to3R: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE ~ PACKED ARRAY [0..2*BITS[WORD]) OF [0..1]; buf: ARRAY [0..BITS[WORD]) OF Line; RRCell2to3: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; bufupl: CARD ~ UNITS[Line]; dstupl3: CARD ~ 3*dstupl; nz0: CARD ¬ BlockRotate.RotateRight[src: src + srcupl*BITS[WORD], srcPitch: srcupl, dst: bp, dstPitch: UNITS[Line]]; nz1: CARD ¬ BlockRotate.RotateRight[src: src, srcPitch: srcupl, dst: bp + UNITS[WORD], dstPitch: UNITS[Line]]; IF Basics.BITOR[nz0, nz1] = 0 THEN RETURN [0]; THROUGH [0..BITS[WORD]/2) DO [] ¬ ResampleCell2to3[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 2*UNITS[Line]; dst ¬ dst + dstupl3; ENDLOOP; RETURN [1] }; MaskBitmapHelp[device, bitmap, bitsToDevice, 2*BITS[WORD], BITS[WORD], RRCell2to3]; RETURN [TRUE] }; mb2to3I: SMB ~ RegisterSpecialMaskBitmap[1.5, 180, MB2to3I]; MB2to3I: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE ~ PACKED ARRAY [0..2*BITS[WORD]) OF [0..1]; buf: ARRAY [0..BITS[WORD]) OF Line; ICell2to3: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; nz0: CARD ¬ BlockRotate180[src: src, srcupl: srcupl, dst: bp+UNITS[WORD], dstupl: UNITS[Line]]; nz1: CARD ¬ BlockRotate180[src: src+UNITS[WORD], srcupl: srcupl, dst: bp, dstupl: UNITS[Line]]; bufupl: CARD ~ UNITS[Line]; dstupl3: CARD ~ 3*dstupl; IF Basics.BITOR[nz0, nz1] = 0 THEN RETURN [0]; THROUGH [0..BITS[WORD]/2) DO [] ¬ ResampleCell2to3[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 2*UNITS[Line]; dst ¬ dst + dstupl3; ENDLOOP; RETURN [1] }; MaskBitmapHelp[device, bitmap, bitsToDevice, BITS[WORD], 2*BITS[WORD], ICell2to3]; RETURN [TRUE] }; ResampleCell2to1: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { srcP0: POINTER TO WORD ¬ LOOPHOLE[src]; srcP1: POINTER TO WORD ¬ LOOPHOLE[LOOPHOLE[srcP0, CARD]+srcupl]; dstP: POINTER TO WORD ¬ LOOPHOLE[dst]; w0: WORD ¬ Basics.BITOR[srcP0­, srcP1­]; w1: WORD ¬ Basics.BITOR[(srcP0+SIZE[WORD])­, (srcP1+SIZE[WORD])­]; m: WORD ¬ 55555555h; w0 ¬ Basics.BITOR[Basics.BITAND[w0, m], Basics.BITAND[w0 / 2, m]]; w1 ¬ Basics.BITOR[Basics.BITAND[w1, m], Basics.BITAND[w1 / 2, m]]; m ¬ 33333333h; w0 ¬ Basics.BITOR[Basics.BITAND[w0, m], Basics.BITAND[w0 / 2, m]]; w1 ¬ Basics.BITOR[Basics.BITAND[w1, m], Basics.BITAND[w1 / 2, m]]; m ¬ 0f0f0f0fh; w0 ¬ Basics.BITOR[Basics.BITAND[w0, m], Basics.BITAND[w0 / 4, m]]; w1 ¬ Basics.BITOR[Basics.BITAND[w1, m], Basics.BITAND[w1 / 4, m]]; m ¬ 00ff00ffh; w0 ¬ Basics.BITOR[Basics.BITAND[w0, m], Basics.BITAND[w0 / 16, m]]; w1 ¬ Basics.BITOR[Basics.BITAND[w1, m], Basics.BITAND[w1 / 16, m]]; m ¬ 0000ffffh; w0 ¬ Basics.BITOR[Basics.BITAND[w0, m], Basics.BITAND[w0 / 256, m]]; w1 ¬ Basics.BITOR[Basics.BITAND[w1, m], Basics.BITAND[w1 / 256, m]]; w0 ¬ w0 * (256*256) + w1; dstP­ ¬ w0; RETURN [w0]; }; mb2to1: SMB ~ RegisterSpecialMaskBitmap[0.5, 0, MB2to1]; MB2to1: SpecialMaskBitmapProc ~ TRUSTED { MaskBitmapHelp[device, bitmap, bitsToDevice, 2, 64, ResampleCell2to1]; RETURN [TRUE] }; mb2to1L: SMB ~ RegisterSpecialMaskBitmap[0.5, 90, MB2to1L]; MB2to1L: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE = ARRAY [0..1] OF WORD; Buf: TYPE ~ ARRAY [0..BITS[WORD]) OF Line; buf: Buf; RRCell2to1: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; bufupl: CARD ~ UNITS[Line]; nz0: CARD ¬ BlockRotate.RotateLeft[src: src, srcPitch: srcupl, dst: bp, dstPitch: UNITS[Line]]; nz1: CARD ¬ BlockRotate.RotateLeft[src: src + srcupl*BITS[WORD], srcPitch: srcupl, dst: bp + UNITS[WORD], dstPitch: UNITS[Line]]; IF Basics.BITOR[nz0, nz1] = 0 THEN RETURN [0]; THROUGH [0..BITS[WORD]/2) DO [] ¬ ResampleCell2to1[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 2*UNITS[Line]; dst ¬ dst + dstupl; ENDLOOP; RETURN [1]; }; MaskBitmapHelp[device, bitmap, bitsToDevice, BITS[Line], BITS[WORD], RRCell2to1]; RETURN [TRUE] }; mb2to1R: SMB ~ RegisterSpecialMaskBitmap[0.5, -90, MB2to1R]; MB2to1R: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE = ARRAY [0..1] OF WORD; Buf: TYPE ~ ARRAY [0..BITS[WORD]) OF Line; buf: Buf; RRCell2to1: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; bufupl: CARD ~ UNITS[Line]; nz0: CARD ¬ BlockRotate.RotateRight[src: src + srcupl*BITS[WORD], srcPitch: srcupl, dst: bp, dstPitch: UNITS[Line]]; nz1: CARD ¬ BlockRotate.RotateRight[src: src, srcPitch: srcupl, dst: bp + UNITS[WORD], dstPitch: UNITS[Line]]; IF Basics.BITOR[nz0, nz1] = 0 THEN RETURN [0]; THROUGH [0..BITS[WORD]/2) DO [] ¬ ResampleCell2to1[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 2*UNITS[Line]; dst ¬ dst + dstupl; ENDLOOP; RETURN [1] }; MaskBitmapHelp[device, bitmap, bitsToDevice, BITS[Line], BITS[WORD], RRCell2to1]; RETURN [TRUE] }; mb2to1I: SMB ~ RegisterSpecialMaskBitmap[0.5, 180, MB2to1I]; MB2to1I: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE = ARRAY [0..1] OF WORD; Buf: TYPE ~ ARRAY [0..BITS[WORD]) OF Line; buf: Buf; ICell2to1: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; nz0: CARD ¬ BlockRotate180[src: src, srcupl: srcupl, dst: bp+UNITS[WORD], dstupl: UNITS[Line]]; nz1: CARD ¬ BlockRotate180[src: src+UNITS[WORD], srcupl: srcupl, dst: bp, dstupl: UNITS[Line]]; bufupl: CARD ~ UNITS[Line]; IF Basics.BITOR[nz0, nz1] = 0 THEN RETURN [0]; THROUGH [0..BITS[WORD]/2) DO [] ¬ ResampleCell2to1[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 2*UNITS[Line]; dst ¬ dst + dstupl; ENDLOOP; RETURN [1] }; MaskBitmapHelp[device, bitmap, bitsToDevice, BITS[WORD], BITS[Line], ICell2to1]; RETURN [TRUE] }; ResampleCell4to5: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bpw: NAT = BITS[WORD]; sSizeSrc: NAT = 4; fSizeSrc: NAT = 4*bpw; sSizeDst: NAT = 5; fSizeDst: NAT = 5*bpw; SrcWord: TYPE ~ PACKED ARRAY [0..BYTES[WORD]) OF BYTE; SrcLine: TYPE ~ PACKED ARRAY [0..fSizeSrc/bpw) OF Basics.FWORD; srcP: POINTER TO SrcLine ¬ LOOPHOLE[src]; dstP: POINTER TO PACKED ARRAY [0..fSizeDst/bpw) OF Basics.FWORD ¬ LOOPHOLE[dst]; Fetch: UNSAFE PROC [k: [0..fSizeSrc/bpw)] RETURNS [CARD32] ~ INLINE {RETURN [Basics.Card32FromF[srcP[k]]]}; srcA: CARD32 ¬ Fetch[0]; srcB: CARD32 ¬ Fetch[1]; Munge: SAFE PROC [w: CARD32, m: CARD32, s: [0..31)] RETURNS [CARD32] ~ CHECKED INLINE{OPEN Basics; RETURN[ BITOR[BITAND[w, BITNOT[m]], BITRSHIFT[BITAND[w, m], s]] ]}; m4: CARD32 ~ 0000FFFFH; m2: CARD32 ~ Munge[00FF00FFH, m4, 4]; m1: CARD32 ~ Munge[Munge[0F0F0F0FH, m4, 4], 00FF00FFH, 2]; Spread: SAFE PROC [w: CARD32] RETURNS [CARD32] ~ CHECKED INLINE { w ¬ Munge[w, m4, 4]; w ¬ Munge[w, m2, 2]; w ¬ Munge[w, m1, 1]; RETURN [Basics.BITOR[w, w/2]] }; dstA0, dstA1, dstA2, dstA3, dstA4: CARD32 ¬ 0; dstB0, dstB1, dstB2, dstB3, dstB4: CARD32; THROUGH [0..4) DO srcA ¬ Fetch[0]; dstB0 ¬ Spread[srcA]; srcB ¬ Fetch[1]; dstB1 ¬ Spread[srcA * (2**(32-6)) + srcB / (2**6)]; srcA ¬ Fetch[2]; dstB2 ¬ Spread[srcB * (2**(32-13)) + srcA / (2**13)]; srcB ¬ Fetch[3]; dstB3 ¬ Spread[srcA * (2**(32-19)) + srcB / (2**19)]; dstB4 ¬ Spread[srcB * (2**(32-26))]; dstP[0] ¬ Basics.FFromCard32[Basics.BITOR[dstA0, dstB0]]; dstP[1] ¬ Basics.FFromCard32[Basics.BITOR[dstA1, dstB1]]; dstP[2] ¬ Basics.FFromCard32[Basics.BITOR[dstA2, dstB2]]; dstP[3] ¬ Basics.FFromCard32[Basics.BITOR[dstA3, dstB3]]; dstP[4] ¬ Basics.FFromCard32[Basics.BITOR[dstA4, dstB4]]; dstA0 ¬ dstB0; dstA1 ¬ dstB1; dstA2 ¬ dstB2; dstA3 ¬ dstB3; dstA4 ¬ dstB4; dstP ¬ dstP + dstupl; srcP ¬ srcP + srcupl; ENDLOOP; dstP[0] ¬ Basics.FFromCard32[dstB0]; dstP[1] ¬ Basics.FFromCard32[dstB1]; dstP[2] ¬ Basics.FFromCard32[dstB2]; dstP[3] ¬ Basics.FFromCard32[dstB3]; dstP[4] ¬ Basics.FFromCard32[dstB4]; RETURN [1] }; mb4to5: SMB ~ RegisterSpecialMaskBitmap[1.25, 0, MB4to5]; MB4to5: SpecialMaskBitmapProc ~ TRUSTED { MaskBitmapHelp[device, bitmap, bitsToDevice, 4, 4*32, ResampleCell4to5]; RETURN [TRUE] }; mb4to5L: SMB ~ RegisterSpecialMaskBitmap[1.25, 90, MB4to5L]; MB4to5L: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE ~ PACKED ARRAY [0..4*BITS[WORD]) OF [0..1]; buf: ARRAY [0..BITS[WORD]) OF Line; RLCell4to5: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; bufupl: CARD ~ UNITS[Line]; nz: CARD ¬ 0; FOR i: NAT IN [0..4) DO nz ¬ Basics.BITOR[nz, BlockRotate.RotateLeft[src: src, srcPitch: srcupl, dst: bp, dstPitch: UNITS[Line]] ]; src ¬ src + srcupl*BITS[WORD]; bp ¬ bp + UNITS[WORD]; ENDLOOP; IF nz # 0 THEN { dstupl5: CARD ~ 5*dstupl; bp ¬ @buf; THROUGH [0..BITS[WORD]/4) DO [] ¬ ResampleCell4to5[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 4*UNITS[Line]; dst ¬ dst + dstupl5; ENDLOOP; }; RETURN [nz] }; MaskBitmapHelp[device, bitmap, bitsToDevice, 4*BITS[WORD], BITS[WORD], RLCell4to5]; RETURN [TRUE] }; mb4to5R: SMB ~ RegisterSpecialMaskBitmap[1.25, -90, MB4to5R]; MB4to5R: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE ~ PACKED ARRAY [0..4*BITS[WORD]) OF [0..1]; buf: ARRAY [0..BITS[WORD]) OF Line; RRCell4to5: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { bp: POINTER ¬ @buf; bufupl: CARD ~ UNITS[Line]; nz: CARD ¬ 0; src ¬ src + srcupl*BITS[WORD]*4; FOR i: NAT IN [0..4) DO src ¬ src - srcupl*BITS[WORD]; nz ¬ Basics.BITOR[nz, BlockRotate.RotateRight[src: src, srcPitch: srcupl, dst: bp, dstPitch: UNITS[Line]] ]; bp ¬ bp + UNITS[WORD]; ENDLOOP; IF nz # 0 THEN { dstupl5: CARD ~ 5*dstupl; bp ¬ @buf; THROUGH [0..BITS[WORD]/4) DO [] ¬ ResampleCell4to5[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 4*UNITS[Line]; dst ¬ dst + dstupl5; ENDLOOP; }; RETURN [nz] }; MaskBitmapHelp[device, bitmap, bitsToDevice, 4*BITS[WORD], BITS[WORD], RRCell4to5]; RETURN [TRUE] }; mb4to5I: SMB ~ RegisterSpecialMaskBitmap[1.25, 180, MB4to5I]; MB4to5I: SpecialMaskBitmapProc ~ TRUSTED { Line: TYPE ~ PACKED ARRAY [0..4*BITS[WORD]) OF [0..1]; buf: ARRAY [0..BITS[WORD]) OF Line; ICell4to5: UNSAFE PROC [src: POINTER, srcupl: CARD, dst: POINTER, dstupl: CARD] RETURNS [WORD] ~ UNCHECKED { nz: CARD ¬ 0; bp: POINTER ¬ @buf + 4*UNITS[WORD]; FOR i: NAT IN [0..4) DO bp ¬ bp - UNITS[WORD]; nz ¬ Basics.BITOR[nz, BlockRotate180[src: src, srcupl: srcupl, dst: bp, dstupl: UNITS[Line]] ]; src ¬ src + UNITS[WORD]; ENDLOOP; IF nz # 0 THEN { dstupl5: CARD ~ 5*dstupl; bp ¬ @buf; THROUGH [0..BITS[WORD]/4) DO [] ¬ ResampleCell4to5[src: bp, srcupl: UNITS[Line], dst: dst, dstupl: dstupl]; bp ¬ bp + 4*UNITS[Line]; dst ¬ dst + dstupl5; ENDLOOP; }; RETURN [nz] }; MaskBitmapHelp[device, bitmap, bitsToDevice, 32, 4*32, ICell4to5]; RETURN [TRUE] }; BoxyMaskBitmap: PROC [device: Device, bitmap: SampleMap, bitsToDevice: Transformation] RETURNS [BOOL ¬ FALSE] = { IF (bitsToDevice.form # 0 AND (device.state.allow.unorderedBoxes OR bitsToDevice.form = 3) AND IsAllInteger[bitsToDevice]) THEN { clipper: DeviceClipper ~ device.worksState.clipper; a: INTEGER = Real.Round[bitsToDevice.a]; b: INTEGER = Real.Round[bitsToDevice.b]; c: INT = Real.Round[bitsToDevice.c]; d: INTEGER = Real.Round[bitsToDevice.d]; e: INTEGER = Real.Round[bitsToDevice.e]; f: INT = Real.Round[bitsToDevice.f]; Map: PROC [p: SF.Vec] RETURNS [IntVec] = { -- for SMul RETURN[[ImagerSys.SMul[a, p.s] + ImagerSys.SMul[b, p.f] + c, ImagerSys.SMul[d, p.s] + ImagerSys.SMul[e, p.f] + f]] }; srcBox: SF.Box = ImagerSample.GetBox[bitmap]; -- Overall bounds, in source bitmap coordinates; dstBox: SF.Box = ClippedBounds[clipper.clipBox, Map[srcBox.min], Map[srcBox.max]]; -- Overall bounds, in device coordinates; GenerateBoxes: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] = { FOR each: LIST OF SF.Box ¬ clipper.clipMask, each.rest UNTIL each = NIL DO --SF.BoxList visibleBox: SF.Box = SF.Intersect[dstBox, each.first]; IF SF.Nonempty[visibleBox] THEN { srcVisibleRealBox: ImagerBox.Box = ImagerBox.BoxFromRectangle[ ImagerTransformation.InverseTransformRectangle[ bitsToDevice, [x: visibleBox.min.s, y: visibleBox.min.f, w: visibleBox.max.s - visibleBox.min.s, h: visibleBox.max.f - visibleBox.min.f]]]; srcVisibleBox: SF.Box = ClippedBounds[ clipBox: srcBox, p0: [Real.Floor[srcVisibleRealBox.xmin], Real.Floor[srcVisibleRealBox.ymin]], p1: [Real.Ceiling[srcVisibleRealBox.xmax], Real.Ceiling[srcVisibleRealBox.ymax]]]; ProcessPartiallyVisibleBox: --SF.BoxAction-- PROC [box: SF.Box] = { clippedBox: SF.Box = ClippedBounds[ visibleBox, Map[box.min], Map[box.max]]; IF SF.Nonempty[clippedBox] THEN boxAction[clippedBox]; }; visibleBitmap: SampleMap ¬ ImagerSample.Clip[bitmap, srcVisibleBox]; ImagerSample.BoxesFromBitmap[map: visibleBitmap, boxAction: ProcessPartiallyVisibleBox]; TRUSTED { ImagerSample.ReleaseDescriptor[visibleBitmap] }; }; ENDLOOP; }; IF SF.Nonempty[dstBox] THEN device.class.MaskBoxes[device: device, bounds: dstBox, boxes: GenerateBoxes]; RETURN[TRUE]; }; }; MakeMultiple: PROC [x, m: CARD] RETURNS [CARD] ~ { mod: CARD ~ x MOD m; RETURN [IF mod = 0 THEN x ELSE x + (m-mod)] }; minScaleForFoxySwitch: CHAR['m..'m] ~ ImagerSwitches.Define[switch: 'm, name: $maskpixelswitchover, doc: "scale value for which maskpixel may use other than simple point-sampling", defaultValue: NEW[REAL ¬ 1.5]]; MinScaleForFoxy: PROC RETURNS [REAL] ~ { WITH ImagerSwitches.Value[minScaleForFoxySwitch] SELECT FROM r: REF REAL => RETURN [r^]; ENDCASE => RETURN [1.0]; }; FoxyMaskBitmap: PROC [device: Device, bitmap: SampleMap, bitsToDevice: Transformation] RETURNS [BOOL ¬ FALSE] ~ { IF (device.state.allow.multipleCoverage AND ImagerTransformation.SingularValues[bitsToDevice].y >= MinScaleForFoxy[]) THEN { clipper: DeviceClipper ~ device.worksState.clipper; box: SF.Box ¬ ImagerSample.GetBox[bitmap]; clip: ImagerBox.Box ¬ ImagerBox.BoxFromRectangle[bitsToDevice.InverseTransformRectangle[[x: clipper.clipBox.min.s, y: clipper.clipBox.min.f, w: SF.SizeS[clipper.clipBox], h: SF.SizeF[clipper.clipBox]]]]; IF box.min.s < clip.xmin THEN box.min.s ¬ MIN[Real.Floor[clip.xmin], INTEGER.LAST]; IF box.min.f < clip.ymin THEN box.min.f ¬ MIN[Real.Floor[clip.ymin], INTEGER.LAST]; IF box.max.s > clip.xmax THEN box.max.s ¬ MAX[Real.Ceiling[clip.xmax], INTEGER.FIRST]; IF box.max.f > clip.ymax THEN box.max.f ¬ MAX[Real.Ceiling[clip.ymax], INTEGER.FIRST]; IF SF.Nonempty[box] THEN { bytes: NAT ~ (box.max.f-box.min.f+7)/8; textBuf: REF TEXT ~ RefText.ObtainScratch[nChars: bytes]; i: NAT ¬ 0; String: XStringProc ~ { i ¬ 0; WHILE i < bytes DO charAction[Char.Make[set: 0, code: ORD[textBuf[i]]]]; i ¬ i + 1; ENDLOOP; }; charToDevice: Transformation ¬ ImagerTransformation.TranslateTo[bitsToDevice, [0, 0]]; fontAtom: Font ~ ImagerFont.Modify[bitFont, charToDevice]; cp: ImagerDeviceVector.DVec ~ NEW[ImagerDeviceVector.DVecRep]; TryFastCP: PROC = { IF NOT cp.scaled THEN { IF ABS[cp.fv.x] < worryReal AND ABS[cp.fv.y] < worryReal THEN { cp.scaled ¬ TRUE; cp.sv.s ¬ ImagerScaled.FromReal[cp.fv.x]; cp.sv.f ¬ ImagerScaled.FromReal[cp.fv.y]; }; }; }; deviceEsc: VEC = ImagerTransformation.TransformVec[bitsToDevice, [0, 8]]; -- fixed width FloatCP: PROC ~ { IF cp.scaled THEN { cp.scaled ¬ FALSE; cp.fv.x ¬ ImagerScaled.Float[cp.sv.s]; cp.fv.y ¬ ImagerScaled.Float[cp.sv.f]; }; }; DoMetrics: PROC [char: XChar] = { FloatCP[]; cp.fv.x ¬ cp.fv.x + deviceEsc.x; cp.fv.y ¬ cp.fv.y + deviceEsc.y; TryFastCP[]; }; HardChar: PROC [char: XChar] = { Path: PathProc ~ { BitPath[code: Char.Code[char], m: charToDevice, moveTo: moveTo, lineTo: lineTo]; }; FloatCP[]; ImagerTransformation.ApplyTranslateTo[charToDevice, cp.fv]; device.works.MaskFill[device: device, path: Path, oddWrap: FALSE, pathToDevice: NIL]; DoMetrics[char]; }; HardMetrics: PROC [charMask: ImagerMaskCache.CharMask] = { IF cp.scaled AND charMask.char = Char.Make[0, 0] THEN TRUSTED { ds: Basics.LongNumber ~ charMask.escapement.s; df: Basics.LongNumber ~ charMask.escapement.f; lbytes: INTEGER ~ bytes; ltextBuf: POINTER TO Basics.RawBytes ~ LOOPHOLE[textBuf, POINTER]+UNITS[TEXT[0]]; li: INTEGER ¬ i; s: Basics.LongNumber ¬ cp.sv.s; f: Basics.LongNumber ¬ cp.sv.f; WHILE li < lbytes AND ltextBuf[li] = 0 DO s.li ¬ s.li + ds.li; f.li ¬ f.li + df.li; li ¬ li + 1; ENDLOOP; i ¬ li-1; -- the minus 1 is because the String proc is going to bump i again. cp.sv.s ¬ LOOPHOLE[s]; cp.sv.f ¬ LOOPHOLE[f]; } ELSE { DoMetrics[charMask.char]; }; }; textBufAsSampleMap: SampleMap ¬ NIL; TRUSTED { pointer: LONG POINTER ~ LOOPHOLE[textBuf, LONG POINTER] + SIZE[TEXT[0]]; textBufAsSampleMap ¬ ImagerSample.ObtainUnsafeDescriptor[size: [s: 1, f: box.max.f-box.min.f], bitsPerSample: 1, bitsPerLine: bytes*8, base: [word: pointer, bit: 0], ref: textBuf, words: WORDS[TEXT[textBuf.maxLength]]-WORDS[TEXT[0]]]; }; ImagerSample.Clear[textBufAsSampleMap]; FOR s: INTEGER IN [box.min.s..box.max.s) DO ImagerSample.BasicTransfer[dst: textBufAsSampleMap, src: bitmap, srcMin: [s, box.min.f], size: [1, box.max.f-box.min.f]]; cp.scaled ¬ FALSE; cp.fv ¬ ImagerTransformation.Transform[bitsToDevice, [s, box.min.f]]; TryFastCP[]; device.works.Show[ device: device, fontAtom: fontAtom, string: String, cp: cp, hardChar: HardChar, -- Needed rarely hardMetrics: HardMetrics, -- handles runs of zeros - this "hard" case is actually easy! easyMetrics: ordinary, noImage: FALSE ]; ENDLOOP; TRUSTED { ImagerSample.ReleaseDescriptor[textBufAsSampleMap] }; ImagerTransformation.Destroy[charToDevice]; -- don't destroy it too soon! RefText.ReleaseScratch[textBuf]; }; RETURN[TRUE]; }; }; StandardMaskBitmap: PUBLIC PROC [device: Device, bitmap: SampleMap, bitsToDevice: Transformation] = { form: NAT ~ bitsToDevice.form; IF ABS[bitsToDevice.c] < bigTranslate AND ABS[bitsToDevice.f] < bigTranslate AND (device.state.allow.bitmap OR device.state.allow.unorderedBoxes) AND form < 11 THEN { FOR tail: LIST OF SMB ¬ smbRegistry[bitsToDevice.form], tail.rest UNTIL tail = NIL DO t: Transformation ~ tail.first.templateT; Eql: PROC [a, b: REAL] RETURNS [BOOL] ~ INLINE { RETURN [IF a > 0.0 THEN (16.0+a) = (16.0+b) ELSE (16.0-a) = (16.0-b)] }; IF form > 2 OR (Eql[bitsToDevice.a, t.a] AND Eql[bitsToDevice.b, t.b] AND Eql[bitsToDevice.d, t.d] AND Eql[bitsToDevice.e, t.e]) THEN { IF tail.first.specialMaskBitmap[device, bitmap, bitsToDevice] THEN RETURN; }; ENDLOOP; }; IF BoxyMaskBitmap[device, bitmap, bitsToDevice] THEN RETURN; IF FoxyMaskBitmap[device, bitmap, bitsToDevice] THEN RETURN; HardMaskSampledBits[device, bitmap, bitsToDevice]; }; bitsPerChunk: INT ¬ 1048576; StandardMaskPixelArray: PUBLIC PROC [device: Device, bitmap: PixelArray, clientToDevice: Transformation] ~ { maskToDevice: Transformation ¬ ImagerTransformation.Concat[bitmap.m, clientToDevice]; clipper: DeviceClipper = device.worksState.clipper; srcVisibleRealBox: ImagerBox.Box = ImagerBox.BoxFromRectangle[ ImagerTransformation.InverseTransformRectangle[ maskToDevice, [x: clipper.clipBox.min.s, y: clipper.clipBox.min.f, w: clipper.clipBox.max.s - clipper.clipBox.min.s, h: clipper.clipBox.max.f - clipper.clipBox.min.f]]]; srcVisibleBox: SF.Box = ClippedBounds[ clipBox: [max: [bitmap.sSize, bitmap.fSize]], p0: [Real.Floor[srcVisibleRealBox.xmin], Real.Floor[srcVisibleRealBox.ymin]], p1: [Real.Ceiling[srcVisibleRealBox.xmax], Real.Ceiling[srcVisibleRealBox.ymax]]]; sSizeV: NAT ~ srcVisibleBox.max.s-srcVisibleBox.min.s; fSizeV: NAT ~ srcVisibleBox.max.f-srcVisibleBox.min.f; IF sSizeV > 0 AND fSizeV > 0 THEN { goodblock: NAT ~ 64; -- Chosen to reduce block fragmentation for fast rotation cases sBufSize: NAT ~ MIN[ sSizeV, MAX[((bitsPerChunk+fSizeV-1)/fSizeV+(goodblock-1))/goodblock, 1]*goodblock ]; scratch: ImagerSample.SampleMap ~ ImagerSample.ObtainScratchMap[box: [max: [sBufSize, fSizeV]]]; band: ImagerSample.SampleMap ¬ scratch; ImagerTransformation.ApplyPreTranslate[maskToDevice, [srcVisibleBox.min.s, srcVisibleBox.min.f]]; FOR s: INT ¬ 0, s+sBufSize WHILE s < sSizeV DO IF sSizeV-s < sBufSize THEN band ¬ ImagerSample.Clip[scratch, [max: [s: sSizeV-s, f: fSizeV]]]; ImagerPixelArray.Transfer[pa: bitmap, dst: band, s: s+srcVisibleBox.min.s, f: srcVisibleBox.min.f]; device.works.MaskBitmap[ device: device, bitmap: band, bitsToDevice: maskToDevice ]; ImagerTransformation.ApplyPreTranslate[maskToDevice, [sBufSize, 0]]; ENDLOOP; IF band # scratch THEN TRUSTED { ImagerSample.ReleaseDescriptor[band] }; ImagerSample.ReleaseScratchMap[scratch]; }; ImagerTransformation.Destroy[maskToDevice]; }; ObtainPaddedBitmap: PROC [bitmap: SampleMap, pad: INTEGER ¬ 1] RETURNS [padded: SampleMap] ~ { box: SF.Box ¬ ImagerSample.GetBox[bitmap]; box.min.s ¬ box.min.s - pad; box.min.f ¬ box.min.f - pad; box.max.s ¬ box.max.s + pad; box.max.f ¬ box.max.f + pad; padded ¬ ImagerSample.ObtainScratchMap[box]; ImagerSample.Clear[padded]; ImagerSample.Transfer[dst: padded, src: bitmap]; }; HardMaskSampledBits: PROC [device: Device, bitmap: SampleMap, bitsToDevice: Transformation] = { padded: SampleMap ~ ObtainPaddedBitmap[bitmap]; MaskBoundary: PathProc = { srcBox: SF.Box = ImagerSample.GetBox[bitmap]; x0: REAL = srcBox.min.s-0.125; y0: REAL = srcBox.min.f-0.125; x1: REAL = srcBox.max.s+0.125; y1: REAL = srcBox.max.f+0.125; moveTo[[x0, y0]]; lineTo[[x1, y0]]; lineTo[[x1, y1]]; lineTo[[x0, y1]]; }; Nest1: PROC [bounds: SF.Box, boxGenerator: SF.BoxGenerator] = { Nest2: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] = { pixelMap: ImagerPixel.PixelMap ¬ ImagerPixel.MakePixelMap[s0: padded]; Nest3: ImagerPixel.ResampleAction = TRUSTED { count: INTEGER = pixels.length; b: POINTER TO Basics.RawWords ~ ImagerSample.PointerToSamples[buffer: pixels[0], start: 0, count: count]; i: INTEGER ¬ 0; DO WHILE i < count AND b[i] = 0 DO i ¬ i + 1 ENDLOOP; IF i = count THEN EXIT ELSE { i0: INTEGER ¬ i; WHILE i < count AND b[i] = 1 DO i ¬ i + 1 ENDLOOP; boxAction[[min: [min.s, min.f + i0], max: [min.s + 1, min.f + i]]]; }; ENDLOOP; }; ImagerPixel.Resample[self: pixelMap, m: bitsToDevice, interpolate: FALSE, boxes: boxGenerator, bounds: bounds, action: Nest3]; }; device.class.MaskBoxes[device: device, bounds: bounds, boxes: Nest2]; }; clipper: DeviceClipper ~ device.worksState.clipper; BoxesFromPath[action: Nest1, path: MaskBoundary, oddWrap: FALSE, pathToDevice: bitsToDevice, clipper: clipper]; ImagerSample.ReleaseScratchMap[padded]; }; StandardMaskBoxes: PUBLIC PROC [device: Device, bounds: SF.Box, boxes: SF.BoxGenerator] = { clipper: DeviceClipper ~ device.worksState.clipper; IF clipper.clipMask # NIL THEN { IF SF.Inside[bounds, clipper.clipMask.first] AND device.state.allow.unorderedBoxes THEN {device.class.MaskBoxes[device: device, bounds: bounds, boxes: boxes]} ELSE { manhattan: ManhattanPolygon ~ ImagerManhattan.DestructiveIntersection[ImagerManhattan.CreateFromBoxes[boxes], clipper.clipMask]; IF manhattan # NIL THEN { ManhattanBoxes: SF.BoxGenerator ~ { FOR each: LIST OF SF.Box ¬ manhattan, each.rest UNTIL each=NIL DO boxAction[each.first]; ENDLOOP; }; bounds: SF.Box ~ ImagerManhattan.BoundingBox[manhattan]; device.class.MaskBoxes[device: device, bounds: bounds, boxes: ManhattanBoxes]; ImagerManhattan.Destroy[manhattan]; }; }; }; }; StandardMaskCharMask: PUBLIC PROC [device: Device, charMask: ImagerMaskCache.CharMask, cp: ImagerDeviceVector.DVec] RETURNS [ok: BOOL] = { clipper: DeviceClipper ~ device.worksState.clipper; IF clipper.clipMask = NIL THEN RETURN[ok: TRUE]; IF NOT cp.scaled THEN RETURN [FALSE]; IF charMask # NIL THEN {DO -- retry here if a culled representation was found. s: INTEGER = ImagerScaled.Round[cp.sv.s]; f: INTEGER = ImagerScaled.Round[cp.sv.f]; sMin: INT = charMask.box.min.s + s; sMax: INT = charMask.box.max.s + s; fMin: INT = charMask.box.min.f + f; fMax: INT = charMask.box.max.f + f; IF sMin >= FIRST[INTEGER] AND sMax <= LAST[INTEGER] AND fMin >= FIRST[INTEGER] AND fMax <= LAST[INTEGER] THEN { box: SF.Box = [[INTEGER[sMin], INTEGER[fMin]], [INTEGER[sMax], INTEGER[fMax]]]; dstBox: SF.Box = SF.Intersect[box, clipper.clipBox]; IF SF.Empty[dstBox] THEN RETURN[ok: TRUE]; IF charMask.rep = culled THEN { charMask ¬ ImagerFontWorks.CaptureChar[ font: charMask.font, char: charMask.char, parameters: ImagerMaskCache.GetParameters[device.parm.fontCache], metricsOnly: FALSE ]; IF charMask.rep = culled THEN ERROR; -- We really want that mask! ImagerMaskCache.Store[device.parm.fontCache, charMask]; LOOP; }; IF charMask.rep = maskNotCacheable THEN RETURN[ok: FALSE]; -- only the metrics were in cache IF SF.Inside[box, clipper.clipMask.first] AND ((charMask.rep = raster AND device.state.allow.rasterChar) OR (charMask.rep = runs AND device.state.allow.runGroupChar)) THEN { device.class.MaskChar[device: device, delta: [s, f], mask: charMask]; RETURN[ok: TRUE]; }; IF charMask.rep = raster AND device.state.allow.bitmap THEN { bitmap: SampleMap ¬ ImagerMaskCache.BitmapFromCharMask[charMask]; Boxes: PROC [action: PROC [SF.Box]] = { ImagerManhattan.ClipBoxToMask[box: dstBox, mask: clipper.clipMask, action: action] }; device.class.MaskBitmap[device: device, bitmap: bitmap, delta: [s, f], bounds: dstBox, boxes: Boxes]; TRUSTED {ImagerSample.ReleaseDescriptor[bitmap]}; } ELSE { FOR each: LIST OF SF.Box ¬ clipper.clipMask, each.rest UNTIL each = NIL DO bounds: SF.Box ~ SFInline.Intersect[dstBox, each.first]; IF SFInline.Nonempty[bounds] THEN { GenerateBoxes: --SF.BoxGenerator-- PROC [boxAction: SF.BoxAction] = { ImagerMaskCache.BoxesFromCharMask[charMask: charMask, boxAction: boxAction, delta: [s, f], clip: bounds]; }; device.class.MaskBoxes[device: device, bounds: bounds, boxes: GenerateBoxes]; }; ENDLOOP; }; RETURN[ok: TRUE]; }; IF sMax < clipper.clipBox.min.s OR sMin > clipper.clipBox.max.s OR fMax < clipper.clipBox.min.f OR fMin > clipper.clipBox.max.f THEN RETURN[ok: TRUE]; -- character was culled EXIT ENDLOOP }; RETURN[ok: FALSE]; }; hashMask: CARDINAL ¬ 255; -- for experimenting with hash functions ByteHashFromLONG: PROC [ln: Basics.LongNumber] RETURNS [BYTE] = { c: CARDINAL ¬ Basics.BITXOR[ln.hi, ln.lo]; RETURN [Basics.BITAND[Basics.BITXOR[c, c/256], hashMask]] }; Ord: PROC [char: XChar] RETURNS [CARDINAL] = INLINE {RETURN [LOOPHOLE[char]]}; NoOverflow: PROC [a, b: ImagerScaled.Value] RETURNS [BOOL] = INLINE { OPEN ImagerScaled; -- USING IntRep RETURN [ IF IntRep[a] >= 0 THEN (IntRep[b] <= LAST[INT]-IntRep[a]) ELSE (IntRep[b] >= FIRST[INT]-IntRep[a]) ] }; StandardShow: PUBLIC PROC [device: Device, fontAtom: Font, string: XStringProc, cp: ImagerDeviceVector.DVec, hardChar: ImagerFont.XCharProc, hardMetrics: PROC [charMask: ImagerMaskCache.CharMask], easyMetrics: EasyMetrics, noImage: BOOL] = { IF device.parm.fontCache # NIL THEN { fontAtomHash: CARDINAL ¬ ByteHashFromLONG[LOOPHOLE[fontAtom]]; -- some function of current font atom charArray: ImagerMaskCache.SmallCache = ImagerMaskCache.ObtainSmallCache[device.parm.fontCache]; ByteHash: PROC [char: XChar] RETURNS [BYTE] = INLINE { RETURN [Basics.BITXOR[fontAtomHash, Basics.BITXOR[Ord[char], Char.Set[char]]] MOD 256] }; MediumCharAction: PROC [char: XChar] = { m: ImagerMaskCache.CharMask; IF cp.scaled AND (m ¬ CachedLookup[char]) # NIL THEN { IF noImage OR device.works.MaskCharMask[device: device, charMask: m, cp: cp] THEN { IF (SELECT easyMetrics FROM all => TRUE, ordinary => m.flags = ordinaryMetrics, ENDCASE => FALSE) THEN { IF NoOverflow[cp.sv.s, m.escapement.s] AND NoOverflow[cp.sv.f, m.escapement.f] THEN { cp.sv.s ¬ ImagerScaled.PLUS[cp.sv.s, m.escapement.s]; cp.sv.f ¬ ImagerScaled.PLUS[cp.sv.f, m.escapement.f]; RETURN }; }; hardMetrics[m]; RETURN; }; }; hardChar[char]; -- do this one the hard way. }; CachedLookup: PROC [char: XChar] RETURNS [m: ImagerMaskCache.CharMask ¬ NIL] = { IF charArray # NIL THEN { m ¬ charArray[ByteHash[char]]; IF m # NIL AND m.char = char AND m.font = fontAtom THEN RETURN [m]; }; WITH m ¬ ImagerMaskCache.Fetch[device.parm.fontCache, fontAtom, char] SELECT FROM r: RasterMask => { IF MAX[SF.SizeS[r.box], SF.SizeF[r.box], ABS[ImagerScaled.Floor[r.escapement.s]], ABS[ImagerScaled.Floor[r.escapement.f]]] < worryNat/8 THEN { IF charArray # NIL THEN charArray[ByteHash[char]] ¬ r; }; }; ENDCASE => IF m = NIL THEN { clipper: DeviceClipper ~ device.worksState.clipper; IF Char.Set[char] = 1B THEN RETURN[NIL]; -- Character set 1B is `reserved' for PostScript user-defined characters. The BuildChar procedure in a PS user-defined font will do the capturing for us, so we must avoid ImagerTypeface.CaptureChar. Returning NIL causes hardChar to be executed in MediumCharAction. It is assumed that the hardChar proc in the PS user-defined case will execute BuildChar and store the captured mask in the cache if appropriate, just as the following two statements would. m ¬ ImagerFontWorks.CaptureChar[-- never returns NIL font: fontAtom, char: char, parameters: ImagerMaskCache.GetParameters[device.parm.fontCache], metricsOnly: NOT (-- guess about visibility (no disaster if wrong) cp.scaled AND ImagerScaled.Floor[cp.sv.s] IN [clipper.clipBox.min.s..clipper.clipBox.max.s) AND ImagerScaled.Floor[cp.sv.f] IN [clipper.clipBox.min.f..clipper.clipBox.max.f)) ]; ImagerMaskCache.Store[device.parm.fontCache, m]; }; }; box: SF.Box ¬ []; NewBox: PROC [p: SF.Vec] RETURNS [SF.Box] = { clipper: DeviceClipper ~ device.worksState.clipper; FOR each: LIST OF SF.Box ¬ clipper.clipMask, each.rest UNTIL each = NIL DO box: SF.Box = each.first; IF SF.In[p, box] THEN RETURN [box]; IF box.min.s > p.s THEN EXIT; ENDLOOP; RETURN [[min: SF.maxVec, max: SF.minVec]]; }; IF device.state.allow.rawBitmaps AND charArray # NIL THEN { head: LIST OF ImagerSample.RawDescriptor ¬ IF device.state.scratchRawBitmapList = NIL THEN device.state.scratchRawBitmapList ¬ LIST[[[], 0, NIL]] ELSE device.state.scratchRawBitmapList; last: LIST OF ImagerSample.RawDescriptor ¬ head; save: LIST OF ImagerSample.RawDescriptor ¬ NIL; Flush: PROC = TRUSTED INLINE { save ¬ last.rest; last.rest ¬ NIL; device.class.MaskRawBitmaps[device: device, list: head.rest]; last.rest ¬ save; last ¬ head; }; CachedRasterLookup: PROC [char: XChar] RETURNS [m: RasterMask ¬ NIL] = { WITH ImagerMaskCache.Fetch[device.parm.fontCache, fontAtom, char] SELECT FROM r: RasterMask => { IF MAX[SF.SizeS[r.box], SF.SizeF[r.box], ABS[ImagerScaled.Floor[r.escapement.s]], ABS[ImagerScaled.Floor[r.escapement.f]]] < worryNat/8 THEN { h: BYTE = ByteHash[char]; old: RasterMask ~ charArray[h]; IF old # NIL THEN Flush[]; charArray[h] ¬ m ¬ r; }; }; ENDCASE => NULL; }; FastCharAction: PROC [char: XChar] = { localCP: ImagerDeviceVector.DVec = cp; IF localCP.scaled THEN { localCPs: ImagerScaled.Value = localCP.sv.s; localCPf: ImagerScaled.Value = localCP.sv.f; m: RasterMask ¬ charArray[ByteHash[char]]; SELECT TRUE FROM m = NIL, m.char # char, m.font # fontAtom => { m ¬ CachedRasterLookup[char]; IF m = NIL THEN GO TO notEasy; }; ENDCASE; { deltaS: INTEGER = ImagerScaled.Round[localCPs]; deltaF: INTEGER = ImagerScaled.Round[localCPf]; charBoxMinS: INTEGER = m.box.min.s + deltaS; charBoxMinF: INTEGER = m.box.min.f + deltaF; charBoxMaxS: INTEGER = m.box.max.s + deltaS; charBoxMaxF: INTEGER = m.box.max.f + deltaF; clipper: DeviceClipper ~ device.worksState.clipper; IF NOT noImage THEN { IF charBoxMinS >= box.min.s AND charBoxMinF >= box.min.f AND charBoxMaxS <= box.max.s AND charBoxMaxF <= box.max.f THEN GO TO quick; IF charBoxMinS >= clipper.clipBox.max.s OR charBoxMinF >= clipper.clipBox.max.f OR charBoxMaxS <= clipper.clipBox.min.s OR charBoxMaxF <= clipper.clipBox.min.f THEN GO TO notVisible; FOR each: LIST OF SF.Box ¬ clipper.clipMask, each.rest UNTIL each = NIL DO IF charBoxMinS >= each.first.min.s AND charBoxMinF >= each.first.min.f AND charBoxMaxS <= each.first.max.s AND charBoxMaxF <= each.first.max.f THEN { box ¬ each.first; GO TO quick; }; ENDLOOP; GO TO notQuick; EXITS quick => { fSize: INTEGER = charBoxMaxF-charBoxMinF; IF fSize > 0 THEN TRUSTED { bplMask: CARDINAL = LAST[CARDINAL]-CARDINAL[bitsPerWord-1]; fNat: NAT = Basics.BITAND[LOOPHOLE[fSize+(bitsPerWord-1)], bplMask]; rest: LIST OF ImagerSample.RawDescriptor ¬ last.rest; IF rest = NIL THEN last.rest ¬ rest ¬ LIST[[[], 0, NIL]]; last ¬ rest; rest.first.box ¬ [ min: [s: charBoxMinS, f: charBoxMinF], max: [s: charBoxMaxS, f: charBoxMaxF]]; rest.first.bitsPerLine ¬ fNat; rest.first.basePointer ¬ LOOPHOLE[m, POINTER] + SIZE[ImagerMaskCache.CharMaskRep.raster[0]]; }; SELECT easyMetrics FROM all => {}; ordinary => IF m.flags # ordinaryMetrics THEN GO TO noAdd; ENDCASE => GO TO noAdd; GO TO addAndReturn; }; notQuick => { [] ¬ device.works.MaskCharMask[ device: device, charMask: m, cp: localCP]; -- should never fail }; notVisible => {}; }; SELECT easyMetrics FROM all => {}; ordinary => IF m.flags # ordinaryMetrics THEN GO TO noAdd; ENDCASE => GO TO noAdd; IF NoOverflow[localCPs, m.escapement.s] AND NoOverflow[localCPf, m.escapement.f] THEN GO TO addAndReturn; EXITS noAdd => {}; addAndReturn => { cp.sv.s ¬ ImagerScaled.PLUS[localCPs, m.escapement.s]; cp.sv.f ¬ ImagerScaled.PLUS[localCPf, m.escapement.f]; RETURN; }; }; hardMetrics[m]; RETURN; EXITS notEasy => {}; }; IF last#head THEN Flush[]; MediumCharAction[char]; -- do this one the slower way. }; string[FastCharAction]; IF last # head THEN Flush[]; ImagerMaskCache.ReleaseSmallCache[device.parm.fontCache, charArray]; RETURN; }; string[MediumCharAction]; IF charArray # NIL THEN ImagerMaskCache.ReleaseSmallCache[device.parm.fontCache, charArray]; RETURN; }; string[hardChar]; }; bitFont: Font ~ ImagerTypeface.MakeFont[MakeBitTypeface[], ImagerTransformation.Scale[1]]; MakeBitTypeface: PROC RETURNS [Typeface] ~ INLINE { class: ImagerTypeface.TypefaceClass ¬ NEW[ImagerTypeface.TypefaceClassRep ¬ [type: $BitTypeface, Contains: BitContains, NextChar: BitNextChar, Escapement: BitEscapement, Amplified: BitAmplified, Correction: BitCorrection, BoundingBox: BitBoundingBox, FontBoundingBox: BitFontBoundingBox, Ligature: BitLigature, NextLigature: BitNextLigature, Kern: BitKern, NextKern: BitNextKern, MapChar: BitMapChar]]; typeface: Typeface ¬ NEW[ImagerTypeface.TypefaceRep ¬ [class: class, data: NIL]]; RETURN [typeface] }; BitContains: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ {RETURN [Char.Set[char] = 0]}; BitNextChar: PROC [self: Typeface, char: XChar] RETURNS [next: XChar] ~ {RETURN [IF char = nullXChar THEN Char.Make[0, 0] ELSE IF ORD[char] >= 255 THEN nullXChar ELSE SUCC[char]]}; BitEscapement: PROC [self: Typeface, char: XChar] RETURNS [VEC] ~ {RETURN [[0, 8]]}; BitAmplified: PROC [self: Typeface, char: XChar] RETURNS [BOOL] ~ {RETURN [FALSE]}; BitCorrection: PROC [self: Typeface, char: XChar] RETURNS [ImagerFont.CorrectionType] ~ {RETURN [IF char = Char.Make[0, 0] THEN space ELSE mask]}; BitBoundingBox: PROC [self: Typeface, char: XChar] RETURNS [Extents] ~ { RETURN [[leftExtent: 0, rightExtent: 1, descent: 0, ascent: 8]]; }; BitFontBoundingBox: PROC [self: Typeface] RETURNS [Extents] ~ { RETURN [[leftExtent: 0, rightExtent: 1, descent: 0, ascent: 8]]; }; BitLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; BitNextLigature: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; BitKern: PROC [self: Typeface, char, successor: XChar] RETURNS [VEC] ~ { RETURN [[0.0, 0.0]]; }; BitNextKern: PROC [self: Typeface, char, successor: XChar] RETURNS [XChar] ~ { RETURN [nullXChar]; }; BitMapChar: ImagerFontWorks.MapCharProc ~ { BitPath[code: Char.Code[char], m: font.charToClient, moveTo: moveTo, lineTo: lineTo]; }; bitPen: ImagerPen.Pen ~ BitPen[]; BitPen: PROC RETURNS [ImagerPen.Pen] ~ { p: ImagerPen.Pen ¬ NEW[ImagerPen.PenRep[4]]; p.bounds.x ¬ 0.5; p.bounds.y ¬ 0.5; p[0] ¬ [ 0.5, 0.5 ]; p[1] ¬ [-0.5, 0.5 ]; p[2] ¬ [-0.5, -0.5 ]; p[3] ¬ [ 0.5, -0.5 ]; RETURN [p] }; BitPath: PROC [code: BYTE, m: Transformation, moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc] ~ { p0: VEC ¬ ImagerTransformation.Transform[m, [0, 0]]; v1: VEC ~ ImagerTransformation.TransformVec[m, [1, 0]]; v2: VEC ~ ImagerTransformation.TransformVec[m, [0, 1]]; P: ImagerPath.PathProc ~ { ImagerStroke.MapBitPath[byte: code, p0: p0, v1: v1, v2: v2, moveTo: moveTo, lineTo: lineTo]; }; epsilon: REAL ~ 3.90625e-3; -- 2.0**(-8) IF v1.x + v2.x > 0 THEN p0.x ¬ p0.x + epsilon; IF v1.x + v2.x < 0 THEN p0.x ¬ p0.x - epsilon; IF v1.y + v2.y > 0 THEN p0.y ¬ p0.y + epsilon; IF v1.y + v2.y < 0 THEN p0.y ¬ p0.y - epsilon; ImagerStroke.ConvolvePenWithPath[path: P, pen: bitPen, moveTo: moveTo, lineTo: lineTo]; }; InterchangeState: TYPE ~ REF InterchangeStateRep; InterchangeStateRep: TYPE ~ ImagerDeviceInterchange.InterchangeStateRep; interchangeStateCache: InterchangeState ¬ NIL; ObtainInterchangeState: PUBLIC PROC RETURNS [InterchangeState] ~ { Locked: ENTRY PROC RETURNS [iState: InterchangeState] ~ INLINE { iState ¬ interchangeStateCache; interchangeStateCache ¬ NIL }; iState: InterchangeState ¬ Locked[]; IF iState = NIL THEN { iState ¬ NEW[InterchangeStateRep] }; RETURN [iState] }; DestroyInterchangeState: PUBLIC PROC [iState: InterchangeState] ~ { IF iState # NIL THEN { iState.device ¬ NIL; IF iState.clientToView # NIL THEN { ImagerTransformation.Destroy[iState.clientToView]; iState.clientToView ¬ NIL; }; IF iState.viewToSurface # NIL THEN { ImagerTransformation.Destroy[iState.viewToSurface]; iState.viewToSurface ¬ NIL; }; IF iState.surfaceToDevice # NIL THEN { ImagerTransformation.Destroy[iState.surfaceToDevice]; iState.surfaceToDevice ¬ NIL; }; iState.color ¬ NIL; { P: ENTRY PROC ~ INLINE { interchangeStateCache ¬ iState }; P[] }; }; }; standardWorksClass: ImagerDevice.WorksClass ~ NEW[ImagerDevice.WorksClassRep ¬ [ Clip: StandardClip, MaskFill: StandardMaskFill, MaskRectangle: StandardMaskRectangle, MaskStroke: StandardMaskStroke, MaskVector: StandardMaskVector, MaskDashedStroke: StandardMaskDashedStroke, MaskBitmap: StandardMaskBitmap, MaskPixelArray: StandardMaskPixelArray, MaskBoxes: StandardMaskBoxes, MaskCharMask: StandardMaskCharMask, Show: StandardShow ]]; MakeDevice: PUBLIC PROC [class: ImagerDevice.DeviceClass, parm: ImagerDevice.DeviceParm, state: ImagerDevice.DeviceState, data: REF] RETURNS [ImagerDevice.Device] ~ { device: ImagerDevice.Device ¬ NEW[ImagerDevice.DeviceRepr ¬ [ works: standardWorksClass, worksState: [clipper: NEW[ImagerDevice.DeviceClipperRep], clipperToDevice: NIL, clientClipper: NIL], class: class, parm: parm, state: IF state # NIL THEN state ELSE NEW[ImagerDevice.DeviceStateRep ¬ [ allow: [], -- SetColor will set bounds: [min: [s: 0, f: 0], max: [s: parm.sSize, f: parm.fSize]], scratchRawBitmapList: NIL ]], data: data ]]; RETURN [device] }; END. L ImagerDeviceWorksImpl.mesa Copyright Σ 1989, 1990, 1991, 1992, 1993, 1994 by Xerox Corporation. All rights reserved. Michael Plass, February 3, 1994 5:24 pm PST Russ Atkinson (RRA) August 18, 1993 8:10 pm PDT Types and Constants Setup The difference between this and ImagerScanConverter.CreatePath is that here we use the scanFatSwitch to set the scan conversion mode. Note that we intentionally do not use this switch for strokes! Clipping Fills Strokes ASSERT[r=0.5]; -- see comment for ImagerStroke.PathFromStroke N. B. The node on this device path should not be changed from its default value - to work properly, the strokes must be rendered in center-sampling mode. Try for the interesting case of just a straight line. Easy case - it is just a single, straight line. This is a square or butt end, and nice in device space; go for speed We get here if we would have ended up in an infinite loop (due to roundoff). This will just be a rectangle in device coordinates. zero-length vector; either warn or no-op, depending on stroke end MaskBitmap support Computes bounding box of p0 and p1, clipped against clipBox. BoxGenerator: TYPE ~ PROC [boxAction: BoxAction]; This does the bookkeeping needed to run a cell-resampling primitive over a source. The resampleCell primitive knows about the size of the source and destination. The values of sSizeSrc and fSizeSrc should be selected so that both fSizeSrc and fSizeDst work out to be multiples of BITS[WORD]. The resampleCell primitive should return a non-zero value whenever the cell contains any non-zero bits. When the resampleCell primitive returns zero, it need not have stored the zero bits. Fast case - do the resampling directly out of the source. Otherwise need to make a copy of the cell to take care of alignment and/or edge effects ImagerSample.Tile[map: buffer, tile: ImagerSample.TileFromStipple[41H], function: [or, null]]; -- for debug. Special cases for MaskBitmap Easy case and rotations Rotates a 32 - bit square block by 180 degrees. 3/2 scale Precomputes a table for 2 -> 3 resampling. mb2to3: SMB ~ RegisterSpecialMaskBitmap[1.5, 0, MB2to3]; MB2to3: SpecialMaskBitmapProc ~ TRUSTED { MaskBitmapHelp[device, bitmap, bitsToDevice, 2, 64, ResampleCell2to3]; RETURN [TRUE] }; Especially interesting for a 200 -> 300 resolution conversion It turns out that MaskBitmapHelp entails a little too much overhead for certain applications; hence we retain this special case. 2/1 scale xyxyxyxy -> 0a0b0c0d [in each byte, each letter = 1 bit] 0a0b0c0d -> 00ab00cd [in each byte, each letter = 1 bit] 00ab00cd -> 0000abcd [in each byte, each letter = 1 bit] 0A0B0C0D -> 00AB00CD [in each word, each letter = 4 bits] 00AB00CD -> 0000ABCD [in each word, each letter = 4 bits] 4/5 scale The rest of this in not quite right. However... More general cases for MaskBitmap Come here once for each box in the clipping region. Come here once for each box in the clipping region that intersects the bitmap area. This box indicates a region of 1-bits in the source bitmap coordinates; transform to device coordinates, clip it and pass it to the device. Rounds x up to a multiple of m. N.B. charToDevice gets side-effected in HardChar; don't destroy it too soon! This arm is a fast case for skipping over runs of zeros. Control gets here because the bitFont marks character 0 Make local copies of the uplevel references so they can live in registers during the loop: Should check for overflow here... FREE[@cp]; MaskBitmap First check the registered special cases; these require device support for MaskBitmap. Equal, with a little fuzz Maybe boxes are good: Finally the two general-purpose cases. MaskPixelArray [pixels: PixelBuffer, min: SF.Vec] MaskBoxes MaskCharMask only the metrics were in cache, but we should be able to get the mask, too All visible: go for it Partly visible: clip it Show Be careful about overflow. We could take noImage into account here, but since the primary use of noImage is in pass 1 of CORRECT, it is likely that we would need the mask anyway pretty soon. - mfp We won't bother to try putting it into the small cache This is to ensure that we always hang onto a REF for all the masks that we might have pointers into, just so they won't get freed out from under us. The ones in the charArray are OK, but we better flush now; just in case the old one is in use. M.B. will need to update reference counts here. - mfp Test for complete containment Test for any intersection in the clipping box Scan the clipping list looking for a containing box. If one is found, then use it for the fast case. This is a specialized expansion of NewBox, and should be significantly faster. At this point, the mask REF is in charArray, and we ensure that it stays there until the Flush. Thus the raster storage won't get freed prematurely. This is why it is OK to form this pointer. - mfp Can't overflow here because we were not clipped; we don't cache characters with huge escapements. IF last#head THEN Flush[]; -- Don't need unless we insist the masks are done in order; it should be OK to do them out of order because they are all the same color. Be careful about overflow. BitFont PROC [font: Font, char: XChar, parameters: Parameters, moveTo: MoveToProc, lineTo: LineToProc, curveTo: CurveToProc, arcTo: ArcToProc, conicTo: ConicToProc] RETURNS [success: BOOL ¬ TRUE] Trace out a path describing the bits in device space, bloated by just enough so that they will overlap or at least touch no matter how the current position gets rounded to the grid. [moveTo: MoveToProc, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc] The following adjustment is to move the bloated path just a teensy bit in the correct direction to disambiguate certain edges that tend to fall very close to pixel centers; this provides more uniform rendering for patterns that are the same except for rotations by multiples of 90 degrees. The value of epsilon is chosen to be large enough so that it does not get lost in the floating-point fuzz when the value of p0 is around 10000. This is because of the viewOrigin used by ImagerMaskCaptureImpl. ImagerDeviceInterchange Some boring scratch-memory management. MakeDevice Κ^©–(cedarcode) style•NewlineDelimiter ™headšœ™Icodešœ ΟeœO™ZL™+L™/L™šΟk ˜ L˜L˜ L˜Lšœ žœ-˜Lšœžœžœ%˜8šŸœΟcœžœ žœ˜šžœ8žœžœž˜LšŸœžœžœ˜Ešžœ˜Lšžœ&˜*Lšžœ(˜,—Lšœ˜—Lšœš˜šL˜oL˜šLšœ˜Lšœ˜Lšœ(˜(Lšžœ˜—L˜L˜2L˜4L˜0L˜L˜——™šŸœžœžœ+žœ#˜oL˜3L˜hL˜,L˜(L˜L˜—šŸœžœ3žœžœ˜XL˜3Lšœ žœ3˜>Lšœžœ4˜>šžœžœ˜#•StartOfExpansion"[inner: SF.Box, outer: SF.Box]šžœžœ*žœžœ5˜‰šžœ˜–S -- [which: [0..1], sMin: INTEGER, sCount: NAT, f0: Scaled.Value, df: Scaled.Value]šŸ œžœ*˜=LšœR˜RL˜—LšœV˜VLšœ˜—šžœ˜šŸ œ œžœ žœ˜ELšœ)˜)šžœžœžœ ž˜šžœ˜L˜„L˜—šžœ˜šŸ œ œžœžœ ˜6Lš žœžœžœžœžœ˜Gšžœžœžœžœžœžœžœž˜NLšœ žœ(˜3Lšžœžœ˜6Lšžœ˜—L˜—L˜xL˜——L˜—LšœM˜MLšœ˜——Lšœ˜—Lšœ˜L˜—š Ÿ œžœžœ žœžœžœ˜HL˜ Lšžœ žœ ˜Lšžœ žœ ˜Lšžœ˜L˜L˜—šŸœžœžœX˜zL˜3šžœžœžœ(˜Mšžœ˜LšœžœQ˜XLšœžœm˜tLšœžœD˜OLšœžœD˜OLšœžœD˜OLšœžœD˜OLšžœ žœžœ˜9Lšžœ žœžœ˜9šžœ žœ žœ˜Lšœžœ&˜-šŸœžœ žœžœ ˜'L˜PL˜—L˜BL˜—L˜—šžœ˜šŸ œ˜L˜#L˜1L˜?L˜1L˜—LšœDžœ#˜lL˜——L˜L˜——™š Ÿœžœžœ*žœfžœ˜ΠL˜3Lšœžœ˜Lš Ÿœžœžœžœžœžœ˜[šŸœ  ˜-Lšœ-˜-Lšœ˜—šŸœ ˜3Lšœ.˜.Lšœ˜—šŸœ ˜BLšœ=™=Lšœ4˜4Lšœ˜—šŸ œ n˜’šŸ œ  ˜3Lšœ  Ι˜Τšžœž˜Lšžœ.˜2Lšžœ(˜,—™šL˜?—Lšœ;˜;Lšœ˜—Lšœ]˜]Lšœ˜—L˜Άšžœžœžœ˜L˜#Lšœ(˜(Lšœ˜—Lšœ˜L˜—LšŸœžœžœ˜JšŸ œžœ ˜6JšŸ œžœ ˜6šŸœžœ ˜2J˜—šœ žœžœ˜Lšœžœžœ˜Lšœžœ ˜Lšœžœ˜Lšœ˜L˜—šŸœžœžœ˜?L˜Lšœžœ˜šŸœ˜#Lšžœ žœžœžœ˜AL˜—šŸœ˜#Lšžœ žœžœžœ˜BLšœ˜—Lšœzžœ˜†L˜Lšžœ ˜L˜L˜—Lšœ žœžœ˜š Ÿœžœžœžœžœ%˜OL˜—šŸœžœžœ.žœ žœžœžœžœ žœ žœ žœ%žœ žœžœ˜ƒL˜3LšŸ œΚ˜ΤLšžœžœžœžœ˜&šžœ žœžœžœžœžœžœ˜]L™5L˜&šžœ žœ˜L™/Lšžœžœ8˜Pš žœžœžœžœž˜*š œ"žœžœžœžœ˜‰L™DL–4[m: ImagerTransformation.Transformation, v: VEC]šœžœ<˜DL–4[m: ImagerTransformation.Transformation, v: VEC]šœžœ<˜DLšœ žœ˜ Lšœ žœ˜ Lšœžœ˜L–4[m: ImagerTransformation.Transformation, v: VEC]šœžœ˜Lšžœ žœžœ ˜L˜Lšžœ˜—L˜—Jšœžœ)˜0Jšœžœ˜!Jšœžœ˜!Jšœ žœ,˜Lšœžœ žœžœ˜+LšœžœO˜VLšœžœ˜"Lšœžœ˜"Lšœ žœ˜$Lšœ*žœ˜.L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšœ žœžœ 1˜JL–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšœ žœ  :˜SL–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšœ žœ *˜A–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšžœžœž˜–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜)L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšœžœ˜&–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentš žœžœžœžœžœ˜ L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšœžœžœ˜L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜4L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜GL–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜?L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜?L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜—L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜—L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšžœ˜—šžœžœžœž˜@Lšœžœžœžœ"˜9Lšœ žœžœžœ˜!Lšœ žœžœžœ˜"Lšœžœ ˜Lšœžœ ˜šžœžœžœž˜@Lšœžœ˜ L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšœ žœ˜–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšžœ žœžœžœ&žœžœ&žœžœžœžœžœžœ˜Γ–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšžœ˜L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent™9L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentš œžœžœžœžœžœ˜dL–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜JL–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent˜—–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšžœ˜L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescent™WL˜ L˜LL˜\L–S(xerox/distinct/yellow) backgroundNamedColor 9 backgroundAscent 3 backgroundDescentšœ˜——šžœžœ˜š žœžœžœžœžœ˜=L˜fLšžœ žœžœžœ ˜QL˜Lšœ˜—Lšžœžœ˜4L˜—L˜L˜"L˜"Lšžœ˜—L˜$L˜$šžœžœ˜L˜fLšžœ žœžœžœ ˜QLšœ˜—Lšžœ˜—J˜;Lšœ_  ™lšœžœžœžœ˜ML˜L˜L˜L˜L˜—J˜'J˜,J˜J˜J˜Lšœ˜—L˜L˜——™Lš œžœžœCžœžœžœ˜|Lšžœžœžœ˜%šœžœžœ˜%L˜L˜(L˜—Lšœžœ )˜ALšœ žœžœžœžœžœžœžœ˜?šŸœžœžœ žœ žœžœžœ˜rL˜KL˜!Lšœžœžœ#˜1Lšœžœ˜1Lšžœ˜ Lšœ˜——™šŸœžœžœžœ žœžœ žœžœžœž œ˜qJ™/Lšœžœ˜ Lšœžœžœžœ˜)Lšœžœžœžœ˜LšœžœΟf œ˜Lšœžœ‘ œ˜Lšœžœ‘ œ˜Lšœžœ‘ œ˜Lšœžœ˜ šžœ ž˜L˜J˜Jšœ žœ˜J˜Jšœ žœžœžœ˜IJšœ žœžœžœ˜IJšœ žœžœžœ˜IJšœ žœžœžœ˜IJ˜L˜Lšžœ˜—Lšžœ˜ L˜L˜—Lšœžœ-˜8šŸœ˜!L˜3LšœžœP˜WšŸœžœ žœžœ ˜'L˜OL˜—šžœžœžœ˜šœžœžœžœ˜ML˜L˜L˜FL˜—L˜—Lšžœžœ˜ Lšœ˜L˜—Lšœ žœ/˜;šŸœžœ˜*LšŸ œžœžœžœžœžœ žœžœžœžœ žœžœžœ˜‹Lšœ5žœ˜KLšžœžœ˜ Lšœ˜L˜—Lšœ žœ0˜<šŸœžœ˜*LšŸ œžœžœžœžœžœ žœžœžœžœ žœžœžœ˜Lšœ5žœ˜LLšžœžœ˜ Lšœ˜L˜—Lšœ žœ0˜<šŸœžœ˜*L˜ELšžœžœ˜ Lšœ˜L˜—L˜—™ Lš œžœžœžœžœžœ˜;Lšœžœ˜$šŸ œžœžœžœ˜%J™*Lšœžœžœ˜)šžœ žœž˜šžœ žœž˜šžœ žœž˜šžœ žœž˜L˜HLšžœ˜—Lšžœ˜—Lšžœ˜—Lšžœ˜—Lšžœžœ˜ L˜L˜—šŸœžœžœžœ žœžœ žœžœžœž œ˜sLš œžœžœžœžœžœžœ ˜BLšœžœžœžœžœžœžœžœžœžœžœ˜SLšœžœžœžœžœžœžœžœ˜ELš œžœžœžœžœ ˜+Lš œžœžœžœžœ ˜+Lšœžœ˜Lšœžœ˜Lš œžœ žœžœžœ˜:šžœ˜ Lšžœ˜"šžœ˜L˜ML˜]L˜8Lšœ˜——L˜$L˜$L˜$L˜L˜L˜Lšœžœžœžœ˜5Lšœ žœ ˜šžœ ˜ Lšžœ˜"šžœ˜L˜ML˜]L˜8Lšœ˜——L˜Lšœ$žœ˜9Lšœ$žœ˜9Lšœ$žœ˜9L˜L˜$L˜$L˜$Lšžœ˜ ˜L˜——Lšœžœ-™8šŸœžœ™)L™FLšžœžœ™ Lšœ™L™—Lšœžœ5˜@šŸœœ˜)L™=L™€Lšœžœžœ˜Lšœžœ˜šžœžœž˜˜)L˜=Lšœžœ'˜/š žœžœžœžœžœžœ˜3Lšœ žœ ˜Lšœ žœžœ˜Lšœ˜—L˜—Lšžœ˜—šžœ˜ L˜3L˜cLšœžœ#˜-Lšœ žœžœ˜"Lšœžœ,˜6šœ žœ˜L˜&L˜*—LšœžœD˜NL˜JLšœžœ"˜1Lšœžœ$žœžœ˜;L˜TLšœžœ˜ šžœžœ žœž˜1Lšœ žœ˜Lšœ žœ˜šžœžœ žœž˜0šžœ žœžœžœ˜:šžœ˜L˜L˜\šœ žœ˜'Lš œžœžœžœžœžœžœ˜KL˜%L˜—L˜—šžœ˜šœ žœ˜'L˜&L˜%L˜—Lšœ˜——Lš œžœžœžœžœžœ˜:Lš œžœžœžœ žœžœ˜;Lšžœ˜—Lšžœ žœžœ˜0L˜Lšžœ˜—šžœžœ˜Lšžœžœ&žœžœ˜7L˜—L˜$L˜$L˜.Lšžœžœ˜ Lšœ˜—L˜L˜—Lšœ žœ/˜;šŸœžœ˜*Lš œžœžœžœžœžœžœ˜6Lš œžœžœžœžœ˜#šŸ œžœžœžœ žœžœ žœžœžœž œ˜mLšœžœ˜Lšœžœžœ˜Lšœ žœ ˜LšœžœIžœ˜_Lš œžœ,žœžœžœžœ žœ˜Lšžœžœžœžœ˜.šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšžœ˜ L˜—Lš œ/žœžœžœžœ˜SLšžœžœ˜ Lšœ˜L˜—Lšœ žœ0˜<šŸœžœ˜*Lš œžœžœžœžœžœžœ˜6Lš œžœžœžœžœ˜#šŸ œžœžœžœ žœžœ žœžœžœž œ˜mLšœžœ˜Lšœžœžœ˜Lšœ žœ ˜Lš œžœ-žœžœ(žœ˜tLš œžœAžœžœ žœ˜nLšžœžœžœžœ˜.šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšžœ˜ L˜—Lš œ/žœžœžœžœ˜SLšžœžœ˜ Lšœ˜L˜—Lšœ žœ0˜<šŸœžœ˜*Lš œžœžœžœžœžœžœ˜6Lš œžœžœžœžœ˜#šŸ œžœžœžœ žœžœ žœžœžœž œ˜lLšœžœ˜Lš œžœ4žœžœ žœ˜_Lš œžœžœžœ$žœ˜_Lšœžœžœ˜Lšœ žœ ˜Lšžœžœžœžœ˜.šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšžœ˜ L˜—Lš œ-žœžœžœžœ˜RLšžœžœ˜ Lšœ˜L˜——™ šŸœžœžœžœ žœžœ žœžœžœž œ˜sLš œžœžœžœžœ˜'Lš œžœžœžœžœžœžœ ˜@Lš œžœžœžœžœ˜&Lšœžœ žœ˜(Lš œžœ žœžœžœ žœžœ˜BLšœžœ ˜L˜L™8Lšœ žœžœžœ ˜BLšœ žœžœžœ ˜BL˜L™8L˜Lšœ žœžœžœ ˜BLšœ žœžœžœ ˜BL˜L™8L˜Lšœ žœžœžœ ˜BLšœ žœžœžœ ˜BL˜L™9L˜Lšœ žœžœžœ˜CLšœ žœžœžœ˜CL˜L™9L˜Lšœ žœžœžœ˜DLšœ žœžœžœ˜DL˜L˜L˜ Lšžœ˜ ˜L˜——Lšœžœ-˜8šŸœžœ˜)L˜FLšžœžœ˜ Lšœ˜L˜—Lšœ žœ/˜;šŸœžœ˜*Lš œžœžœžœžœ˜"Lš œžœžœžœžœžœ˜*L˜ šŸ œžœžœžœ žœžœ žœžœžœž œ˜mLšœžœ˜Lšœžœžœ˜LšœžœIžœ˜_Lš œžœ,žœžœžœžœ žœ˜Lšžœžœžœžœ˜.šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšžœ˜ L˜—Lšœ-žœžœžœ˜QLšžœžœ˜ Lšœ˜L˜—Lšœ žœ0˜<šŸœžœ˜*Lš œžœžœžœžœ˜"Lš œžœžœžœžœžœ˜*L˜ šŸ œžœžœžœ žœžœ žœžœžœž œ˜mLšœžœ˜Lšœžœžœ˜Lš œžœ-žœžœ(žœ˜tLš œžœAžœžœ žœ˜nLšžœžœžœžœ˜.šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšžœ˜ L˜—Lšœ-žœžœžœ˜QLšžœžœ˜ Lšœ˜L˜—Lšœ žœ0˜<šŸœžœ˜*Lš œžœžœžœžœ˜"Lš œžœžœžœžœžœ˜*L˜ šŸ œžœžœžœ žœžœ žœžœžœž œ˜lLšœžœ˜Lš œžœ4žœžœ žœ˜_Lš œžœžœžœ$žœ˜_Lšœžœžœ˜Lšžœžœžœžœ˜.šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšžœ˜ L˜—Lšœ-žœžœžœ˜PLšžœžœ˜ Lšœ˜L˜——™ šŸœžœžœžœ žœžœ žœžœžœž œ˜sLšœžœžœžœ˜Lšœ žœ˜Lšœ žœ ˜Lšœ žœ˜Lšœ žœ ˜Lšœ žœžœžœžœžœžœžœ˜6Lš œ žœžœžœžœžœ˜?Lšœžœžœ žœ˜)Lšœžœžœžœžœžœžœžœ˜PLšŸœžœžœžœžœžœžœ ˜kLšœžœ ˜Lšœžœ ˜šŸœžœžœžœžœžœžœžœžœžœ žœ˜jLš žœžœžœž œžœ ˜7L˜—Lšœžœ ˜Lšœžœ˜%Lšœžœ0˜:šŸœž œžœžœžœžœžœ˜AL˜L˜L˜Lšžœ žœ ˜L˜—Lšœ#žœ˜.Lšœ#žœ˜*šžœž˜L˜L˜L˜L™0L˜3L˜L˜5L˜L˜5L˜$Lšœ$žœ˜9Lšœ$žœ˜9Lšœ$žœ˜9Lšœ$žœ˜9Lšœ$žœ˜9L˜L˜L˜L˜L˜L˜L˜Lšžœ˜—L˜$L˜$L˜$L˜$L˜$Lšžœ˜ ˜L˜——Lšœžœ.˜9šŸœžœ˜)L˜HLšžœžœ˜ Lšœ˜L˜—Lšœ žœ0˜<šŸœžœ˜*Lš œžœžœžœžœžœžœ˜6Lš œžœžœžœžœ˜#šŸ œžœžœžœ žœžœ žœžœžœž œ˜mLšœžœ˜Lšœžœžœ˜Lšœžœ˜ šžœžœžœž˜šœ žœ˜LšœFžœ˜RL˜—Lšœžœžœ˜Lšœ žœžœ˜Lšžœ˜—šžœžœ˜Lšœ žœ ˜L˜ šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšœ˜—Lšžœ˜ L˜—Lš œ/žœžœžœžœ˜SLšžœžœ˜ Lšœ˜L˜—Lšœ žœ1˜=šŸœžœ˜*Lš œžœžœžœžœžœžœ˜6Lš œžœžœžœžœ˜#šŸ œžœžœžœ žœžœ žœžœžœž œ˜mLšœžœ˜Lšœžœžœ˜Lšœžœ˜ Lšœžœžœ˜ šžœžœžœž˜Lšœžœžœ˜šœ žœ˜LšœGžœ˜SL˜—Lšœ žœžœ˜Lšžœ˜—šžœžœ˜Lšœ žœ ˜L˜ šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšœ˜—Lšžœ˜ L˜—Lš œ/žœžœžœžœ˜SLšžœžœ˜ Lšœ˜L˜—Lšœ žœ1˜=šŸœžœ˜*Lš œžœžœžœžœžœžœ˜6Lš œžœžœžœžœ˜#šŸ œžœžœžœ žœžœ žœžœžœž œ˜lLšœžœ˜ Lšœžœ žœžœ˜#šžœžœžœž˜Lšœ žœžœ˜šœ žœ˜Lšœ:žœ˜FL˜—Lšœ žœžœ˜Lšžœ˜—šžœžœ˜Lšœ žœ ˜L˜ šžœžœžœž˜Lšœ'žœ"˜NLšœ žœ˜L˜Lšžœ˜—Lšœ˜—Lšžœ˜ Lšœ˜—L˜BLšžœžœ˜ Lšœ˜L˜——™!š ŸœžœCžœžœžœ˜qšžœΟz˜LšΠkz’$£’˜@š£’œžœ˜&L˜3Lšœžœ˜(Lšœžœ˜(Lšœžœ˜$Lšœžœ˜(Lšœžœ˜(Lšœžœ˜$š Ÿœžœžœžœ  ˜7Lšžœl˜rL˜—Lšœžœ% 0˜_LšœžœJ )˜}šŸ œ œžœ žœ˜Ešžœžœžœžœ#žœžœžœ  ˜XLšœ3™3Lšœ žœžœ˜6šžœžœžœ˜!LšœS™S˜>˜/L˜ L˜}——šœžœ˜&L˜L˜ML˜R—šŸœ œžœžœ ˜CLšœG™GLšœC™Cšœ žœ˜#L˜(—Lšžœžœžœ˜6L˜—LšœD˜DL˜XLšžœ3˜:L˜—Lšžœ˜—L˜—LšžœžœžœN˜iLšžœžœ˜ L˜——Lšœ˜L˜—š Ÿ œžœžœžœžœ˜2L™Lšœžœžœ˜Lšžœžœ žœžœ ˜+L˜L˜—šœžœ¨žœžœ ˜ΤL˜—šŸœžœžœžœ˜(šžœ-žœž˜šŸ œžœ˜šžœžœ žœ˜š žœžœžœžœžœ˜?Lšœ žœ˜Lšœ)˜)Lšœ)˜)Lšœ˜—Lšœ˜—Lšœ˜—Lšœ žœ< ˜XšŸœžœ˜šžœ žœ˜Lšœ žœ˜Lšœ&˜&Lšœ&˜&Lšœ˜—Lšœ˜—šŸ œžœ˜!Lšœ ˜ Lšœ ˜ Lšœ ˜ Lšœ ˜ Lšœ˜—šŸœžœ˜ šŸœ˜L˜PLšœ˜—Lšœ ˜ Lšœ;˜;Lšœ;žœžœ˜ULšœ˜Lšœ˜—šŸ œžœ)˜:šžœ žœ ˜0šžœžœ˜L™rL™ZLšœ.˜.Lšœ.˜.Lšœžœ ˜Lš œ žœžœžœ žœžœžœ˜QLšœžœ˜Lšœ˜Lšœ˜šžœ žœž˜)J™!Lšœ˜Lšœ˜Lšœ ˜ Lšžœ˜—Lšœ  C˜MLšœ žœ˜Lšœ žœ˜Lšœ˜—šžœ˜Lšœ˜Lšœ˜——Lšœ˜—Lšœ žœ˜$šžœ˜ Lšœ žœžœžœ žœžœžœžœ˜HLš œ»žœžœžœžœ˜κLšœ˜—Lšœ'˜'šžœžœžœž˜+Lšœy˜yLšœ žœ˜LšœE˜ELšœ ˜ šœ˜Lšœ˜L˜Lšœ˜Lšœ˜Lšœ ˜$Lšœ =˜WLšœ˜Lšœ ž˜L˜—Lšžœ˜—Lšžœ8˜?Lšœ, ˜ILšœ ˜ Lšœ ™ Lšœ˜—Lšžœžœ˜ Lšœ˜—Lšœ˜L˜——™ šŸœžœžœF˜eL™VLšœžœ˜šžœžœ žœžœ žœžœ$žœ žœ˜¦š žœžœžœžœ-žœžœž˜UJ˜)š Ÿœžœžœžœžœžœ˜0L™Lšžœžœ žœžœ˜ELšœ˜—š žœ žœžœžœžœžœ˜‡Lšžœ<žœžœ˜JLšœ˜—Lšžœ˜—Lšœ˜—L™Lšžœ.žœžœ˜˜/L˜ L˜›——šœžœ˜&L˜-L˜ML˜R—Lšœžœ+˜6Lšœžœ+˜6šžœ žœ žœ˜#Jšœ žœ ?˜Tšœ žœžœ˜L˜LšžœG˜JL˜—L˜`Lšœ'˜'L˜a–ϊ[self: IIPixelArray.PixelArray, i: NAT _ 0, s: INT _ 0, f: INT _ 0, dst: IISample.SampleMap, dstMin: SF.Vec _ [s: 0, f: 0], size: SF.Vec _ [s: 32767, f: 32767], function: IISample.Function _ [dstFunc: null, srcFunc: null]]šžœžœžœ ž˜.LšžœžœD˜_L˜cšœ˜Lšœ˜Lšœ ˜ L˜Lšœ˜—LšœD˜DLšžœ˜—Lšžœžœžœ*˜HLšœ(˜(Lšœ˜—Lšœ+˜+Lšœ˜L˜—šŸœžœžœžœ˜^Jšœžœ#˜*J˜J˜J˜J˜J˜,J˜J˜0Lšœ˜L˜—šŸœžœF˜_L˜/šŸ œ˜Lšœžœ#˜-Lšœžœ˜Lšœžœ˜Lšœžœ˜Lšœžœ˜L˜L˜L˜L˜L˜—šŸœžœ žœžœ˜?šŸœ œžœ žœ˜=L˜FšŸœžœ˜-Lšœžœ™"Lšœžœ˜Lšœžœžœ\˜iLšœžœ˜šž˜Lšžœ žœ žœ žœ˜2šžœ žœžœžœ˜Lšœžœ˜Lšžœ žœ žœ žœ˜2L˜CL˜—Lšžœ˜—L˜—LšœCžœ6˜~L˜—L˜EL˜—L˜3Lšœ:žœ0˜oL˜'L˜L˜——™ š Ÿœžœžœžœ žœ˜[L˜3šžœžœžœ˜ šžœžœ(žœ"˜RLšžœG˜Kšžœ˜Lšœ€˜€šžœ žœžœ˜šŸœžœ˜#š žœžœžœžœžœžœž˜AL˜Lšžœ˜—L˜—Lšœžœ.˜8L˜NL˜#L˜—L˜——L˜—L˜L˜——™ š ŸœžœžœSžœžœ˜ŠL˜3Lš žœžœžœžœžœ˜0Lš žœžœ žœžœžœ˜%š žœ žœžœžœ 3˜NLšœžœ˜)Lšœžœ˜)Lšœžœ˜#Lšœžœ˜#Lšœžœ˜#Lšœžœ˜#Lš žœ žœžœžœ žœžœ˜3šžœ žœžœžœ žœžœžœ˜;Lš œžœ žœžœ žœžœ ˜OLšœžœžœ!˜4Lš žœžœžœžœžœ˜*šžœžœ˜LšœJ™Jšœ'˜'L˜L˜L˜ALšœ ž˜L˜—Lšžœžœžœ ˜AL˜7Lšžœ˜L˜—Lš žœ!žœžœžœ !˜]šžœžœ$˜)Lšž˜Lšœžœ˜:Lšžœžœ#žœ˜DLšœ™L˜ELšžœžœ˜L˜—Lšœ™šžœžœ˜6šžœ˜LšœA˜Ašœžœ žœžœ ˜'L˜RL˜—L˜eLšžœ*˜1L˜—šžœ˜š žœžœžœžœ#žœžœž˜JLšœžœ.˜8šžœžœ˜#šŸ œ œžœ žœ˜EL˜iLšœ˜—L˜MLšœ˜—Lšžœ˜—L˜——Lšžœžœ˜L˜—Lšžœ˜Lšžœ˜Lšžœ˜Lš žœžœžœžœ ˜OLšžœž˜ L˜—Lšžœžœ˜L˜L˜——™Lšœ žœ (˜BšŸœžœžœžœ˜ALšœžœ žœ˜*Lšžœ žœžœ˜9Lšœ˜L˜—šŸœžœžœžœžœžœžœ ˜NL˜—š Ÿ œžœžœžœžœ˜ELšžœ  œ˜"šžœ˜šžœ˜Lšžœžœžœ ˜'Lšžœžœžœ ˜(—Lšœ˜—Lšœ˜L˜—š Ÿ œžœžœžœJžœ˜ρšžœžœžœ˜%Lšœžœžœ  %˜dLšœ`˜`š Ÿœžœžœžœžœ˜6Lšžœ žœžœžœ˜VLšœ˜—šŸœžœ˜(L˜šžœ žœžœžœ˜6–[device: ImagerDevice.Device, charMask: ImagerMaskCache.CharMask, cp: ImagerDeviceVector.DVec, clipper: ImagerDeviceWorks.DeviceClipper]šžœ žœ@žœ˜Sšžœžœ žœžœ)žœžœžœ˜hL™šžœ%žœ%žœ˜ULšœžœ˜5Lšœžœ˜5Lšž˜Lšœ˜—Lšœ˜—Lšœ˜Lšžœ˜Lšœ˜—Lšœ˜—Lšœ ˜,Lšœ˜—šŸ œžœžœ žœ˜Pšžœ žœžœ˜Lšœ˜Lš žœžœžœžœžœžœ˜CLšœ˜—šžœBžœž˜Qšœ˜šžœžœžœžœžœ&žœ3žœ˜ŽLšžœ žœžœ˜6Lšœ˜—Lšœ˜—šžœžœžœžœ˜L˜3Lš žœžœžœžœ Θ˜ςšœ  ˜4Lšœ˜Lšœ ˜ LšœA˜Ašœ žœ 0˜BLšœ©™©Lšœ ž˜ Lšœžœ0ž˜QLšœžœ0˜N—L˜—Lšœ0˜0L™6Lšœ˜——Lšœ˜—Lšœžœ ˜š Ÿœžœžœžœžœ ˜-L˜3š žœžœžœžœ#žœžœž˜JLšœžœ˜Lšžœžœ žœžœ˜#Lšžœžœžœ˜Lšžœ˜—Lšžœžœžœ ˜*Lšœ˜—šžœžœ žœžœ˜;Lšœžœžœžœ%žœžœ%žœ žœžœ#˜ΉLšœžœžœ#˜0Lšœžœžœžœ˜/šŸœžœžœžœ˜Lšœ˜Lšœ žœ˜Lšœ=˜=Lšœ˜Lšœ ˜ Lšœ˜—šŸœžœžœžœ˜Hšžœ>žœž˜Mšœ˜šžœžœžœžœžœ&žœ3žœ˜ŽLšœžœ˜Lšœ˜šžœžœžœ ˜Lšœτ™τL™5—Lšœ˜Lšœ˜—Lšœ˜—Lšžœžœ˜—Lšœ˜—šŸœžœ˜&Lšœ&˜&šžœžœ˜Lšœ,˜,Lšœ,˜,Lšœ*˜*šžœžœž˜šœžœ'˜.Lšœ˜Lš žœžœžœžœžœ ˜L˜—Lšžœ˜—˜Lšœžœ ˜/Lšœžœ ˜/Lšœ žœ˜,Lšœ žœ˜,Lšœ žœ˜,Lšœ žœ˜,L˜3šžœžœ žœ˜L™šžœž˜Lšœž˜Lšœž˜Lšœžœžœžœ˜*—L™-šžœ&ž˜*Lšœ%ž˜'Lšœ%ž˜'Lšœ%žœžœžœ ˜;—š žœžœžœžœ#žœžœž˜JL™΅šžœ!ž˜&Lšœ ž˜#Lšœ ž˜#šœ žœ˜&Lšœ˜Lšžœžœ˜ L˜——Lšžœ˜—Lšžœžœ ˜šž˜˜ Lšœžœ˜)šžœ žœžœ˜Lš œ žœžœžœžœ˜;Lšœžœ žœžœ"˜DLšœžœžœ(˜5Lš žœžœžœžœ žœ˜9Lšœ ˜ Lšœa˜aLšœ˜šœžœžœžœ(˜]Lšœžœ­™Θ—L˜—šžœ ž˜L˜ Lš œ žœžœžœžœ˜:Lšžœžœžœ˜—L™aLšžœžœ˜L˜—˜ Lšžœ žœ’™£LšœK ˜_L˜—L˜—L˜—šžœ ž˜L˜ Lš œ žœžœžœžœ˜:Lšžœžœžœ˜—šžœ&žœ&ž˜UL™Lšžœžœ˜—šž˜Lšœ ˜ ˜Lšœžœ˜6Lšœžœ˜6Lšžœ˜L˜——L˜—Lšœ˜Lšžœ˜Lšžœ˜L˜—Lšžœ žœ ˜Lšœ ˜6Lšœ˜—Lšœ˜Lšžœ žœ ˜L˜DLšžœ˜L˜—Lšœ˜šžœ žœž˜LšœD˜D—Lšžœ˜Lšœ˜—Lšœ˜Lšœ˜L™——šœ™LšœZ˜ZšŸœžœžœžœ˜3Lšœ&žœ8ŸœŸœŸ œŸ œŸ œŸ œŸœŸœŸ œŸœ ŸœŸœ˜’Lšœžœ3žœ˜QLšžœ ˜Lšœ˜L˜—Lš Ÿ œžœžœžœžœ˜_LšŸ œžœžœžœžœžœžœžœžœžœ žœžœ ˜΄Lš Ÿ œžœžœžœžœ ˜TLš Ÿ œžœžœžœžœžœ˜SLšŸ œžœžœ žœžœžœžœ˜’šŸœžœžœ˜HLšžœ:˜@Lšœ˜—šŸœžœžœ˜?Lšžœ:˜@Lšœ˜—šŸ œžœ*žœ ˜NLšžœ ˜Lšœ˜—šŸœžœ*žœ ˜RLšžœ ˜Lšœ˜—šŸœžœ*žœžœ˜HLšžœ˜Lšœ˜—šŸ œžœ*žœ ˜NLšžœ ˜Lšœ˜—šŸ œ!˜+Lšœ»™»L˜ULšœ˜L˜—Lšœ!˜!šŸœžœžœ˜(Lšœžœ˜,Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšžœ˜ Lšœ˜L˜—šŸœžœžœV˜oLšœ΅™΅Lšœžœ-˜4Lšœžœ0˜7Lšœžœ0˜7šœ˜Lšœf™fLšœ\˜\Lšœ˜—Lšœ žœ  ˜(L™τLšžœžœ˜.Lšžœžœ˜.Lšžœžœ˜.Lšžœžœ˜.L˜WLšœ˜L˜——šœ™™&L˜—Lšœžœžœ˜1Lšœžœ/˜HLšœ*žœ˜.šŸœžœžœžœ˜BLš Ÿœžœžœžœžœ;žœ˜Lšœ$˜$Lšžœ žœžœ žœ˜;Lšžœ ˜Lšœ˜L˜—šŸœžœžœ˜Cšžœ žœžœ˜Lšœžœ˜šžœžœžœ˜#Lšœ2˜2Lšœžœ˜Lšœ˜—šžœžœžœ˜$Lšœ3˜3Lšœžœ˜Lšœ˜—šžœžœžœ˜&Lšœ5˜5Lšœžœ˜Lšœ˜—Lšœžœ˜Lš œŸœžœžœžœ+˜CLšœ˜—Lšœ˜——™ šœ.žœ˜PLšŸœ˜LšŸœ˜LšŸ œ˜%LšŸ œ˜LšŸ œ˜LšŸœ˜+LšŸ œ˜LšŸœ˜'LšŸ œ˜LšŸ œ˜#LšŸœ˜Lšœ˜L˜—š Ÿ œžœžœižœžœ˜¦šœžœ˜=L˜Lšœžœ2žœžœ˜dL˜ L˜ šœžœ žœžœž˜%šžœ ˜#Lšœ  ˜L˜ALšœž˜L˜——Lšœ ˜ Lšœ˜—Lšžœ ˜L˜L˜—L˜—Lšžœ˜—…—"0™%