Color Management: fix crash when displaying render preview if OCIO was disabled buildtime
[blender.git] / intern / opencolorio / ocio_capi.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Xavier Thomas
22  *                 Lukas Toene,
23  *                 Sergey Sharybin
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include <iostream>
29 #include <string.h>
30
31 #include <OpenColorIO/OpenColorIO.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #define OCIO_CAPI_IMPLEMENTATION
36 #include "ocio_capi.h"
37
38 #ifdef NDEBUG
39 #  define OCIO_abort()
40 #else
41 #  include <stdlib.h>
42 #  define OCIO_abort() abort()
43 #endif
44
45 #if defined(_MSC_VER)
46 #  define __func__ __FUNCTION__
47 #endif
48
49 #define MEM_NEW(type) new(MEM_mallocN(sizeof(type), __func__)) type()
50 #define MEM_DELETE(what, type) { what->~type(); MEM_freeN(what); } (void)0
51
52 static void OCIO_reportError(const char *err)
53 {
54         std::cerr << "OpenColorIO Error: " << err << std::endl;
55
56         OCIO_abort();
57 }
58
59 static void OCIO_reportException(Exception &exception)
60 {
61         OCIO_reportError(exception.what());
62 }
63
64 ConstConfigRcPtr *OCIO_getCurrentConfig(void)
65 {
66         ConstConfigRcPtr *config = MEM_NEW(ConstConfigRcPtr);
67
68         try {
69                 *config = GetCurrentConfig();
70
71                 if(*config)
72                         return config;
73         }
74         catch (Exception &exception) {
75                 OCIO_reportException(exception);
76         }
77
78         return NULL;
79 }
80
81 ConstConfigRcPtr *OCIO_getDefaultConfig(void)
82 {
83         return NULL;
84 }
85
86 void OCIO_setCurrentConfig(const ConstConfigRcPtr *config)
87 {
88         try {
89                 SetCurrentConfig(*config);
90         }
91         catch (Exception &exception) {
92                 OCIO_reportException(exception);
93         }
94 }
95
96 ConstConfigRcPtr *OCIO_configCreateFromEnv(void)
97 {
98         ConstConfigRcPtr *config = MEM_NEW(ConstConfigRcPtr);
99
100         try {
101                 *config = Config::CreateFromEnv();
102
103                 if (*config)
104                         return config;
105         }
106         catch (Exception &exception) {
107                 OCIO_reportException(exception);
108         }
109
110         return NULL;
111 }
112
113
114 ConstConfigRcPtr *OCIO_configCreateFromFile(const char *filename)
115 {
116         ConstConfigRcPtr *config = MEM_NEW(ConstConfigRcPtr);
117
118         try {
119                 *config = Config::CreateFromFile(filename);
120
121                 if (*config)
122                         return config;
123         }
124         catch (Exception &exception) {
125                 OCIO_reportException(exception);
126         }
127
128         return NULL;
129 }
130
131 void OCIO_configRelease(ConstConfigRcPtr *config)
132 {
133         MEM_DELETE(config, ConstConfigRcPtr);
134 }
135
136 int OCIO_configGetNumColorSpaces(ConstConfigRcPtr *config)
137 {
138         try {
139                 return (*config)->getNumColorSpaces();
140         }
141         catch (Exception &exception) {
142                 OCIO_reportException(exception);
143         }
144
145         return 0;
146 }
147
148 const char *OCIO_configGetColorSpaceNameByIndex(ConstConfigRcPtr *config, int index)
149 {
150         try {
151                 return (*config)->getColorSpaceNameByIndex(index);
152         }
153         catch (Exception &exception) {
154                 OCIO_reportException(exception);
155         }
156
157         return NULL;
158 }
159
160 ConstColorSpaceRcPtr *OCIO_configGetColorSpace(ConstConfigRcPtr *config, const char *name)
161 {
162         ConstColorSpaceRcPtr *cs = MEM_NEW(ConstColorSpaceRcPtr);
163
164         try {
165                 *cs = (*config)->getColorSpace(name);
166
167                 if (*cs)
168                         return cs;
169         }
170         catch (Exception &exception) {
171                 OCIO_reportException(exception);
172                 MEM_DELETE(cs, ConstColorSpaceRcPtr);
173         }
174
175         return NULL;
176 }
177
178 int OCIO_configGetIndexForColorSpace(ConstConfigRcPtr *config, const char *name)
179 {
180         try {
181                 return (*config)->getIndexForColorSpace(name);
182         }
183         catch (Exception &exception) {
184                 OCIO_reportException(exception);
185         }
186
187         return -1;
188 }
189
190 const char *OCIO_configGetDefaultDisplay(ConstConfigRcPtr *config)
191 {
192         try {
193                 return (*config)->getDefaultDisplay();
194         }
195         catch (Exception &exception) {
196                 OCIO_reportException(exception);
197         }
198
199         return NULL;
200 }
201
202 int OCIO_configGetNumDisplays(ConstConfigRcPtr* config)
203 {
204         try {
205                 return (*config)->getNumDisplays();
206         }
207         catch (Exception &exception) {
208                 OCIO_reportException(exception);
209         }
210
211         return 0;
212 }
213
214 const char *OCIO_configGetDisplay(ConstConfigRcPtr *config, int index)
215 {
216         try {
217                 return (*config)->getDisplay(index);
218         }
219         catch (Exception &exception) {
220                 OCIO_reportException(exception);
221         }
222
223         return NULL;
224 }
225
226 const char *OCIO_configGetDefaultView(ConstConfigRcPtr *config, const char *display)
227 {
228         try {
229                 return (*config)->getDefaultView(display);
230         }
231         catch (Exception &exception) {
232                 OCIO_reportException(exception);
233         }
234
235         return NULL;
236 }
237
238 int OCIO_configGetNumViews(ConstConfigRcPtr *config, const char *display)
239 {
240         try {
241                 return (*config)->getNumViews(display);
242         }
243         catch (Exception &exception) {
244                 OCIO_reportException(exception);
245         }
246
247         return 0;
248 }
249
250 const char *OCIO_configGetView(ConstConfigRcPtr *config, const char *display, int index)
251 {
252         try {
253                 return (*config)->getView(display, index);
254         }
255         catch (Exception &exception) {
256                 OCIO_reportException(exception);
257         }
258
259         return NULL;
260 }
261
262 const char *OCIO_configGetDisplayColorSpaceName(ConstConfigRcPtr *config, const char *display, const char *view)
263 {
264         try {
265                 return (*config)->getDisplayColorSpaceName(display, view);
266         }
267         catch (Exception &exception) {
268                 OCIO_reportException(exception);
269         }
270
271         return NULL;
272 }
273
274 int OCIO_colorSpaceIsInvertible(ConstColorSpaceRcPtr *cs)
275 {
276         const char *family = (*cs)->getFamily();
277
278         if (!strcmp(family, "rrt") || !strcmp(family, "display")) {
279                 /* assume display and rrt transformations are not invertible
280                  * in fact some of them could be, but it doesn't make much sense to allow use them as invertible
281                  */
282                 return false;
283         }
284
285         if ((*cs)->isData()) {
286                 /* data color spaces don't have transformation at all */
287                 return true;
288         }
289
290         if ((*cs)->getTransform(COLORSPACE_DIR_TO_REFERENCE)) {
291                 /* if there's defined transform to reference space, color space could be converted to scene linear */
292                 return true;
293         }
294
295         return true;
296 }
297
298 void OCIO_colorSpaceRelease(ConstColorSpaceRcPtr *cs)
299 {
300         MEM_DELETE(cs, ConstColorSpaceRcPtr);
301 }
302
303 ConstProcessorRcPtr *OCIO_configGetProcessorWithNames(ConstConfigRcPtr *config, const char *srcName, const char *dstName)
304 {
305         ConstProcessorRcPtr *p = MEM_NEW(ConstProcessorRcPtr);
306
307         try {
308                 *p = (*config)->getProcessor(srcName, dstName);
309
310                 if (*p)
311                         return p;
312         }
313         catch (Exception &exception) {
314                 OCIO_reportException(exception);
315         }
316
317         return 0;
318 }
319
320 ConstProcessorRcPtr *OCIO_configGetProcessor(ConstConfigRcPtr *config, ConstTransformRcPtr *transform)
321 {
322         ConstProcessorRcPtr *p = MEM_NEW(ConstProcessorRcPtr);
323
324         try {
325                 *p = (*config)->getProcessor(*transform);
326
327                 if (*p)
328                         return p;
329         }
330         catch (Exception &exception) {
331                 OCIO_reportException(exception);
332         }
333
334         return NULL;
335 }
336
337 void OCIO_processorApply(ConstProcessorRcPtr *processor, PackedImageDesc *img)
338 {
339         try {
340                 (*processor)->apply(*img);
341         }
342         catch (Exception &exception) {
343                 OCIO_reportException(exception);
344         }
345 }
346
347 void OCIO_processorApply_predivide(ConstProcessorRcPtr *processor, PackedImageDesc *img)
348 {
349         try {
350                 int channels = img->getNumChannels();
351
352                 if (channels == 4) {
353                         float *pixels = img->getData();
354
355                         int width = img->getWidth();
356                         int height = img->getHeight();
357
358                         for (int y = 0; y < height; y++) {
359                                 for (int x = 0; x < width; x++) {
360                                         float *pixel = pixels + 4 * (y * width + x);
361
362                                         OCIO_processorApplyRGBA_predivide(processor, pixel);
363                                 }
364                         }
365                 }
366                 else {
367                         (*processor)->apply(*img);
368                 }
369         }
370         catch (Exception &exception) {
371                 OCIO_reportException(exception);
372         }
373 }
374
375 void OCIO_processorApplyRGB(ConstProcessorRcPtr *processor, float *pixel)
376 {
377         (*processor)->applyRGB(pixel);
378 }
379
380 void OCIO_processorApplyRGBA(ConstProcessorRcPtr *processor, float *pixel)
381 {
382         (*processor)->applyRGBA(pixel);
383 }
384
385 void OCIO_processorApplyRGBA_predivide(ConstProcessorRcPtr *processor, float *pixel)
386 {
387         if (pixel[3] == 1.0f || pixel[3] == 0.0f) {
388                 (*processor)->applyRGBA(pixel);
389         }
390         else {
391                 float alpha, inv_alpha;
392
393                 alpha = pixel[3];
394                 inv_alpha = 1.0f / alpha;
395
396                 pixel[0] *= inv_alpha;
397                 pixel[1] *= inv_alpha;
398                 pixel[2] *= inv_alpha;
399
400                 (*processor)->applyRGBA(pixel);
401
402                 pixel[0] *= alpha;
403                 pixel[1] *= alpha;
404                 pixel[2] *= alpha;
405         }
406 }
407
408 void OCIO_processorRelease(ConstProcessorRcPtr *p)
409 {
410         p->~ConstProcessorRcPtr();
411         MEM_freeN(p);
412 }
413
414 const char *OCIO_colorSpaceGetName(ConstColorSpaceRcPtr *cs)
415 {
416         return (*cs)->getName();
417 }
418
419 const char *OCIO_colorSpaceGetDescription(ConstColorSpaceRcPtr *cs)
420 {
421         return (*cs)->getDescription();
422 }
423
424 const char *OCIO_colorSpaceGetFamily(ConstColorSpaceRcPtr *cs)
425 {
426         return (*cs)->getFamily();
427 }
428
429 DisplayTransformRcPtr *OCIO_createDisplayTransform(void)
430 {
431         DisplayTransformRcPtr *dt = MEM_NEW(DisplayTransformRcPtr);
432
433         *dt = DisplayTransform::Create();
434
435         return dt;
436 }
437
438 void OCIO_displayTransformSetInputColorSpaceName(DisplayTransformRcPtr *dt, const char *name)
439 {
440         (*dt)->setInputColorSpaceName(name);
441 }
442
443 void OCIO_displayTransformSetDisplay(DisplayTransformRcPtr *dt, const char *name)
444 {
445         (*dt)->setDisplay(name);
446 }
447
448 void OCIO_displayTransformSetView(DisplayTransformRcPtr *dt, const char *name)
449 {
450         (*dt)->setView(name);
451 }
452
453 void OCIO_displayTransformSetDisplayCC(DisplayTransformRcPtr *dt, ConstTransformRcPtr *t)
454 {
455         (*dt)->setDisplayCC(*t);
456 }
457
458 void OCIO_displayTransformSetLinearCC(DisplayTransformRcPtr *dt, ConstTransformRcPtr *t)
459 {
460         (*dt)->setLinearCC(*t);
461 }
462
463 void OCIO_displayTransformRelease(DisplayTransformRcPtr *dt)
464 {
465         MEM_DELETE(dt, DisplayTransformRcPtr);
466 }
467
468 PackedImageDesc *OCIO_createPackedImageDesc(float *data, long width, long height, long numChannels,
469                                             long chanStrideBytes, long xStrideBytes, long yStrideBytes)
470 {
471         try {
472                 void *mem = MEM_mallocN(sizeof(PackedImageDesc), __func__);
473                 PackedImageDesc *id = new(mem) PackedImageDesc(data, width, height, numChannels, chanStrideBytes, xStrideBytes, yStrideBytes);
474
475                 return id;
476         }
477         catch (Exception &exception) {
478                 OCIO_reportException(exception);
479         }
480
481         return NULL;
482 }
483
484 void OCIO_packedImageDescRelease(PackedImageDesc* id)
485 {
486         MEM_DELETE(id, PackedImageDesc);
487 }
488
489 ExponentTransformRcPtr *OCIO_createExponentTransform(void)
490 {
491         ExponentTransformRcPtr *et = MEM_NEW(ExponentTransformRcPtr);
492
493         *et = ExponentTransform::Create();
494
495         return et;
496 }
497
498 void OCIO_exponentTransformSetValue(ExponentTransformRcPtr *et, const float *exponent)
499 {
500         (*et)->setValue(exponent);
501 }
502
503 void OCIO_exponentTransformRelease(ExponentTransformRcPtr *et)
504 {
505         MEM_DELETE(et, ExponentTransformRcPtr);
506 }
507
508 MatrixTransformRcPtr *OCIO_createMatrixTransform(void)
509 {
510         MatrixTransformRcPtr *mt = MEM_NEW(MatrixTransformRcPtr);
511
512         *mt = MatrixTransform::Create();
513
514         return mt;
515 }
516
517 void OCIO_matrixTransformSetValue(MatrixTransformRcPtr *mt, const float *m44, const float *offset4)
518 {
519         (*mt)->setValue(m44, offset4);
520 }
521
522 void OCIO_matrixTransformRelease(MatrixTransformRcPtr *mt)
523 {
524         MEM_DELETE(mt, MatrixTransformRcPtr);
525 }
526
527 void OCIO_matrixTransformScale(float * m44, float * offset4, const float *scale4f)
528 {
529         MatrixTransform::Scale(m44, offset4, scale4f);
530 }