DIRECTORY ColorRegistry, FunctionCache, Imager, ImagerBackdoor, ImagerBox, ImagerBrick, ImagerRavenBitmapContext, ImagerColor, ImagerColorPrivate, ImagerDevice, ImagerFont, ImagerMaskCache, ImagerPath, ImagerPixel, ImagerPixelArray, ImagerPrivate, ImagerRaster, ImagerSample, ImagerState, ImagerTransformation, Real, SF, Vector2; ImagerRavenBitmapContextImpl: CEDAR PROGRAM IMPORTS FunctionCache, Imager, ImagerBackdoor, ImagerColor, ImagerColorPrivate, ImagerDevice, ImagerMaskCache, ImagerPixel, ImagerPixelArray, ImagerRaster, ImagerSample, ImagerState, ImagerTransformation, Real, SF, Vector2 EXPORTS ImagerRavenBitmapContext, Imager SHARES ImagerSample ~ BEGIN Color: TYPE ~ ImagerColor.Color; ColorOperator: TYPE ~ ImagerColor.ColorOperator; Context: TYPE ~ Imager.Context; Device: TYPE ~ ImagerDevice.Device; Object: TYPE ~ Imager.Object; PixelMap: TYPE ~ ImagerPixel.PixelMap; PixelArray: TYPE ~ ImagerPixelArray.PixelArray; RasterSampleMap: TYPE ~ ImagerSample.RasterSampleMap; Rectangle: TYPE ~ ImagerBox.Rectangle; SampleMap: TYPE ~ ImagerSample.SampleMap; ScanMode: TYPE ~ ImagerTransformation.ScanMode; Transformation: TYPE ~ ImagerTransformation.Transformation; VEC: TYPE ~ Vector2.VEC; classCode: PUBLIC ATOM ¬ $Bitmap; Case: TYPE ~ {constant, tile, sampled, sampledBlack}; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ bitmap: SampleMap ¬ NIL, savedBuffer: ImagerSample.SampleMap ¬ NIL, case: Case ¬ constant, constant: [0..1] ¬ 1, function: ImagerSample.Function ¬ [null, null], maskBitmapFunction: ImagerSample.Function ¬ [null, null], tile: ImagerBrick.Brick ¬ [0, NIL, 0], -- bitmap for case=tile separation: ATOM, brick: ImagerBrick.Brick ¬ [0, NIL, 0], -- thresholds paToDevice: Transformation, source: ImagerPixel.PixelMap ¬ NIL, scratchSampleMapThatIsInUse: SampleMap ¬ NIL, -- SampleMap that should go back to the scratch pool at the next SetColor scratchStippleMap: SampleMap ¬ NIL, -- scratch storage for stipple case scratchPixelMap: PixelMap ¬ NIL, -- scratch descriptor for AccessBuffer scratchBitmapContext: Imager.Context ¬ NIL -- for making fancy tiles ]; Create: PUBLIC PROC [deviceSpaceSize: SF.Vec, scanMode: ScanMode, surfaceUnitsPerInch: VEC, pixelUnits: BOOL, fontCacheName: ATOM, deviceCode: ATOM ¬ $Raven300] RETURNS [Context] ~ { data: Data ~ NEW[DataRep ¬ [paToDevice: ImagerTransformation.Scale[1]]]; deviceParm: ImagerDevice.DeviceParm ¬ ImagerDevice.MakeDeviceParm[ class: deviceClass, sSize: deviceSpaceSize.s, fSize: deviceSpaceSize.f, scanMode: scanMode, surfaceUnitsPerInch: surfaceUnitsPerInch, surfaceUnitsPerPixel: 1, fontCache: IF fontCacheName = NIL THEN NIL ELSE ImagerMaskCache.GetNamedCache[fontCacheName] ]; context: Context ~ ImagerRaster.Create[class: contextClass, deviceClass: deviceClass, deviceParm: deviceParm, data: data, pixelUnits: pixelUnits]; black: ImagerColor.Color ¬ ImagerColor.Find["Xerox/Research/Distinct/Black"]; white: ImagerColor.Color ¬ ImagerColor.Find["Xerox/Research/Distinct/White"]; oldClass: REF ClassRep ¬ context.class; --crack the opaque type newClass: REF ClassRep ¬ NEW[ClassRep ¬ oldClass­]; ImagerRasterMaskVector ¬ oldClass.MaskVector; ImagerRasterMaskStroke ¬ oldClass.MaskStroke; newClass.MaskStroke ¬ MyMaskStroke; newClass.MaskVector ¬ MyMaskVector; Imager.PutProp[context, $DistinctBlack, black]; Imager.PutProp[context, $DistinctWhite, white]; Imager.PutProp[context, $DeviceCode, deviceCode]; Imager.PutProp[context, $DeviceIndex, NEW[NAT ¬ (SELECT deviceCode FROM $Raven300 => 7, $BWPlatemaker => 6, ENDCASE => 7)] ]; --from ColorRegistryImpl context.class ¬ newClass; --replace the class with our local subclass data.brick ¬ defaultBrick; ImagerRaster.SetDeviceClipBox[context, [[0,0], [0,0]]]; RETURN [context]; }; SetBitmap: PUBLIC PROC [context: Context, bitmap: SampleMap] ~ { data: Data ~ NARROW[ImagerRaster.GetDevice[context].data]; data.bitmap ¬ bitmap; ImagerRaster.SetDeviceClipBox[context, IF bitmap = NIL THEN [] ELSE ImagerSample.GetBox[bitmap]]; }; GetBitmap: PUBLIC PROC [context: Context] RETURNS [SampleMap] ~ { data: Data ~ NARROW[ImagerRaster.GetDevice[context].data]; RETURN [data.bitmap]; }; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; PathProc: TYPE = ImagerPath.PathProc; ImagerRasterMaskVector: PROC [context: Context, p1, p2: VEC] ¬ NIL; ImagerRasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ¬ NIL; DoDash: PROC[context: Context] RETURNS[BOOLEAN] = { WITH ImagerBackdoor.GetColor[context] SELECT FROM color: ImagerColor.SpecialColor => IF color.type=$Distinct THEN { nameData: ColorRegistry.Data ¬ NARROW[color.data]; IF nameData.id#$Black AND nameData.id#$White AND nameData.id#$Gray THEN { widthInPixels: REAL ¬ Vector2.Length[ImagerBackdoor.TransformVec[context: context, v: [ImagerBackdoor.GetReal[context, strokeWidth], 0], from: client, to: device]]; IF widthInPixels < 10 THEN RETURN[TRUE]; --define 10 device pixels as ok for texture }}; ENDCASE; RETURN[FALSE]; }; MyMaskVector: PROC [context: Context, p1, p2: VEC] = { IF DoDash[context] THEN { black: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctBlack]]; white: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctWhite]]; path: ImagerPath.PathProc = {moveTo[p1]; lineTo[p2]}; color: ImagerColor.SpecialColor ¬ NARROW[ImagerState.StateGetColor[context]]; ImagerState.StateSetColor[context, white]; ImagerRasterMaskVector[context, p1, p2]; ImagerState.StateSetColor[context, black]; IRBMaskDashedStroke[context, color, path, FALSE]; ImagerState.StateSetColor[context, color]; } ELSE ImagerRasterMaskVector[context, p1, p2]; }; MyMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] = { IF DoDash[context] THEN { black: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctBlack]]; white: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctWhite]]; color: ImagerColor.SpecialColor ¬ NARROW[ImagerState.StateGetColor[context]]; ImagerState.StateSetColor[context, white]; ImagerRasterMaskStroke[context, path, closed]; ImagerState.StateSetColor[context, black]; IRBMaskDashedStroke[context, color, path, closed]; ImagerState.StateSetColor[context, color]; } ELSE ImagerRasterMaskStroke[context, path, closed]; }; yellowDash: ARRAY [0..3] OF REAL ¬ [0,2.5,0,2.5]; redDash: ARRAY [0..3] OF REAL ¬ [3,2,3,2]; greenDash: ARRAY [0..3] OF REAL ¬ [1,2,1,2]; blueDash: ARRAY [0..3] OF REAL ¬ [4,2,1,2]; IRBMaskDashedStroke: PROC [context: Context, color: ImagerColor.SpecialColor, path: PathProc, closed: BOOL] = { nameData: ColorRegistry.Data ¬ NARROW[color.data]; Inner: PROC ~ { unit: REAL ¬ 8; array: ARRAY [0..3] OF REAL ¬ SELECT nameData.id FROM $Red => redDash, $Green => greenDash, $Blue => blueDash, $Yellow => yellowDash, ENDCASE => ERROR; Pattern: PROC[i: NAT] RETURNS[REAL] = { v: VEC ¬ ImagerBackdoor.TransformVec[context: context, v: [array[i]*unit, 0], from: device, to: client]; RETURN[Vector2.Length[v]]}; Imager.SetStrokeEnd[context, round]; Imager.SetStrokeJoint[context, round]; Imager.MaskDashedStroke[context, path, 4, Pattern, 0, 0]; }; Imager.DoSave[context, Inner]; }; MakeSimpleBrick: PROC [t: ARRAY [0..4) OF PACKED ARRAY [0..4) OF [0..16)] RETURNS [ImagerBrick.Brick] ~ { b: SampleMap ~ ImagerSample.NewSampleMap[box: [max: [4, 4]], bitsPerSample: 8]; FOR s: NAT IN [0..4) DO FOR f: NAT IN [0..4) DO ImagerSample.Put[b, [s, f], t[s][f]]; ENDLOOP; ENDLOOP; RETURN [[maxSample: 15, sampleMap: b, phase: 0]] }; coarseBrick: ImagerBrick.Brick ¬ MakeSimpleBrick[[ [00, 01, 13, 14], [08, 02, 03, 15], [09, 10, 04, 05], [07, 11, 12, 06] ]]; fineBrick: ImagerBrick.Brick ¬ MakeSimpleBrick[[ [00, 08, 02, 10], [14, 04, 12, 06], [03, 11, 01, 09], [13, 07, 15, 05] ]]; defaultBrick: ImagerBrick.Brick ¬ fineBrick; SetBrick: PUBLIC PROC [context: Context, brick: ImagerBrick.Brick] ~ { WITH ImagerRaster.GetDevice[context].data SELECT FROM data: Data => { data.brick ¬ brick; Imager.SetColor[context, ImagerBackdoor.GetColor[context]]; -- to re-validate the color }; ENDCASE => NULL; }; GetBrick: PUBLIC PROC [context: Context] RETURNS [ImagerBrick.Brick] ~ { WITH ImagerRaster.GetDevice[context].data SELECT FROM data: Data => { RETURN [data.brick] }; ENDCASE => NULL; RETURN [[0, NIL, 0]] }; SetSeparation: PUBLIC PROC [context: Context, separation: ATOM] ~ { WITH ImagerRaster.GetDevice[context].data SELECT FROM data: Data => { data.separation ¬ separation; Imager.SetColor[context, ImagerBackdoor.GetColor[context]]; -- to re-validate the color }; ENDCASE => NULL; }; GetSeparation: PUBLIC PROC [context: Context] RETURNS [ATOM] ~ { WITH ImagerRaster.GetDevice[context].data SELECT FROM data: Data => { RETURN [data.separation] }; ENDCASE => NULL; RETURN [NIL] }; GetExtendedBox: PROC [brick: ImagerBrick.Brick] RETURNS [SF.Box] ~ { box: SF.Box ¬ ImagerSample.GetBox[brick.sampleMap]; size: SF.Vec ~ ImagerSample.GetSize[brick.sampleMap]; fSizeBox: NAT ~ size.f; IF brick.phase = 0 AND size.f IN [1..16) AND size.s IN [1..16) AND 16 MOD NAT[size.f] = 0 THEN { box.max.f ¬ box.min.f+16; }; RETURN [box] }; MakeTile: PROC [f: REAL, brick: ImagerBrick.Brick] RETURNS [ImagerBrick.Brick] ~ { box: SF.Box ~ GetExtendedBox[brick]; sample: CARDINAL ~ Real.Round[f*(brick.maxSample+1)]; b: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[box]]; sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box]; FOR s: INTEGER IN [box.min.s..box.max.s) DO ImagerSample.GetTileSamples[tile: brick.sampleMap, phase: brick.phase, initIndex: [s, box.min.f], buffer: b]; FOR j: INTEGER IN [0..b.length) DO b[j] ¬ IF sample > b[j] THEN 1 ELSE 0; ENDLOOP; ImagerSample.PutSamples[map: sampleMap, initIndex: [s, box.min.f], buffer: b]; ENDLOOP; ImagerSample.ReleaseScratchSamples[b]; RETURN [[maxSample: 1, sampleMap: sampleMap, phase: brick.phase]]; }; FractionFromConstantColor: PROC [data: Data, c: ImagerColor.OpConstantColor] RETURNS [REAL] ~ { f: REAL ~ ImagerColorPrivate.GrayFromColor[c]; RETURN [f] }; SampledColorData: TYPE ~ REF SampledColorDataRep; SampledColorDataRep: TYPE ~ RECORD [ pa: PixelArray, colorOperator: ColorOperator, separation: ATOM, maxSample: CARDINAL, filterDiameter: NAT, source: ImagerPixel.PixelMap ]; me: REF TEXT ~ "Bitmap"; -- a globally unique REF for use as a clientID in the global cache. BitmapSetColor: PROC [device: Device, color: Color, viewToDevice: Transformation] ~ { data: Data ~ NARROW [device.data]; realRaster: BOOL ~ WITH data.bitmap SELECT FROM r: RasterSampleMap => TRUE ENDCASE => FALSE; allowBitmaps, hardSampledCase: BOOL; { data.source ¬ NIL; data.tile.sampleMap ¬ NIL; IF data.scratchSampleMapThatIsInUse # NIL THEN { ImagerSample.ReleaseScratchMap[data.scratchSampleMapThatIsInUse]; data.scratchSampleMapThatIsInUse ¬ NIL; }; }; WITH color SELECT FROM c: ImagerColor.OpConstantColor => { f: REAL ~ FractionFromConstantColor[data, c]; data.case ¬ constant; data.function ¬ [null, null]; SELECT f FROM < 0.00001 => { data.case ¬ constant; data.constant ¬ 0 }; > 0.99999 => { data.case ¬ constant; data.constant ¬ 1 }; ENDCASE => { data.tile ¬ MakeTile[f, data.brick]; data.scratchSampleMapThatIsInUse ¬ data.tile.sampleMap; data.case ¬ tile; }; }; sampledColor: ImagerColor.SampledColor => { BitmapSetSampledColor[device: device, sampledColor: sampledColor, viewToDevice: viewToDevice]; }; s: ImagerColor.SampledBlack => { pa: PixelArray ~ s.pa; ImagerTransformation.ApplyCat[data.paToDevice, pa.m, s.um, viewToDevice]; data.function ¬ IF s.clear THEN [or, null] ELSE [null, null]; SELECT TRUE FROM data.paToDevice.form = 3 AND data.paToDevice.integerTrans => { min: SF.Vec ~ [s: data.paToDevice.tx, f: data.paToDevice.ty]; max: SF.Vec ~ [s: min.s + pa.sSize, f: min.f + pa.fSize]; sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box: [min: min, max: max]]; ImagerPixelArray.Transfer[pa: pa, dst: sampleMap, dstMin: min]; data.case ¬ tile; data.tile.maxSample ¬ 1; data.tile.sampleMap ¬ sampleMap; data.scratchSampleMapThatIsInUse ¬ sampleMap; data.tile.phase ¬ 0; }; newStuff AND TryTransformingBitmapIntoTile[data, pa] => { }; ENDCASE => { sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box: [max: [pa.sSize, pa.fSize]]]; ImagerPixelArray.Transfer[pa: pa, dst: sampleMap]; data.case ¬ sampledBlack; data.source ¬ ImagerPixel.MakePixelMap[sampleMap]; data.scratchSampleMapThatIsInUse ¬ sampleMap; }; }; s: ImagerColor.SpecialColor => { SELECT s.type FROM $Stipple => { stippleData: ImagerColorPrivate.StippleData ~ NARROW[s.data]; data.function ¬ stippleData.function; SELECT stippleData.word FROM 00000H => { data.case ¬ constant; data.constant ¬ 0 }; 0FFFFH => { data.case ¬ constant; data.constant ¬ 1 }; ENDCASE => { data.case ¬ tile; data.tile.sampleMap ¬ data.scratchStippleMap ¬ ImagerSample.TileFromStipple[stipple: stippleData.word, scratch: data.scratchStippleMap]; data.tile.phase ¬ 0; }; }; $BitmapTile => { list: LIST OF REF ~ NARROW[s.data]; brick: REF ImagerBrick.Brick ~ NARROW[list.first]; rf: REF ImagerSample.Function ~ NARROW[list.rest.first]; data.function ¬ rf­; data.case ¬ tile; data.tile.sampleMap ¬ brick.sampleMap; data.tile.phase ¬ brick.phase; }; <<$Distinct, $ChipNDale => { -- the distinct named colors nameData: ColorRegistry.Data ¬ NARROW[s.data]; deviceIndex: NAT ¬ NARROW[Imager.GetProp[context, $DeviceIndex], REF NAT]­; deviceCode: ATOM ¬ NARROW[Imager.GetProp[context, $DeviceCode]]; value: REF ColorRegistry.DisplayBrick; ColorRegistryProcs.InitPrinterColors[deviceType: deviceCode, colorType: s.type]; --won't do any work if already initialized value ¬ NARROW[nameData.deviceValues[deviceIndex]]; data.function ¬ value.function; data.case ¬ tile; data.tile.sampleMap ¬ value.tile.sampleMap; data.tile.phase ¬ value.tile.phase; };>> ENDCASE => { IF s.substitute # NIL THEN { BitmapSetColor[device, s.substitute, viewToDevice] } ELSE { ERROR Imager.Error[[code: $unknownSpecialColor, explanation: "Unknown special color has no substitute value"]] }; }; }; ENDCASE => ERROR; data.maskBitmapFunction ¬ [null, null]; IF data.case = constant THEN SELECT data.function FROM [null, null] => { SELECT data.constant FROM 0 => data.maskBitmapFunction ¬ [and, complement]; 1 => data.maskBitmapFunction ¬ [or, null]; ENDCASE => NULL; }; [xor, null] => { IF data.constant = 1 THEN data.maskBitmapFunction ¬ [xor, null]; }; ENDCASE => NULL; allowBitmaps ¬ (data.maskBitmapFunction # [null, null]); hardSampledCase ¬ data.case = sampledBlack OR data.case = sampled; device.state.allow ¬ [ unorderedBoxes: NOT hardSampledCase, multipleCoverage: (data.function.dstFunc # xor AND NOT hardSampledCase), regionFill: data.case = constant AND realRaster, bitmap: allowBitmaps, rawBitmaps: allowBitmaps AND realRaster, runGroupChar: FALSE, rasterChar: FALSE, maskThinVector: FALSE ]; }; newStuff: BOOL ¬ TRUE; -- for testing TryTransformingBitmapIntoTile: PROC [data: Data, pa: PixelArray] RETURNS [BOOL] ~ { m: Transformation ~ data.paToDevice; IF m.form#0 THEN { sRect: Rectangle ~ [0, 0, pa.sSize, pa.fSize]; dRect: Rectangle ~ ImagerTransformation.TransformRectangle[m, sRect]; IF MAX[ABS[dRect.x], ABS[dRect.y], ABS[dRect.w], ABS[dRect.h]] <= 16383 AND ABS[dRect.w*dRect.h] <= 1048576.0 THEN { sSize: NAT ~ Real.Round[dRect.w]; fSize: NAT ~ Real.Round[dRect.h]; IF dRect.w-sSize+4.0 = 4.0 AND dRect.h-fSize+4.0 = 4.0 THEN { action: PROC ~ { pamInverse: Transformation ~ ImagerTransformation.Invert[pa.m]; Imager.TranslateT[data.scratchBitmapContext, [-dRect.x, -dRect.y]]; Imager.ConcatT[data.scratchBitmapContext, m]; Imager.ConcatT[data.scratchBitmapContext, pamInverse]; Imager.MaskPixel[data.scratchBitmapContext, pa]; ImagerTransformation.Destroy[pamInverse]; }; sampleMap: SampleMap ¬ ImagerSample.ObtainScratchMap[box: [max: [sSize, fSize]]]; tileMap: SampleMap ~ ImagerSample.Shift[sampleMap, [s: Real.Floor[dRect.x+0.5], f: Real.Floor[dRect.y+0.5]]]; ImagerSample.Clear[sampleMap]; IF data.scratchBitmapContext = NIL THEN data.scratchBitmapContext ¬ Create[deviceSpaceSize: SF.maxVec, scanMode: [slow: right, fast: up], surfaceUnitsPerInch: [1,1], pixelUnits: TRUE, fontCacheName: $Bitmap]; SetBitmap[data.scratchBitmapContext, sampleMap]; Imager.DoSave[data.scratchBitmapContext, action]; SetBitmap[data.scratchBitmapContext, NIL]; TRUSTED {ImagerSample.ReleaseDescriptor[sampleMap]; sampleMap ¬ NIL}; data.case ¬ tile; data.tile.maxSample ¬ 1; data.tile.sampleMap ¬ tileMap; data.scratchSampleMapThatIsInUse ¬ tileMap; data.tile.phase ¬ 0; RETURN [TRUE]; }; }; }; RETURN [FALSE]; }; ComputeSource: PROC [sampledColor: ImagerColor.SampledColor, maxOut: PROC [i: NAT] RETURNS [CARDINAL], filterDiameter: NAT, separation: ATOM] RETURNS [ImagerPixel.PixelMap] ~ { colorOperator: ColorOperator ~ sampledColor.colorOperator; pixelMap: ImagerPixel.PixelMap ~ ImagerColorPrivate.Translate[colorOperator: sampledColor.colorOperator, output: [type: $Y, samplesPerPixelOut: 1], pa: sampledColor.pa, maxOut: maxOut]; RETURN [pixelMap] }; BitmapSetSampledColor: PROC [device: Device, sampledColor: ImagerColor.SampledColor, viewToDevice: Transformation] ~ { data: Data ~ NARROW [device.data]; pa: PixelArray ~ sampledColor.pa; largerPaSize: NAT ~ MIN[pa.sSize, pa.fSize] + 1; cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[]; filterDiameter: NAT ¬ 1; -- for low-pass filtering scd: SampledColorData ¬ NIL; compare: FunctionCache.CompareProc ~ { WITH argument SELECT FROM scd: SampledColorData => RETURN [scd.pa = pa AND scd.colorOperator = sampledColor.colorOperator AND scd.separation = data.separation AND scd.maxSample = data.brick.maxSample+1 AND scd.filterDiameter = filterDiameter]; ENDCASE => RETURN [FALSE] }; maxOut: PROC [i: NAT] RETURNS [CARDINAL] ~ { RETURN [data.brick.maxSample+1] }; ImagerTransformation.ApplyCat[data.paToDevice, pa.m, sampledColor.um, viewToDevice]; IF interpolate THEN filterDiameter ¬ MIN[MAX[Real.Round[1.0/MAX[ImagerTransformation.SingularValues[data.paToDevice].y, 1.0/largerPaSize]], 1], 255]; data.case ¬ sampled; data.function ¬ [null, null]; IF pa.immutable THEN { scd ¬ NARROW[FunctionCache.Lookup[cache, compare, me].value]; IF scd = NIL THEN { pixelMap: ImagerPixel.PixelMap ~ ComputeSource[sampledColor, maxOut, filterDiameter, data.separation]; scd ¬ NEW [SampledColorDataRep ¬ [pa: pa, colorOperator: sampledColor.colorOperator, separation: data.separation, maxSample: data.brick.maxSample+1, filterDiameter: filterDiameter, source: pixelMap]]; FunctionCache.Insert[x: cache, argument: scd, value: scd, size: ImagerSample.WordsForMap[size: ImagerSample.GetSize[pixelMap[0]], bitsPerSample: ImagerSample.GetBitsPerSample[pixelMap[0]]], clientID: me]; }; data.source ¬ scd.source; } ELSE { data.source ¬ ComputeSource[sampledColor, maxOut, filterDiameter, data.separation]; }; }; interpolate: BOOL ¬ FALSE; BitmapMaskBoxes: PROC [device: Device, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { data: Data ~ NARROW[device.data]; SELECT data.case FROM constant => { ImagerSample.FillBoxes[map: data.bitmap, boxes: boxes, value: data.constant, function: data.function]; }; tile => { ImagerSample.TileBoxes[map: data.bitmap, boxes: boxes, tile: data.tile.sampleMap, phase: data.tile.phase, function: data.function]; }; sampled => TRUSTED { buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[bounds]]; Action: SAFE PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ TRUSTED { buffer.length ¬ pixels.length; ImagerSample.GetTileSamples[tile: data.brick.sampleMap, initIndex: min, buffer: buffer, phase: data.brick.phase]; ImagerSample.Halftone[map: data.bitmap, min: min, sampleBuffer: pixels[0], thresholdBuffer: buffer, function: data.function]; }; ImagerPixel.Resample[self: data.source, m: data.paToDevice, interpolate: interpolate, boxes: boxes, bounds: bounds, action: Action]; ImagerSample.ReleaseScratchSamples[buffer]; }; sampledBlack => { Action: PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ { ImagerSample.PutSamples[map: data.bitmap, initIndex: min, buffer: pixels[0], start: 0, count: pixels.length, function: data.function]; }; ImagerPixel.Resample[self: data.source, m: data.paToDevice, interpolate: FALSE, boxes: boxes, bounds: bounds, action: Action]; }; ENDCASE => ERROR; }; BitmapMaskRegion: PROC [device: Device, bounds: SF.Box, edgeGenerator: PROC [ImagerSample.EdgeAction]] ~ { data: Data ~ NARROW[device.data]; IF data.case # constant THEN ERROR; ImagerSample.RegionFill[dst: NARROW[data.bitmap], edgeGenerator: edgeGenerator, value: data.constant, function: data.function]; }; BitmapMaskBitmap: PROC [device: Device, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { data: Data ~ NARROW[device.data]; ImagerSample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes, function: data.maskBitmapFunction]; }; BitmapMaskRawBitmaps: PROC [device: Device, list: LIST OF ImagerSample.RawDescriptor] ~ { data: Data ~ NARROW[device.data]; ImagerSample.RawListTransfer[dst: NARROW[data.bitmap], src: list, function: data.maskBitmapFunction]; }; BitmapDrawBitmap: PROC [device: Device, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { data: Data ~ NARROW[device.data]; ImagerSample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes]; }; BitmapMoveBox: PROC [device: Device, dstMin, srcMin, size: SF.Vec] ~ { data: Data ~ NARROW[device.data]; ImagerSample.Move[map: data.bitmap, dstMin: dstMin, srcMin: srcMin, size: size]; }; BitmapSwitchBuffer: PROC [device: Device, bounds: SF.Box, copy: BOOL, alt: BOOL] ~ { data: Data ~ NARROW[device.data]; oldBuffer: SampleMap ~ data.bitmap; newBuffer: SampleMap ~ IF alt THEN ImagerSample.ObtainScratchMap[box: bounds, bitsPerSample: 1] ELSE data.savedBuffer; IF copy THEN ImagerSample.Transfer[dst: newBuffer, src: oldBuffer]; data.bitmap ¬ newBuffer; IF alt THEN { data.savedBuffer ¬ oldBuffer } ELSE { data.savedBuffer ¬ NIL; ImagerSample.ReleaseScratchMap[oldBuffer] }; }; objectCache: FunctionCache.Cache ¬ FunctionCache.Create[maxEntries: 30, maxTotalSize: 250000]; ObjectCacheData: TYPE ~ REF ObjectCacheDataRep; ObjectCacheDataRep: TYPE ~ RECORD [ object: Object, clientToDevice: Transformation, v: SELECT tag: * FROM notCached => [], bitmap => [bitmap: SampleMap], ENDCASE ]; < {ok ¬ FALSE; CONTINUE}]; IF ok THEN { o: REF ObjectCacheDataRep.bitmap ¬ NEW[ObjectCacheDataRep.bitmap]; objectCacheData ¬ o; o.bitmap ¬ bitmap; size ¬ ImagerSample.WordsForMap[ImagerSample.GetSize[bitmap]] + 10; } ELSE { objectCacheData ¬ NEW[ObjectCacheDataRep.notCached]; size ¬ SIZE[ObjectCacheDataRep.notCached]; }; objectCacheData.object ¬ object; objectCacheData.clientToDevice ¬ ImagerTransformation.Copy[clientToDevice]; FunctionCache.Insert[x: objectCache, argument: objectCacheData, value: objectCacheData, size: size, clientID: NIL]; }; WITH objectCacheData SELECT FROM ocd: REF ObjectCacheDataRep.bitmap => { t1: VEC ~ ImagerTransformation.TransformVec[clientToDevice, position]; t2: VEC ~ [clientToDevice.c, clientToDevice.f]; t3: VEC ~ [ocd.clientToDevice.c, ocd.clientToDevice.f]; t: VEC ¬ Vector2.Add[t1, Vector2.Sub[t2, t3]]; save: PROC ~ { ImagerBackdoor.SetT[context, identity]; Imager.MaskBitmap[context: context, bitmap: ocd.bitmap, scanMode: [slow: right, fast: up], position: t]; }; Imager.DoSave[context, save]; ImagerTransformation.Destroy[clientToDevice]; RETURN; }; ENDCASE => NULL; }; ImagerTransformation.Destroy[clientToDevice]; }; ImagerPrivate.DefaultDrawObject[context, object, position, interactive]; }; identity: Transformation ~ ImagerTransformation.Scale[1]; >> BitmapGetBufferColorOperator: PROC [context: Context] RETURNS [ColorOperator] ~ { RETURN [ImagerColor.NewColorOperatorGrayLinear[sWhite: 0, sBlack: 1]] }; BitmapAccessBuffer: PROC [device: Device, box: SF.Box, action: PROC [pixelMap: PixelMap]] ~ { data: Data ~ NARROW[device.data]; bitmap: SampleMap ~ ImagerSample.Clip[data.bitmap, box]; pixelMap: PixelMap ~ IF data.scratchPixelMap = NIL THEN NEW[ImagerPixel.PixelMapRep[1]] ELSE data.scratchPixelMap; data.scratchPixelMap ¬ NIL; pixelMap.box ¬ ImagerSample.GetBox[bitmap]; pixelMap[0] ¬ bitmap; action[pixelMap]; pixelMap[0] ¬ NIL; TRUSTED {ImagerSample.ReleaseDescriptor[bitmap]}; data.scratchPixelMap ¬ pixelMap; }; deviceClass: ImagerDevice.DeviceClass ~ NEW[ImagerDevice.DeviceClassRep ¬ [ SetColor: BitmapSetColor, SetPriority: NIL, MaskBoxes: BitmapMaskBoxes, MaskRegion: BitmapMaskRegion, MaskBitmap: BitmapMaskBitmap, MaskRawBitmaps: BitmapMaskRawBitmaps, DrawBitmap: BitmapDrawBitmap, MaskChar: NIL, MoveBox: BitmapMoveBox, SwitchBuffer: BitmapSwitchBuffer, AccessBuffer: BitmapAccessBuffer ]]; contextClass: ImagerPrivate.Class ~ CreateClass[]; CreateClass: PROC RETURNS [class: ImagerPrivate.Class] ~ INLINE { class ¬ ImagerRaster.CreateClass[type: classCode]; <> class.GetBufferColorOperator ¬ BitmapGetBufferColorOperator; }; END. ΈImagerRavenBitmapContextImpl.mesa Copyright Σ 1986, 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved. Michael Plass, September 18, 1989 11:01:05 am PDT Doug Wyatt, January 19, 1987 9:01:22 pm PST Maureen Stone, June 21, 1987 8:33:01 pm PDT Tim Diebert: January 4, 1988 10:34:08 am PST Last tweaked by Mike Spreitzer on April 17, 1990 11:34:15 am PDT Willie-s, April 2, 1992 8:11 pm PST Types Public Procedures For the dashed line hack: Stash the MaskStroke and the MaskVector Procs. Dashed Line Hack The great, dashed line hack. In each of these procedures, the call to SetColor will be noticed by the call to Validate in ImagerRaster. The color has to be set at the conclusion of the call in case the next masks are areas. Color Setup This computes a box for a bitmap tile that will hit a fast case of BITBLT, if this is possible. Eventually this should do separation, black overprinting, etc. First clean up any scratch storage from earlier calls. This bitmap is already in the right orientation for the output raster; just use it! TryTransformingBitmapIntoTile actually did the setup. This bitmap is not easy to turn into a simple tile, so we resort to the slow way. This is for specifying fancier stipples. It would be cleaner if there were a data type defined in an interface somewhere for this, but for now we use a LIST. This bitmap is not quite right for the output raster, but we can make it so. Do any pre-filtering here. Need a box-filter implementation somewhere. If ComputeSource got a pixel map that had sample maps out of the scratch pool, we could do data.scratchSampleMapThatIsInUse _ data.source[0]; here. Masking Procs Objects boxes => [boxes: ManhattanPolygon], Classes Κ–(cedarcode) style•NewlineDelimiter ˜codešœ!™!Kšœ ΟeœI™TK™1K™+K™+K™,K™@K™#K™—šΟk ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ K˜ Kšœ˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšžœ˜Kšœ˜—K˜KšΠlnœž ˜+KšžœΜžœ ˜ήKšžœ!˜(Kšžœ ˜Kšœž˜head™Kšœžœ˜ Kšœžœ˜0Kšœ žœ˜Kšœžœ˜#Kšœžœ˜Kšœ žœ˜&Kšœ žœ˜/Kšœžœ ˜5Kšœ žœ˜&Kšœ žœ˜)Kšœ žœ!˜/Kšœžœ'˜;šžœžœ žœ˜K˜—šœ žœžœ ˜!K˜—Kšœžœ+˜5Kšœžœžœ ˜šœ žœžœ˜Kšœžœ˜Kšœ&žœ˜*K˜K˜K˜/K˜9KšœžœΟc˜>Kšœ žœ˜Kšœžœ  ˜5Kšœ˜Kšœžœ˜#Kšœ)žœ œ  =˜wKšœžœ #˜GKšœžœ &˜GKšœ'žœ ˜DKšœ˜K˜——™šΟnœžœžœžœ/žœžœžœžœžœ˜ΆKšœ žœ8˜H˜BKšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ)˜)Kšœ˜Kš œ žœžœžœžœžœ-˜\Kšœ˜—Kšœ’˜’K™K™IK˜MK˜MKšœ žœ ˜@Kšœ žœ žœ˜3K˜-K˜-K˜#K˜#K˜/K˜/Kšœ1˜1Kš œ&žœžœžœ žœ%žœ  ˜–Kšœ +˜FK˜K˜Kšœ7˜7Kšžœ ˜Kšœ˜K˜—š‘ œž œ*˜@Kšœ žœ'˜:K˜Kš œ'žœ žœžœžœ˜aKšœ˜K˜—š‘ œž œžœ˜AKšœ žœ'˜:Kšžœ˜Kšœ˜K˜——™Kšœ žœžœ˜/Kšœ žœ˜%K˜K™αKš‘œžœžœžœ˜CKš‘œžœ,žœžœ˜TK˜š‘œžœžœžœ˜3šžœ"žœž˜1šœ#žœžœ˜AKšœžœ ˜2Kšžœžœžœžœ˜IK•StartOfExpansione[context: Imager.Context, v: VEC, from: ImagerBackdoor.CoordSys, to: ImagerBackdoor.CoordSys]šœžœ‘˜€Kš žœžœžœžœ +˜UK˜—Kšžœ˜Kšžœžœ˜—K˜—š‘ œžœžœ˜6šžœžœ˜Kšœžœ*˜KKšœžœ*˜KKšœ5˜5Kšœ"žœ%˜MK˜*Kšœ(˜(K˜*Kšœ*žœ˜1K˜*K˜—Kšœžœ)˜.K˜—š‘ œžœ,žœ˜Gšžœžœ˜Kšœžœ*˜KKšœžœ*˜KKšœ"žœ%˜MK˜*Kšœ.˜.K˜*Kšœ2˜2K˜*K˜—Kšžœ/˜3K˜—K˜Kšœ žœžœžœ˜1Kšœ žœžœžœ ˜*Kšœ žœžœžœ ˜,Kšœ žœžœžœ ˜+š‘œžœMžœ˜oKšœžœ ˜2š‘œžœ˜Kšœžœ˜š œžœžœžœžœ ž˜5Kšœ%˜%Kšœ)˜)Kšžœžœ˜—š ‘œžœžœžœžœ˜'K–e[context: Imager.Context, v: VEC, from: ImagerBackdoor.CoordSys, to: ImagerBackdoor.CoordSys]šœžœb˜hKšžœ˜—K˜$K˜&K˜9Kšœ˜—Kšœ˜K˜K˜——™ š‘œžœžœžœžœžœžœ žœ˜iKšœO˜Ošžœžœžœž˜šžœžœžœž˜Kšœ%˜%Kšžœ˜—Kšžœ˜—Kšžœ*˜0Kšœ˜K˜—˜2Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜K˜—˜0Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜K˜—˜,K˜—š‘œžœžœ1˜Fšžœ&žœž˜5˜K˜Kšœ< ˜WK˜—Kšžœžœ˜—Kšœ˜K˜—š‘œžœžœžœ˜Hšžœ&žœž˜5˜Kšžœ ˜K˜—Kšžœžœ˜—Kšžœžœ˜Kšœ˜K˜—š‘ œžœžœ žœ˜Cšžœ&žœž˜5˜K˜Kšœ< ˜WK˜—Kšžœžœ˜—Kšœ˜K˜—š ‘ œžœžœžœžœ˜@šžœ&žœž˜5Kšœžœ˜+Kšžœžœ˜—Kšžœžœ˜ Kšœ˜K˜—š‘œžœžœžœ ˜DK™_Kšœžœ,˜3Kšœžœ-˜5Kšœ žœ ˜šžœžœžœ žœžœ žœžœžœ žœ˜`K˜Kšœ˜—Kšžœ˜ Kšœ˜K˜—š‘œžœžœžœ˜RKšœžœ˜$Kšœžœ%˜5KšœAžœ ˜PKšœ:˜:šžœžœžœž˜+Kšœm˜mšžœžœžœž˜"Kšœžœžœžœ˜&Kšžœ˜—KšœN˜NKšžœ˜—Kšœ&˜&Kšžœ<˜BKšœ˜K˜—š‘œžœ.žœžœ˜_Kšœžœ'˜.K™>Kšžœ˜ Kšœ˜K˜—Kšœžœžœ˜1šœžœžœ˜$Kšœ˜Kšœ˜Kšœ žœ˜Kšœ žœ˜Kšœžœ˜Kšœ˜Kšœ˜K˜—šœžœžœ  C˜\K˜—š‘œžœA˜UKšœ žœ˜"Kšœ žœžœ žœžœžœžœžœ˜\Kšœžœ˜$šœ˜K™6Kšœžœ˜Kšœžœ˜šžœ$žœžœ˜0KšœA˜AKšœ#žœ˜'Kšœ˜—Kšœ˜—šžœžœž˜šœ#˜#Kšœžœ&˜-K˜K˜šžœž˜ K˜9K˜9šžœ˜ K˜$K˜7K˜K˜——Kšœ˜—šœ+˜+Kšœ^˜^Kšœ˜—šœ ˜ Kšœ˜KšœI˜IKšœžœ žœ žœ˜=šžœžœž˜šœžœ"˜>K™SKšœžœ6˜=Kšœžœ2˜9KšœP˜PKšœ?˜?K˜K˜K˜ K˜-K˜Kšœ˜—šœ žœ-˜9Kšœ5™5Kšœ˜—šžœ˜ K™QKšœW˜WKšœ2˜2K˜K˜2K˜-Kšœ˜——Kšœ˜—šœ ˜ šžœž˜šœ ˜ Kšœ.žœ ˜=K˜%šžœž˜KšΟfœ0˜6Kš’œ0˜6šžœ˜ K˜K˜ˆK˜Kšœ˜——Kšœ˜—šœ˜K™žKš œžœžœžœžœ ˜#Kšœžœžœ ˜2Kšœžœžœ˜8K˜K˜K˜&K˜Kšœ˜—šΟyΠcy˜;Kš£œ£Πky£ ˜.Kš £ ₯£œ£₯£(₯£₯£œ£˜KKš£ ₯£œ£₯£'˜@Kš£₯£˜&Kš£Q€*˜{Kš£œ₯£%˜3Kš£œ£˜Kš£ œ£˜Kš£œ£˜+Kš£œ£˜#Kš£˜—šžœ˜ šžœž˜Kšžœ7˜;Kšžœžœl˜x—Kšœ˜——Kšœ˜—Kšžœžœ˜—K˜'šžœžœžœž˜6šœ˜šžœž˜K˜1K˜*Kšžœžœ˜—Kšœ˜—šœ˜Kšžœžœ'˜@Kšœ˜—Kšžœžœ˜—K˜8Kšœ+žœ˜B˜Kšœžœ˜$Kšœ/žœžœ˜HKšœ!žœ ˜0Kšœ˜Kšœžœ ˜(Kšœžœ˜Kšœ žœ˜Kšœž˜Kšœ˜—Kšœ˜K˜—Kšœ žœžœ ˜%š‘œžœžœžœ˜SKšœ$˜$šžœ žœ˜Kšœ.˜.KšœE˜Ešžœžœžœ žœ žœ žœžœžœžœ˜tKšœžœ˜!Kšœžœ˜!šžœžœžœ˜=K™Lšœžœ˜Kšœ?˜?KšœC˜CKšœ-˜-Kšœ6˜6Kšœ0˜0Kšœ)˜)Kšœ˜—K˜QKšœm˜mKšœ˜Kš žœžœžœ5žœTžœ˜ΠKšœ0˜0Kšœ1˜1Kšœ%žœ˜*Kšžœ9žœ˜EK˜K˜K˜K˜+K˜Kšžœžœ˜Kšœ˜—Kšœ˜—Kšœ˜—Kšžœžœ˜Kšœ˜K˜—š‘ œžœ2žœžœžœžœžœžœžœ˜°Kšœ:˜:KšœΉ˜ΉK™GKšžœ ˜Kšœ˜K˜—š‘œžœ[˜vKšœ žœ˜"Kšœ!˜!Kšœžœžœ˜0Kšœ9˜9Kšœžœ ˜3Kšœžœ˜šœ&˜&šžœ žœž˜Kš œžœžœ0žœ"žœ(žœ&˜ΩKšžœžœžœ˜—Kšœ˜—Kš œžœžœžœžœžœ˜OKšœT˜TKš žœ žœžœžœžœV˜•K˜K˜šžœ ˜šžœ˜Kšœžœ1˜=šžœžœžœ˜Kšœf˜fKšœžœΏ˜ΘK–U[size: SF.Vec, bitsPerSample: IISample.BitsPerSample _ 1, bitsPerLine: NAT _ 0]šœΜ˜ΜKšœ˜—K˜Kšœ˜—šžœ˜K˜SKšœ[Οb2œ™“Kšœ˜——Kšœ˜——™ šœ žœžœ˜K˜—š‘œžœžœ žœ˜RKšœ žœ˜!šžœ ž˜˜ Kšœf˜fK˜—˜ K–¨[self: IISample.SampleMap, boxes: SF.BoxGenerator, tile: IISample.SampleMap, phase: NAT _ 0, function: IISample.Function _ [dstFunc: null, srcFunc: null]]šœƒ˜ƒK˜—šœ žœ˜KšœFžœ˜Xš ‘œžœžœ(žœžœ˜LK˜Kšœq˜qKšœ}˜}Kšœ˜—Kšœ„˜„Kšœ+˜+K˜—šœ˜š‘œžœ(žœ ˜?K–τ[map: IISample.SampleMap, initIndex: SF.Vec _ [s: 0, f: 0], delta: SF.Vec _ [s: 0, f: 1], buffer: IISample.SampleBuffer, start: NAT _ 0, count: NAT _ 32767, function: IISample.Function _ [dstFunc: null, srcFunc: null]]šœ†˜†Kšœ˜—KšœIžœ0˜~Kšœ˜—Kšžœžœ˜—Kšœ˜K˜—š‘œžœžœžœ˜jKšœ žœ˜!Kšžœžœžœ˜#Kšœžœ\˜Kšœ˜K˜—š ‘œžœ,žœžœ žœ˜uKšœ žœ˜!Kšœy˜yKšœ˜K˜—š‘œžœžœžœ ˜YKšœ žœ˜!Kšœ ‘œžœ=˜eKšœ˜K˜—š ‘œžœ,žœžœ žœ˜uKšœ žœ˜!KšœV˜VKšœ˜K˜—š‘ œžœ(žœ ˜FKšœ žœ˜!KšœP˜PKšœ˜K˜—š ‘œžœžœ žœžœ˜TKšœ žœ˜!Kšœ#˜#Kšœžœžœ>žœ˜vKšžœžœ7˜CK˜šžœ˜Kšžœ!˜%Kšžœžœ/˜L—Kšœ˜K˜——šœ™–:[maxEntries: INT _ 10, maxTotalSize: INT _ 2147483647]˜^K˜—Kšœžœžœ˜/šœžœžœ˜#Kšœ˜Kšœ˜šœžœž˜K˜K˜K™#Kšž˜—Kšœ˜K˜—šΠny£₯£ ˜)K˜—š §£₯£₯£.₯£₯£˜fKš£Z˜ZKš£w˜wKš£i˜iKš₯£%₯£₯£€˜Bš₯£ ₯£˜K–j[x: FunctionCache.Cache, compare: FunctionCache.CompareProc, clientID: FunctionCache.ClientID _ NIL]š£n˜nš §£₯£ ₯£₯£₯£˜0Kš£₯£ ˜(Kš₯£₯£₯£₯£˜+Kš₯£O˜UKš£˜—Kš£X˜Xš₯£₯£.₯£˜:Kš£!œ£₯£C₯£ ˜xš₯£₯£₯£˜Kš£œ£₯£˜Kš£₯£œ£˜Kš£₯£œ£₯£˜Kš§£₯£Q˜Yš£œ£5₯£˜CKš£œ£₯£₯£˜2—š₯£˜š₯£˜Kš£₯£œ£₯£˜BKš£œ£˜Kš£ œ£˜Kš£œ£=˜CKš£˜—š₯£˜Kš£œ£₯£˜4Kš£œ£₯£˜*Kš£˜——Kš£œ£˜ Kš£œ£+˜KK–‘[x: FunctionCache.Cache, argument: FunctionCache.Domain, value: FunctionCache.Range, size: INT, clientID: FunctionCache.ClientID _ NIL]š£n₯£˜sKš£˜—š₯£₯£₯˜ š£₯£˜'Kš£₯£?˜FKš£₯£(˜/Kš£₯£0˜7Kš£₯£œ£&˜.š£₯£˜Kš£'˜'Kš£h˜hKš£˜—Kš£˜Kš£-˜-Kš₯£˜Kš£˜—Kš₯£₯£˜—Kš£˜—Kš£-˜-Kš£˜—Kš£G₯˜HKš£˜Kš£9˜9Kš§˜K˜—š‘œžœžœ˜QKšžœ?˜EKšœ˜K˜—š‘œžœžœžœ˜]Kšœ žœ˜!Kšœ8˜8Kš œžœžœžœžœžœ˜rKšœžœ˜K˜+K˜Kšœ˜Kšœžœ˜Kšžœ*˜1K˜ Kšœ˜K˜——™šœ)žœ ˜LKš‘œ˜Kš‘ œžœ˜Kš‘ œ˜Kš‘ œ˜Kš‘ œ˜Kš‘œ˜%Kš‘ œ˜Kš‘œžœ˜Kš‘œ˜Kš‘ œ˜!Kš‘ œ˜ Kšœ˜K˜—Kšœ2˜2š‘ œžœžœ žœ˜AK˜2Kš£œ£˜(K˜