ColorMapImpl.mesa
last edited by Maureen Stone, August 20, 1984 11:27:00 am PDT
last edited by Doug Wyatt, December 20, 1983 4:10 pm
Color definitions and colormap routines
DIRECTORY
CGColor USING [HSVToRGB],
ColorMap USING [GrayTable, GrayRec],
ColorPackagePrivate USING [ColorMapProc, terminal],
Real USING [RoundI],
RealFns USING [Power],
Terminal USING [Color, GetColorBitmapState, GetColorMode, SetBlueMap, SetColor, SetGreenMap, SetRedMap];
ColorMapImpl: CEDAR PROGRAM
IMPORTS CGColor, ColorPackagePrivate, Real, RealFns, Terminal
EXPORTS ColorMap, ColorPackagePrivate = {
OPEN ColorPackagePrivate;
Color: TYPE = Terminal.Color;
Triple: TYPE = RECORD[r,g,b: Color];
ColorTable: ARRAY [0..255] OF Triple ← ALL[[0,0,0]];
blue: Color=2; red: Color=4; magenta: Color=6; green: Color=8; cyan: Color=10; yellow: Color=12;
NoMap: PROC RETURNS[BOOLEAN] = {
RETURN[terminal.GetColorBitmapState=none OR terminal.GetColorMode.full];
};
StandardMap: PUBLIC PROC = {
IF NoMap[] THEN RETURN;
GrayWedge[]; -- start with a gray wedge.
IF terminal.GetColorMode.bitsPerPixelChannelA < 4 THEN RETURN;
throw out half the grays to make room for colors
FOR i: Color IN [2..256) DO
IF i MOD 2 = 0 THEN SetColor[i,0,0,0];
ENDLOOP;
put in the standard colors,
SetRGBColor[blue,0,0,1]; --blue
SetRGBColor[red,1,0,0]; --red
SetRGBColor[magenta,1,0,1]; --magenta
SetRGBColor[green,0,1,0]; --green
SetRGBColor[cyan,0,1,1]; --cyan
SetRGBColor[yellow,1,1,0]; --yellow
SetUpGrayTable[];
};
sets up the standard color map (definition changing daily!)
functional colors. These values change with each display mode
changing the color map with SetRGBColor or SetHSVColor may invalidate these values
StandardMap[] will fix them
foreground: Color ← 255;
background: Color ← 0;
standardGrayTable: ColorMap.GrayTable ← NEW[ColorMap.GrayRec[256]];
grayTable: ColorMap.GrayTable ← standardGrayTable;
sets up a gamma corrected gray wedge as the color map. Good for
showing AIS files
GrayMap: PUBLIC PROC = {
GrayWedge[];
SetUpGrayTable[];
};
GrayWedge: PROC = {
color: REAL ← 0;
step: REAL ← 0;
nentries: CARDINAL ← 0;
bitsPerPixel: CARDINAL ← terminal.GetColorMode.bitsPerPixelChannelA;
IF NoMap[] THEN RETURN;
SELECT bitsPerPixel 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 => ERROR;
FOR i: CARDINAL IN [0..nentries) DO
SetRGBColor[i,color,color,color];
color ← color+step;
ENDLOOP;
};
Searches the colormap for all the gray values and sets up a GrayTable
Do this after you are finished setting your colormap if you want the color device
to do its best for gray images.
SetUpGrayTable: PUBLIC PROC = {
lastValid: INTEGER ← -1;
firstValid: INTEGER ← 1000;
nextValid,prevValid: INTEGER ← 0;
maxCIndex: INTEGER ← MaxIndex[];
need a table that can have "unassigned" entries
table: ARRAY[0..255] OF INTEGERALL[-1];
IF NoMap[] THEN RETURN;
search the colortable for grays
FOR i: INTEGER IN [0..maxCIndex] DO
IF ColorTable[i].r=ColorTable[i].g AND ColorTable[i].g=ColorTable[i].b THEN {
intensity: INTEGER ← ColorTable[i].r;
want to use the first occurance of a color as the correct one (matches GetIndex)
IF table[intensity]=-1 THEN table[intensity] ← i;
IF intensity>lastValid THEN lastValid ← intensity;
IF intensity<firstValid THEN firstValid ← intensity;
};
grayTable[i] ← 0; --zero this out while we're looping
ENDLOOP;
IF firstValid>maxCIndex THEN RETURN; --no Grays
FOR i: INTEGER IN [0..firstValid] DO grayTable[i] ← table[firstValid] ENDLOOP;
nextValid ← prevValid ← firstValid;
UNTIL nextValid>=lastValid DO
mid: REAL;
k: INTEGER;
nextValid ← nextValid+1;
UNTIL table[nextValid] > -1 DO nextValid ← nextValid+1 ENDLOOP;
interpolate between prevValid and nextValid
mid ← (nextValid+prevValid)/2.0;
k ← prevValid;
UNTIL k>mid DO grayTable[k]← table[prevValid]; k ← k+1; ENDLOOP;
UNTIL k>nextValid DO grayTable[k]← table[nextValid]; k ← k+1; ENDLOOP;
prevValid ← nextValid;
ENDLOOP;
FOR i: INTEGER IN [lastValid..255] DO grayTable[i] ← table[lastValid] ENDLOOP;
};
gamma: REAL ← 1.0/2.2;
SetGamma: PUBLIC PROC[g: REAL] = {
gamma ← 1.0/g;
IF terminal.GetColorMode.full THEN FullComp[]};
GetGamma: PUBLIC PROC RETURNS[REAL] = {RETURN[gamma]};
gamma should be in the range [1..3]
Comp: PROCEDURE [intensity: REAL] RETURNS [REAL] = {
IF intensity=0 THEN RETURN[0];
IF intensity=1 THEN RETURN[1];
intensity ← RealFns.Power[intensity, gamma];
RETURN[MAX[MIN[1,intensity],0]];
};
FullComp: PROC = {
step: REAL ← 1.0/255.0;
v: REAL ← 0;
FOR i: CARDINAL IN [0..256) DO
terminal.SetRedMap[in: i, out: ToByte[Comp[v]]];
terminal.SetGreenMap[in: i, out: ToByte[Comp[v]]];
terminal.SetBlueMap[in: i, out: ToByte[Comp[v]]];
v ← v+step;
ENDLOOP;
};
ColorError: PUBLIC SIGNAL[why: LONG STRING] = CODE;
SetRGBColor: PUBLIC PROCEDURE[index: CARDINAL ← 0, r,g,b: REAL] = {
IF NoMap[] THEN RETURN;
IF index>MaxIndex[] THEN SIGNAL ColorError["index out of range"];
SetColor[index,r,g,b];
};
MaxIndex: PROC RETURNS[max: CARDINAL] = {
max ← (SELECT terminal.GetColorMode.bitsPerPixelChannelA FROM
1 => 1, 2 => 3, 4 => 15, 8 => 255, ENDCASE => 0);
};
SetHSVColor: PUBLIC PROCEDURE[index: CARDINAL ← 0, h,s,v: REAL] = {
r,g,b: REAL;
IF NoMap[] THEN RETURN;
IF index>MaxIndex[] THEN SIGNAL ColorError["index out of range"];
[r,g,b] ← CGColor.HSVToRGB[h,s,v];
SetColor[index,r,g,b];
};
ToByte: PROC [v: REAL] RETURNS[Color] = {
IF v NOT IN [0..1] THEN SIGNAL ColorError["value out of range"];
RETURN[MAX[0,MIN[255,Real.RoundI[v*255]]]];
};
FromByte: PROC [b: Color] RETURNS[REAL] = {
RETURN[MAX[0,MIN[1,b/255.0]]];
};
SetColor: PROC[index: Color, r,g,b: REAL] = {
ir,ig,ib: Color;
ir ← ToByte[Comp[r]];
ig ← ToByte[Comp[g]];
ib ← ToByte[Comp[b]];
terminal.SetColor[aChannelValue: index, red: ir, green: ig, blue: ib];
ColorTable[index] ← [r: ToByte[r],g: ToByte[g],b: ToByte[b]];
};
returns a table that maps intensities [0..255] into pixel values for the current mode
GetGrayTable: PUBLIC PROC RETURNS[ColorMap.GrayTable] = {RETURN[grayTable]};
SetGrayTable: PUBLIC PROC[table: ColorMap.GrayTable] = {
IF table=NIL THEN grayTable standardGrayTable
ELSE {
IF table.length#256 THEN ERROR; --hey, this is a hack feature anyway
grayTable table;
};
};
finds color in the colormap or returns a compromise (currently a gray value).
This is expensive, so the device should keep a cache.
MyGetIndex: PROC[r,g,b: Color] RETURNS[Color] = {
maxIndex: Color ← MaxIndex[]; --gets the maximum index for the current mod
intensity: REAL;
gray: Color;
IF NoMap[] THEN RETURN[0];
FOR i: Color IN [0..maxIndex] DO
IF r=ColorTable[i].r AND g=ColorTable[i].g AND b=ColorTable[i].b THEN RETURN[i];
ENDLOOP;
not found, so return the corresponding gray value
intensity ← .11*b+.30*r+.59*g; --NTSC luminance
gray ← Real.RoundI[intensity];
RETURN[grayTable[gray]];
};
GetIndexProc: ColorPackagePrivate.ColorMapProc ← MyGetIndex;
SetNewColorMapProc: PUBLIC PROC[new: ColorPackagePrivate.ColorMapProc] = {
GetIndexProc ← IF new=NIL THEN MyGetIndex ELSE new};
GetIndex: PUBLIC PROC [r,g,b: Color] RETURNS[Color] = {
RETURN[GetIndexProc[r,g,b]];
};
GetColor: PUBLIC PROC[index: Color] RETURNS[r,g,b: Color] = {
IF index>MaxIndex[] THEN ColorError["index out of range"];
[r,g,b] ← ColorTable[index];
RETURN[r,g,b];
};
}.