1 // Copyright Contributors to the Open Shading Language project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4 
5 
6 /////////////////////////////////////////////////////////////////////////
7 /// \file
8 ///
9 /// Shader interpreter implementation of color operations.
10 ///
11 /////////////////////////////////////////////////////////////////////////
12 
13 #include "oslexec_pvt.h"
14 #include <OSL/dual.h>
15 #include <OSL/dual_vec.h>
16 #include <OSL/Imathx/Imathx.h>
17 #include <OSL/device_string.h>
18 
19 #include <OpenImageIO/fmath.h>
20 
21 #if defined(_MSC_VER) && _MSC_VER < 1800
22 using OIIO::expm1;
23 using OIIO::cbrtf;
24 #endif
25 
26 #ifdef __CUDACC__
27   #include <optix.h>
28 #endif
29 
30 #include "opcolor.h"
31 
32 OSL_NAMESPACE_ENTER
33 
34 namespace pvt {
35 
36 // CIE colour matching functions xBar, yBar, and zBar for
37 //   wavelengths from 380 through 780 nanometers, every 5
38 //   nanometers.  For a wavelength lambda in this range:
39 //        cie_colour_match[(lambda - 380) / 5][0] = xBar
40 //        cie_colour_match[(lambda - 380) / 5][1] = yBar
41 //        cie_colour_match[(lambda - 380) / 5][2] = zBar
42 OSL_CONSTANT_DATA const static float cie_colour_match[81][3] =
43 {
44     {0.0014,0.0000,0.0065}, {0.0022,0.0001,0.0105}, {0.0042,0.0001,0.0201},
45     {0.0076,0.0002,0.0362}, {0.0143,0.0004,0.0679}, {0.0232,0.0006,0.1102},
46     {0.0435,0.0012,0.2074}, {0.0776,0.0022,0.3713}, {0.1344,0.0040,0.6456},
47     {0.2148,0.0073,1.0391}, {0.2839,0.0116,1.3856}, {0.3285,0.0168,1.6230},
48     {0.3483,0.0230,1.7471}, {0.3481,0.0298,1.7826}, {0.3362,0.0380,1.7721},
49     {0.3187,0.0480,1.7441}, {0.2908,0.0600,1.6692}, {0.2511,0.0739,1.5281},
50     {0.1954,0.0910,1.2876}, {0.1421,0.1126,1.0419}, {0.0956,0.1390,0.8130},
51     {0.0580,0.1693,0.6162}, {0.0320,0.2080,0.4652}, {0.0147,0.2586,0.3533},
52     {0.0049,0.3230,0.2720}, {0.0024,0.4073,0.2123}, {0.0093,0.5030,0.1582},
53     {0.0291,0.6082,0.1117}, {0.0633,0.7100,0.0782}, {0.1096,0.7932,0.0573},
54     {0.1655,0.8620,0.0422}, {0.2257,0.9149,0.0298}, {0.2904,0.9540,0.0203},
55     {0.3597,0.9803,0.0134}, {0.4334,0.9950,0.0087}, {0.5121,1.0000,0.0057},
56     {0.5945,0.9950,0.0039}, {0.6784,0.9786,0.0027}, {0.7621,0.9520,0.0021},
57     {0.8425,0.9154,0.0018}, {0.9163,0.8700,0.0017}, {0.9786,0.8163,0.0014},
58     {1.0263,0.7570,0.0011}, {1.0567,0.6949,0.0010}, {1.0622,0.6310,0.0008},
59     {1.0456,0.5668,0.0006}, {1.0026,0.5030,0.0003}, {0.9384,0.4412,0.0002},
60     {0.8544,0.3810,0.0002}, {0.7514,0.3210,0.0001}, {0.6424,0.2650,0.0000},
61     {0.5419,0.2170,0.0000}, {0.4479,0.1750,0.0000}, {0.3608,0.1382,0.0000},
62     {0.2835,0.1070,0.0000}, {0.2187,0.0816,0.0000}, {0.1649,0.0610,0.0000},
63     {0.1212,0.0446,0.0000}, {0.0874,0.0320,0.0000}, {0.0636,0.0232,0.0000},
64     {0.0468,0.0170,0.0000}, {0.0329,0.0119,0.0000}, {0.0227,0.0082,0.0000},
65     {0.0158,0.0057,0.0000}, {0.0114,0.0041,0.0000}, {0.0081,0.0029,0.0000},
66     {0.0058,0.0021,0.0000}, {0.0041,0.0015,0.0000}, {0.0029,0.0010,0.0000},
67     {0.0020,0.0007,0.0000}, {0.0014,0.0005,0.0000}, {0.0010,0.0004,0.0000},
68     {0.0007,0.0002,0.0000}, {0.0005,0.0002,0.0000}, {0.0003,0.0001,0.0000},
69     {0.0002,0.0001,0.0000}, {0.0002,0.0001,0.0000}, {0.0001,0.0000,0.0000},
70     {0.0001,0.0000,0.0000}, {0.0001,0.0000,0.0000}, {0.0000,0.0000,0.0000}
71 };
72 
73 // White point chromaticities.
74 #define IlluminantC   0.3101, 0.3162          /* For NTSC television */
75 #define IlluminantD65 0.3127, 0.3291          /* For EBU and SMPTE */
76 #define IlluminantE   0.33333333, 0.33333333  /* CIE equal-energy illuminant */
77 
78 OSL_CONSTANT_DATA const static ColorSystem::Chroma k_color_systems[11] = {
79    // Index, Name       xRed    yRed   xGreen  yGreen   xBlue  yBlue    White point
80    /* 0  Rec709    */ { 0.64,   0.33,   0.30,   0.60,   0.15,   0.06,   IlluminantD65 },
81    /* 1  sRGB      */ { 0.64,   0.33,   0.30,   0.60,   0.15,   0.06,   IlluminantD65 },
82    /* 2  NTSC      */ { 0.67,   0.33,   0.21,   0.71,   0.14,   0.08,   IlluminantC },
83    /* 3  EBU       */ { 0.64,   0.33,   0.29,   0.60,   0.15,   0.06,   IlluminantD65 },
84    /* 4  PAL       */ { 0.64,   0.33,   0.29,   0.60,   0.15,   0.06,   IlluminantD65 },
85    /* 5  SECAM     */ { 0.64,   0.33,   0.29,   0.60,   0.15,   0.06,   IlluminantD65 },
86    /* 6  SMPTE     */ { 0.630,  0.340,  0.310,  0.595,  0.155,  0.070,  IlluminantD65 },
87    /* 7  HDTV      */ { 0.670,  0.330,  0.210,  0.710,  0.150,  0.060,  IlluminantD65 },
88    /* 8  CIE       */ { 0.7355, 0.2645, 0.2658, 0.7243, 0.1669, 0.0085, IlluminantE },
89    /* 9  AdobeRGB  */ { 0.64,   0.33,   0.21,   0.71,   0.15,   0.06,   IlluminantD65 },
90    /* 10 XYZ       */ { 1.0,    0.0,    0.0,    1.0,    0.0,    0.0,    IlluminantE },
91 };
92 
93 
94 OSL_HOSTDEVICE const ColorSystem::Chroma*
fromString(StringParam colorspace)95 ColorSystem::fromString(StringParam colorspace) {
96     if (colorspace == StringParams::Rec709)
97         return &k_color_systems[0];
98     if (colorspace == StringParams::sRGB)
99         return &k_color_systems[1];
100     if (colorspace == StringParams::NTSC)
101         return &k_color_systems[2];
102     if (colorspace == StringParams::EBU)
103         return &k_color_systems[3];
104     if (colorspace == StringParams::PAL)
105         return &k_color_systems[4];
106     if (colorspace == StringParams::SECAM)
107         return &k_color_systems[5];
108     if (colorspace == StringParams::SMPTE)
109         return &k_color_systems[6];
110     if (colorspace == StringParams::HDTV)
111         return &k_color_systems[7];
112     if (colorspace == StringParams::CIE)
113         return &k_color_systems[8];
114     if (colorspace == StringParams::AdobeRGB)
115         return &k_color_systems[9];
116     if (colorspace == StringParams::XYZ)
117         return &k_color_systems[10];
118     return nullptr;
119 }
120 
121 
122 
123 namespace {
124 
125 
126 template <typename COLOR3> OSL_HOSTDEVICE
127 static COLOR3
hsv_to_rgb(const COLOR3 & hsv)128 hsv_to_rgb (const COLOR3& hsv)
129 {
130     // Reference for this technique: Foley & van Dam
131     using FLOAT = typename ScalarFromVec<COLOR3>::type;
132     FLOAT h = comp_x(hsv), s = comp_y(hsv), v = comp_z(hsv);
133     if (s < 0.0001f) {
134       return make_Color3 (v, v, v);
135     } else {
136         using std::floor;   // to pick up the float one
137         using OIIO::ifloor;
138         h = 6.0f * (h - floor(h));  // expand to [0..6)
139         int hi = ifloor(h);
140         FLOAT f = h - FLOAT(hi);
141         FLOAT p = v * (1.0f-s);
142         FLOAT q = v * (1.0f-s*f);
143         FLOAT t = v * (1.0f-s*(1.0f-f));
144         switch (hi) {
145         case 0 : return make_Color3 (v, t, p);
146         case 1 : return make_Color3 (q, v, p);
147         case 2 : return make_Color3 (p, v, t);
148         case 3 : return make_Color3 (p, q, v);
149         case 4 : return make_Color3 (t, p, v);
150         default: return make_Color3 (v, p, q);
151 	    }
152     }
153 }
154 
155 
156 template <typename COLOR3> OSL_HOSTDEVICE
157 static COLOR3
rgb_to_hsv(const COLOR3 & rgb)158 rgb_to_hsv (const COLOR3& rgb)
159 {
160     // See Foley & van Dam
161     using FLOAT = typename ScalarFromVec<COLOR3>::type;
162     FLOAT r = comp_x(rgb), g = comp_y(rgb), b = comp_z(rgb);
163     FLOAT mincomp = std::min (r, std::min (g, b));
164     FLOAT maxcomp = std::max (r, std::max (g, b));
165     FLOAT delta = maxcomp - mincomp;  // chroma
166     FLOAT h, s, v;
167     v = maxcomp;
168     if (maxcomp > 0.0f)
169         s = delta / maxcomp;
170     else s = 0.0f;
171     if (s <= 0.0f)
172         h = 0.0f;
173     else {
174         if      (r >= maxcomp) h = (g-b) / delta;
175         else if (g >= maxcomp) h = 2.0f + (b-r) / delta;
176         else                   h = 4.0f + (r-g) / delta;
177         h *= (1.0f/6.0f);
178         if (h < 0.0f)
179             h += 1.0f;
180     }
181     return make_Color3 (h, s, v);
182 }
183 
184 
185 
186 template <typename COLOR3> OSL_HOSTDEVICE
187 static COLOR3
hsl_to_rgb(const COLOR3 & hsl)188 hsl_to_rgb (const COLOR3& hsl)
189 {
190     using FLOAT = typename ScalarFromVec<COLOR3>::type;
191     FLOAT h = comp_x(hsl), s = comp_y(hsl), l = comp_z(hsl);
192     // Easiest to convert hsl -> hsv, then hsv -> RGB (per Foley & van Dam)
193     FLOAT v = (l <= 0.5f) ? (l * (1.0f + s)) : (l * (1.0f - s) + s);
194     if (v <= 0.0f) {
195         return make_Color3 (0.0f, 0.0f, 0.0f);
196     } else {
197         FLOAT min = 2.0f * l - v;
198         s = (v - min) / v;
199         return hsv_to_rgb (make_Color3(h, s, v));
200     }
201 }
202 
203 
204 
205 template <typename COLOR3> OSL_HOSTDEVICE
206 static COLOR3
rgb_to_hsl(const COLOR3 & rgb)207 rgb_to_hsl (const COLOR3& rgb)
208 {
209     // See Foley & van Dam
210     // First convert rgb to hsv, then to hsl
211     using FLOAT = typename ScalarFromVec<COLOR3>::type;
212     FLOAT minval = std::min (comp_x(rgb), std::min (comp_y(rgb), comp_z(rgb)));
213     COLOR3 hsv = rgb_to_hsv (rgb);
214     FLOAT maxval = comp_z(hsv);   // v == maxval
215     FLOAT h = comp_x(hsv), s, l = (minval+maxval) / 2.0f;
216     if (equalVal (minval, maxval))
217         s = 0.0f;  // special 'achromatic' case, hue is 0
218     else if (l <= 0.5f)
219         s = (maxval - minval) / (maxval + minval);
220     else
221         s = (maxval - minval) / (2.0f - maxval - minval);
222     return make_Color3 (h, s, l);
223 }
224 
225 
226 
227 template <typename COLOR3> OSL_HOSTDEVICE
228 static COLOR3
YIQ_to_rgb(const COLOR3 & YIQ)229 YIQ_to_rgb (const COLOR3& YIQ)
230 {
231     return YIQ * Matrix33(1.0000,  1.0000,  1.0000,
232                           0.9557, -0.2716, -1.1082,
233                           0.6199, -0.6469,  1.7051);
234 }
235 
236 
237 template <typename COLOR3> OSL_HOSTDEVICE
238 static COLOR3
rgb_to_YIQ(const COLOR3 & rgb)239 rgb_to_YIQ (const COLOR3& rgb)
240 {
241     return rgb * Matrix33(0.299,  0.596,  0.212,
242                           0.587, -0.275, -0.523,
243                           0.114, -0.321,  0.311);
244 }
245 
246 
247 
248 #if 0
249 OSL_HOSTDEVICE inline Color3
250 XYZ_to_xyY (const Color3 &XYZ)
251 {
252     float n = (XYZ[0] + XYZ[1] + XYZ[2]);
253     float n_inv = (n >= 1.0e-6 ? 1.0f/n : 0.0f);
254     return Color3 (XYZ[0]*n_inv, XYZ[1]*n_inv, XYZ[1]);
255     // N.B. http://brucelindbloom.com/ suggests returning xy of the
256     // reference white in the X+Y+Z==0 case.
257 }
258 #endif
259 
260 
261 template <typename COLOR3> OSL_HOSTDEVICE
262 static COLOR3
xyY_to_XYZ(const COLOR3 & xyY)263 xyY_to_XYZ (const COLOR3 &xyY)
264 {
265     using FLOAT = typename ScalarFromVec<COLOR3>::type;
266     FLOAT Y = comp_z(xyY);
267     FLOAT Y_y = (comp_y(xyY) > 1.0e-6f ? Y/comp_y(xyY) : 0.0f);
268     FLOAT X = Y_y * comp_x(xyY);
269     FLOAT Z = Y_y * (1.0f - comp_x(xyY) - comp_y(xyY));
270     return make_Color3 (X, Y, Z);
271 }
272 
273 
274 
275 template <typename COLOR3> OSL_HOSTDEVICE
276 static COLOR3
sRGB_to_linear(const COLOR3 & srgb)277 sRGB_to_linear (const COLOR3& srgb)
278 {
279     // See Foley & van Dam
280     using FLOAT = typename ScalarFromVec<COLOR3>::type;
281     using namespace OIIO;
282     //using safe_pow = std::conditional<is_Dual<COLOR3>::value, OSL::safe_pow, OIIO::safe_pow>::type;
283     FLOAT r = comp_x(srgb), g = comp_y(srgb), b = comp_z(srgb);
284     auto convert = [] (FLOAT x) -> FLOAT {
285         return (x <= 0.04045f) ?
286                      (x * (1.0f / 12.92f)) :
287                      safe_pow((x + 0.055f) * (1.0f / 1.055f), FLOAT(2.4f));
288     };
289     return make_Color3 (convert(r), convert(g), convert(b));
290 }
291 
292 
293 
294 template <typename COLOR3> OSL_HOSTDEVICE
295 static COLOR3
linear_to_sRGB(const COLOR3 & rgb)296 linear_to_sRGB (const COLOR3& rgb)
297 {
298     // See Foley & van Dam
299     using FLOAT = typename ScalarFromVec<COLOR3>::type;
300     using namespace OIIO;
301     //using safe_pow = std::conditional<is_Dual<COLOR3>::value, OSL::safe_pow, OIIO::safe_pow>::type;
302     FLOAT r = comp_x(rgb), g = comp_y(rgb), b = comp_z(rgb);
303     auto convert = [] (FLOAT x) -> FLOAT {
304         return (x <= 0.0031308f) ?
305                       (12.92f * x)           :
306                       (1.055f * safe_pow(x, FLOAT(1.f / 2.4f)) - 0.055f);
307     };
308 
309     return make_Color3 (convert(r), convert(g), convert(b));
310 }
311 
312 
313 
314 
315 // Spectral rendering routines inspired by those found at:
316 //   http://www.fourmilab.ch/documents/specrend/specrend.c
317 // which bore the notice:
318 //                Colour Rendering of Spectra
319 //                     by John Walker
320 //                  http://www.fourmilab.ch/
321 //         Last updated: March 9, 2003
322 //           This program is in the public domain.
323 //    For complete information about the techniques employed in
324 //    this program, see the World-Wide Web document:
325 //             http://www.fourmilab.ch/documents/specrend/
326 
327 
328 
329 // Functor that calculates, by Planck's radiation law, the black body
330 // emittance at temperature (in Kelvin) and given wavelength (in nm).
331 // This is the differential (per unit of wavelength) flux density, in
332 // W/m^2 in the range [wavelength,wavelength+dwavelength].
333 class bb_spectrum {
334 public:
bb_spectrum(float temperature=5000)335     OSL_HOSTDEVICE bb_spectrum (float temperature=5000) : m_temp(temperature) { }
operator ()(float wavelength_nm) const336     OSL_HOSTDEVICE float operator() (float wavelength_nm) const {
337         double wlm = wavelength_nm * 1e-9;   // Wavelength in meters
338         const double c1 = 3.74183e-16; // 2*pi*h*c^2, W*m^2
339         const double c2 = 1.4388e-2;   // h*c/k, m*K
340                                        // h is Planck's const, k is Boltzmann's
341         return float((c1 * std::pow(wlm,-5.0)) / ::expm1(c2 / (wlm * m_temp)));
342     }
343 private:
344     double m_temp;
345 };
346 
347 
348 
349 // For a given wavelength lambda (in nm), return the XYZ triple giving the
350 // XYZ color corresponding to that single wavelength;
351 OSL_HOSTDEVICE static Color3
wavelength_color_XYZ(float lambda_nm)352 wavelength_color_XYZ (float lambda_nm)
353 {
354     float ii = (lambda_nm-380.0f) / 5.0f;  // scaled 0..80
355     int i = (int) ii;
356     if (i < 0 || i >= 80)
357         return Color3(0.0f,0.0f,0.0f);
358     ii -= i;
359     const float *c = cie_colour_match[i];
360     Color3 XYZ = OIIO::lerp (Color3(c[0], c[1], c[2]),
361                              Color3(c[3], c[4], c[5]), ii);
362 #if 0
363     float n = (XYZ[0] + XYZ[1] + XYZ[2]);
364     float n_inv = (n >= 1.0e-6f ? 1.0f/n : 0.0f);
365     XYZ *= n_inv;
366 #endif
367     return XYZ;
368 }
369 
370 
371 
372 // Integrate the CIE color matching values, weighted by function
373 // spec_intens(lambda_nm), returning the aggregate XYZ color.
374 template<class SPECTRUM> OSL_HOSTDEVICE
375 static Color3
spectrum_to_XYZ(const SPECTRUM & spec_intens)376 spectrum_to_XYZ (const SPECTRUM &spec_intens)
377 {
378     float X = 0, Y = 0, Z = 0;
379     const float dlambda = 5.0f * 1e-9;  // in meters
380     for (int i = 0; i < 81; ++i) {
381         float lambda = 380.0f + 5.0f * i;
382         // N.B. spec_intens returns result in W/m^2 but it's a differential,
383         // needs to be scaled by dlambda!
384         float Me = spec_intens(lambda) * dlambda;
385         X += Me * cie_colour_match[i][0];
386         Y += Me * cie_colour_match[i][1];
387         Z += Me * cie_colour_match[i][2];
388     }
389     return Color3 (X, Y, Z);
390 }
391 
392 
393 #if 0
394 // If the requested RGB shade contains a negative weight for one of the
395 // primaries, it lies outside the colour gamut accessible from the given
396 // triple of primaries.  Desaturate it by adding white, equal quantities
397 // of R, G, and B, enough to make RGB all positive.  The function
398 // returns true if the components were modified, zero otherwise.
399 OSL_HOSTDEVICE inline bool
400 constrain_rgb (Color3 &rgb)
401 {
402     // Amount of white needed is w = - min(0,r,g,b)
403     float w = 0.0f;
404     w = (0 < rgb.x) ? w : rgb.x;
405     w = (w < rgb.y) ? w : rgb.y;
406     w = (w < rgb.z) ? w : rgb.z;
407     w = -w;
408 
409     // Add just enough white to make r, g, b all positive.
410     if (w > 0) {
411         rgb.x += w;  rgb.y += w;  rgb.z += w;
412         return true;   // Color modified to fit RGB gamut
413     }
414 
415     return false;  // color was within RGB gamut
416 }
417 
418 
419 
420 // Rescale rgb so its largest component is 1.0, and return the original
421 // largest component.
422 OSL_HOSTDEVICE inline float
423 norm_rgb (Color3 &rgb)
424 {
425     float greatest = std::max(rgb.x, std::max(rgb.y, rgb.z));
426     if (greatest > 1.0e-12f)
427         rgb *= 1.0f/greatest;
428     return greatest;
429 }
430 #endif
431 
432 
clamp_zero(Color3 & c)433 OSL_HOSTDEVICE inline void clamp_zero (Color3 &c)
434 {
435     if (c.x < 0.0f)
436         c.x = 0.0f;
437     if (c.y < 0.0f)
438         c.y = 0.0f;
439     if (c.z < 0.0f)
440         c.z = 0.0f;
441 }
442 
443 
444 
445 OSL_HOSTDEVICE inline Color3
colpow(const Color3 & c,float p)446 colpow (const Color3 &c, float p)
447 {
448     return Color3 (powf(c.x,p), powf(c.y,p), powf(c.z,p));
449 }
450 
451 
452 };  // End anonymous namespace
453 
454 
455 
456 // In order to speed up the blackbody computation, we have a table
457 // storing the precomputed BB values for a range of temperatures.  Less
458 // than BB_DRAPER always returns 0.  Greater than BB_MAX_TABLE_RANGE
459 // does the full computation, we think it'll be rare to inquire higher
460 // temperatures.
461 //
462 // Since the bb function is so nonlinear, we actually space the table
463 // entries nonlinearly, with the relationship between the table index i
464 // and the temperature T as follows:
465 //   i = ((T-Draper)/spacing)^(1/xpower)
466 //   T = pow(i, xpower) * spacing + Draper
467 // And furthermore, we store in the table the true value raised ^(1/5).
468 // I tuned this a bit, and with the current values we can have all
469 // blackbody results accurate to within 0.1% with a table size of 317
470 // (about 5 KB of data).
471 #define BB_DRAPER 800.0f /* really 798K, below this visible BB is negligible */
472 #define BB_MAX_TABLE_RANGE 12000.0f /* max temp for which we use the table */
473 #define BB_TABLE_XPOWER 1.5f       // NOTE: not used, hardcoded into expressions below
474 #define BB_TABLE_YPOWER 5.0f       // NOTE: decode is hardcoded
475 #define BB_TABLE_SPACING 2.0f
476 
BB_TABLE_MAP(float i)477 OSL_HOSTDEVICE inline float BB_TABLE_MAP(float i) {
478     // return powf (i, BB_TABLE_XPOWER) * BB_TABLE_SPACING + BB_DRAPER;
479     float is = sqrtf(i);
480     float ip = is * is * is; // ^3/2
481     return ip * BB_TABLE_SPACING + BB_DRAPER;
482 }
483 
BB_TABLE_UNMAP(float T)484 OSL_HOSTDEVICE inline float BB_TABLE_UNMAP(float T) {
485     // return powf ((T - BB_DRAPER) / BB_TABLE_SPACING, 1.0f/BB_TABLE_XPOWER);
486     float t  = (T - BB_DRAPER) / BB_TABLE_SPACING;
487     float ic = cbrtf(t);
488     return ic * ic; // ^2/3
489 }
490 
491 
492 OSL_HOSTDEVICE bool
set_colorspace(StringParam colorspace)493 ColorSystem::set_colorspace (StringParam colorspace)
494 {
495     if (colorspace == m_colorspace)
496         return true;
497 
498     const Chroma* chroma = fromString(colorspace);
499     if (!chroma)
500         return false;
501 
502     // Record the current colorspace
503     m_colorspace = colorspace;
504 
505     m_Red.setValue (chroma->xRed, chroma->yRed, 0.0f);
506     m_Green.setValue (chroma->xGreen, chroma->yGreen, 0.0f);
507     m_Blue.setValue (chroma->xBlue, chroma->yBlue, 0.0f);
508     m_White.setValue (chroma->xWhite, chroma->yWhite, 0.0f);
509     // set z values to normalize
510     m_Red.z   = 1.0f - (m_Red.x   + m_Red.y);
511     m_Green.z = 1.0f - (m_Green.x + m_Green.y);
512     m_Blue.z  = 1.0f - (m_Blue.x  + m_Blue.y);
513     m_White.z = 1.0f - (m_White.x + m_White.y);
514 
515     const Color3 &R(m_Red), &G(m_Green), &B(m_Blue), &W(m_White);
516     // xyz -> rgb matrix, before scaling to white.
517     Color3 r (G.y*B.z - B.y*G.z, B.x*G.z - G.x*B.z, G.x*B.y - B.x*G.y);
518     Color3 g (B.y*R.z - R.y*B.z, R.x*B.z - B.x*R.z, B.x*R.y - R.x*B.y);
519     Color3 b (R.y*G.z - G.y*R.z, G.x*R.z - R.x*G.z, R.x*G.y - G.x*R.y);
520     Color3 w (r.dot(W), g.dot(W), b.dot(W));  // White scaling factor
521     if (W.y != 0.0f)  // divide by W.y to scale luminance to 1.0
522         w *= 1.0f/W.y;
523     // xyz -> rgb matrix, correctly scaled to white.
524     r /= w.x;
525     g /= w.y;
526     b /= w.z;
527     m_XYZ2RGB = Matrix33 (r.x, g.x, b.x,
528                           r.y, g.y, b.y,
529                           r.z, g.z, b.z);
530     m_RGB2XYZ = m_XYZ2RGB.inverse();
531     m_luminance_scale = Color3 (m_RGB2XYZ.x[0][1], m_RGB2XYZ.x[1][1], m_RGB2XYZ.x[2][1]);
532 
533     // Mathematical imprecision can lead to the luminance scale not
534     // quite summing to 1.0.  If it's very close, adjust to make it
535     // exact.
536     float lum2 = (1.0f - m_luminance_scale.x - m_luminance_scale.y);
537     if (fabsf(lum2 - m_luminance_scale.z) < 0.001f)
538         m_luminance_scale.z = lum2;
539 
540     // Precompute a table of blackbody values
541     // FIXME: With c++14 and constexpr cbrtf, this could be static_assert
542     assert( std::ceil(BB_TABLE_UNMAP(BB_MAX_TABLE_RANGE)) <
543             std::extent<decltype(m_blackbody_table)>::value);
544 
545     float lastT = 0;
546     for (int i = 0;  lastT <= BB_MAX_TABLE_RANGE; ++i) {
547         float T = BB_TABLE_MAP(float(i));
548         lastT = T;
549         bb_spectrum spec (T);
550         Color3 rgb = XYZ_to_RGB (spectrum_to_XYZ (spec));
551         clamp_zero (rgb);
552         rgb = colpow (rgb, 1.0f/BB_TABLE_YPOWER);
553         m_blackbody_table[i] = rgb;
554         // std::cout << "Table[" << i << "; T=" << T << "] = " << rgb << "\n";
555     }
556 
557 #if 0 && !defined(__CUDACC__)
558     std::cout << "Made " << m_blackbody_table.size() << " table entries for blackbody\n";
559 
560     // Sanity checks
561     std::cout << "m_XYZ2RGB = " << m_XYZ2RGB << "\n";
562     std::cout << "m_RGB2XYZ = " << m_RGB2XYZ << "\n";
563     std::cout << "m_luminance_scale = " << m_luminance_scale << "\n";
564 #endif
565     return true;
566 }
567 
568 // For Optix, this will be defined by the renderer. Otherwise inline a getter.
569 #ifdef __CUDACC__
570     extern "C"  __device__
571     void osl_printf (void* sg_, char* fmt_str, void* args) __attribute__((weak));
572 
573     extern "C" __device__ int
574     rend_get_userdata (StringParam name, void* data, int data_size,
575                        const OSL::TypeDesc& type, int index) __attribute__((weak));
576 
577     OSL_HOSTDEVICE void
error(StringParam src,StringParam dst,Context sg)578     ColorSystem::error(StringParam src, StringParam dst, Context sg) {
579         const char* args[2] = { src.c_str(), dst.c_str() };
580         osl_printf (sg,
581             (char*) // FIXME!
582             "ERROR: Unknown color space transformation \"%s\" -> \"%s\"\n",
583             args);
584     }
585 
op_color_colorsystem(void * sg)586     __device__ static inline ColorSystem& op_color_colorsystem (void *sg) {
587         void* ptr;
588         rend_get_userdata(StringParams::colorsystem, &ptr, 8, OSL::TypeDesc::PTR, 0);
589         return *((ColorSystem*)ptr);
590     }
591 
op_color_context(void * sg)592     __device__ static inline void* op_color_context (void *sg) {
593         return sg;
594     }
595 #else
596     void
error(StringParam src,StringParam dst,Context context)597     ColorSystem::error(StringParam src, StringParam dst, Context context) {
598         context->errorf("Unknown color space transformation"
599                         " \"%s\" -> \"%s\"", src, dst);
600     }
601 
op_color_colorsystem(void * sg)602     static inline ColorSystem& op_color_colorsystem (void *sg) {
603         return ((ShaderGlobals *)sg)->context->shadingsys().colorsystem();
604     }
605 
op_color_context(void * sg)606     static inline OSL::ShadingContext* op_color_context (void *sg) {
607         return (ShadingContext*)((ShaderGlobals *)sg)->context;
608     }
609 #endif
610 
611 
612 
613 template <typename Color> OSL_HOSTDEVICE Color
ocio_transform(StringParam fromspace,StringParam tospace,const Color & C,Context ctx)614 ColorSystem::ocio_transform (StringParam fromspace, StringParam tospace,
615                              const Color& C, Context ctx)
616 {
617 #if OIIO_HAS_COLORPROCESSOR
618     Color Cout;
619     if (ctx->shadingsys().ocio_transform(fromspace, tospace, C, Cout))
620         return Cout;
621 #endif
622 
623     error (fromspace, tospace, ctx);
624     return C;
625 }
626 
627 
628 
629 OSL_HOSTDEVICE Color3
to_rgb(StringParam fromspace,const Color3 & C,Context context)630 ColorSystem::to_rgb (StringParam fromspace, const Color3& C, Context context)
631 {
632     if (fromspace == StringParams::RGB || fromspace == StringParams::rgb
633          || fromspace == m_colorspace)
634         return C;
635     if (fromspace == StringParams::hsv)
636         return hsv_to_rgb (C);
637     if (fromspace == StringParams::hsl)
638         return hsl_to_rgb (C);
639     if (fromspace == StringParams::YIQ)
640         return YIQ_to_rgb (C);
641     if (fromspace == StringParams::XYZ)
642         return XYZ_to_RGB (C);
643     if (fromspace == StringParams::xyY)
644         return XYZ_to_RGB (xyY_to_XYZ (C));
645     else
646         return ocio_transform (fromspace, StringParams::RGB, C, context);
647 }
648 
649 
650 
651 OSL_HOSTDEVICE Color3
from_rgb(StringParam tospace,const Color3 & C,Context context)652 ColorSystem::from_rgb (StringParam tospace, const Color3& C, Context context)
653 {
654     if (tospace == StringParams::RGB || tospace == StringParams::rgb
655          || tospace == m_colorspace)
656         return C;
657     if (tospace == StringParams::hsv)
658         return rgb_to_hsv (C);
659     if (tospace == StringParams::hsl)
660         return rgb_to_hsl (C);
661     if (tospace == StringParams::YIQ)
662         return rgb_to_YIQ (C);
663     if (tospace == StringParams::XYZ)
664         return RGB_to_XYZ (C);
665     if (tospace == StringParams::xyY)
666         return RGB_to_XYZ (xyY_to_XYZ (C));
667     else
668         return ocio_transform (StringParams::RGB, tospace, C, context);
669 }
670 
671 
672 
673 template <typename COLOR> OSL_HOSTDEVICE COLOR
transformc(StringParam fromspace,StringParam tospace,const COLOR & C,Context context)674 ColorSystem::transformc (StringParam fromspace, StringParam tospace,
675                          const COLOR& C, Context context)
676 {
677     bool use_colorconfig = false;
678     COLOR Crgb;
679     if (fromspace == StringParams::RGB || fromspace == StringParams::rgb
680          || fromspace == StringParams::linear || fromspace == m_colorspace)
681         Crgb = C;
682     else if (fromspace == StringParams::hsv)
683         Crgb = hsv_to_rgb (C);
684     else if (fromspace == StringParams::hsl)
685         Crgb = hsl_to_rgb (C);
686     else if (fromspace == StringParams::YIQ)
687         Crgb = YIQ_to_rgb (C);
688     else if (fromspace == StringParams::XYZ)
689         Crgb = XYZ_to_RGB (C);
690     else if (fromspace == StringParams::xyY)
691         Crgb = XYZ_to_RGB (xyY_to_XYZ (C));
692     else if (fromspace == StringParams::sRGB)
693         Crgb = sRGB_to_linear (C);
694     else {
695         use_colorconfig = true;
696     }
697 
698     COLOR Cto;
699     if (use_colorconfig) {
700         // do things the ColorConfig way, so skip all these other clauses...
701     }
702     else if (tospace == StringParams::RGB || tospace == StringParams::rgb
703          || tospace == StringParams::linear || tospace == m_colorspace)
704         Cto = Crgb;
705     else if (tospace == StringParams::hsv)
706         Cto = rgb_to_hsv (Crgb);
707     else if (tospace == StringParams::hsl)
708         Cto = rgb_to_hsl (Crgb);
709     else if (tospace == StringParams::YIQ)
710         Cto = rgb_to_YIQ (Crgb);
711     else if (tospace == StringParams::XYZ)
712         Cto = RGB_to_XYZ (Crgb);
713     else if (tospace == StringParams::xyY)
714         Cto = RGB_to_XYZ (xyY_to_XYZ (Crgb));
715     else if (tospace == StringParams::sRGB)
716         Cto = linear_to_sRGB (Crgb);
717     else {
718         use_colorconfig = true;
719     }
720 
721     if (use_colorconfig) {
722         Cto = ocio_transform (fromspace, tospace, C, context);
723     }
724 
725     return Cto;
726 }
727 
728 
729 
730 OSL_HOSTDEVICE Dual2<Color3>
transformc(StringParam fromspace,StringParam tospace,const Dual2<Color3> & color,Context ctx)731 ColorSystem::transformc (StringParam fromspace, StringParam tospace,
732                          const Dual2<Color3>& color, Context ctx) {
733     return transformc<Dual2<Color3>>(fromspace, tospace, color, ctx);
734 }
735 
736 
737 
738 OSL_HOSTDEVICE Color3
transformc(StringParam fromspace,StringParam tospace,const Color3 & color,Context ctx)739 ColorSystem::transformc (StringParam fromspace, StringParam tospace,
740                          const Color3& color, Context ctx) {
741     return transformc<Color3>(fromspace, tospace, color, ctx);
742 }
743 
744 
745 
746 OSL_HOSTDEVICE Color3
blackbody_rgb(float T)747 ColorSystem::blackbody_rgb (float T)
748 {
749     if (T < BB_DRAPER)
750         return Color3(1.0e-6f,0.0f,0.0f);  // very very dim red
751     if (T < BB_MAX_TABLE_RANGE) {
752         float t = BB_TABLE_UNMAP(T);
753         int ti = (int)t;
754         t -= ti;
755         Color3 rgb = OIIO::lerp (m_blackbody_table[ti], m_blackbody_table[ti+1], t);
756         //return colpow(rgb, BB_TABLE_YPOWER);
757         Color3 rgb2 = rgb * rgb;
758         Color3 rgb4 = rgb2 * rgb2;
759         return rgb4 * rgb; // ^5
760     }
761     // Otherwise, compute for real
762     bb_spectrum spec (T);
763     Color3 rgb = XYZ_to_RGB (spectrum_to_XYZ (spec));
764     clamp_zero (rgb);
765     return rgb;
766 }
767 
768 
769 
770 } // namespace pvt
771 
772 
773 
osl_blackbody_vf(void * sg,void * out,float temp)774 OSL_SHADEOP OSL_HOSTDEVICE void osl_blackbody_vf (void *sg, void *out, float temp)
775 {
776     ColorSystem &cs = op_color_colorsystem(sg);
777     *(Color3 *)out = cs.blackbody_rgb (temp);
778 }
779 
780 
781 
osl_wavelength_color_vf(void * sg,void * out,float lambda)782 OSL_SHADEOP OSL_HOSTDEVICE void osl_wavelength_color_vf (void *sg, void *out, float lambda)
783 {
784     ColorSystem &cs = op_color_colorsystem(sg);
785     Color3 rgb = cs.XYZ_to_RGB (wavelength_color_XYZ (lambda));
786 //    constrain_rgb (rgb);
787     rgb *= 1.0/2.52;    // Empirical scale from lg to make all comps <= 1
788 //    norm_rgb (rgb);
789     clamp_zero (rgb);
790     *(Color3 *)out = rgb;
791 }
792 
793 
794 
osl_luminance_fv(void * sg,void * out,void * c)795 OSL_SHADEOP OSL_HOSTDEVICE void osl_luminance_fv (void *sg, void *out, void *c)
796 {
797     ColorSystem &cs = op_color_colorsystem(sg);
798     ((float *)out)[0] = cs.luminance (((const Color3 *)c)[0]);
799 }
800 
801 
802 
osl_luminance_dfdv(void * sg,void * out,void * c)803 OSL_SHADEOP OSL_HOSTDEVICE void osl_luminance_dfdv (void *sg, void *out, void *c)
804 {
805     ColorSystem &cs = op_color_colorsystem(sg);
806     ((float *)out)[0] = cs.luminance (((const Color3 *)c)[0]);
807     ((float *)out)[1] = cs.luminance (((const Color3 *)c)[1]);
808     ((float *)out)[2] = cs.luminance (((const Color3 *)c)[2]);
809 }
810 
811 
812 
813 OSL_SHADEOP OSL_HOSTDEVICE void
osl_prepend_color_from(void * sg,void * c_,const char * from)814 osl_prepend_color_from (void *sg, void *c_, const char *from)
815 {
816     ColorSystem &cs = op_color_colorsystem(sg);
817     COL(c_) = cs.to_rgb (HDSTR(from), COL(c_), op_color_context(sg));
818 }
819 
820 
821 
822 OSL_SHADEOP OSL_HOSTDEVICE int
osl_transformc(void * sg,void * Cin,int Cin_derivs,void * Cout,int Cout_derivs,void * from_,void * to_)823 osl_transformc (void *sg, void *Cin, int Cin_derivs,
824                 void *Cout, int Cout_derivs,
825                 void *from_, void *to_)
826 {
827     ColorSystem &cs = op_color_colorsystem(sg);
828     StringParam from = HDSTR(from_);
829     StringParam to = HDSTR(to_);
830 
831     if (Cout_derivs) {
832         if (Cin_derivs) {
833             DCOL(Cout) = cs.transformc (from, to, DCOL(Cin), op_color_context(sg));
834             return true;
835         } else {
836             // We had output derivs, but not input. Zero the output
837             // derivs and fall through to the non-deriv case.
838             ((Color3 *)Cout)[1].setValue (0.0f, 0.0f, 0.0f);
839             ((Color3 *)Cout)[2].setValue (0.0f, 0.0f, 0.0f);
840         }
841     }
842 
843     // No-derivs case
844     COL(Cout) = cs.transformc (from, to, COL(Cin), op_color_context(sg));
845     return true;
846 }
847 
848 
849 
850 OSL_NAMESPACE_EXIT
851