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 #include "ofxsLut.h"
25
26 #include <algorithm>
27 #ifdef _WIN32
28 typedef unsigned __int32 uint32_t;
29 typedef unsigned char uint8_t;
30 #else
31 #include <stdint.h>
32 #endif
33 #include <limits>
34 #include <cmath>
35
36 #ifndef M_PI
37 #define M_PI 3.14159265358979323846264338327950288 /* pi */
38 #endif
39
40 namespace OFX {
41 namespace Color {
42 // compile-time endianness checking found on:
43 // http://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine
44 // if(O32_HOST_ORDER == O32_BIG_ENDIAN) will always be optimized by gcc -O2
45 enum
46 {
47 O32_LITTLE_ENDIAN = 0x03020100ul,
48 O32_BIG_ENDIAN = 0x00010203ul,
49 O32_PDP_ENDIAN = 0x01000302ul
50 };
51
52 union o32_host_order_t
53 {
54 uint8_t bytes[4];
55 uint32_t value;
56 };
57
58 static const o32_host_order_t o32_host_order = {
59 { 0, 1, 2, 3 }
60 };
61 #define O32_HOST_ORDER (o32_host_order.value)
62 unsigned short
hipart(const float f)63 Lut::hipart(const float f)
64 {
65 union
66 {
67 float f;
68 unsigned short us[2];
69 }
70
71 tmp;
72
73 tmp.us[0] = tmp.us[1] = 0;
74 tmp.f = f;
75
76 if (O32_HOST_ORDER == O32_BIG_ENDIAN) {
77 return tmp.us[0];
78 } else if (O32_HOST_ORDER == O32_LITTLE_ENDIAN) {
79 return tmp.us[1];
80 } else {
81 assert( (O32_HOST_ORDER == O32_LITTLE_ENDIAN) || (O32_HOST_ORDER == O32_BIG_ENDIAN) );
82
83 return 0;
84 }
85 }
86
87 float
index_to_float(const unsigned short i)88 Lut::index_to_float(const unsigned short i)
89 {
90 union
91 {
92 float f;
93 unsigned short us[2];
94 }
95
96 tmp;
97
98 /* positive and negative zeros, and all gradual underflow, turn into zero: */
99 if ( ( i < 0x80) || ( ( i >= 0x8000) && ( i < 0x8080) ) ) {
100 return 0;
101 }
102 /* All NaN's and infinity turn into the largest possible legal float: */
103 if ( ( i >= 0x7f80) && ( i < 0x8000) ) {
104 return std::numeric_limits<float>::max();
105 }
106 if (i >= 0xff80) {
107 return -std::numeric_limits<float>::max();
108 }
109 if (O32_HOST_ORDER == O32_BIG_ENDIAN) {
110 tmp.us[0] = i;
111 tmp.us[1] = 0x8000;
112 } else if (O32_HOST_ORDER == O32_LITTLE_ENDIAN) {
113 tmp.us[0] = 0x8000;
114 tmp.us[1] = i;
115 } else {
116 assert( (O32_HOST_ORDER == O32_LITTLE_ENDIAN) || (O32_HOST_ORDER == O32_BIG_ENDIAN) );
117 }
118
119 return tmp.f;
120 }
121
122 // r,g,b values are linear values from 0 to 1
123 // h = [0,OFXS_HUE_CIRCLE], s = [0,1], v = [0,1]
124 // if s == 0, then h = 0 (undefined)
125 // Reference:
126 // "Color gamut transform pairs", Alvy Ray Smith, Proceeding SIGGRAPH '78
127 // https://doi.org/10.1145/800248.807361
128 // http://www.icst.pku.edu.cn/F/course/ImageProcessing/2018/resource/Color78.pdf
129 void
rgb_to_hsv(float r,float g,float b,float * h,float * s,float * v)130 rgb_to_hsv( float r,
131 float g,
132 float b,
133 float *h,
134 float *s,
135 float *v )
136 {
137 float min = (std::min)((std::min)(r, g), b);
138 float max = (std::max)((std::max)(r, g), b);
139
140 *v = max; // v
141
142 float delta = max - min;
143
144 if (max != 0.) {
145 *s = delta / max; // s
146 } else {
147 // r = g = b = 0 // s = 0, v is undefined
148 *s = 0.f;
149 *h = 0.f;
150
151 return;
152 }
153
154 if (delta == 0.) {
155 *h = 0.f; // gray
156 } else if (r == max) {
157 *h = (g - b) / delta; // between yellow & magenta
158 } else if (g == max) {
159 *h = 2 + (b - r) / delta; // between cyan & yellow
160 } else {
161 *h = 4 + (r - g) / delta; // between magenta & cyan
162 }
163 *h *= OFXS_HUE_CIRCLE / 6;
164 if (*h < 0) {
165 *h += OFXS_HUE_CIRCLE;
166 }
167 }
168
169 // r,g,b values are linear values from 0 to 1
170 // h = [0,OFXS_HUE_CIRCLE], s = [0,1], v = [0,1]
171 // if s == 0, then h = 0 (undefined)
172 // Reference:
173 // "Color gamut transform pairs", Alvy Ray Smith, Proceeding SIGGRAPH '78
174 // https://doi.org/10.1145/800248.807361
175 // http://www.icst.pku.edu.cn/F/course/ImageProcessing/2018/resource/Color78.pdf
176 void
hsv_to_rgb(float h,float s,float v,float * r,float * g,float * b)177 hsv_to_rgb(float h,
178 float s,
179 float v,
180 float *r,
181 float *g,
182 float *b)
183 {
184 if (s == 0) {
185 // achromatic (grey)
186 *r = *g = *b = v;
187
188 return;
189 }
190
191 h *= 6. / OFXS_HUE_CIRCLE; // sector 0 to 5
192 int i = (int)std::floor(h);
193 float f = h - i; // factorial part of h
194 i = (i >= 0) ? (i % 6) : (i % 6) + 6; // take h modulo 360
195 float p = v * ( 1 - s );
196 float q = v * ( 1 - s * f );
197 float t = v * ( 1 - s * ( 1 - f ) );
198
199 switch (i) {
200 case 0:
201 *r = v;
202 *g = t;
203 *b = p;
204 break;
205 case 1:
206 *r = q;
207 *g = v;
208 *b = p;
209 break;
210 case 2:
211 *r = p;
212 *g = v;
213 *b = t;
214 break;
215 case 3:
216 *r = p;
217 *g = q;
218 *b = v;
219 break;
220 case 4:
221 *r = t;
222 *g = p;
223 *b = v;
224 break;
225 default: // case 5:
226 *r = v;
227 *g = p;
228 *b = q;
229 break;
230 }
231 } // hsv_to_rgb
232
233 void
rgb_to_hsl(float r,float g,float b,float * h,float * s,float * l)234 rgb_to_hsl( float r,
235 float g,
236 float b,
237 float *h,
238 float *s,
239 float *l )
240 {
241 float min = (std::min)((std::min)(r, g), b);
242 float max = (std::max)((std::max)(r, g), b);
243
244 *l = (min + max) / 2;
245
246 float delta = max - min;
247
248 if (max != 0.) {
249 *s = (*l <= 0.5) ? ( delta / (max + min) ) : ( delta / (2 - max - min) ); // s = delta/(1-abs(2L-1))
250 } else {
251 // r = g = b = 0 // s = 0
252 *s = 0.f;
253 *h = 0.f;
254
255 return;
256 }
257
258 if (delta == 0.) {
259 *h = 0.f; // gray
260 } else if (r == max) {
261 *h = (g - b) / delta; // between yellow & magenta
262 } else if (g == max) {
263 *h = 2 + (b - r) / delta; // between cyan & yellow
264 } else {
265 *h = 4 + (r - g) / delta; // between magenta & cyan
266 }
267 *h *= OFXS_HUE_CIRCLE / 6;
268 if (*h < 0) {
269 *h += OFXS_HUE_CIRCLE;
270 }
271 }
272
273 void
hsl_to_rgb(float h,float s,float l,float * r,float * g,float * b)274 hsl_to_rgb(float h,
275 float s,
276 float l,
277 float *r,
278 float *g,
279 float *b)
280 {
281 if (s == 0) {
282 // achromatic (grey)
283 *r = *g = *b = l;
284
285 return;
286 }
287
288 h *= 6.f / OFXS_HUE_CIRCLE; // sector 0 to 5
289 int i = (int)std::floor(h);
290 float f = h - i; // factorial part of h
291 i = (i >= 0) ? (i % 6) : (i % 6) + 6; // take h modulo 360
292 float v = (l <= 0.5f) ? ( l * (1.0f + s) ) : (l + s - l * s);
293 float p = l + l - v;
294 float sv = (v - p ) / v;
295 float vsf = v * sv * f;
296 float t = p + vsf;
297 float q = v - vsf;
298
299 switch (i) {
300 case 0:
301 *r = v;
302 *g = t;
303 *b = p;
304 break;
305 case 1:
306 *r = q;
307 *g = v;
308 *b = p;
309 break;
310 case 2:
311 *r = p;
312 *g = v;
313 *b = t;
314 break;
315 case 3:
316 *r = p;
317 *g = q;
318 *b = v;
319 break;
320 case 4:
321 *r = t;
322 *g = p;
323 *b = v;
324 break;
325 default: // case 5:
326 *r = v;
327 *g = p;
328 *b = q;
329 break;
330 }
331 } // hsl_to_rgb
332
333 //! Convert pixel values from RGB to HSI color spaces.
334 void
rgb_to_hsi(float r,float g,float b,float * h,float * s,float * i)335 rgb_to_hsi( float r,
336 float g,
337 float b,
338 float *h,
339 float *s,
340 float *i )
341 {
342 float nR = r; //(r < 0 ? 0 : (r > 1. ? 1. : r));
343 float nG = g; //(g < 0 ? 0 : (g > 1. ? 1. : g));
344 float nB = b; //(b < 0 ? 0 : (b > 1. ? 1. : b));
345 float m = (std::min)((std::min)(nR, nG), nB);
346 float theta = (float)(std::acos( 0.5f * ( (nR - nG) + (nR - nB) ) / std::sqrt( (std::max)( 0.f, (nR - nG) * (nR - nG) + (nR - nB) * (nG - nB) ) ) ) * (OFXS_HUE_CIRCLE / 2) / M_PI);
347 float sum = nR + nG + nB;
348
349 if (theta > 0) {
350 *h = (nB <= nG) ? theta : (OFXS_HUE_CIRCLE - theta);
351 } else {
352 *h = 0.;
353 }
354 if (sum > 0) {
355 *s = 1 - 3 / sum * m;
356 } else {
357 *s = 0.;
358 }
359 *i = sum / 3;
360 }
361
362 void
hsi_to_rgb(float h,float s,float i,float * r,float * g,float * b)363 hsi_to_rgb(float h,
364 float s,
365 float i,
366 float *r,
367 float *g,
368 float *b)
369 {
370 float a = i * (1 - s);
371
372 if ( h < (OFXS_HUE_CIRCLE / 3) ) {
373 *b = a;
374 *r = (float)( i * ( 1 + s * std::cos( h * M_PI / (OFXS_HUE_CIRCLE / 2) ) / std::cos( ( (OFXS_HUE_CIRCLE / 6) - h ) * M_PI / (OFXS_HUE_CIRCLE / 2) ) ) );
375 *g = 3 * i - (*r + *b);
376 } else if ( h < (OFXS_HUE_CIRCLE * 2 / 3) ) {
377 h -= OFXS_HUE_CIRCLE / 3;
378 *r = a;
379 *g = (float)( i * ( 1 + s * std::cos( h * M_PI / (OFXS_HUE_CIRCLE / 2) ) / std::cos( ( (OFXS_HUE_CIRCLE / 6) - h ) * M_PI / (OFXS_HUE_CIRCLE / 2) ) ) );
380 *b = 3 * i - (*r + *g);
381 } else {
382 h -= OFXS_HUE_CIRCLE * 2 / 3;
383 *g = a;
384 *b = (float)( i * ( 1 + s * std::cos( h * M_PI / (OFXS_HUE_CIRCLE / 2) ) / std::cos( ( (OFXS_HUE_CIRCLE / 6) - h ) * M_PI / (OFXS_HUE_CIRCLE / 2) ) ) );
385 *r = 3 * i - (*g + *b);
386 }
387 } // hsi_to_rgb
388
389 // R'G'B' in the range 0-1 to Y'CbCr in the video range
390 // (Y' = 16/255 to 235/255, CbCr = 16/255 to 240/255)
391 void
rgb_to_ycbcr601(float r,float g,float b,float * y,float * cb,float * cr)392 rgb_to_ycbcr601(float r,
393 float g,
394 float b,
395 float *y,
396 float *cb,
397 float *cr)
398 {
399 /// ref: CImg (BT.601)
400 //*y = ((255*(66*r + 129*g + 25*b) + 128)/256 + 16)/255;
401 //*cb = ((255*(-38*r - 74*g + 112*b) + 128)/256 + 128)/255,
402 //*cr = ((255*(112*r - 94*g - 18*b) + 128)/256 + 128)/255;
403
404 /// ref: http://www.equasys.de/colorconversion.html (BT.601)
405 /// also http://www.intersil.com/data/an/AN9717.pdf
406 *y = 0.257f * r + 0.504f * g + 0.098f * b + 16 / 255.f;
407 *cb = -0.148f * r - 0.291f * g + 0.439f * b + 128 / 255.f;
408 *cr = 0.439f * r - 0.368f * g - 0.071f * b + 128 / 255.f;
409 }
410
411 // Y'CbCr in the video range (Y' = 16/255 to 235/255, CbCr = 16/255 to 240/255)
412 // to R'G'B' in the range 0-1
413 void
ycbcr_to_rgb601(float y,float cb,float cr,float * r,float * g,float * b)414 ycbcr_to_rgb601(float y,
415 float cb,
416 float cr,
417 float *r,
418 float *g,
419 float *b)
420 {
421 /// ref: CImg (BT.601)
422 //y = y * 255 - 16;
423 //cb = cb * 255 - 128;
424 //cr = cr * 255 - 128;
425 //*r = (298 * y + 409 * cr + 128)/256/255;
426 //*g = (298 * y - 100 * cb - 208 * cr + 128)/256/255;
427 //*b = (298 * y + 516 * cb + 128)/256/255;
428
429 /// ref: http://www.equasys.de/colorconversion.html (BT.601)
430 /// also http://www.intersil.com/data/an/AN9717.pdf
431 *r = 1.164f * (y - 16 / 255.f) + 1.596f * (cr - 128 / 255.f);
432 *g = 1.164f * (y - 16 / 255.f) - 0.813f * (cr - 128 / 255.f) - 0.392f * (cb - 128 / 255.f);
433 *b = 1.164f * (y - 16 / 255.f) + 2.017f * (cb - 128 / 255.f);
434 } // ycbcr_to_rgb
435
436 // R'G'B' in the range 0-1 to Y'CbCr in the video range
437 // (Y' = 16/255 to 235/255, CbCr = 16/255 to 240/255)
438 void
rgb_to_ycbcr709(float r,float g,float b,float * y,float * cb,float * cr)439 rgb_to_ycbcr709(float r,
440 float g,
441 float b,
442 float *y,
443 float *cb,
444 float *cr)
445 {
446 // ref: http://www.poynton.com/PDFs/coloureq.pdf (BT.709)
447 //*y = 0.2215 * r +0.7154 * g +0.0721 * b;
448 //*cb = -0.1145 * r -0.3855 * g +0.5000 * b + 128./255;
449 //*cr = 0.5016 * r -0.4556 * g -0.0459 * b + 128./255;
450
451 // ref: http://www.equasys.de/colorconversion.html (BT.709)
452 *y = 0.183f * r + 0.614f * g + 0.062f * b + 16 / 255.f;
453 *cb = -0.101f * r - 0.339f * g + 0.439f * b + 128 / 255.f;
454 *cr = 0.439f * r - 0.399f * g - 0.040f * b + 128 / 255.f;
455 }
456
457 // Y'CbCr in the video range (Y' = 16/255 to 235/255, CbCr = 16/255 to 240/255)
458 // to R'G'B' in the range 0-1
459 void
ycbcr_to_rgb709(float y,float cb,float cr,float * r,float * g,float * b)460 ycbcr_to_rgb709(float y,
461 float cb,
462 float cr,
463 float *r,
464 float *g,
465 float *b)
466 {
467 // ref: http://www.equasys.de/colorconversion.html (BT.709)
468 *r = 1.164f * (y - 16 / 255.f) + 1.793f * (cr - 128 / 255.f);
469 *g = 1.164f * (y - 16 / 255.f) - 0.533f * (cr - 128 / 255.f) - 0.213f * (cb - 128 / 255.f);
470 *b = 1.164f * (y - 16 / 255.f) + 2.112f * (cb - 128 / 255.f);
471 } // ycbcr_to_rgb
472
473 // R'G'B' in the range 0-1 to Y'CbCr Analog (Y' in the range 0-1, PbPr in the range -0.5 - 0.5)
474 void
rgb_to_ypbpr601(float r,float g,float b,float * y,float * pb,float * pr)475 rgb_to_ypbpr601(float r,
476 float g,
477 float b,
478 float *y,
479 float *pb,
480 float *pr)
481 {
482 // ref: https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
483 // also http://www.equasys.de/colorconversion.html (BT.601)
484 // and http://public.kitware.com/vxl/doc/release/core/vil/html/vil__colour__space_8cxx_source.html
485 //*y = 0.299f * r + 0.587f * g + 0.114f * b;
486 //*pb = -0.168736f * r - 0.331264f * g + 0.500f * b;
487 //*pr = 0.500f * r - 0.418688f * g - 0.081312f * b;
488
489 #define Kb 0.114f
490 #define Kr 0.299f
491 *y = Kr * r + (1 - Kr - Kb) * g + Kb * b;
492 *pb = (b - *y) / ( 2 * (1 - Kb) );
493 *pr = (r - *y) / ( 2 * (1 - Kr) );
494 }
495
496 // Y'CbCr Analog (Y' in the range 0-1, PbPr in the range -0.5 - 0.5) to R'G'B' in the range 0-1
497 void
ypbpr_to_rgb601(float y,float pb,float pr,float * r,float * g,float * b)498 ypbpr_to_rgb601(float y,
499 float pb,
500 float pr,
501 float *r,
502 float *g,
503 float *b)
504 {
505 // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
506 // also ref: http://www.equasys.de/colorconversion.html (BT.601)
507 // and http://public.kitware.com/vxl/doc/release/core/vil/html/vil__colour__space_8cxx_source.html
508 //*r = y + 1.402f * pr,
509 //*g = y - 0.344136 * pb - 0.714136f * pr;
510 //*b = y + 1.772f * pb;
511
512 *b = pb * ( 2 * (1 - Kb) ) + y;
513 *r = pr * ( 2 * (1 - Kr) ) + y;
514 *g = (y - Kr * *r - Kb * *b) / (1 - Kr - Kb);
515 #undef Kb
516 #undef Kr
517 } // yuv_to_rgb
518
519 // R'G'B' in the range 0-1 to Y'CbCr Analog (Y' in the range 0-1, PbPr in the range -0.5 - 0.5)
520 void
rgb_to_ypbpr709(float r,float g,float b,float * y,float * pb,float * pr)521 rgb_to_ypbpr709(float r,
522 float g,
523 float b,
524 float *y,
525 float *pb,
526 float *pr)
527 {
528 // ref: http://www.equasys.de/colorconversion.html (BT.709)
529 //*y = 0.2126f * r + 0.7152f * g + 0.0722f * b;
530 //*pb = -0.115f * r - 0.385f * g + 0.500f * b; // or (b - y)/1.8556
531 //*pr = 0.500f * r - 0.454f * g - 0.046f * b; // or (r - y)/1.5748
532
533 // ref: http://www.poynton.com/PDFs/coloureq.pdf (10.5)
534 //*y = 0.2215f * r + 0.7154f * g + 0.0721f * b;
535 //*pb = -0.1145f * r - 0.3855f * g + 0.5000f * b;
536 //*pr = 0.5016f * r - 0.4556f * g - 0.0459f * b;
537
538 //*y = 0.2126390058 * r + 0.7151686783 * g + 0.07219231534 * b;
539 //*pb = (b - *y) / 1.8556;
540 //*pr = (r - *y) / 1.5748;
541 #define Kb 0.07219231534f
542 #define Kr 0.2126390058f
543 *y = Kr * r + (1 - Kr - Kb) * g + Kb * b;
544 *pb = (b - *y) / ( 2 * (1 - Kb) );
545 *pr = (r - *y) / ( 2 * (1 - Kr) );
546 }
547
548 // Y'CbCr Analog (Y in the range 0-1, PbPr in the range -0.5 - 0.5) to R'G'B' in the range 0-1
549 void
ypbpr_to_rgb709(float y,float pb,float pr,float * r,float * g,float * b)550 ypbpr_to_rgb709(float y,
551 float pb,
552 float pr,
553 float *r,
554 float *g,
555 float *b)
556 {
557 // ref: http://www.equasys.de/colorconversion.html (BT.709)
558 //*r = y + 1.575f * pr,
559 //*g = y - 0.187f * pb - 0.468f * pr;
560 //*b = y + 1.856f * pb;
561
562 // ref: http://www.poynton.com/PDFs/coloureq.pdf (10.5)
563 // (there is a sign error on the R' coeff for Cr in Poynton's doc)
564 //*r = y + 1.5701f * pr,
565 //*g = y - 0.1870f * pb - 0.4664f * pr;
566 //*b = y + 1.8556f * pb;
567
568 //*b = pb * 1.8556 + y;
569 //*r = pr * 1.5748 + y;
570 ////*g = (y - 0.2126f * *r - 0.0722f * *b) / 0.7152f;
571 //*g = (y - 0.2126390058 * *r - 0.07219231534 * *b) / 0.7151686783;
572 *b = pb * ( 2 * (1 - Kb) ) + y;
573 *r = pr * ( 2 * (1 - Kr) ) + y;
574 *g = (y - Kr * *r - Kb * *b) / (1 - Kr - Kb);
575 #undef Kb
576 #undef Kr
577 } // yuv_to_rgb
578
579 // R'G'B' in the range 0-1 to Y'CbCr Analog (Y' in the range 0-1, PbPr in the range -0.5 - 0.5)
580 void
rgb_to_ypbpr2020(float r,float g,float b,float * y,float * pb,float * pr)581 rgb_to_ypbpr2020(float r,
582 float g,
583 float b,
584 float *y,
585 float *pb,
586 float *pr)
587 {
588 // ref: https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-0-201208-S!!PDF-E.pdf
589 // (Rec.2020, table 4 p4)
590 //
591 //*y = 0.2627f * r + 0.6780f * g + 0.0593f * b;
592 //*pb = (b - *y) / 1.8814;
593 //*pr = (r - *y) / 1.4746;
594
595 // ref: http://www.poynton.com/PDFs/coloureq.pdf (10.5)
596 //*y = 0.2215f * r + 0.7154f * g + 0.0721f * b;
597 //*pb = -0.1145f * r - 0.3855f * g + 0.5000f * b;
598 //*pr = 0.5016f * r - 0.4556f * g - 0.0459f * b;
599 #define Kb 0.0593f
600 #define Kr 0.2627f
601 *y = Kr * r + (1 - Kr - Kb) * g + Kb * b;
602 *pb = (b - *y) / ( 2 * (1 - Kb) );
603 *pr = (r - *y) / ( 2 * (1 - Kr) );
604 }
605
606 // Y'CbCr Analog (Y in the range 0-1, PbPr in the range -0.5 - 0.5) to R'G'B' in the range 0-1
607 void
ypbpr_to_rgb2020(float y,float pb,float pr,float * r,float * g,float * b)608 ypbpr_to_rgb2020(float y,
609 float pb,
610 float pr,
611 float *r,
612 float *g,
613 float *b)
614 {
615 // ref: https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-0-201208-S!!PDF-E.pdf
616 // (Rec.2020, table 4 p4)
617 //
618 //*b = pb * 1.8814 + y;
619 //*r = pr * 1.4746 + y;
620 //*g = (y - 0.2627f * *r - 0.0593f * *b) / 0.6780f;
621 *b = pb * ( 2 * (1 - Kb) ) + y;
622 *r = pr * ( 2 * (1 - Kr) ) + y;
623 *g = (y - Kr * *r - Kb * *b) / (1 - Kr - Kb);
624 #undef Kb
625 #undef Kr
626 } // yuv_to_rgb
627
628 // R'G'B' in the range 0-1 to Y'UV (Y' in the range 0-1, U in the range -0.436 - 0.436,
629 // V in the range -0.615 - 0.615)
630 void
rgb_to_yuv601(float r,float g,float b,float * y,float * u,float * v)631 rgb_to_yuv601(float r,
632 float g,
633 float b,
634 float *y,
635 float *u,
636 float *v)
637 {
638 /// ref: https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
639 *y = 0.299f * r + 0.587f * g + 0.114f * b;
640 *u = -0.14713f * r - 0.28886f * g + 0.436f * b;
641 *v = 0.615f * r - 0.51499f * g - 0.10001f * b;
642 }
643
644 // Y'UV (Y' in the range 0-1, U in the range -0.436 - 0.436,
645 // V in the range -0.615 - 0.615) to R'G'B' in the range 0-1
646 void
yuv_to_rgb601(float y,float u,float v,float * r,float * g,float * b)647 yuv_to_rgb601(float y,
648 float u,
649 float v,
650 float *r,
651 float *g,
652 float *b)
653 {
654 /// ref: https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601
655 *r = y + 1.13983f * v,
656 *g = y - 0.39465f * u - 0.58060f * v;
657 *b = y + 2.03211f * u;
658 } // yuv_to_rgb
659
660 // R'G'B' in the range 0-1 to Y'UV (Y' in the range 0-1, U in the range -0.436 - 0.436,
661 // V in the range -0.615 - 0.615)
662 void
rgb_to_yuv709(float r,float g,float b,float * y,float * u,float * v)663 rgb_to_yuv709(float r,
664 float g,
665 float b,
666 float *y,
667 float *u,
668 float *v)
669 {
670 /// ref: https://en.wikipedia.org/wiki/YUV#HDTV_with_BT.709
671 *y = 0.2126f * r + 0.7152f * g + 0.0722f * b;
672 *u = -0.09991f * r - 0.33609f * g + 0.436f * b;
673 *v = 0.615f * r - 0.55861f * g - 0.05639f * b;
674 }
675
676 // Y'UV (Y in the range 0-1, U in the range -0.436 - 0.436,
677 // V in the range -0.615 - 0.615) to R'G'B' in the range 0-1
678 void
yuv_to_rgb709(float y,float u,float v,float * r,float * g,float * b)679 yuv_to_rgb709(float y,
680 float u,
681 float v,
682 float *r,
683 float *g,
684 float *b)
685 {
686 /// ref: https://en.wikipedia.org/wiki/YUV#HDTV_with_BT.709
687 *r = y + 1.28033f * v,
688 *g = y - 0.21482f * u - 0.38059f * v;
689 *b = y + 2.12798f * u;
690 } // yuv_to_rgb
691
692 static inline
693 float
labf(float x)694 labf(float x)
695 {
696 return ( (x) >= 0.008856f ? ( std::pow(x, (float)1 / 3) ) : (7.787f * x + 16.0f / 116) );
697 }
698
699 // Convert pixel values from XYZ to Lab color spaces.
700 // Uses the standard D65 white point.
701 void
xyz_to_lab(float x,float y,float z,float * l,float * a,float * b)702 xyz_to_lab(float x,
703 float y,
704 float z,
705 float *l,
706 float *a,
707 float *b)
708 {
709 const float fx = labf( x / (0.412453f + 0.357580f + 0.180423f) );
710 const float fy = labf( y / (0.212671f + 0.715160f + 0.072169f) );
711 const float fz = labf( z / (0.019334f + 0.119193f + 0.950227f) );
712
713 *l = 116 * fy - 16;
714 *a = 500 * (fx - fy);
715 *b = 200 * (fy - fz);
716 }
717
718 static inline
719 float
labfi(float x)720 labfi(float x)
721 {
722 return ( x >= 0.206893f ? (x * x * x) : ( (x - 16.0f / 116) / 7.787f ) );
723 }
724
725 // Convert pixel values from Lab to XYZ color spaces.
726 // Uses the standard D65 white point.
727 void
lab_to_xyz(float l,float a,float b,float * x,float * y,float * z)728 lab_to_xyz(float l,
729 float a,
730 float b,
731 float *x,
732 float *y,
733 float *z)
734 {
735 const float cy = (l + 16) / 116;
736
737 *y = (0.212671f + 0.715160f + 0.072169f) * labfi(cy);
738 const float cx = a / 500 + cy;
739 *x = (0.412453f + 0.357580f + 0.180423f) * labfi(cx);
740 const float cz = cy - b / 200;
741 *z = (0.019334f + 0.119193f + 0.950227f) * labfi(cz);
742 }
743
744 // Convert pixel values from RGB to Lab color spaces.
745 // Uses the standard D65 white point.
746 void
rgb709_to_lab(float r,float g,float b,float * l,float * a,float * b_)747 rgb709_to_lab(float r,
748 float g,
749 float b,
750 float *l,
751 float *a,
752 float *b_)
753 {
754 float x, y, z;
755
756 rgb709_to_xyz(r, g, b, &x, &y, &z);
757 xyz_to_lab(x, y, z, l, a, b_);
758 }
759
760 // Convert pixel values from RGB to Lab color spaces.
761 // Uses the standard D65 white point.
762 void
lab_to_rgb709(float l,float a,float b,float * r,float * g,float * b_)763 lab_to_rgb709(float l,
764 float a,
765 float b,
766 float *r,
767 float *g,
768 float *b_)
769 {
770 float x, y, z;
771
772 lab_to_xyz(l, a, b, &x, &y, &z);
773 xyz_to_rgb709(x, y, z, r, g, b_);
774 }
775 } // namespace Color
776 } //namespace OFX
777
778