ImagerColorMapImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, July 30, 1985 10:30:03 am PDT
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 POINTERNIL,
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 BOOLALL[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: NATNAT.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 BOOLALL[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: NATNAT.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 BOOLALL[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 BOOLALL[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];
};
};
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: BOOLTRUE] = {
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];
};
LoadEntries: PUBLIC PROC [vt: Virtual, mapEntries: LIST OF MapEntry, gamma: REAL ← 2.2, b: ChannelValue ← 0, shared: BOOLTRUE] ~ {
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.