<> <> <> <> <> <<>> DIRECTORY CGColorWithCIE USING [Color, GetStipple, undefined, PhosphorType], Basics USING[BITSHIFT, BITAND], Real USING [FixI, RoundC], ImagerColor USING [RGB, HSV, HSL, YIQ, HSVFromRGB, RGBFromHSV, HSLFromRGB, RGBFromHSL, YIQFromRGB, RGBFromYIQ], Matrix, RuntimeError USING [BoundsFault]; CGColorWithCIEImpl: CEDAR PROGRAM IMPORTS CGColorWithCIE, Basics, Real, RuntimeError, Matrix, ImagerColor EXPORTS CGColorWithCIE = { Color: TYPE = CGColorWithCIE.Color; Byte: TYPE = [0..256); undefined: REAL= CGColorWithCIE.undefined; 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 ToByte: PROC[v: REAL] RETURNS[Byte] = INLINE { RETURN[Real.RoundC[ToRange[v]*255]] }; -- assumes v IN[0..1] MakeRGB: PROC[r, g, b: Byte] RETURNS[Color] = INLINE { RETURN[[tag: rgb, r: r, g: g, b: b]] }; IntensityToColor: PUBLIC PROC[intensity: REAL] RETURNS[Color] = { i: Byte _ ToByte[ToRange[intensity]]; RETURN[MakeRGB[i, i, i]] }; RGBToColor: PUBLIC PROC[r,g,b: REAL] RETURNS[Color] = { red: Byte _ ToByte[ToRange[r]]; grn: Byte _ ToByte[ToRange[g]]; blu: Byte _ ToByte[ToRange[b]]; RETURN[MakeRGB[red, grn, blu]] }; HSVToColor: PUBLIC PROC[h,s,v: REAL] RETURNS[Color] = { r,g,b: REAL; [r,g,b] _ HSVToRGB[h, s, v]; RETURN[MakeRGB[ToByte[r],ToByte[g],ToByte[b]]] }; ColorToIntensity: PUBLIC PROC[color: Color] RETURNS[intensity: REAL] = { SELECT color.tag FROM rgb => { i: REAL; IF color.b=color.r AND color.g=color.r THEN i _ color.r ELSE i _ 0.30*color.r+0.11*color.b+0.59*color.g; intensity _ i/255.0 }; stipple => intensity _ StippleToIntensity[color]; ENDCASE => intensity _ 0; RETURN[intensity]; }; ColorToHSV: PUBLIC PROC[color: Color] RETURNS[h, s, v: REAL] = { SELECT color.tag FROM rgb => [h,s,v] _ RGBToHSV[color.r/255.0, color.g/255.0, color.b/255.0]; stipple => { h _ s _ 0; v _ StippleToIntensity[color] }; ENDCASE => h _ s _ v _ 0; RETURN[h,s,v]; }; ColorToRGB: PUBLIC PROC[color: Color] RETURNS[r, g, b: REAL] = { SELECT color.tag FROM rgb => RETURN[r: color.r/255.0, g: color.g/255.0, b: color.b/255.0]; stipple => { i: REAL _ StippleToIntensity[color]; RETURN[i, i, i] }; ENDCASE => RETURN[0, 0, 0]; }; StippleToIntensity: PROC[color: Color] RETURNS[intensity: REAL] = { -- assumes color.tag = stipple bits: CARDINAL _ CGColorWithCIE.GetStipple[color]; count: NAT _ 0; FOR i: CARDINAL IN[0..16) DO TRUSTED { IF Basics.BITAND[bits,1]=0 THEN count _ count+1; -- count "white" bits bits _ Basics.BITSHIFT[bits,-1] }; ENDLOOP; intensity _ count/16.0; }; <> <<"Color Gamut Transform Pairs" by Alvy Ray Smith>> <> <> 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; }; rgbToYiqMatrix: Matrix.MatrixN _ Matrix.Create[3,3]; yiqToRgbMatrix: Matrix.MatrixN _ Matrix.Create[3,3]; YIQToRGB: PUBLIC PROC[y, i, q: REAL] RETURNS[r, g, b: REAL] = { rgbMatrix, yiqMatrix: Matrix.MatrixN; rgbMatrix _ Matrix.Create[3,1]; yiqMatrix _ Matrix.Create[3,1]; yiqMatrix[0][0] _ y; yiqMatrix[1][0] _ i * 1.2 - 0.6; yiqMatrix[2][0] _ q * 1.04 - 0.52; rgbMatrix _ Matrix.Multiply[yiqToRgbMatrix, yiqMatrix]; r _ rgbMatrix[0][0]; g _ rgbMatrix[1][0]; b _ rgbMatrix[2][0]; r _ MIN[r,1]; g _ MIN[g,1]; b _ MIN[b,1]; --control precision problems r _ MAX[r,0]; g _ MAX[g,0]; b _ MAX[b,0]; }; RGBToYIQ: PUBLIC PROC[r, g, b: REAL] RETURNS[y, i, q: REAL] = { rgbMatrix, yiqMatrix: Matrix.MatrixN; rgbMatrix _ Matrix.Create[3,1]; yiqMatrix _ Matrix.Create[3,1]; rgbMatrix[0][0] _ r; rgbMatrix[1][0] _ g; rgbMatrix[2][0] _ b; yiqMatrix _ Matrix.Multiply[rgbToYiqMatrix, rgbMatrix]; y _ yiqMatrix[0][0]; i _ (yiqMatrix[1][0] + 0.6) / 1.2; q _ (yiqMatrix[2][0] + 0.52) / 1.04; y _ MIN[y,1]; i _ MIN[i,1]; q _ MIN[q,1]; --control precision problems y _ MAX[y,0]; i _ MAX[i,0]; q _ MAX[q,0]; }; rgbToArgybMatrix: Matrix.MatrixN _ Matrix.Create[3,3]; argybToRgbMatrix: Matrix.MatrixN _ Matrix.Create[3,3]; ARgYbToRGB: PUBLIC PROC[a, rg, yb: REAL] RETURNS[r, g, b: REAL] = { rgbMatrix, argybMatrix: Matrix.MatrixN; rgbMatrix _ Matrix.Create[3,1]; argybMatrix _ Matrix.Create[3,1]; argybMatrix[0][0] _ a; argybMatrix[1][0] _ rg - 0.5; argybMatrix[2][0] _ yb - 0.5; rgbMatrix _ Matrix.Multiply[argybToRgbMatrix, argybMatrix]; r _ rgbMatrix[0][0]; g _ rgbMatrix[1][0]; b _ rgbMatrix[2][0]; r _ MIN[r,1]; g _ MIN[g,1]; b _ MIN[b,1]; --control precision problems r _ MAX[r,0]; g _ MAX[g,0]; b _ MAX[b,0]; }; RGBToARgYb: PUBLIC PROC[r, g, b: REAL] RETURNS[a, rg, yb: REAL] = { rgbMatrix, argybMatrix: Matrix.MatrixN; rgbMatrix _ Matrix.Create[3,1]; argybMatrix _ Matrix.Create[3,1]; rgbMatrix[0][0] _ r; rgbMatrix[1][0] _ g; rgbMatrix[2][0] _ b; argybMatrix _ Matrix.Multiply[rgbToArgybMatrix, rgbMatrix]; a _ argybMatrix[0][0]; rg _ argybMatrix[1][0] + 0.5; yb _ argybMatrix[2][0] + 0.5; a _ MIN[a,1]; rg _ MIN[rg,1]; yb _ MIN[yb,1]; --control precision problems a _ MAX[a,0]; rg _ MAX[rg,0]; yb _ MAX[yb,0]; }; Real0: TYPE = REAL _ 0; Matrix3: TYPE = ARRAY [0..3) OF Row3; Row3: TYPE = ARRAY [0..3) OF Real0; Column3: TYPE = ARRAY [0..3) OF Real0; ToCIE: Matrix3; ToRGB: Matrix3; yScale: REAL _ 1; Initialized: BOOLEAN _ FALSE; InitCIE: PUBLIC PROC[xr,yr,xg,yg,xb,yb: REAL, whiteY: REAL _ 1] = { matrix: Matrix.MatrixN; ToCIE[0][0] _xr; ToCIE[1][0] _yr; ToCIE[2][0] _1-(xr+yr); ToCIE[0][1] _xg; ToCIE[1][1] _yg; ToCIE[2][1] _1-(xg+yg); ToCIE[0][2] _xb; ToCIE[1][2] _yb; ToCIE[2][2] _1-(xb+yb); Initialized _ TRUE; yScale _ whiteY/(yr+yg+yb); matrix _ Matrix.Create[3,3]; FOR i: INTEGER IN [0..3) DO FOR j: INTEGER IN [0..3) DO matrix[i][j] _ ToCIE[i][j]; ENDLOOP; ENDLOOP; matrix _ Matrix.Invert[matrix]; FOR i: INTEGER IN [0..3) DO FOR j: INTEGER IN [0..3) DO ToRGB[i][j] _ matrix[i][j]; ENDLOOP; ENDLOOP; }; 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; InitDefaultCIE: PUBLIC PROC[type: CGColorWithCIE.PhosphorType _ long] = { SELECT type FROM long => InitCIE[longXR,longYR,longXG,longYG,longXB,longYB]; normal => InitCIE[normalXR,normalYR,normalXG,normalYG,normalXB,normalYB]; ENDCASE => ERROR; }; GetDefaultValues: PUBLIC PROC[type: CGColorWithCIE.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: CGColorWithCIE.PhosphorType] = { SELECT type FROM long => { longXR _ xr; longYR _ yr; longXG _ xg; longYG _ yg; longXB _ xb; longYB _ yb; }; normal => { normalXR _ xr; normalYR _ yr; normalXG _ xg; normalYG _ yg; normalXB _ xb; normalYB _ yb; }; ENDCASE => ERROR; }; CIEToRGB: PUBLIC PROC[x,y, Y: REAL] RETURNS[r, g, b: REAL] = { <> rgb,cie: Column3; s: REAL; IF ~Initialized THEN SIGNAL Uninitialized; s _ Y/(yScale*y); --now a real Y cie[0] _ x*s; cie[1] _ y*s; cie[2] _ s-(x+y)*s; rgb _ MultiplyVec[ToRGB,cie]; r _ rgb[0]; g _rgb[1]; b _ rgb[2]; }; RGBToCIE: PUBLIC PROC[r, g, b: REAL] RETURNS [x,y, Y: REAL] = { cie,rgb: Column3; s: REAL; IF ~Initialized THEN SIGNAL Uninitialized; rgb _ [r,g,b]; cie _ MultiplyVec[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]*yScale; }; GetMaxY: PUBLIC PROC[x,y: REAL] RETURNS[Y: REAL] = { <> cie,ymax: Column3; cie _ [x/y,1,(1-(x+y))/y]; ymax _ MultiplyVec[ToRGB,cie]; --actually the inverses Y _ MAX[MAX[ymax[0],ymax[1]],ymax[2]]; Y _ yScale/Y; }; 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; }; ContrastInRGB: PUBLIC PROC[rgb: ImagerColor.RGB] RETURNS[rgbContrast: ImagerColor.RGB] = { IF rgb.R < 0.5 THEN rgbContrast.R _ 1.0 ELSE rgbContrast.R _ 0.0; IF rgb.G < 0.5 THEN rgbContrast.G _ 1.0 ELSE rgbContrast.G _ 0.0; IF rgb.B < 0.5 THEN rgbContrast.B _ 1.0 ELSE rgbContrast.B _ 0.0; }; ContrastInHSV: PUBLIC PROC[rgb: ImagerColor.RGB] RETURNS[hsvContrast: ImagerColor.RGB] = { hsvContrastInHSV: ImagerColor.HSV; hsv: ImagerColor.HSV _ ImagerColor.HSVFromRGB[rgb]; hsvContrastInHSV.H _ hsv.H + 0.5; IF hsvContrastInHSV.H > 1.0 THEN hsvContrastInHSV.H _ hsvContrastInHSV.H - 1.0; IF hsv.S < 0.5 THEN hsvContrastInHSV.S _ 1.0 ELSE hsvContrastInHSV.S _ 0.0; IF hsv.V < 0.5 THEN hsvContrastInHSV.V _ 1.0 ELSE hsvContrastInHSV.V _ 0.0; hsvContrast _ ImagerColor.RGBFromHSV[hsvContrastInHSV]; }; ContrastInHSL: PUBLIC PROC[rgb: ImagerColor.RGB] RETURNS[hslContrast: ImagerColor.RGB] = { hslContrastInHSL: ImagerColor.HSL; hsl: ImagerColor.HSL _ ImagerColor.HSLFromRGB[rgb]; hslContrastInHSL.H _ hsl.H + 0.5; IF hslContrastInHSL.H > 1.0 THEN hslContrastInHSL.H _ hslContrastInHSL.H - 1.0; IF hsl.S < 0.5 THEN hslContrastInHSL.S _ 1.0 ELSE hslContrastInHSL.S _ 0.0; IF hsl.L < 0.5 THEN hslContrastInHSL.L _ 1.0 ELSE hslContrastInHSL.L _ 0.0; hslContrast _ ImagerColor.RGBFromHSL[hslContrastInHSL]; }; ContrastInYIQ: PUBLIC PROC[rgb: ImagerColor.RGB] RETURNS[yiqContrast: ImagerColor.RGB] = { yiqContrastInYIQ: ImagerColor.YIQ; yiq: ImagerColor.YIQ _ ImagerColor.YIQFromRGB[rgb]; yiq.I _ (yiq.I + 0.6) / 1.2; yiq.Q _ (yiq.Q + 0.52) / 1.04; IF yiq.Y < 0.5 THEN yiqContrastInYIQ.Y _ 1.0 ELSE yiqContrastInYIQ.Y _ 0.0; IF yiq.I < 0.5 THEN yiqContrastInYIQ.I _ 1.0 ELSE yiqContrastInYIQ.I _ 0.0; IF yiq.Q < 0.5 THEN yiqContrastInYIQ.Q _ 1.0 ELSE yiqContrastInYIQ.Q _ 0.0; yiqContrastInYIQ.I _ yiqContrastInYIQ.I * 1.2 - 0.6; yiqContrastInYIQ.Q _ yiqContrastInYIQ.Q * 1.04 - 0.52; yiqContrast _ ImagerColor.RGBFromYIQ[yiqContrastInYIQ]; yiqContrast.R _ MIN[yiqContrast.R,1]; yiqContrast.G _ MIN[yiqContrast.G,1]; yiqContrast.B _ MIN[yiqContrast.B,1]; yiqContrast.R _ MAX[yiqContrast.R,0]; yiqContrast.G _ MAX[yiqContrast.G,0]; yiqContrast.B _ MAX[yiqContrast.B,0]; }; ContrastInARgYb: PUBLIC PROC[rgb: ImagerColor.RGB] RETURNS[argybContrast: ImagerColor.RGB] = { a, rg, yb: REAL; aContrastInARgYb, rgContrastInARgYb, ybContrastInARgYb: REAL; [a, rg, yb] _ RGBToARgYb[rgb.R, rgb.G, rgb.B]; IF a < 0.5 THEN aContrastInARgYb _ 1.0 ELSE aContrastInARgYb _ 0.0; IF rg < 0.5 THEN rgContrastInARgYb _ 1.0 ELSE rgContrastInARgYb _ 0.0; IF yb < 0.5 THEN ybContrastInARgYb _ 1.0 ELSE ybContrastInARgYb _ 0.0; [argybContrast.R, argybContrast.G, argybContrast.B] _ ARgYbToRGB[aContrastInARgYb, rgContrastInARgYb, ybContrastInARgYb]; argybContrast.R _ MIN[argybContrast.R,1]; argybContrast.G _ MIN[argybContrast.G,1]; argybContrast.B _ MIN[argybContrast.B,1]; argybContrast.R _ MAX[argybContrast.R,0]; argybContrast.G _ MAX[argybContrast.G,0]; argybContrast.B _ MAX[argybContrast.B,0]; }; <<>> <> <<>> rgbToYiqMatrix[0][0] _ 0.3; rgbToYiqMatrix[0][1] _ 0.59; rgbToYiqMatrix[0][2] _ 0.11; rgbToYiqMatrix[1][0] _ 0.6; rgbToYiqMatrix[1][1] _ -0.28; rgbToYiqMatrix[1][2] _ -0.32; rgbToYiqMatrix[2][0] _ 0.21; rgbToYiqMatrix[2][1] _ -0.52; rgbToYiqMatrix[2][2] _ 0.31; yiqToRgbMatrix _ Matrix.Invert[rgbToYiqMatrix]; <<>> rgbToArgybMatrix[0][0] _ 0.3; rgbToArgybMatrix[0][1] _ 0.59; rgbToArgybMatrix[0][2] _ 0.11; rgbToArgybMatrix[1][0] _ 0.5; rgbToArgybMatrix[1][1] _ -0.5; rgbToArgybMatrix[1][2] _ 0.0; rgbToArgybMatrix[2][0] _ 0.25; rgbToArgybMatrix[2][1] _ 0.25; rgbToArgybMatrix[2][2] _ -0.5; argybToRgbMatrix _ Matrix.Invert[rgbToArgybMatrix]; }.