DIRECTORY Atom, Basics, CedarProcess, Convert, G3dColorDisplaySupport, G3dRender, G3dRenderWithPixels, G3dSortandDisplay, Imager, ImagerBackdoor, ImagerColor, ImagerColorFns, ImagerPixel, ImagerSample, ImageTwiddle, IO, NamedColors, Real, RealFns, Rope, Terminal, ThreeDViewer; ImageTwiddleImpl: CEDAR MONITOR IMPORTS Atom, Basics, CedarProcess, Convert, G3dColorDisplaySupport, G3dRender, G3dRenderWithPixels, Imager, ImagerBackdoor, ImagerColor, ImagerColorFns, ImagerPixel, ImagerSample, IO, NamedColors, Real, RealFns, Rope, Terminal, ThreeDViewer EXPORTS ImageTwiddle ~ BEGIN Context: TYPE ~ G3dRender.Context; ContextClass: TYPE ~ G3dRender.ContextClass; Triple: TYPE ~ G3dRender.Triple; RGB: TYPE ~ G3dRender.RGB; IntegerPair: TYPE ~ G3dRender.IntegerPair; IntRGB: TYPE ~ RECORD[ r, g, b: CARDINAL]; IntRGBSequence: TYPE ~ RECORD [ SEQUENCE length: NAT OF IntRGB ]; NatSequence: TYPE ~ G3dRender.NatSequence; NatSequenceRep: TYPE ~ G3dRender.NatSequenceRep; Patch: TYPE ~ G3dRender.Patch; ClipState: TYPE ~ G3dRender.ClipState; ImagerProc: TYPE ~ G3dRender.ImagerProc; ImagerProcRec: TYPE ~ G3dRender.ImagerProcRec; PixelMap: TYPE ~ ImagerPixel.PixelMap; SetNamedColor: PUBLIC PROC [imagerCtx: Imager.Context, color: Rope.ROPE] ~ { clr: RGB _ ImagerColorFns.RGBFromHSL[ NamedColors.RopeToHSL[color] ]; Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[clr] ]; }; SetRGBColor: PUBLIC PROC [imagerCtx: Imager.Context, clr: RGB] ~ { Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[clr] ]; }; Rotate8BitClrMap: PUBLIC PROC [vt: Terminal.Virtual, firstValue, lastValue, duration: NAT _ 0] ~ { r, g, b: ARRAY [0..256) OF [0..256); times: NAT; IF lastValue = 0 THEN lastValue _ 255; FOR i: NAT IN [firstValue .. lastValue] DO [r[i], g[i], b[i]] _ Terminal.GetColor[ vt, i]; ENDLOOP; IF duration = 0 THEN times _ 0 -- will be one complete cycle ELSE times _ duration * 75; -- seconds times fields per second times _ times + -- Fill out full cycle (lastValue - firstValue + 1) - times MOD (lastValue - firstValue + 1); FOR i: NAT IN [0..times) DO tr, tg, tb: [0..256); CedarProcess.CheckAbort[]; -- check for external abort request before proceeding vt.WaitForBWVerticalRetrace[]; -- await top of scan (to control update rate) tr _ r[firstValue]; tg _ g[firstValue]; tb _ b[firstValue]; FOR j: NAT IN (firstValue .. lastValue] DO r[j-1] _ r[j]; g[j-1] _ g[j]; b[j-1] _ b[j]; ENDLOOP; r[lastValue] _ tr; g[lastValue] _ tg; b[lastValue] _ tb; FOR i: NAT IN [firstValue .. lastValue] DO Terminal.SetColor[ vt, i, 0, r[i], g[i], b[i]]; ENDLOOP; ENDLOOP; }; Show8BitClrMap: PUBLIC PROC [context: Context] ~ { ThreeDViewer.DrawInViewer[ context, NEW[ImagerProcRec _ [ViewerShowClrMap, NIL] ] ]; }; ViewerShowClrMap: ImagerProc ~ { DoShowClrMap: PROC[pixelMap: PixelMap] ~ { firstBottom: INTEGER _ Real.Fix[.75 * context.viewPort.h]; size: INTEGER _ Real.Fix[ .025 * MIN[context.viewPort.h, context.viewPort.w] ]; firstTop: INTEGER _ firstBottom + size; firstLeft: INTEGER _ Real.Fix[context.viewPort.w/2.0] - 16*size; firstRight: INTEGER _ firstLeft + size; FOR i: NAT IN [0..256) DO top: NAT _ firstTop + size * (i / 32); bottom: NAT _ firstBottom + size * (i / 32); left: NAT _ firstLeft + size * (i MOD 32); right: NAT _ firstRight + size * (i MOD 32); ImagerSample.Fill[pixelMap[0], [min: [f: left, s: bottom], max: [f: right, s: top] ], i]; ENDLOOP; }; ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoShowClrMap, context.viewPort^]; }; ShowMapOnLog: PUBLIC PROC [context: Context, vt: Terminal.Virtual] ~ { RopeFromQuad: PROC[ i, r, g, b: CARD] RETURNS[ Rope.ROPE ] ~ { output: Rope.ROPE _ Rope.Cat[ " \t ", Convert.RopeFromCard[i], " ", Convert.RopeFromCard[r], " " ]; output _ Rope.Cat[ output, Convert.RopeFromCard[g], " ", Convert.RopeFromCard[b] ]; RETURN[ output ]; }; log: IO.STREAM _ NARROW[ Atom.GetPropFromList[context.props, $Log] ]; state: Terminal.ColorMode _ vt.GetColorMode[]; maxVal: REAL _ IF state.full THEN 255.0 ELSE REAL[Basics.BITSHIFT[1, state.bitsPerPixelChannelA] - 1]; FOR i: NAT IN [ 0 .. INTEGER[Real.Fix[maxVal]] ] DO -- linear ramp exponentiated jr, jg, jb: CARD; IF Terminal.GetColorMode[vt].full THEN { jr _ vt.GetRedMap[i]; jg _ vt.GetGreenMap[i]; jb _ vt.GetBlueMap[i]; } ELSE [jr, jg, jb] _ vt.GetColor[i]; IF log # NIL THEN { log.PutRope[ RopeFromQuad[ i, jr, jg, jb ] ]; IF (i/4) * 4 = i THEN log.PutRope["\n"]; G3dRender.FlushLog[context]; }; ENDLOOP; }; LoadMultiRamps: PUBLIC PROC [vt: Terminal.Virtual, colors: LIST OF RGB] ~ { clr: REF IntRGBSequence _ NEW[ IntRGBSequence[32] ]; numClrs, thisClr, nextClr, rampLength: NAT _ 0; state: Terminal.ColorMode _ vt.GetColorMode[]; maxVal: REAL _ IF state.full THEN 255.0 ELSE REAL[Basics.BITSHIFT[1, state.bitsPerPixelChannelA] - 1]; WHILE colors # NIL DO clr[numClrs].r _ Real.Fix[maxVal * MAX[0.0, MIN[1.0, colors.first.R]] ]; clr[numClrs].g _ Real.Fix[maxVal * MAX[0.0, MIN[1.0, colors.first.G]] ]; clr[numClrs].b _ Real.Fix[maxVal * MAX[0.0, MIN[1.0, colors.first.B]] ]; numClrs _ numClrs + 1; colors _ colors.rest; ENDLOOP; rampLength _ Real.Fix[maxVal] / (numClrs-1); FOR i: NAT IN [ 0 .. INTEGER[Real.Fix[maxVal]] ] DO jr, jg, jb: [0..256); position: NAT _ i MOD rampLength; IF position = 0 THEN IF nextClr < numClrs-1 THEN { thisClr _ nextClr; nextClr _ nextClr + 1; } ELSE thisClr _ nextClr; jr _ clr[thisClr].r * (rampLength - position) / rampLength + clr[nextClr].r * position / rampLength; jg _ clr[thisClr].g * (rampLength - position) / rampLength + clr[nextClr].g * position / rampLength; jb _ clr[thisClr].b * (rampLength - position) / rampLength + clr[nextClr].b * position / rampLength; IF Terminal.GetColorMode[vt].full THEN { vt.SetRedMap[i, jr]; vt.SetGreenMap[i, jg]; vt.SetBlueMap[i, jb]; } ELSE vt.SetColor[i, 0, jr, jg, jb]; ENDLOOP; }; ThresholdClrMap: PUBLIC PROC [vt: Terminal.Virtual, levels: NAT] ~ { Linearize: PROC [value: REAL] RETURNS[NAT] ~ { RETURN[ Real.Round[RealFns.Power[value / 255.0, .43] * 255.0] ]; }; FOR i: NAT IN [0..256) DO -- grayscale j: NAT _ (i * levels / 256) * (256 / levels); gray: NAT _ Linearize[j]; vt.SetColor[i, 0, gray, gray, gray]; ENDLOOP; }; threshTable: NatSequence _ NEW[ NatSequenceRep[256] ]; threshLevels: NAT _ 0; ThresholdImage: PUBLIC PROC [context: Context, levels: NAT] ~ { threshLevels _ levels; IF context.viewer # NIL -- do through viewer THEN context.class.drawInViewer[context, NEW[ImagerProcRec _ [ThresholdImage2, NIL]]] ELSE DoThresholdImage[context.pixels]; -- do directly }; ThresholdImage2: ImagerProc ~ { ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoThresholdImage, context.viewPort^]; }; DoThresholdImage: PROC [pixelMap: PixelMap] ~ { Action: PROC ~ { Linearize: PROC [value: REAL] RETURNS[NAT] ~ { RETURN[ Real.Round[RealFns.Power[value / 255.0, .43] * 255.0] ]; }; maxValue: REAL _ 255.0; xStart: NAT _ Real.Round[pixelMap.box.min.f]; yStart: NAT _ Real.Round[pixelMap.box.min.s]; width: NAT _ Real.Round[pixelMap.box.max.f - pixelMap.box.min.f]; height: NAT _ Real.Round[pixelMap.box.max.s - pixelMap.box.min.s]; scanSeg: ImagerPixel.PixelBuffer _ ImagerPixel.NewPixels[ pixelMap.samplesPerPixel, width ]; FOR i: NAT IN [0..256) DO j: NAT _ (i * threshLevels / 256) * (256 / threshLevels); threshTable[i] _ j; ENDLOOP; FOR y: NAT IN [ yStart .. height + yStart ) DO ImagerPixel.GetPixels[ self: pixelMap, pixels: scanSeg, initIndex: [f: xStart, s: y], count: width ]; FOR x: NAT IN [ 0 .. width ) DO IF scanSeg.samplesPerPixel > 2 THEN { scanSeg[0][x] _ threshTable[scanSeg[0][x]]; scanSeg[1][x] _ threshTable[scanSeg[1][x]]; scanSeg[2][x] _ threshTable[scanSeg[2][x]]; } ELSE scanSeg[0][x] _ threshTable[scanSeg[0][x]]; ENDLOOP; ImagerPixel.PutPixels[ self: pixelMap, pixels: scanSeg, initIndex: [f: xStart, s: y], count: width ]; ENDLOOP; }; CedarProcess.DoWithPriority[background, Action]; }; AdjustValueRamp: PUBLIC PROC[context: Context, exponent: RGB] ~ { Action: PROC[] ~ { maxValue: REAL _ 255.0; xStart: NAT _ Real.Round[context.viewPort.x]; yStart: NAT _ Real.Round[context.viewPort.x]; width: NAT _ Real.Round[context.viewPort.w]; height: NAT _ Real.Round[context.viewPort.h]; expTableR: NatSequence _ NEW[ NatSequenceRep[256] ]; expTableG: NatSequence _ NEW[ NatSequenceRep[256] ]; expTableB: NatSequence _ NEW[ NatSequenceRep[256] ]; scanSeg: ImagerPixel.PixelBuffer _ ImagerPixel.NewPixels[ context.pixels.samplesPerPixel, width ]; SELECT context.class.displayType FROM $FullColor => FOR i: NAT IN [0..256) DO expTableR[i] _ Real.Round[ RealFns.Power[ i/255.0, exponent.R] * 255.0 ]; expTableG[i] _ Real.Round[ RealFns.Power[ i/255.0, exponent.G] * 255.0 ]; expTableB[i] _ Real.Round[ RealFns.Power[ i/255.0, exponent.B] * 255.0 ]; ENDLOOP; $Gray => { exp: REAL _ (exponent.R + exponent.G + exponent.B) / 3.0; FOR i: NAT IN [0..256) DO expTableG[i] _ Real.Round[ RealFns.Power[ i/255.0, exp] * 255.0 ]; ENDLOOP; }; ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Improper renderMode"]; FOR y: NAT IN [ yStart .. height + yStart ) DO ImagerPixel.GetPixels[ self: context.pixels, pixels: scanSeg, initIndex: [f: xStart, s: y], count: width ]; FOR x: NAT IN [ 0 .. width ) DO SELECT context.class.displayType FROM $FullColor => { scanSeg[0][x] _ expTableR[scanSeg[0][x]]; scanSeg[1][x] _ expTableG[scanSeg[1][x]]; scanSeg[2][x] _ expTableB[scanSeg[2][x]]; }; $Gray => scanSeg[0][x] _ expTableG[scanSeg[0][x]]; ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Improper renderMode"]; ENDLOOP; ImagerPixel.PutPixels[ self: context.pixels, pixels: scanSeg, initIndex: [f: xStart, s: y], count: width ]; ENDLOOP; }; CedarProcess.DoWithPriority[background, Action]; }; AdjustSaturation: PUBLIC PROC[context: Context, percent: REAL] ~ { Action: PROC[] ~ { maxValue: REAL _ 255.0; xStart: NAT _ Real.Round[context.viewPort.x]; yStart: NAT _ Real.Round[context.viewPort.x]; width: NAT _ Real.Round[context.viewPort.w]; height: NAT _ Real.Round[context.viewPort.h]; scanSeg: ImagerPixel.PixelBuffer _ ImagerPixel.NewPixels[ context.pixels.samplesPerPixel, width ]; FOR y: NAT IN [ yStart .. height + yStart ) DO ImagerPixel.GetPixels[ self: context.pixels, pixels: scanSeg, initIndex: [f: xStart, s: y], count: width ]; FOR x: NAT IN [ 0 .. width ) DO SELECT context.class.displayType FROM $FullColor => { scale: REAL; redVal: CARDINAL _ scanSeg[0][x]; grnVal: CARDINAL _ scanSeg[1][x]; bluVal: CARDINAL _ scanSeg[2][x]; min: CARDINAL _ MIN[redVal, grnVal, bluVal]; max: CARDINAL _ MAX[redVal, grnVal, bluVal]; newMin: CARDINAL _ MIN[max, INTEGER[Real.Fix[percent * min]]]; IF (min = 0) OR (max - min = 0) THEN LOOP; scale _ 1.0 * (max - newMin) / (max - min); redVal _ Real.Fix[ (redVal - min) * scale + newMin ]; grnVal _ Real.Fix[ (grnVal - min) * scale + newMin ]; bluVal _ Real.Fix[ (bluVal - min) * scale + newMin ]; scanSeg[0][x] _ redVal; scanSeg[1][x] _ grnVal; scanSeg[2][x] _ bluVal; }; ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Only for full color"]; ENDLOOP; ImagerPixel.PutPixels[ self: context.pixels, pixels: scanSeg, initIndex: [f: xStart, s: y], count: width ]; ENDLOOP; }; CedarProcess.DoWithPriority[background, Action]; }; LoadOldStd8BitClrMap: PUBLIC PROC [vt: Terminal.Virtual] ~ { Linearize: PROC [value: REAL] RETURNS[NAT] ~ { RETURN[ Real.Round[RealFns.Power[value / 255.0, .43] * 255.0] ]; }; FOR i: NAT IN [0..40) DO -- grayscale gray: NAT _ Linearize[i*6.5]; vt.SetColor[i + 216, 0, gray, gray, gray]; ENDLOOP; FOR i: NAT IN [0..216) DO -- 6 x 6 x 6 color cube vt.SetColor[i, 0, Linearize[42.5 * (i/36 + 1)], Linearize[42.5 * ((i/6) MOD 6 + 1)], Linearize[42.5 * (i MOD 6 + 1)] ]; ENDLOOP; vt.SetColor[0, 0, 0, 0, 0]; }; SetUpTerrainColors: PUBLIC PROC[vt: Terminal.Virtual] ~ { terrainHues: ARRAY [0..4) OF RGB _ [[.05, .4, .0], [.5, .6, .1], [.4, .4, .4], [1., 1., 1.]]; skyHue: RGB _ [.5, .5, 1.0]; waterHue: RGB _ [.2, .2, .8]; start: NAT _ 8; -- start^ _ 8; length: NAT _ 240; valueStepSize, minValue, rampValue, index, hueRampSize: NAT _ 0; hueRampSize _ 238 / (LENGTH[terrainHues] + 1); valueStepSize _ 256 / hueRampSize; minValue _ 256 - valueStepSize*hueRampSize; FOR j: NAT IN [0..4) DO -- do hue steps from each hue to next currentHue: RGB _ [terrainHues[j].R, terrainHues[j].G, terrainHues[j].B]; rampValue _ minValue; FOR i: NAT IN [0..hueRampSize) DO -- build hue ramps r, g, b: NAT; r _ Real.Round[ rampValue * currentHue.R ]; g _ Real.Round[ rampValue * currentHue.G ]; b _ Real.Round[ rampValue * currentHue.B ]; vt.SetColor[index+start, 0, r, g, b ]; rampValue _ rampValue + valueStepSize; index _ index + 1; ENDLOOP; ENDLOOP; vt.SetColor[239+start, 0, Real.Fix[skyHue.R*255], Real.Fix[skyHue.G*255], Real.Fix[skyHue.B*255] ]; vt.SetColor[start, 0, Real.Fix[waterHue.R*255], Real.Fix[waterHue.G*255], Real.Fix[waterHue.B*255] ]; }; DitherImage: PUBLIC PROC[dstContext, rgbContext: Context] ~ { G3dColorDisplaySupport.DitherImage[dstContext, rgbContext]; -- convenience call }; ScaleDownImage: PUBLIC PROC[dstContext, srcContext: Context, strtchFctr: REAL _ 1.0] ~{ Action: PROC ~ { srcHght: NAT _ Real.Round[srcContext.viewPort.h]; dstHght: NAT _ Real.Round[dstContext.viewPort.h]; srcWdth: NAT _ Real.Round[srcContext.viewPort.w]; dstWdth: NAT _ Real.Round[dstContext.viewPort.w]; numHits: NatSequence _ NEW[ NatSequenceRep[dstWdth] ]; -- holds divisor for average xPos, yPos: INTEGER; xCtr, yCtr: NAT _ 0; scanSegIn, scanSegOut: ImagerPixel.PixelBuffer; scanSegIn _ ImagerPixel.ObtainScratchPixels[ srcContext.pixels.samplesPerPixel, srcWdth ]; scanSegOut _ ImagerPixel.ObtainScratchPixels[ dstContext.pixels.samplesPerPixel, dstWdth ]; FOR x: NAT IN [0..dstWdth) DO -- clear the pixels FOR i: NAT IN [0..scanSegOut.samplesPerPixel) DO scanSegOut[i][x] _ 0; ENDLOOP; ENDLOOP; yPos _ 2 * dstHght - srcHght; -- offset for vertical incrementer FOR x: NAT IN [0..dstWdth) DO numHits[x] _ 0; ENDLOOP; -- clear divisor array FOR y: NAT IN [ 0 .. srcHght ) DO -- work up through source scan lines ImagerPixel.GetPixels[ self: srcContext.pixels, pixels: scanSegIn, initIndex: [f: 0, s: y], count: srcWdth ]; xPos _ 2 * dstWdth - srcWdth; xCtr _ 0; FOR x: NAT IN [ 0 .. srcWdth ) DO -- work across source scan line FOR i: NAT IN [0..scanSegOut.samplesPerPixel) DO scanSegOut[i][xCtr] _ scanSegOut[i][xCtr] + scanSegIn[i][x]; -- sum intensities ENDLOOP; numHits[xCtr] _ numHits[xCtr] + 1; -- sum hits per pixel IF xPos > 0 THEN { xCtr _ xCtr + 1; xPos _ xPos - 2 * srcWdth; IF xCtr >= dstWdth THEN EXIT; }; xPos _ xPos + 2 * dstWdth; ENDLOOP; IF yPos > 0 THEN { -- big increment exceeded FOR x: NAT IN [0..dstWdth) DO FOR i: NAT IN [0..scanSegOut.samplesPerPixel) DO scanSegOut[i][x] _ Real.Fix[ strtchFctr * scanSegOut[i][x] / numHits[x] ]; ENDLOOP; numHits[x] _ 0; ENDLOOP; ImagerPixel.PutPixels[ self: dstContext.pixels, pixels: scanSegOut, initIndex: [f: 0, s: yCtr], count: dstWdth ]; FOR x: NAT IN [0..dstWdth) DO -- clear the pixels FOR i: NAT IN [0..scanSegOut.samplesPerPixel) DO scanSegOut[i][x] _ 0; ENDLOOP; ENDLOOP; yPos _ yPos - 2 * srcHght; -- subtract big increment from position yCtr _ yCtr + 1; IF yCtr >= dstHght THEN EXIT; }; yPos _ yPos + 2 * dstHght; -- add in little incrment until big increment exceeded ENDLOOP; ImagerPixel.ReleaseScratchPixels[scanSegIn]; ImagerPixel.ReleaseScratchPixels[scanSegOut]; IF dstContext.viewer # NIL THEN -- make sure image gets copied to screen ThreeDViewer.DrawInViewer[ dstContext, NEW[ImagerProcRec _ [G3dColorDisplaySupport.StuffBuf, NIL]] ]; }; IF srcContext.viewPort = NIL THEN srcContext.class.validateDisplay[srcContext]; IF dstContext.viewPort = NIL THEN dstContext.class.validateDisplay[dstContext]; IF dstContext.pixels = NIL THEN G3dRenderWithPixels.BufferRendering[dstContext, TRUE]; CedarProcess.DoWithPriority[background, Action]; -- be nice to other processess }; END. ΪImageTwiddleImpl.mesa Last Edited by: Crow, April 21, 1989 6:34:48 pm PDT Bloomenthal, September 26, 1988 12:04:42 pm PDT Types Colors Rotates color map entries in given range Print color map on log for context set color map to constant steps simulating restricted number of grey levels adjust pixel values over whole image to conform restricted number of grey levels threshTable[i] _ Linearize[j]; Adjust the values in the display memory for TRC or ? (only for full-color or gray) Adjust the values in the display memory for Saturation (only for full-color) setting up colors for old terrain pictures { forest, savannah, rock, snow } AIS Files and Texture Images Bresenham-like walk across image, areas averaged down Κ[˜head™J™3Icode™/IdefaultšΟk œΠœ-œ ˜–—head2šœœ˜Jšœλ˜ςJšœ ˜J˜Jšœ˜J˜—head3šΠbi™Iunitšœ œ˜#Mšœœ˜.Mšœ œ˜#Jšœœ œ˜Jšœœ˜,Jšœ œœ œ˜-Jš œœœœ œœ ˜AJšœœ˜,Jšœœ˜1Jšœ œ˜"Jšœœ˜)Jšœœ˜*Mšœœ˜/Jšœ œ˜)—šΟb™ašΟn œœœ)œ˜LLšœœ=˜EQ˜ šœœœœ˜+Q˜/Qšœ˜—Qšœ˜—Q˜Q˜— š œ œ˜2Jšœ$œ$œ˜TQ˜Q˜—š œ˜ š  œœ˜*Qšœ œ&˜:Qšœœœ+˜OQšœ œ˜'Qšœ œ.˜@Qšœ œ˜( šœœœ ˜Qšœœ˜'Qšœœ"˜-Qšœœœ˜+Qšœœœ˜-Q˜YQšœ˜—Q˜—J˜QJ˜— š  œ œ-˜FQ™"š   œœœœœ˜>Jšœ œa˜rJ˜UJšœ ˜J˜—Jšœœœœ.˜EQ˜. šœœœ ˜Qšœ˜ Qšœœœ%˜>—š œœœœœ‘˜VMšœ œ˜šœ ˜"šœ˜M˜M˜M˜M˜—Mšœ ˜$—šœœœ˜J˜-Jšœœ˜(J˜J˜—Jšœ˜—J˜J˜—š  œœœ œœœ˜KQšœœœ˜4Qšœ'œ˜/Q˜. šœœœ ˜Qšœ˜ Qšœœœ%˜>— šœ œ˜Qšœ#œœ˜HQšœ#œœ˜HQšœ#œœ˜HQ˜Q˜Qšœ˜—J˜,š œœœœ˜3M˜Mšœ œœ ˜!šœœœ˜,Mšœ5˜9Mšœ˜—M˜kJ˜hJ˜hšœ ˜"MšœN˜RMšœ ˜$—Qšœ˜—J˜—š œœœ œ˜DQ™Kš   œœ œœœ˜.Jšœ:˜@J˜— š œœœ œ‘˜4Jšœœ'˜-Jšœœ˜J˜%Jšœ˜—Q˜—Qšœœ˜6Jšœœ˜š œœœœ˜?Qšœ œ˜ šœœ ‘˜7Qšœ%œ#œ˜UMšœ%‘˜7—J˜—š œ˜JšœU˜UJ˜—š œœ˜/Q™Pš œœ˜š   œœ œœœ˜.Jšœ:˜@J˜—Jšœ œ ˜Qšœœ"˜-Qšœœ"˜-Qšœœ7˜AQšœœ7˜B˜9Jšœ˜J˜—šœœœ ˜Jšœœ3˜9Jšœ˜Jšœ™Jšœ˜— šœœœ˜.Jšœl˜l šœœœ˜šœ˜šœ˜Qšœ+˜+Qšœ+˜+Qšœ+˜+Q˜—Jšœ,˜0—Qšœ˜—Qšœm˜mQšœ˜—Q˜—J˜0Q˜Q™—š œœœœ˜AJ™Rš œœ˜Jšœ œ ˜Qšœœ"˜-Qšœœ"˜-Qšœœ"˜,Qšœœ"˜-Qšœœ˜4Qšœœ˜4Qšœœ˜4˜9J˜%J˜— šœ˜%šœœœœ ˜(J˜IJ˜IJ˜IJšœ˜—šœ ˜ Jšœœ0˜9šœœœ ˜J˜BJšœ˜—J˜—Jšœœ3˜D— šœœœ˜.J˜r šœœœ˜šœœ˜&˜Q˜)Q˜)Q˜)Q˜—J˜4Jšœœ3˜D—Qšœ˜—Q˜sQšœ˜—Q˜—J˜0J˜J˜—š œœœœ˜BJ™Lš œœ˜Jšœ œ ˜Qšœœ"˜-Qšœœ"˜-Qšœœ"˜,Qšœœ"˜-˜9J˜%J˜— šœœœ˜.J˜r šœœœ˜šœœ˜&˜Qšœœ˜ Qšœœ˜!Qšœœ˜!Qšœœ˜!Qšœœœ˜,Qšœœœ˜,Qšœœœœ˜>Qš œœœœœ˜*Q˜+Q˜7Q˜6Q˜6Q˜Q˜Q˜Q˜—Jšœœ3˜D—Qšœ˜—Q˜sQšœ˜—Q˜—J˜0J˜J˜— š œ œ˜<š   œœ œœœ˜.Jšœ:˜@J˜— š œœœ œ‘˜3Jšœœ˜J˜+Jšœ˜—š œœœ œ ‘˜;˜J˜Jšœœ ˜%Jšœœ˜J˜—Jšœ˜—Q˜Q˜Q™—š œ œ˜9Jš‘*™*šœ œœœ=˜]J™ —Jšœœ˜Jšœ œ˜Jšœœ ‘˜#Qšœœ˜Qšœ8œ˜@Qšœœ˜.Q˜"Q˜+ š œœœœ‘%˜AJšœ œ;˜JJ˜š œœœœ‘˜7Jšœ œ˜ J˜/J˜+J˜+J˜(J˜&J˜Jšœ˜—Jšœ˜—J˜iJ˜j ˜J™———šŸ™š  œ œ%˜=Jšœ>‘˜QJšœ ˜—š œœœ.œ ˜WJ™5š œœ˜Jšœ œ%˜1Jšœ œ%˜1Jšœ œ%˜1Jšœ œ%˜1Jšœœ‘˜SJšœ œ˜Jšœ œ˜Q˜0Q˜ZQ˜\ š œœœœ ‘˜:Qš œœœ!œ œ˜QQšœ˜—Jšœ$‘"˜FJš œœœœœ‘˜Qš œœœœ‘$˜JJ˜tJ˜J˜ š œœœœ‘˜E šœœœ!˜0Qšœ=‘˜OQšœ˜—Jšœ*‘˜?šœ œ˜Jšœ1œœœ˜NJ˜—J˜Jšœ˜— šœ œ‘˜7 šœœœœ˜ šœœœ!˜0Q˜JQšœ˜—Q˜Qšœ˜—J˜x š œœœœ ‘˜:Qš œœœ!œ œ˜QQšœ˜—Qšœ‘'˜FQšœœœœ˜1Q˜—Jšœ‘6˜RJšœ˜—J˜,J˜-šœœœ‘(˜K˜J˜ Jšœ3œ˜;J˜——J˜—Jšœœœ.˜OJšœœœ.˜OJšœœœ1œ˜VJšœ4‘˜RJ˜——Jš˜—…—>μQ!