DIRECTORY ColorModels, Real USING [FixI], RuntimeError USING [BoundsFault], Atom USING [GetProp, PutProp]; ColorModelsImpl: CEDAR PROGRAM IMPORTS Real, RuntimeError, Atom EXPORTS ColorModels ~ BEGIN 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 }; 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.0 THEN SIGNAL InvalidColor; r _ g _ b _ lightness; } 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; longXG: REAL _ 0.22; longXB: REAL _ 0.23; longYR: REAL _ 0.325; longYG: REAL _ 0.62; longYB: REAL _ 0.2; normalXR: REAL _ 0.615; normalXG: REAL _ 0.3; normalXB: REAL _ 0.15; normalYR: REAL _ 0.34; normalYG: REAL _ 0.59; normalYB: REAL _ 0.065; HitachiLPxR: REAL _ 0.603; HitachiLPxG: REAL _ 0.220; HitachiLPxB: REAL _ 0.151; HitachiLPyR: REAL _ 0.327; HitachiLPyG: REAL _ 0.619; HitachiLPyB: REAL _ 0.064; HitachiNPxR: REAL _ 0.610; HitachiNPxG: REAL _ 0.298; HitachiNPxB: REAL _ 0.151; HitachiNPyR: REAL _ 0.342; HitachiNPyG: REAL _ 0.588; HitachiNPyB: REAL _ 0.064; phosphorRegistrationKey: ATOM ~ $ImagerPhosphorSpec; GetPhosphorCalibration: PUBLIC PROC[type: PhosphorType _ $DefaultLP] RETURNS [calibration: Calibration] = { calibration _ NARROW[Atom.GetProp[type, phosphorRegistrationKey]]; }; RegisterPhosphorCalibration: PUBLIC PROC[xr,yr,xg,yg,xb,yb: REAL, type: PhosphorType] ~ { calibration: Calibration _ InitCIE[xr,yr,xg,yg,xb,yb]; Atom.PutProp[type, phosphorRegistrationKey, calibration]; }; 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.3101,0.3163,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; }; RegisterPhosphorCalibration[longXR, longYR, longXG, longYG, longXB, longYB, $DefaultLP]; RegisterPhosphorCalibration[normalXR, normalYR, normalXG, normalYG, normalXB, normalYB, $DefaultNP]; RegisterPhosphorCalibration[HitachiLPxR, HitachiLPyR, HitachiLPxG, HitachiLPyG, HitachiLPxB, HitachiLPyB, $HitachiLP]; RegisterPhosphorCalibration[HitachiNPxR, HitachiNPyR, HitachiNPxG, HitachiNPyG, HitachiNPxB, HitachiNPyB, $HitachiNP]; END. θColorModelsImpl.mesa - Conversion routines between color models Last edited by Maureen Stone, February 10, 1984 3:48:09 pm PST Last Edited by: Pier, January 18, 1984 3:50 pm Last Edited by: Crow, April 17, 1984 10:27:25 am PST ensures that v is in [0..1]; raises BoundsFault if not 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 default CIE coordinates for calibration Hitachi's CIE coordinates (from catalog # CE-E500R, Jan. 1982) 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 register the known calibrations Κ <– "cedar" style˜procšΟi œ Οc+™?Kšœ>™>Jšœ.™.Jšœ4™4—šΟk ˜ K˜ KšœŸœ˜Kšœ Ÿœ˜!KšœŸœ˜!—KšœŸœŸœ˜KšŸœ˜ KšŸœ ˜šœŸ˜KšŸœ ˜—˜KšœŸœŸœŸœ˜!KšœŸœŸœŸœ˜#—KšΟnœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜LšœŸœŸœ˜)Kšž$ ž™6Kšœ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šœ ŸœŸœ ž˜0KšŸœŸœ˜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šŸœ ŸœŸœž˜-K˜KšœŸœ ŸœŸœ˜8K˜K˜K˜KšŸœ Ÿœ ž˜8KšŸœŸœ Ÿœ ž˜>KšŸœŸœ Ÿœ ž˜>KšŸœŸœ˜K˜ KšŸœŸœ ˜K˜—š  œŸœŸœŸœ ŸœŸœ˜fKšœŸœ˜"Kšœ]˜]Kšœ]˜]Kšœ]˜]Kšœ'˜'Kšœ/˜/K˜Kšœ'™'KšœŸœŸœŸœ˜?KšœŸœŸœŸœ˜@Kšœ ŸœŸœŸœ ˜FKšœ ŸœŸœŸœ ˜EK™>Kšœ ŸœŸœŸœ ˜PKšœ ŸœŸœŸœ ˜PKšœ ŸœŸœŸœ ˜QKšœ ŸœŸœŸœ ˜Q—KšœŸœ˜4š œŸ œ"Ÿœ˜kKšœŸœ.˜BK˜—š œŸ œŸœ˜YKšœ6˜6Kšœ9˜9Kšœ˜—š œŸœŸœŸœŸœŸœ Ÿœ˜XKšœ*™*Kšœ˜KšœŸœ˜Kšœ,˜,K˜/Kšœ)˜)K˜"K˜—š  œŸ œ ŸœŸœŸœŸœ˜YKšœ˜KšœŸ˜K˜Kšœ)˜)Kšœž˜0KšŸœŸœŸœž!˜FKšœ:˜:K˜—š  œŸœŸœŸœŸœŸœ˜NKšœ;™;Kšœ˜K˜Kšœ+ž˜BKšŸœŸœŸœ˜&Kšœ˜K˜K˜Kšœ<™