1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2019 Esteban Tovagliari, The appleseedhq Organization
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28 
29 #pragma once
30 
31 // appleseed.foundation headers.
32 #include "foundation/image/color.h"
33 #include "foundation/image/colorspace.h"
34 #include "foundation/image/regularspectrum.h"
35 #include "foundation/math/fp.h"
36 #include "foundation/math/scalar.h"
37 #include "foundation/platform/compiler.h"
38 #ifdef APPLESEED_USE_SSE
39 #include "foundation/platform/sse.h"
40 #endif
41 #include "foundation/platform/types.h"
42 #include "foundation/utility/poison.h"
43 
44 // Standard headers.
45 #include <algorithm>
46 #include <cassert>
47 #include <cmath>
48 #include <cstddef>
49 
50 namespace renderer
51 {
52 
53 //
54 // ...
55 //
56 
57 template <typename T>
58 class RGBSpectrum
59 {
60   public:
61     // Value type and number of samples.
62     typedef T ValueType;
63     static const size_t Samples = 3;
64 
65     // Number of stored samples such that the size of the sample array is a multiple of 16 bytes.
66     static const size_t StoredSamples = (((3 * sizeof(T)) + 15) & ~15) / sizeof(T);
67 
68     enum Mode
69     {
70         RGB = 0,            // RGBSpectrum stores and operates on RGB triplets
71     };
72 
73     enum Intent
74     {
75         Reflectance = 0,    // this spectrum represents a reflectance in [0, 1]^N
76         Illuminance = 1     // this spectrum represents an illuminance in [0, infinity)^N
77     };
78 
79     // Change the current thread-local spectrum mode. Return the previous mode.
80     static Mode set_mode(const Mode mode);
81 
82     // Return the current thread-local spectrum mode.
83     static Mode get_mode();
84 
85     // Return the number of active color channels for the current spectrum mode.
86     static size_t size();
87 
88     // Constructors.
89 #ifdef APPLESEED_USE_SSE
90     RGBSpectrum();                                      // leave all components uninitialized
91 #else
92 #if !defined(_MSC_VER) || _MSC_VER >= 1800
93     RGBSpectrum() = default;                            // leave all components uninitialized
94 #else
RGBSpectrum()95     RGBSpectrum() {}                                    // leave all components uninitialized
96 #endif
97 #endif
98     explicit RGBSpectrum(const ValueType val);          // set all components to `val`
99     RGBSpectrum(
100         const foundation::Color<ValueType, 3>&              rgb,
101         const foundation::LightingConditions&               lighting_conditions,
102         const Intent                                        intent);
103 
104     template<size_t N>
105     RGBSpectrum(
106         const foundation::RegularSpectrum<ValueType, N>&    spectrum,
107         const foundation::LightingConditions&               lighting_conditions,
108         const Intent                                        intent);
109 
110     // Construct a spectrum from another spectrum of a different type.
111     template <typename U>
112     RGBSpectrum(const RGBSpectrum<U>& rhs);
113 
114     // Construct a spectrum from an array of `3` scalars.
115     static RGBSpectrum from_array(const ValueType* rhs);
116 
117     // Set all components to a given value.
118     void set(const ValueType val);
119 
120     // Initialize the spectrum from a linear RGB value.
121     void set(
122         const foundation::Color<ValueType, 3>&              rgb,
123         const foundation::LightingConditions&               lighting_conditions,
124         const Intent                                        intent);
125 
126     // Initialize the spectrum from a spectral value.
127     template <size_t N>
128     void set(
129         const foundation::RegularSpectrum<ValueType, N>&    spectrum,
130         const foundation::LightingConditions&               lighting_conditions,
131         const Intent                                        intent);
132 
133     // Unchecked array subscripting.
134     ValueType& operator[](const size_t i);
135     const ValueType& operator[](const size_t i) const;
136 
137     // Convert the spectrum to a linear RGB color.
138     foundation::Color<ValueType, 3> to_rgb(
139         const foundation::LightingConditions&   lighting_conditions) const;
140 
141     // Convert the spectrum to a CIE XYZ color.
142     foundation::Color<ValueType, 3> to_ciexyz(
143         const foundation::LightingConditions&   lighting_conditions) const;
144 
145   private:
146     APPLESEED_SIMD4_ALIGN ValueType m_samples[StoredSamples];
147 };
148 
149 // Exact inequality and equality tests.
150 template <typename T> bool operator!=(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
151 template <typename T> bool operator==(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
152 
153 // Spectrum arithmetic.
154 template <typename T> RGBSpectrum<T>  operator+ (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
155 template <typename T> RGBSpectrum<T>  operator- (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
156 template <typename T> RGBSpectrum<T>  operator- (const RGBSpectrum<T>& lhs);
157 template <typename T> RGBSpectrum<T>  operator* (const RGBSpectrum<T>& lhs, const T rhs);
158 template <typename T> RGBSpectrum<T>  operator* (const T lhs, const RGBSpectrum<T>& rhs);
159 template <typename T> RGBSpectrum<T>  operator* (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
160 template <typename T> RGBSpectrum<T>  operator/ (const RGBSpectrum<T>& lhs, const T rhs);
161 template <typename T> RGBSpectrum<T>  operator/ (const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
162 template <typename T> RGBSpectrum<T>& operator+=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
163 template <typename T> RGBSpectrum<T>& operator-=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
164 template <typename T> RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const T rhs);
165 template <typename T> RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
166 template <typename T> RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const T rhs);
167 template <typename T> RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs);
168 
169 // Multiply-add: a = a + b * c.
170 template <typename T> void madd(RGBSpectrum<T>& a, const RGBSpectrum<T>& b, const RGBSpectrum<T>& c);
171 template <typename T> void madd(RGBSpectrum<T>& a, const RGBSpectrum<T>& b, const T c);
172 
173 
174 //
175 // Full specializations for spectra of type float and double.
176 //
177 
178 typedef RGBSpectrum<float> RGBSpectrumf;
179 typedef RGBSpectrum<double> RGBSpectrumd;
180 
181 }   // namespace renderer
182 
183 namespace foundation
184 {
185 
186 // Return whether all components of a spectrum are exactly zero.
187 template <typename T> bool is_zero(const renderer::RGBSpectrum<T>& s);
188 
189 // Approximate equality tests.
190 template <typename T> bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs);
191 template <typename T> bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs, const T eps);
192 
193 // Approximate zero tests.
194 template <typename T> bool fz(const renderer::RGBSpectrum<T>& s);
195 template <typename T> bool fz(const renderer::RGBSpectrum<T>& s, const T eps);
196 
197 // Component-wise reciprocal.
198 template <typename T> renderer::RGBSpectrum<T> rcp(const renderer::RGBSpectrum<T>& s);
199 
200 // Return whether all components of a spectrum are in [0,1].
201 template <typename T> bool is_saturated(const renderer::RGBSpectrum<T>& s);
202 
203 // Clamp the argument to [0,1].
204 template <typename T> renderer::RGBSpectrum<T> saturate(const renderer::RGBSpectrum<T>& s);
205 template <typename T> void saturate_in_place(renderer::RGBSpectrum<T>& s);
206 
207 // Clamp the argument to [min, max].
208 template <typename T> renderer::RGBSpectrum<T> clamp(const renderer::RGBSpectrum<T>& s, const T min, const T max);
209 template <typename T> void clamp_in_place(renderer::RGBSpectrum<T>& s, const T min, const T max);
210 
211 // Clamp the argument to [min, +infinity).
212 template <typename T> renderer::RGBSpectrum<T> clamp_low(const renderer::RGBSpectrum<T>& s, const T min);
213 template <typename T> void clamp_low_in_place(renderer::RGBSpectrum<T>& s, const T min);
214 
215 // Clamp the argument to (-infinity, max].
216 template <typename T> renderer::RGBSpectrum<T> clamp_high(const renderer::RGBSpectrum<T>& s, const T max);
217 template <typename T> void clamp_high_in_place(renderer::RGBSpectrum<T>& s, const T max);
218 
219 // Component-wise linear interpolation between a and b.
220 template <typename T> renderer::RGBSpectrum<T> lerp(
221     const renderer::RGBSpectrum<T>& a,
222     const renderer::RGBSpectrum<T>& b,
223     const renderer::RGBSpectrum<T>& t);
224 
225 // Return the smallest or largest signed component of a spectrum.
226 template <typename T> T min_value(const renderer::RGBSpectrum<T>& s);
227 template <typename T> T max_value(const renderer::RGBSpectrum<T>& s);
228 
229 // Return the index of the smallest or largest signed component of a spectrum.
230 template <typename T> size_t min_index(const renderer::RGBSpectrum<T>& s);
231 template <typename T> size_t max_index(const renderer::RGBSpectrum<T>& s);
232 
233 // Return the index of the smallest or largest component of a spectrum, in absolute value.
234 template <typename T> size_t min_abs_index(const renderer::RGBSpectrum<T>& s);
235 template <typename T> size_t max_abs_index(const renderer::RGBSpectrum<T>& s);
236 
237 // Return the sum of the components of a spectrum.
238 template <typename T> T sum_value(const renderer::RGBSpectrum<T>& s);
239 
240 // Return the average value of a spectrum.
241 template <typename T> T average_value(const renderer::RGBSpectrum<T>& s);
242 
243 // Return true if a spectrum contains at least one NaN value.
244 template <typename T> bool has_nan(const renderer::RGBSpectrum<T>& s);
245 
246 // Return true if all components of a spectrum are finite (not NaN, not infinite).
247 template <typename T> bool is_finite(const renderer::RGBSpectrum<T>& s);
248 
249 // Return the square root of a spectrum.
250 template <typename T> renderer::RGBSpectrum<T> sqrt(const renderer::RGBSpectrum<T>& s);
251 
252 // Raise a spectrum to a given power.
253 template <typename T> renderer::RGBSpectrum<T> pow(const renderer::RGBSpectrum<T>& x, const T y);
254 
255 // Raise a spectrum to a given power, component-wise.
256 template <typename T> renderer::RGBSpectrum<T> pow(
257     const renderer::RGBSpectrum<T>& x,
258     const renderer::RGBSpectrum<T>& y);
259 
260 // Compute the logarithm of a spectrum.
261 template <typename T> renderer::RGBSpectrum<T> log(const renderer::RGBSpectrum<T>& s);
262 
263 // Compute the exponential of a spectrum.
264 template <typename T> renderer::RGBSpectrum<T> exp(const renderer::RGBSpectrum<T>& s);
265 
266 }   // namespace foundation
267 
268 
269 //
270 // RGBSpectrum class implementation.
271 //
272 
273 namespace renderer
274 {
275 
276 template <typename T>
set_mode(const Mode mode)277 typename RGBSpectrum<T>::Mode RGBSpectrum<T>::set_mode(const Mode mode)
278 {
279     return RGB;
280 }
281 
282 template <typename T>
get_mode()283 inline typename RGBSpectrum<T>::Mode RGBSpectrum<T>::get_mode()
284 {
285     return RGB;
286 }
287 
288 template <typename T>
size()289 inline size_t RGBSpectrum<T>::size()
290 {
291     return 3;
292 }
293 
294 #ifdef APPLESEED_USE_SSE
295 
296 template <typename T>
RGBSpectrum()297 inline RGBSpectrum<T>::RGBSpectrum()
298 {
299     m_samples[3] = T(0.0);
300 }
301 
302 #endif
303 
304 template <typename T>
RGBSpectrum(const ValueType val)305 inline RGBSpectrum<T>::RGBSpectrum(const ValueType val)
306 {
307     set(val);
308 
309 #ifdef APPLESEED_USE_SSE
310     m_samples[3] = T(0.0);
311 #endif
312 }
313 
314 template <typename T>
RGBSpectrum(const foundation::Color<ValueType,3> & rgb,const foundation::LightingConditions & lighting_conditions,const Intent intent)315 inline RGBSpectrum<T>::RGBSpectrum(
316     const foundation::Color<ValueType, 3>&              rgb,
317     const foundation::LightingConditions&               lighting_conditions,
318     const Intent                                        intent)
319 {
320     set(rgb, lighting_conditions, intent);
321 
322 #ifdef APPLESEED_USE_SSE
323     m_samples[3] = T(0.0);
324 #endif
325 }
326 
327 template <typename T>
328 template <size_t N>
RGBSpectrum(const foundation::RegularSpectrum<ValueType,N> & spectrum,const foundation::LightingConditions & lighting_conditions,const Intent intent)329 inline RGBSpectrum<T>::RGBSpectrum(
330     const foundation::RegularSpectrum<ValueType, N>&    spectrum,
331     const foundation::LightingConditions&               lighting_conditions,
332     const Intent                                        intent)
333 {
334     set(spectrum, lighting_conditions, intent);
335 
336 #ifdef APPLESEED_USE_SSE
337     m_samples[3] = T(0.0);
338 #endif
339 }
340 
341 template <typename T>
342 template <typename U>
RGBSpectrum(const RGBSpectrum<U> & rhs)343 inline RGBSpectrum<T>::RGBSpectrum(const RGBSpectrum<U>& rhs)
344 {
345     for (size_t i = 0; i < 3; ++i)
346         m_samples[i] = static_cast<ValueType>(rhs[i]);
347 
348 #ifdef APPLESEED_USE_SSE
349     m_samples[3] = T(0.0);
350 #endif
351 }
352 
353 template <typename T>
from_array(const ValueType * rhs)354 inline RGBSpectrum<T> RGBSpectrum<T>::from_array(const ValueType* rhs)
355 {
356     assert(rhs);
357 
358     RGBSpectrum result;
359 
360     for (size_t i = 0; i < 3; ++i)
361         result.m_samples[i] = rhs[i];
362 
363     return result;
364 }
365 
366 template <typename T>
set(const ValueType val)367 inline void RGBSpectrum<T>::set(const ValueType val)
368 {
369     for (size_t i = 0; i < 3; ++i)
370         m_samples[i] = val;
371 }
372 
373 template <typename T>
set(const foundation::Color<ValueType,3> & rgb,const foundation::LightingConditions & lighting_conditions,const Intent intent)374 void RGBSpectrum<T>::set(
375     const foundation::Color<ValueType, 3>&              rgb,
376     const foundation::LightingConditions&               lighting_conditions,
377     const Intent                                        intent)
378 {
379     m_samples[0] = rgb[0];
380     m_samples[1] = rgb[1];
381     m_samples[2] = rgb[2];
382 }
383 
384 template <typename T>
385 template <size_t N>
set(const foundation::RegularSpectrum<ValueType,N> & spectrum,const foundation::LightingConditions & lighting_conditions,const Intent intent)386 void RGBSpectrum<T>::set(
387     const foundation::RegularSpectrum<ValueType, N>&    spectrum,
388     const foundation::LightingConditions&               lighting_conditions,
389     const Intent                                        intent)
390 {
391     reinterpret_cast<foundation::Color<T, 3>&>(m_samples[0]) =
392         foundation::ciexyz_to_linear_rgb(
393             foundation::spectrum_to_ciexyz<T>(lighting_conditions, spectrum));
394 }
395 
396 template <typename T>
397 inline T& RGBSpectrum<T>::operator[](const size_t i)
398 {
399     assert(i < 3);
400     return m_samples[i];
401 }
402 
403 template <typename T>
404 inline const T& RGBSpectrum<T>::operator[](const size_t i) const
405 {
406     assert(i < 3);
407     return m_samples[i];
408 }
409 
410 template <typename T>
to_rgb(const foundation::LightingConditions & lighting_conditions)411 inline foundation::Color<T, 3> RGBSpectrum<T>::to_rgb(
412     const foundation::LightingConditions& lighting_conditions) const
413 {
414     return foundation::Color<T, 3>(m_samples[0], m_samples[1], m_samples[2]);
415 }
416 
417 template <typename T>
to_ciexyz(const foundation::LightingConditions & lighting_conditions)418 inline foundation::Color<T, 3> RGBSpectrum<T>::to_ciexyz(
419     const foundation::LightingConditions& lighting_conditions) const
420 {
421     return
422         linear_rgb_to_ciexyz(
423             foundation::Color<T, 3>(m_samples[0], m_samples[1], m_samples[2]));
424 }
425 
426 template <typename T>
427 inline bool operator!=(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
428 {
429     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
430     {
431         if (lhs[i] != rhs[i])
432             return true;
433     }
434 
435     return false;
436 }
437 
438 template <typename T>
439 inline bool operator==(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
440 {
441     return !(lhs != rhs);
442 }
443 
444 template <typename T>
445 inline RGBSpectrum<T> operator+(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
446 {
447     RGBSpectrum<T> result;
448 
449     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
450         result[i] = lhs[i] + rhs[i];
451 
452     return result;
453 }
454 
455 template <typename T>
456 inline RGBSpectrum<T> operator-(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
457 {
458     RGBSpectrum<T> result;
459 
460     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
461         result[i] = lhs[i] - rhs[i];
462 
463     return result;
464 }
465 
466 template <typename T>
467 inline RGBSpectrum<T> operator-(const RGBSpectrum<T>& lhs)
468 {
469     RGBSpectrum<T> result;
470 
471     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
472         result[i] = -lhs[i];
473 
474     return result;
475 }
476 
477 template <typename T>
478 inline RGBSpectrum<T> operator*(const RGBSpectrum<T>& lhs, const T rhs)
479 {
480     RGBSpectrum<T> result;
481 
482     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
483         result[i] = lhs[i] * rhs;
484 
485     return result;
486 }
487 
488 template <typename T>
489 inline RGBSpectrum<T> operator*(const T lhs, const RGBSpectrum<T>& rhs)
490 {
491     return rhs * lhs;
492 }
493 
494 template <typename T>
495 inline RGBSpectrum<T> operator*(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
496 {
497     RGBSpectrum<T> result;
498 
499     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
500         result[i] = lhs[i] * rhs[i];
501 
502     return result;
503 }
504 
505 template <typename T>
506 inline RGBSpectrum<T> operator/(const RGBSpectrum<T>& lhs, const T rhs)
507 {
508     RGBSpectrum<T> result;
509 
510     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
511         result[i] = lhs[i] / rhs;
512 
513     return result;
514 }
515 
516 template <>
517 inline RGBSpectrum<float> operator/(const RGBSpectrum<float>& lhs, const float rhs)
518 {
519     return lhs * (1.0f / rhs);
520 }
521 
522 template <size_t N>
523 inline RGBSpectrum<double> operator/(const RGBSpectrum<double>& lhs, const double rhs)
524 {
525     return lhs * (1.0 / rhs);
526 }
527 
528 template <size_t N>
529 inline RGBSpectrum<long double> operator/(const RGBSpectrum<long double>& lhs, const long double rhs)
530 {
531     return lhs * (1.0L / rhs);
532 }
533 
534 template <typename T>
535 inline RGBSpectrum<T> operator/(const RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
536 {
537     RGBSpectrum<T> result;
538 
539     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
540         result[i] = lhs[i] / rhs[i];
541 
542     return result;
543 }
544 
545 template <typename T>
546 inline RGBSpectrum<T>& operator+=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
547 {
548     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
549         lhs[i] += rhs[i];
550 
551     return lhs;
552 }
553 
554 #ifdef APPLESEED_USE_SSE
555 
556 template <>
557 APPLESEED_FORCE_INLINE RGBSpectrum<float>& operator+=(RGBSpectrum<float>& lhs, const RGBSpectrum<float>& rhs)
558 {
559     _mm_store_ps(&lhs[ 0], _mm_add_ps(_mm_load_ps(&lhs[ 0]), _mm_load_ps(&rhs[ 0])));
560 
561     if (RGBSpectrum<float>::size() > 3)
562     {
563         _mm_store_ps(&lhs[ 4], _mm_add_ps(_mm_load_ps(&lhs[ 4]), _mm_load_ps(&rhs[ 4])));
564         _mm_store_ps(&lhs[ 8], _mm_add_ps(_mm_load_ps(&lhs[ 8]), _mm_load_ps(&rhs[ 8])));
565         _mm_store_ps(&lhs[12], _mm_add_ps(_mm_load_ps(&lhs[12]), _mm_load_ps(&rhs[12])));
566         _mm_store_ps(&lhs[16], _mm_add_ps(_mm_load_ps(&lhs[16]), _mm_load_ps(&rhs[16])));
567         _mm_store_ps(&lhs[20], _mm_add_ps(_mm_load_ps(&lhs[20]), _mm_load_ps(&rhs[20])));
568         _mm_store_ps(&lhs[24], _mm_add_ps(_mm_load_ps(&lhs[24]), _mm_load_ps(&rhs[24])));
569         _mm_store_ps(&lhs[28], _mm_add_ps(_mm_load_ps(&lhs[28]), _mm_load_ps(&rhs[28])));
570     }
571 
572     return lhs;
573 }
574 
575 #endif  // APPLESEED_USE_SSE
576 
577 template <typename T>
578 inline RGBSpectrum<T>& operator-=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
579 {
580     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
581         lhs[i] -= rhs[i];
582 
583     return lhs;
584 }
585 
586 template <typename T>
587 inline RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const T rhs)
588 {
589     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
590         lhs[i] *= rhs;
591 
592     return lhs;
593 }
594 
595 #ifdef APPLESEED_USE_SSE
596 
597 template <>
598 APPLESEED_FORCE_INLINE RGBSpectrum<float>& operator*=(RGBSpectrum<float>& lhs, const float rhs)
599 {
600     const __m128 mrhs = _mm_set1_ps(rhs);
601 
602     _mm_store_ps(&lhs[ 0], _mm_mul_ps(_mm_load_ps(&lhs[ 0]), mrhs));
603 
604     if (RGBSpectrum<float>::size() > 3)
605     {
606         _mm_store_ps(&lhs[ 4], _mm_mul_ps(_mm_load_ps(&lhs[ 4]), mrhs));
607         _mm_store_ps(&lhs[ 8], _mm_mul_ps(_mm_load_ps(&lhs[ 8]), mrhs));
608         _mm_store_ps(&lhs[12], _mm_mul_ps(_mm_load_ps(&lhs[12]), mrhs));
609         _mm_store_ps(&lhs[16], _mm_mul_ps(_mm_load_ps(&lhs[16]), mrhs));
610         _mm_store_ps(&lhs[20], _mm_mul_ps(_mm_load_ps(&lhs[20]), mrhs));
611         _mm_store_ps(&lhs[24], _mm_mul_ps(_mm_load_ps(&lhs[24]), mrhs));
612         _mm_store_ps(&lhs[28], _mm_mul_ps(_mm_load_ps(&lhs[28]), mrhs));
613     }
614 
615     return lhs;
616 }
617 
618 #endif  // APPLESEED_USE_SSE
619 
620 template <typename T>
621 inline RGBSpectrum<T>& operator*=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
622 {
623     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
624         lhs[i] *= rhs[i];
625 
626     return lhs;
627 }
628 
629 #ifdef APPLESEED_USE_SSE
630 
631 template <>
632 APPLESEED_FORCE_INLINE RGBSpectrum<float>& operator*=(RGBSpectrum<float>& lhs, const RGBSpectrum<float>& rhs)
633 {
634     _mm_store_ps(&lhs[ 0], _mm_mul_ps(_mm_load_ps(&lhs[ 0]), _mm_load_ps(&rhs[ 0])));
635 
636     if (RGBSpectrum<float>::size() > 3)
637     {
638         _mm_store_ps(&lhs[ 4], _mm_mul_ps(_mm_load_ps(&lhs[ 4]), _mm_load_ps(&rhs[ 4])));
639         _mm_store_ps(&lhs[ 8], _mm_mul_ps(_mm_load_ps(&lhs[ 8]), _mm_load_ps(&rhs[ 8])));
640         _mm_store_ps(&lhs[12], _mm_mul_ps(_mm_load_ps(&lhs[12]), _mm_load_ps(&rhs[12])));
641         _mm_store_ps(&lhs[16], _mm_mul_ps(_mm_load_ps(&lhs[16]), _mm_load_ps(&rhs[16])));
642         _mm_store_ps(&lhs[20], _mm_mul_ps(_mm_load_ps(&lhs[20]), _mm_load_ps(&rhs[20])));
643         _mm_store_ps(&lhs[24], _mm_mul_ps(_mm_load_ps(&lhs[24]), _mm_load_ps(&rhs[24])));
644         _mm_store_ps(&lhs[28], _mm_mul_ps(_mm_load_ps(&lhs[28]), _mm_load_ps(&rhs[28])));
645     }
646 
647     return lhs;
648 }
649 
650 #endif  // APPLESEED_USE_SSE
651 
652 template <typename T>
653 inline RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const T rhs)
654 {
655     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
656         lhs[i] /= rhs;
657 
658     return lhs;
659 }
660 
661 inline RGBSpectrum<float>& operator/=(RGBSpectrum<float>& lhs, const float rhs)
662 {
663     return lhs *= 1.0f / rhs;
664 }
665 
666 inline RGBSpectrum<double>& operator/=(RGBSpectrum<double>& lhs, const double rhs)
667 {
668     return lhs *= 1.0 / rhs;
669 }
670 
671 inline RGBSpectrum<long double>& operator/=(RGBSpectrum<long double>& lhs, const long double rhs)
672 {
673     return lhs *= 1.0L / rhs;
674 }
675 
676 template <typename T>
677 inline RGBSpectrum<T>& operator/=(RGBSpectrum<T>& lhs, const RGBSpectrum<T>& rhs)
678 {
679     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
680         lhs[i] /= rhs[i];
681 
682     return lhs;
683 }
684 
685 template <typename T>
madd(RGBSpectrum<T> & a,const RGBSpectrum<T> & b,const RGBSpectrum<T> & c)686 inline void madd(
687     RGBSpectrum<T>&                  a,
688     const RGBSpectrum<T>&            b,
689     const RGBSpectrum<T>&            c)
690 {
691     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
692         a[i] += b[i] * c[i];
693 }
694 
695 template <typename T>
madd(RGBSpectrum<T> & a,const RGBSpectrum<T> & b,const T c)696 inline void madd(
697     RGBSpectrum<T>&                  a,
698     const RGBSpectrum<T>&            b,
699     const T                          c)
700 {
701     for (size_t i = 0; i < RGBSpectrum<T>::size(); ++i)
702         a[i] += b[i] * c;
703 }
704 
705 #ifdef APPLESEED_USE_SSE
706 
707 template <>
madd(RGBSpectrum<float> & a,const RGBSpectrum<float> & b,const RGBSpectrum<float> & c)708 APPLESEED_FORCE_INLINE void madd(
709     RGBSpectrum<float>&             a,
710     const RGBSpectrum<float>&       b,
711     const RGBSpectrum<float>&       c)
712 {
713     _mm_store_ps(&a[0], _mm_add_ps(_mm_load_ps(&a[0]), _mm_mul_ps(_mm_load_ps(&b[0]), _mm_load_ps(&c[0]))));
714 }
715 
716 template <>
madd(RGBSpectrum<float> & a,const RGBSpectrum<float> & b,const float c)717 APPLESEED_FORCE_INLINE void madd(
718     RGBSpectrum<float>&             a,
719     const RGBSpectrum<float>&       b,
720     const float                             c)
721 {
722     const __m128 k = _mm_set_ps1(c);
723     _mm_store_ps(&a[0], _mm_add_ps(_mm_load_ps(&a[0]), _mm_mul_ps(_mm_load_ps(&b[0]), k)));
724 }
725 
726 #endif  // APPLESEED_USE_SSE
727 
728 }       // namespace renderer
729 
730 namespace foundation
731 {
732 
733 template <typename T>
is_zero(const renderer::RGBSpectrum<T> & s)734 inline bool is_zero(const renderer::RGBSpectrum<T>& s)
735 {
736     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
737     {
738         if (s[i] != T(0.0))
739             return false;
740     }
741 
742     return true;
743 }
744 
745 template <typename T>
feq(const renderer::RGBSpectrum<T> & lhs,const renderer::RGBSpectrum<T> & rhs)746 inline bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs)
747 {
748     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
749     {
750         if (!feq(lhs[i], rhs[i]))
751             return false;
752     }
753 
754     return true;
755 }
756 
757 template <typename T>
feq(const renderer::RGBSpectrum<T> & lhs,const renderer::RGBSpectrum<T> & rhs,const T eps)758 inline bool feq(const renderer::RGBSpectrum<T>& lhs, const renderer::RGBSpectrum<T>& rhs, const T eps)
759 {
760     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
761     {
762         if (!feq(lhs[i], rhs[i], eps))
763             return false;
764     }
765 
766     return true;
767 }
768 
769 template <typename T>
fz(const renderer::RGBSpectrum<T> & s)770 inline bool fz(const renderer::RGBSpectrum<T>& s)
771 {
772     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
773     {
774         if (!fz(s[i]))
775             return false;
776     }
777 
778     return true;
779 }
780 
781 template <typename T>
fz(const renderer::RGBSpectrum<T> & s,const T eps)782 inline bool fz(const renderer::RGBSpectrum<T>& s, const T eps)
783 {
784     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
785     {
786         if (!fz(s[i], eps))
787             return false;
788     }
789 
790     return true;
791 }
792 
793 template <typename T>
rcp(const renderer::RGBSpectrum<T> & s)794 inline renderer::RGBSpectrum<T> rcp(const renderer::RGBSpectrum<T>& s)
795 {
796     renderer::RGBSpectrum<T> result;
797 
798     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
799         result[i] = T(1.0) / s[i];
800 
801     return result;
802 }
803 
804 template <typename T>
is_saturated(const renderer::RGBSpectrum<T> & s)805 inline bool is_saturated(const renderer::RGBSpectrum<T>& s)
806 {
807     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
808     {
809         if (s[i] < T(0.0) || s[i] > T(1.0))
810             return false;
811     }
812 
813     return true;
814 }
815 
816 template <typename T>
saturate(const renderer::RGBSpectrum<T> & s)817 inline renderer::RGBSpectrum<T> saturate(const renderer::RGBSpectrum<T>& s)
818 {
819     renderer::RGBSpectrum<T> result;
820 
821     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
822         result[i] = saturate(s[i]);
823 
824     return result;
825 }
826 
827 template <typename T>
saturate_in_place(renderer::RGBSpectrum<T> & s)828 inline void saturate_in_place(renderer::RGBSpectrum<T>& s)
829 {
830     clamp_in_place(s, T(0.0), T(1.0));
831 }
832 
833 template <typename T>
clamp(const renderer::RGBSpectrum<T> & s,const T min,const T max)834 inline renderer::RGBSpectrum<T> clamp(const renderer::RGBSpectrum<T>& s, const T min, const T max)
835 {
836     renderer::RGBSpectrum<T> result;
837 
838     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
839         result[i] = clamp(s[i], min, max);
840 
841     return result;
842 }
843 
844 template <typename T>
clamp_in_place(renderer::RGBSpectrum<T> & s,const T min,const T max)845 inline void clamp_in_place(renderer::RGBSpectrum<T>& s, const T min, const T max)
846 {
847     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
848     {
849         if (s[i] < min)
850             s[i] = min;
851 
852         if (s[i] > max)
853             s[i] = max;
854     }
855 }
856 
857 template <typename T>
clamp_low(const renderer::RGBSpectrum<T> & s,const T min)858 inline renderer::RGBSpectrum<T> clamp_low(const renderer::RGBSpectrum<T>& s, const T min)
859 {
860     renderer::RGBSpectrum<T> result;
861 
862     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
863         result[i] = std::max(s[i], min);
864 
865     return result;
866 }
867 
868 template <typename T>
clamp_low_in_place(renderer::RGBSpectrum<T> & s,const T min)869 inline void clamp_low_in_place(renderer::RGBSpectrum<T>& s, const T min)
870 {
871     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
872     {
873         if (s[i] < min)
874             s[i] = min;
875     }
876 }
877 
878 template <typename T>
clamp_high(const renderer::RGBSpectrum<T> & s,const T max)879 inline renderer::RGBSpectrum<T> clamp_high(const renderer::RGBSpectrum<T>& s, const T max)
880 {
881     renderer::RGBSpectrum<T> result;
882 
883     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
884         result[i] = std::min(s[i], max);
885 
886     return result;
887 }
888 
889 template <typename T>
clamp_high_in_place(renderer::RGBSpectrum<T> & s,const T max)890 inline void clamp_high_in_place(renderer::RGBSpectrum<T>& s, const T max)
891 {
892     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
893     {
894         if (s[i] > max)
895             s[i] = max;
896     }
897 }
898 
899 template <typename T>
lerp(const renderer::RGBSpectrum<T> & a,const renderer::RGBSpectrum<T> & b,const renderer::RGBSpectrum<T> & t)900 inline renderer::RGBSpectrum<T> lerp(
901     const renderer::RGBSpectrum<T>& a,
902     const renderer::RGBSpectrum<T>& b,
903     const renderer::RGBSpectrum<T>& t)
904 {
905     renderer::RGBSpectrum<T> result;
906 
907     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
908         result[i] = foundation::lerp(a[i], b[i], t[i]);
909 
910     return result;
911 }
912 
913 #ifdef APPLESEED_USE_SSE
914 
915 template <>
lerp(const renderer::RGBSpectrum<float> & a,const renderer::RGBSpectrum<float> & b,const renderer::RGBSpectrum<float> & t)916 APPLESEED_FORCE_INLINE renderer::RGBSpectrum<float> lerp(
917     const renderer::RGBSpectrum<float>& a,
918     const renderer::RGBSpectrum<float>& b,
919     const renderer::RGBSpectrum<float>& t)
920 {
921     renderer::RGBSpectrum<float> result;
922 
923     __m128 one4 = _mm_set1_ps(1.0f);
924     __m128 t4 = _mm_load_ps(&t[0]);
925     __m128 one_minus_t4 = _mm_sub_ps(one4, t4);
926     __m128 x = _mm_mul_ps(_mm_load_ps(&a[0]), one_minus_t4);
927     __m128 y = _mm_mul_ps(_mm_load_ps(&b[0]), t4);
928     _mm_store_ps(&result[0], _mm_add_ps(x, y));
929 
930     return result;
931 }
932 
933 #endif  // APPLESEED_USE_SSE
934 
935 template <typename T>
min_value(const renderer::RGBSpectrum<T> & s)936 inline T min_value(const renderer::RGBSpectrum<T>& s)
937 {
938     return std::min(std::min(s[0], s[1]), s[2]);
939 }
940 
941 template <typename T>
max_value(const renderer::RGBSpectrum<T> & s)942 inline T max_value(const renderer::RGBSpectrum<T>& s)
943 {
944     return std::max(std::max(s[0], s[1]), s[2]);
945 }
946 
947 template <typename T>
min_index(const renderer::RGBSpectrum<T> & s)948 inline size_t min_index(const renderer::RGBSpectrum<T>& s)
949 {
950     size_t index = 0;
951     T value = s[0];
952 
953     for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
954     {
955         const T x = s[i];
956 
957         if (value > x)
958         {
959             value = x;
960             index = i;
961         }
962     }
963 
964     return index;
965 }
966 
967 template <typename T>
max_index(const renderer::RGBSpectrum<T> & s)968 inline size_t max_index(const renderer::RGBSpectrum<T>& s)
969 {
970     size_t index = 0;
971     T value = s[0];
972 
973     for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
974     {
975         const T x = s[i];
976 
977         if (value < x)
978         {
979             value = x;
980             index = i;
981         }
982     }
983 
984     return index;
985 }
986 
987 template <typename T>
min_abs_index(const renderer::RGBSpectrum<T> & s)988 inline size_t min_abs_index(const renderer::RGBSpectrum<T>& s)
989 {
990     size_t index = 0;
991     T value = std::abs(s[0]);
992 
993     for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
994     {
995         const T x = std::abs(s[i]);
996 
997         if (value > x)
998         {
999             value = x;
1000             index = i;
1001         }
1002     }
1003 
1004     return index;
1005 }
1006 
1007 template <typename T>
max_abs_index(const renderer::RGBSpectrum<T> & s)1008 inline size_t max_abs_index(const renderer::RGBSpectrum<T>& s)
1009 {
1010     size_t index = 0;
1011     T value = std::abs(s[0]);
1012 
1013     for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1014     {
1015         const T x = std::abs(s[i]);
1016 
1017         if (value < x)
1018         {
1019             value = x;
1020             index = i;
1021         }
1022     }
1023 
1024     return index;
1025 }
1026 
1027 template <typename T>
sum_value(const renderer::RGBSpectrum<T> & s)1028 inline T sum_value(const renderer::RGBSpectrum<T>& s)
1029 {
1030     T sum = s[0];
1031 
1032     for (size_t i = 1, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1033         sum += s[i];
1034 
1035     return sum;
1036 }
1037 
1038 template <typename T>
average_value(const renderer::RGBSpectrum<T> & s)1039 inline T average_value(const renderer::RGBSpectrum<T>& s)
1040 {
1041     return sum_value(s) / renderer::RGBSpectrum<T>::size();
1042 }
1043 
1044 template <typename T>
has_nan(const renderer::RGBSpectrum<T> & s)1045 inline bool has_nan(const renderer::RGBSpectrum<T>& s)
1046 {
1047     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1048     {
1049         if (s[i] != s[i])
1050             return true;
1051     }
1052 
1053     return false;
1054 }
1055 
1056 template <typename T>
is_finite(const renderer::RGBSpectrum<T> & s)1057 inline bool is_finite(const renderer::RGBSpectrum<T>& s)
1058 {
1059     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1060     {
1061         if (!FP<T>::is_finite(s[i]))
1062             return false;
1063     }
1064 
1065     return true;
1066 }
1067 
1068 template <typename T>
sqrt(const renderer::RGBSpectrum<T> & s)1069 inline renderer::RGBSpectrum<T> sqrt(const renderer::RGBSpectrum<T>& s)
1070 {
1071     renderer::RGBSpectrum<T> result;
1072 
1073     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1074         result[i] = std::sqrt(s[i]);
1075 
1076     return result;
1077 }
1078 
1079 #ifdef APPLESEED_USE_SSE
1080 
sqrt(const renderer::RGBSpectrum<float> & s)1081 APPLESEED_FORCE_INLINE renderer::RGBSpectrum<float> sqrt(const renderer::RGBSpectrum<float>& s)
1082 {
1083     renderer::RGBSpectrum<float> result;
1084     _mm_store_ps(&result[ 0], _mm_sqrt_ps(_mm_load_ps(&s[ 0])));
1085     return result;
1086 }
1087 
1088 #endif  // APPLESEED_USE_SSE
1089 
1090 template <typename T>
pow(const renderer::RGBSpectrum<T> & x,const T y)1091 inline renderer::RGBSpectrum<T> pow(const renderer::RGBSpectrum<T>& x, const T y)
1092 {
1093     renderer::RGBSpectrum<T> result;
1094 
1095     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1096         result[i] = std::pow(x[i], y);
1097 
1098     return result;
1099 }
1100 
1101 template <typename T>
pow(const renderer::RGBSpectrum<T> & x,const renderer::RGBSpectrum<T> & y)1102 inline renderer::RGBSpectrum<T> pow(
1103     const renderer::RGBSpectrum<T>& x,
1104     const renderer::RGBSpectrum<T>& y)
1105 {
1106     renderer::RGBSpectrum<T> result;
1107 
1108     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1109         result[i] = std::pow(x[i], y[i]);
1110 
1111     return result;
1112 }
1113 
1114 template <typename T>
log(const renderer::RGBSpectrum<T> & s)1115 inline renderer::RGBSpectrum<T> log(const renderer::RGBSpectrum<T>& s)
1116 {
1117     renderer::RGBSpectrum<T> result;
1118 
1119     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1120         result[i] = std::log(s[i]);
1121 
1122     return result;
1123 }
1124 
1125 template <typename T>
exp(const renderer::RGBSpectrum<T> & s)1126 inline renderer::RGBSpectrum<T> exp(const renderer::RGBSpectrum<T>& s)
1127 {
1128     renderer::RGBSpectrum<T> result;
1129 
1130     for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1131         result[i] = std::exp(s[i]);
1132 
1133     return result;
1134 }
1135 
1136 template <typename T>
1137 class PoisonImpl<renderer::RGBSpectrum<T>>
1138 {
1139   public:
do_poison(renderer::RGBSpectrum<T> & s)1140     static void do_poison(renderer::RGBSpectrum<T>& s)
1141     {
1142         for (size_t i = 0, e = renderer::RGBSpectrum<T>::size(); i < e; ++i)
1143             debug_poison(s[i]);
1144     }
1145 };
1146 
1147 }   // namespace foundation
1148