-- ColorMapImpl.mesa -- last edited by Maureen Stone, October 5, 1982 2:23 pm -- last edited by Doug Wyatt, September 1, 1982 3:53 pm -- Color definitions and colormap routines DIRECTORY ColorDisplay USING [Byte, curmode, disconnected, SetBlueMap, SetColor, SetGreenMap, SetRedMap], CGColor USING [HSVToRGB], ColorMap USING [GrayTable, GrayRec], ColorPackagePrivate USING [ColorMapProc], Real USING [RoundI], RealFns USING [Power]; ColorMapImpl: CEDAR PROGRAM IMPORTS ColorDisplay, Real, RealFns, CGColor EXPORTS ColorMap, ColorPackagePrivate = { Byte: TYPE = ColorDisplay.Byte; Triple: TYPE = RECORD[r,g,b: Byte]; ColorTable: ARRAY [0..255] OF Triple _ ALL[[0,0,0]]; blue: Byte=2; red: Byte=4; magenta: Byte=6; green: Byte=8; cyan: Byte=10; yellow: Byte=12; NoMap: PROC RETURNS[BOOLEAN] = {OPEN ColorDisplay; IF curmode=disconnected OR curmode.full THEN RETURN[TRUE] ELSE RETURN[FALSE]}; StandardMap: PUBLIC PROC = { IF NoMap[] THEN RETURN; GrayWedge[]; -- start with a gray wedge. IF ColorDisplay.curmode.bitsPerPixelA < 4 THEN RETURN; --throw out half the grays to make room for colors FOR i: Byte 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: Byte _ 255; background: Byte _ 0; grayTable: ColorMap.GrayTable _ NEW[ColorMap.GrayRec[256]]; --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 _ ColorDisplay.curmode.bitsPerPixelA; 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 intensitymaxCIndex 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 ColorDisplay.curmode.full THEN FullComp[]}; GetGamma: PUBLIC PROC RETURNS[gamma: 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 TRUSTED { ColorDisplay.SetRedMap[in: i, out: ToByte[Comp[v]]]; ColorDisplay.SetGreenMap[in: i, out: ToByte[Comp[v]]]; ColorDisplay.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 ColorDisplay.curmode.bitsPerPixelA 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[Byte] = { 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: Byte] RETURNS[REAL] = { RETURN[MAX[0,MIN[1,b/255.0]]]; }; SetColor: PROC[index: Byte, r,g,b: REAL] = { ir,ig,ib: Byte; ir _ ToByte[Comp[r]]; ig _ ToByte[Comp[g]]; ib _ ToByte[Comp[b]]; TRUSTED {ColorDisplay.SetColor[pixelA: index, r: ir,g: ig,b: 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]}; --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: Byte] RETURNS[Byte] = { maxIndex: Byte _ MaxIndex[]; --gets the maximum index for the current mod intensity: REAL; gray: Byte; IF NoMap[] THEN RETURN[0]; FOR i: Byte 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 _ new}; GetIndex: PUBLIC PROC [r,g,b: Byte] RETURNS[Byte] = { RETURN[GetIndexProc[r,g,b]]; }; GetColor: PUBLIC PROC[index: Byte] RETURNS[r,g,b: Byte] = { IF index>MaxIndex[] THEN ColorError["index out of range"]; [r,g,b] _ ColorTable[index]; RETURN[r,g,b]; }; }.