--Conversion routines between color models DIRECTORY ColorModels, Real USING [FixI], RuntimeError USING [BoundsFault]; ColorModelsImpl: CEDAR PROGRAM IMPORTS Real, RuntimeError EXPORTS ColorModels = {OPEN ColorModels; InvalidColor: PUBLIC SIGNAL=CODE; Uninitialized: PUBLIC SIGNAL=CODE; ToRange: PROC[v: REAL] RETURNS[REAL] = INLINE { IF v IN[0..1] THEN RETURN[v] ELSE ERROR RuntimeError.BoundsFault }; -- ensures that v is in [0..1]; raises BoundsFault if not HSVToRGB: PUBLIC PROC[h,s,v: REAL] RETURNS[r,g,b: REAL] = { hue: REAL; saturation: REAL _ ToRange[s]; value: REAL _ ToRange[v]; ihue: INTEGER; fhue,m,n,k: REAL; IF h=undefined THEN IF saturation=0 THEN {r _ g _ b _ value; RETURN} ELSE SIGNAL InvalidColor ELSE hue _ ToRange[h]; hue _ hue*6; ihue _ Real.FixI[hue]; --integer hue fhue _ hue-ihue; --fractional hue IF ihue=6 THEN ihue _ 0; m _ value*(1-saturation); n _ value*(1-(saturation*fhue)); k _ value*(1-(saturation*(1-fhue))); SELECT ihue FROM 0 => RETURN[value,k,m]; 1 => RETURN[n,value,m]; 2 => RETURN[m,value,k]; 3 => RETURN[m,n,value]; 4 => RETURN[k,m,value]; 5 => RETURN[value,m,n]; ENDCASE => RETURN[0,0,0]; }; RGBToHSV: PUBLIC PROC[r,g,b: REAL] RETURNS[h,s,v: REAL] = { max,min,rc,gc,bc: REAL; r _ ToRange[r]; g _ ToRange[g]; b _ ToRange[b]; min _ MIN[MIN[r,g],b]; --amount of white v _ max _ MAX[MAX[r,g],b];--maximum "brightness" IF max#0 THEN s _ (max-min)/max ELSE s _ 0; IF s=0 THEN RETURN[undefined,0,v]; --gray rc _ (max - r)/(max - min); gc _ (max - g)/(max - min); bc _ (max - b)/(max - min); IF r=max THEN h_bc-gc ELSE IF g=max THEN h_2+rc-bc ELSE IF b=max THEN h_4+gc-rc; h _ h / 6.0; IF h<0 THEN h_h+1; RETURN[h, s, v]; }; HSLToRGB: PUBLIC PROC[h, s, l: REAL] RETURNS[r, g, b: REAL] = { m1,m2,hue,saturation, lightness: REAL; Value: PROC[n1,n2,h1: REAL] RETURNS[v: REAL] = { IF h1 > 360 THEN h1 _ h1-360; IF h1 < 0 THEN h1 _ h1+360; v _ SELECT TRUE FROM h1 IN [0..60) => n1+(n2-n1)*h1/60, h1 IN [60..180) => n2, h1 IN [180..240) => n1+(n2-n1)*(240-h1)/60, ENDCASE => n1; }; saturation _ ToRange[s]; lightness _ ToRange[l]; IF h=undefined THEN IF saturation=0 THEN {r _ g _ b _ lightness} ELSE SIGNAL InvalidColor ELSE hue _ 360*ToRange[h]; m2 _ IF lightness <= 0.5 THEN lightness*(1+saturation) ELSE lightness+saturation-lightness*saturation; m1 _ 2*lightness-m2; r _ Value[m1,m2,hue+120]; g _ Value[m1,m2,hue]; b _ Value[m1,m2,hue-120]; }; RGBToHSL: PUBLIC PROC[r, g, b: REAL] RETURNS[h, s, l: REAL] = { max,min,rc,gc,bc,del: REAL; red: REAL _ ToRange[r]; green: REAL _ ToRange[g]; blue: REAL _ ToRange[b]; max _ MAX[red,MAX[green,blue]]; min _ MIN[red,MIN[green,blue]]; l _ (max+min)/2; IF max=min THEN RETURN[undefined,0,l]; --gray del _ max-min; s _ IF l <= 0.5 THEN del/(max+min) ELSE del/(2-max-min); rc _ (max-red)/del; gc _ (max-green)/del; bc _ (max-blue)/del; IF max = red THEN h _ bc-gc --between yellow and magenta ELSE IF max = green THEN h _ 2+rc-bc --between cyan and yellow ELSE IF max = blue THEN h _ 4+gc-rc --between magenta and cyan ELSE SIGNAL InvalidColor; h _ h/6.0; IF h < 0 THEN h _ h+1; }; InitCIE: PUBLIC PROC[xr,yr,xg,yg,xb,yb: REAL, whiteY: REAL _ 1] RETURNS [calibration: Calibration] = { calibration _ NEW[CalibrationRec]; calibration.toCIE[0][0] _xr; calibration.toCIE[1][0] _yr; calibration.toCIE[2][0] _1-(xr+yr); calibration.toCIE[0][1] _xg; calibration.toCIE[1][1] _yg; calibration.toCIE[2][1] _1-(xg+yg); calibration.toCIE[0][2] _xb; calibration.toCIE[1][2] _yb; calibration.toCIE[2][2] _1-(xb+yb); calibration.yScale _ whiteY/(yr+yg+yb); calibration.toRGB _ Invert3[calibration.toCIE]; }; longXR: REAL _ 0.6; longYR: REAL _ 0.325; longXG: REAL _ 0.22; longYG: REAL _ 0.62; longXB: REAL _ 0.23; longYB: REAL _ 0.2; normalXR: REAL _ 0.615; normalYR: REAL _ 0.34; normalXG: REAL _ 0.3; normalYG: REAL _ 0.59; normalXB: REAL _ 0.15; normalYB: REAL _ 0.065; longCalibration: Calibration _ InitCIE[longXR,longYR,longXG,longYG,longXB,longYB]; normalCalibration: Calibration _ InitCIE[normalXR,normalYR,normalXG,normalYG,normalXB,normalYB]; GetDefaultCalibration: PUBLIC PROC[type: PhosphorType _ long] RETURNS [calibration: Calibration] = { SELECT type FROM long => calibration _ longCalibration; normal => calibration _ normalCalibration; ENDCASE => ERROR; }; GetDefaultValues: PUBLIC PROC[type: PhosphorType] RETURNS[xr,yr,xg,yg,xb,yb: REAL] = { SELECT type FROM long => RETURN[xr: longXR, yr: longYR, xg: longXG, yg: longYG, xb: longXB, yb: longYB]; normal => RETURN[xr: normalXR, yr: normalYR, xg: normalXG, yg: normalYG, xb: normalXB, yb: normalYB]; ENDCASE => ERROR; }; SetDefaultValues: PUBLIC PROC[xr,yr,xg,yg,xb,yb: REAL, type: PhosphorType] = { SELECT type FROM long => { longXR _ xr; longYR _ yr; longXG _ xg; longYG _ yg; longXB _ xb; longYB _ yb; longCalibration _ InitCIE[longXR,longYR,longXG,longYG,longXB,longYB]; }; normal => { normalXR _ xr; normalYR _ yr; normalXG _ xg; normalYG _ yg; normalXB _ xb; normalYB _ yb; normalCalibration _ InitCIE[normalXR,normalYR,normalXG,normalYG,normalXB,normalYB]; }; ENDCASE => ERROR; }; CIEToRGB: PUBLIC PROC[x,y, Y: REAL, calibration: Calibration] RETURNS[r, g, b: REAL] = { rgb,cie: Column3; s: REAL; s _ Y/(calibration.yScale*y); --now a real Y cie[0] _ x*s; cie[1] _ y*s; cie[2] _ s-(x+y)*s; rgb _ MultiplyVec[calibration.toRGB,cie]; r _ rgb[0]; g _rgb[1]; b _ rgb[2]; }; RGBToCIE: PUBLIC PROC[r, g, b: REAL, calibration: Calibration] RETURNS [x,y, Y: REAL] = { cie,rgb: Column3; s: REAL; rgb _ [r,g,b]; cie _ MultiplyVec[calibration.toCIE,rgb]; s _ cie[0]+cie[1]+cie[2]; --cie must be positive IF s=0 THEN RETURN[0,0,0]; --r, g, b =0 means color is black x _ cie[0]/s; y _ cie[1]/s; Y _ cie[1]*calibration.yScale; }; GetMaxY: PUBLIC PROC[x,y: REAL, calibration: Calibration] RETURNS[Y: REAL] = { cie,ymax: Column3; cie _ [x/y,1,(1-(x+y))/y]; ymax _ MultiplyVec[calibration.toRGB,cie]; --actually the inverses Y _ MAX[MAX[ymax[0],ymax[1]],ymax[2]]; Y _ calibration.yScale/Y; }; Matrix2: TYPE = ARRAY [0..2) OF Row2; Row2: TYPE = ARRAY [0..2) OF Real0; Column2: TYPE = ARRAY [0..2) OF Real0; MultiplyVec: PROC[a: Matrix3, v: Column3] RETURNS [c: Column3] = { FOR i: INTEGER IN [0..3) DO c[i] _ 0; FOR j: INTEGER IN [0..3) DO c[i] _ c[i]+a[i][j]*v[j]; ENDLOOP; ENDLOOP; }; Invert3: PROCEDURE [a: Matrix3] RETURNS [ai: Matrix3] = { nrows,ncols: INTEGER _ 3; det: REAL; Aij: Matrix2; det _ Determinant3[a]; FOR i: INTEGER IN [0..nrows) DO FOR j: INTEGER IN [0..ncols) DO Aij _ MakeAij[a,i,j]; ai[j][i] _ Sign[i,j]*Determinant2[Aij]/det; ENDLOOP; ENDLOOP; RETURN[ai]; }; Determinant3: PROC[a: Matrix3] RETURNS [det: REAL] = { nrows,ncols: INTEGER _ 3; i,j: INTEGER; Aij: Matrix2; det _ 0; j _ 0; --always use column 0 for now FOR i IN [0..nrows) DO Aij _ MakeAij[a,i,j]; det _ det + a[i][j]*Sign[i,j]*Determinant2[Aij]; ENDLOOP; RETURN[det]; }; Determinant2: PROC[a: Matrix2] RETURNS [det: REAL] = { det _ a[0][0]*a[1][1]-a[0][1]*a[1][0]; }; Sign: PROC[i,j: INTEGER] RETURNS[REAL] = {RETURN[IF (i+j) MOD 2 = 0 THEN 1 ELSE -1]}; MakeAij: PROC[a: Matrix3, i,j: INTEGER] RETURNS[Aij: Matrix2]= { n,m: INTEGER _ 0; --row index, column index for new matrix FOR row: INTEGER IN [0..3) DO IF row=i THEN LOOP; --row index for original matrix m _ 0; --column index for new matrix FOR col: INTEGER IN [0..3) DO IF col=j THEN LOOP; Aij[n][m] _ a[row][col]; m _ m+1; ENDLOOP; n _ n+1; ENDLOOP; }; }. π--ColorModelsImpl.mesa --Last edited by Maureen Stone, February 10, 1984 3:48:09 pm PST --Last Edited by: Pier, January 18, 1984 3:50 pm These algorithms use the hexacone model described in "Color Gamut Transform Pairs" by Alvy Ray Smith Siggraph 1978, p. 12. Algorithms from Foley and van Dam cache the default calibrations may return values outside the range [0..1] work this out by algebra, find we can use ToRGB to solve it Matrix routines. Specialized versions here to avoid imports Κ «– "cedar" style˜IprocšœΟi œ ™Kšœ@™@JšΟcœ.™0Kšž+˜+šΟk ˜ K˜ KšœŸœ˜Kšœ Ÿœ˜"—KšœŸœŸœ˜KšŸœ˜KšŸœŸœ ˜(K˜KšœŸœŸœŸœ˜!KšœŸœŸœŸœ˜"K˜KšΟnœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜LKšœŸœŸœ˜)Kšž' ž˜:Kšœ˜Kšœ4™4Kšœ/™/Kšœ™Kšœ!™!Kšœ˜š  œŸœŸœŸœŸœŸœ˜;KšœŸœ˜ Kšœ Ÿœ˜KšœŸœ˜KšœŸœ˜Kšœ Ÿœ˜šŸœ Ÿœ˜KšŸœŸœŸœ˜0KšŸœŸœ ˜—KšŸœ˜Kšœ ˜ Kšœž ˜$Kšœž˜!KšŸœŸœ ˜Kšœ˜Kšœ ˜ Kšœ$˜$šŸœŸ˜KšœŸœ ˜KšœŸœ ˜KšœŸœ ˜KšœŸœ ˜KšœŸœ ˜KšœŸœ ˜KšŸœŸœ˜—Kšœ˜—K˜š  œŸœŸœŸœŸœŸœ˜;KšœŸœ˜Kšœ/˜/KšœŸœŸœ ž˜)Kšœ ŸœŸœ ž˜0KšŸœŸœ˜KšŸœ˜ KšŸœŸœŸœž˜*Kšœ˜Kšœ˜Kšœ˜KšŸœŸœ˜KšŸœŸœŸœ ˜KšŸœŸœŸœ ˜Kšœ ˜ KšŸœŸœ˜KšŸœ ˜Kšœ˜—K˜š  œŸœŸœ ŸœŸœ Ÿœ˜?Kšœ!Ÿœ˜&š  œŸœ ŸœŸœŸœ˜0KšŸœ Ÿœ ˜KšŸœŸœ ˜šœŸœŸœŸ˜KšœŸœ˜"KšœŸœ˜KšœŸœ&˜+KšŸœ˜—K˜—K˜K˜šŸœ Ÿ˜KšŸœŸœ˜,KšŸœŸœ ˜—KšŸœ˜šœŸœ˜KšŸœ˜KšŸœ+˜/—K˜K˜K˜K˜K˜—š  œŸœŸœ ŸœŸœ Ÿœ˜?KšœŸœ˜KšœŸœ˜KšœŸœ˜KšœŸœ˜KšœŸœŸœ˜KšœŸœŸœ˜K˜KšŸœ ŸœŸœž˜-K˜KšœŸœ ŸœŸœ˜8K˜K˜K˜KšŸœ Ÿœ ž˜8KšŸœŸœ Ÿœ ž˜>KšŸœŸœ Ÿœ ž˜>KšŸœŸœ˜K˜ KšŸœŸœ ˜K˜—J˜J˜š  œŸœŸœŸœ ŸœŸœ˜fKšœŸœ˜"Kšœ]˜]Kšœ]˜]Kšœ]˜]Kšœ'˜'Kšœ/˜/K˜—KšœŸœ˜KšœŸœ ˜KšœŸœ˜KšœŸœ˜KšœŸœ˜KšœŸœ˜Kšœ Ÿœ ˜Kšœ Ÿœ˜Kšœ Ÿœ˜Kšœ Ÿœ˜Kšœ Ÿœ˜Kšœ Ÿœ ˜Kšœ™KšœR˜RKšœ`˜`š œŸ œŸœ˜dšŸœŸ˜Kšœ'˜'KšœŸœ˜*KšŸœŸœ˜—K˜—š œŸ œŸœŸœ˜VšŸœŸ˜KšœŸœI˜WKšœ ŸœU˜eKšŸœŸœ˜—K˜—š œŸ œŸœ˜NšŸœŸ˜šœ ˜ Kšœ˜Kšœ˜Kšœ˜KšœE˜EKšœ˜—šœ ˜ Kšœ˜Kšœ˜Kšœ˜KšœS˜SKšœ˜—KšŸœŸœ˜K˜——K˜Kš œŸœŸœŸœŸœŸœ Ÿœ˜Xšœ*™*Kšœ˜KšœŸœ˜Kšœ,˜,K˜/Kšœ)˜)K˜"K˜—š  œŸ œ ŸœŸœŸœŸœ˜YKšœ˜KšœŸ˜K˜Kšœ)˜)Kšœž˜0KšŸœŸœŸœ ž!˜