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