DIRECTORY Basics USING [BITSHIFT], ImagerColorMap USING [FullMapProc, Gun, MapEntry, MapProc, NotifierRep, State, StateRep], Process USING [Detach], Real USING [Round, RoundC], RealFns USING [Power], Terminal USING [ChannelValue, ColorMode, ColorValue, FrameBuffer, GetBlueMap, GetColor, GetColorBitmapState, GetColorFrameBufferA, GetColorFrameBufferB, GetColorMode, GetGreenMap, GetRedMap, SetBlueMap, SetColor, SetGreenMap, SetRedMap, Virtual]; ImagerColorMapImpl: CEDAR MONITOR IMPORTS Basics, Process, Terminal, Real, RealFns EXPORTS ImagerColorMap ~ BEGIN State: TYPE ~ ImagerColorMap.State; StateRep: TYPE ~ ImagerColorMap.StateRep; NotifierRep: TYPE ~ ImagerColorMap.NotifierRep; MapEntry: TYPE ~ ImagerColorMap.MapEntry; MapProc: TYPE ~ ImagerColorMap.MapProc; FullMapProc: TYPE ~ ImagerColorMap.FullMapProc; Gun: TYPE ~ ImagerColorMap.Gun; ChannelValue: TYPE ~ Terminal.ChannelValue; ColorValue: TYPE ~ Terminal.ColorValue; Virtual: TYPE ~ Terminal.Virtual; TerminalData: TYPE ~ REF TerminalDataRep; TerminalDataRep: TYPE ~ RECORD [ vt: Virtual, mode: Terminal.ColorMode, frameID: ARRAY {A, B} OF FrameID, state: State, shared: PACKED ARRAY [0..1024) OF BOOL, gamma: REAL ]; FrameID: TYPE ~ RECORD [ base: LONG POINTER _ NIL, wordsPerLine: NAT _ 0, bitsPerPixel: NAT _ 0, width: NAT _ 0, height: NAT _ 0 ]; IDFromFrame: PROC [frame: Terminal.FrameBuffer] RETURNS [id: FrameID _ []] ~ { IF frame = NIL THEN NULL ELSE id _ [frame.base, frame.wordsPerLine, frame.bitsPerPixel, frame.width, frame.height]; }; terminalDataList: LIST OF TerminalData _ NIL; FindData: INTERNAL PROC [vt: Virtual] RETURNS [TerminalData] ~ { FOR p: LIST OF TerminalData _ terminalDataList, p.rest UNTIL p = NIL DO IF p.first = NIL THEN NULL ELSE IF Terminal.GetColorBitmapState[vt] = none THEN p.first _ NIL ELSE IF Terminal.GetColorMode[vt] # p.first.mode THEN p.first _ NIL ELSE { frame: Terminal.FrameBuffer _ Terminal.GetColorFrameBufferA[vt]; IF IDFromFrame[frame] # p.first.frameID[A] THEN p.first _ NIL ELSE IF p.first.mode.bitsPerPixelChannelB#0 OR p.first.mode.full THEN { frame _ Terminal.GetColorFrameBufferB[vt]; IF IDFromFrame[frame] # p.first.frameID[B] THEN p.first _ NIL }; }; ENDLOOP; WHILE terminalDataList#NIL AND terminalDataList.first = NIL DO terminalDataList _ terminalDataList.rest; ENDLOOP; FOR p: LIST OF TerminalData _ terminalDataList, p.rest UNTIL p = NIL DO WHILE p.rest#NIL AND p.rest.first = NIL DO p.rest _ p.rest.rest; ENDLOOP; ENDLOOP; FOR p: LIST OF TerminalData _ terminalDataList, p.rest UNTIL p = NIL DO IF p.first # NIL AND vt = p.first.vt THEN RETURN [p.first]; ENDLOOP; IF Terminal.GetColorBitmapState[vt] # none THEN { frameA: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferA[vt]; frameB: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferB[vt]; state: State ~ NEW[StateRep _ [next: NIL, first: 0, count: 0]]; t: TerminalData ~ NEW[TerminalDataRep _ [ vt: vt, mode: Terminal.GetColorMode[vt], frameID: [IDFromFrame[frameA], IDFromFrame[frameB]], state: state, shared: ALL[FALSE], gamma: 1.0 ]]; terminalDataList _ CONS[t, terminalDataList]; RETURN [t]; }; RETURN [NIL]; }; SetGamma: PUBLIC ENTRY PROC [vt: Virtual, gamma: REAL] ~ { data: TerminalData ~ FindData[vt]; IF data#NIL THEN data.gamma _ gamma; }; GetGamma: PUBLIC ENTRY PROC [vt: Virtual] RETURNS [gamma: REAL _ 1.0] ~ { data: TerminalData ~ FindData[vt]; IF data#NIL THEN gamma _ data.gamma; }; ApplyGamma: PUBLIC PROC [v: REAL, gamma: REAL] RETURNS [ColorValue] ~ { g: REAL ~ MIN[MAX[gamma, 0.01], 100.0]; uncorrected: REAL ~ MIN[MAX[v, 0.0], 1.0]; corrected: REAL ~ RealFns.Power[uncorrected, 1.0/g]; RETURN [Real.Round[MIN[MAX[corrected, 0.0], 1.0]*ColorValue.LAST]]; }; ApplyGammaInverse: PUBLIC PROC [colorValue: ColorValue, gamma: REAL] RETURNS [REAL] ~ { g: REAL ~ MIN[MAX[gamma, 0.01], 100.0]; corrected: REAL ~ REAL[colorValue]/REAL[ColorValue.LAST]; uncorrected: REAL ~ RealFns.Power[corrected, g]; RETURN [uncorrected]; }; IndexFromChannels: PROC [a, b: ChannelValue, mode: Terminal.ColorMode] RETURNS [CARDINAL] ~ { IF mode.full THEN RETURN [a] ELSE RETURN [Basics.BITSHIFT[a, mode.bitsPerPixelChannelB] + b]; }; ChannelValues: TYPE ~ RECORD[a, b: ChannelValue]; ChannelsFromIndex: PROC [index: CARDINAL, mode: Terminal.ColorMode] RETURNS [ChannelValues] ~ { IF mode.full THEN RETURN [[index, 0]] ELSE { a: ChannelValue _ Basics.BITSHIFT[index, -(mode.bitsPerPixelChannelB)]; b: ChannelValue _ index-Basics.BITSHIFT[a, mode.bitsPerPixelChannelB]; RETURN [[a, b]]; }; }; Change: PUBLIC ENTRY PROC [vt: Virtual, action: PROC[set: MapProc]] ~ { ENABLE UNWIND => NULL; data: TerminalData ~ FindData[vt]; changed: PACKED ARRAY [0..1024) OF BOOL _ ALL[FALSE]; maxChanged: NAT _ 0; minChanged: NAT _ 1024; Set: PROC [a, b: ChannelValue, red, green, blue: ColorValue, shared: BOOL] ~ { index: CARDINAL ~ IndexFromChannels[a, b, data.mode]; Terminal.SetColor[vt: vt, aChannelValue: a, bChannelValue: b, red: red, green: green, blue: blue]; IF index < 1024 THEN { data.shared[index] _ shared; changed[index] _ TRUE; }; IF index+1 > maxChanged THEN maxChanged _ index+1; IF index < minChanged THEN minChanged _ index; }; IF data # NIL AND NOT data.mode.full THEN { c: NAT _ NAT.LAST; action[Set]; c _ minChanged; WHILE c < maxChanged DO d: NAT _ c; WHILE d < maxChanged AND changed[d] DO d _ d + 1 ENDLOOP; data.state.first _ c; data.state.count _ d-c; data.state.next _ NEW[StateRep _ [next: NIL, first: 0, count: 0]]; data.state _ data.state.next; c _ d; WHILE c < maxChanged AND NOT changed[c] DO c _ c + 1 ENDLOOP; ENDLOOP; }; TRUSTED {Process.Detach[FORK NotifyChanges]}; }; ChangeFull: PUBLIC ENTRY PROC [vt: Virtual, action: PROC[set: FullMapProc]] ~ { ENABLE UNWIND => NULL; data: TerminalData ~ FindData[vt]; changed: PACKED ARRAY ChannelValue OF BOOL _ ALL[FALSE]; maxChanged: NAT _ 0; minChanged: NAT _ 1024; Set: PROC [gun: Gun, in: ChannelValue, out: ColorValue] ~ { index: CARDINAL ~ in; SELECT gun FROM red => {Terminal.SetRedMap[vt, in, out]}; green => {Terminal.SetGreenMap[vt, in, out]}; blue => {Terminal.SetBlueMap[vt, in, out]}; ENDCASE => NULL; changed[in] _ TRUE; IF in+1 > maxChanged THEN maxChanged _ in+1; IF in < minChanged THEN minChanged _ in; }; IF data # NIL AND data.mode.full THEN { c: NAT _ NAT.LAST; action[Set]; c _ minChanged; WHILE c < maxChanged DO d: NAT _ c; WHILE d < maxChanged AND changed[d] DO d _ d + 1 ENDLOOP; data.state.first _ c; data.state.count _ d-c; data.state.next _ NEW[StateRep _ [next: NIL, first: 0, count: 0]]; data.state _ data.state.next; c _ d; WHILE c < maxChanged AND NOT changed[c] DO c _ c + 1 ENDLOOP; ENDLOOP; }; TRUSTED {Process.Detach[FORK NotifyChanges]}; }; Shared: PUBLIC ENTRY PROC [vt: Virtual, a, b: ChannelValue] RETURNS [BOOL] ~ { data: TerminalData ~ FindData[vt]; IF data = NIL THEN RETURN [FALSE] ELSE { index: CARDINAL ~ IndexFromChannels[a, b, data.mode]; IF index < 1024 THEN RETURN [data.shared[index]] ELSE RETURN [FALSE]; }; }; StateHistory: TYPE ~ RECORD [ state: State _ NIL, count: INT _ 0, data: TerminalData _ NIL ]; GetStateHistory: INTERNAL PROC [vt: Virtual, since: State] RETURNS [s: StateHistory _ []] ~ { s.data _ FindData[vt]; IF s.data # NIL THEN { mode: Terminal.ColorMode ~ Terminal.GetColorMode[vt]; nMap: [0..1024] ~ IF mode.full THEN 256 ELSE Basics.BITSHIFT[1, mode.bitsPerPixelChannelA+mode.bitsPerPixelChannelB]; p: State _ s.state _ IF since = NIL THEN NEW[StateRep _ [next: s.data.state, first: 0, count: nMap]] ELSE since; UNTIL p=NIL OR p = s.data.state DO s.count _ s.count + 1; p _ p.next; ENDLOOP; IF p = NIL THEN RETURN [GetStateHistory[vt, NIL]]; }; }; GetChanges: PUBLIC ENTRY PROC [vt: Virtual, since: State, change: MapProc] RETURNS [new: State] ~ { ENABLE UNWIND => NULL; stateHistory: StateHistory _ GetStateHistory[vt, since]; changed: PACKED ARRAY [0..1024) OF BOOL _ ALL[FALSE]; WHILE stateHistory.count # 0 DO mode: Terminal.ColorMode ~ stateHistory.data.mode; FOR index: CARDINAL IN [stateHistory.state.first..stateHistory.state.count) DO chan: ChannelValues ~ ChannelsFromIndex[index, mode]; red, green, blue: ColorValue; [red, green, blue] _ Terminal.GetColor[vt, chan.a, chan.b]; IF NOT changed[index] THEN { change[chan.a, chan.b, red, green, blue, stateHistory.data.shared[index]]; changed[index] _ TRUE; }; ENDLOOP; stateHistory.state _ stateHistory.state.next; stateHistory.count _ stateHistory.count - 1; ENDLOOP; RETURN [stateHistory.state]; }; GetFullChanges: PUBLIC ENTRY PROC [vt: Virtual, since: State, change: FullMapProc] RETURNS [new: State] ~ { ENABLE UNWIND => NULL; stateHistory: StateHistory _ GetStateHistory[vt, since]; changed: PACKED ARRAY [0..256) OF BOOL _ ALL[FALSE]; WHILE stateHistory.count # 0 DO FOR index: CARDINAL IN [stateHistory.state.first..stateHistory.state.count) DO IF NOT changed[index] THEN { change[gun: red, in: index, out: Terminal.GetRedMap[vt, index]]; change[gun: green, in: index, out: Terminal.GetGreenMap[vt, index]]; change[gun: blue, in: index, out: Terminal.GetBlueMap[vt, index]]; changed[index] _ TRUE; }; ENDLOOP; stateHistory.state _ stateHistory.state.next; stateHistory.count _ stateHistory.count - 1; ENDLOOP; RETURN [stateHistory.state]; }; SetFullColorMap: PROC [vt: Virtual, gamma: REAL _ 2.2] ~ { mode: Terminal.ColorMode ~ Terminal.GetColorMode[vt]; stdGrayFullAction: PROC[set: FullMapProc] ~ { r: REAL _ 1.0/255.0; FOR i: ChannelValue IN ChannelValue DO out: ColorValue ~ ApplyGamma[i*r, gamma]; set[gun: red, in: i, out: out]; set[gun: green, in: i, out: out]; set[gun: blue, in: i, out: out]; ENDLOOP; }; ChangeFull[vt, stdGrayFullAction]; }; standard: ARRAY [0..3] OF LIST OF MapEntry _ InitColorMapEntries[]; StandardColorMapEntries: PUBLIC PROC [bitsPerPixel: [1..8]] RETURNS [LIST OF MapEntry] ~ { lgBitsPerPixel: NAT ~ SELECT bitsPerPixel FROM 1=>0, 2=>1, 4=>2, 8=>3, ENDCASE => ERROR; RETURN [standard[lgBitsPerPixel]] }; InitColorMapEntries: PROC RETURNS [s: ARRAY [0..3] OF LIST OF MapEntry] ~ { FOR lgBitsPerPixel: NAT IN [0..3] DO bitsPerPixel: NAT ~ Basics.BITSHIFT[1, lgBitsPerPixel]; maxPixel: CARDINAL ~ Basics.BITSHIFT[1, bitsPerPixel]-1; list: LIST OF MapEntry _ NIL; SetColor: PROC [a: ChannelValue, r, g, b: REAL] ~ { red: ColorValue ~ Real.RoundC[r*ChannelValue.LAST]; green: ColorValue ~ Real.RoundC[g*ChannelValue.LAST]; blue: ColorValue ~ Real.RoundC[b*ChannelValue.LAST]; list _ CONS[[a, red, green, blue], list]; }; SetComplements: PROC [a: ChannelValue, r, g, b: REAL] ~ { SetColor[a, r, g, b]; SetColor[maxPixel-a, 1.0-r, 1.0-g, 1.0-b]; }; Do: PROC ~ { k: NAT _ 0; IF lgBitsPerPixel = 0 THEN { SetComplements[0, 1, 1, 1]; RETURN; }; IF lgBitsPerPixel = 1 THEN { SetColor[0, 0, 0, 0]; SetColor[1, 0, 0, 1]; SetColor[2, 1, 0, 0]; SetColor[3, 0, 1, 0]; RETURN; }; IF lgBitsPerPixel = 2 THEN { SetComplements[0, 0, 0, 0]; SetComplements[1, 0, 0, 1]; SetComplements[2, 0, 1, 0]; SetComplements[3, 1, 0, 0]; SetComplements[6, .333, .333, .333]; SetComplements[5, 0, 0, .5]; SetComplements[6, 0, .5, 0]; SetComplements[7, .5, 0, 0]; RETURN; }; FOR b: NAT IN [0..4) DO FOR g: NAT IN [0..6) DO FOR r: NAT IN [0..5) DO a: NAT ~ (r*6+g)*4+b; IF a < 60 THEN SetComplements[a, r/4.0, g/5.0, b/3.0]; ENDLOOP; ENDLOOP; ENDLOOP; }; Do[]; s[lgBitsPerPixel] _ list; ENDLOOP; }; SetStandardColorMap: PUBLIC PROC [vt: Virtual, gamma: REAL _ 2.2] ~ { mode: Terminal.ColorMode ~ Terminal.GetColorMode[vt]; IF mode.full THEN SetFullColorMap[vt, gamma] ELSE { SetStandardGrayMap[vt, gamma]; -- fill unused entries LoadEntries[vt, StandardColorMapEntries[mode.bitsPerPixelChannelA], gamma]; }; }; LoadEntries: PUBLIC PROC [vt: Virtual, mapEntries: LIST OF MapEntry, gamma: REAL _ 2.2, b: ChannelValue _ 0, shared: BOOL _ TRUE] ~ { mode: Terminal.ColorMode ~ Terminal.GetColorMode[vt]; IF mode.full THEN NULL ELSE { mapAction: PROC[set: MapProc] ~ { r: REAL _ 1.0/LAST[ColorValue]; FOR m: LIST OF MapEntry _ mapEntries, m.rest UNTIL m = NIL DO red: ColorValue ~ ApplyGamma[m.first.red*r, gamma]; green: ColorValue ~ ApplyGamma[m.first.green*r, gamma]; blue: ColorValue ~ ApplyGamma[m.first.blue*r, gamma]; set[a: m.first.mapIndex, b: b, red: red, green: green, blue: blue, shared: shared]; ENDLOOP; }; Change[vt, mapAction]; }; SetGamma[vt, gamma]; }; SetStandardGrayMap: PUBLIC PROC [vt: Virtual, gamma: REAL _ 2.2] ~ { mode: Terminal.ColorMode ~ Terminal.GetColorMode[vt]; IF mode.full THEN SetFullColorMap[vt, gamma] ELSE IF mode.bitsPerPixelChannelA=1 THEN { min: Terminal.ColorValue ~ Terminal.ColorValue.FIRST; max: Terminal.ColorValue ~ Terminal.ColorValue.LAST; bwAction: PROC[set: MapProc] ~ { set[a: 0, red: max, green: max, blue: max]; -- white set[a: 1, red: min, green: min, blue: min]; -- black }; Change[vt, bwAction]; } ELSE { size: NAT ~ Basics.BITSHIFT[1, mode.bitsPerPixelChannelA]; bSize: NAT ~ Basics.BITSHIFT[1, mode.bitsPerPixelChannelB]; max: NAT ~ size-1; grayAction: PROC[set: MapProc] ~ { r: REAL _ 1.0/MAX[max, 1]; FOR b: NAT IN[0..bSize) DO FOR i: NAT IN[0..size) DO out: ColorValue ~ ApplyGamma[i*r, gamma]; set[a: i, b: b, red: out, green: out, blue: out]; ENDLOOP; ENDLOOP; }; Change[vt, grayAction]; }; SetGamma[vt, gamma]; }; NotifyChanges: PROC ~ { FOR p: LIST OF NotifierRep _ GetNotifiers[], p.rest UNTIL p = NIL DO p.first.proc[p.first.data ! ABORTED => CONTINUE]; ENDLOOP; }; notifier: LIST OF NotifierRep _ NIL; GetNotifiers: ENTRY PROC RETURNS [LIST OF NotifierRep] ~ { RETURN [notifier]; }; Register: PUBLIC ENTRY PROC [colorMapChanged: NotifierRep] ~ { notifier _ CONS[colorMapChanged, notifier]; }; UnRegister: PUBLIC ENTRY PROC [colorMapChanged: NotifierRep] ~ { p: LIST OF NotifierRep _ notifier; notifier _ NIL; WHILE p # NIL DO IF p.first = colorMapChanged THEN p _ p.rest ELSE {t: LIST OF NotifierRep _ p.rest; p.rest _ notifier; notifier _ p; p _ t}; ENDLOOP; }; END. ImagerColorMapImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Michael Plass, July 30, 1985 10:30:03 am PDT SetStandardColorMap: PUBLIC PROC [vt: Virtual, gamma: REAL _ 2.2] ~ { mode: Terminal.ColorMode ~ Terminal.GetColorMode[vt]; IF mode.full THEN SetFullColorMap[vt, gamma] ELSE IF mode.bitsPerPixelChannelA=1 THEN SetStandardGrayMap[vt, gamma] ELSE { blueCV: NAT ~ 2; redCV: NAT ~ 4; magentaCV: NAT ~ 6; greenCV: NAT ~ 8; cyanCV: NAT ~ 10; yellowCV: NAT ~ 12; size: NAT ~ Basics.BITSHIFT[1, mode.bitsPerPixelChannelA]; bSize: NAT ~ Basics.BITSHIFT[1, mode.bitsPerPixelChannelB]; max: NAT ~ size-1; colorAction: PROC[set: MapProc] ~ { c: NAT _ 0; SetColor: PROC [a: ChannelValue, r, g, b: REAL, shared: BOOL _ TRUE] = { red: ColorValue ~ ApplyGamma[r, gamma]; green: ColorValue ~ ApplyGamma[g, gamma]; blue: ColorValue ~ ApplyGamma[b, gamma]; FOR b: NAT IN [0..bSize) DO set[a: a, b: b, red: red, green: green, blue: blue, shared: shared]; ENDLOOP; }; GrayWedge: PROC ~ { color: REAL _ 0; step: REAL _ 0; nentries: CARDINAL _ 0; bitsPerPixel: CARDINAL _ Terminal.GetColorMode[vt].bitsPerPixelChannelA; SELECT Terminal.GetColorMode[vt].bitsPerPixelChannelA FROM 1 => {step _ 1.0; nentries _ 2}; 2 => {step _ 1.0/7; nentries _ 4}; 4 => {step _ 1.0/15; nentries _ 16}; 8 => {step _ 1.0/255; nentries _ 256}; ENDCASE => RETURN; FOR i: CARDINAL IN [0..nentries) DO SetColor[i, color, color, color]; color _ color+step; ENDLOOP; }; GrayWedge[]; IF Terminal.GetColorMode[vt].bitsPerPixelChannelA < 4 THEN RETURN; FOR i: ChannelValue IN [2..256) DO IF i MOD 2 = 0 THEN SetColor[i, 0, 0, 0, FALSE]; ENDLOOP; SetColor[(c _ c + 2), 0, 0, 1]; --blue SetColor[(c _ c + 2), 1, 0, 0]; --red SetColor[(c _ c + 2), 1, 0, 1]; --magenta SetColor[(c _ c + 2), 0, 1, 0]; --green SetColor[(c _ c + 2), 0, 1, 1]; --cyan SetColor[(c _ c + 2), 1, 1, 0]; --yellow SetColor[(c _ c + 2), 0.075, 0.5, 0.3]; -- darkBrown SetColor[(c _ c + 2), 0.075, 0.5, 0.5]; -- brown SetColor[(c _ c + 2), 0.075, 0.4, 0.8]; -- tan SetColor[(c _ c + 2), 0.0, 1.0, 0.4]; -- darkRed SetColor[(c _ c + 2), 0.0, 0.5, 1.0]; -- lightRed SetColor[(c _ c + 2), 0.075, 1.0, 1.0]; -- orange SetColor[(c _ c + 2), 0.167, 1, 0.5]; -- darkYellow SetColor[(c _ c + 2), 0.167, 0.6, 1.0]; -- lightYellow SetColor[(c _ c + 2), 0.333, 1.0, 0.4]; -- darkGreen SetColor[(c _ c + 2), 0.333, 0.5, 1.0]; -- lightGreen SetColor[(c _ c + 2), 0.67, 1.0, 0.5]; -- darkBlue SetColor[(c _ c + 2), 0.67, 0.4, 1.0]; -- lightBlue SetColor[(c _ c + 2), 0.776, 1, 0.8]; -- purple SetColor[(c _ c + 2), 0.776, 0.5, 0.8]; -- violet SetColor[(c _ c + 2), 0.833, 0.4, 1.0]; -- pink SetColor[(c _ c + 2), 0.0, 0.0, 0.3]; -- darkGray SetColor[(c _ c + 2), 0.0, 0.0, 0.77]; -- lightGray }; Change[vt, colorAction]; }; SetGamma[vt, gamma]; }; Κ@˜šœ™Icodešœ Οmœ1™Kšœ)˜)Kšžœ˜—š žœžœžœ)žœžœž˜Gš žœžœžœžœž˜*Kšœ˜Kšžœ˜—Kšžœ˜—š žœžœžœ)žœžœž˜GKš žœ žœžœžœžœ ˜;Kšžœ˜—šžœ)žœ˜1KšœA˜AKšœA˜AKšœžœžœ˜?šœžœ˜)Kšœ˜Kšœ!˜!Kšœ4˜4Kšœ ˜ Kšœžœžœ˜K˜ Kšœ˜—Kšœžœ˜-Kšžœ˜ Kšœ˜—Kšžœžœ˜ Kšœ˜K˜—š  œžœžœžœžœ˜:Kšœ"˜"Kšžœžœžœ˜$Kšœ˜K˜—š  œžœžœžœžœ žœ ˜IKšœ"˜"Kšžœžœžœ˜$Kšœ˜K˜—š   œžœžœžœ žœžœ˜GKšœžœžœžœ˜'Kšœ žœžœžœ˜*Kšœ žœ%˜4Kšžœ žœžœ"žœ˜CKšœ˜K˜—š  œžœžœ!žœžœžœ˜WKšœžœžœžœ˜'Kš œ žœžœ žœ žœ˜9Kšœ žœ˜0Kšžœ˜Kšœ˜K˜—š œžœ0žœžœ˜]Kšžœ žœžœ˜Kšžœžœ žœ$˜@K˜K˜—Kšœžœžœ˜1š œžœ žœžœ˜_Kšžœ žœžœ ˜%šžœ˜Kšœžœ&˜GKšœžœ˜FKšžœ ˜Kšœ˜—K˜K˜—š  œžœžœžœžœ˜GKšžœžœžœ˜K˜"Kš œ žœžœ žœžœžœžœ˜5Kšœ žœ˜Kšœ žœ˜š œžœ<žœ˜NK•StartOfExpansionο[vt: Terminal.Virtual, aChannelValue: ColorDisplayDefs.ChannelValue _ 0, bChannelValue: ColorDisplayDefs.ChannelValue _ 0, red: ColorDisplayDefs.ColorValue, green: ColorDisplayDefs.ColorValue, blue: ColorDisplayDefs.ColorValue]šœžœ&˜5Kšœb˜bšžœžœ˜Kšœ˜Kšœžœ˜K˜—Kšžœžœ˜2Kšžœžœ˜.Kšœ˜—š žœžœžœžœžœ˜+Jšœžœžœžœ˜Kšœ ˜ Kšœ˜šžœž˜Kšœžœ˜ Kšžœžœ žœ žœ˜9Kšœ˜Kšœ˜Kšœžœžœ˜BKšœ˜Kšœ˜Kš žœžœžœ žœ žœ˜=Kšžœ˜—Kšœ˜—Kšžœžœ˜-Kšœ˜K˜—š   œžœžœžœžœ˜OKšžœžœžœ˜K˜"Kš œ žœžœžœžœžœžœ˜8Kšœ žœ˜Kšœ žœ˜š œžœ2˜;K–ο[vt: Terminal.Virtual, aChannelValue: ColorDisplayDefs.ChannelValue _ 0, bChannelValue: ColorDisplayDefs.ChannelValue _ 0, red: ColorDisplayDefs.ColorValue, green: ColorDisplayDefs.ColorValue, blue: ColorDisplayDefs.ColorValue]šœžœ˜šžœž˜Kšœ)˜)Kšœ-˜-Kšœ+˜+Kšžœžœ˜—Kšœžœ˜Kšžœžœ˜,Kšžœžœ˜(Kšœ˜—šžœžœžœžœ˜'Jšœžœžœžœ˜Kšœ ˜ Kšœ˜šžœž˜Kšœžœ˜ Kšžœžœ žœ žœ˜9Kšœ˜Kšœ˜Kšœžœžœ˜BKšœ˜Kšœ˜Kš žœžœžœ žœ žœ˜=Kšžœ˜—Kšœ˜—Kšžœžœ˜-Kšœ˜K˜—š  œžœžœžœ#žœžœ˜NKšœ"˜"Kš žœžœžœžœžœ˜!šžœ˜Kšœžœ&˜5Kšžœžœžœ˜0Kšžœžœžœ˜Kšœ˜—Kšœ˜K˜—šœžœžœ˜Kšœžœ˜Kšœžœ˜Kšœž˜Kšœ˜K˜—š œžœžœžœ˜]Kšœ˜šžœ žœžœ˜Kšœ5˜5Kš œžœ žœžœžœ9˜uKš œžœ žœžœžœ9žœ˜pšžœžœžœž˜"Kšœ˜K˜ Kšžœ˜ —Kš žœžœžœžœžœ˜2Kšœ˜—Kšœ˜K˜—š   œžœžœžœ.žœ˜cKšžœžœžœ˜Kšœ8˜8Kš œ žœžœ žœžœžœžœ˜5šžœž˜Kšœ2˜2šžœžœžœ6ž˜NKšœ5˜5Kšœ˜Kšœ;˜;šžœžœžœ˜KšœJ˜JKšœžœ˜Kšœ˜—Kšžœ˜—Kšœ-˜-Kšœ,˜,Kšžœ˜—Kšžœ˜Kšœ˜K˜—š  œžœžœžœ2žœ˜kKšžœžœžœ˜Kšœ8˜8Kš œ žœžœ žœžœžœžœ˜4šžœž˜šžœžœžœ6ž˜Nšžœžœžœ˜Kšœ@˜@KšœD˜DKšœB˜BKšœžœ˜Kšœ˜—Kšžœ˜—Kšœ-˜-Kšœ,˜,Kšžœ˜—Kšžœ˜Kšœ˜K˜—š œžœžœ ˜:K˜5šœžœ˜-Kšœžœ ˜šžœžœž˜&Kšœ)˜)Kšœ˜Kšœ!˜!Kšœ ˜ Kšžœ˜—Kšœ˜—Kšœ"˜"Kšœ˜K˜—š œ žœžœžœžœ"˜CK˜—š  œžœžœžœžœžœ˜ZKš œžœžœžœžœžœ˜XKšžœ˜!Kšœ˜K˜—š œžœžœžœžœžœžœ˜Kšžœžœžœž˜$Kšœžœ žœ˜7Kšœ žœ žœ˜8Kšœžœžœ žœ˜š œžœžœ˜3Jšœ-žœ˜3Jšœ/žœ˜5Jšœ.žœ˜4Kšœžœ˜)J˜—š œžœžœ˜9Kšœ˜Kšœ*˜*Kšœ˜—š œžœ˜ Kšœžœ˜ šžœžœ˜Kšœ˜Kšžœ˜Kšœ˜—šžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšžœ˜Kšœ˜—šžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ$˜$Kšœ˜Kšœ˜Kšœ˜Kšžœ˜Kšœ˜—šžœžœžœž˜šžœžœžœž˜šžœžœžœž˜Kšœžœ˜Kšžœžœ(˜6Kšžœ˜—Kšžœ˜—Kšžœ˜—Kšœ˜—K˜Kšœ˜Kšžœ˜—Kšœ˜K˜—š œžœžœžœ ˜EK˜5Kšžœ žœ˜,šžœ˜KšœΟc˜5KšœK˜KKšœ˜—Kšœ˜K˜—š œžœžœžœ ™EK™5Kšžœ žœ™,Kšžœžœžœ™Fšžœ™Jšœžœ™Jšœžœ™Jšœ žœ™Jšœ žœ™Jšœžœ™Jšœ žœ™Kšœžœ žœ™:Kšœžœ žœ™;Kšœžœ ™šœ žœ™#Kšœžœ™ š  œžœžœ žœžœ™HJšœ'™'Jšœ)™)Jšœ(™(šžœžœžœ ž™KšœD™DKšžœ™—J™—š  œžœ™Jšœžœ™Jšœžœ™Jšœ žœ™Jšœžœ2™Hšžœ0ž™:J™ J™"J™$J™&Jšžœžœ™—šžœžœžœž™#Jšœ!™!J™Jšžœ™—J™—Jšœ ™ Jšžœ4žœžœ™Bšžœžœ ž™"Jšžœžœžœžœ™0Jšžœ™—Jšœ!‘™'Jšœ!‘™&Jšœ!‘ ™*Jšœ!‘™(Jšœ!‘™'Jšœ!‘™)Kšœ)‘ ™5Kšœ)‘™1Kšœ)‘™/Kšœ'‘ ™1Kšœ'‘ ™2Kšœ)‘ ™2Kšœ'‘ ™4Kšœ)‘™7Kšœ)‘ ™5Kšœ)‘ ™6Kšœ(‘ ™3Kšœ(‘ ™4Kšœ'‘ ™0Kšœ)‘ ™2Kšœ)‘™0Kšœ'‘ ™2Kšœ(‘ ™4Kšœ™—Kšœ™K™—Kšœ™Kšœ™K™—š  œžœžœžœžœžœ%žœžœ˜…K˜5Kšžœ žœž˜šžœ˜šœ žœ˜!Kšœžœžœ ˜š žœžœžœžœžœž˜=Kšœ3˜3Kšœ7˜7Kšœ5˜5KšœS˜SKšžœ˜—Kšœ˜—Kšœ˜K˜—Kšœ˜Kšœ˜J˜—š œžœžœžœ ˜DK˜5Kšžœ žœ˜,šžœžœžœ˜*Kšœ/žœ˜5Kšœ/žœ˜4šœ žœ˜ Kšœ,‘˜4Kšœ,‘˜4Kšœ˜—Kšœ˜K˜—šžœ˜Kšœžœ žœ˜:Kšœžœ žœ˜;Kšœžœ ˜šœ žœ˜"Kšœžœžœ ˜šžœžœžœ ž˜šžœžœžœ ž˜Kšœ)˜)Kšœ1˜1Kšžœ˜—Kšžœ˜—Kšœ˜—Kšœ˜K˜—Kšœ˜Kšœ˜J˜—š  œžœ˜š žœžœžœ&žœžœž˜DKšœžœžœ˜1Kšžœ˜—Kšœ˜J˜—šœ žœžœžœ˜$J˜—š   œžœžœžœžœžœ˜:Kšžœ ˜Kšœ˜J™—š œžœžœžœ#˜>Jšœ žœ˜+Kšœ˜J™—š  œžœžœžœ#˜@Kšœžœžœ˜"Kšœ žœ˜šžœžœž˜Kšžœžœ ˜,Kšžœžœžœ?˜OKšžœ˜—Kšœ˜—K˜—K˜Kšžœ˜J™—…—5²Xς