<> <> <> <<>> 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]; }; }; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <> <> <> <> <> <