ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/ccolor.c
Revision: 3.10
Committed: Wed Apr 5 00:54:50 2017 UTC (7 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 3.9: +3 -3 lines
Log Message:
Minor optimization

File Contents

# User Rev Content
1 greg 3.1 #ifndef lint
2 greg 3.10 static const char RCSid[] = "$Id: ccolor.c,v 3.9 2016/01/23 18:58:35 greg Exp $";
3 greg 3.1 #endif
4     /*
5     * Spectral color handling routines
6     */
7    
8     #include <stdio.h>
9 greg 3.9 #include <stdlib.h>
10 greg 3.1 #include <math.h>
11     #include "ccolor.h"
12    
13 greg 3.9 #undef frand
14     #define frand() (rand()*(1./(RAND_MAX+.5)))
15    
16 greg 3.4 /* Sharp primary matrix */
17     float XYZtoSharp[3][3] = {
18     { 1.2694, -0.0988, -0.1706},
19     {-0.8364, 1.8006, 0.0357},
20     { 0.0297, -0.0315, 1.0018}
21     };
22     /* inverse Sharp primary matrix */
23     float XYZfromSharp[3][3] = {
24     { 0.8156, 0.0472, 0.1372},
25     { 0.3791, 0.5769, 0.0440},
26     {-0.0123, 0.0167, 0.9955}
27     };
28 greg 3.1
29 greg 3.5 const C_COLOR c_dfcolor = C_DEFCOLOR;
30 greg 3.1
31 greg 3.9 const C_CHROMA c_dfchroma = 49750; /* c_encodeChroma(&c_dfcolor) */
32    
33 greg 3.1 /* CIE 1931 Standard Observer curves */
34 greg 3.5 const C_COLOR c_x31 = { 1, NULL, C_CDSPEC|C_CSSPEC|C_CSXY|C_CSEFF,
35 greg 3.1 {14,42,143,435,1344,2839,3483,3362,2908,1954,956,
36     320,49,93,633,1655,2904,4334,5945,7621,9163,10263,
37     10622,10026,8544,6424,4479,2835,1649,874,468,227,
38     114,58,29,14,7,3,2,1,0}, 106836L, .467, .368, 362.230
39     };
40 greg 3.5 const C_COLOR c_y31 = { 1, NULL, C_CDSPEC|C_CSSPEC|C_CSXY|C_CSEFF,
41 greg 3.1 {0,1,4,12,40,116,230,380,600,910,1390,2080,3230,
42     5030,7100,8620,9540,9950,9950,9520,8700,7570,6310,
43     5030,3810,2650,1750,1070,610,320,170,82,41,21,10,
44     5,2,1,1,0,0}, 106856L, .398, .542, 493.525
45     };
46 greg 3.5 const C_COLOR c_z31 = { 1, NULL, C_CDSPEC|C_CSSPEC|C_CSXY|C_CSEFF,
47 greg 3.1 {65,201,679,2074,6456,13856,17471,17721,16692,
48     12876,8130,4652,2720,1582,782,422,203,87,39,21,17,
49     11,8,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
50     106770L, .147, .077, 54.363
51     };
52     /* Derived CIE 1931 Primaries (imaginary) */
53 greg 3.2 static const C_COLOR cie_xp = { 1, NULL, C_CDSPEC|C_CSSPEC|C_CSXY,
54 greg 3.1 {-174,-198,-195,-197,-202,-213,-235,-272,-333,
55     -444,-688,-1232,-2393,-4497,-6876,-6758,-5256,
56     -3100,-815,1320,3200,4782,5998,6861,7408,7754,
57     7980,8120,8199,8240,8271,8292,8309,8283,8469,
58     8336,8336,8336,8336,8336,8336},
59     127424L, 1., .0,
60     };
61 greg 3.2 static const C_COLOR cie_yp = { 1, NULL, C_CDSPEC|C_CSSPEC|C_CSXY,
62 greg 3.1 {-451,-431,-431,-430,-427,-417,-399,-366,-312,
63     -204,57,691,2142,4990,8810,9871,9122,7321,5145,
64     3023,1123,-473,-1704,-2572,-3127,-3474,-3704,
65     -3846,-3927,-3968,-3999,-4021,-4038,-4012,-4201,
66     -4066,-4066,-4066,-4066,-4066,-4066},
67     -23035L, .0, 1.,
68     };
69 greg 3.2 static const C_COLOR cie_zp = { 1, NULL, C_CDSPEC|C_CSSPEC|C_CSXY,
70 greg 3.1 {4051,4054,4052,4053,4054,4056,4059,4064,4071,
71     4074,4056,3967,3677,2933,1492,313,-440,-795,
72     -904,-918,-898,-884,-869,-863,-855,-855,-851,
73     -848,-847,-846,-846,-846,-845,-846,-843,-845,
74     -845,-845,-845,-845,-845},
75     36057L, .0, .0,
76     };
77    
78 greg 3.2
79 greg 3.4 /* convert to sharpened RGB color for low-error operations */
80     void
81 greg 3.2 c_toSharpRGB(C_COLOR *cin, double cieY, float cout[3])
82     {
83     double xyz[3];
84    
85     c_ccvt(cin, C_CSXY);
86    
87     xyz[0] = cin->cx/cin->cy * cieY;
88     xyz[1] = cieY;
89     xyz[2] = (1. - cin->cx - cin->cy)/cin->cy * cieY;
90    
91 greg 3.4 cout[0] = XYZtoSharp[0][0]*xyz[0] + XYZtoSharp[0][1]*xyz[1] +
92     XYZtoSharp[0][2]*xyz[2];
93     cout[1] = XYZtoSharp[1][0]*xyz[0] + XYZtoSharp[1][1]*xyz[1] +
94     XYZtoSharp[1][2]*xyz[2];
95     cout[2] = XYZtoSharp[2][0]*xyz[0] + XYZtoSharp[2][1]*xyz[1] +
96     XYZtoSharp[2][2]*xyz[2];
97 greg 3.2 }
98    
99 greg 3.4 /* convert back from sharpened RGB color */
100     double
101 greg 3.2 c_fromSharpRGB(float cin[3], C_COLOR *cout)
102     {
103     double xyz[3], sf;
104    
105 greg 3.8 xyz[1] = XYZfromSharp[1][0]*cin[0] + XYZfromSharp[1][1]*cin[1] +
106     XYZfromSharp[1][2]*cin[2];
107     if (xyz[1] <= 1e-6) {
108     *cout = c_dfcolor; /* punting, here... */
109     return xyz[1];
110     }
111 greg 3.4 xyz[0] = XYZfromSharp[0][0]*cin[0] + XYZfromSharp[0][1]*cin[1] +
112     XYZfromSharp[0][2]*cin[2];
113     xyz[2] = XYZfromSharp[2][0]*cin[0] + XYZfromSharp[2][1]*cin[1] +
114     XYZfromSharp[2][2]*cin[2];
115 greg 3.8
116 greg 3.2 sf = 1./(xyz[0] + xyz[1] + xyz[2]);
117    
118     cout->cx = xyz[0] * sf;
119     cout->cy = xyz[1] * sf;
120     cout->flags = C_CDXY|C_CSXY;
121    
122     return(xyz[1]);
123     }
124    
125 greg 3.6 /* assign arbitrary spectrum and return Y value */
126 greg 3.3 double
127     c_sset(C_COLOR *clr, double wlmin, double wlmax, const float spec[], int nwl)
128     {
129     double yval, scale;
130     float va[C_CNSS];
131     int i, pos, n, imax, wl;
132     double wl0, wlstep;
133     double boxpos, boxstep;
134     /* check arguments */
135     if ((nwl <= 1) | (spec == NULL) | (wlmin >= C_CMAXWL) |
136     (wlmax <= C_CMINWL) | (wlmin >= wlmax))
137     return(0.);
138     wlstep = (wlmax - wlmin)/(nwl-1);
139     while (wlmin < C_CMINWL) {
140     wlmin += wlstep;
141     --nwl; ++spec;
142     }
143     while (wlmax > C_CMAXWL) {
144     wlmax -= wlstep;
145     --nwl;
146     }
147 greg 3.6 if ((nwl <= 1) | (wlmin >= wlmax))
148     return(0.);
149 greg 3.3 imax = nwl; /* box filter if necessary */
150     boxpos = 0;
151     boxstep = 1;
152     if (wlstep < C_CWLI) {
153     imax = (wlmax - wlmin)/C_CWLI + 1e-7;
154     boxpos = (wlmin - C_CMINWL)/C_CWLI;
155     boxstep = wlstep/C_CWLI;
156     wlstep = C_CWLI;
157     }
158     scale = 0.; /* get values and maximum */
159     yval = 0.;
160     pos = 0;
161     for (i = 0; i < imax; i++) {
162     va[i] = 0.; n = 0;
163     while (boxpos < i+.5 && pos < nwl) {
164     va[i] += spec[pos++];
165     n++;
166     boxpos += boxstep;
167     }
168     if (n > 1)
169     va[i] /= (double)n;
170     if (va[i] > scale)
171     scale = va[i];
172     else if (va[i] < -scale)
173     scale = -va[i];
174 greg 3.5 yval += va[i] * c_y31.ssamp[i];
175 greg 3.3 }
176     if (scale <= 1e-7)
177     return(0.);
178 greg 3.5 yval /= (double)c_y31.ssum;
179 greg 3.3 scale = C_CMAXV / scale;
180     clr->ssum = 0; /* convert to our spacing */
181     wl0 = wlmin;
182     pos = 0;
183     for (i = 0, wl = C_CMINWL; i < C_CNSS; i++, wl += C_CWLI)
184     if ((wl < wlmin) | (wl > wlmax))
185     clr->ssamp[i] = 0;
186     else {
187     while (wl0 + wlstep < wl+1e-7) {
188     wl0 += wlstep;
189     pos++;
190     }
191     if ((wl+1e-7 >= wl0) & (wl-1e-7 <= wl0))
192 greg 3.9 clr->ssamp[i] = scale*va[pos] + frand();
193 greg 3.3 else /* interpolate if necessary */
194 greg 3.9 clr->ssamp[i] = frand() + scale / wlstep *
195 greg 3.3 ( va[pos]*(wl0+wlstep - wl) +
196     va[pos+1]*(wl - wl0) );
197     clr->ssum += clr->ssamp[i];
198     }
199     clr->flags = C_CDSPEC|C_CSSPEC;
200     return(yval);
201     }
202    
203 greg 3.1 /* check if color is grey */
204     int
205     c_isgrey(C_COLOR *clr)
206     {
207     if (!(clr->flags & (C_CSXY|C_CSSPEC)))
208     return(1); /* no settings == grey */
209    
210     c_ccvt(clr, C_CSXY);
211    
212     return(clr->cx >= .323 && clr->cx <= .343 &&
213     clr->cy >= .323 && clr->cy <= .343);
214     }
215    
216     /* convert color representations */
217     void
218     c_ccvt(C_COLOR *clr, int fl)
219     {
220     double x, y, z;
221     int i;
222    
223     fl &= ~clr->flags; /* ignore what's done */
224     if (!fl) /* everything's done! */
225     return;
226     if (!(clr->flags & (C_CSXY|C_CSSPEC))) {
227     *clr = c_dfcolor; /* nothing was set! */
228     return;
229     }
230     if (fl & C_CSXY) { /* cspec -> cxy */
231     x = y = z = 0.;
232     for (i = 0; i < C_CNSS; i++) {
233 greg 3.5 x += c_x31.ssamp[i] * clr->ssamp[i];
234     y += c_y31.ssamp[i] * clr->ssamp[i];
235     z += c_z31.ssamp[i] * clr->ssamp[i];
236 greg 3.1 }
237 greg 3.5 x /= (double)c_x31.ssum;
238     y /= (double)c_y31.ssum;
239     z /= (double)c_z31.ssum;
240 greg 3.1 z += x + y;
241     clr->cx = x / z;
242     clr->cy = y / z;
243     clr->flags |= C_CSXY;
244 greg 3.6 }
245     if (fl & C_CSSPEC) { /* cxy -> cspec */
246 greg 3.1 x = clr->cx;
247     y = clr->cy;
248     z = 1. - x - y;
249     clr->ssum = 0;
250     for (i = 0; i < C_CNSS; i++) {
251     clr->ssamp[i] = x*cie_xp.ssamp[i] + y*cie_yp.ssamp[i]
252 greg 3.9 + z*cie_zp.ssamp[i] + frand();
253 greg 3.1 if (clr->ssamp[i] < 0) /* out of gamut! */
254     clr->ssamp[i] = 0;
255     else
256     clr->ssum += clr->ssamp[i];
257     }
258     clr->flags |= C_CSSPEC;
259     }
260     if (fl & C_CSEFF) { /* compute efficacy */
261     if (clr->flags & C_CSSPEC) { /* from spectrum */
262     y = 0.;
263     for (i = 0; i < C_CNSS; i++)
264 greg 3.5 y += c_y31.ssamp[i] * clr->ssamp[i];
265 greg 3.1 clr->eff = C_CLPWM * y / clr->ssum;
266     } else /* clr->flags & C_CSXY */ { /* from (x,y) */
267 greg 3.5 clr->eff = clr->cx*c_x31.eff + clr->cy*c_y31.eff +
268     (1. - clr->cx - clr->cy)*c_z31.eff;
269 greg 3.1 }
270     clr->flags |= C_CSEFF;
271     }
272     }
273    
274 greg 3.2 /* mix two colors according to weights given -- cres may be c1 or c2 */
275 greg 3.1 void
276     c_cmix(C_COLOR *cres, double w1, C_COLOR *c1, double w2, C_COLOR *c2)
277     {
278     double scale;
279     int i;
280    
281     if ((c1->flags|c2->flags) & C_CDSPEC) { /* spectral mixing */
282 greg 3.2 float cmix[C_CNSS];
283    
284 greg 3.1 c_ccvt(c1, C_CSSPEC|C_CSEFF);
285     c_ccvt(c2, C_CSSPEC|C_CSEFF);
286     w1 /= c1->eff*c1->ssum;
287     w2 /= c2->eff*c2->ssum;
288     scale = 0.;
289     for (i = 0; i < C_CNSS; i++) {
290     cmix[i] = w1*c1->ssamp[i] + w2*c2->ssamp[i];
291     if (cmix[i] > scale)
292     scale = cmix[i];
293 greg 3.3 else if (cmix[i] < -scale)
294     scale = -cmix[i];
295 greg 3.1 }
296     scale = C_CMAXV / scale;
297     cres->ssum = 0;
298     for (i = 0; i < C_CNSS; i++)
299 greg 3.9 cres->ssum += cres->ssamp[i] = scale*cmix[i] + frand();
300 greg 3.1 cres->flags = C_CDSPEC|C_CSSPEC;
301     } else { /* CIE xy mixing */
302     c_ccvt(c1, C_CSXY);
303     c_ccvt(c2, C_CSXY);
304     w1 *= c2->cy;
305     w2 *= c1->cy;
306     scale = w1 + w2;
307     if (scale == 0.) {
308     *cres = c_dfcolor;
309     return;
310     }
311     scale = 1. / scale;
312     cres->cx = (w1*c1->cx + w2*c2->cx) * scale;
313     cres->cy = (w1*c1->cy + w2*c2->cy) * scale;
314     cres->flags = C_CDXY|C_CSXY;
315     }
316     }
317    
318 greg 3.2 /* multiply two colors -- cres may be c1 or c2 */
319     double
320     c_cmult(C_COLOR *cres, C_COLOR *c1, double y1, C_COLOR *c2, double y2)
321     {
322     double yres;
323     int i;
324    
325     if ((c1->flags|c2->flags) & C_CDSPEC) {
326     long cmix[C_CNSS], cmax = 0; /* spectral multiply */
327    
328     c_ccvt(c1, C_CSSPEC|C_CSEFF);
329     c_ccvt(c2, C_CSSPEC|C_CSEFF);
330     for (i = 0; i < C_CNSS; i++) {
331     cmix[i] = c1->ssamp[i] * c2->ssamp[i];
332     if (cmix[i] > cmax)
333     cmax = cmix[i];
334 greg 3.3 else if (cmix[i] < -cmax)
335     cmax = -cmix[i];
336 greg 3.2 }
337     cmax /= C_CMAXV;
338     if (!cmax) {
339     *cres = c_dfcolor;
340     return(0.);
341     }
342     cres->ssum = 0;
343     for (i = 0; i < C_CNSS; i++)
344     cres->ssum += cres->ssamp[i] = cmix[i] / cmax;
345     cres->flags = C_CDSPEC|C_CSSPEC;
346    
347 greg 3.6 c_ccvt(cres, C_CSEFF); /* below is correct */
348 greg 3.5 yres = y1 * y2 * c_y31.ssum * C_CLPWM /
349 greg 3.2 (c1->eff*c1->ssum * c2->eff*c2->ssum) *
350     cres->eff*( cres->ssum*(double)cmax +
351     C_CNSS/2.0*(cmax-1) );
352     } else {
353     float rgb1[3], rgb2[3]; /* CIE xy multiply */
354    
355     c_toSharpRGB(c1, y1, rgb1);
356     c_toSharpRGB(c2, y2, rgb2);
357    
358     rgb2[0] *= rgb1[0]; rgb2[1] *= rgb1[1]; rgb2[2] *= rgb1[2];
359    
360     yres = c_fromSharpRGB(rgb2, cres);
361     }
362     return(yres);
363     }
364    
365 greg 3.1 #define C1 3.741832e-16 /* W-m^2 */
366     #define C2 1.4388e-2 /* m-K */
367    
368     #define bbsp(l,t) (C1/((l)*(l)*(l)*(l)*(l)*(exp(C2/((t)*(l)))-1.)))
369 greg 3.2 #define bblm(t) (C2*0.2/(t))
370 greg 3.1
371     /* set black body spectrum */
372     int
373     c_bbtemp(C_COLOR *clr, double tk)
374     {
375     double sf, wl;
376     int i;
377    
378     if (tk < 1000)
379     return(0);
380     wl = bblm(tk); /* scalefactor based on peak */
381     if (wl < C_CMINWL*1e-9)
382     wl = C_CMINWL*1e-9;
383     else if (wl > C_CMAXWL*1e-9)
384     wl = C_CMAXWL*1e-9;
385     sf = C_CMAXV/bbsp(wl,tk);
386     clr->ssum = 0;
387     for (i = 0; i < C_CNSS; i++) {
388     wl = (C_CMINWL + i*C_CWLI)*1e-9;
389 greg 3.9 clr->ssum += clr->ssamp[i] = sf*bbsp(wl,tk) + frand();
390 greg 3.1 }
391     clr->flags = C_CDSPEC|C_CSSPEC;
392     return(1);
393     }
394    
395     #undef C1
396     #undef C2
397     #undef bbsp
398     #undef bblm
399 greg 3.7
400     #define UV_NORMF 410.
401    
402     /* encode (x,y) chromaticity */
403     C_CHROMA
404     c_encodeChroma(C_COLOR *clr)
405     {
406     double df;
407     int ub, vb;
408    
409     c_ccvt(clr, C_CSXY);
410     df = UV_NORMF/(-2.*clr->cx + 12.*clr->cy + 3.);
411 greg 3.9 ub = 4.*clr->cx*df + frand();
412     if (ub > 0xff) ub = 0xff;
413 greg 3.10 else ub *= (ub > 0);
414 greg 3.9 vb = 9.*clr->cy*df + frand();
415     if (vb > 0xff) vb = 0xff;
416 greg 3.10 else vb *= (vb > 0);
417 greg 3.7
418     return(vb<<8 | ub);
419     }
420    
421     /* decode (x,y) chromaticity */
422     void
423     c_decodeChroma(C_COLOR *cres, C_CHROMA ccode)
424     {
425 greg 3.9 double up = (ccode & 0xff)*(1./UV_NORMF);
426     double vp = (ccode>>8 & 0xff)*(1./UV_NORMF);
427 greg 3.7 double df = 1./(6.*up - 16.*vp + 12.);
428    
429     cres->cx = 9.*up * df;
430     cres->cy = 4.*vp * df;
431     cres->flags = C_CDXY|C_CSXY;
432     }
433    
434     #undef UV_NORMF