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 INTEGER ← ALL[-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];
};
}.