1
2//
3// This source file is part of appleseed.
4// Visit http://appleseedhq.net/ for additional information and resources.
5//
6// This software is released under the MIT license.
7//
8// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9// Copyright (c) 2014-2017 Francois Beaune, The appleseedhq Organization
10//
11// Permission is hereby granted, free of charge, to any person obtaining a copy
12// of this software and associated documentation files (the "Software"), to deal
13// in the Software without restriction, including without limitation the rights
14// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15// copies of the Software, and to permit persons to whom the Software is
16// furnished to do so, subject to the following conditions:
17//
18// The above copyright notice and this permission notice shall be included in
19// all copies or substantial portions of the Software.
20//
21// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27// THE SOFTWARE.
28//
29
30#ifndef APPLESEED_FOUNDATION_IMAGE_COLORSPACE_H
31#define APPLESEED_FOUNDATION_IMAGE_COLORSPACE_H
32
33// appleseed.foundation headers.
34#include "foundation/image/color.h"
35#include "foundation/image/regularspectrum.h"
36#include "foundation/math/fastmath.h"
37#include "foundation/math/scalar.h"
38#include "foundation/math/spline.h"
39#include "foundation/platform/compiler.h"
40#ifdef APPLESEED_USE_SSE
41#include "foundation/platform/sse.h"
42#endif
43#include "foundation/utility/otherwise.h"
44
45// appleseed.main headers.
46#include "main/dllsymbol.h"
47
48// Boost headers.
49#include "boost/static_assert.hpp"
50
51// Standard headers.
52#include <algorithm>
53#include <cmath>
54#include <cstddef>
55
56namespace foundation
57{
58
59//
60// Enumeration of all supported color spaces.
61//
62
63enum ColorSpace
64{
65 ColorSpaceLinearRGB, // linear RGB
66 ColorSpaceSRGB, // sRGB
67 ColorSpaceHSV, // Hue Saturation Value (HSV, or HSB)
68 ColorSpaceHLS, // Hue Lightness Saturation (HLS, or HSL)
69 ColorSpaceCIEXYZ, // CIE XYZ
70 ColorSpaceCIExyY, // CIE xyY
71 ColorSpaceSpectral // spectral
72};
73
74// Return a string identifying a color space.
75APPLESEED_DLLSYMBOL const char* color_space_name(const ColorSpace color_space);
76
77
78//
79// Transform a tristimulus value from one 3D color space to another 3D color space.
80//
81
82template <typename T>
83Color<T, 3> transform_color(
84 const Color<T, 3>& color,
85 const ColorSpace from,
86 const ColorSpace to);
87
88
89//
90// Basis vectors to convert the CIE xy chromaticity of a D series (daylight) illuminant to a spectrum.
91//
92// Reference:
93//
94// http://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
95//
96
97extern const RegularSpectrum31f DaylightS0; // mean spectral radiant power
98extern const RegularSpectrum31f DaylightS1; // first characteristic vector (yellow-blue variation)
99extern const RegularSpectrum31f DaylightS2; // second characteristic vector (pink-green variation)
100
101
102//
103// Standard illuminants.
104//
105// References:
106//
107// http://en.wikipedia.org/wiki/Standard_illuminant
108// http://cvrl.ioo.ucl.ac.uk/
109//
110
111// Daylight illuminants.
112extern const RegularSpectrum31f IlluminantCIED65; // CIE D65
113
114// Incandescent lighting illuminants.
115extern const RegularSpectrum31f IlluminantCIEA; // CIE A (black body radiator at 2856 K)
116
117
118//
119// Color Matching Functions (CMF).
120//
121// References:
122//
123// http://en.wikipedia.org/wiki/CIE_1931_color_space
124// http://cvrl.ioo.ucl.ac.uk/
125//
126
127// XYZ color matching functions.
128extern const RegularSpectrum31f XYZCMFCIE19312Deg[3]; // CIE 1931 2-deg
129extern const RegularSpectrum31f XYZCMFCIE1931Judd2Deg[3]; // CIE 1931 2-deg, modified by Judd (1951)
130extern const RegularSpectrum31f XYZCMFCIE1931JuddVos2Deg[3]; // CIE 1931 2-deg, modified by Judd (1951) and Vos (1978)
131extern const RegularSpectrum31f XYZCMFCIE196410Deg[3]; // CIE 1964 10-deg (recommended)
132
133// RGB color matching functions.
134extern const RegularSpectrum31f RGBCMFStilesBurch19552Deg[3]; // Stiles and Burch (1955) 2-deg
135extern const RegularSpectrum31f RGBCMFStilesBurch195910Deg[3]; // Stiles and Burch (1959) 10-deg (recommended)
136
137
138//
139// Basis spectra for RGB-to-spectrum conversion.
140//
141
142// Basis spectra for reflectance conversions.
143extern const RegularSpectrum31f RGBToSpectrumWhiteReflectance;
144extern const RegularSpectrum31f RGBToSpectrumCyanReflectance;
145extern const RegularSpectrum31f RGBToSpectrumMagentaReflectance;
146extern const RegularSpectrum31f RGBToSpectrumYellowReflectance;
147extern const RegularSpectrum31f RGBToSpectrumRedReflectance;
148extern const RegularSpectrum31f RGBToSpectrumGreenReflectance;
149extern const RegularSpectrum31f RGBToSpectrumBlueReflectance;
150
151// Basis spectra for illuminance conversions.
152extern const RegularSpectrum31f RGBToSpectrumWhiteIlluminance;
153extern const RegularSpectrum31f RGBToSpectrumCyanIlluminance;
154extern const RegularSpectrum31f RGBToSpectrumMagentaIlluminance;
155extern const RegularSpectrum31f RGBToSpectrumYellowIlluminance;
156extern const RegularSpectrum31f RGBToSpectrumRedIlluminance;
157extern const RegularSpectrum31f RGBToSpectrumGreenIlluminance;
158extern const RegularSpectrum31f RGBToSpectrumBlueIlluminance;
159
160
161//
162// Lighting conditions, defined as a set of color matching functions and an illuminant.
163//
164
165class LightingConditions
166{
167 public:
168 APPLESEED_SIMD4_ALIGN Color4f m_cmf[32]; // precomputed values of (cmf[0], cmf[1], cmf[2]) * illuminant
169
170 LightingConditions(); // leaves the object uninitialized
171
172 LightingConditions(
173 const RegularSpectrum31f& illuminant, // illuminant
174 const RegularSpectrum31f cmf[3]); // color matching functions
175};
176
177
178//
179// HSV <-> linear RGB transformations.
180//
181// References:
182//
183// http://en.literateprograms.org/RGB_to_HSV_color_space_conversion_%28C%29
184// http://en.wikipedia.org/wiki/HLS_color_space
185//
186// Note:
187//
188// If hsv is a three-component color expressed in the HSV color space, then:
189//
190// hsv[0] is the Hue in [0, 360)
191// hsv[1] is the Saturation in [0, 1]
192// hsv[2] is the Value in [0, 1]
193//
194
195// Convert a color from the HSV color space to the linear RGB color space.
196template <typename T>
197Color<T, 3> hsv_to_linear_rgb(const Color<T, 3>& hsv);
198
199// Convert a color from the linear RGB color space to the HSV color space.
200template <typename T>
201Color<T, 3> linear_rgb_to_hsv(const Color<T, 3>& linear_rgb);
202
203
204//
205// HSL <-> linear RGB transformations.
206//
207// Reference:
208//
209// http://en.wikipedia.org/wiki/HLS_color_space
210//
211// Note:
212//
213// If hsl is a three-component color expressed in the HSL color space, then:
214//
215// hsl[0] is the Hue in [0, 360)
216// hsl[1] is the Saturation in [0, 1]
217// hsl[2] is the Lightness in [0, 1]
218//
219
220// Convert a color from the HSL color space to the linear RGB color space.
221template <typename T>
222Color<T, 3> hsl_to_linear_rgb(const Color<T, 3>& hsl);
223
224// Convert a color from the linear RGB color space to the HSL color space.
225template <typename T>
226Color<T, 3> linear_rgb_to_hsl(const Color<T, 3>& linear_rgb);
227
228
229//
230// CIE XYZ <-> linear RGB transformations.
231//
232// References:
233//
234// http://en.wikipedia.org/wiki/SRGB_color_space
235// http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.txt
236// http://graphics.stanford.edu/courses/cs148-10-summer/docs/2010--kerr--cie_xyz.pdf
237//
238
239// Convert a color from the CIE XYZ color space to the linear RGB color space.
240template <typename T>
241Color<T, 3> ciexyz_to_linear_rgb(const Color<T, 3>& xyz);
242
243// Convert a color from the linear RGB color space to the CIE XYZ color space.
244template <typename T>
245Color<T, 3> linear_rgb_to_ciexyz(const Color<T, 3>& linear_rgb);
246
247
248//
249// CIE XYZ <-> CIE xyY transformations.
250//
251// Reference:
252//
253// http://en.wikipedia.org/wiki/CIE_1931_color_space#The_CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space
254//
255
256// Convert a color from the CIE XYZ color space to the CIE xyY color space.
257template <typename T>
258Color<T, 3> ciexyz_to_ciexyy(const Color<T, 3>& xyz);
259
260// Convert a color from the CIE xyY color space to the CIE XYZ color space.
261template <typename T>
262Color<T, 3> ciexyy_to_ciexyz(const Color<T, 3>& xyy);
263
264
265//
266// Linear RGB <-> sRGB transformations.
267//
268// Reference:
269//
270// http://en.wikipedia.org/wiki/SRGB_color_space
271//
272
273// Convert a color component from the linear RGB color space to the sRGB color space.
274template <typename T>
275T linear_rgb_to_srgb(const T c);
276
277// Convert a color component from the sRGB color space to the linear RGB color space.
278template <typename T>
279T srgb_to_linear_rgb(const T c);
280
281// Convert a color from the linear RGB color space to the sRGB color space.
282template <typename T>
283Color<T, 3> linear_rgb_to_srgb(const Color<T, 3>& linear_rgb);
284
285// Convert a color from the sRGB color space to the linear RGB color space.
286template <typename T>
287Color<T, 3> srgb_to_linear_rgb(const Color<T, 3>& srgb);
288
289// Variants of the above functions using a fast approximation of the power function.
290float fast_linear_rgb_to_srgb(const float c);
291float fast_srgb_to_linear_rgb(const float c);
292#ifdef APPLESEED_USE_SSE
293inline __m128 fast_linear_rgb_to_srgb(const __m128 linear_rgb);
294#endif
295Color3f fast_linear_rgb_to_srgb(const Color3f& linear_rgb);
296Color3f fast_srgb_to_linear_rgb(const Color3f& srgb);
297
298// Variants of the above functions using an even faster approximation of the power function.
299float faster_linear_rgb_to_srgb(const float c);
300float faster_srgb_to_linear_rgb(const float c);
301#ifdef APPLESEED_USE_SSE
302inline __m128 faster_linear_rgb_to_srgb(const __m128 linear_rgb);
303#endif
304Color3f faster_linear_rgb_to_srgb(const Color3f& linear_rgb);
305Color3f faster_srgb_to_linear_rgb(const Color3f& srgb);
306
307
308//
309// Compute the relative luminance of a linear RGB triplet as defined
310// in the ITU-R Recommendation BT.709 (Rec. 709):
311//
312// Y = 0.212671 R + 0.715160 G + 0.072169 B
313//
314// This is equivalent to transforming the linear RGB triplet to the
315// CIE XYZ color space and keeping the Y component.
316//
317// References:
318//
319// http://en.wikipedia.org/wiki/Rec._709
320// http://en.wikipedia.org/wiki/Luminance_(relative)
321//
322
323template <typename T>
324T luminance(const Color<T, 3>& linear_rgb);
325
326
327//
328// Spectrum <-> CIE XYZ transformations.
329//
330
331// Convert a spectrum to a color in the CIE XYZ color space.
332template <typename T, typename SpectrumType>
333Color<T, 3> spectrum_to_ciexyz(
334 const LightingConditions& lighting,
335 const SpectrumType& spectrum);
336
337// Convert a spectrum to a color in the CIE XYZ color space using the CIE D65 illuminant
338// and the CIE 1964 10-deg color matching functions.
339APPLESEED_DLLSYMBOL void spectrum_to_ciexyz_standard(
340 const float spectrum[],
341 float ciexyz[3]);
342
343// Convert a reflectance in the CIE XYZ color space to a spectrum.
344template <typename T, typename SpectrumType>
345void ciexyz_reflectance_to_spectrum(
346 const Color<T, 3>& xyz,
347 SpectrumType& spectrum);
348
349// Convert an illuminance in the CIE XYZ color space to a spectrum.
350template <typename T, typename SpectrumType>
351void ciexyz_illuminance_to_spectrum(
352 const Color<T, 3>& xyz,
353 SpectrumType& spectrum);
354
355
356//
357// Convert the CIE xy chromaticity of a D series (daylight) illuminant to a spectrum.
358//
359
360template <typename T, typename SpectrumType>
361void daylight_ciexy_to_spectrum(
362 const T x,
363 const T y,
364 SpectrumType& spectrum);
365
366
367//
368// Linear RGB to spectrum transformation.
369//
370// The spectrum to linear RGB transformation can be obtained by first
371// converting the spectrum to the CIE XYZ color space, then converting
372// the resulting CIE XYZ color to the linear RGB color space.
373//
374// Reference:
375//
376// An RGB to Spectrum Conversion for Reflectances, Brian Smits, University of Utah
377// http://www.cs.utah.edu/~bes/papers/color/
378//
379
380// Convert a linear RGB reflectance value to a spectrum,
381// without clamping the spectrum values.
382template <typename T, typename SpectrumType>
383void linear_rgb_reflectance_to_spectrum_unclamped(
384 const Color<T, 3>& linear_rgb,
385 SpectrumType& spectrum);
386
387// Convert a linear RGB reflectance value to a spectrum.
388template <typename T, typename SpectrumType>
389void linear_rgb_reflectance_to_spectrum(
390 const Color<T, 3>& linear_rgb,
391 SpectrumType& spectrum);
392
393// Convert a linear RGB illuminance value to a spectrum.
394template <typename T, typename SpectrumType>
395void linear_rgb_illuminance_to_spectrum(
396 const Color<T, 3>& linear_rgb,
397 SpectrumType& spectrum);
398
399
400//
401// Spectrum <-> Spectrum transformation.
402//
403
404// Resample a spectrum from one set of wavelengths to another.
405template <typename T>
406void spectrum_to_spectrum(
407 const size_t input_count,
408 const T input_wavelength[],
409 const T input_spectrum[],
410 const size_t output_count,
411 const T output_wavelength[],
412 T output_spectrum[],
413 T working_storage[] = 0);
414
415
416//
417// Transform a tristimulus value from one 3D color space to another 3D color space.
418//
419
420template <typename T>
421Color<T, 3> transform_color(
422 const Color<T, 3>& color,
423 const ColorSpace from,
424 const ColorSpace to)
425{
426 switch (from)
427 {
428 case ColorSpaceLinearRGB:
429 switch (to)
430 {
431 case ColorSpaceLinearRGB: return color;
432 case ColorSpaceSRGB: return linear_rgb_to_srgb(color);
433 case ColorSpaceCIEXYZ: return linear_rgb_to_ciexyz(color);
434 assert_otherwise;
435 }
436
437 case ColorSpaceSRGB:
438 switch (to)
439 {
440 case ColorSpaceLinearRGB: return srgb_to_linear_rgb(color);
441 case ColorSpaceSRGB: return color;
442 case ColorSpaceCIEXYZ: return linear_rgb_to_ciexyz(srgb_to_linear_rgb(color));
443 assert_otherwise;
444 }
445
446 case ColorSpaceCIEXYZ:
447 switch (to)
448 {
449 case ColorSpaceLinearRGB: return ciexyz_to_linear_rgb(color);
450 case ColorSpaceSRGB: return linear_rgb_to_srgb(ciexyz_to_linear_rgb(color));
451 case ColorSpaceCIEXYZ: return color;
452 assert_otherwise;
453 }
454
455 assert_otherwise;
456 }
457
458 // Keep the compiler happy.
459 return color;
460}
461
462
463//
464// HSV <-> linear RGB transformations implementation.
465//
466
467template <typename T>
468inline Color<T, 3> hsv_to_linear_rgb(const Color<T, 3>& hsv)
469{
470 // Compute chroma.
471 const T c = hsv[1] * hsv[2];
472
473 // Compute value.
474 Color<T, 3> linear_rgb(hsv[2] - c);
475
476 // Compute RGB color.
477 const T hprime = hsv[0] * T(1.0 / 60.0);
478 const T x = c * (T(1.0) - std::abs(mod(hprime, T(2.0)) - T(1.0)));
479 switch (truncate<int>(hprime))
480 {
481 case 0: linear_rgb[0] += c; linear_rgb[1] += x; break;
482 case 1: linear_rgb[0] += x; linear_rgb[1] += c; break;
483 case 2: linear_rgb[1] += c; linear_rgb[2] += x; break;
484 case 3: linear_rgb[1] += x; linear_rgb[2] += c; break;
485 case 4: linear_rgb[0] += x; linear_rgb[2] += c; break;
486 case 5: linear_rgb[0] += c; linear_rgb[2] += x; break;
487 assert_otherwise;
488 }
489
490 return linear_rgb;
491}
492
493template <typename T>
494inline Color<T, 3> linear_rgb_to_hsv(const Color<T, 3>& linear_rgb)
495{
496 // Compute value.
497 const T value = max_value(linear_rgb);
498
499 // Value is zero: return black.
500 if (value == T(0.0))
501 return Color<T, 3>(0.0, 0.0, 0.0);
502
503 // Compute chroma.
504 const T chroma = value - min_value(linear_rgb);
505
506 // Chroma is zero: return gray.
507 if (chroma == T(0.0))
508 return Color<T, 3>(0.0, 0.0, value);
509
510 // Compute hue.
511 const T h =
512 value == linear_rgb[0] ? (linear_rgb[1] - linear_rgb[2]) / chroma :
513 value == linear_rgb[1] ? (linear_rgb[2] - linear_rgb[0]) / chroma + T(2.0) :
514 (linear_rgb[0] - linear_rgb[1]) / chroma + T(4.0);
515 const T hue = mod(h * T(60.0), T(360.0));
516
517 // Compute saturation.
518 const T saturation = chroma / value;
519
520 return Color<T, 3>(hue, saturation, value);
521}
522
523
524//
525// HSL <-> linear RGB transformations implementation.
526//
527
528template <typename T>
529inline Color<T, 3> hsl_to_linear_rgb(const Color<T, 3>& hsl)
530{
531 // Compute chroma.
532 const T c = hsl[1] * (T(1.0) - std::abs(hsl[2] + hsl[2] - T(1.0)));
533
534 // Compute lightness.
535 Color<T, 3> linear_rgb(hsl[2] - T(0.5) * c);
536
537 // Compute RGB color.
538 const T hprime = hsl[0] * T(1.0 / 60.0);
539 const T x = c * (T(1.0) - std::abs(mod(hprime, T(2.0)) - T(1.0)));
540 switch (truncate<int>(hprime))
541 {
542 case 6: // fallthrough
543 case 0: linear_rgb[0] += c; linear_rgb[1] += x; break;
544 case 1: linear_rgb[0] += x; linear_rgb[1] += c; break;
545 case 2: linear_rgb[1] += c; linear_rgb[2] += x; break;
546 case 3: linear_rgb[1] += x; linear_rgb[2] += c; break;
547 case 4: linear_rgb[0] += x; linear_rgb[2] += c; break;
548 case 5: linear_rgb[0] += c; linear_rgb[2] += x; break;
549 assert_otherwise;
550 }
551
552 return linear_rgb;
553}
554
555template <typename T>
556inline Color<T, 3> linear_rgb_to_hsl(const Color<T, 3>& linear_rgb)
557{
558 // Compute chroma.
559 const T max_val = max_value(linear_rgb);
560 const T min_val = min_value(linear_rgb);
561 const T c = max_val - min_val;
562
563 // Special case for zero chroma.
564 if (c == T(0.0))
565 return Color<T, 3>(0.0);
566
567 // Compute hue.
568 const T hprime =
569 max_val == linear_rgb[0] ? mod((linear_rgb[1] - linear_rgb[2]) / c, T(6.0)) :
570 max_val == linear_rgb[1] ? (linear_rgb[2] - linear_rgb[0]) / c + T(2.0) :
571 (linear_rgb[0] - linear_rgb[1]) / c + T(4.0);
572 const T hue = hprime * T(60.0);
573
574 // Compute lightness and saturation.
575 const T lightness = T(0.5) * (min_val + max_val);
576 const T saturation = c / (T(1.0) - std::abs(lightness + lightness - T(1.0)));
577
578 return Color<T, 3>(hue, saturation, lightness);
579}
580
581
582//
583// CIE XYZ <-> linear RGB transformations implementation.
584//
585
586template <typename T>
587inline Color<T, 3> ciexyz_to_linear_rgb(const Color<T, 3>& xyz)
588{
589 return
590 clamp_low(
591 Color<T, 3>(
592 T( 3.240479) * xyz[0] + T(-1.537150) * xyz[1] + T(-0.498535) * xyz[2],
593 T(-0.969256) * xyz[0] + T( 1.875991) * xyz[1] + T( 0.041556) * xyz[2],
594 T( 0.055648) * xyz[0] + T(-0.204043) * xyz[1] + T( 1.057311) * xyz[2]),
595 T(0.0));
596}
597
598template <typename T>
599inline Color<T, 3> linear_rgb_to_ciexyz(const Color<T, 3>& linear_rgb)
600{
601 return
602 clamp_low(
603 Color<T, 3>(
604 T(0.412453) * linear_rgb[0] + T(0.357580) * linear_rgb[1] + T(0.180423) * linear_rgb[2],
605 T(0.212671) * linear_rgb[0] + T(0.715160) * linear_rgb[1] + T(0.072169) * linear_rgb[2],
606 T(0.019334) * linear_rgb[0] + T(0.119193) * linear_rgb[1] + T(0.950227) * linear_rgb[2]),
607 T(0.0));
608}
609
610
611//
612// CIE XYZ <-> CIE xyY transformations implementation.
613//
614
615template <typename T>
616inline Color<T, 3> ciexyz_to_ciexyy(const Color<T, 3>& xyz)
617{
618 const T rcp_sum = T(1.0) / (xyz[0] + xyz[1] + xyz[2]);
619 return Color<T, 3>(xyz[0] * rcp_sum, xyz[1] * rcp_sum, xyz[1]);
620}
621
622template <typename T>
623inline Color<T, 3> ciexyy_to_ciexyz(const Color<T, 3>& xyy)
624{
625 const T y = xyy[2] / xyy[1];
626 return Color<T, 3>(y * xyy[0], xyy[2], y * (T(1.0) - xyy[0] - xyy[1]));
627}
628
629
630//
631// Linear RGB <-> sRGB transformations implementation.
632//
633
634template <typename T>
635inline T linear_rgb_to_srgb(const T c)
636{
637 return c <= T(0.0031308)
638 ? T(12.92) * c
639 : T(1.055) * std::pow(c, T(1.0 / 2.4)) - T(0.055);
640}
641
642template <typename T>
643inline T srgb_to_linear_rgb(const T c)
644{
645 return c <= T(0.04045)
646 ? T(1.0 / 12.92) * c
647 : std::pow((c + T(0.055)) * T(1.0 / 1.055), T(2.4));
648}
649
650template <typename T>
651inline Color<T, 3> linear_rgb_to_srgb(const Color<T, 3>& linear_rgb)
652{
653 return Color<T, 3>(
654 linear_rgb_to_srgb(linear_rgb[0]),
655 linear_rgb_to_srgb(linear_rgb[1]),
656 linear_rgb_to_srgb(linear_rgb[2]));
657}
658
659template <typename T>
660inline Color<T, 3> srgb_to_linear_rgb(const Color<T, 3>& srgb)
661{
662 return Color<T, 3>(
663 srgb_to_linear_rgb(srgb[0]),
664 srgb_to_linear_rgb(srgb[1]),
665 srgb_to_linear_rgb(srgb[2]));
666}
667
668inline float fast_linear_rgb_to_srgb(const float c)
669{
670 return c <= 0.0031308f
671 ? 12.92f * c
672 : 1.055f * fast_pow(c, 1.0f / 2.4f) - 0.055f;
673}
674
675inline float fast_srgb_to_linear_rgb(const float c)
676{
677 return c <= 0.04045f
678 ? (1.0f / 12.92f) * c
679 : fast_pow((c + 0.055f) * (1.0f / 1.055f), 2.4f);
680}
681
682#ifdef APPLESEED_USE_SSE
683
684inline __m128 fast_linear_rgb_to_srgb(const __m128 linear_rgb)
685{
686 // Apply 2.4 gamma correction.
687 const __m128 y = fast_pow(linear_rgb, _mm_set1_ps(1.0f / 2.4f));
688
689 // Compute both outcomes of the branch.
690 const __m128 a = _mm_mul_ps(_mm_set1_ps(12.92f), linear_rgb);
691 const __m128 b = _mm_sub_ps(_mm_mul_ps(_mm_set1_ps(1.055f), y), _mm_set1_ps(0.055f));
692
693 // Interleave them based on the comparison result.
694 const __m128 mask = _mm_cmple_ps(linear_rgb, _mm_set1_ps(0.0031308f));
695 return _mm_add_ps(_mm_and_ps(mask, a), _mm_andnot_ps(mask, b));
696}
697
698inline Color3f fast_linear_rgb_to_srgb(const Color3f& linear_rgb)
699{
700 APPLESEED_SIMD4_ALIGN float transfer[4] =
701 {
702 linear_rgb[0],
703 linear_rgb[1],
704 linear_rgb[2],
705 linear_rgb[2]
706 };
707
708 _mm_store_ps(transfer, fast_linear_rgb_to_srgb(_mm_load_ps(transfer)));
709
710 return Color3f(transfer[0], transfer[1], transfer[2]);
711}
712
713#else
714
715inline Color3f fast_linear_rgb_to_srgb(const Color3f& linear_rgb)
716{
717 return Color3f(
718 fast_linear_rgb_to_srgb(linear_rgb[0]),
719 fast_linear_rgb_to_srgb(linear_rgb[1]),
720 fast_linear_rgb_to_srgb(linear_rgb[2]));
721}
722
723#endif // APPLESEED_USE_SSE
724
725inline Color3f fast_srgb_to_linear_rgb(const Color3f& srgb)
726{
727 return Color3f(
728 fast_srgb_to_linear_rgb(srgb[0]),
729 fast_srgb_to_linear_rgb(srgb[1]),
730 fast_srgb_to_linear_rgb(srgb[2]));
731}
732
733inline float faster_linear_rgb_to_srgb(const float c)
734{
735 return c <= 0.0031308f
736 ? 12.92f * c
737 : 1.055f * faster_pow(c, 1.0f / 2.4f) - 0.055f;
738}
739
740inline float faster_srgb_to_linear_rgb(const float c)
741{
742 return c <= 0.04045f
743 ? (1.0f / 12.92f) * c
744 : faster_pow((c + 0.055f) * (1.0f / 1.055f), 2.4f);
745}
746
747#ifdef APPLESEED_USE_SSE
748
749inline __m128 faster_linear_rgb_to_srgb(const __m128 linear_rgb)
750{
751 // Apply 2.4 gamma correction.
752 const __m128 y = faster_pow(linear_rgb, _mm_set1_ps(1.0f / 2.4f));
753
754 // Compute both outcomes of the branch.
755 const __m128 a = _mm_mul_ps(_mm_set1_ps(12.92f), linear_rgb);
756 const __m128 b = _mm_sub_ps(_mm_mul_ps(_mm_set1_ps(1.055f), y), _mm_set1_ps(0.055f));
757
758 // Interleave them based on the comparison result.
759 const __m128 mask = _mm_cmple_ps(linear_rgb, _mm_set1_ps(0.0031308f));
760 return _mm_add_ps(_mm_and_ps(mask, a), _mm_andnot_ps(mask, b));
761}
762
763inline Color3f faster_linear_rgb_to_srgb(const Color3f& linear_rgb)
764{
765 APPLESEED_SIMD4_ALIGN float transfer[4] =
766 {
767 linear_rgb[0],
768 linear_rgb[1],
769 linear_rgb[2],
770 linear_rgb[2]
771 };
772
773 _mm_store_ps(transfer, faster_linear_rgb_to_srgb(_mm_load_ps(transfer)));
774
775 return Color3f(transfer[0], transfer[1], transfer[2]);
776}
777
778#else
779
780inline Color3f faster_linear_rgb_to_srgb(const Color3f& linear_rgb)
781{
782 return Color3f(
783 faster_linear_rgb_to_srgb(linear_rgb[0]),
784 faster_linear_rgb_to_srgb(linear_rgb[1]),
785 faster_linear_rgb_to_srgb(linear_rgb[2]));
786}
787
788#endif // APPLESEED_USE_SSE
789
790inline Color3f faster_srgb_to_linear_rgb(const Color3f& srgb)
791{
792 return Color3f(
793 faster_srgb_to_linear_rgb(srgb[0]),
794 faster_srgb_to_linear_rgb(srgb[1]),
795 faster_srgb_to_linear_rgb(srgb[2]));
796}
797
798
799//
800// Relative luminance function implementation.
801//
802
803template <typename T>
804inline T luminance(const Color<T, 3>& linear_rgb)
805{
806 return
807 T(0.212671) * linear_rgb[0] +
808 T(0.715160) * linear_rgb[1] +
809 T(0.072169) * linear_rgb[2];
810}
811
812
813//
814// Spectrum <-> CIE XYZ transformations implementation.
815//
816
817template <typename T, typename SpectrumType>
818Color<T, 3> spectrum_to_ciexyz(
819 const LightingConditions& lighting,
820 const SpectrumType& spectrum)
821{
822 BOOST_STATIC_ASSERT(SpectrumType::Samples == 31);
823
824 T x = T(0.0);
825 T y = T(0.0);
826 T z = T(0.0);
827
828 for (size_t w = 0; w < 31; ++w)
829 {
830 const T val = spectrum[w];
831 x += lighting.m_cmf[w][0] * val;
832 y += lighting.m_cmf[w][1] * val;
833 z += lighting.m_cmf[w][2] * val;
834 }
835
836 return Color<T, 3>(x, y, z);
837}
838
839#ifdef APPLESEED_USE_SSE
840
841template <>
842inline Color3f spectrum_to_ciexyz<float, RegularSpectrum31f>(
843 const LightingConditions& lighting,
844 const RegularSpectrum31f& spectrum)
845{
846 __m128 xyz1 = _mm_setzero_ps();
847 __m128 xyz2 = _mm_setzero_ps();
848 __m128 xyz3 = _mm_setzero_ps();
849 __m128 xyz4 = _mm_setzero_ps();
850
851 for (size_t w = 0; w < 8; ++w)
852 {
853 xyz1 = _mm_add_ps(xyz1, _mm_mul_ps(_mm_set1_ps(spectrum[4 * w + 0]), _mm_load_ps(&lighting.m_cmf[4 * w + 0][0])));
854 xyz2 = _mm_add_ps(xyz2, _mm_mul_ps(_mm_set1_ps(spectrum[4 * w + 1]), _mm_load_ps(&lighting.m_cmf[4 * w + 1][0])));
855 xyz3 = _mm_add_ps(xyz3, _mm_mul_ps(_mm_set1_ps(spectrum[4 * w + 2]), _mm_load_ps(&lighting.m_cmf[4 * w + 2][0])));
856 xyz4 = _mm_add_ps(xyz4, _mm_mul_ps(_mm_set1_ps(spectrum[4 * w + 3]), _mm_load_ps(&lighting.m_cmf[4 * w + 3][0])));
857 }
858
859 xyz1 = _mm_add_ps(xyz1, xyz2);
860 xyz3 = _mm_add_ps(xyz3, xyz4);
861 xyz1 = _mm_add_ps(xyz1, xyz3);
862
863 APPLESEED_SIMD4_ALIGN float transfer[4];
864 _mm_store_ps(transfer, xyz1);
865
866 return Color3f(transfer[0], transfer[1], transfer[2]);
867}
868
869#endif // APPLESEED_USE_SSE
870
871template <typename T, typename SpectrumType>
872void ciexyz_reflectance_to_spectrum(
873 const Color<T, 3>& xyz,
874 SpectrumType& spectrum)
875{
876 linear_rgb_reflectance_to_spectrum(
877 ciexyz_to_linear_rgb(xyz),
878 spectrum);
879}
880
881template <typename T, typename SpectrumType>
882void ciexyz_illuminance_to_spectrum(
883 const Color<T, 3>& xyz,
884 SpectrumType& spectrum)
885{
886 linear_rgb_illuminance_to_spectrum(
887 ciexyz_to_linear_rgb(xyz),
888 spectrum);
889}
890
891
892//
893// Convert the CIE xy chromaticity of a D series (daylight) illuminant to a spectrum.
894//
895
896template <>
897inline void daylight_ciexy_to_spectrum<float, RegularSpectrum31f>(
898 const float x,
899 const float y,
900 RegularSpectrum31f& spectrum)
901{
902 const float rcp_m = 1.0f / (0.0241f + 0.2562f * x - 0.7341f * y);
903 const float m1 = (-1.3515f - 1.7703f * x + 5.9114f * y) * rcp_m;
904 const float m2 = (0.0300f - 31.4424f * x + 30.0717f * y) * rcp_m;
905
906 spectrum = DaylightS0;
907
908 RegularSpectrum31f s1 = DaylightS1;
909 s1 *= m1;
910 spectrum += s1;
911
912 RegularSpectrum31f s2 = DaylightS2;
913 s2 *= m2;
914 spectrum += s2;
915}
916
917
918//
919// Linear RGB to spectrum transformation implementation.
920//
921
922namespace impl
923{
924 template <typename T, typename SpectrumType>
925 void linear_rgb_to_spectrum_approximation(
926 const Color<T, 3>& linear_rgb,
927 SpectrumType& spectrum)
928 {
929 for (size_t w = 0; w < 10; ++w)
930 spectrum[w] = static_cast<T>(linear_rgb[2]);
931
932 for (size_t w = 10; w < 20; ++w)
933 spectrum[w] = static_cast<T>(linear_rgb[1]);
934
935 for (size_t w = 20; w < 31; ++w)
936 spectrum[w] = static_cast<T>(linear_rgb[0]);
937 }
938
939 template <typename T, typename SpectrumType>
940 void linear_rgb_to_spectrum(
941 const Color<T, 3>& linear_rgb,
942 const SpectrumType& white,
943 const SpectrumType& cyan,
944 const SpectrumType& magenta,
945 const SpectrumType& yellow,
946 const SpectrumType& red,
947 const SpectrumType& green,
948 const SpectrumType& blue,
949 SpectrumType& spectrum)
950 {
951 const T r = linear_rgb[0];
952 const T g = linear_rgb[1];
953 const T b = linear_rgb[2];
954 SpectrumType tmp;
955
956 if (r <= g && r <= b)
957 {
958 spectrum = white;
959 spectrum *= r;
960
961 if (g <= b)
962 {
963 tmp = cyan;
964 tmp *= g - r;
965 spectrum += tmp;
966
967 tmp = blue;
968 tmp *= b - g;
969 spectrum += tmp;
970 }
971 else
972 {
973 tmp = cyan;
974 tmp *= b - r;
975 spectrum += tmp;
976
977 tmp = green;
978 tmp *= g - b;
979 spectrum += tmp;
980 }
981 }
982 else if (g <= r && g <= b)
983 {
984 spectrum = white;
985 spectrum *= g;
986
987 if (r <= b)
988 {
989 tmp = magenta;
990 tmp *= r - g;
991 spectrum += tmp;
992
993 tmp = blue;
994 tmp *= b - r;
995 spectrum += tmp;
996 }
997 else
998 {
999 tmp = magenta;
1000 tmp *= b - g;
1001 spectrum += tmp;
1002
1003 tmp = red;
1004 tmp *= r - b;
1005 spectrum += tmp;
1006 }
1007 }
1008 else
1009 {
1010 spectrum = white;
1011 spectrum *= b;
1012
1013 if (r <= g)
1014 {
1015 tmp = yellow;
1016 tmp *= r - b;
1017 spectrum += tmp;
1018
1019 tmp = green;
1020 tmp *= g - r;
1021 spectrum += tmp;
1022 }
1023 else
1024 {
1025 tmp = yellow;
1026 tmp *= g - b;
1027 spectrum += tmp;
1028
1029 tmp = red;
1030 tmp *= r - g;
1031 spectrum += tmp;
1032 }
1033 }
1034 }
1035}
1036
1037template <typename T, typename SpectrumType>
1038void linear_rgb_reflectance_to_spectrum_unclamped(
1039 const Color<T, 3>& linear_rgb,
1040 SpectrumType& spectrum)
1041{
1042 impl::linear_rgb_to_spectrum(
1043 linear_rgb,
1044 RGBToSpectrumWhiteReflectance,
1045 RGBToSpectrumCyanReflectance,
1046 RGBToSpectrumMagentaReflectance,
1047 RGBToSpectrumYellowReflectance,
1048 RGBToSpectrumRedReflectance,
1049 RGBToSpectrumGreenReflectance,
1050 RGBToSpectrumBlueReflectance,
1051 spectrum);
1052}
1053
1054template <typename T, typename SpectrumType>
1055void linear_rgb_reflectance_to_spectrum(
1056 const Color<T, 3>& linear_rgb,
1057 SpectrumType& spectrum)
1058{
1059 linear_rgb_reflectance_to_spectrum_unclamped(linear_rgb, spectrum);
1060 clamp_low_in_place(spectrum, T(0.0));
1061}
1062
1063template <typename T, typename SpectrumType>
1064void linear_rgb_illuminance_to_spectrum(
1065 const Color<T, 3>& linear_rgb,
1066 SpectrumType& spectrum)
1067{
1068 /* This gives an undesirable blue tint...
1069 impl::linear_rgb_to_spectrum(
1070 linear_rgb,
1071 RGBToSpectrumWhiteIlluminance,
1072 RGBToSpectrumCyanIlluminance,
1073 RGBToSpectrumMagentaIlluminance,
1074 RGBToSpectrumYellowIlluminance,
1075 RGBToSpectrumRedIlluminance,
1076 RGBToSpectrumGreenIlluminance,
1077 RGBToSpectrumBlueIlluminance,
1078 spectrum); */
1079
1080 impl::linear_rgb_to_spectrum(
1081 linear_rgb,
1082 RGBToSpectrumWhiteReflectance,
1083 RGBToSpectrumCyanReflectance,
1084 RGBToSpectrumMagentaReflectance,
1085 RGBToSpectrumYellowReflectance,
1086 RGBToSpectrumRedReflectance,
1087 RGBToSpectrumGreenReflectance,
1088 RGBToSpectrumBlueReflectance,
1089 spectrum);
1090
1091 clamp_low_in_place(spectrum, T(0.0));
1092}
1093
1094
1095//
1096// Spectrum <-> Spectrum transformation implementation.
1097//
1098
1099template <typename T>
1100void spectrum_to_spectrum(
1101 const size_t input_count,
1102 const T input_wavelength[],
1103 const T input_spectrum[],
1104 const size_t output_count,
1105 const T output_wavelength[],
1106 T output_spectrum[],
1107 T working_storage[])
1108{
1109 const bool own_memory = (working_storage == 0);
1110
1111 if (own_memory)
1112 working_storage = new T[input_count];
1113
1114 compute_cardinal_spline_tangents(
1115 input_count, // [in] knot count
1116 input_wavelength, // [in] knot x
1117 &input_spectrum[0], // [in] knot y
1118 T(0.0), // [in] tension
1119 working_storage); // [out] knot derivatives
1120
1121 cubic_hermite_spline(
1122 input_count, // [in] knot count
1123 input_wavelength, // [in] knot x
1124 &input_spectrum[0], // [in] knot y
1125 working_storage, // [in] knot derivatives
1126 output_count, // [in] point count
1127 output_wavelength, // [in] point x
1128 &output_spectrum[0]); // [out] point y
1129
1130 if (own_memory)
1131 delete [] working_storage;
1132}
1133
1134} // namespace foundation
1135
1136#endif // !APPLESEED_FOUNDATION_IMAGE_COLORSPACE_H
1137