1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * This file is part of openfx-supportext <https://github.com/devernay/openfx-supportext>,
4  * Copyright (C) 2013-2018 INRIA
5  *
6  * openfx-supportext is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * openfx-supportext is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with openfx-supportext.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
18  * ***** END LICENSE BLOCK ***** */
19 
20 /*
21  * OFX color-spaces transformations support as-well as bit-depth conversions.
22  */
23 
24 #ifndef openfx_supportext_ofxsLut_h
25 #define openfx_supportext_ofxsLut_h
26 
27 #include <string>
28 #include <map>
29 #include <cmath>
30 #include <cassert>
31 #include <cstring> // for memcpy
32 #include <cstdlib> // for rand
33 #include <memory> // for auto_ptr
34 
35 #include "ofxCore.h"
36 #include "ofxsImageEffect.h"
37 #include "ofxsMacros.h"
38 #include "ofxsPixelProcessor.h"
39 #include "ofxsMultiThread.h"
40 #include "ofxsThreadSuite.h"
41 
42 #define OFXS_HUE_CIRCLE 1.f // if hue should be between 0 and 1
43 //#define OFXS_HUE_CIRCLE 360.f // if hue should be in degrees
44 
45 namespace OFX {
46 namespace Color {
47 /// numvals should be 256 for byte, 65536 for 16-bits, etc.
48 
49 /// maps 0-(numvals-1) to 0.-1.
50 template<int numvals>
51 float
intToFloat(int value)52 intToFloat(int value)
53 {
54     return value * ( 1.f / (numvals - 1) );
55 }
56 
57 /// maps °.-1. to 0-(numvals-1)
58 template<int numvals>
59 int
floatToInt(float value)60 floatToInt(float value)
61 {
62     if (value <= 0) {
63         return 0;
64     } else if (value >= 1.) {
65         return numvals - 1;
66     }
67 
68     //return value * (numvals - 1) + 0.5; // gives a bad result with gcc 7.2.0 32 bits for value=intToFloat<0xff01>(0x8080)
69     float v = value * (numvals - 1) + 0.5f;
70     return int(v);
71 }
72 
73 /// maps 0x0-0xffff to 0x0-0xff
74 inline unsigned char
uint16ToChar(unsigned short quantum)75 uint16ToChar(unsigned short quantum)
76 {
77     // see ScaleQuantumToChar() in ImageMagick's magick/quantum.h
78 
79     /* test:
80        for(int i=0; i < 0x10000; ++i) {
81        printf("%x -> %x,%x\n", i, uint16ToChar(i), floatToInt<256>(intToFloat<65536>(i)));
82        assert(uint16ToChar(i) == floatToInt<256>(intToFloat<65536>(i)));
83        }
84      */
85     return (unsigned char) ( ( (quantum + 128UL) - ( (quantum + 128UL) >> 8 ) ) >> 8 );
86 }
87 
88 /// maps 0x0-0xff to 0x0-0xffff
89 inline unsigned short
charToUint16(unsigned char quantum)90 charToUint16(unsigned char quantum)
91 {
92     /* test:
93        for(int i=0; i < 0x100; ++i) {
94        printf("%x -> %x,%x\n", i, charToUint16(i), floatToInt<65536>(intToFloat<256>(i)));
95        assert(charToUint16(i) == floatToInt<65536>(intToFloat<256>(i)));
96        assert(i == uint16ToChar(charToUint16(i)));
97        }
98      */
99     return (unsigned short) ( (quantum << 8) | quantum );
100 }
101 
102 // maps 0x0-0xff00 to 0x0-0xff
103 inline unsigned char
uint8xxToChar(unsigned short quantum)104 uint8xxToChar(unsigned short quantum)
105 {
106     /* test:
107        for(int i=0; i < 0xff01; ++i) {
108        printf("%x -> %x,%x, err=%d\n", i, uint8xxToChar(i), floatToInt<256>(intToFloat<0xff01>(i)),i - charToUint8xx(uint8xxToChar(i)));
109        assert(uint8xxToChar(i) == floatToInt<256>(intToFloat<0xff01>(i)));
110        }
111      */
112     return (unsigned char) ( (quantum + 0x80) >> 8 );
113 }
114 
115 // maps 0x0-0xff to 0x0-0xff00
116 inline unsigned short
charToUint8xx(unsigned char quantum)117 charToUint8xx(unsigned char quantum)
118 {
119     /* test:
120        for(int i=0; i < 0x100; ++i) {
121        printf("%x -> %x,%x\n", i, charToUint8xx(i), floatToInt<0xff01>(intToFloat<256>(i)));
122        assert(charToUint8xx(i) == floatToInt<0xff01>(intToFloat<256>(i)));
123        assert(i == uint8xxToChar(charToUint8xx(i)));
124        }
125      */
126     return (unsigned short) (quantum << 8);
127 }
128 
129 /* @brief Converts a float ranging in [0 - 1.f] in the desired color-space to linear color-space also ranging in [0 - 1.f]*/
130 typedef float (*fromColorSpaceFunctionV1)(float v);
131 
132 /* @brief Converts a float ranging in [0 - 1.f] in  linear color-space to the desired color-space to also ranging in [0 - 1.f]*/
133 typedef float (*toColorSpaceFunctionV1)(float v);
134 
135 
136 /**
137  * @brief A Lut (look-up table) used to speed-up color-spaces conversions.
138  * If you plan on doing linear conversion, you should just use the Linear class instead.
139  **/
140 class Lut
141 {
142     template<class MUTEX>
143     friend class LutManager;
144 
145     std::string _name;                 ///< name of the lut
146     fromColorSpaceFunctionV1 _fromFunc;
147     toColorSpaceFunctionV1 _toFunc;
148 
149     /// the fast lookup tables are mutable, because they are automatically initialized post-construction,
150     /// and never change afterwards
151     mutable unsigned short toFunc_hipart_to_uint8xx[0x10000];                 /// contains  2^16 = 65536 values between 0-255
152     mutable float fromFunc_uint8_to_float[256];                 /// values between 0-1.f
153 
154 private:
155     // Luts should be allocated and destroyed  through the LutManager
Lut(const std::string & name,fromColorSpaceFunctionV1 fromFunc,toColorSpaceFunctionV1 toFunc)156     Lut(const std::string & name,
157         fromColorSpaceFunctionV1 fromFunc,
158         toColorSpaceFunctionV1 toFunc)
159         : _name(name)
160         , _fromFunc(fromFunc)
161         , _toFunc(toFunc)
162     {
163         fillTables();
164     }
165 
~Lut()166     virtual ~Lut()
167     {
168     }
169 
170 
171     ///init luts
172     ///it uses fromColorSpaceFloatToLinearFloat(float) and toColorSpaceFloatFromLinearFloat(float)
173     ///Called by validate()
fillTables()174     void fillTables() const
175     {
176         // fill all
177         for (int i = 0; i < 0x10000; ++i) {
178             float inp = index_to_float( (unsigned short)i );
179             float f = _toFunc(inp);
180             toFunc_hipart_to_uint8xx[i] = Color::floatToInt<0xff01>(f);
181         }
182         // fill fromFunc_uint8_to_float, and make sure that
183         // the entries of toFunc_hipart_to_uint8xx corresponding
184         // to the transform of each byte value contain the same value,
185         // so that toFunc(fromFunc(b)) is identity
186         //
187         for (int b = 0; b < 256; ++b) {
188             float f = _fromFunc( Color::intToFloat<256>(b) );
189             fromFunc_uint8_to_float[b] = f;
190             int i = hipart(f);
191             toFunc_hipart_to_uint8xx[i] = Color::charToUint8xx(b);
192         }
193     }
194 
195 public:
196 
197     /* @brief Converts a float ranging in [0 - 1.f] in the desired color-space to linear color-space also ranging in [0 - 1.f]
198      * This function is not fast!
199      * @see fromColorSpaceFloatToLinearFloatFast(float)
200      */
fromColorSpaceFloatToLinearFloat(float v)201     float fromColorSpaceFloatToLinearFloat(float v) const WARN_UNUSED_RETURN
202     {
203         return _fromFunc(v);
204     }
205 
206     /* @brief Converts a float ranging in [0 - 1.f] in  linear color-space to the desired color-space to also ranging in [0 - 1.f]
207      * This function is not fast!
208      */
toColorSpaceFloatFromLinearFloat(float v)209     float toColorSpaceFloatFromLinearFloat(float v) const WARN_UNUSED_RETURN
210     {
211         return _toFunc(v);
212     }
213 
214     /* @brief Converts a float ranging in [0 - 1.f] in linear color-space using the look-up tables.
215      * @return A float in [0 - 1.f] in the destination color-space.
216      */
217     // It is not recommended to use this function, because the output is quantized
218     // If one really needs float, one has to use the full function (or OpenColorIO)
219 
220     /* @brief Converts a float ranging in [0 - 1.f] in linear color-space using the look-up tables.
221      * @return A byte in [0 - 255] in the destination color-space.
222      */
toColorSpaceUint8FromLinearFloatFast(float v)223     unsigned char toColorSpaceUint8FromLinearFloatFast(float v) const WARN_UNUSED_RETURN
224     {
225         return Color::uint8xxToChar(toFunc_hipart_to_uint8xx[hipart(v)]);
226     }
227 
228     /* @brief Converts a float ranging in [0 - 1.f] in linear color-space using the look-up tables.
229      * @return An unsigned short in [0 - 0xff00] in the destination color-space.
230      */
toColorSpaceUint8xxFromLinearFloatFast(float v)231     unsigned short toColorSpaceUint8xxFromLinearFloatFast(float v) const WARN_UNUSED_RETURN
232     {
233         return toFunc_hipart_to_uint8xx[hipart(v)];
234     }
235 
236     // the following only works for increasing LUTs
237 
238     /* @brief Converts a float ranging in [0 - 1.f] in linear color-space using the look-up tables.
239      * @return An unsigned short in [0 - 65535] in the destination color-space.
240      * This function uses localluy linear approximations of the transfer function.
241      */
toColorSpaceUint16FromLinearFloatFast(float v)242     unsigned short toColorSpaceUint16FromLinearFloatFast(float v) const WARN_UNUSED_RETURN
243     {
244         // algorithm:
245         // - convert to 8 bits -> val8u
246         // - convert val8u-1, val8u and val8u+1 to float
247         // - interpolate linearly in the right interval
248         unsigned char v8u = toColorSpaceUint8FromLinearFloatFast(v);
249         unsigned char v8u_next, v8u_prev;
250         float v32f_next, v32f_prev;
251         if (v8u == 0) {
252             v8u_prev = 0;
253             v8u_next = 1;
254             v32f_prev = fromColorSpaceUint8ToLinearFloatFast(0);
255             v32f_next = fromColorSpaceUint8ToLinearFloatFast(1);
256         } else if (v8u == 255) {
257             v8u_prev = 254;
258             v8u_next = 255;
259             v32f_prev = fromColorSpaceUint8ToLinearFloatFast(254);
260             v32f_next = fromColorSpaceUint8ToLinearFloatFast(255);
261         } else {
262             float v32f = fromColorSpaceUint8ToLinearFloatFast(v8u);
263             // we suppose the LUT is an increasing func
264             if (v < v32f) {
265                 v8u_prev = v8u - 1;
266                 v32f_prev = fromColorSpaceUint8ToLinearFloatFast(v8u_prev);
267                 v8u_next = v8u;
268                 v32f_next = v32f;
269             } else {
270                 v8u_prev = v8u;
271                 v32f_prev = v32f;
272                 v8u_next = v8u + 1;
273                 v32f_next = fromColorSpaceUint8ToLinearFloatFast(v8u_next);
274             }
275         }
276 
277         // interpolate linearly
278         return (short)((v8u_prev << 8) + v8u_prev + (v - v32f_prev) * ( ( (v8u_next - v8u_prev) << 8 ) + (v8u_next + v8u_prev) ) / (v32f_next - v32f_prev) + 0.5f);
279     }
280 
281     /* @brief Converts a byte ranging in [0 - 255] in the destination color-space using the look-up tables.
282      * @return A float in [0 - 1.f] in linear color-space.
283      */
fromColorSpaceUint8ToLinearFloatFast(unsigned char v)284     float fromColorSpaceUint8ToLinearFloatFast(unsigned char v) const WARN_UNUSED_RETURN
285     {
286         return fromFunc_uint8_to_float[v];
287     }
288 
289     /* @brief Converts a short ranging in [0 - 65535] in the destination color-space using the look-up tables.
290      * @return A float in [0 - 1.f] in linear color-space.
291      */
fromColorSpaceUint16ToLinearFloatFast(unsigned short v)292     float fromColorSpaceUint16ToLinearFloatFast(unsigned short v) const WARN_UNUSED_RETURN
293     {
294         // the following is from ImageMagick's quantum.h
295         unsigned char v8u_prev = ( v - (v >> 8) ) >> 8;
296         unsigned char v8u_next = v8u_prev + 1;
297         unsigned short v16u_prev = (v8u_prev << 8) + v8u_prev;
298         unsigned short v16u_next = (v8u_next << 8) + v8u_next;
299         float v32f_prev = fromColorSpaceUint8ToLinearFloatFast(v8u_prev);
300         float v32f_next = fromColorSpaceUint8ToLinearFloatFast(v8u_next);
301 
302         // interpolate linearly
303         return v32f_prev + (v - v16u_prev) * (v32f_next - v32f_prev) / (v16u_next - v16u_prev);
304     }
305 
306     /* @brief convert from float to byte with dithering (error diffusion).
307      It uses random numbers for error diffusion, and thus the result is different at each function call. */
to_byte_packed_dither(const void * pixelData,const OfxRectI & bounds,OFX::PixelComponentEnum pixelComponents,int pixelComponentCount,OFX::BitDepthEnum bitDepth,int rowBytes,const OfxRectI & renderWindow,void * dstPixelData,const OfxRectI & dstBounds,OFX::PixelComponentEnum dstPixelComponents,int dstPixelComponentCount,OFX::BitDepthEnum dstBitDepth,int dstRowBytes)308     void to_byte_packed_dither(const void* pixelData,
309                                const OfxRectI & bounds,
310                                OFX::PixelComponentEnum pixelComponents,
311                                int pixelComponentCount,
312                                OFX::BitDepthEnum bitDepth,
313                                int rowBytes,
314                                const OfxRectI & renderWindow,
315                                void* dstPixelData,
316                                const OfxRectI & dstBounds,
317                                OFX::PixelComponentEnum dstPixelComponents,
318                                int dstPixelComponentCount,
319                                OFX::BitDepthEnum dstBitDepth,
320                                int dstRowBytes) const
321     {
322         assert(bitDepth == eBitDepthFloat && dstBitDepth == eBitDepthUByte && pixelComponents == dstPixelComponents);
323         assert(bounds.x1 <= renderWindow.x1 && renderWindow.x2 <= bounds.x2 &&
324                bounds.y1 <= renderWindow.y1 && renderWindow.y2 <= bounds.y2 &&
325                dstBounds.x1 <= renderWindow.x1 && renderWindow.x2 <= dstBounds.x2 &&
326                dstBounds.y1 <= renderWindow.y1 && renderWindow.y2 <= dstBounds.y2);
327         if (pixelComponents == ePixelComponentAlpha) {
328             // alpha: no dither
329             return to_byte_packed_nodither(pixelData, bounds, pixelComponents, pixelComponentCount, bitDepth, rowBytes,
330                                            renderWindow,
331                                            dstPixelData, dstBounds, dstPixelComponents, dstPixelComponentCount, dstBitDepth, dstRowBytes);
332         }
333         //validate();
334 
335         const int nComponents = dstPixelComponentCount;
336         assert(dstPixelComponentCount == 3 || dstPixelComponentCount == 4);
337 
338         for (int y = renderWindow.y1; y < renderWindow.y2; ++y) {
339             // coverity[dont_call]
340             int xstart = renderWindow.x1 + std::rand() % (renderWindow.x2 - renderWindow.x1);
341             unsigned error[3] = {
342                 0x80, 0x80, 0x80
343             };
344             const float *src_pixels = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, xstart, y);
345             unsigned char *dst_pixels = (unsigned char*)OFX::getPixelAddress(dstPixelData, dstBounds, dstPixelComponents, dstBitDepth, dstRowBytes, xstart, y);
346 
347             /* go forward from starting point to end of line: */
348             const float *src_end = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, renderWindow.x2, y, false);
349 
350             while (src_pixels < src_end) {
351                 for (int k = 0; k < 3; ++k) {
352                     error[k] = (error[k] & 0xff) + toColorSpaceUint8xxFromLinearFloatFast(src_pixels[k]);
353                     assert(error[k] < 0x10000);
354                     dst_pixels[k] = (unsigned char)(error[k] >> 8);
355                 }
356                 if (nComponents == 4) {
357                     // alpha channel: no dithering
358                     dst_pixels[3] = floatToInt<256>(src_pixels[3]);
359                 }
360                 dst_pixels += nComponents;
361                 src_pixels += nComponents;
362             }
363 
364             if (xstart > 0) {
365                 /* go backward from starting point to start of line: */
366                 src_pixels = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, xstart - 1, y);
367                 src_end = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, 0, y);
368                 dst_pixels = (unsigned char*)OFX::getPixelAddress(dstPixelData, dstBounds, dstPixelComponents, dstBitDepth, dstRowBytes, xstart - 1, y);
369 
370                 for (int i = 0; i < 3; ++i) {
371                     error[i] = 0x80;
372                 }
373 
374                 while (src_pixels >= src_end) {
375                     for (int k = 0; k < 3; ++k) {
376                         error[k] = (error[k] & 0xff) + toColorSpaceUint8xxFromLinearFloatFast(src_pixels[k]);
377                         assert(error[k] < 0x10000);
378                         dst_pixels[k] = (unsigned char)(error[k] >> 8);
379                     }
380                     if (nComponents == 4) {
381                         // alpha channel: no colorspace conversion & no dithering
382                         dst_pixels[3] = floatToInt<256>(src_pixels[3]);
383                     }
384                     dst_pixels -= nComponents;
385                     src_pixels -= nComponents;
386                 }
387             }
388         }
389     } // to_byte_packed_dither
390 
391     /* @brief convert from float to byte without dithering. */
to_byte_packed_nodither(const void * pixelData,const OfxRectI & bounds,OFX::PixelComponentEnum pixelComponents,int pixelComponentCount,OFX::BitDepthEnum bitDepth,int rowBytes,const OfxRectI & renderWindow,void * dstPixelData,const OfxRectI & dstBounds,OFX::PixelComponentEnum dstPixelComponents,int dstPixelComponentCount,OFX::BitDepthEnum dstBitDepth,int dstRowBytes)392     void to_byte_packed_nodither(const void* pixelData,
393                                  const OfxRectI & bounds,
394                                  OFX::PixelComponentEnum pixelComponents,
395                                  int pixelComponentCount,
396                                  OFX::BitDepthEnum bitDepth,
397                                  int rowBytes,
398                                  const OfxRectI & renderWindow,
399                                  void* dstPixelData,
400                                  const OfxRectI & dstBounds,
401                                  OFX::PixelComponentEnum dstPixelComponents,
402                                  int dstPixelComponentCount,
403                                  OFX::BitDepthEnum dstBitDepth,
404                                  int dstRowBytes) const
405     {
406         assert(bitDepth == eBitDepthFloat && dstBitDepth == eBitDepthUByte);
407         assert(pixelComponents == ePixelComponentRGBA || pixelComponents == ePixelComponentRGB || pixelComponents == ePixelComponentAlpha);
408         assert(dstPixelComponents == ePixelComponentRGBA || dstPixelComponents == ePixelComponentRGB || dstPixelComponents == ePixelComponentAlpha);
409         assert(bounds.x1 <= renderWindow.x1 && renderWindow.x2 <= bounds.x2 &&
410                bounds.y1 <= renderWindow.y1 && renderWindow.y2 <= bounds.y2 &&
411                dstBounds.x1 <= renderWindow.x1 && renderWindow.x2 <= dstBounds.x2 &&
412                dstBounds.y1 <= renderWindow.y1 && renderWindow.y2 <= dstBounds.y2);
413         //validate();
414 
415         const int srcComponents = pixelComponentCount;
416         const int dstComponents = dstPixelComponentCount;
417 
418         for (int y = renderWindow.y1; y < renderWindow.y2; ++y) {
419             const float *src_pixels = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, 0, y);
420             unsigned char *dst_pixels = (unsigned char*)OFX::getPixelAddress(dstPixelData, dstBounds, dstPixelComponents, dstBitDepth, dstRowBytes, 0, y);
421             const float *src_end = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, renderWindow.x2, y, false);
422             unsigned char tmpPixel[4] = {0, 0, 0, 0};
423             while (src_pixels != src_end) {
424                 if (srcComponents == 1) {
425                     // alpha channel: no colorspace conversion
426                     tmpPixel[3] = floatToInt<256>(src_pixels[0]);
427                 } else {
428                     for (int k = 0; k < 3; ++k) {
429                         tmpPixel[k] = toColorSpaceUint8FromLinearFloatFast(src_pixels[k]);
430                     }
431                     if (srcComponents == 4) {
432                         // alpha channel: no colorspace conversion
433                         tmpPixel[3] = floatToInt<256>(src_pixels[3]);
434                     }
435                 }
436                 if (dstComponents == 1) {
437                     dst_pixels[0] = tmpPixel[3];
438                 } else {
439                     for (int k = 0; k < dstComponents; ++k) {
440                         dst_pixels[k] = tmpPixel[k];
441                     }
442                 }
443                 dst_pixels += dstComponents;
444                 src_pixels += srcComponents;
445             }
446         }
447     } // to_byte_packed_nodither
448 
449     /* @brief uses Rec.709 to convert from color to grayscale. */
to_byte_grayscale_nodither(const void * pixelData,const OfxRectI & bounds,OFX::PixelComponentEnum pixelComponents,int pixelComponentCount,OFX::BitDepthEnum bitDepth,int rowBytes,const OfxRectI & renderWindow,void * dstPixelData,const OfxRectI & dstBounds,OFX::PixelComponentEnum dstPixelComponents,int dstPixelComponentCount,OFX::BitDepthEnum dstBitDepth,int dstRowBytes)450     void to_byte_grayscale_nodither(const void* pixelData,
451                                     const OfxRectI & bounds,
452                                     OFX::PixelComponentEnum pixelComponents,
453                                     int pixelComponentCount,
454                                     OFX::BitDepthEnum bitDepth,
455                                     int rowBytes,
456                                     const OfxRectI & renderWindow,
457                                     void* dstPixelData,
458                                     const OfxRectI & dstBounds,
459                                     OFX::PixelComponentEnum dstPixelComponents,
460                                     int dstPixelComponentCount,
461                                     OFX::BitDepthEnum dstBitDepth,
462                                     int dstRowBytes) const
463     {
464         assert(bitDepth == eBitDepthFloat && dstBitDepth == eBitDepthUByte &&
465                (pixelComponents == ePixelComponentRGB || pixelComponents == ePixelComponentRGBA) &&
466                dstPixelComponents == ePixelComponentAlpha &&
467                (pixelComponentCount == 3 || pixelComponentCount == 4) &&
468                dstPixelComponentCount == 1);
469         assert(bounds.x1 <= renderWindow.x1 && renderWindow.x2 <= bounds.x2 &&
470                bounds.y1 <= renderWindow.y1 && renderWindow.y2 <= bounds.y2 &&
471                dstBounds.x1 <= renderWindow.x1 && renderWindow.x2 <= dstBounds.x2 &&
472                dstBounds.y1 <= renderWindow.y1 && renderWindow.y2 <= dstBounds.y2);
473         //validate();
474 
475         const int srcComponents = pixelComponentCount;
476 
477         for (int y = renderWindow.y1; y < renderWindow.y2; ++y) {
478             const float *src_pixels = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, 0, y);
479             unsigned char *dst_pixels = (unsigned char*)OFX::getPixelAddress(dstPixelData, dstBounds, dstPixelComponents, dstBitDepth, dstRowBytes, 0, y);
480             const float *src_end = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, renderWindow.x2, y, false);
481 
482             while (src_pixels != src_end) {
483                 float l = 0.2126f * src_pixels[0] + 0.7152f * src_pixels[1] + 0.0722f * src_pixels[2]; // Rec.709 luminance formula
484                 dst_pixels[0] = toColorSpaceUint8FromLinearFloatFast(l);
485                 ++dst_pixels;
486                 src_pixels += srcComponents;
487             }
488         }
489     } // to_byte_packed_nodither
490 
491     /* @brief convert from float to short without dithering. */
to_short_packed(const void * pixelData,const OfxRectI & bounds,OFX::PixelComponentEnum pixelComponents,int pixelComponentCount,OFX::BitDepthEnum bitDepth,int rowBytes,const OfxRectI & renderWindow,void * dstPixelData,const OfxRectI & dstBounds,OFX::PixelComponentEnum dstPixelComponents,int dstPixelComponentCount,OFX::BitDepthEnum dstBitDepth,int dstRowBytes)492     void to_short_packed(const void* pixelData,
493                          const OfxRectI & bounds,
494                          OFX::PixelComponentEnum pixelComponents,
495                          int pixelComponentCount,
496                          OFX::BitDepthEnum bitDepth,
497                          int rowBytes,
498                          const OfxRectI & renderWindow,
499                          void* dstPixelData,
500                          const OfxRectI & dstBounds,
501                          OFX::PixelComponentEnum dstPixelComponents,
502                          int dstPixelComponentCount,
503                          OFX::BitDepthEnum dstBitDepth,
504                          int dstRowBytes) const
505     {
506         assert(bitDepth == eBitDepthFloat && dstBitDepth == eBitDepthUShort && pixelComponents == dstPixelComponents && pixelComponentCount == dstPixelComponentCount);
507         assert(bounds.x1 <= renderWindow.x1 && renderWindow.x2 <= bounds.x2 &&
508                bounds.y1 <= renderWindow.y1 && renderWindow.y2 <= bounds.y2 &&
509                dstBounds.x1 <= renderWindow.x1 && renderWindow.x2 <= dstBounds.x2 &&
510                dstBounds.y1 <= renderWindow.y1 && renderWindow.y2 <= dstBounds.y2);
511         //validate();
512 
513         const int nComponents = pixelComponentCount;
514 
515         for (int y = renderWindow.y1; y < renderWindow.y2; ++y) {
516             const float *src_pixels = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, 0, y);
517             unsigned short *dst_pixels = (unsigned short*)OFX::getPixelAddress(dstPixelData, dstBounds, dstPixelComponents, dstBitDepth, dstRowBytes, 0, y);
518             const float *src_end = (const float*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, renderWindow.x2, y, false);
519 
520             while (src_pixels != src_end) {
521                 if (nComponents == 1) {
522                     // alpha channel: no colorspace conversion
523                     dst_pixels[0] = floatToInt<65536>(src_pixels[0]);
524                 } else {
525                     for (int k = 0; k < 3; ++k) {
526                         dst_pixels[k] = toColorSpaceUint16FromLinearFloatFast(src_pixels[k]);
527                     }
528                     if (nComponents == 4) {
529                         // alpha channel: no colorspace conversion
530                         dst_pixels[3] = floatToInt<65536>(src_pixels[3]);
531                     }
532                 }
533                 dst_pixels += nComponents;
534                 src_pixels += nComponents;
535             }
536         }
537     }
538 
from_byte_packed(const void * pixelData,const OfxRectI & bounds,OFX::PixelComponentEnum pixelComponents,int pixelComponentCount,OFX::BitDepthEnum bitDepth,int rowBytes,const OfxRectI & renderWindow,void * dstPixelData,const OfxRectI & dstBounds,OFX::PixelComponentEnum dstPixelComponents,int dstPixelComponentCount,OFX::BitDepthEnum dstBitDepth,int dstRowBytes)539     void from_byte_packed(const void* pixelData,
540                           const OfxRectI & bounds,
541                           OFX::PixelComponentEnum pixelComponents,
542                           int pixelComponentCount,
543                           OFX::BitDepthEnum bitDepth,
544                           int rowBytes,
545                           const OfxRectI & renderWindow,
546                           void* dstPixelData,
547                           const OfxRectI & dstBounds,
548                           OFX::PixelComponentEnum dstPixelComponents,
549                           int dstPixelComponentCount,
550                           OFX::BitDepthEnum dstBitDepth,
551                           int dstRowBytes) const
552     {
553         assert(bitDepth == eBitDepthUByte && dstBitDepth == eBitDepthFloat && pixelComponents == dstPixelComponents && pixelComponentCount == dstPixelComponentCount);
554         assert(bounds.x1 <= renderWindow.x1 && renderWindow.x2 <= bounds.x2 &&
555                bounds.y1 <= renderWindow.y1 && renderWindow.y2 <= bounds.y2 &&
556                dstBounds.x1 <= renderWindow.x1 && renderWindow.x2 <= dstBounds.x2 &&
557                dstBounds.y1 <= renderWindow.y1 && renderWindow.y2 <= dstBounds.y2);
558         //validate();
559 
560         const int nComponents = pixelComponentCount;
561 
562         for (int y = renderWindow.y1; y < renderWindow.y2; ++y) {
563             const unsigned char *src_pixels = (const unsigned char*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, 0, y);
564             float *dst_pixels = (float*)OFX::getPixelAddress(dstPixelData, dstBounds, dstPixelComponents, dstBitDepth, dstRowBytes, 0, y);
565             const unsigned char *src_end = (const unsigned char*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, renderWindow.x2, y, false);
566 
567 
568             while (src_pixels != src_end) {
569                 if (nComponents == 1) {
570                     *dst_pixels++ = intToFloat<256>(*src_pixels++);
571                 } else {
572                     for (int k = 0; k < 3; ++k) {
573                         dst_pixels[k] = fromColorSpaceUint8ToLinearFloatFast(src_pixels[k]);
574                     }
575                     if (nComponents == 4) {
576                         // alpha channel: no colorspace conversion
577                         dst_pixels[3] = intToFloat<256>(src_pixels[3]);
578                     }
579                 }
580                 dst_pixels += nComponents;
581                 src_pixels += nComponents;
582             }
583         }
584     }
585 
from_short_packed(const void * pixelData,const OfxRectI & bounds,OFX::PixelComponentEnum pixelComponents,int pixelComponentCount,OFX::BitDepthEnum bitDepth,int rowBytes,const OfxRectI & renderWindow,void * dstPixelData,const OfxRectI & dstBounds,OFX::PixelComponentEnum dstPixelComponents,int dstPixelComponentCount,OFX::BitDepthEnum dstBitDepth,int dstRowBytes)586     void from_short_packed(const void* pixelData,
587                            const OfxRectI & bounds,
588                            OFX::PixelComponentEnum pixelComponents,
589                            int pixelComponentCount,
590                            OFX::BitDepthEnum bitDepth,
591                            int rowBytes,
592                            const OfxRectI & renderWindow,
593                            void* dstPixelData,
594                            const OfxRectI & dstBounds,
595                            OFX::PixelComponentEnum dstPixelComponents,
596                            int dstPixelComponentCount,
597                            OFX::BitDepthEnum dstBitDepth,
598                            int dstRowBytes) const
599     {
600         assert(bitDepth == eBitDepthUShort && dstBitDepth == eBitDepthFloat && pixelComponents == dstPixelComponents && pixelComponentCount == dstPixelComponentCount);
601         assert(bounds.x1 <= renderWindow.x1 && renderWindow.x2 <= bounds.x2 &&
602                bounds.y1 <= renderWindow.y1 && renderWindow.y2 <= bounds.y2 &&
603                dstBounds.x1 <= renderWindow.x1 && renderWindow.x2 <= dstBounds.x2 &&
604                dstBounds.y1 <= renderWindow.y1 && renderWindow.y2 <= dstBounds.y2);
605         //validate();
606 
607         const int nComponents = pixelComponentCount;
608 
609         for (int y = renderWindow.y1; y < renderWindow.y2; ++y) {
610             const unsigned short *src_pixels = (const unsigned short*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, 0, y);
611             float *dst_pixels = (float*)OFX::getPixelAddress(dstPixelData, dstBounds, dstPixelComponents, dstBitDepth, dstRowBytes, 0, y);
612             const unsigned short *src_end = (const unsigned short*)OFX::getPixelAddress(pixelData, bounds, pixelComponents, bitDepth, rowBytes, renderWindow.x2, y, false);
613 
614 
615             while (src_pixels != src_end) {
616                 if (nComponents == 1) {
617                     *dst_pixels++ = intToFloat<65536>(*src_pixels++);
618                 } else {
619                     for (int k = 0; k < 3; ++k) {
620                         dst_pixels[k] = fromColorSpaceUint16ToLinearFloatFast(src_pixels[k]);
621                     }
622                     if (nComponents == 4) {
623                         // alpha channel: no colorspace conversion
624                         dst_pixels[3] = intToFloat<65536>(src_pixels[3]);
625                     }
626                 }
627                 dst_pixels += nComponents;
628                 src_pixels += nComponents;
629             }
630         }
631     }
632 
633 private:
634     static float index_to_float(const unsigned short i);
635     static unsigned short hipart(const float f);
636 };
637 
638 
639 ////////////////////////////////////////////////////////////////
640 // Transfer functions
641 //
642 // from_func_*: EOTF (Electro-Optical Transfer Function)
643 // to_func_*: OETF (Opto-Electronic Transfer Function)
644 //
645 // more can be found at:
646 // https://github.com/colour-science/colour/tree/develop/colour/models/rgb/transfer_functions
647 //
648 ////////////////////////////////////////////////////////////////
649 
650 inline float
from_func_linear(float v)651 from_func_linear(float v)
652 {
653     return v;
654 }
655 
656 inline float
to_func_linear(float v)657 to_func_linear(float v)
658 {
659     return v;
660 }
661 
662 /// from sRGB to Linear Electro-Optical Transfer Function (EOTF)
663 inline float
from_func_srgb(float v)664 from_func_srgb(float v)
665 {
666     if (v < 0.04045f) {
667         return (v < 0.0f) ? 0.0f : v * (1.0f / 12.92f);
668     } else {
669         return std::pow( (v + 0.055f) * (1.0f / 1.055f), 2.4f );
670     }
671 }
672 
673 /// to sRGB from Linear Opto-Electronic Transfer Function (OETF)
674 inline float
to_func_srgb(float v)675 to_func_srgb(float v)
676 {
677     if (v < 0.0031308f) {
678         return (v < 0.0f) ? 0.0f : v * 12.92f;
679     } else {
680         return 1.055f * std::pow(v, 1.0f / 2.4f) - 0.055f;
681     }
682 }
683 
684 // Rec.709 and Rec.2020 share the same transfer function (and illuminant), except that
685 // Rec.2020 is more precise.
686 // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-0-201208-S!!PDF-E.pdf
687 // Since this is float, we use the coefficients from Rec.2020
688 
689 /// From Rec.709 to Linear Electro-Optical Transfer Function (EOTF)
690 inline float
from_func_Rec709(float v)691 from_func_Rec709(float v)
692 {
693     //if (v < 0.081f) {
694     if (v < 0.08145f) {
695         return (v < 0.0f) ? 0.0f : v * (1.0f / 4.5f);
696     } else {
697         //return std::pow( (v + 0.099f) * (1.0f / 1.099f), (1.0f / 0.45f) );
698         return std::pow( (v + 0.0993f) * (1.0f / 1.0993f), (1.0f / 0.45f) );
699     }
700 }
701 
702 // see above comment
703 /// to Rec.709 from Linear Opto-Electronic Transfer Function (OETF)
704 inline float
to_func_Rec709(float v)705 to_func_Rec709(float v)
706 {
707     //if (v < 0.018f) {
708     if (v < 0.0181f) {
709         return (v < 0.0f) ? 0.0f : v * 4.5f;
710     } else {
711         //return 1.099f * std::pow(v, 0.45f) - 0.099f;
712         return 1.0993f * std::pow(v, 0.45f) - (1.0993f - 1.f);
713     }
714 }
715 
716 /*
717    Following the formula:
718    offset = pow(10,(blackpoint - whitepoint) * 0.002 / gamma)
719    gain = 1/(1-offset)
720    linear = gain * (pow(10,(1023*v - whitepoint)*0.002/gamma) - offset)
721    cineon = (log10((v + offset) /gain)/ (0.002 / gamma) + whitepoint)/1023
722    Here we're using: blackpoint = 95.0
723    whitepoint = 685.0
724    gammasensito = 0.6
725  */
726 /// from Cineon to Linear Electro-Optical Transfer Function (EOTF)
727 inline float
from_func_Cineon(float v)728 from_func_Cineon(float v)
729 {
730     //return ( 1.f / ( 1.f - std::pow(10.f, -1.97f) ) ) * std::pow(10.f, ( (1023.f * v) - 685.f ) * 0.002f / 0.6f);
731     //float offset = std::pow(10.f, (95.f - 685.f)*0.002f/0.6f);
732     //float offset = 0.01079775161f;
733     return ( 1.f / ( 1.f - 0.01079775161f ) ) * ( std::pow(10.f, ( (1023.f * v) - 685.f ) * 0.002f / 0.6f) - 0.01079775161f);
734 }
735 
736 /// to Cineon from Linear Opto-Electronic Transfer Function (OETF)
737 inline float
to_func_Cineon(float v)738 to_func_Cineon(float v)
739 {
740     //float offset = std::pow(10.f, -1.97f);
741     //float offset = std::pow(10.f, (95.f - 685.f)*0.002f/0.6f);
742     //float offset = 0.01079775161f;
743 
744     //return (std::log10( (v + offset) / ( 1.f / (1.f - offset) ) ) / 0.0033f + 685.0f) / 1023.f;
745     return (std::log10( (v + 0.01079775161f) / ( 1.f / (1.f - 0.01079775161f) ) ) / (0.002f / 0.6f) + 685.0f) / 1023.f;
746 }
747 
748 /// from Gamma 1.8 to Linear Electro-Optical Transfer Function (EOTF)
749 inline float
from_func_Gamma1_8(float v)750 from_func_Gamma1_8(float v)
751 {
752     return (v < 0.0f) ? 0.0f : std::pow(v, 1.8f);
753 }
754 
755 /// to Gamma 1.8 from Linear Opto-Electronic Transfer Function (OETF)
756 inline float
to_func_Gamma1_8(float v)757 to_func_Gamma1_8(float v)
758 {
759     return (v < 0.0f) ? 0.0f : std::pow(v, 0.55f);
760 }
761 
762 /// from Gamma 2.2 to Linear Electro-Optical Transfer Function (EOTF)
763 inline float
from_func_Gamma2_2(float v)764 from_func_Gamma2_2(float v)
765 {
766     return (v < 0.0f) ? 0.0f : std::pow(v, 2.2f);
767 }
768 
769 /// to Gamma 2.2 from Linear Opto-Electronic Transfer Function (OETF)
770 inline float
to_func_Gamma2_2(float v)771 to_func_Gamma2_2(float v)
772 {
773     return (v < 0.0f) ? 0.0f : std::pow(v, 0.45f);
774 }
775 
776 /// from Panalog to Linear Electro-Optical Transfer Function (EOTF)
777 inline float
from_func_Panalog(float v)778 from_func_Panalog(float v)
779 {
780     return (std::pow(10.f, (1023.f * v - 681.f) / 444.f) - 0.0408f) / (1.0f - 0.0408f);
781 }
782 
783 /// to Panalog from Linear Opto-Electronic Transfer Function (OETF)
784 inline float
to_func_Panalog(float v)785 to_func_Panalog(float v)
786 {
787     return (444.f * std::log10(0.0408f + (1.0f - 0.0408f) * v) + 681.f) / 1023.f;
788 }
789 
790 /// from REDLog to Linear Electro-Optical Transfer Function (EOTF)
791 inline float
from_func_REDLog(float v)792 from_func_REDLog(float v)
793 {
794     return (std::pow(10.f, (1023.f * v - 1023.f) / 511.f) - 0.01f) / (1.0f - 0.01f);
795 }
796 
797 /// to REDLog from Linear Opto-Electronic Transfer Function (OETF)
798     inline float
to_func_REDLog(float v)799 to_func_REDLog(float v)
800 {
801     return (511.f * std::log10(0.01f + (1.0f - 0.01f) * v) + 1023.f) / 1023.f;
802 }
803 
804 /// from ViperLog to Linear Electro-Optical Transfer Function (EOTF)
805 inline float
from_func_ViperLog(float v)806 from_func_ViperLog(float v)
807 {
808     return std::pow(10.f, (1023.f * v - 1023.f) / 500.f);
809 }
810 
811 /// to ViperLog from Linear Opto-Electronic Transfer Function (OETF)
812 inline float
to_func_ViperLog(float v)813 to_func_ViperLog(float v)
814 {
815     return (500.f * std::log10(v) + 1023.f) / 1023.f;
816 }
817 
818 /// from AlexaV3LogC to Linear Electro-Optical Transfer Function (EOTF)
819 inline float
from_func_AlexaV3LogC(float v)820 from_func_AlexaV3LogC(float v)
821 {
822     // ref: "ALEXA LOG C Curve-Usage in VFX" PDF, p9
823     return v > 0.1496582f ? std::pow(10.f, (v - 0.385537f) / 0.2471896f) * 0.18f - 0.00937677f
824            : ( v / 0.9661776f - 0.04378604f) * 0.18f - 0.00937677f;
825 }
826 
827 /// from Linear to AlexaV3LogC (EI=800) Opto-Electronic Transfer Function (OETF)
828 inline float
to_func_AlexaV3LogC(float v)829 to_func_AlexaV3LogC(float v)
830 {
831     // ref: "ALEXA LOG C Curve-Usage in VFX" PDF, p9
832     return v > 0.010591f ?  0.247190f * std::log10(5.555556f * v + 0.052272f) + 0.385537f
833            : v * 5.367655f + 0.092809f;
834 }
835 
836 /// from SLog1 to Linear Electro-Optical Transfer Function (EOTF)
837 inline float
from_func_SLog1(float v)838 from_func_SLog1(float v)
839 {
840     // ref: https://pro.sony.com/bbsccms/assets/files/micro/dmpc/training/S-Log2_Technical_PaperV1_0.pdf
841     return (float)(v >= 90./1023. ? (std::pow( 10., (((v*1023.0-64.0)/(940.0-64.0)-0.616596-0.03)/0.432699))-0.037584)*0.9
842            : ((v*1023.0-64.0)/(940.0-64.0)-0.030001222851889303)/5.*0.9);
843 }
844 
845 /// from Linear to SLog1 Opto-Electronic Transfer Function (OETF)
846 inline float
to_func_SLog1(float v)847 to_func_SLog1(float v)
848 {
849     // ref: https://pro.sony.com/bbsccms/assets/files/micro/dmpc/training/S-Log2_Technical_PaperV1_0.pdf
850     return (float)(v >= -0.00008153227156 ? ((std::log10((v / 0.9) + 0.037584) * 0.432699 +0.616596+0.03)*(940.0-64.0) + 64.)/1023.
851            : (((v / 0.9) * 5. + 0.030001222851889303)*(940.0-64.0) + 64.)/1023);
852 }
853 
854 /// from SLog2 to Linear Electro-Optical Transfer Function (EOTF)
855 inline float
from_func_SLog2(float v)856 from_func_SLog2(float v)
857 {
858     // http://community.thefoundry.co.uk/discussion/topic.aspx?f=189&t=100372
859     // nuke.root().knob('luts').addCurve("SLog2-Ref", "{ (t>=90.0/1023.0)? 219.0*(pow(10.0, (((t*1023.0-64.0)/(940.0-64.0)-0.616596-0.03)/0.432699))-0.037584)/155.0*0.9 : ((t*1023.0-64.0)/(940.0-64.0)-0.030001222851889303)/3.53881278538813*0.9 }")
860     // ref: https://pro.sony.com/bbsccms/assets/files/micro/dmpc/training/S-Log2_Technical_PaperV1_0.pdf
861     return (float)(v >= 90./1023. ? 219.0 * (std::pow( 10., (((v*1023.0-64.0)/(940.0-64.0)-0.616596-0.03)/0.432699))-0.037584)/155.0*0.9
862           : ((v*1023.0-64.0)/(940.0-64.0)-0.030001222851889303)/3.53881278538813*0.9);
863 }
864 
865 /// from Linear to SLog2 Opto-Electronic Transfer Function (OETF)
866 inline float
to_func_SLog2(float v)867 to_func_SLog2(float v)
868 {
869     // ref: https://pro.sony.com/bbsccms/assets/files/micro/dmpc/training/S-Log2_Technical_PaperV1_0.pdf
870     return (float)(v >= -0.00008153227156 ? ((std::log10((v / 0.9) * 155. / 219. + 0.037584) * 0.432699 +0.616596+0.03)*(940.0-64.0) + 64.)/1023.
871             : (((v / 0.9) * 3.53881278538813 + 0.030001222851889303)*(940.0-64.0) + 64.)/1023);
872 }
873 
874 /// from SLog3 to Linear Electro-Optical Transfer Function (EOTF)
875 inline float
from_func_SLog3(float v)876 from_func_SLog3(float v)
877 {
878     // http://www.sony.co.uk/pro/support/attachment/1237494271390/1237494271406/technical-summary-for-s-gamut3-cine-s-log3-and-s-gamut3-s-log3.pdf
879     return (float)(v >= 171.2102946929 / 1023.0 ? std::pow(10.0, ((v * 1023.0 - 420.0) / 261.5)) * (0.18 + 0.01) - 0.01
880           : (v * 1023.0 - 95.0) * 0.01125000 / (171.2102946929 - 95.0));
881 }
882 
883 /// from Linear to SLog3 Opto-Electronic Transfer Function (OETF)
884 inline float
to_func_SLog3(float v)885 to_func_SLog3(float v)
886 {
887     // http://www.sony.co.uk/pro/support/attachment/1237494271390/1237494271406/technical-summary-for-s-gamut3-cine-s-log3-and-s-gamut3-s-log3.pdf
888     return (float)(v >= 0.01125000 ? (420.0 + std::log10((v + 0.01) / (0.18 + 0.01)) * 261.5) / 1023.0
889          : (v * (171.2102946929 - 95.0)/0.01125000 + 95.0) / 1023.0);
890 }
891 
892 // from V-Log to Linear Electro-Optical Transfer Function (EOTF)
893 inline float
from_func_VLog(float v)894 from_func_VLog(float v)
895 {
896     // http://pro-av.panasonic.net/en/varicam/common/pdf/VARICAM_V-Log_V-Gamut.pdf
897     const float cut2 = 0.181f, b = 0.00873f, c = 0.241514f, d = 0.598206f;
898     return v < cut2 ? ( (v - 0.125f) / 5.6f ) : (std::pow(10.f, ( (v - d) / c) ) - b);
899 }
900 
901 /// from Linear to VLog Opto-Electronic Transfer Function (OETF)
902 inline float
to_func_VLog(float v)903 to_func_VLog(float v)
904 {
905     // http://pro-av.panasonic.net/en/varicam/common/pdf/VARICAM_V-Log_V-Gamut.pdf
906     const float cut1 = 0.01f, b = 0.00873f, c = 0.241514f, d = 0.598206f;
907     return v < cut1 ? (5.6f * v + 0.125f) : (c * std::log10(v + b) + d);
908 }
909 
910 /// convert RGB to HSV
911 /// In Nuke's viewer, sRGB values are used (apply to_func_srgb to linear
912 /// RGB values before calling this fuunction)
913 // r,g,b values are from 0 to 1
914 /// h = [0,360], s = [0,1], v = [0,1]
915 ///		if s == 0, then h = -1 (undefined)
916 void rgb_to_hsv( float r, float g, float b, float *h, float *s, float *v );
917 void hsv_to_rgb( float h, float s, float v, float *r, float *g, float *b );
918 
919 void rgb_to_hsl( float r, float g, float b, float *h, float *s, float *l );
920 void hsl_to_rgb( float h, float s, float l, float *r, float *g, float *b );
921 
922 void rgb_to_hsi( float r, float g, float b, float *h, float *s, float *i );
923 void hsi_to_rgb( float h, float s, float i, float *r, float *g, float *b );
924 
925 void rgb_to_ycbcr601( float r, float g, float b, float *y, float *cb, float *cr );
926 void ycbcr_to_rgb601( float y, float cb, float cr, float *r, float *g, float *b );
927 
928 void rgb_to_ycbcr709( float r, float g, float b, float *y, float *cb, float *cr );
929 void ycbcr_to_rgb709( float y, float cb, float cr, float *r, float *g, float *b );
930 
931 void rgb_to_ypbpr601( float r, float g, float b, float *y, float *pb, float *pr );
932 void ypbpr_to_rgb601( float y, float pb, float pr, float *r, float *g, float *b );
933 
934 void rgb_to_ypbpr709( float r, float g, float b, float *y, float *pb, float *pr );
935 void ypbpr_to_rgb709( float y, float pb, float pr, float *r, float *g, float *b );
936 
937 void rgb_to_ypbpr2020( float r, float g, float b, float *y, float *pb, float *pr );
938 void ypbpr_to_rgb2020( float y, float pb, float pr, float *r, float *g, float *b );
939 
940 void rgb_to_yuv601( float r, float g, float b, float *y, float *u, float *v );
941 void yuv_to_rgb601( float y, float u, float v, float *r, float *g, float *b );
942 
943 void rgb_to_yuv709( float r, float g, float b, float *y, float *u, float *v );
944 void yuv_to_rgb709( float y, float u, float v, float *r, float *g, float *b );
945 
946 // r,g,b values are from 0 to 1
947 // Convert pixel values from linear RGB_709 or sRGB to XYZ color spaces.
948 // Uses the standard D65 white point.
949 template<typename T>
rgb709_to_y(T r,T g,T b)950 T rgb709_to_y( T r, T g, T b )
951 {
952     // coefficients are those of http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
953     // from 07 Apr 2017
954     return (T)(0.2126729f * r + 0.7151522f * g + 0.0721750f * b);
955 }
956 
957 template<typename T>
958 void
rgb709_to_xyz(T r,T g,T b,T * x,T * y,T * z)959 rgb709_to_xyz(T r,
960               T g,
961               T b,
962               T *x,
963               T *y,
964               T *z)
965 {
966     //*x = 0.412453f * r + 0.357580f * g + 0.180423f * b;
967     //*y = 0.212671f * r + 0.715160f * g + 0.072169f * b;
968     //*z = 0.019334f * r + 0.119193f * g + 0.950227f * b;
969     // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
970     //> with(linalg):
971     //> M:=matrix([[3.2409699419, -1.5373831776, -0.4986107603],[-0.9692436363, 1.8759675015, 0.0415550574],[ 0.0556300797, -0.2039769589,  1.0569715142]]);
972     //> inverse(M);
973 
974     // coefficients are those of http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
975     // from 07 Apr 2017
976     *x = (T)(0.4124564f * r + 0.3575761f * g + 0.1804375f * b);
977     *y = (T)(rgb709_to_y(r, g, b));
978     *z = (T)(0.0193339f * r + 0.1191920f * g + 0.9503041f * b);
979 }
980 
981 // Convert pixel values from XYZ to linear RGB_709 or sRGB color spaces.
982 // Uses the standard D65 white point.
983 template<typename T>
984 void
xyz_to_rgb709(T x,T y,T z,T * r,T * g,T * b)985 xyz_to_rgb709(T x,
986               T y,
987               T z,
988               T *r,
989               T *g,
990               T *b)
991 {
992     //*r =  3.240479f * x - 1.537150f * y - 0.498535f * z;
993     //*g = -0.969256f * x + 1.875992f * y + 0.041556f * z;
994     //*b =  0.055648f * x - 0.204043f * y + 1.057311f * z;
995     // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
996     //*r =  3.2409699419 * x + -1.5373831776 * y + -0.4986107603 * z;
997     //*g = -0.9692436363 * x +  1.8759675015 * y +  0.0415550574 * z;
998     //*b =  0.0556300797 * x + -0.2039769589 * y +  1.0569715142 * z;
999 
1000     // coefficients are those of http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
1001     // from 07 Apr 2017
1002     *r = (T)( 3.2404542f * x + -1.5371385f * y + -0.4985314f * z);
1003     *g = (T)(-0.9692660f * x +  1.8760108f * y +  0.0415560f * z);
1004     *b = (T)( 0.0556434f * x + -0.2040259f * y +  1.0572252f * z);
1005 }
1006 
1007 // r,g,b values are from 0 to 1
1008 // Convert pixel values from RGB_2020 to XYZ color spaces.
1009 // Uses the standard D65 white point.
1010 template<typename T>
rgb2020_to_y(T r,T g,T b)1011 T rgb2020_to_y( T r, T g, T b )
1012 {
1013     return (T)(0.2627002119 * r + 0.6779980711 * g + 0.0593017165 * b);
1014 }
1015 
1016 template<typename T>
1017 void
rgb2020_to_xyz(T r,T g,T b,T * x,T * y,T * z)1018 rgb2020_to_xyz(T r,
1019                T g,
1020                T b,
1021                T *x,
1022                T *y,
1023                T *z)
1024 {
1025     //> with(linalg):
1026     //> P:=matrix([[1.7166511880,-0.3556707838,-0.2533662814],[-0.6666843518,1.6164812366,0.0157685458],[0.0176398574,-0.0427706133,0.9421031212]]);
1027     //> inverse(P);
1028 
1029     *x = (T)(0.6369580481 * r + 0.1446169036 * g + 0.1688809752 * b);
1030     *y = (T)(rgb2020_to_y(r, g, b));
1031     *z = (T)(0.0000000000 * r + 0.0280726931 * g + 1.060985058  * b);
1032 }
1033 
1034 // Convert pixel values from XYZ to RGB_2020 color spaces.
1035 // Uses the standard D65 white point.
1036 template<typename T>
1037 void
xyz_to_rgb2020(T x,T y,T z,T * r,T * g,T * b)1038 xyz_to_rgb2020(T x,
1039                T y,
1040                T z,
1041                T *r,
1042                T *g,
1043                T *b)
1044 {
1045     // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
1046     *r = (T)( 1.7166511880 * x + -0.3556707838 * y + -0.2533662814 * z);
1047     *g = (T)(-0.6666843518 * x +  1.6164812366 * y +  0.0157685458 * z);
1048     *b = (T)( 0.0176398574 * x + -0.0427706133 * y +  0.9421031212 * z);
1049 }
1050 
1051 // r,g,b values are from 0 to 1
1052 // Convert pixel values from RGB_ACES_AP0 to XYZ color spaces.
1053 // Uses the ACES white point (approx. D60).
1054 template<typename T>
rgbACESAP0_to_y(T r,T g,T b)1055 T rgbACESAP0_to_y( T r, T g, T b )
1056 {
1057     return (T)(0.3439664498 * r + 0.7281660966 * g + -0.0721325464 * b);
1058 }
1059 
1060 template<typename T>
1061 void
rgbACESAP0_to_xyz(T r,T g,T b,T * x,T * y,T * z)1062 rgbACESAP0_to_xyz(T r,
1063                   T g,
1064                   T b,
1065                   T *x,
1066                   T *y,
1067                   T *z)
1068 {
1069     // https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#Converting_ACES_RGB_values_to_CIE_XYZ_values
1070     // and
1071     // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
1072     *x = (T)(0.9525523959 * r + 0.0000000000 * g +  0.0000936786 * b);
1073     *y = (T)(rgbACESAP0_to_y(r, g, b));
1074     *z = (T)(0.0000000000 * r + 0.0000000000 * g +  1.0088251844 * b);
1075 }
1076 
1077 // Convert pixel values from XYZ to RGB_ACES_AP0 (with the ACES illuminant) color spaces.
1078 // Uses the ACES white point (approx. D60).
1079 template<typename T>
1080 void
xyz_to_rgbACESAP0(T x,T y,T z,T * r,T * g,T * b)1081 xyz_to_rgbACESAP0(T x,
1082                   T y,
1083                   T z,
1084                   T *r,
1085                   T *g,
1086                   T *b)
1087 {
1088     // https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#Converting_ACES_RGB_values_to_CIE_XYZ_values
1089     // and
1090     // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
1091     *r = (T)( 1.0498110175 * x +  0.0000000000 * y + -0.0000974845 * z);
1092     *g = (T)(-0.4959030231 * x +  1.3733130458 * y +  0.0982400361 * z);
1093     *b = (T)( 0.0000000000 * x +  0.0000000000 * y +  0.9912520182 * z);
1094 }
1095 
1096 // r,g,b values are from 0 to 1
1097 // Convert pixel values from RGB_ACES_AP1 to XYZ color spaces.
1098 // Uses the ACES white point (approx. D60).
1099 template<typename T>
rgbACESAP1_to_y(T r,T g,T b)1100 T rgbACESAP1_to_y( T r, T g, T b )
1101 {
1102     return (T)(0.2722287168 * r +  0.6740817658 * g +  0.0536895174 * b);
1103 }
1104 
1105 template<typename T>
1106 void
rgbACESAP1_to_xyz(T r,T g,T b,T * x,T * y,T * z)1107 rgbACESAP1_to_xyz(T r,
1108                   T g,
1109                   T b,
1110                   T *x,
1111                   T *y,
1112                   T *z)
1113 {
1114     // https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#Converting_ACES_RGB_values_to_CIE_XYZ_values
1115     // and
1116     // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
1117     *x = (T)( 0.6624541811 * r +  0.1340042065 * g +  0.1561876870 * b);
1118     *y = (T)( rgbACESAP1_to_y(r, g, b));
1119     *z = (T)(-0.0055746495 * r +  0.0040607335 * g +  1.0103391003 * b);
1120 }
1121 
1122 // Convert pixel values from XYZ to RGB_ACES_AP1 color spaces.
1123 // Uses the ACES white point (approx. D60).
1124 template<typename T>
1125 void
xyz_to_rgbACESAP1(T x,T y,T z,T * r,T * g,T * b)1126 xyz_to_rgbACESAP1(T x,
1127                   T y,
1128                   T z,
1129                   T *r,
1130                   T *g,
1131                   T *b)
1132 {
1133     // https://en.wikipedia.org/wiki/Academy_Color_Encoding_System#Converting_ACES_RGB_values_to_CIE_XYZ_values
1134     // and
1135     // https://github.com/ampas/aces-dev/blob/master/transforms/ctl/README-MATRIX.md
1136     *r = (T)( 1.6410233797 * x + -0.3248032942 * y + -0.2364246952 * z);
1137     *g = (T)(-0.6636628587 * x +  1.6153315917 * y +  0.0167563477 * z);
1138     *b = (T)( 0.0117218943 * x + -0.0082844420 * y +  0.9883948585 * z);
1139 }
1140 
1141 void xyz_to_lab( float x, float y, float z, float *l, float *a, float *b );
1142 void lab_to_xyz( float l, float a, float b, float *x, float *y, float *z );
1143 
1144 void rgb709_to_lab( float r, float g, float b, float *l, float *a, float *b_ );
1145 void lab_to_rgb709( float l, float a, float b, float *r, float *g, float *b_ );
1146 
1147 
1148 // an object that holds precomputed LUTs for the whole application.
1149 // The LutManager object should be constructed in the plugin factory's load() function, and destructed in the unload() function
1150 // Luts are allocated on request, and destructed either on request, or when the LutManager is destroyed
1151 template <class MUTEX>
1152 class LutManager
1153 {
1154     typedef OFX::MultiThread::AutoMutexT<MUTEX> AutoMutex;
1155 
1156     typedef std::map<std::string, const Lut* > LutsMap;
1157 
1158 public:
LutManager()1159     LutManager()
1160     : _lock()
1161     , _luts()
1162     {
1163     }
1164 
~LutManager()1165     ~LutManager()
1166     {
1167         for (typename LutsMap::iterator it = _luts.begin(); it != _luts.end(); ++it) {
1168             delete it->second;
1169         }
1170     }
1171 
1172     /**
1173      * @brief Returns a pointer to a lut with the given name and the given from and to functions.
1174      * If a lut with the same name didn't already exist, then it will create one.
1175      * Ownership of the returned pointer remains to the LutManager.
1176      * You must release the lut when you are done using it.
1177      * @WARNING: Not thread-safe. You should call it in the load() action of your plug-in
1178      **/
getLut(const std::string & name,fromColorSpaceFunctionV1 fromFunc,toColorSpaceFunctionV1 toFunc)1179     const Lut* getLut(const std::string & name,
1180                                  fromColorSpaceFunctionV1 fromFunc,
1181                                  toColorSpaceFunctionV1 toFunc)
1182     {
1183         AutoMutex l(_lock);
1184         typename LutsMap::iterator found = _luts.find(name);
1185 
1186         if ( found != _luts.end() ) {
1187 
1188             return found->second;
1189         } else {
1190             Lut* lut = new Lut(name, fromFunc, toFunc);;
1191             //lut->validate();
1192             _luts[name] = lut;
1193 
1194             return lut;
1195         }
1196 
1197         return NULL;
1198     }
1199 
1200     /**
1201      * @brief Release a lut previously retrieved with getLut()
1202      **/
releaseLut(const std::string & name)1203     void releaseLut(const std::string& name)
1204     {
1205         AutoMutex l(_lock);
1206         typename LutsMap::iterator found = _luts.find(name);
1207         if ( found != _luts.end() ) {
1208             delete found->second;
1209             _luts.erase(found);
1210         }
1211     }
1212 
1213     ///buit-ins color-spaces
linearLut()1214     const Lut* linearLut()
1215     {
1216         return getLut("Linear", from_func_linear, to_func_linear);
1217     }
1218 
sRGBLut()1219     const Lut* sRGBLut()
1220     {
1221         return getLut("sRGB", from_func_srgb, to_func_srgb);
1222     }
1223 
Rec709Lut()1224     const Lut* Rec709Lut()
1225     {
1226         return getLut("Rec709", from_func_Rec709, to_func_Rec709);
1227     }
1228 
CineonLut()1229     const Lut* CineonLut()
1230     {
1231         return getLut("Cineon", from_func_Cineon, to_func_Cineon);
1232     }
1233 
Gamma1_8Lut()1234     const Lut* Gamma1_8Lut()
1235     {
1236         return getLut("Gamma1_8", from_func_Gamma1_8, to_func_Gamma1_8);
1237     }
1238 
Gamma2_2Lut()1239     const Lut* Gamma2_2Lut()
1240     {
1241         return getLut("Gamma2_2", from_func_Gamma2_2, to_func_Gamma2_2);
1242     }
1243 
PanalogLut()1244     const Lut* PanalogLut()
1245     {
1246         return getLut("Panalog", from_func_Panalog, to_func_Panalog);
1247     }
1248 
ViperLogLut()1249     const Lut* ViperLogLut()
1250     {
1251         return getLut("ViperLog", from_func_ViperLog, to_func_ViperLog);
1252     }
1253 
REDLogLut()1254     const Lut* REDLogLut()
1255     {
1256         return getLut("REDLog", from_func_REDLog, to_func_REDLog);
1257     }
1258 
AlexaV3LogCLut()1259     const Lut* AlexaV3LogCLut()
1260     {
1261         return getLut("AlexaV3LogC", from_func_AlexaV3LogC, to_func_AlexaV3LogC);
1262     }
1263 
SLog1Lut()1264     const Lut* SLog1Lut()
1265     {
1266         return getLut("SLog1", from_func_SLog1, to_func_SLog1);
1267     }
1268 
SLog2Lut()1269     const Lut* SLog2Lut()
1270     {
1271         return getLut("SLog2", from_func_SLog2, to_func_SLog2);
1272     }
1273 
SLog3Lut()1274     const Lut* SLog3Lut()
1275     {
1276         return getLut("SLog3", from_func_SLog3, to_func_SLog3);
1277     }
1278 
VLogLut()1279     const Lut* VLogLut()
1280     {
1281         return getLut("V-Log", from_func_VLog, to_func_VLog);
1282     }
1283 
1284 private:
1285     LutManager &operator= (const LutManager &);
1286     LutManager(const LutManager &);
1287 
1288 
1289     mutable MUTEX _lock;                 ///< protects _luts
1290     LutsMap _luts;
1291 };
1292 
1293 }         //namespace Color
1294 }     //namespace OFX
1295 
1296 /*
1297  RGB reference primaries.
1298 
1299  more can be found at https://github.com/colour-science/colour/tree/develop/colour/models/rgb/dataset
1300 
1301  ACES-Gamut
1302  http://www.digitalpreservation.gov/partners/documents/IIF_Overview_August_2010.pdf
1303  -      |  CIE x  |  CIE y
1304  Red    | 0.73470 | 0.26530
1305  Green  | 0.00000 | 1.00000
1306  Blue   | 0.00010 | -0.07700
1307  White  | 0.32168 | 0.33767 (approx. D60)
1308 
1309  Sony S-Gamut3.Cine
1310  http://www.sony.co.uk/pro/support/attachment/1237494271390/1237494271406/technical-summary-for-s-gamut3-cine-s-log3-and-s-gamut3-s-log3.pdf
1311  -      |  CIE x  |  CIE y
1312  Red    | 0.76600 | 0.27500
1313  Green  | 0.22500 | 0.80000
1314  Blue   | 0.08900 | -0.08700
1315  White  | 0.31270 | 0.32900 (D65)
1316 
1317  Sony S-Gamut/S-Gamut3
1318  http://www.sony.co.uk/pro/support/attachment/1237494271390/1237494271406/technical-summary-for-s-gamut3-cine-s-log3-and-s-gamut3-s-log3.pdf
1319  -      |  CIE x  |  CIE y
1320  Red    | 0.73000 | 0.28000
1321  Green  | 0.14000 | 0.85500
1322  Blue   | 0.10000 | -0.05000
1323  White  | 0.31270 | 0.32900 (D65)
1324 
1325  DCI-P3
1326  http://www.sony.co.uk/pro/support/attachment/1237494271390/1237494271406/technical-summary-for-s-gamut3-cine-s-log3-and-s-gamut3-s-log3.pdf
1327  -      |  CIE x  |  CIE y
1328  Red    | 0.68000 | 0.32000
1329  Green  | 0.26500 | 0.69000
1330  Blue   | 0.15000 | 0.06000
1331  White  | 0.31400 | 0.35100 (DCI)
1332 
1333  sRGB/Rec709
1334  http://en.wikipedia.org/wiki/SRGB
1335  -      |  CIE x  |  CIE y
1336  Red    | 0.64000 | 0.33000
1337  Green  | 0.30000 | 0.60000
1338  Blue   | 0.15000 | 0.06000
1339  White  | 0.31270 | 0.32900 (D65)
1340 
1341  */
1342 
1343 #endif // ifndef openfx_supportext_ofxsLut_h
1344