DIRECTORY Basics USING [bitsPerWord, LongMult], Imager USING [black, Box, ClassRep, Color, ColorOperator, Context, ContextRep, DoSave, DoSaveAll, Error, MaskBits, MaskRectangle, MaskRectangleI, metersPerInch, PixelArray, SetColor, SetXY, StateRep, Trans, white], ImagerBackdoor USING [Clipper, Visibility], ImagerBox USING [BoxFromRect], ImagerCache USING [Ref], ImagerDevice USING [CharMask, Class, Device, DeviceBox, RunProc], ImagerFont USING [Font, FontImplRep, XChar, XStringProc], ImagerFontPrivate USING [FontImpl, FontImplRep, MakeFontAtom], ImagerManhattan USING [BoundingBox, Copy, CreateFromBox, Destroy, Polygon], ImagerManhattanExtras USING [DestructiveClip, DestructiveDifference, DestructiveIntersection], ImagerMask USING [RunsFromBits], ImagerPath USING [MapOutline, PathProc], ImagerPixelArray USING [MaxSampleValue, PixelArray, UnsafeGetBits], ImagerPixelMap USING [DeviceRectangle, Intersect], ImagerPrivate USING [Class, ClassRep], ImagerRaster USING [FontTuner], ImagerRasterPrivate USING [AndFlags, CharArrayRep, Data, DataRep, Flags, NotFlags, OrFlags, RasterShow, RasterShowText], ImagerSample USING [GetPointSamples, ObtainScratchBuffer, ReleaseScratchBuffer, Sample, SampleBuffer, Sampler, SamplerRep, SetSamplerIncrements, SetSamplerPosition], ImagerScanConverter USING [BoundingBox, ConvertToManhattanPolygon, ConvertToRuns, CreatePath, DevicePath], ImagerState USING [ChangeFlags, notChanged, State, StateClip, StateClipRectangle, StateClipRectangleI, StateConcatT, StateCorrect, StateCorrectMask, StateCorrectSpace, StateDontCorrect, StateDoSave, StateGetClipper, StateGetColor, StateGetCP, StateGetFont, StateGetInt, StateGetReal, StateGetStrokeDashes, StateGetT, StateMaskUnderline, StateMove, StateRep, StateRotateT, StateScale2T, StateSetClipper, StateSetColor, StateSetCorrectMeasure, StateSetCorrectTolerance, StateSetFont, StateSetGray, StateSetInt, StateSetReal, StateSetSampledBlack, StateSetSampledColor, StateSetStrokeDashes, StateSetT, StateSetXY, StateSetXYRel, StateSpace, StateStartUnderline, StateTranslateT], ImagerStroke USING [PathFromStroke], ImagerTransformation USING [ApplyCat, ApplyPostConcat, ApplyPostTranslate, ApplyPreConcat, ApplyPreTranslate, ApplyTranslateTo, InverseTransform, InverseTransformRectangle, Rectangle, Rotate, Scale, Scale2, Transform, Transformation, TransformationRep], Process USING [CheckForAbort], Real USING [Fix, Round], RealFns USING [AlmostEqual], Rope USING [ROPE], Vector2 USING [Div, VEC], VM USING [AddressForPageNumber, Free, Interval, PagesForWords, SimpleAllocate]; ImagerRasterImpl: CEDAR PROGRAM IMPORTS Basics, Imager, ImagerBox, ImagerFontPrivate, ImagerManhattan, ImagerManhattanExtras, ImagerMask, ImagerPath, ImagerPixelArray, ImagerPixelMap, ImagerRasterPrivate, ImagerSample, ImagerScanConverter, ImagerState, ImagerStroke, ImagerTransformation, Process, Real, RealFns, Vector2, VM EXPORTS Imager, ImagerBackdoor, ImagerFont, ImagerRaster, ImagerRasterPrivate ~ BEGIN OPEN ImagerState, ImagerRasterPrivate; Class: TYPE ~ ImagerPrivate.Class; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep State: TYPE ~ ImagerState.State; StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep ROPE: TYPE ~ Rope.ROPE; CharMask: TYPE ~ ImagerDevice.CharMask; Clipper: TYPE ~ ImagerBackdoor.Clipper; Color: TYPE ~ Imager.Color; ColorOperator: TYPE ~ Imager.ColorOperator; Context: TYPE ~ Imager.Context; Device: TYPE ~ ImagerDevice.Device; DeviceBox: TYPE ~ ImagerDevice.DeviceBox; RunProc: TYPE ~ ImagerDevice.RunProc; DevicePath: TYPE ~ ImagerScanConverter.DevicePath; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; Font: TYPE ~ ImagerFont.Font; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; PathProc: TYPE ~ ImagerPath.PathProc; PixelArray: TYPE ~ Imager.PixelArray; Rectangle: TYPE ~ ImagerTransformation.Rectangle; Transformation: TYPE ~ ImagerTransformation.Transformation; TransformationRep: TYPE ~ ImagerTransformation.TransformationRep; VEC: TYPE ~ Vector2.VEC; XChar: TYPE ~ ImagerFont.XChar; XStringProc: TYPE ~ ImagerFont.XStringProc; FontImpl: TYPE ~ ImagerFontPrivate.FontImpl; FontImplRep: PUBLIC TYPE ~ ImagerFontPrivate.FontImplRep; -- export to to ImagerFont DeviceBoxFromDeviceRectangle: PROC [r: DeviceRectangle] RETURNS [DeviceBox] ~ { RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]]; }; DeviceRectangleFromDeviceBox: PROC [b: DeviceBox] RETURNS [DeviceRectangle] ~ { RETURN[[sMin: b.smin, fMin: b.fmin, sSize: b.smax-b.smin, fSize: b.fmax-b.fmin]]; }; Create: PUBLIC PROC [device: Device, pixelUnits: BOOL, fontCache: ImagerCache.Ref, rastWeight: REAL, fontTuner: ImagerRaster.FontTuner, class: Class ] RETURNS [Context] ~ { data: Data ~ NEW[DataRep _ [device: device]]; state: State ~ NEW[StateRep _ []]; metersToSurface: VEC ~ device.surfaceUnitsPerInch.Div[Imager.metersPerInch]; data.rastWeight _ rastWeight; data.fontTuner _ fontTuner; data.viewToDevice _ ImagerTransformation.Scale[1]; data.clientToDevice _ ImagerTransformation.Scale[1]; data.maskToDevice _ ImagerTransformation.Scale[1]; data.charToDevice _ ImagerTransformation.Scale[1]; data.fontCache _ fontCache; data.charArray _ NEW[CharArrayRep _ ALL[NIL]]; DViewReset[data]; IF pixelUnits THEN state.T _ ImagerTransformation.Scale[1] ELSE state.T _ ImagerTransformation.Scale2[metersToSurface]; state.color _ Imager.black; IF class=NIL THEN class _ rasterClass; RETURN[NEW[Imager.ContextRep _ [class: class, state: state, data: data]]]; }; CreateClass: PUBLIC PROC [type: ATOM] RETURNS [Class] ~ { class: Class ~ NEW[ClassRep _ rasterClass^]; class.type _ type; RETURN[class]; }; ValidateClientToDevice: PROC [data: Data, state: State] ~ { data.clientToDevice.ApplyCat[state.T, data.viewToDevice]; data.valid.clientToDevice _ TRUE; }; ValidateFontInfo: PROC [data: Data, state: State] ~ { font: Font ~ state.font; IF font = NIL THEN ERROR Imager.Error[[code: $noFont, explanation: "Failed to set font before calling Show."]] ELSE { impl: FontImpl ~ font.impl; IF NOT data.valid.clientToDevice THEN ValidateClientToDevice[data, state]; data.charToDevice^ _ data.clientToDevice^; data.charToDevice.ApplyTranslateTo[[0, 0]]; data.charToDevice.ApplyPreConcat[font.charToClient]; data.fontAtom _ ImagerFontPrivate.MakeFontAtom[impl.typeface, data.charToDevice]; data.valid.fontInfo _ TRUE; }; }; ValidateClientClipper: PROC [data: Data, state: State] ~ { cc: ImagerManhattan.Polygon _ NIL; ImagerManhattan.Destroy[data.clientClipMask]; cc _ ImagerManhattan.Copy[data.viewClipMask]; IF data.specialClipPresent THEN { cc _ ImagerManhattanExtras.DestructiveClip[cc, data.specialClipRect]; }; FOR each: Clipper _ state.clipper, each.rest UNTIL each=NIL DO path: PathProc ~ { ImagerPath.MapOutline[outline: each.first.outline, moveTo: moveTo, lineTo: lineTo, curveTo: curveTo, conicTo: conicTo, arcTo: arcTo] }; this: ManhattanPolygon; data.devicePath _ ImagerScanConverter.CreatePath[path: path, transformation: data.viewToDevice, clipBox: data.viewClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath, clipBox: data.viewClipBox, parityFill: each.first.parity]; cc _ CombineManhattan[cc, this, each.first.exclude]; ImagerManhattan.Destroy[this]; ENDLOOP; data.clientClipMask _ cc; data.clientClipRect _ ImagerManhattan.BoundingBox[cc]; data.clientClipBox _ DeviceBoxFromDeviceRectangle[data.clientClipRect]; data.clientClipBoxOnly _ (cc=NIL OR cc.rest=NIL); data.valid.clientClipper _ TRUE; }; ValidateColor: PROC [data: Data, state: State] ~ { device: Device ~ data.device; device.class.SetColor[device, state.color, data.viewToDevice]; data.valid.deviceColor _ TRUE; }; ValidatePriority: PROC [data: Data, state: State] ~ { device: Device ~ data.device; device.class.SetPriority[device, state.np.priorityImportant#0]; data.valid.devicePriority _ TRUE; }; NoteStateChanges: PUBLIC PROC [data: Data, state: State] ~ { changed: ImagerState.ChangeFlags ~ state.changed; state.changed _ ImagerState.notChanged; IF changed.T THEN data.valid.clientToDevice _ data.valid.fontInfo _ FALSE; IF changed.color THEN data.valid.deviceColor _ FALSE; IF changed.priority THEN data.valid.devicePriority _ FALSE; IF changed.clipper THEN data.valid.clientClipper _ FALSE; IF changed.font THEN data.valid.fontInfo _ FALSE; }; ValidateIfNeeded: PUBLIC PROC [data: Data, state: State, needs: Flags] ~ { fix: Flags ~ AndFlags[needs, NotFlags[data.valid]]; IF fix.clientToDevice THEN ValidateClientToDevice[data, state]; IF fix.deviceColor THEN ValidateColor[data, state]; IF fix.devicePriority THEN ValidatePriority[data, state]; IF fix.clientClipper THEN ValidateClientClipper[data, state]; IF fix.fontInfo THEN ValidateFontInfo[data, state]; IF AndFlags[data.valid, needs]#needs THEN ERROR; }; Validate: PROC [data: Data, state: State, needs: Flags] ~ INLINE { IF state.changed#ImagerState.notChanged THEN NoteStateChanges[data, state]; IF AndFlags[data.valid, needs]#needs THEN ValidateIfNeeded[data, state, needs]; }; Verify: PROC [data: Data, needs: Flags] ~ INLINE { IF AndFlags[data.valid, needs]#needs THEN ERROR; }; runsFromPathNeeds: Flags ~ [clientClipper: TRUE]; RunsFromPath: PROC [data: Data, path: PathProc, parity: BOOL _ FALSE, transformation: Transformation _ NIL, action: PROC [bounds: DeviceBox, runs: PROC[RunProc]] ] ~ { clipBox: DeviceRectangle ~ data.clientClipRect; clipMask: ManhattanPolygon ~ data.clientClipMask; devicePath: DevicePath ~ ImagerScanConverter.CreatePath[path: path, transformation: transformation, clipBox: clipBox, scratch: data.devicePath]; rect: DeviceRectangle ~ ImagerScanConverter.BoundingBox[devicePath]; bounds: DeviceBox ~ DeviceBoxFromDeviceRectangle[rect.Intersect[clipBox]]; runs: PROC [run: RunProc] ~ { IF clipMask#NIL AND clipMask.rest=NIL THEN { ImagerScanConverter.ConvertToRuns[devicePath: devicePath, runProc: run, clipBox: clipMask.first, parityFill: parity]; } ELSE { rem: ManhattanPolygon _ clipMask; clip: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { WHILE rem#NIL AND rem.first.sMin+rem.first.sSize<=sMin DO rem _ rem.rest ENDLOOP; FOR l: LIST OF DeviceRectangle _ rem, l.rest UNTIL l=NIL OR l.first.sMin>sMin DO fMinRun: INTEGER ~ MAX[l.first.fMin, fMin]; fMaxRun: INTEGER ~ MIN[l.first.fMin+l.first.fSize, fMin+fSize]; IF fMaxRun > fMinRun THEN run[sMin, fMinRun, fMaxRun-fMinRun]; ENDLOOP; }; ImagerScanConverter.ConvertToRuns[devicePath: devicePath, runProc: clip, clipBox: clipBox, parityFill: parity]; }; }; Verify[data, runsFromPathNeeds]; Process.CheckForAbort[]; action[bounds, runs]; data.devicePath _ devicePath; }; maskRunsNeeds: Flags ~ OrFlags[runsFromPathNeeds, [deviceColor: TRUE, devicePriority: TRUE]]; MaskRuns: PROC [data: Data, path: PathProc, parity: BOOL _ FALSE, transformation: Transformation _ NIL] ~ { maskRunsAction: PROC [bounds: DeviceBox, runs: PROC[RunProc]] ~ { device: Device ~ data.device; device.class.MaskRuns[device: device, bounds: bounds, runs: runs]; }; Verify[data, maskRunsNeeds]; RunsFromPath[data: data, path: path, parity: parity, transformation: transformation, action: maskRunsAction]; }; ClipBoxToMask: PUBLIC PROC [box: DeviceBox, mask: ManhattanPolygon, action: PROC[DeviceBox]] ~ { FOR list: LIST OF DeviceRectangle _ mask, list.rest UNTIL list=NIL DO c: DeviceBox ~ DeviceBoxFromDeviceRectangle[list.first]; IF box.sminc.smin AND box.fminc.fmin THEN action[[smin: MAX[box.smin, c.smin], smax: MIN[box.smax, c.smax], fmin: MAX[box.fmin, c.fmin], fmax: MIN[box.fmax, c.fmax]]]; ENDLOOP; }; maskNeeds: Flags ~ OrFlags[maskRunsNeeds, [clientToDevice: TRUE]]; RasterMaskFill: PROC [context: Context, path: PathProc, parity: BOOL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; Validate[data, state, maskNeeds]; MaskRuns[data: data, path: path, parity: parity, transformation: data.clientToDevice]; }; }; specialRectangles: BOOL _ TRUE; RasterMaskRectangle: PROC [context: Context, r: Rectangle] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; device: Device ~ data.device; Validate[data, state, maskNeeds]; IF specialRectangles AND data.clientToDevice.form # 0 AND device.class.MaskBoxes#NIL THEN { clientToDevice: Transformation ~ data.clientToDevice; clip: DeviceBox ~ data.clientClipBox; p0: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x, r.y]]; p1: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x+r.w, r.y+r.h]]; s0: INT _ Real.Fix[MAX[MIN[p0.x+0.5, clip.smax], clip.smin]]; s1: INT _ Real.Fix[MAX[MIN[p1.x+0.5, clip.smax], clip.smin]]; f0: INT _ Real.Fix[MAX[MIN[p0.y+0.5, clip.fmax], clip.fmin]]; f1: INT _ Real.Fix[MAX[MIN[p1.y+0.5, clip.fmax], clip.fmin]]; Process.CheckForAbort[]; IF s1box.smin AND fminbox.fmin THEN { IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MaskBoxes[device: device, bounds: box, boxes: boxes]; }; } ELSE RasterMaskRectangle[context, [x: x, y: y, w: w, h: h]]; }; }; RasterMaskUnderline: PROC [context: Context, dy, h: REAL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { end: VEC ~ state.T.InverseTransform[state.p.cp]; -- current position (client space) start: VEC ~ [state.np.underlineStart, end.y-dy-h]; -- corner of underline (client space) underline: PROC ~ { Imager.SetXY[context, start]; Imager.Trans[context]; Imager.MaskRectangle[context, [0, 0, end.x-start.x, h]]; }; Imager.DoSaveAll[context, underline]; }; }; RasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; strokePath: PathProc ~ { ImagerStroke.PathFromStroke[path: path, closed: closed, m: data.clientToDevice, width: state.np.strokeWidth, end: state.np.strokeEnd, joint: state.np.strokeJoint, moveTo: moveTo, lineTo: lineTo, conicTo: conicTo, curveTo: curveTo]; }; Validate[data, state, maskNeeds]; MaskRuns[data: data, path: strokePath]; }; }; RasterMaskVector: PROC [context: Context, p1, p2: VEC] ~ { vectorPath: PathProc ~ { moveTo[p1]; lineTo[p2] }; RasterMaskStroke[context: context, path: vectorPath, closed: FALSE]; }; bitsPerWord: NAT ~ Basics.bitsPerWord; SMul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ INLINE {RETURN [IF a#0 THEN Smul[a,b] ELSE 0]}; Smul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ { HighBit: PROC [a: INTEGER] RETURNS [[0..1]] ~ INLINE { RETURN [LOOPHOLE[a, PACKED ARRAY [0..Basics.bitsPerWord) OF [0..1]][0]]; }; Card: PROC [i: INTEGER] RETURNS [NAT] ~ INLINE {RETURN [LOOPHOLE[i]]}; SELECT HighBit[a]*2+HighBit[b] FROM 0 => RETURN [Basics.LongMult[Card[a], Card[b]]]; 1 => RETURN [-Basics.LongMult[Card[a], Card[-b]]]; 2 => RETURN [-Basics.LongMult[Card[-a], Card[b]]]; 3 => RETURN [Basics.LongMult[Card[-a], Card[-b]]]; ENDCASE => ERROR; }; worryReal: REAL _ LAST[NAT]*0.5; IsAllInteger: 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, Real.Round[r], -18] THEN RETURN [TRUE]; }; RETURN [FALSE]; }; RETURN [Is[m.a] AND Is[m.b] AND Is[m.c] AND Is[m.d] AND Is[m.e] AND Is[m.f]]; }; Ceiling: PROC [r: REAL] RETURNS [i: INT] ~ { i _ Real.Round[r]; IF i < r THEN i _ i + 1; }; SF: TYPE ~ RECORD [s, f: INT]; ClippedBounds: PROC [clipBox: DeviceBox, p0, p1: SF] RETURNS [DeviceBox] ~ { 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.smin]; p0.f _ MAX[p0.f, clipBox.fmin]; p1.s _ MIN[p1.s, clipBox.smax]; p1.f _ MIN[p1.f, clipBox.fmax]; IF p0.s < p1.s AND p0.f < p1.f THEN RETURN [[smin: p0.s, fmin: p0.f, smax: p1.s, fmax: p1.f]] ELSE RETURN [[smin: clipBox.smin, fmin: clipBox.fmin, smax: clipBox.smin, fmax: clipBox.fmin]]; }; specialMaskBits: BOOL _ TRUE; MaskSampledBits: PROC [data: Data, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT, sSize, fSize: NAT, maskToDevice: Transformation, constant: BOOL] ~ { device: Device ~ data.device; SELECT TRUE FROM maskToDevice.form=3 AND maskToDevice.integerTrans AND device.class.MaskBits#NIL => { smin: INT ~ maskToDevice.tx; smax: INT ~ smin+sSize; fmin: INT ~ maskToDevice.ty; fmax: INT ~ fmin+fSize; box: DeviceBox _ data.clientClipBox; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.clientClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MaskBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine, ts: smin-sMin, tf: fmin-fMin, boxes: boxes]; }; }; specialMaskBits AND maskToDevice.form#0 AND IsAllInteger[maskToDevice] AND constant AND device.class.MaskBoxes#NIL => { a: INTEGER ~ Real.Round[maskToDevice.a]; b: INTEGER ~ Real.Round[maskToDevice.b]; c: INTEGER ~ maskToDevice.tx; d: INTEGER ~ Real.Round[maskToDevice.d]; e: INTEGER ~ Real.Round[maskToDevice.e]; f: INTEGER ~ maskToDevice.ty; SF: TYPE ~ RECORD [s, f: INT]; Map: PROC [x, y: INTEGER] RETURNS [SF] ~ { RETURN [[SMul[a, x] + SMul[b, y] + c, SMul[d, x] + SMul[e, y] + f]]; }; box: DeviceBox ~ ClippedBounds[data.clientClipBox, [c, f], Map[sSize, fSize]]; boxes: PROC[action: PROC[DeviceBox]] ~ { myAction: PROC[clipBox: DeviceBox] ~ { run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { clippedBox: DeviceBox ~ ClippedBounds[clipBox, Map[sMin, fMin], Map[sMin+1, fMin+fSize]]; IF clippedBox.smin # clippedBox.smax THEN action[clippedBox]; }; sb: Imager.Box ~ ImagerBox.BoxFromRect[ ImagerTransformation.InverseTransformRectangle[maskToDevice, [ x: clipBox.smin, y: clipBox.fmin, w: clipBox.smax-clipBox.smin, h: clipBox.fmax-clipBox.fmin ]] ]; srcBox: DeviceBox ~ ClippedBounds[ clipBox: [smin: 0, fmin: 0, smax: sSize, fmax: fSize], p0: [Real.Fix[sb.xmin], Real.Fix[sb.ymin]], p1: [Ceiling[sb.xmax], Ceiling[sb.ymax]] ]; ImagerMask.RunsFromBits[base: base, wordsPerLine: wordsPerLine, sBits: srcBox.smin+sMin, fBits: srcBox.fmin+fMin, sSize: srcBox.smax-srcBox.smin, fSize: srcBox.fmax-srcBox.fmin, sRuns: srcBox.smin, fRuns: srcBox.fmin, run: run]; }; ClipBoxToMask[box: box, mask: data.clientClipMask, action: myAction]; }; device.class.MaskBoxes[device: device, bounds: box, boxes: boxes]; }; ENDCASE => HardMaskSampledBits[data, base, wordsPerLine, sMin, fMin, sSize, fSize, maskToDevice]; }; HardMaskSampledBits: PROC [data: Data, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT, sSize, fSize: NAT, maskToDevice: Transformation] ~ { sampler: ImagerSample.Sampler ~ NEW[ImagerSample.SamplerRep _ []]; maskBitsAction: PROC [bounds: DeviceBox, runs: PROC [RunProc]] ~ { device: Device ~ data.device; buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchBuffer[ iSize: 1, jSize: bounds.fmax-bounds.fmin]; maskBitsRuns: PROC [run: RunProc] ~ { maskBitsRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { state: {off, on} _ off; f0: NAT _ 0; ImagerSample.GetPointSamples[sampler: sampler, s: sMin, f: fMin, buffer: buffer, bi: 0, bj: 0, count: fSize]; FOR f: NAT IN[0..fSize) DO x: ImagerSample.Sample ~ buffer[f]; SELECT state FROM off => IF x#0 THEN { f0 _ f; state _ on }; on => IF x=0 THEN { run[sMin: sMin, fMin: fMin+f0, fSize: f-f0]; state _ off }; ENDCASE => ERROR; ENDLOOP; IF state=on THEN run[sMin: sMin, fMin: fMin+f0, fSize: fSize-f0]; }; runs[maskBitsRun]; }; ImagerSample.SetSamplerPosition[sampler: sampler, m: maskToDevice, s: bounds.smin, f: bounds.fmin]; device.class.MaskRuns[device: device, bounds: bounds, runs: maskBitsRuns]; ImagerSample.ReleaseScratchBuffer[buffer]; }; maskBoundary: PathProc ~ { moveTo[[0, 0]]; lineTo[[sSize, 0]]; lineTo[[sSize, fSize]]; lineTo[[0, fSize]]; }; sampler.base _ base; sampler.wordsPerLine _ wordsPerLine; sampler.bitsPerSample _ 1; sampler.sMin _ sMin; sampler.fMin _ fMin; sampler.sSize _ sSize; sampler.fSize _ fSize; ImagerSample.SetSamplerIncrements[sampler, maskToDevice]; RunsFromPath[data: data, path: maskBoundary, transformation: maskToDevice, action: maskBitsAction]; }; bitsToClient: Transformation ~ ImagerTransformation.Rotate[-90]; RasterMaskBits: PROC [context: Context, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT _ 0, sSize, fSize: NAT, tx, ty: INTEGER] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; maskToDevice: Transformation ~ data.maskToDevice; Validate[data, state, maskNeeds]; maskToDevice^ _ bitsToClient^; maskToDevice.ApplyPostTranslate[[tx, ty]]; maskToDevice.ApplyPostConcat[data.clientToDevice]; MaskSampledBits[data: data, base: base, wordsPerLine: wordsPerLine, sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize, maskToDevice: maskToDevice, constant: state.color.tag = constant]; }; }; RasterDrawBits: PROC [context: Context, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT _ 0, sSize, fSize: NAT, tx, ty: INTEGER] ~ { drawBitsAction: PROC ~ { Imager.SetColor[context: context, color: Imager.white]; Imager.MaskRectangleI[context: context, x: tx, y: ty, w: fSize, h: -sSize]; Imager.SetColor[context: context, color: Imager.black]; Imager.MaskBits[context: context, base: base, wordsPerLine: wordsPerLine, sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize, tx: tx, ty: ty]; }; state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; maskToDevice: Transformation ~ data.maskToDevice; Validate[data, state, maskNeeds]; maskToDevice^ _ bitsToClient^; maskToDevice.ApplyPostTranslate[[tx, ty]]; maskToDevice.ApplyPostConcat[data.clientToDevice]; IF maskToDevice.form=3 AND maskToDevice.integerTrans AND data.device.class.DrawBits # NIL THEN { smin: INT ~ maskToDevice.tx; smax: INT ~ smin+sSize; fmin: INT ~ maskToDevice.ty; fmax: INT ~ fmin+fSize; box: DeviceBox _ data.clientClipBox; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.clientClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { device: Device ~ data.device; IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.DrawBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine, ts: smin-sMin, tf: fmin-fMin, boxes: boxes]; }; } ELSE Imager.DoSave[context, drawBitsAction]; }; }; RasterMaskPixel: PROC [context: Context, pa: PixelArray] ~ { state: State ~ context.state; IF pa.samplesPerPixel=1 THEN NULL ELSE ERROR Imager.Error[[code: $illegalPixelMask, explanation: "MaskPixel argument must have one sample per pixel."]]; IF ImagerPixelArray.MaxSampleValue[pa, 0]=1 THEN NULL ELSE ERROR Imager.Error[[code: $illegalPixelMask, explanation: "MaskPixel argument must have one bit per sample."]]; IF state.np.noImage=0 THEN TRUSTED { rast: NAT ~ (pa.fSize+bitsPerWord)/bitsPerWord; words: INT ~ Basics.LongMult[pa.sSize, rast]; vm: VM.Interval ~ VM.SimpleAllocate[VM.PagesForWords[words]]; pointer: LONG POINTER ~ VM.AddressForPageNumber[vm.page]; DoMask: UNSAFE PROC ~ { data: Data ~ NARROW[context.data]; maskToDevice: Transformation ~ data.maskToDevice; Validate[data, state, maskNeeds]; maskToDevice.ApplyCat[pa.m, data.clientToDevice]; ImagerPixelArray.UnsafeGetBits[pa: pa, i: 0, s: 0, f: 0, dst: [word: pointer, bit: 0], dstBpl: rast*bitsPerWord, width: pa.fSize, height: pa.sSize]; MaskSampledBits[data: data, base: pointer, wordsPerLine: rast, sMin: 0, fMin: 0, sSize: pa.sSize, fSize: pa.fSize, maskToDevice: maskToDevice, constant: state.color.tag = constant]; }; DoMask[ ! UNWIND => VM.Free[vm]]; VM.Free[vm]; }; }; RasterGetBounds: PROC [context: Context] RETURNS [Rectangle] ~ { state: State ~ context.state; data: Data ~ NARROW[context.data]; Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]]; RETURN[data.clientToDevice.InverseTransformRectangle[[ x: data.clientClipRect.sMin, y: data.clientClipRect.fMin, w: data.clientClipRect.sSize, h: data.clientClipRect.fSize ]]]; }; DViewReset: PROC [data: Data] ~ { data.viewToDevice^ _ data.device.surfaceToDevice^; data.viewClipBox _ DeviceRectangleFromDeviceBox[data.device.box]; ImagerManhattan.Destroy[data.viewClipMask]; data.viewClipMask _ ImagerManhattan.CreateFromBox[data.viewClipBox]; data.valid.clientToDevice _ FALSE; data.valid.clientClipper _ FALSE; data.valid.fontInfo _ FALSE; data.valid.deviceColor _ FALSE; }; DViewTranslate: PROC [data: Data, x, y: INTEGER] ~ { data.viewToDevice.ApplyPreTranslate[[x, y]]; data.valid.clientToDevice _ FALSE; data.valid.clientClipper _ FALSE; data.valid.deviceColor _ FALSE; }; DViewClipRectangleI: PROC [data: Data, x, y, w, h: INTEGER, exclude: BOOL] ~ { viewToDevice: Transformation ~ data.viewToDevice; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { old: ManhattanPolygon ~ data.viewClipMask; smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h]; fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w]; rect: DeviceRectangle ~ [sMin: smin, fMin: fmin, sSize: ABS[h], fSize: ABS[w]]; IF exclude THEN { t: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[rect]; data.viewClipMask _ ImagerManhattanExtras.DestructiveDifference[data.viewClipMask, t]; ImagerManhattan.Destroy[t]; } ELSE data.viewClipMask _ ImagerManhattanExtras.DestructiveClip[data.viewClipMask, rect]; data.viewClipBox _ ImagerManhattan.BoundingBox[data.viewClipMask]; data.valid.clientClipper _ FALSE; } ELSE { path: PathProc ~ { moveTo[[x, y]]; lineTo[[x+w, y]]; lineTo[[x+w, y+h]]; lineTo[[x, y+h]]; }; DViewClip[data: data, path: path, parity: FALSE, exclude: exclude]; }; }; CombineManhattan: PROC [a, b: ManhattanPolygon, exclude: BOOL] RETURNS [ManhattanPolygon] ~ { IF exclude THEN RETURN [ImagerManhattanExtras.DestructiveDifference[a, b]] ELSE RETURN [ImagerManhattanExtras.DestructiveIntersection[a, b]]; }; DViewClip: PROC [data: Data, path: PathProc, parity: BOOL, exclude: BOOL] ~ { this: ManhattanPolygon; data.devicePath _ ImagerScanConverter.CreatePath[path: path, transformation: data.viewToDevice, clipBox: data.viewClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath, clipBox: data.viewClipBox, parityFill: parity]; data.viewClipMask _ CombineManhattan[data.viewClipMask, this, exclude]; ImagerManhattan.Destroy[this]; data.viewClipBox _ ImagerManhattan.BoundingBox[data.viewClipMask]; data.valid.clientClipper _ FALSE; }; ViewReset: PUBLIC PROC [context: Context] ~ { WITH context.data SELECT FROM data: Data => DViewReset[data]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewReset not implemented"]]; }; ViewTranslateI: PUBLIC PROC [context: Context, x, y: INTEGER] ~ { WITH context.data SELECT FROM data: Data => DViewTranslate[data, x, y]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewTranslateI not implemented"]]; }; ViewClip: PUBLIC PROC [context: Context, path: PathProc, parity: BOOL, exclude: BOOL] ~ { WITH context.data SELECT FROM data: Data => DViewClip[data: data, path: path, parity: parity, exclude: exclude]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewClip not implemented"]]; }; ViewClipRectangleI: PUBLIC PROC [context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ { WITH context.data SELECT FROM data: Data => DViewClipRectangleI[data: data, x: x, y: y, w: w, h: h, exclude: exclude]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewClipRectangleI not implemented"]]; }; ViewFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { state: State ~ context.state; IF state=NIL THEN ERROR Imager.Error[[$unimplemented, "ViewFromClient not implemented"]]; RETURN[state.T.Transform[p]]; }; ClientFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { state: State ~ context.state; IF state=NIL THEN ERROR Imager.Error[[$unimplemented, "ClientFromView not implemented"]]; RETURN[state.T.InverseTransform[p]]; }; DeviceFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => RETURN[data.viewToDevice.Transform[p]]; ENDCASE => ERROR Imager.Error[[$unimplemented, "DeviceFromView not implemented"]]; }; ViewFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => RETURN[data.viewToDevice.InverseTransform[p]]; ENDCASE => ERROR Imager.Error[[$unimplemented, "ViewFromDevice not implemented"]]; }; DeviceFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => { Validate[data, context.state, [clientToDevice: TRUE]]; RETURN[data.clientToDevice.Transform[p]]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "DeviceFromClient not implemented"]]; }; ClientFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => { Validate[data, context.state, [clientToDevice: TRUE]]; RETURN[data.clientToDevice.InverseTransform[p]]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "ClientFromDevice not implemented"]]; }; MoveViewRectangle: PUBLIC PROC [context: Context, width, height, fromX, fromY, toX, toY: INTEGER] ~ { state: State ~ context.state; WITH context.data SELECT FROM data: Data => { Validate[data, state, maskNeeds]; DMoveViewRectangle[data: data, width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "MoveViewRectangle not implemented"]]; }; DMoveViewRectangle: PROC [data: Data, width, height, fromX, fromY, toX, toY: INTEGER] ~ { viewToDevice: Transformation ~ data.viewToDevice; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { smax: INT _ INT[viewToDevice.tx]-toY; smin: INT _ smax-height; fmin: INT _ INT[viewToDevice.ty]+toX; fmax: INT _ fmin+width; box: DeviceBox _ DeviceBoxFromDeviceRectangle[data.viewClipBox]; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.viewClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { device: Device ~ data.device; IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MoveBoxes[device: device, ts: fromY-toY, tf: toX-fromX, boxes: boxes]; }; } ELSE ERROR; -- hard case }; Visibility: TYPE ~ ImagerBackdoor.Visibility; TestViewRectangle: PUBLIC PROC [context: Imager.Context, x, y, w, h: INTEGER] RETURNS [Visibility] ~ { state: State ~ context.state; WITH context.data SELECT FROM data: Data => { Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]]; RETURN[DTestViewRectangle[data: data, x: x, y: y, w: w, h: h]]; }; ENDCASE => ERROR Imager.Error[[$unimplemented, "TestRectangleI not implemented"]]; }; DTestViewRectangle: PROC [data: Data, x, y, w, h: INTEGER] RETURNS [Visibility] ~ { viewToDevice: Transformation ~ data.viewToDevice; viewClipMask: ManhattanPolygon ~ data.viewClipMask; IF viewClipMask=NIL THEN RETURN[none]; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { clip: DeviceRectangle ~ data.viewClipBox; csmin: INTEGER ~ clip.sMin; csmax: INTEGER ~ csmin+clip.sSize; cfmin: INTEGER ~ clip.fMin; cfmax: INTEGER ~ cfmin+clip.fSize; smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h]; smax: INTEGER ~ smin+ABS[h]; fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w]; fmax: INTEGER ~ fmin+ABS[w]; IF fmin>=cfmax OR smin>=csmax OR fmax<=cfmin OR smax<=csmin THEN RETURN[none]; IF viewClipMask.rest#NIL THEN RETURN[part]; IF fmincfmax OR smax>csmax THEN RETURN[part]; RETURN[all]; } ELSE { RETURN[part]; }; }; RasterGetClipper: PUBLIC PROC[context: Context] RETURNS[clipper: Clipper] ~ { data: Data ~ NARROW[context.data]; IF data.specialClipPresent THEN { ERROR Imager.Error[[$unimplemented, "GetClipper during ImagerOps.DoWithBuffer"]]; }; RETURN [ImagerState.StateGetClipper[context]]; }; rasterClass: Class ~ NEW[ClassRep _ [ type: $Raster, DoSave: ImagerState.StateDoSave, SetInt: ImagerState.StateSetInt, SetReal: ImagerState.StateSetReal, SetT: ImagerState.StateSetT, SetFont: ImagerState.StateSetFont, SetColor: ImagerState.StateSetColor, SetClipper: ImagerState.StateSetClipper, SetStrokeDashes: ImagerState.StateSetStrokeDashes, GetInt: ImagerState.StateGetInt, GetReal: ImagerState.StateGetReal, GetT: ImagerState.StateGetT, GetFont: ImagerState.StateGetFont, GetColor: ImagerState.StateGetColor, GetClipper: RasterGetClipper, GetStrokeDashes: ImagerState.StateGetStrokeDashes, ConcatT: ImagerState.StateConcatT, Scale2T: ImagerState.StateScale2T, RotateT: ImagerState.StateRotateT, TranslateT: ImagerState.StateTranslateT, Move: ImagerState.StateMove, SetXY: ImagerState.StateSetXY, SetXYRel: ImagerState.StateSetXYRel, Show: ImagerRasterPrivate.RasterShow, ShowText: ImagerRasterPrivate.RasterShowText, StartUnderline: ImagerState.StateStartUnderline, MaskUnderline: ImagerState.StateMaskUnderline, CorrectMask: ImagerState.StateCorrectMask, CorrectSpace: ImagerState.StateCorrectSpace, Space: ImagerState.StateSpace, SetCorrectMeasure: ImagerState.StateSetCorrectMeasure, SetCorrectTolerance: ImagerState.StateSetCorrectTolerance, Correct: ImagerState.StateCorrect, DontCorrect: ImagerState.StateDontCorrect, SetGray: ImagerState.StateSetGray, SetSampledColor: ImagerState.StateSetSampledColor, SetSampledBlack: ImagerState.StateSetSampledBlack, MaskFill: RasterMaskFill, MaskStroke: RasterMaskStroke, MaskRectangle: RasterMaskRectangle, MaskRectangleI: RasterMaskRectangleI, MaskVector: RasterMaskVector, MaskPixel: RasterMaskPixel, MaskBits: RasterMaskBits, DrawBits: RasterDrawBits, Clip: ImagerState.StateClip, ClipRectangle: ImagerState.StateClipRectangle, ClipRectangleI: ImagerState.StateClipRectangleI, GetCP: ImagerState.StateGetCP, GetBoundingRectangle: RasterGetBounds, propList: NIL ]]; END. :ImagerRasterImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Michael Plass, November 1, 1985 9:31:41 am PST Doug Wyatt, May 30, 1985 11:15:48 pm PDT USING [T, color, clipper, underlineStart, noImage, priorityImportant] USING [T, color, clipper, strokeWidth, strokeStyle, noImage, priorityImportant] Overflow cannot happen. Computes bounding box of p0 and p1, clipped against clipBox. Note that translation doesn't invalidate charToDevice rect: Rectangle ~ viewToDevice.TransformRectangle[[x, y, w, h]]; -- test against viewClipBox -- Κ"#˜codešœ™Kšœ Οmœ7™BK™.Kšœ(™(—K˜šΟk ˜ Jšœžœ˜%JšœžœΚ˜ΦJšœžœ˜+Jšœ žœ˜Jšœ žœ˜Jšœ žœ/˜AJšœ žœ)˜9Jšœžœ'˜>Jšœžœ6˜KJšœžœC˜^Jšœ žœ˜ Jšœ žœ˜(Jšœžœ-˜CJšœžœ˜2Jšœžœ˜&Jšœ žœ ˜Jšœžœ_˜xJšœ žœ“˜₯JšœžœQ˜jJšœ žœ”˜₯Jšœ žœ˜$Jšœžœγ˜ύJšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœžœ˜Jšœžœžœ˜JšžœžœG˜O—K˜KšΠblœžœž˜Kšžœ›ž˜€KšžœF˜MKšœžœžœ"˜.K˜Kšœžœ˜"Kšœ žœžœΟc˜LK˜Kšœžœ˜ Kšœ žœžœ ˜JK˜šžœžœžœ˜K˜—Kšœ žœ˜'Kšœ žœ˜'Kšœžœ˜Kšœžœ˜+Kšœ žœ˜Kšœžœ˜#Kšœ žœ˜)Kšœ žœ˜%Kšœ žœ"˜2Kšœžœ"˜7Kšœžœ˜Kšœžœ˜1Kšœ žœ˜%Kšœ žœ˜%Kšœ žœ"˜1Kšœžœ'˜;Kšœžœ*˜AKšžœžœ žœ˜Kšœžœ˜Kšœ žœ˜+Kšœ žœ˜,Kšœ žœžœ" ˜TK˜šΟnœžœžœ˜OKšžœK˜QK˜K˜—š‘œžœžœ˜OKšžœK˜QK˜K˜—K˜š ‘œžœžœžœ+žœ4žœ˜­Kšœ žœ˜-Kšœžœ˜"Kšœžœ8˜LKšœ˜Kšœ˜Kšœ2˜2Kšœ4˜4Kšœ2˜2Kšœ2˜2Kšœ˜Kšœžœžœžœ˜.K˜Kšžœ žœ(˜:Kšžœ8˜Kšœœ˜œKšœ˜Kšœ—˜—Kšœ˜Kšœ4˜4Kšœ˜Kšžœ˜—Kšœ˜Kšœ6˜6KšœG˜GKšœžœžœ žœ˜1Kšœžœ˜ K˜K˜—š‘ œžœ˜2K˜K˜>Kšœžœ˜K˜K˜—š‘œžœ˜5K˜K˜?Kšœžœ˜!K˜K˜—š‘œžœžœ˜Kšž˜—Kšœ˜—Kšœp˜pK˜—K˜—Kšœ ˜ K˜K˜K˜K˜K˜—šœ@žœžœ˜]K˜—š ‘œžœ'žœžœ#žœ˜lšœžœžœ˜AK˜KšœB˜BK˜—Kšœ˜Kšœn˜nK˜K˜—š‘ œž œ2žœ˜`š žœžœžœ#žœžœž˜EKšœ8˜8š žœžœžœžœž˜SKš œžœžœžœžœ˜~—Kšžœ˜—K˜K˜—K˜šœ;žœ˜BK˜—š‘œžœ,žœ˜IK˜šžœžœ˜Kšœ žœ˜"Kšœ!˜!KšœV˜VK˜—K˜K˜—šœžœžœ˜K˜—š‘œžœ%˜>Kšœ˜šžœžœ˜Kšœ žœ˜"J˜Kšœ!˜!š žœžœžœžœžœ˜[Jšœ5˜5Jšœ%˜%Jšœžœ>˜EJšœžœF˜MJšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=K˜Kšžœžœ žœ˜5Kšžœžœ žœ˜5šžœžœžœ˜Jšœ:˜:šœžœ žœ˜(KšœC˜CKšœ˜—JšœB˜BJšœ˜—Jšœ˜—šžœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—KšœO˜OJšœ˜—K˜—Kšœ˜K˜—š‘œžœ žœ˜FKšœ˜šžœžœ˜Kšœ žœ˜"K˜Kšœ˜Kšœ!˜!Kšœ%˜%š žœžœžœžœžœ˜^Kšœžœžœžœ ˜9Kšœžœžœžœ ˜9Kšœ$˜$šœžœ žœ˜(KšœC˜CKšœ˜—K˜Kšžœ žœ žœ$˜AKšžœ žœ žœ$˜Aš žœžœžœžœžœ˜MJšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœB˜BK˜—K˜—Kšžœ8˜˜DKšœ˜—KšœN˜Nšœžœ žœ˜(šœ žœ˜&šœžœžœ žœ˜/KšœY˜YKšžœ#žœ˜=Kšœ˜—šœ'˜'šœ>˜>Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—J˜—•StartOfExpansion€[base: LONG POINTER, wordsPerLine: NAT, sBits: NAT, fBits: NAT, sSize: NAT, fSize: NAT, sRuns: INTEGER, fRuns: INTEGER, run: ImagerDevice.RunProc]šœ"˜"Kšœ6˜6Kšœ+˜+Kšœ(˜(Kšœ˜—Kšœδ˜δKšœ˜—KšœE˜EKšœ˜—KšœB˜BKšœ˜—KšžœZ˜a—K˜K˜—š‘œžœžœžœžœžœžœ#˜”Kšœ žœ˜Bšœžœžœ˜BK˜Kšœp˜pšœžœ˜%šœ žœžœ žœ˜7K˜Kšœžœ˜ Kšœn˜nšžœžœžœ ž˜Kšœ#˜#šžœž˜Kšœžœžœ˜*Kšœžœžœ>˜OKšžœžœ˜—Kšžœ˜—Kšžœ žœ1˜AK˜—K˜K˜—Kšœd˜dK˜JKšœ*˜*K˜—šœ˜KšœO˜OKšœ˜—Kšœ˜Kšœ$˜$Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ9˜9Kšœd˜dK˜K˜—šœ@˜@K˜—š‘œžœžœžœžœžœžœ žœ˜ŒKšœ˜šžœžœ˜Kšœ žœ˜"Kšœ1˜1Kšœ!˜!Kšœ˜Kšœ*˜*Kšœ2˜2Kšœ»˜»K˜—K˜K˜—š‘œžœžœžœžœžœžœ žœ˜Œšœžœ˜Kšœ7˜7KšœK˜KKšœ7˜7Kšœ˜Kšœ˜—Kšœ˜šžœžœ˜Kšœ žœ˜"Kšœ1˜1Kšœ!˜!Kšœ˜Kšœ*˜*Kšœ2˜2š žœžœžœžœžœ˜`Kšœžœžœ˜4Kšœžœžœ˜4Kšœ$˜$šœžœ žœ˜(KšœC˜CKšœ˜—š žœžœžœžœžœ˜MK˜Jšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœ€˜€K˜—Kšœ˜—Kšžœ(˜,K˜—K˜K˜—š‘œžœ'˜Kšœžœžœžœ˜=Kšœ@˜@šœžœ žœ˜(KšœA˜AKšœ˜—š žœžœžœžœžœ˜MK˜Jšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœS˜SK˜—K˜—Kšžœžœ  ˜K˜K˜—Kšœ žœ˜-K˜š ‘œžœžœ'žœžœ˜gKšœ˜šžœžœž˜šœ˜Kšœ'žœžœ˜CKšžœ9˜?K˜—KšžœžœB˜R—K˜—K˜š‘œžœžœžœ˜SK˜1K˜3Kšžœžœžœžœ˜&šžœžœžœ˜;K˜)Kšœžœ ˜Kšœžœ˜"Kšœžœ ˜Kšœžœ˜"Kšœžœžœ ˜,Kšœžœžœ˜Kšœžœžœ ˜,Kšœžœžœ˜Kš žœ žœ žœ žœ žœžœ˜NKšžœžœžœžœ˜+Kš žœ žœ žœ žœ žœžœ˜JKšžœ˜ K˜—šžœ˜K™@Kš ™Kšžœ˜ K˜—K˜K˜—š‘œžœžœžœ˜MKšœ žœ˜"šžœžœ˜!KšžœL˜QKšœ˜—Kšžœ(˜.K˜K˜—šœžœ ˜%Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ"˜"Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ(˜(Kšœ2˜2Kšœ ˜ Kšœ"˜"Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ˜Kšœ2˜2Kšœ"˜"Kšœ"˜"Kšœ"˜"Kšœ(˜(Kšœ˜Kšœ˜Kšœ$˜$Kšœ%˜%Kšœ-˜-Kšœ0˜0Kšœ.˜.Kšœ*˜*Kšœ,˜,Kšœ˜Kšœ6˜6Kšœ:˜:Kšœ"˜"Kšœ*˜*Kšœ"˜"Kšœ2˜2Kšœ2˜2Kšœ˜Kšœ˜Kšœ#˜#Kšœ%˜%Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ.˜.Kšœ0˜0Kšœ˜Kšœ&˜&Kšœ ž˜ K˜K˜—K˜Kšžœ˜—…—ŠΊ―