1 /* ***** BEGIN LICENSE BLOCK *****
2 * This file is part of openfx-io <https://github.com/MrKepzie/openfx-io>,
3 * Copyright (C) 2013-2018 INRIA
4 *
5 * openfx-io is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * openfx-io is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with openfx-io. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17 * ***** END LICENSE BLOCK ***** */
18
19 /*
20 * OFX GenericOCIO plugin add-on.
21 * Adds OpenColorIO functionality to any plugin.
22 */
23
24 #include "GenericOCIO.h"
25
26 /*
27 http://opencolorio.org/userguide/config_syntax.html#roles
28
29 A description of all roles. Note that applications may interpret or use these differently.
30
31 color_picking - Colors in a color-selection UI can be displayed in this space, while selecting colors in a different working space (e.g scene_linear or texture_paint)
32 color_timing - colorspace used for applying color corrections, e.g user-specified grade within an image viewer (if the application uses the DisplayTransform::setDisplayCC API method)
33 compositing_log - a log colorspace used for certain processing operations (plate resizing, pulling keys, degrain, etc). Used by the OCIOLogConvert Nuke node.
34 data - used when writing data outputs such as normals, depth data, and other “non color” data. The colorspace in this role should typically have data: true specified, so no color transforms are applied.
35 default - when strictparsing: false, this colorspace is used as a fallback. If not defined, the scene_linear role is used
36 matte_paint - Colorspace which matte-paintings are created in (for more information, see the guide on baking ICC profiles for Photoshop, and spi-vfx)
37 reference - Colorspace used for reference imagery (e.g sRGB images from the internet)
38 scene_linear - The scene-referred linear-to-light colorspace, typically used as reference space (see Terminology)
39 texture_paint - Similar to matte_paint but for painting textures for 3D objects (see the description of texture painting in SPI’s pipeline)
40 */
41
42 #include <cstring>
43 #include <cstdlib>
44 #ifdef DEBUG
45 #include <cstdio>
46 #define DBG(x) x
47 #else
48 #define DBG(x) (void)0
49 #endif
50 #include <string>
51 #include <stdexcept>
52 #include <ofxsParam.h>
53 #include <ofxsImageEffect.h>
54 #include <ofxsLog.h>
55 #include <ofxNatron.h>
56 #include "ofxsMacros.h"
57
58 #ifdef OFX_IO_USING_OCIO
59 #include <OpenColorIO/OpenColorIO.h>
60 namespace OCIO = OCIO_NAMESPACE;
61 #endif
62
63 using std::string;
64
65 NAMESPACE_OFX_ENTER
66 NAMESPACE_OFX_IO_ENTER
67
68 #ifdef OFX_IO_USING_OCIO
69 static bool gWasOCIOEnvVarFound = false;
70 static bool gHostIsNatron = false;
71 #endif
72
73
74 // define to disable hiding parameters (useful for debugging)
75 //#define OFX_OCIO_NOSECRET
76
77 static string
trim(string const & str)78 trim(string const & str)
79 {
80 const string whitespace = " \t\f\v\n\r";
81
82 std::size_t first = str.find_first_not_of(whitespace);
83
84 // If there is no non-whitespace character, both first and last will be string::npos (-1)
85 // There is no point in checking both, since if either doesn't work, the
86 // other won't work, either.
87 if (first == string::npos) {
88 return "";
89 }
90
91 std::size_t last = str.find_last_not_of(whitespace);
92
93 return str.substr(first, last - first + 1);
94 }
95
96 static string
whitespacify(string str)97 whitespacify(string str)
98 {
99 std::replace( str.begin(), str.end(), '\t', ' ');
100 std::replace( str.begin(), str.end(), '\f', ' ');
101 std::replace( str.begin(), str.end(), '\v', ' ');
102 std::replace( str.begin(), str.end(), '\n', ' ');
103 std::replace( str.begin(), str.end(), '\r', ' ');
104
105 return str;
106 }
107
108 #ifdef OFX_IO_USING_OCIO
109 static const char*
colorSpaceName(OCIO::ConstConfigRcPtr config,const char * colorSpaceNameDefault)110 colorSpaceName(OCIO::ConstConfigRcPtr config,
111 const char* colorSpaceNameDefault)
112 {
113 OpenColorIO::ConstColorSpaceRcPtr cs;
114 if ( !strcmp(colorSpaceNameDefault, "sRGB") || !strcmp(colorSpaceNameDefault, "srgb") ) {
115 if ( ( cs = config->getColorSpace("sRGB") ) ) {
116 // nuke-default, blender, natron
117 return cs->getName();
118 } else if ( ( cs = config->getColorSpace("sRGB D65") ) ) {
119 // blender-cycles
120 return cs->getName();
121 } else if ( ( cs = config->getColorSpace("sRGB (D60 sim.)") ) ) {
122 // out_srgbd60sim or "sRGB (D60 sim.)" in aces 1.0.0
123 return cs->getName();
124 } else if ( ( cs = config->getColorSpace("out_srgbd60sim") ) ) {
125 // out_srgbd60sim or "sRGB (D60 sim.)" in aces 1.0.0
126 return cs->getName();
127 } else if ( ( cs = config->getColorSpace("rrt_Gamma2.2") ) ) {
128 // rrt_Gamma2.2 in aces 0.7.1
129 return cs->getName();
130 } else if ( ( cs = config->getColorSpace("rrt_srgb") ) ) {
131 // rrt_srgb in aces 0.1.1
132 return cs->getName();
133 } else if ( ( cs = config->getColorSpace("srgb8") ) ) {
134 // srgb8 in spi-vfx
135 return cs->getName();
136 } else if ( ( cs = config->getColorSpace("vd16") ) ) {
137 // vd16 in spi-anim
138 return cs->getName();
139 } else if ( ( cs = config->getColorSpace("VD16") ) ) {
140 // VD16 in blender
141 return cs->getName();
142 }
143 } else if ( !strcmp(colorSpaceNameDefault, "AdobeRGB") || !strcmp(colorSpaceNameDefault, "adobergb") ) {
144 if ( ( cs = config->getColorSpace("AdobeRGB") ) ) {
145 // natron
146 return cs->getName();
147 }
148 } else if ( !strcmp(colorSpaceNameDefault, "Rec709") || !strcmp(colorSpaceNameDefault, "rec709") ) {
149 if ( ( cs = config->getColorSpace("Rec709") ) ) {
150 // nuke-default
151 return cs->getName();
152 } else if ( ( cs = config->getColorSpace("nuke_rec709") ) ) {
153 // blender
154 return cs->getName();
155 } else if ( ( cs = config->getColorSpace("Rec.709 - Full") ) ) {
156 // out_rec709full or "Rec.709 - Full" in aces 1.0.0
157 return cs->getName();
158 } else if ( ( cs = config->getColorSpace("out_rec709full") ) ) {
159 // out_rec709full or "Rec.709 - Full" in aces 1.0.0
160 return cs->getName();
161 } else if ( ( cs = config->getColorSpace("rrt_rec709_full_100nits") ) ) {
162 // rrt_rec709_full_100nits in aces 0.7.1
163 return cs->getName();
164 } else if ( ( cs = config->getColorSpace("rrt_rec709") ) ) {
165 // rrt_rec709 in aces 0.1.1
166 return cs->getName();
167 } else if ( ( cs = config->getColorSpace("hd10") ) ) {
168 // hd10 in spi-anim and spi-vfx
169 return cs->getName();
170 }
171 } else if ( !strcmp(colorSpaceNameDefault, "KodakLog") || !strcmp(colorSpaceNameDefault, "kodaklog") ) {
172 if ( ( cs = config->getColorSpace("Cineon") ) ) {
173 // Cineon in nuke-default
174 return cs->getName();
175 } else if ( ( cs = config->getColorSpace("REDlogFilm") ) ) {
176 // REDlogFilm in aces 1.0.0
177 return cs->getName();
178 } else if ( ( cs = config->getColorSpace("cineon") ) ) {
179 // cineon in aces 0.7.1
180 return cs->getName();
181 } else if ( ( cs = config->getColorSpace("adx10") ) ) {
182 // adx10 in aces 0.1.1
183 return cs->getName();
184 } else if ( ( cs = config->getColorSpace("lg10") ) ) {
185 // lg10 in spi-vfx
186 return cs->getName();
187 } else if ( ( cs = config->getColorSpace("lm10") ) ) {
188 // lm10 in spi-anim
189 return cs->getName();
190 } else {
191 return OCIO::ROLE_COMPOSITING_LOG; // reasonable default
192 }
193 } else if ( !strcmp(colorSpaceNameDefault, "Linear") || !strcmp(colorSpaceNameDefault, "linear") ) {
194 return OCIO::ROLE_SCENE_LINEAR;
195 // lnf in spi-vfx
196 } else if ( ( cs = config->getColorSpace(colorSpaceNameDefault) ) ) {
197 // maybe we're lucky
198 return cs->getName();
199 }
200
201 // unlucky
202 return colorSpaceNameDefault;
203 } // colorSpaceName
204
205 static string
canonicalizeColorSpace(OCIO::ConstConfigRcPtr config,const string & csname)206 canonicalizeColorSpace(OCIO::ConstConfigRcPtr config,
207 const string &csname)
208 {
209 if (!config) {
210 return csname;
211 }
212 const int defaultcs = config->getIndexForColorSpace(OCIO::ROLE_DEFAULT);
213 const int referencecs = config->getIndexForColorSpace(OCIO::ROLE_REFERENCE);
214 const int datacs = config->getIndexForColorSpace(OCIO::ROLE_DATA);
215 const int colorpickingcs = config->getIndexForColorSpace(OCIO::ROLE_COLOR_PICKING);
216 const int scenelinearcs = config->getIndexForColorSpace(OCIO::ROLE_SCENE_LINEAR);
217 const int compositinglogcs = config->getIndexForColorSpace(OCIO::ROLE_COMPOSITING_LOG);
218 const int colortimingcs = config->getIndexForColorSpace(OCIO::ROLE_COLOR_TIMING);
219 const int texturepaintcs = config->getIndexForColorSpace(OCIO::ROLE_TEXTURE_PAINT);
220 const int mattepaintcs = config->getIndexForColorSpace(OCIO::ROLE_MATTE_PAINT);
221 int inputSpaceIndex = config->getIndexForColorSpace( csname.c_str() );
222 if (inputSpaceIndex == scenelinearcs) {
223 return OCIO::ROLE_SCENE_LINEAR;
224 } else if (inputSpaceIndex == defaultcs) {
225 return OCIO::ROLE_DEFAULT;
226 } else if (inputSpaceIndex == referencecs) {
227 return OCIO::ROLE_REFERENCE;
228 } else if (inputSpaceIndex == datacs) {
229 return OCIO::ROLE_DATA;
230 } else if (inputSpaceIndex == colorpickingcs) {
231 return OCIO::ROLE_COLOR_PICKING;
232 } else if (inputSpaceIndex == compositinglogcs) {
233 return OCIO::ROLE_COMPOSITING_LOG;
234 } else if (inputSpaceIndex == colortimingcs) {
235 return OCIO::ROLE_COLOR_TIMING;
236 } else if (inputSpaceIndex == texturepaintcs) {
237 return OCIO::ROLE_TEXTURE_PAINT;
238 } else if (inputSpaceIndex == mattepaintcs) {
239 return OCIO::ROLE_MATTE_PAINT;
240 }
241
242 return csname;
243 }
244
245 #endif // ifdef OFX_IO_USING_OCIO
246
GenericOCIO(ImageEffect * parent)247 GenericOCIO::GenericOCIO(ImageEffect* parent)
248 : _parent(parent)
249 , _created(false)
250 #ifdef OFX_IO_USING_OCIO
251 , _ocioConfigFileName()
252 , _ocioConfigFile(NULL)
253 , _inputSpace(NULL)
254 , _outputSpace(NULL)
255 #ifdef OFX_OCIO_CHOICE
256 , _choiceIsOk(true)
257 , _choiceFileName()
258 , _inputSpaceChoice(NULL)
259 , _outputSpaceChoice(NULL)
260 #endif
261 , _contextKey1(NULL)
262 , _contextValue1(NULL)
263 , _contextKey2(NULL)
264 , _contextValue2(NULL)
265 , _contextKey3(NULL)
266 , _contextValue3(NULL)
267 , _contextKey4(NULL)
268 , _contextValue4(NULL)
269 , _config()
270 #endif
271 {
272 #ifdef OFX_IO_USING_OCIO
273 _ocioConfigFile = _parent->fetchStringParam(kOCIOParamConfigFile);
274 if ( _parent->paramExists(kOCIOParamInputSpace) ) {
275 _inputSpace = _parent->fetchStringParam(kOCIOParamInputSpace);
276 }
277 if ( _parent->paramExists(kOCIOParamOutputSpace) ) {
278 _outputSpace = _parent->fetchStringParam(kOCIOParamOutputSpace);
279 }
280 #ifdef OFX_OCIO_CHOICE
281 _ocioConfigFile->getDefault(_choiceFileName);
282 if (_inputSpace) {
283 _inputSpaceChoice = _parent->fetchChoiceParam(kOCIOParamInputSpaceChoice);
284 }
285 if (_outputSpace) {
286 _outputSpaceChoice = _parent->fetchChoiceParam(kOCIOParamOutputSpaceChoice);
287 }
288 #endif
289 loadConfig();
290 #ifdef OFX_OCIO_CHOICE
291 if (!_config) {
292 # ifndef OFX_OCIO_NOSECRET
293 if (_inputSpace) {
294 _inputSpaceChoice->setIsSecretAndDisabled(true);
295 }
296 if (_outputSpace) {
297 _outputSpaceChoice->setIsSecretAndDisabled(true);
298 }
299 # endif
300 }
301 #endif
302 if ( _parent->paramExists(kOCIOParamContextKey1) ) {
303 _contextKey1 = _parent->fetchStringParam(kOCIOParamContextKey1);
304 _contextValue1 = _parent->fetchStringParam(kOCIOParamContextValue1);
305 _contextKey2 = _parent->fetchStringParam(kOCIOParamContextKey2);
306 _contextValue2 = _parent->fetchStringParam(kOCIOParamContextValue2);
307 _contextKey3 = _parent->fetchStringParam(kOCIOParamContextKey3);
308 _contextValue3 = _parent->fetchStringParam(kOCIOParamContextValue3);
309 _contextKey4 = _parent->fetchStringParam(kOCIOParamContextKey4);
310 _contextValue4 = _parent->fetchStringParam(kOCIOParamContextValue4);
311 assert(_contextKey1 && _contextKey2 && _contextKey3 && _contextKey4);
312 assert(_contextValue1 && _contextValue2 && _contextValue3 && _contextValue4);
313 }
314 #endif
315 // setup the GUI
316 // setValue() may be called from createInstance, according to
317 // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#SettingParams
318 inputCheck(0.);
319 outputCheck(0.);
320 _created = true;
321 }
322
323 #ifdef OFX_IO_USING_OCIO
324 #ifdef OFX_OCIO_CHOICE
325
326 // ChoiceParamType may be ChoiceParamDescriptor or ChoiceParam
327 template <typename ChoiceParamType>
328 static void
buildChoiceMenu(OCIO::ConstConfigRcPtr config,ChoiceParamType * choice,bool cascading,const string & name="")329 buildChoiceMenu(OCIO::ConstConfigRcPtr config,
330 ChoiceParamType* choice,
331 bool cascading,
332 const string& name = "")
333 {
334 //DBG(std::printf("%p->resetOptions\n", (void*)choice));
335 choice->resetOptions();
336 assert(choice->getNOptions() == 0);
337 if (!config) {
338 return;
339 }
340 int def = -1;
341 int defaultcs = config->getIndexForColorSpace(OCIO::ROLE_DEFAULT);
342 int referencecs = config->getIndexForColorSpace(OCIO::ROLE_REFERENCE);
343 int datacs = config->getIndexForColorSpace(OCIO::ROLE_DATA);
344 int colorpickingcs = config->getIndexForColorSpace(OCIO::ROLE_COLOR_PICKING);
345 int scenelinearcs = config->getIndexForColorSpace(OCIO::ROLE_SCENE_LINEAR);
346 int compositinglogcs = config->getIndexForColorSpace(OCIO::ROLE_COMPOSITING_LOG);
347 int colortimingcs = config->getIndexForColorSpace(OCIO::ROLE_COLOR_TIMING);
348 int texturepaintcs = config->getIndexForColorSpace(OCIO::ROLE_TEXTURE_PAINT);
349 int mattepaintcs = config->getIndexForColorSpace(OCIO::ROLE_MATTE_PAINT);
350 for (int i = 0; i < config->getNumColorSpaces(); ++i) {
351 string csname = config->getColorSpaceNameByIndex(i);
352 string msg;
353 // set the default value, in case the GUI uses it
354 if ( !name.empty() && (csname == name) ) {
355 def = i;
356 }
357 OCIO::ConstColorSpaceRcPtr cs = config->getColorSpace( csname.c_str() );
358 if (cascading) {
359 string family = config->getColorSpace( csname.c_str() )->getFamily();
360 if ( !family.empty() ) {
361 csname = family + "/" + csname;
362 }
363 }
364 string csdesc = cs ? cs->getDescription() : "(no colorspace)";
365 csdesc = whitespacify( trim(csdesc) );
366 int csdesclen = csdesc.size();
367 if (csdesclen > 0) {
368 msg += csdesc;
369 }
370 bool first = true;
371 int roles = 0;
372 if (i == defaultcs) {
373 msg += first ? " (" : ", ";
374 msg += OCIO::ROLE_DEFAULT;
375 first = false;
376 ++roles;
377 }
378 if (i == referencecs) {
379 msg += first ? " (" : ", ";
380 msg += OCIO::ROLE_REFERENCE;
381 first = false;
382 ++roles;
383 }
384 if (i == datacs) {
385 msg += first ? " (" : ", ";
386 msg += OCIO::ROLE_DATA;
387 first = false;
388 ++roles;
389 }
390 if (i == colorpickingcs) {
391 msg += first ? " (" : ", ";
392 msg += OCIO::ROLE_COLOR_PICKING;
393 first = false;
394 ++roles;
395 }
396 if (i == scenelinearcs) {
397 msg += first ? " (" : ", ";
398 msg += OCIO::ROLE_SCENE_LINEAR;
399 first = false;
400 ++roles;
401 }
402 if (i == compositinglogcs) {
403 msg += first ? " (" : ", ";
404 msg += OCIO::ROLE_COMPOSITING_LOG;
405 first = false;
406 ++roles;
407 }
408 if (i == colortimingcs) {
409 msg += first ? " (" : ", ";
410 msg += OCIO::ROLE_COLOR_TIMING;
411 first = false;
412 ++roles;
413 }
414 if (i == texturepaintcs) {
415 msg += first ? " (" : ", ";
416 msg += OCIO::ROLE_TEXTURE_PAINT;
417 first = false;
418 ++roles;
419 }
420 if (i == mattepaintcs) {
421 msg += first ? " (" : ", ";
422 msg += OCIO::ROLE_MATTE_PAINT;
423 first = false;
424 ++roles;
425 }
426 if (roles > 0) {
427 msg += ')';
428 }
429 //DBG(printf("%p->appendOption(\"%s\",\"%s\") (%d->%d options)\n", (void*)choice, csname.c_str(), msg.c_str(), i, i+1));
430 assert(choice->getNOptions() == i);
431 choice->appendOption(csname, msg);
432 assert(choice->getNOptions() == i + 1);
433 }
434 if (def != -1) {
435 choice->setDefault(def);
436 }
437 } // buildChoiceMenu
438
439 #endif // ifdef OFX_OCIO_CHOICE
440 #endif // ifdef OFX_IO_USING_OCIO
441
442 void
loadConfig()443 GenericOCIO::loadConfig()
444 {
445 #ifdef OFX_IO_USING_OCIO
446 string filename;
447 _ocioConfigFile->getValue(filename);
448
449 if (filename == _ocioConfigFileName) {
450 return;
451 }
452 _config.reset();
453 try {
454 _ocioConfigFileName = filename;
455 _config = OCIO::Config::CreateFromFile( _ocioConfigFileName.c_str() );
456 } catch (OCIO::Exception &e) {
457 _ocioConfigFileName.clear();
458 if (_inputSpace) {
459 _inputSpace->setEnabled(false);
460 # ifdef OFX_OCIO_CHOICE
461 _inputSpaceChoice->setEnabled(false);
462 # endif
463 }
464 if (_outputSpace) {
465 _outputSpace->setEnabled(false);
466 # ifdef OFX_OCIO_CHOICE
467 _outputSpaceChoice->setEnabled(false);
468 # endif
469 }
470 }
471 #ifdef OFX_OCIO_CHOICE
472 if (_config) {
473 if (gHostIsNatron) {
474 // the choice menu can only be modified in Natron
475 // Natron supports changing the entries in a choiceparam
476 // Nuke (at least up to 8.0v3) does not
477 if (_inputSpace) {
478 buildChoiceMenu( _config, _inputSpaceChoice, _inputSpaceChoice->getIsCascading() );
479 }
480 if (_outputSpace) {
481 buildChoiceMenu( _config, _outputSpaceChoice, _outputSpaceChoice->getIsCascading() );
482 }
483 _choiceFileName = _ocioConfigFileName;
484 }
485 _choiceIsOk = (_ocioConfigFileName == _choiceFileName);
486 // do not set values during CreateInstance!!
487 ////inputCheck(); // may set values
488 ////outputCheck(); // may set values
489 }
490 #endif
491 #endif
492 } // GenericOCIO::loadConfig
493
494 bool
configIsDefault() const495 GenericOCIO::configIsDefault() const
496 {
497 #ifdef OFX_IO_USING_OCIO
498 string filename;
499 _ocioConfigFile->getValue(filename);
500 string defaultFilename;
501 _ocioConfigFile->getDefault(defaultFilename);
502
503 return (filename == defaultFilename);
504 #else
505
506 return true;
507 #endif
508 }
509
510 #ifdef OFX_IO_USING_OCIO
511 OCIO::ConstContextRcPtr
getLocalContext(double time) const512 GenericOCIO::getLocalContext(double time) const
513 {
514 OCIO::ConstContextRcPtr context = _config->getCurrentContext();
515 OCIO::ContextRcPtr mutableContext;
516
517 if (_contextKey1) {
518 string contextKey1;
519 _contextKey1->getValueAtTime(time, contextKey1);
520 if ( !contextKey1.empty() ) {
521 string contextValue1;
522 _contextValue1->getValueAtTime(time, contextValue1);
523
524 if (!mutableContext) {
525 mutableContext = context->createEditableCopy();
526 }
527 mutableContext->setStringVar( contextKey1.c_str(), contextValue1.c_str() );
528 }
529 }
530 if (_contextKey2) {
531 string contextKey2;
532 _contextKey2->getValueAtTime(time, contextKey2);
533 if ( !contextKey2.empty() ) {
534 string contextValue2;
535 _contextValue2->getValueAtTime(time, contextValue2);
536
537 if (!mutableContext) {
538 mutableContext = context->createEditableCopy();
539 }
540 mutableContext->setStringVar( contextKey2.c_str(), contextValue2.c_str() );
541 }
542 }
543 if (_contextKey3) {
544 string contextKey3;
545 _contextKey3->getValueAtTime(time, contextKey3);
546 if ( !contextKey3.empty() ) {
547 string contextValue3;
548 _contextValue3->getValueAtTime(time, contextValue3);
549
550 if (!mutableContext) {
551 mutableContext = context->createEditableCopy();
552 }
553 mutableContext->setStringVar( contextKey3.c_str(), contextValue3.c_str() );
554 }
555 }
556 if (_contextKey4) {
557 string contextKey4;
558 _contextKey4->getValueAtTime(time, contextKey4);
559 if ( !contextKey4.empty() ) {
560 string contextValue4;
561 _contextValue4->getValueAtTime(time, contextValue4);
562
563 if (!mutableContext) {
564 mutableContext = context->createEditableCopy();
565 }
566 mutableContext->setStringVar( contextKey4.c_str(), contextValue4.c_str() );
567 }
568 }
569
570 if (mutableContext) {
571 context = mutableContext;
572 }
573
574 return context;
575 } // GenericOCIO::getLocalContext
576
577 #endif // ifdef OFX_IO_USING_OCIO
578
579 bool
isIdentity(double time) const580 GenericOCIO::isIdentity(double time) const
581 {
582 assert(_created);
583 #ifdef OFX_IO_USING_OCIO
584 if (!_config) {
585 string filename;
586 _ocioConfigFile->getValue(filename);
587 _parent->setPersistentMessage( Message::eMessageError, "", "Invalid OCIO config. file \"" + filename + "\"" );
588 throwSuiteStatusException(kOfxStatFailed);
589
590 return false;
591 }
592 string inputSpace;
593 getInputColorspaceAtTime(time, inputSpace);
594 string outputSpace;
595 getOutputColorspaceAtTime(time, outputSpace);
596 if (inputSpace == outputSpace) {
597 return true;
598 }
599 try {
600 // maybe the names are not the same, but it's still a no-op (e.g. "scene_linear" and "linear")
601 OCIO::ConstContextRcPtr context = getLocalContext(time);//_config->getCurrentContext();
602 OCIO::ConstProcessorRcPtr proc = _config->getProcessor( context, inputSpace.c_str(), outputSpace.c_str() );
603
604 return proc->isNoOp();
605 } catch (const std::exception& e) {
606 _parent->setPersistentMessage( Message::eMessageError, "", e.what() );
607 throwSuiteStatusException(kOfxStatFailed);
608
609 return false;
610 }
611 #else
612
613 return true;
614 #endif
615 }
616
617 // sets the correct choice menu item from the inputSpace string value
618 void
inputCheck(double time)619 GenericOCIO::inputCheck(double time)
620 {
621 #ifdef OFX_IO_USING_OCIO
622 #ifdef OFX_OCIO_CHOICE
623 if (!_config || !_inputSpace) {
624 return;
625 }
626 if (!_choiceIsOk) {
627 // choice menu is dirty, only use the text entry
628 #ifdef OFX_OCIO_NOSECRET
629 _inputSpace->setEnabled(true);
630 _inputSpaceChoice->setEnabled(false);
631 #else
632 _inputSpace->setIsSecretAndDisabled(false);
633 _inputSpaceChoice->setIsSecretAndDisabled(true);
634 #endif
635
636 return;
637 }
638 string inputSpaceName;
639 getInputColorspaceAtTime(time, inputSpaceName);
640 int inputSpaceIndex = _config->getIndexForColorSpace( inputSpaceName.c_str() );
641 if (inputSpaceIndex >= 0) {
642 int inputSpaceIndexOld;
643 _inputSpaceChoice->getValueAtTime(time, inputSpaceIndexOld);
644 // avoid an infinite loop on bad hosts (for examples those which don't set args.reason correctly)
645 if (inputSpaceIndexOld != inputSpaceIndex) {
646 _inputSpaceChoice->setValue(inputSpaceIndex);
647 }
648 #ifdef OFX_OCIO_NOSECRET
649 _inputSpace->setEnabled(false);
650 _inputSpaceChoice->setEnabled(true);
651 #else
652 _inputSpace->setIsSecretAndDisabled(true);
653 _inputSpaceChoice->setIsSecretAndDisabled(false);
654 #endif
655 } else {
656 // the input space name is not valid
657 #ifdef OFX_OCIO_NOSECRET
658 _inputSpace->setEnabled(true);
659 _inputSpaceChoice->setEnabled(false);
660 #else
661 _inputSpace->setIsSecretAndDisabled(false);
662 _inputSpaceChoice->setIsSecretAndDisabled(true);
663 #endif
664 }
665 #endif
666 #endif
667 }
668
669 // sets the correct choice menu item from the outputSpace string value
670 void
outputCheck(double time)671 GenericOCIO::outputCheck(double time)
672 {
673 #ifdef OFX_IO_USING_OCIO
674 #ifdef OFX_OCIO_CHOICE
675 if (!_config || !_outputSpace) {
676 return;
677 }
678 if (!_choiceIsOk) {
679 // choice menu is dirty, only use the text entry
680 #ifdef OFX_OCIO_NOSECRET
681 _outputSpace->setEnabled(true);
682 _outputSpaceChoice->setEnabled(false);
683 #else
684 _outputSpace->setIsSecretAndDisabled(false);
685 _outputSpaceChoice->setIsSecretAndDisabled(true);
686 #endif
687
688 return;
689 }
690 string outputSpaceName;
691 getOutputColorspaceAtTime(time, outputSpaceName);
692 int outputSpaceIndex = _config->getIndexForColorSpace( outputSpaceName.c_str() );
693 if (outputSpaceIndex >= 0) {
694 int outputSpaceIndexOld;
695 _outputSpaceChoice->getValueAtTime(time, outputSpaceIndexOld);
696 // avoid an infinite loop on bad hosts (for examples those which don't set args.reason correctly)
697 if (outputSpaceIndexOld != outputSpaceIndex) {
698 _outputSpaceChoice->setValue(outputSpaceIndex);
699 }
700 #ifdef OFX_OCIO_NOSECRET
701 _outputSpace->setEnabled(false);
702 _outputSpaceChoice->setEnabled(true);
703 #else
704 _outputSpace->setIsSecretAndDisabled(true);
705 _outputSpaceChoice->setIsSecretAndDisabled(false);
706 #endif
707 } else {
708 // the output space name is not valid
709 #ifdef OFX_OCIO_NOSECRET
710 _outputSpace->setEnabled(true);
711 _outputSpaceChoice->setEnabled(false);
712 #else
713 _outputSpace->setIsSecretAndDisabled(false);
714 _outputSpaceChoice->setIsSecretAndDisabled(true);
715 #endif
716 }
717 #endif
718 #endif
719 }
720
721 void
apply(double time,const OfxRectI & renderWindow,Image * img)722 GenericOCIO::apply(double time,
723 const OfxRectI& renderWindow,
724 Image* img)
725 {
726 assert(_created);
727 #ifdef OFX_IO_USING_OCIO
728 BitDepthEnum bitDepth = img->getPixelDepth();
729 if (bitDepth != eBitDepthFloat) {
730 throw std::runtime_error("OCIO: invalid pixel depth (only float is supported)");
731 }
732
733 apply( time, renderWindow, (float*)img->getPixelData(), img->getBounds(), img->getPixelComponents(), img->getPixelComponentCount(), img->getRowBytes() );
734 #endif
735 }
736
737 #ifdef OFX_IO_USING_OCIO
738 OCIO::ConstProcessorRcPtr
getProcessor() const739 GenericOCIO::getProcessor() const
740 {
741 AutoMutex guard(_procMutex);
742
743 return _proc;
744 };
745 void
setValues(const string & inputSpace,const string & outputSpace)746 GenericOCIO::setValues(const string& inputSpace,
747 const string& outputSpace)
748 {
749 return setValues( _config->getCurrentContext(), inputSpace.c_str(), outputSpace.c_str() );
750 }
751
752 void
setValues(const OCIO::ConstContextRcPtr & context,const string & inputSpace,const string & outputSpace)753 GenericOCIO::setValues(const OCIO::ConstContextRcPtr &context,
754 const string& inputSpace,
755 const string& outputSpace)
756 {
757 AutoMutex guard(_procMutex);
758
759 if ( !_proc ||
760 ( context != _procContext) ||
761 ( inputSpace != _procInputSpace) ||
762 ( outputSpace != _procOutputSpace) ) {
763 _procContext = context;
764 _procInputSpace = inputSpace;
765 _procOutputSpace = outputSpace;
766 _proc = _config->getProcessor( context, inputSpace.c_str(), outputSpace.c_str() );
767 }
768 }
769
770 void
multiThreadProcessImages(OfxRectI renderWindow)771 OCIOProcessor::multiThreadProcessImages(OfxRectI renderWindow)
772 {
773 assert(_dstBounds.x1 <= renderWindow.x1 && renderWindow.x1 <= renderWindow.x2 && renderWindow.x2 <= _dstBounds.x2);
774 assert(_dstBounds.y1 <= renderWindow.y1 && renderWindow.y1 <= renderWindow.y2 && renderWindow.y2 <= _dstBounds.y2);
775 // Ensure there are pixels to render otherwise OCIO::PackedImageDesc will throw an exception.
776 if ( (renderWindow.y2 <= renderWindow.y1) || (renderWindow.x2 <= renderWindow.x1) ) {
777 return;
778 }
779 #ifdef OFX_IO_USING_OCIO
780 if (!_proc) {
781 throw std::logic_error("OCIO configuration not loaded");
782 }
783 int numChannels;
784 int pixelBytes;
785 switch (_dstPixelComponents) {
786 case ePixelComponentRGBA:
787 numChannels = 4;
788 break;
789 case ePixelComponentRGB:
790 numChannels = 3;
791 break;
792 //case ePixelComponentAlpha: pixelBytes = 1; break;
793 default:
794 throwSuiteStatusException(kOfxStatErrFormat);
795
796 return;
797 }
798
799 pixelBytes = numChannels * sizeof(float);
800 size_t pixelDataOffset = (size_t)(renderWindow.y1 - _dstBounds.y1) * _dstRowBytes + (size_t)(renderWindow.x1 - _dstBounds.x1) * pixelBytes;
801 float *pix = (float *) ( ( (char *) _dstPixelData ) + pixelDataOffset ); // (char*)dstImg->getPixelAddress(renderWindow.x1, renderWindow.y1);
802 try {
803 if (_proc) {
804 OCIO::PackedImageDesc img(pix, renderWindow.x2 - renderWindow.x1, renderWindow.y2 - renderWindow.y1, numChannels, sizeof(float), pixelBytes, _dstRowBytes);
805 _proc->apply(img);
806 }
807 } catch (OCIO::Exception &e) {
808 _instance->setPersistentMessage( Message::eMessageError, "", string("OpenColorIO error: ") + e.what() );
809 throw std::runtime_error( string("OpenColorIO error: ") + e.what() );
810 }
811 #endif
812 }
813
814 #endif // OFX_IO_USING_OCIO
815
816 #ifdef OFX_IO_USING_OCIO
817 OCIO::ConstProcessorRcPtr
getOrCreateProcessor(double time)818 GenericOCIO::getOrCreateProcessor(double time)
819 {
820 if (!_config) {
821 return OCIO::ConstProcessorRcPtr();
822 }
823 string inputSpace;
824 getInputColorspaceAtTime(time, inputSpace);
825 string outputSpace;
826 getOutputColorspaceAtTime(time, outputSpace);
827 OCIO::ConstContextRcPtr context = getLocalContext(time);//_config->getCurrentContext();
828 setValues(context, inputSpace, outputSpace);
829
830 return getProcessor();
831 }
832
833 #endif // OFX_IO_USING_OCIO
834
835 void
apply(double time,const OfxRectI & renderWindow,float * pixelData,const OfxRectI & bounds,PixelComponentEnum pixelComponents,int pixelComponentCount,int rowBytes)836 GenericOCIO::apply(double time,
837 const OfxRectI& renderWindow,
838 float *pixelData,
839 const OfxRectI& bounds,
840 PixelComponentEnum pixelComponents,
841 int pixelComponentCount,
842 int rowBytes)
843 {
844 assert(_created);
845 #ifdef OFX_IO_USING_OCIO
846
847 if (!_created) {
848 return;
849 }
850
851 if ( isIdentity(time) ) {
852 return;
853 }
854 // are we in the image bounds
855 if ( (renderWindow.x1 < bounds.x1) || (renderWindow.x1 >= bounds.x2) || (renderWindow.y1 < bounds.y1) || (renderWindow.y1 >= bounds.y2) ||
856 ( renderWindow.x2 <= bounds.x1) || ( renderWindow.x2 > bounds.x2) || ( renderWindow.y2 <= bounds.y1) || ( renderWindow.y2 > bounds.y2) ) {
857 _parent->setPersistentMessage(Message::eMessageError, "", "OCIO: render window outside of image bounds");
858 throwSuiteStatusException(kOfxStatFailed);
859 }
860 if ( (pixelComponents != ePixelComponentRGBA) && (pixelComponents != ePixelComponentRGB) ) {
861 _parent->setPersistentMessage(Message::eMessageError, "", "OCIO: invalid components (only RGB and RGBA are supported)");
862 throwSuiteStatusException(kOfxStatFailed);
863 }
864
865 OCIO::ConstProcessorRcPtr proc = getOrCreateProcessor(time);
866 if (!proc) {
867 _parent->setPersistentMessage( Message::eMessageError, "", "Cannot create OCIO processor" );
868 throwSuiteStatusException(kOfxStatFailed);
869
870 return;
871 }
872
873 OCIOProcessor processor(*_parent);
874 // set the images
875 processor.setDstImg(pixelData, bounds, pixelComponents, pixelComponentCount, eBitDepthFloat, rowBytes);
876
877 processor.setProcessor(proc);
878
879 // set the render window
880 processor.setRenderWindow(renderWindow);
881
882 // Call the base class process member, this will call the derived templated process code
883 processor.process();
884 #endif
885 }
886
887 void
changedParam(const InstanceChangedArgs & args,const string & paramName)888 GenericOCIO::changedParam(const InstanceChangedArgs &args,
889 const string ¶mName)
890 {
891 assert(_created);
892 #ifdef OFX_IO_USING_OCIO
893 if ( (paramName == kOCIOParamConfigFile) && (args.reason != eChangeTime) ) {
894 // must clear persistent message, or render() is not called by Nuke after an error
895 _parent->clearPersistentMessage();
896 // compute canonical inputSpace and outputSpace before changing the config,
897 // if different from inputSpace and outputSpace they must be set to the canonical value after changing ocio config
898 string inputSpace;
899 getInputColorspaceAtTime(args.time, inputSpace);
900 string inputSpaceCanonical = canonicalizeColorSpace(_config, inputSpace);
901 if (inputSpaceCanonical != inputSpace) {
902 _inputSpace->setValue(inputSpaceCanonical);
903 }
904 if (_outputSpace) {
905 string outputSpace;
906 getOutputColorspaceAtTime(args.time, outputSpace);
907 string outputSpaceCanonical = canonicalizeColorSpace(_config, outputSpace);
908 if (outputSpaceCanonical != outputSpace) {
909 _outputSpace->setValue(outputSpaceCanonical);
910 }
911 }
912
913 loadConfig(); // re-load the new OCIO config
914 //if inputspace or outputspace are not valid in the new config, reset them to "default"
915 if (_config) {
916 string inputSpaceName;
917 getInputColorspaceAtTime(args.time, inputSpaceName);
918 int inputSpaceIndex = _config->getIndexForColorSpace( inputSpaceName.c_str() );
919 if (inputSpaceIndex < 0) {
920 OCIO::ConstColorSpaceRcPtr cs;
921 if (!cs) {
922 cs = _config->getColorSpace(OCIO::ROLE_DEFAULT);
923 }
924 if (!cs) {
925 // no default colorspace, fallback to the first one
926 cs = _config->getColorSpace( _config->getColorSpaceNameByIndex(0) );
927 }
928 inputSpaceName = cs ? cs->getName() : OCIO::ROLE_DEFAULT;
929 _inputSpace->setValue(inputSpaceName);
930 }
931 }
932 inputCheck(args.time);
933 if (_config && _outputSpace) {
934 string outputSpaceName;
935 getOutputColorspaceAtTime(args.time, outputSpaceName);
936 int outputSpaceIndex = _config->getIndexForColorSpace( outputSpaceName.c_str() );
937 if (outputSpaceIndex < 0) {
938 OCIO::ConstColorSpaceRcPtr cs;
939 if (!cs) {
940 cs = _config->getColorSpace(OCIO::ROLE_DEFAULT);
941 }
942 if (!cs) {
943 // no default colorspace, fallback to the first one
944 cs = _config->getColorSpace( _config->getColorSpaceNameByIndex(0) );
945 }
946 outputSpaceName = cs ? cs->getName() : OCIO::ROLE_DEFAULT;
947 _outputSpace->setValue(OCIO::ROLE_DEFAULT);
948 }
949 }
950 outputCheck(args.time);
951
952 if ( !_config && (args.reason == eChangeUserEdit) ) {
953 string filename;
954 _ocioConfigFile->getValue(filename);
955 _parent->sendMessage(Message::eMessageError, "", string("Cannot load OCIO config file \"") + filename + '"');
956 }
957 } else if ( (paramName == kOCIOHelpButton) || (paramName == kOCIOHelpLooksButton) || (paramName == kOCIOHelpDisplaysButton) ) {
958 string msg = "OpenColorIO Help\n"
959 "The OCIO configuration file can be set using the \"OCIO\" environment variable, which should contain the full path to the .ocio file.\n"
960 "OpenColorIO version (compiled with / running with): " OCIO_VERSION "/";
961 msg += OCIO::GetVersion();
962 msg += '\n';
963 if (_config) {
964 string configdesc = _config->getDescription();
965 configdesc = whitespacify( trim(configdesc) );
966 if (configdesc.size() > 0) {
967 msg += "\nThis OCIO configuration is ";
968 msg += configdesc;
969 msg += '\n';
970 }
971 msg += '\n';
972 if (paramName == kOCIOHelpLooksButton) {
973 msg += (_config->getNumLooks() <= 0 ? "No look available in this OCIO configuration.\n" : "Available looks in this OCIO Configuration (applied in the given colorspace):\n");
974 for (int i = 0; i < _config->getNumLooks(); ++i) {
975 const char* lkname = _config->getLookNameByIndex(i);
976 OCIO::ConstLookRcPtr lk = _config->getLook(lkname);
977 msg += "- ";
978 msg += lkname;
979 string lkspace = lk->getProcessSpace();
980 msg += " (" + lkspace + ")\n";
981 }
982 msg += '\n';
983 }
984 if (paramName == kOCIOHelpDisplaysButton) {
985 if (_config->getNumDisplays() <= 0) {
986 msg += "No display available in this OCIO configuration.\n";
987 } else {
988 msg += "Available displays and views in this OCIO Configuration:\n";
989 string defaultdisplay = _config->getDefaultDisplay();
990 for (int i = 0; i < _config->getNumDisplays(); ++i) {
991 const char* display = _config->getDisplay(i);
992 msg += "- ";
993 msg += display;
994 if (display == defaultdisplay) {
995 msg += " (default)";
996 }
997 int numViews = _config->getNumViews(display);
998 if (numViews <= 0) {
999 msg += ", no view available.\n";
1000 } else {
1001 msg += ", views: ";
1002 string defaultview = _config->getDefaultView(display);
1003 for (int j = 0; j < numViews; ++j) {
1004 const char* view = _config->getView(display, j);
1005 msg += view;
1006 if (view == defaultview) {
1007 msg += " (default)";
1008 }
1009 if (j < numViews - 1) {
1010 msg += ", ";
1011 }
1012 }
1013 msg += '\n';
1014 }
1015 }
1016 }
1017 msg += '\n';
1018 }
1019 msg += "Available colorspaces in this OCIO Configuration:\n";
1020 int defaultcs = _config->getIndexForColorSpace(OCIO::ROLE_DEFAULT);
1021 int referencecs = _config->getIndexForColorSpace(OCIO::ROLE_REFERENCE);
1022 int datacs = _config->getIndexForColorSpace(OCIO::ROLE_DATA);
1023 int colorpickingcs = _config->getIndexForColorSpace(OCIO::ROLE_COLOR_PICKING);
1024 int scenelinearcs = _config->getIndexForColorSpace(OCIO::ROLE_SCENE_LINEAR);
1025 int compositinglogcs = _config->getIndexForColorSpace(OCIO::ROLE_COMPOSITING_LOG);
1026 int colortimingcs = _config->getIndexForColorSpace(OCIO::ROLE_COLOR_TIMING);
1027 int texturepaintcs = _config->getIndexForColorSpace(OCIO::ROLE_TEXTURE_PAINT);
1028 int mattepaintcs = _config->getIndexForColorSpace(OCIO::ROLE_MATTE_PAINT);
1029
1030 for (int i = 0; i < _config->getNumColorSpaces(); ++i) {
1031 const char* csname = _config->getColorSpaceNameByIndex(i);;
1032 OCIO::ConstColorSpaceRcPtr cs = _config->getColorSpace(csname);
1033 msg += "- ";
1034 msg += csname;
1035 bool first = true;
1036 //int roles = 0;
1037 if (i == defaultcs) {
1038 msg += first ? " (" : ", ";
1039 msg += OCIO::ROLE_DEFAULT;
1040 first = false;
1041 //++roles;
1042 }
1043 if (i == referencecs) {
1044 msg += first ? " (" : ", ";
1045 msg += OCIO::ROLE_REFERENCE;
1046 first = false;
1047 //++roles;
1048 }
1049 if (i == datacs) {
1050 msg += first ? " (" : ", ";
1051 msg += OCIO::ROLE_DATA;
1052 first = false;
1053 //++roles;
1054 }
1055 if (i == colorpickingcs) {
1056 msg += first ? " (" : ", ";
1057 msg += OCIO::ROLE_COLOR_PICKING;
1058 first = false;
1059 //++roles;
1060 }
1061 if (i == scenelinearcs) {
1062 msg += first ? " (" : ", ";
1063 msg += OCIO::ROLE_SCENE_LINEAR;
1064 first = false;
1065 //++roles;
1066 }
1067 if (i == compositinglogcs) {
1068 msg += first ? " (" : ", ";
1069 msg += OCIO::ROLE_COMPOSITING_LOG;
1070 first = false;
1071 //++roles;
1072 }
1073 if (i == colortimingcs) {
1074 msg += first ? " (" : ", ";
1075 msg += OCIO::ROLE_COLOR_TIMING;
1076 first = false;
1077 //++roles;
1078 }
1079 if (i == texturepaintcs) {
1080 msg += first ? " (" : ", ";
1081 msg += OCIO::ROLE_TEXTURE_PAINT;
1082 first = false;
1083 //++roles;
1084 }
1085 if (i == mattepaintcs) {
1086 msg += first ? " (" : ", ";
1087 msg += OCIO::ROLE_MATTE_PAINT;
1088 first = false;
1089 //++roles;
1090 }
1091 if (!first /*&& roles > 0*/) {
1092 msg += ')';
1093 }
1094 string csdesc = cs ? cs->getDescription() : "(no colorspace)";
1095 csdesc = whitespacify( trim(csdesc) );
1096 if ( !csdesc.empty() ) {
1097 msg += ": ";
1098 msg += csdesc;
1099 msg += '\n';
1100 } else {
1101 msg += '\n';
1102 }
1103 }
1104 }
1105 _parent->sendMessage(Message::eMessageMessage, "", msg);
1106 } else if (!_config) {
1107 // the other parameters assume there is a valid config
1108 return;
1109 } else if (paramName == kOCIOParamInputSpace) {
1110 assert(_inputSpace);
1111 if (args.reason == eChangeUserEdit) {
1112 // if the inputspace doesn't correspond to a valid one, reset to default.
1113 // first, canonicalize.
1114 string inputSpace;
1115 getInputColorspaceAtTime(args.time, inputSpace);
1116 string inputSpaceCanonical = canonicalizeColorSpace(_config, inputSpace);
1117 if (inputSpaceCanonical != inputSpace) {
1118 _inputSpace->setValue(inputSpaceCanonical);
1119 inputSpace = inputSpaceCanonical;
1120 }
1121 int inputSpaceIndex = _config->getIndexForColorSpace( inputSpace.c_str() );
1122 if (inputSpaceIndex < 0) {
1123 if (args.reason == eChangeUserEdit) {
1124 _parent->sendMessage(Message::eMessageWarning, "", string("Unknown OCIO colorspace \"") + inputSpace + "\"");
1125 }
1126 OCIO::ConstColorSpaceRcPtr cs;
1127 if (!cs) {
1128 cs = _config->getColorSpace(OCIO::ROLE_DEFAULT);
1129 }
1130 if (!cs) {
1131 // no default colorspace, fallback to the first one
1132 cs = _config->getColorSpace( _config->getColorSpaceNameByIndex(0) );
1133 }
1134 inputSpace = cs ? cs->getName() : OCIO::ROLE_DEFAULT;
1135 _inputSpace->setValue(inputSpace);
1136 inputSpaceIndex = _config->getIndexForColorSpace( inputSpace.c_str() );
1137 assert(inputSpaceIndex >= 0);
1138 }
1139 }
1140 inputCheck(args.time);
1141 }
1142 #ifdef OFX_OCIO_CHOICE
1143 else if ( (paramName == kOCIOParamInputSpaceChoice) && (args.reason == eChangeUserEdit) ) {
1144 assert(_inputSpace);
1145 int inputSpaceIndex;
1146 _inputSpaceChoice->getValueAtTime(args.time, inputSpaceIndex);
1147 string inputSpaceOld;
1148 getInputColorspaceAtTime(args.time, inputSpaceOld);
1149 string inputSpace = canonicalizeColorSpace( _config, _config->getColorSpaceNameByIndex(inputSpaceIndex) );
1150 // avoid an infinite loop on bad hosts (for examples those which don't set args.reason correctly)
1151 if (inputSpace != inputSpaceOld) {
1152 _inputSpace->setValue(inputSpace);
1153 }
1154 }
1155 #endif
1156 else if (paramName == kOCIOParamOutputSpace) {
1157 assert(_outputSpace);
1158 if (args.reason == eChangeUserEdit) {
1159 // if the outputspace doesn't correspond to a valid one, reset to default.
1160 // first, canonicalize.
1161 string outputSpace;
1162 getOutputColorspaceAtTime(args.time, outputSpace);
1163 string outputSpaceCanonical = canonicalizeColorSpace(_config, outputSpace);
1164 if (outputSpaceCanonical != outputSpace) {
1165 _outputSpace->setValue(outputSpaceCanonical);
1166 outputSpace = outputSpaceCanonical;
1167 }
1168 int outputSpaceIndex = _config->getIndexForColorSpace( outputSpace.c_str() );
1169 if (outputSpaceIndex < 0) {
1170 if (args.reason == eChangeUserEdit) {
1171 _parent->sendMessage(Message::eMessageWarning, "", string("Unknown OCIO colorspace \"") + outputSpace + "\"");
1172 }
1173 OCIO::ConstColorSpaceRcPtr cs;
1174 if (!cs) {
1175 cs = _config->getColorSpace(OCIO::ROLE_DEFAULT);
1176 }
1177 if (!cs) {
1178 // no default colorspace, fallback to the first one
1179 cs = _config->getColorSpace( _config->getColorSpaceNameByIndex(0) );
1180 }
1181 outputSpace = cs ? cs->getName() : OCIO::ROLE_DEFAULT;
1182 _outputSpace->setValue(outputSpace);
1183 outputSpaceIndex = _config->getIndexForColorSpace( outputSpace.c_str() );
1184 assert(outputSpaceIndex >= 0);
1185 }
1186 }
1187 outputCheck(args.time);
1188 }
1189 #ifdef OFX_OCIO_CHOICE
1190 else if ( (paramName == kOCIOParamOutputSpaceChoice) && (args.reason == eChangeUserEdit) ) {
1191 assert(_outputSpace);
1192 int outputSpaceIndex;
1193 _outputSpaceChoice->getValueAtTime(args.time, outputSpaceIndex);
1194 string outputSpaceOld;
1195 getOutputColorspaceAtTime(args.time, outputSpaceOld);
1196 string outputSpace = canonicalizeColorSpace( _config, _config->getColorSpaceNameByIndex(outputSpaceIndex) );
1197 // avoid an infinite loop on bad hosts (for examples those which don't set args.reason correctly)
1198 if (outputSpace != outputSpaceOld) {
1199 _outputSpace->setValue(outputSpace);
1200 }
1201 }
1202 #endif // OFX_OCIO_CHOICE
1203
1204
1205 #endif // ifdef OFX_IO_USING_OCIO
1206 } // GenericOCIO::changedParam
1207
1208 #ifdef OFX_IO_USING_OCIO
1209 void
getInputColorspaceDefault(string & v) const1210 GenericOCIO::getInputColorspaceDefault(string &v) const
1211 {
1212 assert(_inputSpace);
1213 _inputSpace->getDefault(v);
1214 }
1215
1216 void
getInputColorspace(string & v) const1217 GenericOCIO::getInputColorspace(string &v) const
1218 {
1219 assert(_inputSpace);
1220 _inputSpace->getValue(v);
1221 }
1222
1223 void
getInputColorspaceAtTime(double time,string & v) const1224 GenericOCIO::getInputColorspaceAtTime(double time,
1225 string &v) const
1226 {
1227 assert(_inputSpace);
1228 _inputSpace->getValueAtTime(time, v);
1229 }
1230
1231 void
getOutputColorspaceDefault(string & v) const1232 GenericOCIO::getOutputColorspaceDefault(string &v) const
1233 {
1234 assert(_outputSpace);
1235 _outputSpace->getDefault(v);
1236 }
1237
1238 void
getOutputColorspace(string & v) const1239 GenericOCIO::getOutputColorspace(string &v) const
1240 {
1241 assert(_outputSpace);
1242 _outputSpace->getValue(v);
1243 }
1244
1245 void
getOutputColorspaceAtTime(double time,string & v) const1246 GenericOCIO::getOutputColorspaceAtTime(double time,
1247 string &v) const
1248 {
1249 assert(_outputSpace);
1250 _outputSpace->getValueAtTime(time, v);
1251 }
1252
1253 #endif
1254
1255
1256 bool
hasColorspace(const char * name) const1257 GenericOCIO::hasColorspace(const char* name) const
1258 {
1259 #ifdef OFX_IO_USING_OCIO
1260
1261 return _config && (bool)_config->getColorSpace(name);
1262 #else
1263
1264 return false;
1265 #endif
1266 }
1267
1268 void
setInputColorspace(const char * name)1269 GenericOCIO::setInputColorspace(const char* name)
1270 {
1271 #ifdef OFX_IO_USING_OCIO
1272 assert(_inputSpace);
1273 _inputSpace->setValue(name);
1274 #endif
1275 }
1276
1277 void
setOutputColorspace(const char * name)1278 GenericOCIO::setOutputColorspace(const char* name)
1279 {
1280 #ifdef OFX_IO_USING_OCIO
1281 assert(_outputSpace);
1282 _outputSpace->setValue(name);
1283 #endif
1284 }
1285
1286 void
purgeCaches()1287 GenericOCIO::purgeCaches()
1288 {
1289 #ifdef OFX_IO_USING_OCIO
1290 OCIO::ClearAllCaches();
1291 #endif
1292 }
1293
1294 void
describeInContextInput(ImageEffectDescriptor & desc,ContextEnum,PageParamDescriptor * page,const char * inputSpaceNameDefault,const char * inputSpaceLabel)1295 GenericOCIO::describeInContextInput(ImageEffectDescriptor &desc,
1296 ContextEnum /*context*/,
1297 PageParamDescriptor *page,
1298 const char* inputSpaceNameDefault,
1299 const char* inputSpaceLabel)
1300 {
1301 #ifdef OFX_IO_USING_OCIO
1302 gHostIsNatron = (getImageEffectHostDescription()->isNatron);
1303
1304 char* file = std::getenv("OCIO");
1305 OCIO::ConstConfigRcPtr config;
1306 if (file != NULL) {
1307 //Add choices
1308 try {
1309 config = OCIO::Config::CreateFromFile(file);
1310 gWasOCIOEnvVarFound = true;
1311 } catch (OCIO::Exception &e) {
1312 }
1313 }
1314 string inputSpaceName, outputSpaceName;
1315 if (config) {
1316 inputSpaceName = canonicalizeColorSpace( config, colorSpaceName(config, inputSpaceNameDefault) );
1317 }
1318
1319 ////////// OCIO config file
1320 {
1321 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamConfigFile);
1322 param->setLabelAndHint(kOCIOParamConfigFileLabel);
1323 param->setStringType(eStringTypeFilePath);
1324 param->setFilePathExists(true);
1325 param->setAnimates(false);
1326 desc.addClipPreferencesSlaveParam(*param);
1327 // the OCIO config can only be set in a portable fashion using the environment variable.
1328 // Nuke, for example, doesn't support changing the entries in a ChoiceParam outside of describeInContext.
1329 // disable it, and set the default from the env variable.
1330 assert( getImageEffectHostDescription() );
1331 if (file == NULL) {
1332 param->setDefault("WARNING: Open an OCIO config file, or set the OCIO environnement variable");
1333 } else if (config) {
1334 param->setDefault(file);
1335 } else {
1336 string s("ERROR: Invalid OCIO configuration '");
1337 s += file;
1338 s += '\'';
1339 param->setDefault(s);
1340 }
1341 if (page) {
1342 page->addChild(*param);
1343 }
1344 }
1345
1346 ///////////Input Color-space
1347 {
1348 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamInputSpace);
1349 param->setLabelAndHint(inputSpaceLabel, kOCIOParamInputSpaceHint);
1350 param->setAnimates(true);
1351 if (config) {
1352 param->setDefault(inputSpaceName);
1353 } else {
1354 //param->setEnabled(false); // done in constructor
1355 }
1356 if (page) {
1357 page->addChild(*param);
1358 }
1359 }
1360
1361 #ifdef OFX_OCIO_CHOICE
1362 {
1363 ChoiceParamDescriptor* param = desc.defineChoiceParam(kOCIOParamInputSpaceChoice);
1364 param->setLabelAndHint(inputSpaceLabel, kOCIOParamInputSpaceHint);
1365 param->setCascading(getImageEffectHostDescription()->supportsCascadingChoices);
1366 if (config) {
1367 buildChoiceMenu(config, param, getImageEffectHostDescription()->supportsCascadingChoices, inputSpaceName);
1368 } else {
1369 //param->setEnabled(false); // done in the plugin constructor
1370 //param->setIsSecret(true); // done in the plugin constructor
1371 }
1372 param->setAnimates(true);
1373 param->setEvaluateOnChange(false); // evaluate only when the StringParam is changed
1374 param->setIsPersistent(false); // don't save/serialize
1375 if (page) {
1376 page->addChild(*param);
1377 }
1378 }
1379 #endif
1380 #endif // ifdef OFX_IO_USING_OCIO
1381 } // GenericOCIO::describeInContextInput
1382
1383 void
describeInContextOutput(ImageEffectDescriptor & desc,ContextEnum,PageParamDescriptor * page,const char * outputSpaceNameDefault,const char * outputSpaceLabel)1384 GenericOCIO::describeInContextOutput(ImageEffectDescriptor &desc,
1385 ContextEnum /*context*/,
1386 PageParamDescriptor *page,
1387 const char* outputSpaceNameDefault,
1388 const char* outputSpaceLabel)
1389 {
1390 #ifdef OFX_IO_USING_OCIO
1391 gHostIsNatron = (getImageEffectHostDescription()->isNatron);
1392
1393 char* file = std::getenv("OCIO");
1394 OCIO::ConstConfigRcPtr config;
1395 if (file != NULL) {
1396 //Add choices
1397 try {
1398 config = OCIO::Config::CreateFromFile(file);
1399 gWasOCIOEnvVarFound = true;
1400 } catch (OCIO::Exception &e) {
1401 }
1402 }
1403 string outputSpaceName;
1404 if (config) {
1405 outputSpaceName = canonicalizeColorSpace( config, colorSpaceName(config, outputSpaceNameDefault) );
1406 }
1407
1408 ///////////Output Color-space
1409 {
1410 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamOutputSpace);
1411 param->setLabelAndHint(outputSpaceLabel, kOCIOParamOutputSpaceHint);
1412 param->setAnimates(true);
1413 if (config) {
1414 param->setDefault(outputSpaceName);
1415 } else {
1416 //param->setEnabled(false); // done in constructor
1417 }
1418 if (page) {
1419 page->addChild(*param);
1420 }
1421 }
1422
1423 #ifdef OFX_OCIO_CHOICE
1424 {
1425 ChoiceParamDescriptor* param = desc.defineChoiceParam(kOCIOParamOutputSpaceChoice);
1426 param->setLabelAndHint(outputSpaceLabel, kOCIOParamOutputSpaceHint);
1427 param->setCascading(getImageEffectHostDescription()->supportsCascadingChoices);
1428 if (config) {
1429 buildChoiceMenu(config, param, getImageEffectHostDescription()->supportsCascadingChoices, outputSpaceName);
1430 } else {
1431 //param->setEnabled(false); // done in the plugin constructor
1432 //param->setIsSecret(true); // done in the plugin constructor
1433 }
1434 param->setAnimates(true);
1435 param->setEvaluateOnChange(false); // evaluate only when the StringParam is changed
1436 param->setIsPersistent(false); // don't save/serialize
1437 if (page) {
1438 page->addChild(*param);
1439 }
1440 }
1441 #endif
1442 #endif // ifdef OFX_IO_USING_OCIO
1443 } // GenericOCIO::describeInContextOutput
1444
1445 void
describeInContextContext(ImageEffectDescriptor & desc,ContextEnum,PageParamDescriptor * page)1446 GenericOCIO::describeInContextContext(ImageEffectDescriptor &desc,
1447 ContextEnum /*context*/,
1448 PageParamDescriptor *page)
1449 {
1450 #ifdef OFX_IO_USING_OCIO
1451 GroupParamDescriptor* group = desc.defineGroupParam(kOCIOParamContext);
1452 group->setLabelAndHint(kOCIOParamContextLabel, kOCIOParamContextHint);
1453 group->setOpen(false);
1454
1455 {
1456 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextKey1);
1457 param->setHint(kOCIOParamContextHint);
1458 param->setAnimates(true);
1459 param->setParent(*group);
1460 param->setLayoutHint(eLayoutHintNoNewLine, 1);
1461 if (page) {
1462 page->addChild(*param);
1463 }
1464 }
1465 {
1466 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextValue1);
1467 param->setHint(kOCIOParamContextHint);
1468 param->setAnimates(true);
1469 param->setParent(*group);
1470 if (page) {
1471 page->addChild(*param);
1472 }
1473 }
1474 {
1475 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextKey2);
1476 param->setHint(kOCIOParamContextHint);
1477 param->setAnimates(true);
1478 param->setParent(*group);
1479 param->setLayoutHint(eLayoutHintNoNewLine, 1);
1480 if (page) {
1481 page->addChild(*param);
1482 }
1483 }
1484 {
1485 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextValue2);
1486 param->setHint(kOCIOParamContextHint);
1487 param->setAnimates(true);
1488 param->setParent(*group);
1489 if (page) {
1490 page->addChild(*param);
1491 }
1492 }
1493 {
1494 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextKey3);
1495 param->setHint(kOCIOParamContextHint);
1496 param->setAnimates(true);
1497 param->setParent(*group);
1498 param->setLayoutHint(eLayoutHintNoNewLine, 1);
1499 if (page) {
1500 page->addChild(*param);
1501 }
1502 }
1503 {
1504 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextValue3);
1505 param->setHint(kOCIOParamContextHint);
1506 param->setAnimates(true);
1507 param->setParent(*group);
1508 if (page) {
1509 page->addChild(*param);
1510 }
1511 }
1512 {
1513 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextKey4);
1514 param->setHint(kOCIOParamContextHint);
1515 param->setAnimates(true);
1516 param->setParent(*group);
1517 param->setLayoutHint(eLayoutHintNoNewLine, 1);
1518 if (page) {
1519 page->addChild(*param);
1520 }
1521 }
1522 {
1523 StringParamDescriptor* param = desc.defineStringParam(kOCIOParamContextValue4);
1524 param->setHint(kOCIOParamContextHint);
1525 param->setAnimates(true);
1526 param->setParent(*group);
1527 if (page) {
1528 page->addChild(*param);
1529 }
1530 }
1531 if (page) {
1532 page->addChild(*group);
1533 }
1534 #endif // ifdef OFX_IO_USING_OCIO
1535 } // GenericOCIO::describeInContextContext
1536
1537 NAMESPACE_OFX_IO_EXIT
1538 NAMESPACE_OFX_EXIT
1539