Fix unused variable warning in release builds
[blender.git] / intern / opencolorio / ocio_impl.cc
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 Blender Foundation.
17  * All rights reserved.
18  */
19
20 #include <cassert>
21 #include <iostream>
22 #include <math.h>
23 #include <sstream>
24 #include <string.h>
25
26 #ifdef _MSC_VER
27 #  pragma warning(push)
28 #  pragma warning(disable : 4251 4275)
29 #endif
30 #include <OpenColorIO/OpenColorIO.h>
31 #ifdef _MSC_VER
32 #  pragma warning(pop)
33 #endif
34
35 using namespace OCIO_NAMESPACE;
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLI_math.h"
40 #include "BLI_math_color.h"
41 #include "BLI_math_matrix.h"
42
43 #include "ocio_impl.h"
44
45 #if !defined(WITH_ASSERT_ABORT)
46 #  define OCIO_abort()
47 #else
48 #  include <stdlib.h>
49 #  define OCIO_abort() abort()
50 #endif
51
52 #if defined(_MSC_VER)
53 #  define __func__ __FUNCTION__
54 #endif
55
56 static void OCIO_reportError(const char *err)
57 {
58   std::cerr << "OpenColorIO Error: " << err << std::endl;
59
60   OCIO_abort();
61 }
62
63 static void OCIO_reportException(Exception &exception)
64 {
65   OCIO_reportError(exception.what());
66 }
67
68 OCIO_ConstConfigRcPtr *OCIOImpl::getCurrentConfig(void)
69 {
70   ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
71
72   try {
73     *config = GetCurrentConfig();
74
75     if (*config)
76       return (OCIO_ConstConfigRcPtr *)config;
77   }
78   catch (Exception &exception) {
79     OCIO_reportException(exception);
80   }
81
82   OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
83
84   return NULL;
85 }
86
87 void OCIOImpl::setCurrentConfig(const OCIO_ConstConfigRcPtr *config)
88 {
89   try {
90     SetCurrentConfig(*(ConstConfigRcPtr *)config);
91   }
92   catch (Exception &exception) {
93     OCIO_reportException(exception);
94   }
95 }
96
97 OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromEnv(void)
98 {
99   ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
100
101   try {
102     *config = Config::CreateFromEnv();
103
104     if (*config)
105       return (OCIO_ConstConfigRcPtr *)config;
106   }
107   catch (Exception &exception) {
108     OCIO_reportException(exception);
109   }
110
111   OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
112
113   return NULL;
114 }
115
116 OCIO_ConstConfigRcPtr *OCIOImpl::configCreateFromFile(const char *filename)
117 {
118   ConstConfigRcPtr *config = OBJECT_GUARDED_NEW(ConstConfigRcPtr);
119
120   try {
121     *config = Config::CreateFromFile(filename);
122
123     if (*config)
124       return (OCIO_ConstConfigRcPtr *)config;
125   }
126   catch (Exception &exception) {
127     OCIO_reportException(exception);
128   }
129
130   OBJECT_GUARDED_DELETE(config, ConstConfigRcPtr);
131
132   return NULL;
133 }
134
135 void OCIOImpl::configRelease(OCIO_ConstConfigRcPtr *config)
136 {
137   OBJECT_GUARDED_DELETE((ConstConfigRcPtr *)config, ConstConfigRcPtr);
138 }
139
140 int OCIOImpl::configGetNumColorSpaces(OCIO_ConstConfigRcPtr *config)
141 {
142   try {
143     return (*(ConstConfigRcPtr *)config)->getNumColorSpaces();
144   }
145   catch (Exception &exception) {
146     OCIO_reportException(exception);
147   }
148
149   return 0;
150 }
151
152 const char *OCIOImpl::configGetColorSpaceNameByIndex(OCIO_ConstConfigRcPtr *config, int index)
153 {
154   try {
155     return (*(ConstConfigRcPtr *)config)->getColorSpaceNameByIndex(index);
156   }
157   catch (Exception &exception) {
158     OCIO_reportException(exception);
159   }
160
161   return NULL;
162 }
163
164 OCIO_ConstColorSpaceRcPtr *OCIOImpl::configGetColorSpace(OCIO_ConstConfigRcPtr *config,
165                                                          const char *name)
166 {
167   ConstColorSpaceRcPtr *cs = OBJECT_GUARDED_NEW(ConstColorSpaceRcPtr);
168
169   try {
170     *cs = (*(ConstConfigRcPtr *)config)->getColorSpace(name);
171
172     if (*cs)
173       return (OCIO_ConstColorSpaceRcPtr *)cs;
174   }
175   catch (Exception &exception) {
176     OCIO_reportException(exception);
177   }
178
179   OBJECT_GUARDED_DELETE(cs, ConstColorSpaceRcPtr);
180
181   return NULL;
182 }
183
184 int OCIOImpl::configGetIndexForColorSpace(OCIO_ConstConfigRcPtr *config, const char *name)
185 {
186   try {
187     return (*(ConstConfigRcPtr *)config)->getIndexForColorSpace(name);
188   }
189   catch (Exception &exception) {
190     OCIO_reportException(exception);
191   }
192
193   return -1;
194 }
195
196 const char *OCIOImpl::configGetDefaultDisplay(OCIO_ConstConfigRcPtr *config)
197 {
198   try {
199     return (*(ConstConfigRcPtr *)config)->getDefaultDisplay();
200   }
201   catch (Exception &exception) {
202     OCIO_reportException(exception);
203   }
204
205   return NULL;
206 }
207
208 int OCIOImpl::configGetNumDisplays(OCIO_ConstConfigRcPtr *config)
209 {
210   try {
211     return (*(ConstConfigRcPtr *)config)->getNumDisplays();
212   }
213   catch (Exception &exception) {
214     OCIO_reportException(exception);
215   }
216
217   return 0;
218 }
219
220 const char *OCIOImpl::configGetDisplay(OCIO_ConstConfigRcPtr *config, int index)
221 {
222   try {
223     return (*(ConstConfigRcPtr *)config)->getDisplay(index);
224   }
225   catch (Exception &exception) {
226     OCIO_reportException(exception);
227   }
228
229   return NULL;
230 }
231
232 const char *OCIOImpl::configGetDefaultView(OCIO_ConstConfigRcPtr *config, const char *display)
233 {
234   try {
235     return (*(ConstConfigRcPtr *)config)->getDefaultView(display);
236   }
237   catch (Exception &exception) {
238     OCIO_reportException(exception);
239   }
240
241   return NULL;
242 }
243
244 int OCIOImpl::configGetNumViews(OCIO_ConstConfigRcPtr *config, const char *display)
245 {
246   try {
247     return (*(ConstConfigRcPtr *)config)->getNumViews(display);
248   }
249   catch (Exception &exception) {
250     OCIO_reportException(exception);
251   }
252
253   return 0;
254 }
255
256 const char *OCIOImpl::configGetView(OCIO_ConstConfigRcPtr *config, const char *display, int index)
257 {
258   try {
259     return (*(ConstConfigRcPtr *)config)->getView(display, index);
260   }
261   catch (Exception &exception) {
262     OCIO_reportException(exception);
263   }
264
265   return NULL;
266 }
267
268 const char *OCIOImpl::configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *config,
269                                                      const char *display,
270                                                      const char *view)
271 {
272   try {
273     return (*(ConstConfigRcPtr *)config)->getDisplayViewColorSpaceName(display, view);
274   }
275   catch (Exception &exception) {
276     OCIO_reportException(exception);
277   }
278
279   return NULL;
280 }
281
282 void OCIOImpl::configGetDefaultLumaCoefs(OCIO_ConstConfigRcPtr *config, float *rgb)
283 {
284   try {
285     double rgb_double[3];
286     (*(ConstConfigRcPtr *)config)->getDefaultLumaCoefs(rgb_double);
287     rgb[0] = rgb_double[0];
288     rgb[1] = rgb_double[1];
289     rgb[2] = rgb_double[2];
290   }
291   catch (Exception &exception) {
292     OCIO_reportException(exception);
293   }
294 }
295
296 static bool to_scene_linear_matrix(ConstConfigRcPtr &config,
297                                    const char *colorspace,
298                                    float to_scene_linear[3][3])
299 {
300   ConstProcessorRcPtr processor;
301   try {
302     processor = config->getProcessor(colorspace, ROLE_SCENE_LINEAR);
303   }
304   catch (Exception &exception) {
305     OCIO_reportException(exception);
306     return false;
307   }
308
309   if (!processor) {
310     return false;
311   }
312
313   ConstCPUProcessorRcPtr cpu_processor = processor->getDefaultCPUProcessor();
314   if (!cpu_processor) {
315     return false;
316   }
317
318   unit_m3(to_scene_linear);
319   cpu_processor->applyRGB(to_scene_linear[0]);
320   cpu_processor->applyRGB(to_scene_linear[1]);
321   cpu_processor->applyRGB(to_scene_linear[2]);
322   return true;
323 }
324
325 void OCIOImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config_, float xyz_to_rgb[3][3])
326 {
327   ConstConfigRcPtr config = (*(ConstConfigRcPtr *)config_);
328
329   /* Default to ITU-BT.709 in case no appropriate transform found.
330    * Note XYZ is defined here as having a D65 white point. */
331   memcpy(xyz_to_rgb, OCIO_XYZ_TO_LINEAR_SRGB, sizeof(OCIO_XYZ_TO_LINEAR_SRGB));
332
333   /* Get from OpenColorO config if it has the required roles. */
334   if (!config->hasRole(ROLE_SCENE_LINEAR)) {
335     return;
336   }
337
338   if (config->hasRole("aces_interchange")) {
339     /* Standard OpenColorIO role, defined as ACES2065-1. */
340     const float xyz_E_to_aces[3][3] = {{1.0498110175f, -0.4959030231f, 0.0f},
341                                        {0.0f, 1.3733130458f, 0.0f},
342                                        {-0.0000974845f, 0.0982400361f, 0.9912520182f}};
343     const float xyz_D65_to_E[3][3] = {
344         {1.0521111f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.9184170f}};
345
346     float aces_to_rgb[3][3];
347     if (to_scene_linear_matrix(config, "aces_interchange", aces_to_rgb)) {
348       mul_m3_series(xyz_to_rgb, aces_to_rgb, xyz_E_to_aces, xyz_D65_to_E);
349     }
350   }
351   else if (config->hasRole("XYZ")) {
352     /* Custom role used before the standard existed. */
353     to_scene_linear_matrix(config, "XYZ", xyz_to_rgb);
354   }
355 }
356
357 int OCIOImpl::configGetNumLooks(OCIO_ConstConfigRcPtr *config)
358 {
359   try {
360     return (*(ConstConfigRcPtr *)config)->getNumLooks();
361   }
362   catch (Exception &exception) {
363     OCIO_reportException(exception);
364   }
365
366   return 0;
367 }
368
369 const char *OCIOImpl::configGetLookNameByIndex(OCIO_ConstConfigRcPtr *config, int index)
370 {
371   try {
372     return (*(ConstConfigRcPtr *)config)->getLookNameByIndex(index);
373   }
374   catch (Exception &exception) {
375     OCIO_reportException(exception);
376   }
377
378   return NULL;
379 }
380
381 OCIO_ConstLookRcPtr *OCIOImpl::configGetLook(OCIO_ConstConfigRcPtr *config, const char *name)
382 {
383   ConstLookRcPtr *look = OBJECT_GUARDED_NEW(ConstLookRcPtr);
384
385   try {
386     *look = (*(ConstConfigRcPtr *)config)->getLook(name);
387
388     if (*look)
389       return (OCIO_ConstLookRcPtr *)look;
390   }
391   catch (Exception &exception) {
392     OCIO_reportException(exception);
393   }
394
395   OBJECT_GUARDED_DELETE(look, ConstLookRcPtr);
396
397   return NULL;
398 }
399
400 const char *OCIOImpl::lookGetProcessSpace(OCIO_ConstLookRcPtr *look)
401 {
402   return (*(ConstLookRcPtr *)look)->getProcessSpace();
403 }
404
405 void OCIOImpl::lookRelease(OCIO_ConstLookRcPtr *look)
406 {
407   OBJECT_GUARDED_DELETE((ConstLookRcPtr *)look, ConstLookRcPtr);
408 }
409
410 int OCIOImpl::colorSpaceIsInvertible(OCIO_ConstColorSpaceRcPtr *cs_)
411 {
412   ConstColorSpaceRcPtr *cs = (ConstColorSpaceRcPtr *)cs_;
413   const char *family = (*cs)->getFamily();
414
415   if (!strcmp(family, "rrt") || !strcmp(family, "display")) {
416     /* assume display and rrt transformations are not invertible in fact some of them could be,
417      * but it doesn't make much sense to allow use them as invertible. */
418     return false;
419   }
420
421   if ((*cs)->isData()) {
422     /* data color spaces don't have transformation at all */
423     return true;
424   }
425
426   if ((*cs)->getTransform(COLORSPACE_DIR_TO_REFERENCE)) {
427     /* if there's defined transform to reference space,
428      * color space could be converted to scene linear. */
429     return true;
430   }
431
432   return true;
433 }
434
435 int OCIOImpl::colorSpaceIsData(OCIO_ConstColorSpaceRcPtr *cs)
436 {
437   return (*(ConstColorSpaceRcPtr *)cs)->isData();
438 }
439
440 static float compare_floats(float a, float b, float abs_diff, int ulp_diff)
441 {
442   /* Returns true if the absolute difference is smaller than abs_diff (for numbers near zero)
443    * or their relative difference is less than ulp_diff ULPs. Based on:
444    * https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ */
445   if (fabsf(a - b) < abs_diff) {
446     return true;
447   }
448
449   if ((a < 0.0f) != (b < 0.0f)) {
450     return false;
451   }
452
453   return (abs((*(int *)&a) - (*(int *)&b)) < ulp_diff);
454 }
455
456 void OCIOImpl::colorSpaceIsBuiltin(OCIO_ConstConfigRcPtr *config_,
457                                    OCIO_ConstColorSpaceRcPtr *cs_,
458                                    bool &is_scene_linear,
459                                    bool &is_srgb)
460 {
461   ConstConfigRcPtr *config = (ConstConfigRcPtr *)config_;
462   ConstColorSpaceRcPtr *cs = (ConstColorSpaceRcPtr *)cs_;
463   ConstProcessorRcPtr processor;
464
465   try {
466     processor = (*config)->getProcessor((*cs)->getName(), "scene_linear");
467   }
468   catch (Exception &) {
469     /* Silently ignore if no conversion possible, then it's not scene linear or sRGB. */
470     is_scene_linear = false;
471     is_srgb = false;
472     return;
473   }
474
475   ConstCPUProcessorRcPtr cpu_processor = processor->getDefaultCPUProcessor();
476
477   is_scene_linear = true;
478   is_srgb = true;
479   for (int i = 0; i < 256; i++) {
480     float v = i / 255.0f;
481
482     float cR[3] = {v, 0, 0};
483     float cG[3] = {0, v, 0};
484     float cB[3] = {0, 0, v};
485     float cW[3] = {v, v, v};
486     cpu_processor->applyRGB(cR);
487     cpu_processor->applyRGB(cG);
488     cpu_processor->applyRGB(cB);
489     cpu_processor->applyRGB(cW);
490
491     /* Make sure that there is no channel crosstalk. */
492     if (fabsf(cR[1]) > 1e-5f || fabsf(cR[2]) > 1e-5f || fabsf(cG[0]) > 1e-5f ||
493         fabsf(cG[2]) > 1e-5f || fabsf(cB[0]) > 1e-5f || fabsf(cB[1]) > 1e-5f) {
494       is_scene_linear = false;
495       is_srgb = false;
496       break;
497     }
498     /* Make sure that the three primaries combine linearly. */
499     if (!compare_floats(cR[0], cW[0], 1e-6f, 64) || !compare_floats(cG[1], cW[1], 1e-6f, 64) ||
500         !compare_floats(cB[2], cW[2], 1e-6f, 64)) {
501       is_scene_linear = false;
502       is_srgb = false;
503       break;
504     }
505     /* Make sure that the three channels behave identically. */
506     if (!compare_floats(cW[0], cW[1], 1e-6f, 64) || !compare_floats(cW[1], cW[2], 1e-6f, 64)) {
507       is_scene_linear = false;
508       is_srgb = false;
509       break;
510     }
511
512     float out_v = (cW[0] + cW[1] + cW[2]) * (1.0f / 3.0f);
513     if (!compare_floats(v, out_v, 1e-6f, 64)) {
514       is_scene_linear = false;
515     }
516     if (!compare_floats(srgb_to_linearrgb(v), out_v, 1e-6f, 64)) {
517       is_srgb = false;
518     }
519   }
520 }
521
522 void OCIOImpl::colorSpaceRelease(OCIO_ConstColorSpaceRcPtr *cs)
523 {
524   OBJECT_GUARDED_DELETE((ConstColorSpaceRcPtr *)cs, ConstColorSpaceRcPtr);
525 }
526
527 OCIO_ConstProcessorRcPtr *OCIOImpl::configGetProcessorWithNames(OCIO_ConstConfigRcPtr *config,
528                                                                 const char *srcName,
529                                                                 const char *dstName)
530 {
531   ConstProcessorRcPtr *processor = OBJECT_GUARDED_NEW(ConstProcessorRcPtr);
532
533   try {
534     *processor = (*(ConstConfigRcPtr *)config)->getProcessor(srcName, dstName);
535
536     if (*processor)
537       return (OCIO_ConstProcessorRcPtr *)processor;
538   }
539   catch (Exception &exception) {
540     OCIO_reportException(exception);
541   }
542
543   OBJECT_GUARDED_DELETE(processor, ConstProcessorRcPtr);
544
545   return 0;
546 }
547
548 void OCIOImpl::processorRelease(OCIO_ConstProcessorRcPtr *processor)
549 {
550   OBJECT_GUARDED_DELETE(processor, ConstProcessorRcPtr);
551 }
552
553 OCIO_ConstCPUProcessorRcPtr *OCIOImpl::processorGetCPUProcessor(
554     OCIO_ConstProcessorRcPtr *processor)
555 {
556   ConstCPUProcessorRcPtr *cpu_processor = OBJECT_GUARDED_NEW(ConstCPUProcessorRcPtr);
557   *cpu_processor = (*(ConstProcessorRcPtr *)processor)->getDefaultCPUProcessor();
558   return (OCIO_ConstCPUProcessorRcPtr *)cpu_processor;
559 }
560
561 void OCIOImpl::cpuProcessorApply(OCIO_ConstCPUProcessorRcPtr *cpu_processor,
562                                  OCIO_PackedImageDesc *img)
563 {
564   try {
565     (*(ConstCPUProcessorRcPtr *)cpu_processor)->apply(*(PackedImageDesc *)img);
566   }
567   catch (Exception &exception) {
568     OCIO_reportException(exception);
569   }
570 }
571
572 void OCIOImpl::cpuProcessorApply_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor,
573                                            OCIO_PackedImageDesc *img_)
574 {
575   try {
576     PackedImageDesc *img = (PackedImageDesc *)img_;
577     int channels = img->getNumChannels();
578
579     if (channels == 4) {
580       assert(img->isFloat());
581       float *pixels = (float *)img->getData();
582
583       int width = img->getWidth();
584       int height = img->getHeight();
585
586       for (int y = 0; y < height; y++) {
587         for (int x = 0; x < width; x++) {
588           float *pixel = pixels + 4 * (y * width + x);
589
590           cpuProcessorApplyRGBA_predivide(cpu_processor, pixel);
591         }
592       }
593     }
594     else {
595       (*(ConstCPUProcessorRcPtr *)cpu_processor)->apply(*img);
596     }
597   }
598   catch (Exception &exception) {
599     OCIO_reportException(exception);
600   }
601 }
602
603 void OCIOImpl::cpuProcessorApplyRGB(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel)
604 {
605   (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGB(pixel);
606 }
607
608 void OCIOImpl::cpuProcessorApplyRGBA(OCIO_ConstCPUProcessorRcPtr *cpu_processor, float *pixel)
609 {
610   (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGBA(pixel);
611 }
612
613 void OCIOImpl::cpuProcessorApplyRGBA_predivide(OCIO_ConstCPUProcessorRcPtr *cpu_processor,
614                                                float *pixel)
615 {
616   if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
617     (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGBA(pixel);
618   }
619   else {
620     float alpha, inv_alpha;
621
622     alpha = pixel[3];
623     inv_alpha = 1.0f / alpha;
624
625     pixel[0] *= inv_alpha;
626     pixel[1] *= inv_alpha;
627     pixel[2] *= inv_alpha;
628
629     (*(ConstCPUProcessorRcPtr *)cpu_processor)->applyRGBA(pixel);
630
631     pixel[0] *= alpha;
632     pixel[1] *= alpha;
633     pixel[2] *= alpha;
634   }
635 }
636
637 void OCIOImpl::cpuProcessorRelease(OCIO_ConstCPUProcessorRcPtr *cpu_processor)
638 {
639   OBJECT_GUARDED_DELETE(cpu_processor, ConstCPUProcessorRcPtr);
640 }
641
642 const char *OCIOImpl::colorSpaceGetName(OCIO_ConstColorSpaceRcPtr *cs)
643 {
644   return (*(ConstColorSpaceRcPtr *)cs)->getName();
645 }
646
647 const char *OCIOImpl::colorSpaceGetDescription(OCIO_ConstColorSpaceRcPtr *cs)
648 {
649   return (*(ConstColorSpaceRcPtr *)cs)->getDescription();
650 }
651
652 const char *OCIOImpl::colorSpaceGetFamily(OCIO_ConstColorSpaceRcPtr *cs)
653 {
654   return (*(ConstColorSpaceRcPtr *)cs)->getFamily();
655 }
656
657 OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr *config_,
658                                                            const char *input,
659                                                            const char *view,
660                                                            const char *display,
661                                                            const char *look,
662                                                            const float scale,
663                                                            const float exponent)
664
665 {
666   ConstConfigRcPtr config = *(ConstConfigRcPtr *)config_;
667   GroupTransformRcPtr group = GroupTransform::Create();
668
669   /* Exposure. */
670   if (scale != 1.0f) {
671     /* Always apply exposure in scene linear. */
672     ColorSpaceTransformRcPtr ct = ColorSpaceTransform::Create();
673     ct->setSrc(input);
674     ct->setDst(ROLE_SCENE_LINEAR);
675     group->appendTransform(ct);
676
677     /* Make further transforms aware of the color space change. */
678     input = ROLE_SCENE_LINEAR;
679
680     /* Apply scale. */
681     MatrixTransformRcPtr mt = MatrixTransform::Create();
682     const double matrix[16] = {
683         scale, 0.0, 0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0, 0.0, 1.0};
684     mt->setMatrix(matrix);
685     group->appendTransform(mt);
686   }
687
688   /* Add look transform. */
689   bool use_look = (look != nullptr && look[0] != 0);
690   if (use_look) {
691     const char *look_output = LookTransform::GetLooksResultColorSpace(
692         config, config->getCurrentContext(), look);
693
694     if (look_output != nullptr && look_output[0] != 0) {
695       LookTransformRcPtr lt = LookTransform::Create();
696       lt->setSrc(input);
697       lt->setDst(look_output);
698       lt->setLooks(look);
699       group->appendTransform(lt);
700
701       /* Make further transforms aware of the color space change. */
702       input = look_output;
703     }
704     else {
705       /* For empty looks, no output color space is returned. */
706       use_look = false;
707     }
708   }
709
710   /* Add view and display transform. */
711   DisplayViewTransformRcPtr dvt = DisplayViewTransform::Create();
712   dvt->setSrc(input);
713   dvt->setLooksBypass(use_look);
714   dvt->setView(view);
715   dvt->setDisplay(display);
716   group->appendTransform(dvt);
717
718   /* Gamma. */
719   if (exponent != 1.0f) {
720     ExponentTransformRcPtr et = ExponentTransform::Create();
721     const double value[4] = {exponent, exponent, exponent, 1.0};
722     et->setValue(value);
723     group->appendTransform(et);
724   }
725
726   /* Create processor from transform. This is the moment were OCIO validates
727    * the entire transform, no need to check for the validity of inputs above. */
728   ConstProcessorRcPtr *p = OBJECT_GUARDED_NEW(ConstProcessorRcPtr);
729
730   try {
731     *p = config->getProcessor(group);
732
733     if (*p)
734       return (OCIO_ConstProcessorRcPtr *)p;
735   }
736   catch (Exception &exception) {
737     OCIO_reportException(exception);
738   }
739
740   OBJECT_GUARDED_DELETE(p, ConstProcessorRcPtr);
741   return NULL;
742 }
743
744 OCIO_PackedImageDesc *OCIOImpl::createOCIO_PackedImageDesc(float *data,
745                                                            long width,
746                                                            long height,
747                                                            long numChannels,
748                                                            long chanStrideBytes,
749                                                            long xStrideBytes,
750                                                            long yStrideBytes)
751 {
752   try {
753     void *mem = MEM_mallocN(sizeof(PackedImageDesc), __func__);
754     PackedImageDesc *id = new (mem) PackedImageDesc(data,
755                                                     width,
756                                                     height,
757                                                     numChannels,
758                                                     BIT_DEPTH_F32,
759                                                     chanStrideBytes,
760                                                     xStrideBytes,
761                                                     yStrideBytes);
762
763     return (OCIO_PackedImageDesc *)id;
764   }
765   catch (Exception &exception) {
766     OCIO_reportException(exception);
767   }
768
769   return NULL;
770 }
771
772 void OCIOImpl::OCIO_PackedImageDescRelease(OCIO_PackedImageDesc *id)
773 {
774   OBJECT_GUARDED_DELETE((PackedImageDesc *)id, PackedImageDesc);
775 }
776
777 const char *OCIOImpl::getVersionString(void)
778 {
779   return GetVersion();
780 }
781
782 int OCIOImpl::getVersionHex(void)
783 {
784   return GetVersionHex();
785 }