1 /*
2 OFX Support Library, a library that skins the OFX plug-in API with C++ classes.
3 Copyright (C) 2004-2007 The Open Effects Association Ltd
4 Author Bruno Nicoletti bruno@thefoundry.co.uk
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 * Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14 * Neither the name The Open Effects Association Ltd, nor the names of its
15 contributors may be used to endorse or promote products derived from this
16 software without specific prior written permission.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 The Open Effects Association Ltd
30 1 Wardour St
31 London W1D 6PA
32 England
33 
34 
35 */
36 
37 /** @brief This file contains code that skins the ofx effect suite */
38 
39 #include "ofxsSupportPrivate.h"
40 #include <algorithm> // for find, min, max
41 #include <cstring> // for strlen
42 #include <sstream> // stringstream
43 #include <set>
44 #ifdef DEBUG
45 #include <iostream>
46 #endif
47 #include <stdexcept>
48 #ifdef OFX_EXTENSIONS_NUKE
49 #include "nuke/fnOfxExtensions.h"
50 #endif
51 #ifdef OFX_EXTENSIONS_NATRON
52 #include "ofxNatron.h"
53 #endif
54 #ifdef OFX_SUPPORTS_OPENGLRENDER
55 #include "ofxOpenGLRender.h"
56 #endif
57 #include "ofxsCore.h"
58 
59 #if defined __APPLE__ || defined __linux__ || defined __FreeBSD__ || __DragonFly__
60 # if __GNUC__ >= 4
61 #  define EXPORT __attribute__((visibility("default")))
62 #  define LOCAL  __attribute__((visibility("hidden")))
63 # else
64 #  define EXPORT
65 #  define LOCAL
66 # endif
67 #elif defined _WIN32
68 #  define EXPORT OfxExport
69 #  define LOCAL
70 #else
71 #  error Not building on your operating system quite yet
72 #endif
73 
74 // string utility functions
ends_with(std::string const & value,std::string const & ending)75 static bool ends_with(std::string const & value, std::string const & ending)
76 {
77   if (ending.size() > value.size()) return false;
78   return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
79 }
80 
starts_with(std::string const & value,std::string const & beginning)81 static bool starts_with(std::string const & value, std::string const & beginning)
82 {
83   if (beginning.size() > value.size()) return false;
84   return std::equal(beginning.begin(), beginning.end(), value.begin());
85 }
86 
87 template<typename T>
88 static inline void
unused(const T &)89 unused(const T&) {}
90 
91 #ifdef DEBUG
92 //table for xml encoding compatibility with expat decoding
93 //see expat/src/xmltok_impl.h
94 //and expat/src/asciitab.h
95 static const int charXMLCompatiblity[] =
96 {
97     /* 0x00 */ 0, 0, 0, 0,
98     /* 0x04 */ 0, 0, 0, 0,
99     /* 0x08 */ 0, 1, 1, 0,
100     /* 0x0C */ 0, 1, 0, 0,
101     /* 0x10 */ 0, 0, 0, 0,
102     /* 0x14 */ 0, 0, 0, 0,
103     /* 0x18 */ 0, 0, 0, 0,
104     /* 0x1C */ 0, 0, 0, 0,
105 };
106 
107 // Check the effect descriptor for potentially bad XML in the OFX Plugin cache.
108 //
109 // The function that wrote string properties to the OFX plugin cache used to
110 // escape only que quote (") character. As a result, if one of the string
111 // properties in the Image Effect descriptor contains special characters,
112 // the host may be unable to read the plugin cache, giving an "xml error 4"
113 // (error 4 is XML_ERROR_INVALID_TOKEN).
114 //
115 // The plugin label and grouping may not contain any of these bad characters.
116 // The plugin description may contain characters that do not corrupt the XML,
117 // because kOfxActionDescribe is usually called at plugin loading.
118 //
119 // Version with the bug:
120 // https://github.com/ofxa/openfx/blob/34ff595a2918e9e4065603d7fc4d500ae9efc421/HostSupport/include/ofxhXml.h
121 // Version without the bug:
122 // https://github.com/ofxa/openfx/blob/64ba52fff61894759fbf3942b92659b02b2b17cd/HostSupport/include/ofxhXml.h
123 //
124 // With the old version:
125 // - All characters within 0x01..0x1f and 0x7F..0x9F, except \t \b \r (0x09 0x0A 0X0D) are forbidden
126 //   in label and grouping, because these may be used to build the GUI before loading the plugin.
127 //   They may appear in the description, although it is not recommended.
128 // - The '<' and '&' characters generate bad XML in all cases
129 // - The '>' and '\'' characters are tolerated by the expat parser, although the XML is not valid
validateXMLString(std::string const & s,bool strict)130 static void validateXMLString(std::string const & s, bool strict)
131 {
132   int lt = 0;
133   int amp = 0;
134   int ctrl = 0;
135   char ctrllast = 0;
136   int ctrlexpatbug = 0; // control characters which expat can not decode anyway, because of a bug in xmltok.c:checkCharRefNumber
137   char ctrlexpatbuglast = 0;
138   for (size_t i=0;i<s.size();i++) {
139     // The are exactly five characters which must be escaped
140     // http://www.w3.org/TR/xml/#syntax
141     switch (s[i]) {
142       case '\t':
143       case '\n':
144       case '\r':
145       case '"':
146       case '\'':
147       case '>':
148         break;
149       case '<':
150         ++lt;
151         break;
152       case '&':
153         ++amp;
154         break;
155       default: {
156         unsigned char c = (unsigned char)(s[i]);
157         if ((0x01 <= c && c <= 0x1f) || (0x7F <= c && c <= 0x9F)) {
158           ++ctrl;
159           ctrllast = c;
160           //characters such ase eot (0x04) and stx (0x02) make expat parser bail
161           //see xmltok.c in expat checkCharRefNumber() to see how expat bails on these chars.
162           //also see expat/src/asciitab.h to see which characters are nonxml compatible post decode
163           //(we can still encode '&' and '<' with this table, but it prevents us from encoding eot)
164           //everything is compatible past ascii 0x20, so we don't check higher than this.
165           if(c <= 0x1F && charXMLCompatiblity[c] == 0) {
166             ++ctrlexpatbug;
167             ctrlexpatbuglast = c;
168           }
169         }
170       } break;
171     }
172   }
173   if (lt || amp || ctrl) {
174     std::cout << "Warning: the following string value may break the OFX plugin cache on older hosts,\n";
175     std::cout << "because it contains:\n";
176     if (lt) {
177       std::cout << lt << " '<' characters\n";
178     }
179     if (amp) {
180       std::cout << amp << " '&' characters\n";
181     }
182     if (ctrl) {
183       if (!strict) {
184         std::cout << "(nonfatal) ";
185       }
186       std::cout << ctrl << " invalid characters in the range 0x01..0x1f or 0x7F..0x9F, last one was ASCII=" << (int)ctrllast << std::endl;
187     }
188     if (ctrlexpatbug) {
189       if (!strict) {
190         std::cout << "(nonfatal) ";
191       }
192       std::cout << ctrlexpatbug << " invalid characters in the range 0x01..0x1f which expat cannot decode (will cause xml error 'reference to invalid character number (14)'), last one was ASCII=" << (int)ctrlexpatbuglast << std::endl;
193 
194     }
195     std::cout << "Raw string value (length=" << s.size() << "):\n";
196     std::cout << s << std::endl;
197   }
198 }
199 #else
200 #define validateXMLString(s,b) (void)0;
201 #endif
202 
203 /** @brief The core 'OFX Support' namespace, used by plugin implementations. All code for these are defined in the common support libraries. */
204 namespace OFX {
205 
206   //Put it all into a map, so we know when to delete what!
207   struct OfxPlugInfo
208   {
OfxPlugInfoOFX::OfxPlugInfo209     OfxPlugInfo(): _factory(NULL), _plug(NULL) {}
OfxPlugInfoOFX::OfxPlugInfo210     OfxPlugInfo(OFX::PluginFactory* f, OfxPlugin* p):_factory(f), _plug(p){}
211     OFX::PluginFactory* _factory;
212     OfxPlugin* _plug;
213   };
214   typedef std::map<std::string, OfxPlugInfo> OfxPlugInfoMap;
215   OfxPlugInfoMap plugInfoMap;
216 
217   typedef std::vector<OfxPlugin*> OfxPluginArray;
218   OfxPluginArray ofxPlugs;
219 
220   /** @brief the global host description */
221   ImageEffectHostDescription gHostDescription;
222   bool gHostDescriptionHasInit = false;
223 
supportsPixelComponent(const PixelComponentEnum component) const224   bool ImageEffectHostDescription::supportsPixelComponent(const PixelComponentEnum component) const
225   {
226     return std::find(_supportedComponents.begin(), _supportedComponents.end(), component) != _supportedComponents.end();
227   }
supportsBitDepth(const BitDepthEnum bitDepth) const228   bool ImageEffectHostDescription::supportsBitDepth( const BitDepthEnum bitDepth) const
229   {
230     return std::find(_supportedPixelDepths.begin(), _supportedPixelDepths.end(), bitDepth) != _supportedPixelDepths.end();
231   }
supportsContext(const ContextEnum context) const232   bool ImageEffectHostDescription::supportsContext(const ContextEnum context) const
233   {
234     return std::find(_supportedContexts.begin(), _supportedContexts.end(), context) != _supportedContexts.end();
235   }
236 
237   /** @return default pixel depth supported by host application. */
getDefaultPixelDepth() const238   BitDepthEnum ImageEffectHostDescription::getDefaultPixelDepth() const
239   {
240     if(!_supportedPixelDepths.empty()) {
241       return _supportedPixelDepths[0];
242     } else {
243       OFX::Log::warning(true, "The host doesn't define supported pixel depth. (size: %d)", (int)_supportedPixelDepths.size());
244       return eBitDepthFloat;
245     }
246   }
247 
248   /** @return default pixel component supported by host application. */
getDefaultPixelComponent() const249   PixelComponentEnum ImageEffectHostDescription::getDefaultPixelComponent() const
250   {
251     if(! _supportedComponents.empty()) {
252       return _supportedComponents[0];
253     } else {
254       OFX::Log::warning(true, "The host doesn't define supported pixel component. (size: %d)", _supportedComponents.size());
255       return ePixelComponentRGBA;
256     }
257   }
258 
getImageEffectHostDescription()259   ImageEffectHostDescription* getImageEffectHostDescription()
260   {
261     if(gHostDescriptionHasInit)
262       return &gHostDescription;
263     return NULL;
264   }
265 
266   namespace Private {
267     // Suite and host pointers
268     OfxHost               *gHost = 0;
269     OfxImageEffectSuiteV1 *gEffectSuite = 0;
270     OfxPropertySuiteV1    *gPropSuite = 0;
271     OfxInteractSuiteV1    *gInteractSuite = 0;
272     OfxParameterSuiteV1   *gParamSuite = 0;
273     OfxMemorySuiteV1      *gMemorySuite = 0;
274     OfxMultiThreadSuiteV1 *gThreadSuite = 0;
275     OfxMessageSuiteV1     *gMessageSuite = 0;
276     OfxMessageSuiteV2     *gMessageSuiteV2 = 0;
277     OfxProgressSuiteV1    *gProgressSuiteV1 = 0;
278     OfxProgressSuiteV2    *gProgressSuiteV2 = 0;
279 #ifdef OFX_SUPPORTS_DIALOG
280     OfxDialogSuiteV1      *gDialogSuiteV1 = 0;
281     OfxDialogSuiteV2      *gDialogSuiteV2 = 0;
282 #endif
283     OfxTimeLineSuiteV1    *gTimeLineSuite = 0;
284     OfxParametricParameterSuiteV1 *gParametricParameterSuite = 0;
285 #ifdef OFX_SUPPORTS_OPENGLRENDER
286     OfxImageEffectOpenGLRenderSuiteV1 *gOpenGLRenderSuite = 0;
287 #endif
288 #ifdef OFX_EXTENSIONS_NUKE
289     NukeOfxCameraSuiteV1* gCameraSuite = 0;
290     FnOfxImageEffectPlaneSuiteV1* gImageEffectPlaneSuiteV1 = 0;
291     FnOfxImageEffectPlaneSuiteV2* gImageEffectPlaneSuiteV2 = 0;
292 #endif
293 #ifdef OFX_EXTENSIONS_VEGAS
294 #if defined(WIN32) || defined(WIN64)
295     OfxHWNDInteractSuiteV1 *gHWNDInteractSuite = 0;
296 #endif // #if defined(WIN32) || defined(WIN64)
297     OfxVegasProgressSuiteV1 *gVegasProgressSuite = 0;
298     OfxVegasStereoscopicImageSuiteV1 *gVegasStereoscopicImageSuite = 0;
299     OfxVegasKeyframeSuiteV1 *gVegasKeyframeSuite = 0;
300 #endif
301 
302     // @brief the set of descriptors, one per context used by kOfxActionDescribeInContext,
303     //'eContextNone' is the one used by the kOfxActionDescribe
304     EffectDescriptorMap gEffectDescriptors;
305   };
306 
307   /** @brief map a std::string to a context */
mapToContextEnum(const std::string & s)308   ContextEnum mapToContextEnum(const std::string &s) OFX_THROW(std::invalid_argument)
309   {
310     if(s == kOfxImageEffectContextGenerator) return eContextGenerator;
311     if(s == kOfxImageEffectContextFilter) return eContextFilter;
312     if(s == kOfxImageEffectContextTransition) return eContextTransition;
313     if(s == kOfxImageEffectContextPaint) return eContextPaint;
314     if(s == kOfxImageEffectContextGeneral) return eContextGeneral;
315     if(s == kOfxImageEffectContextRetimer) return eContextRetimer;
316 #ifdef OFX_EXTENSIONS_TUTTLE
317     if(s == kOfxImageEffectContextReader) return eContextReader;
318     if(s == kOfxImageEffectContextWriter) return eContextWriter;
319 #endif
320 #ifdef OFX_EXTENSIONS_NATRON
321     if(s == kNatronOfxImageEffectContextTracker) return eContextTracker;
322 #endif
323     OFX::Log::error(true, "Unknown image effect context '%s'", s.c_str());
324     throw std::invalid_argument(s);
325   }
326 
mapContextEnumToStr(ContextEnum context)327   const char* mapContextEnumToStr(ContextEnum context) OFX_THROW(std::invalid_argument)
328   {
329     switch (context) {
330       case eContextGenerator:
331         return kOfxImageEffectContextGenerator;
332       case eContextFilter:
333         return kOfxImageEffectContextFilter;
334       case eContextTransition:
335         return kOfxImageEffectContextTransition;
336       case eContextPaint:
337         return kOfxImageEffectContextPaint;
338       case eContextGeneral:
339         return kOfxImageEffectContextGeneral;
340       case eContextRetimer:
341         return kOfxImageEffectContextRetimer;
342 #ifdef OFX_EXTENSIONS_TUTTLE
343       case eContextReader:
344         return kOfxImageEffectContextReader;
345       case eContextWriter:
346         return kOfxImageEffectContextWriter;
347 #endif
348 #ifdef OFX_EXTENSIONS_NATRON
349       case eContextTracker:
350         return kNatronOfxImageEffectContextTracker;
351 #endif
352       default:
353         OFX::Log::error(true, "Unknown context enum '%d'", (int)context);
354         throw std::invalid_argument("unknown ContextEnum");
355     }
356   }
357 
mapMessageTypeEnumToStr(OFX::Message::MessageTypeEnum type)358   const char* mapMessageTypeEnumToStr(OFX::Message::MessageTypeEnum type)
359   {
360     if(type == OFX::Message::eMessageFatal)
361       return kOfxMessageFatal;
362     else if(type == OFX::Message::eMessageError)
363       return kOfxMessageError;
364     else if(type == OFX::Message::eMessageMessage)
365       return kOfxMessageMessage;
366     else if(type == OFX::Message::eMessageWarning)
367       return kOfxMessageWarning;
368     else if(type == OFX::Message::eMessageLog)
369       return kOfxMessageLog;
370     else if(type == OFX::Message::eMessageQuestion)
371       return kOfxMessageQuestion;
372     OFX::Log::error(true, "Unknown message type enum '%d'", type);
373     return 0;
374   }
375 
mapToMessageReplyEnum(OfxStatus stat)376   OFX::Message::MessageReplyEnum mapToMessageReplyEnum(OfxStatus stat)
377   {
378     if(stat == kOfxStatOK)
379       return OFX::Message::eMessageReplyOK;
380     else if(stat == kOfxStatReplyYes)
381       return OFX::Message::eMessageReplyYes;
382     else if(stat == kOfxStatReplyNo)
383       return OFX::Message::eMessageReplyNo;
384     else if(stat == kOfxStatFailed)
385       return OFX::Message::eMessageReplyFailed;
386     OFX::Log::error(true, "Unknown message reply status enum '%d'", stat);
387     return OFX::Message::eMessageReplyFailed;
388   }
389 
390   /** @brief map a std::string to a context */
mapToInstanceChangedReason(const std::string & s)391   InstanceChangeReason mapToInstanceChangedReason(const std::string &s) OFX_THROW(std::invalid_argument)
392   {
393     if(s == kOfxChangePluginEdited) return eChangePluginEdit;
394     if(s == kOfxChangeUserEdited) return eChangeUserEdit;
395     if(s == kOfxChangeTime) return eChangeTime;
396     OFX::Log::error(true, "Unknown instance changed reason '%s'", s.c_str());
397     throw std::invalid_argument(s);
398   }
399 
400   /** @brief turns a bit depth string into and enum */
mapStrToBitDepthEnum(const std::string & str)401   BitDepthEnum mapStrToBitDepthEnum(const std::string &str) OFX_THROW(std::invalid_argument)
402   {
403     if(str == kOfxBitDepthByte) {
404       return eBitDepthUByte;
405     }
406     else if(str == kOfxBitDepthShort) {
407       return eBitDepthUShort;
408     }
409     else if(str == kOfxBitDepthHalf) {
410       return eBitDepthHalf;
411     }
412     else if(str == kOfxBitDepthFloat) {
413       return eBitDepthFloat;
414     }
415 #ifdef OFX_EXTENSIONS_VEGAS
416     else if(str == kOfxBitDepthByteBGR) {
417       return eBitDepthUByteBGRA;
418     }
419     else if(str == kOfxBitDepthShortBGR) {
420       return eBitDepthUShortBGRA;
421     }
422     else if(str == kOfxBitDepthFloatBGR) {
423       return eBitDepthFloatBGRA;
424     }
425 #endif
426     else if(str == kOfxBitDepthNone) {
427       return eBitDepthNone;
428     }
429     else {
430       return eBitDepthCustom;
431     }
432   }
433 
434   /** @brief turns a bit depth string into and enum */
mapBitDepthEnumToStr(BitDepthEnum bitDepth)435   const char* mapBitDepthEnumToStr(BitDepthEnum bitDepth) OFX_THROW(std::invalid_argument)
436   {
437     switch (bitDepth) {
438       case eBitDepthUByte:
439         return kOfxBitDepthByte;
440       case eBitDepthUShort:
441         return kOfxBitDepthShort;
442       case eBitDepthHalf:
443         return kOfxBitDepthHalf;
444       case eBitDepthFloat:
445         return kOfxBitDepthFloat;
446 #ifdef OFX_EXTENSIONS_VEGAS
447       case eBitDepthUByteBGRA:
448         return kOfxBitDepthByteBGR;
449       case eBitDepthUShortBGRA:
450         return kOfxBitDepthShortBGR;
451       case eBitDepthFloatBGRA:
452         return kOfxBitDepthFloatBGR;
453 #endif
454       case eBitDepthNone:
455         return kOfxBitDepthNone;
456       case eBitDepthCustom:
457         return "OfxBitDepthCustom";
458       default:
459         OFX::Log::error(true, "Unknown bit depth enum '%d'", (int)bitDepth);
460         throw std::invalid_argument("unknown BitDepthEnum");
461     }
462   }
463 
464   /** @brief turns a pixel component string into and enum */
mapStrToPixelComponentEnum(const std::string & str)465   PixelComponentEnum mapStrToPixelComponentEnum(const std::string &str) OFX_THROW(std::invalid_argument)
466   {
467     if(str == kOfxImageComponentRGBA) {
468       return ePixelComponentRGBA;
469     }
470     else if(str == kOfxImageComponentRGB) {
471       return ePixelComponentRGB;
472     }
473     else if(str == kOfxImageComponentAlpha) {
474       return ePixelComponentAlpha;
475     }
476     else if(str == kOfxImageComponentNone) {
477       return ePixelComponentNone;
478     }
479 #ifdef OFX_EXTENSIONS_NUKE
480     else if(str == kFnOfxImageComponentMotionVectors) {
481         return ePixelComponentMotionVectors;
482     }
483     else if(str == kFnOfxImageComponentStereoDisparity) {
484         return ePixelComponentStereoDisparity;
485     }
486 #endif
487 #ifdef OFX_EXTENSIONS_NATRON
488     else if (str == kNatronOfxImageComponentXY) {
489         return ePixelComponentXY;
490     }
491 #endif
492     else {
493       return ePixelComponentCustom;
494     }
495   }
496 
497   /** @brief turns a pixel component string into and enum */
mapPixelComponentEnumToStr(PixelComponentEnum pixelComponent)498   const char* mapPixelComponentEnumToStr(PixelComponentEnum pixelComponent) OFX_THROW(std::invalid_argument)
499   {
500     switch (pixelComponent) {
501       case ePixelComponentRGBA:
502         return kOfxImageComponentRGBA;
503       case ePixelComponentRGB:
504         return kOfxImageComponentRGB;
505       case ePixelComponentAlpha:
506         return kOfxImageComponentAlpha;
507       case ePixelComponentNone:
508         return kOfxImageComponentNone;
509 #ifdef OFX_EXTENSIONS_NUKE
510       case ePixelComponentMotionVectors:
511         return kFnOfxImageComponentMotionVectors;
512       case ePixelComponentStereoDisparity:
513         return kFnOfxImageComponentStereoDisparity;
514 #endif
515 #ifdef OFX_EXTENSIONS_NATRON
516       case ePixelComponentXY:
517         return kNatronOfxImageComponentXY;
518 #endif
519       case ePixelComponentCustom:
520         return "OfxImageComponentCustom";
521       default:
522         OFX::Log::error(true, "Unknown pixel component enum '%d'", (int)pixelComponent);
523         throw std::invalid_argument("unknown PixelComponentEnum");
524     }
525   }
526 
527   /** @brief turns a premultiplication string into and enum */
mapStrToPreMultiplicationEnum(const std::string & str)528   static PreMultiplicationEnum mapStrToPreMultiplicationEnum(const std::string &str) OFX_THROW(std::invalid_argument)
529   {
530     if(str == kOfxImageOpaque) {
531       return eImageOpaque;
532     }
533     else if(str == kOfxImagePreMultiplied) {
534       return eImagePreMultiplied;
535     }
536     else if(str == kOfxImageUnPreMultiplied) {
537       return eImageUnPreMultiplied;
538     }
539     else {
540       throw std::invalid_argument("");
541     }
542   }
543 
544   /** @brief turns a field string into and enum */
mapStrToFieldEnum(const std::string & str)545   FieldEnum mapStrToFieldEnum(const std::string &str) OFX_THROW(std::invalid_argument)
546   {
547     if(str == kOfxImageFieldNone) {
548       return eFieldNone;
549     }
550     else if(str == kOfxImageFieldBoth) {
551       return eFieldBoth;
552     }
553     else if(str == kOfxImageFieldLower) {
554       return eFieldLower;
555     }
556     else if(str == kOfxImageFieldUpper) {
557       return eFieldUpper;
558     }
559     else {
560       throw std::invalid_argument("");
561     }
562   }
563 
564 #ifdef OFX_EXTENSIONS_VEGAS
565   /** @brief map a std::string to a RenderQuality */
mapToVegasRenderQualityEnum(const std::string & s)566   VegasRenderQualityEnum mapToVegasRenderQualityEnum(const std::string &s) OFX_THROW(std::invalid_argument)
567   {
568     if(s == kOfxImageEffectPropRenderQualityDraft  )       return eVegasRenderQualityDraft;
569     if(s == kOfxImageEffectPropRenderQualityPreview)       return eVegasRenderQualityPreview;
570     if(s == kOfxImageEffectPropRenderQualityGood   )       return eVegasRenderQualityGood;
571     if(s.empty() || s == kOfxImageEffectPropRenderQualityBest   )       return eVegasRenderQualityBest;
572     OFX::Log::error(true, "Unknown Vegas RenderQuality '%s'", s.c_str());
573     throw std::invalid_argument(s);
574   }
575 
576   /** @brief map a std::string to a context */
mapToVegasContextEnum(const std::string & s)577   VegasContextEnum mapToVegasContextEnum(const std::string &s) OFX_THROW(std::invalid_argument)
578   {
579     if(s.empty() || s == kOfxImageEffectPropVegasContextUnknown)       return eVegasContextUnknown;
580     if(s == kOfxImageEffectPropVegasContextMedia)         return eVegasContextMedia;
581     if(s == kOfxImageEffectPropVegasContextTrack)         return eVegasContextTrack;
582     if(s == kOfxImageEffectPropVegasContextEvent)         return eVegasContextEvent;
583     if(s == kOfxImageEffectPropVegasContextEventFadeIn)   return eVegasContextEventFadeIn;
584     if(s == kOfxImageEffectPropVegasContextEventFadeOut)  return eVegasContextEventFadeOut;
585     if(s == kOfxImageEffectPropVegasContextProject)       return eVegasContextProject;
586     if(s == kOfxImageEffectPropVegasContextGenerator)     return eVegasContextGenerator;
587     OFX::Log::error(true, "Unknown Vegas image effect context '%s'", s.c_str());
588     throw std::invalid_argument(s);
589   }
590 #endif
591 
592 #if defined(OFX_EXTENSIONS_NATRON)
593   /** @brief extract a custom Natron plane defined in the multi-plane extension from the kOfxImageEffectPropComponents property value, @see getPixelComponentsProperty() */
extractCustomPlane(const std::string & comp,std::string * layerName,std::string * layerLabel,std::string * channelsLabel,std::vector<std::string> * channels)594   bool extractCustomPlane(const std::string& comp, std::string* layerName, std::string* layerLabel, std::string* channelsLabel, std::vector<std::string>* channels)
595   {
596 
597     // Find the plane unique identifier
598     const std::size_t foundPlaneLen = std::strlen(kNatronOfxImageComponentsPlaneName);
599     std::size_t foundPlane = comp.find(kNatronOfxImageComponentsPlaneName);
600     if (foundPlane == std::string::npos) {
601       return false;
602     }
603 
604     const std::size_t planeNameStartIdx = foundPlane + foundPlaneLen;
605 
606     // Find the optionnal plane label
607     // If planeLabelStartIdx = 0, there's no plane label.
608     std::size_t planeLabelStartIdx = 0;
609 
610 
611     const std::size_t foundPlaneLabelLen = std::strlen(kNatronOfxImageComponentsPlaneLabel);
612     std::size_t foundPlaneLabel = comp.find(kNatronOfxImageComponentsPlaneLabel, planeNameStartIdx);
613     if (foundPlaneLabel != std::string::npos) {
614         planeLabelStartIdx = foundPlaneLabel + foundPlaneLabelLen;
615     }
616 
617 
618 
619     // Find the optionnal channels label
620     // If channelsLabelStartIdx = 0, there's no channels label.
621     std::size_t channelsLabelStartIdx = 0;
622 
623 
624     const std::size_t foundChannelsLabelLen = std::strlen(kNatronOfxImageComponentsPlaneChannelsLabel);
625 
626     // If there was a plane label before, pick from there otherwise pick from the name
627     std::size_t findChannelsLabelStart = planeLabelStartIdx > 0 ? planeLabelStartIdx : planeNameStartIdx;
628     std::size_t foundChannelsLabel = comp.find(kNatronOfxImageComponentsPlaneChannelsLabel, findChannelsLabelStart);
629     if (foundChannelsLabel != std::string::npos) {
630         channelsLabelStartIdx = foundChannelsLabel + foundChannelsLabelLen;
631     }
632 
633 
634 
635     // Find the first channel
636     // If there was a channels label before, find from there, otherwise if there was a plane label before
637     // find from there, otherwise find from the name.
638     std::size_t findChannelStart = 0;
639     if (channelsLabelStartIdx > 0) {
640         findChannelStart = channelsLabelStartIdx;
641     } else if (planeLabelStartIdx > 0) {
642         findChannelStart = planeLabelStartIdx;
643     } else {
644         findChannelStart = planeNameStartIdx;
645     }
646 
647     const std::size_t foundChannelLen = std::strlen(kNatronOfxImageComponentsPlaneChannel);
648     std::size_t foundChannel = comp.find(kNatronOfxImageComponentsPlaneChannel, findChannelStart);
649     if (foundChannel == std::string::npos) {
650       // There needs to be at least one channel.
651       return false;
652     }
653 
654     // Extract channels label
655     if (channelsLabelStartIdx > 0) {
656         *channelsLabel = comp.substr(channelsLabelStartIdx, foundChannel - channelsLabelStartIdx);
657     }
658 
659     // Extract plane label
660     if (planeLabelStartIdx > 0) {
661         std::size_t endIndex = (foundChannelsLabel != std::string::npos) ? foundChannelsLabel : foundChannel;
662         *layerLabel = comp.substr(planeLabelStartIdx, endIndex - planeLabelStartIdx);
663     }
664 
665     // Extract plane name
666     {
667         std::size_t endIndex;
668         if (foundPlaneLabel != std::string::npos) {
669             // There's a plane label
670             endIndex = foundPlaneLabel;
671         } else if (foundChannelsLabel != std::string::npos) {
672             // There's no plane label but a channels label
673             endIndex = foundChannelsLabel;
674         } else {
675             // No plane label and no channels label
676             endIndex = foundChannel;
677         }
678         *layerName = comp.substr(planeNameStartIdx, endIndex - planeNameStartIdx);
679     }
680 
681     while (foundChannel != std::string::npos) {
682         if (channels->size() >= 4) {
683             // A plane must have between 1 and 4 channels.
684             return false;
685         }
686       findChannelStart = foundChannel + foundChannelLen;
687       std::size_t nextChannel = comp.find(kNatronOfxImageComponentsPlaneChannel, findChannelStart);
688       std::string chan = comp.substr(findChannelStart, nextChannel - findChannelStart);
689       channels->push_back(chan);
690       foundChannel = nextChannel;
691     }
692 
693     return true;
694   } // extractCustomPlane
695 #endif // OFX_EXTENSIONS_NATRON
696 
697 
698   ////////////////////////////////////////////////////////////////////////////////
699   // clip descriptor
700 
701   /** @brief hidden constructor */
ClipDescriptor(const std::string & name,OfxPropertySetHandle props)702   ClipDescriptor::ClipDescriptor(const std::string &name, OfxPropertySetHandle props)
703     : _clipName(name)
704     , _clipProps(props)
705   {
706     OFX::Validation::validateClipDescriptorProperties(props);
707   }
708 
709   /** @brief set the label properties */
setLabel(const std::string & label)710   void ClipDescriptor::setLabel(const std::string &label)
711   {
712     _clipProps.propSetString(kOfxPropLabel, label);
713   }
714 
715   /** @brief set the label properties */
setLabels(const std::string & label,const std::string & shortLabel,const std::string & longLabel)716   void ClipDescriptor::setLabels(const std::string &label, const std::string &shortLabel, const std::string &longLabel)
717   {
718     setLabel(label);
719     _clipProps.propSetString(kOfxPropShortLabel, shortLabel, false);
720     _clipProps.propSetString(kOfxPropLongLabel, longLabel, false);
721   }
722 
723 #ifdef OFX_EXTENSIONS_NATRON
724   /** @brief set the secretness of the clip, defaults to false */
setIsSecret(bool v)725   void ClipDescriptor::setIsSecret(bool v)
726   {
727     _clipProps.propSetInt(kOfxParamPropSecret, v, false);
728   }
729 
730   /** @brief set the clip hint */
731   void
setHint(const std::string & v)732     ClipDescriptor::setHint(const std::string &v)
733   {
734     _clipProps.propSetString(kOfxParamPropHint, v, false);
735   }
736 
737   /** @brief set the clip label and hint */
738   void
setLabelAndHint(const std::string & label,const std::string & hint)739     ClipDescriptor::setLabelAndHint(const std::string &label, const std::string &hint)
740   {
741     setLabel(label);
742     setHint(hint);
743   }
744 #endif
745 
746   /** @brief set how fielded images are extracted from the clip defaults to eFieldExtractDoubled */
setFieldExtraction(FieldExtractionEnum v)747   void ClipDescriptor::setFieldExtraction(FieldExtractionEnum v)
748   {
749     switch(v)
750     {
751     case eFieldExtractBoth :
752       _clipProps.propSetString(kOfxImageClipPropFieldExtraction, kOfxImageFieldBoth);
753       break;
754 
755     case eFieldExtractSingle :
756       _clipProps.propSetString(kOfxImageClipPropFieldExtraction, kOfxImageFieldSingle);
757       break;
758 
759     case eFieldExtractDoubled :
760       _clipProps.propSetString(kOfxImageClipPropFieldExtraction, kOfxImageFieldDoubled);
761       break;
762     }
763   }
764 
765   /** @brief set which components are supported, defaults to none set, this must be called at least once! */
addSupportedComponent(PixelComponentEnum v)766   void ClipDescriptor::addSupportedComponent(PixelComponentEnum v)
767   {
768     int n = _clipProps.propGetDimension(kOfxImageEffectPropSupportedComponents);
769     switch(v)
770     {
771     case ePixelComponentNone :
772       _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentNone, n);
773       break;
774 
775     case ePixelComponentRGBA :
776       _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentRGBA, n);
777       break;
778 
779     case ePixelComponentRGB :
780       _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentRGB, n);
781       break;
782 
783     case ePixelComponentAlpha :
784       _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentAlpha, n);
785       break;
786 #ifdef OFX_EXTENSIONS_NUKE
787     case ePixelComponentMotionVectors :
788       _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kFnOfxImageComponentMotionVectors, n);
789       break;
790 
791     case ePixelComponentStereoDisparity :
792       _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kFnOfxImageComponentStereoDisparity, n);
793       break;
794 #endif
795 #ifdef OFX_EXTENSIONS_NATRON
796     case ePixelComponentXY:
797       _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kNatronOfxImageComponentXY, n);
798       break;
799 #endif
800     case ePixelComponentCustom :
801       break;
802     }
803   }
804 
805   /** @brief set which components are supported, defaults to none set, this must be called at least once! */
addSupportedComponent(const std::string & comp)806   void ClipDescriptor::addSupportedComponent(const std::string &comp)
807   {
808     int n = _clipProps.propGetDimension(kOfxImageEffectPropSupportedComponents);
809     _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, comp, n);
810 
811   }
812 
813   /** @brief say whether we are going to do random temporal access on this clip, defaults to false */
setTemporalClipAccess(bool v)814   void ClipDescriptor::setTemporalClipAccess(bool v)
815   {
816     _clipProps.propSetInt(kOfxImageEffectPropTemporalClipAccess, int(v));
817   }
818 
819   /** @brief say whether if the clip is optional, defaults to false */
setOptional(bool v)820   void ClipDescriptor::setOptional(bool v)
821   {
822     _clipProps.propSetInt(kOfxImageClipPropOptional, int(v));
823   }
824 
825   /** @brief say whether this clip supports tiling, defaults to true */
setSupportsTiles(bool v)826   void ClipDescriptor::setSupportsTiles(bool v)
827   {
828     _clipProps.propSetInt(kOfxImageEffectPropSupportsTiles, int(v));
829   }
830 
831   /** @brief say whether this clip is a 'mask', so the host can know to replace with a roto or similar, defaults to false */
setIsMask(bool v)832   void ClipDescriptor::setIsMask(bool v)
833   {
834     _clipProps.propSetInt(kOfxImageClipPropIsMask, int(v));
835   }
836 
837 #ifdef OFX_EXTENSIONS_NATRON
838   /** @brief say whether this clip may contain images with a distortion function attached */
setCanDistort(bool v)839   void ClipDescriptor::setCanDistort(bool v)
840   {
841     _clipProps.propSetInt(kOfxImageEffectPropCanDistort, int(v), false);
842   }
843 #endif
844 
845 #ifdef OFX_EXTENSIONS_NUKE
846   /** @brief say whether this clip may contain images with a transform attached */
setCanTransform(bool v)847   void ClipDescriptor::setCanTransform(bool v)
848   {
849     _clipProps.propSetInt(kFnOfxImageEffectCanTransform, int(v), false);
850   }
851 #endif
852 
853 #ifdef OFX_EXTENSIONS_NUKE
854   ////////////////////////////////////////////////////////////////////////////////
855   // camera descriptor
856 
857   /** @brief hidden constructor */
CameraDescriptor(const std::string & name,OfxPropertySetHandle props)858   CameraDescriptor::CameraDescriptor(const std::string &name, OfxPropertySetHandle props)
859     : _cameraName(name)
860     , _cameraProps(props)
861   {
862     OFX::Validation::validateCameraDescriptorProperties(props);
863   }
864 
865   /** @brief set the label properties */
setLabel(const std::string & label)866   void CameraDescriptor::setLabel(const std::string &label)
867   {
868     _cameraProps.propSetString(kOfxPropLabel, label);
869   }
870 
871   /** @brief set the label properties */
setLabels(const std::string & label,const std::string & shortLabel,const std::string & longLabel)872   void CameraDescriptor::setLabels(const std::string &label, const std::string &shortLabel, const std::string &longLabel)
873   {
874     setLabel(label);
875     _cameraProps.propSetString(kOfxPropShortLabel, shortLabel, false);
876     _cameraProps.propSetString(kOfxPropLongLabel, longLabel, false);
877   }
878 
879 #ifdef OFX_EXTENSIONS_NATRON
880   /** @brief set the secretness of the camera, defaults to false */
setIsSecret(bool v)881   void CameraDescriptor::setIsSecret(bool v)
882   {
883     _cameraProps.propSetInt(kOfxParamPropSecret, v, false);
884   }
885 
886   /** @brief set the camera hint */
887   void
setHint(const std::string & v)888     CameraDescriptor::setHint(const std::string &v)
889   {
890     _cameraProps.propSetString(kOfxParamPropHint, v, false);
891   }
892 
893   /** @brief set the camera label and hint */
894   void
setLabelAndHint(const std::string & label,const std::string & hint)895     CameraDescriptor::setLabelAndHint(const std::string &label, const std::string &hint)
896   {
897     setLabel(label);
898     setHint(hint);
899   }
900 #endif
901 
902 
903   /** @brief say whether if the camera is optional, defaults to false */
setOptional(bool v)904   void CameraDescriptor::setOptional(bool v)
905   {
906     _cameraProps.propSetInt(kOfxImageClipPropOptional, int(v));
907   }
908 #endif
909 
910   ////////////////////////////////////////////////////////////////////////////////
911   // image effect descriptor
912 
913   /** @brief effect descriptor ctor */
ImageEffectDescriptor(OfxImageEffectHandle handle)914   ImageEffectDescriptor::ImageEffectDescriptor(OfxImageEffectHandle handle)
915     : _effectHandle(handle)
916   {
917     // fetch the property set handle of the effect
918     OfxPropertySetHandle props;
919     OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet(handle, &props);
920     throwSuiteStatusException(stat);
921     _effectProps.propSetHandle(props);
922 
923     OFX::Validation::validatePluginDescriptorProperties(props);
924 
925     // fetch the param set handle and set it in our ParamSetDescriptor base
926     OfxParamSetHandle paramSetHandle;
927     stat = OFX::Private::gEffectSuite->getParamSet(handle, &paramSetHandle);
928     throwSuiteStatusException(stat);
929     setParamSetHandle(paramSetHandle);
930   }
931 
932 
933   /** @brief dtor */
~ImageEffectDescriptor()934   ImageEffectDescriptor::~ImageEffectDescriptor()
935   {
936     // delete any clip descriptors we may have constructed
937     std::map<std::string, ClipDescriptor *>::iterator iter;
938     for(iter = _definedClips.begin(); iter != _definedClips.end(); ++iter) {
939       if(iter->second) {
940         delete iter->second;
941         iter->second = NULL;
942       }
943     }
944 #ifdef OFX_EXTENSIONS_NUKE
945     std::map<std::string, CameraDescriptor *>::iterator it;
946     for(it = _definedCameras.begin(); it != _definedCameras.end(); ++it) {
947       if(it->second) {
948         delete it->second;
949         it->second = NULL;
950       }
951     }
952 #endif
953   }
954 
955   /** @brief, set the label properties in a plugin */
setLabel(const std::string & label)956   void ImageEffectDescriptor::setLabel(const std::string &label)
957   {
958     validateXMLString(label, true);
959     _effectProps.propSetString(kOfxPropLabel, label);
960   }
961 
962   /** @brief, set the label properties in a plugin */
setLabels(const std::string & label,const std::string & shortLabel,const std::string & longLabel)963   void ImageEffectDescriptor::setLabels(const std::string &label, const std::string &shortLabel, const std::string &longLabel)
964   {
965     setLabel(label);
966     validateXMLString(shortLabel, false);
967     _effectProps.propSetString(kOfxPropShortLabel, shortLabel, false);
968     validateXMLString(longLabel, false);
969     _effectProps.propSetString(kOfxPropLongLabel, longLabel, false);
970   }
971 
972 
973   /** @brief, set the version properties in a plugin */
setVersion(int major,int minor,int micro,int build,const std::string & versionLabel)974   void ImageEffectDescriptor::setVersion(int major, int minor, int micro, int build, const std::string &versionLabel)
975   {
976     _effectProps.propSetInt(kOfxPropVersion, major, 0, false); // introduced in OFX 1.2
977     if (minor || micro || build) {
978       _effectProps.propSetInt(kOfxPropVersion, minor, 1, false); // introduced in OFX 1.2
979       if (micro || build) {
980         _effectProps.propSetInt(kOfxPropVersion, micro, 2, false); // introduced in OFX 1.2
981         if (build) {
982           _effectProps.propSetInt(kOfxPropVersion, build, 3, false); // introduced in OFX 1.2
983         }
984       }
985     }
986     if (!versionLabel.empty()) {
987       validateXMLString(versionLabel, false);
988       _effectProps.propSetString(kOfxPropVersionLabel, versionLabel, false);
989     }
990   }
991 
992   /** @brief Set the plugin grouping */
setPluginGrouping(const std::string & group)993   void ImageEffectDescriptor::setPluginGrouping(const std::string &group)
994   {
995     validateXMLString(group, true);
996     _effectProps.propSetString(kOfxImageEffectPluginPropGrouping, group);
997   }
998 
999   /** @brief Set the plugin description, defaults to "" */
setPluginDescription(const std::string & description,bool validate)1000   void ImageEffectDescriptor::setPluginDescription(const std::string &description, bool validate)
1001   {
1002     if (validate) {
1003       validateXMLString(description, false);
1004     }
1005     _effectProps.propSetString(kOfxPropPluginDescription, description, false); // introduced in OFX 1.2
1006   }
1007 
1008   /** @brief Add a context to those supported */
addSupportedContext(ContextEnum v)1009   void ImageEffectDescriptor::addSupportedContext(ContextEnum v)
1010   {
1011     int n = _effectProps.propGetDimension(kOfxImageEffectPropSupportedContexts);
1012     switch (v)
1013     {
1014     case eContextNone :
1015       break;
1016     case eContextGenerator :
1017       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextGenerator, n);
1018       break;
1019     case eContextFilter :
1020       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextFilter, n);
1021       break;
1022     case eContextTransition :
1023       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextTransition, n);
1024       break;
1025     case eContextPaint :
1026       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextPaint, n);
1027       break;
1028     case eContextGeneral :
1029       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextGeneral, n);
1030       break;
1031     case eContextRetimer :
1032       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextRetimer, n);
1033       break;
1034 #ifdef OFX_EXTENSIONS_TUTTLE
1035     case eContextReader :
1036       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextReader, n);
1037       break;
1038     case eContextWriter :
1039       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextWriter, n);
1040       break;
1041 #endif
1042 #ifdef OFX_EXTENSIONS_NATRON
1043     case eContextTracker:
1044       _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kNatronOfxImageEffectContextTracker, n);
1045       break;
1046 #endif
1047     }
1048   }
1049 
setOverlayInteractDescriptor(EffectOverlayDescriptor * desc)1050   void ImageEffectDescriptor::setOverlayInteractDescriptor(EffectOverlayDescriptor* desc)
1051   {
1052     _overlayDescriptor.reset(desc);
1053     if(OFX::gHostDescription.supportsOverlays && desc->getMainEntry())
1054       _effectProps.propSetPointer(kOfxImageEffectPluginPropOverlayInteractV1, (void*)desc->getMainEntry());
1055   }
1056 
1057 #ifdef OFX_EXTENSIONS_VEGAS
1058 #if defined(WIN32) || defined(WIN64)
setHWNDInteractDescriptor(HWNDInteractDescriptor * desc)1059   void ImageEffectDescriptor::setHWNDInteractDescriptor(HWNDInteractDescriptor* desc)
1060   {
1061     _hwndInteractDescriptor.reset(desc);
1062     if(desc->getMainEntry())
1063       _effectProps.propSetPointer(kOfxImageEffectPluginPropHWndInteractV1, (void*)desc->getMainEntry());
1064   }
1065 #endif // #if defined(WIN32) || defined(WIN64)
1066 #endif
1067 
1068   /** @brief Add a pixel depth to those supported */
addSupportedBitDepth(BitDepthEnum v)1069   void ImageEffectDescriptor::addSupportedBitDepth(BitDepthEnum v)
1070   {
1071     int n = _effectProps.propGetDimension(kOfxImageEffectPropSupportedPixelDepths);
1072     switch(v)
1073     {
1074     case eBitDepthNone :
1075       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthNone  , n);
1076       break;
1077     case eBitDepthUByte :
1078       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthByte  , n);
1079       break;
1080     case eBitDepthUShort :
1081       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthShort  , n);
1082       break;
1083     case eBitDepthHalf :
1084       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthHalf  , n);
1085       break;
1086     case eBitDepthFloat :
1087       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthFloat  , n);
1088       break;
1089 #ifdef OFX_EXTENSIONS_VEGAS
1090     case eBitDepthUByteBGRA :
1091       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthByteBGR  , n);
1092       break;
1093     case eBitDepthUShortBGRA :
1094       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthShortBGR  , n);
1095       break;
1096     case eBitDepthFloatBGRA :
1097       _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthFloatBGR  , n);
1098       break;
1099 #endif
1100     case eBitDepthCustom :
1101       break;
1102     }
1103   }
1104 
1105 #ifdef OFX_SUPPORTS_OPENGLRENDER
1106   /** @brief Add a pixel depth to those supported */
addSupportedOpenGLBitDepth(BitDepthEnum v)1107   void ImageEffectDescriptor::addSupportedOpenGLBitDepth(BitDepthEnum v)
1108   {
1109     int n = _effectProps.propGetDimension(kOfxOpenGLPropPixelDepth);
1110     switch(v)
1111     {
1112     case eBitDepthNone :
1113       _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthNone  , n);
1114       break;
1115     case eBitDepthUByte :
1116       _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthByte  , n);
1117       break;
1118     case eBitDepthUShort :
1119       _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthShort  , n);
1120       break;
1121     case eBitDepthHalf :
1122       _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthHalf  , n);
1123       break;
1124     case eBitDepthFloat :
1125       _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthFloat  , n);
1126       break;
1127     default:
1128       break;
1129     }
1130   }
1131 #endif
1132 
1133 #ifdef OFX_EXTENSIONS_TUTTLE
1134   /** @brief Add a file extension to those supported */
addSupportedExtension(const std::string & extension)1135   void ImageEffectDescriptor::addSupportedExtension(const std::string& extension)
1136   {
1137     validateXMLString(extension, false);
1138     // only Tuttle support this property ( out of standard )
1139     //if( OFX::Private::gHostDescription.hostName == "TuttleOfx" ) {
1140     const int n = _effectProps.propGetDimension(kTuttleOfxImageEffectPropSupportedExtensions, false);
1141     _effectProps.propSetString(kTuttleOfxImageEffectPropSupportedExtensions, extension, n, false);
1142   }
1143 
addSupportedExtensions(const std::vector<std::string> & extensions)1144   void ImageEffectDescriptor::addSupportedExtensions(const std::vector<std::string>& extensions)
1145   {
1146     // only Tuttle support this property ( out of standard )
1147     //if( OFX::Private::gHostDescription.hostName == "TuttleOfx" ) {
1148     int n = _effectProps.propGetDimension(kTuttleOfxImageEffectPropSupportedExtensions, false);
1149 
1150     for (std::vector<std::string>::const_iterator it = extensions.begin(); it != extensions.end(); ++it, ++n) {
1151       validateXMLString(*it, false);
1152       _effectProps.propSetString(kTuttleOfxImageEffectPropSupportedExtensions, *it, n, false);
1153     }
1154   }
1155 
addSupportedExtensions(const char * extensions[])1156   void ImageEffectDescriptor::addSupportedExtensions(const char*extensions[])
1157   {
1158     // only Tuttle support this property ( out of standard )
1159     //if( OFX::Private::gHostDescription.hostName == "TuttleOfx" ) {
1160     int n = _effectProps.propGetDimension(kTuttleOfxImageEffectPropSupportedExtensions, false);
1161 
1162     while (*extensions) {
1163       validateXMLString(*extensions, false);
1164       _effectProps.propSetString(kTuttleOfxImageEffectPropSupportedExtensions, *extensions, n, false);
1165       ++extensions;
1166       ++n;
1167     }
1168   }
1169 
setPluginEvaluation(double evaluation)1170   void ImageEffectDescriptor::setPluginEvaluation(double evaluation)
1171   {
1172     // This property is an extension, so it's optional.
1173     _effectProps.propSetDouble(kTuttleOfxImageEffectPropEvaluation, evaluation, false);
1174   }
1175 #endif
1176 
1177   /** @brief Is the plugin single instance only ? */
setSingleInstance(bool v)1178   void ImageEffectDescriptor::setSingleInstance(bool v)
1179   {
1180     _effectProps.propSetInt(kOfxImageEffectPluginPropSingleInstance, int(v));
1181   }
1182 
1183   /** @brief Does the plugin expect the host to perform per frame SMP threading */
setHostFrameThreading(bool v)1184   void ImageEffectDescriptor::setHostFrameThreading(bool v)
1185   {
1186     _effectProps.propSetInt(kOfxImageEffectPluginPropHostFrameThreading, int(v));
1187   }
1188 
1189   /** @brief Does the plugin support multi resolution images */
setSupportsMultiResolution(bool v)1190   void ImageEffectDescriptor::setSupportsMultiResolution(bool v)
1191   {
1192     _effectProps.propSetInt(kOfxImageEffectPropSupportsMultiResolution, int(v));
1193   }
1194 
1195   /** @brief set the instance to be sequentially renderred, this should have been part of clip preferences! */
setSequentialRender(bool v)1196   void ImageEffectDescriptor::setSequentialRender(bool v)
1197   {
1198     _effectProps.propSetInt(kOfxImageEffectInstancePropSequentialRender, int(v), false); // missing in Resolve
1199   }
1200 
1201   /** @brief Have we informed the host we want to be seqentially renderred ? */
getSequentialRender(void) const1202   bool ImageEffectDescriptor::getSequentialRender(void) const
1203   {
1204     return _effectProps.propGetInt(kOfxImageEffectInstancePropSequentialRender) != 0;
1205   }
1206 
1207   /** @brief Does the plugin support image tiling */
setSupportsTiles(bool v)1208   void ImageEffectDescriptor::setSupportsTiles(bool v)
1209   {
1210     _effectProps.propSetInt(kOfxImageEffectPropSupportsTiles, int(v));
1211   }
1212 
1213   /** @brief Does the plugin handles render quality */
setSupportsRenderQuality(bool v)1214   void ImageEffectDescriptor::setSupportsRenderQuality(bool v)
1215   {
1216     _effectProps.propSetInt(kOfxImageEffectPropRenderQualityDraft, int(v), false); // OFX 1.4+
1217   }
1218 
1219   /** @brief Does the plugin perform temporal clip access */
setTemporalClipAccess(bool v)1220   void ImageEffectDescriptor::setTemporalClipAccess(bool v)
1221   {
1222     _effectProps.propSetInt(kOfxImageEffectPropTemporalClipAccess, int(v));
1223   }
1224 
1225   /** @brief Does the plugin want to have render called twice per frame in all circumanstances for fielded images ? */
setRenderTwiceAlways(bool v)1226   void ImageEffectDescriptor::setRenderTwiceAlways(bool v)
1227   {
1228     _effectProps.propSetInt(kOfxImageEffectPluginPropFieldRenderTwiceAlways, int(v));
1229   }
1230 
1231   /** @brief Does the plugin support inputs and output clips of differing depths */
setSupportsMultipleClipDepths(bool v)1232   void ImageEffectDescriptor::setSupportsMultipleClipDepths(bool v)
1233   {
1234     _effectProps.propSetInt(kOfxImageEffectPropSupportsMultipleClipDepths, int(v));
1235   }
1236 
1237   /** @brief Does the plugin support inputs and output clips of pixel aspect ratios */
setSupportsMultipleClipPARs(bool v)1238   void ImageEffectDescriptor::setSupportsMultipleClipPARs(bool v)
1239   {
1240     _effectProps.propSetInt(kOfxImageEffectPropSupportsMultipleClipPARs, int(v));
1241   }
1242 
1243   /** @brief What kind of thread safety does the plugin have */
setRenderThreadSafety(RenderSafetyEnum v)1244   void ImageEffectDescriptor::setRenderThreadSafety(RenderSafetyEnum v)
1245   {
1246     switch(v)
1247     {
1248     case eRenderUnsafe :
1249       _effectProps.propSetString(kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderUnsafe);
1250       break;
1251     case eRenderInstanceSafe :
1252       _effectProps.propSetString(kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderInstanceSafe);
1253       break;
1254     case eRenderFullySafe :
1255       _effectProps.propSetString(kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderFullySafe);
1256       break;
1257     }
1258   }
1259 
1260 #ifdef OFX_EXTENSIONS_NATRON
1261 
setUsesMultiThreading(bool isMultiThreaded)1262   void ImageEffectDescriptor::setUsesMultiThreading(bool isMultiThreaded)
1263   {
1264     _effectProps.propSetInt(kNatronOfxImageEffectPluginUsesMultipleThread, (int)isMultiThreaded, 0, false);
1265   }
1266 
1267     /*Indicates if the host may add a mask that will be handled automatically.*/
setHostMaskingEnabled(bool enabled)1268   void ImageEffectDescriptor::setHostMaskingEnabled(bool enabled)
1269   {
1270     _effectProps.propSetInt(kNatronOfxImageEffectPropHostMasking, (int)enabled, 0, false);
1271   }
1272 
1273     /*Indicates if the host may add a "Mix" double parameter that will dissolve
1274      between the source image at 0 and the full effect at 1.*/
setHostMixingEnabled(bool enabled)1275   void ImageEffectDescriptor::setHostMixingEnabled(bool enabled)
1276   {
1277     _effectProps.propSetInt(kNatronOfxImageEffectPropHostMixing, (int)enabled, 0, false);
1278   }
1279 
1280   /*Indicates if the plug-in description is written in markdown or plain-text otherwise */
setDescriptionIsMarkdown(bool markdown)1281   void ImageEffectDescriptor::setDescriptionIsMarkdown(bool markdown)
1282   {
1283     _effectProps.propSetInt(kNatronOfxPropDescriptionIsMarkdown, (int)markdown, 0, false);
1284   }
1285 
1286   /*The current selection rectangle drawn by the user on the host viewport.
1287    This property is refreshed whenever calling the kOfxActionInstanceChanged action for the parameter kNatronOfxParamSelectionRectangleState to let the plug-in a change to correctly synchronized its selection.
1288    */
getSelectionRectangle()1289   OfxRectI ImageEffectDescriptor::getSelectionRectangle()
1290   {
1291     OfxRectI bounds = { 0, 0, 0, 0 };
1292     bounds.x1 = _effectProps.propGetInt(kNatronOfxImageEffectSelectionRectangle, 0, false);
1293     bounds.y1 = _effectProps.propGetInt(kNatronOfxImageEffectSelectionRectangle, 1, false);
1294     bounds.x2 = _effectProps.propGetInt(kNatronOfxImageEffectSelectionRectangle, 2, false);
1295     bounds.y2 = _effectProps.propGetInt(kNatronOfxImageEffectSelectionRectangle, 3, false);
1296 
1297     return bounds;
1298   }
1299 
addInViewportParam(const std::string & paramName)1300   void ImageEffectDescriptor::addInViewportParam(const std::string& paramName)
1301   {
1302     int n = _effectProps.propGetDimension(kNatronOfxImageEffectPropInViewerContextParamsOrder, false);
1303     _effectProps.propSetString(kNatronOfxImageEffectPropInViewerContextParamsOrder, paramName, n, false);
1304   }
1305 
setDefaultParamInViewportShortcut(const std::string & paramName,int symbolKey,ShortcutModifierEnum modifiers)1306   void ImageEffectDescriptor::setDefaultParamInViewportShortcut(const std::string& paramName, int symbolKey, ShortcutModifierEnum modifiers)
1307   {
1308     int n = _effectProps.propGetDimension(kNatronOfxImageEffectPropInViewerContextDefaultShortcuts, false);
1309     _effectProps.propSetString(kNatronOfxImageEffectPropInViewerContextDefaultShortcuts, paramName, n, false);
1310     _effectProps.propSetInt(kNatronOfxImageEffectPropInViewerContextShortcutSymbol, symbolKey, n, false);
1311     _effectProps.propSetInt(kNatronOfxImageEffectPropInViewerContextShortcutHasControlModifier, (int)(modifiers & eShortcutModifierCtrl), n, false);
1312     _effectProps.propSetInt(kNatronOfxImageEffectPropInViewerContextShortcutHasShiftModifier, (int)(modifiers & eShortcutModifierShift), n, false);
1313     _effectProps.propSetInt(kNatronOfxImageEffectPropInViewerContextShortcutHasAltModifier, (int)(modifiers & eShortcutModifierAlt), n, false);
1314     _effectProps.propSetInt(kNatronOfxImageEffectPropInViewerContextShortcutHasMetaModifier, (int)(modifiers & eShortcutModifierMeta), n, false);
1315   }
1316 
1317   void
addNativeOverlayInteractForParameters(const std::string & interactType,const std::list<ParamDescriptor * > & parameters)1318   ImageEffectDescriptor::addNativeOverlayInteractForParameters(const std::string& interactType, const std::list<ParamDescriptor*>& parameters)
1319   {
1320     std::stringstream ss;
1321     ss << kNatronNativeOverlayType << '_' << interactType;
1322     for (std::list<ParamDescriptor*>::const_iterator it = parameters.begin(); it != parameters.end(); ++it) {
1323       ss << '_' << kNatronNativeOverlayParameterName << '_';
1324       ss << (*it)->getName();
1325       (*it)->setUseHostNativeOverlayHandle(true); // also set the OFX 1.2 property
1326     }
1327     int n = _effectProps.propGetDimension(kNatronOfxPropNativeOverlays, false);
1328     _effectProps.propSetString(kNatronOfxPropNativeOverlays, ss.str(), n, false);
1329   }
1330 #endif
1331 
1332 #ifdef OFX_EXTENSIONS_VEGAS
setPresetThumbnailHint(VegasPresetThumbnailEnum v)1333   void ImageEffectDescriptor::setPresetThumbnailHint(VegasPresetThumbnailEnum v)
1334   {
1335     switch(v)
1336     {
1337     case eVegasPresetThumbnailDefault :
1338       _effectProps.propSetString(kOfxProbPluginVegasPresetThumbnail, kOfxProbPluginVegasPresetThumbnailDefault, false);
1339       break;
1340     case eVegasPresetThumbnailSolidImage :
1341       _effectProps.propSetString(kOfxProbPluginVegasPresetThumbnail, kOfxProbPluginVegasPresetThumbnailSolidImage, false);
1342       break;
1343     case eVegasPresetThumbnailImageWithAlpha :
1344       _effectProps.propSetString(kOfxProbPluginVegasPresetThumbnail, kOfxProbPluginVegasPresetThumbnailImageWithAlpha, false);
1345       break;
1346     }
1347   }
1348 #endif
1349 
1350 #ifdef OFX_EXTENSIONS_RESOLVE
1351   /** @brief Does the plugin support OpenCL Render */
setSupportsOpenCLRender(bool v)1352   void ImageEffectDescriptor::setSupportsOpenCLRender(bool v)
1353   {
1354       _effectProps.propSetString(kOfxImageEffectPropOpenCLRenderSupported, (v ? "true" : "false"), false);
1355   }
1356 
1357   /** @brief Does the plugin support CUDA Render */
setSupportsCudaRender(bool v)1358   void ImageEffectDescriptor::setSupportsCudaRender(bool v)
1359   {
1360       _effectProps.propSetString(kOfxImageEffectPropCudaRenderSupported, (v ? "true" : "false"), false);
1361   }
1362 #endif
1363 
1364 #ifdef OFX_SUPPORTS_OPENGLRENDER
1365   /** @brief Does the plugin support OpenGL accelerated rendering (but is also capable of CPU rendering) ? */
setSupportsOpenGLRender(bool v)1366   void ImageEffectDescriptor::setSupportsOpenGLRender(bool v) {
1367     if (gHostDescription.supportsOpenGLRender) {
1368       _effectProps.propSetString(kOfxImageEffectPropOpenGLRenderSupported, (v ? "true" : "false"));
1369     }
1370   }
1371 
1372   /** @brief Does the plugin require OpenGL accelerated rendering ? */
setNeedsOpenGLRender(bool v)1373   void ImageEffectDescriptor::setNeedsOpenGLRender(bool v) {
1374     if (gHostDescription.supportsOpenGLRender) {
1375       _effectProps.propSetString(kOfxImageEffectPropOpenGLRenderSupported, (v ? "needed" : "false"));
1376     }
1377   }
1378 
addOpenGLBitDepth(BitDepthEnum v)1379   void ImageEffectDescriptor::addOpenGLBitDepth(BitDepthEnum v) {
1380     int n = _effectProps.propGetDimension(kOfxImageEffectPropSupportedPixelDepths);
1381     std::string value = mapBitDepthEnumToStr(v);
1382     if (!value.empty()) {
1383       _effectProps.propSetString(kOfxOpenGLPropPixelDepth, value, n);
1384     }
1385   }
1386 #endif
1387 
1388 #ifdef OFX_EXTENSIONS_NUKE
1389   /** @brief indicate that a plugin or host can handle transform effects */
setCanTransform(bool v)1390   void ImageEffectDescriptor::setCanTransform(bool v)
1391   {
1392     // the header says this property is on the effect instance, but on Nuke it only exists on the effect descriptor
1393     if (gHostDescription.canTransform) {
1394       _effectProps.propSetInt(kFnOfxImageEffectCanTransform, int(v), false);
1395     }
1396   }
1397 
1398   /** @brief Indicates that a host or plugin can fetch more than a type of image from a clip*/
setIsMultiPlanar(bool v)1399   void ImageEffectDescriptor::setIsMultiPlanar(bool v)
1400   {
1401     if (gHostDescription.isMultiPlanar) {
1402       _effectProps.propSetInt(kFnOfxImageEffectPropMultiPlanar, int(v), false);
1403     }
1404   }
1405 
setPassThroughForNotProcessedPlanes(PassThroughLevelEnum v)1406   void ImageEffectDescriptor::setPassThroughForNotProcessedPlanes(PassThroughLevelEnum v)
1407   {
1408     if (gHostDescription.isMultiPlanar) {
1409         _effectProps.propSetInt(kFnOfxImageEffectPropPassThroughComponents, int(v), false);
1410     }
1411   }
1412 
1413   /** @brief Indicates to the host that the plugin is view aware, in which case it will have to use the view calls*/
setIsViewAware(bool v)1414   void ImageEffectDescriptor::setIsViewAware(bool v)
1415   {
1416     if (OFX::Private::gImageEffectPlaneSuiteV2) {
1417       _effectProps.propSetInt(kFnOfxImageEffectPropViewAware, int(v), false);
1418     }
1419   }
1420 
1421   /** @brief Indicates to the host that a view aware plugin produces the same image independent of the view being rendered*/
setIsViewInvariant(ViewInvarianceLevelEnum v)1422   void ImageEffectDescriptor::setIsViewInvariant(ViewInvarianceLevelEnum v)
1423   {
1424     if (OFX::Private::gImageEffectPlaneSuiteV2) {
1425       _effectProps.propSetInt(kFnOfxImageEffectPropViewInvariance, int(v), false);
1426     }
1427   }
1428 #endif
1429 
1430   /** @brief If the slave param changes the clip preferences need to be re-evaluated */
addClipPreferencesSlaveParam(ParamDescriptor & p)1431   void ImageEffectDescriptor::addClipPreferencesSlaveParam(ParamDescriptor &p)
1432   {
1433     int n = _effectProps.propGetDimension(kOfxImageEffectPropClipPreferencesSlaveParam);
1434     validateXMLString(p.getName(), false);
1435 # ifdef DEBUG
1436     if (p.getPropertySet().propGetInt(kOfxParamPropAnimates)) {
1437       std::cout << "Warning: parameter " << p.getName() << " is a clip preferences slave param but is animated\n";
1438     }
1439 # endif
1440     _effectProps.propSetString(kOfxImageEffectPropClipPreferencesSlaveParam, p.getName(), n);
1441   }
1442 
1443 #ifdef OFX_EXTENSIONS_VEGAS
1444   /** @brief Add a guid to tell Vegas that this plug-in can uplift the guid of that older plug-in */
addVegasUpgradePath(const std::string & guidString)1445   void ImageEffectDescriptor::addVegasUpgradePath(const std::string &guidString)
1446   {
1447     int n = _effectProps.propGetDimension(kOfxImageEffectPropVegasUpliftGUID, false);
1448     validateXMLString(guidString, false);
1449     _effectProps.propSetString(kOfxImageEffectPropVegasUpliftGUID, guidString.c_str(), n, false);
1450   }
1451 
1452   /** @brief sets the path to a help file, defaults to none, must be called at least once */
setHelpPath(const std::string & helpPathString)1453   void ImageEffectDescriptor::setHelpPath(const std::string &helpPathString)
1454   {
1455     validateXMLString(helpPathString, false);
1456     _effectProps.propSetString(kOfxImageEffectPropHelpFile, helpPathString.c_str(), false);
1457   }
1458 
1459   /** @brief sets the context ID to a help file if it's a .chm file, defaults to none, must be called at least once */
setHelpContextID(int helpContextID)1460   void ImageEffectDescriptor::setHelpContextID(int helpContextID)
1461   {
1462     _effectProps.propSetInt(kOfxImageEffectPropHelpContextID, helpContextID, false);
1463   }
1464 #endif
1465 
1466   /** @brief Create a clip, only callable from describe in context */
defineClip(const std::string & name)1467   ClipDescriptor *ImageEffectDescriptor::defineClip(const std::string &name)
1468   {
1469     validateXMLString(name, false);
1470     // do we have the clip already
1471     std::map<std::string, ClipDescriptor *>::const_iterator search;
1472     search = _definedClips.find(name);
1473     if(search != _definedClips.end())
1474       return search->second;
1475 
1476     // no, so make it
1477     OfxPropertySetHandle propSet;
1478     OfxStatus stat = OFX::Private::gEffectSuite->clipDefine(_effectHandle, name.c_str(), &propSet);
1479     throwSuiteStatusException(stat);
1480 
1481     ClipDescriptor *clip = new ClipDescriptor(name, propSet);
1482 
1483     std::map<std::string, ClipDescriptor *>::iterator it = _definedClips.find(name);
1484     if (it != _definedClips.end()) {
1485       delete it->second;
1486       it->second = clip;
1487     } else {
1488       _definedClips[name] = clip;
1489     }
1490     _clipComponentsPropNames[name] = std::string("OfxImageClipPropComponents_") + name;
1491     _clipDepthPropNames[name] = std::string("OfxImageClipPropDepth_") + name;
1492     _clipPARPropNames[name] = std::string("OfxImageClipPropPAR_") + name;
1493     _clipROIPropNames[name] = std::string("OfxImageClipPropRoI_") + name;
1494     _clipFrameRangePropNames[name] = std::string("OfxImageClipPropFrameRange_") + name;
1495 #ifdef OFX_EXTENSIONS_NUKE
1496     _clipPlanesPropNames[name] = std::string(kFnOfxImageEffectActionGetClipComponentsPropString) + name;
1497     _clipFrameViewsPropNames[name] = std::string("OfxImageClipPropFrameRangeView_") + name;
1498 #endif
1499     return clip;
1500   }
1501 
1502 #ifdef OFX_EXTENSIONS_NUKE
1503   /** @brief Create a camera, only callable from describe in context */
defineCamera(const std::string & name)1504   CameraDescriptor *ImageEffectDescriptor::defineCamera(const std::string &name)
1505   {
1506     validateXMLString(name, false);
1507     if (OFX::Private::gCameraSuite == NULL) {
1508       throwHostMissingSuiteException(kNukeOfxCameraSuite);
1509     }
1510     // do we have the camera already
1511     std::map<std::string, CameraDescriptor *>::const_iterator search;
1512     search = _definedCameras.find(name);
1513     if(search != _definedCameras.end())
1514       return search->second;
1515 
1516     // no, so make it
1517     OfxPropertySetHandle propSet;
1518     OfxStatus stat = OFX::Private::gCameraSuite->cameraDefine(_effectHandle, name.c_str(), &propSet);
1519     throwSuiteStatusException(stat);
1520 
1521     CameraDescriptor *camera = new CameraDescriptor(name, propSet);
1522 
1523     std::map<std::string, CameraDescriptor *>::iterator it = _definedCameras.find(name);
1524     if (it != _definedCameras.end()) {
1525       delete it->second;
1526       it->second = camera;
1527     } else {
1528       _definedCameras[name] = camera;
1529     }
1530 
1531     return camera;
1532   }
1533 #endif
1534 
1535 #ifdef OFX_EXTENSIONS_NATRON
1536   /** @brief indicate that a plugin or host can handle distortion function effects */
setCanDistort(bool v)1537   void ImageEffectDescriptor::setCanDistort(bool v)
1538   {
1539     // the header says this property is on the effect instance, but on Nuke it only exists on the effect descriptor
1540     if (gHostDescription.canDistort) {
1541       _effectProps.propSetInt(kOfxImageEffectPropCanDistort, int(v), false);
1542     }
1543   }
1544 
1545   /** @brief indicate if the host may add a channel selector */
setChannelSelector(PixelComponentEnum v)1546   void ImageEffectDescriptor::setChannelSelector(PixelComponentEnum v)
1547   {
1548     if (gHostDescription.supportsChannelSelector) {
1549       _effectProps.propSetString(kNatronOfxImageEffectPropChannelSelector, mapPixelComponentEnumToStr(v), false);
1550     }
1551   }
1552 
1553   /** @brief indicate that this plugin is deprecated */
setIsDeprecated(bool v)1554   void ImageEffectDescriptor::setIsDeprecated(bool v)
1555   {
1556     _effectProps.propSetInt(kNatronOfxImageEffectPropDeprecated, (int)v, false);
1557   }
1558 
1559   /** @brief say whether all the planes listed on the output clip in the getClipComponents action should preferably be rendered
1560    at once or not (e.g: optical flow plug-in that could produce bw/fw planes at once)*/
setRenderAllPlanes(bool enabled)1561   void ImageEffectDescriptor::setRenderAllPlanes(bool enabled)
1562   {
1563     _effectProps.propSetInt(kOfxImageEffectPropRenderAllPlanes, (int)enabled, false);
1564   }
1565 #endif
1566 
1567   ////////////////////////////////////////////////////////////////////////////////
1568   // wraps up an image
ImageBase(OfxPropertySetHandle props)1569   ImageBase::ImageBase(OfxPropertySetHandle props)
1570     : _imageProps(props)
1571   {
1572     OFX::Validation::validateImageBaseProperties(props);
1573 
1574     // and fetch all the properties
1575     _rowBytes         = _imageProps.propGetInt(kOfxImagePropRowBytes);
1576     _pixelAspectRatio = _imageProps.propGetDouble(kOfxImagePropPixelAspectRatio);;
1577 
1578     std::string str  = _imageProps.propGetString(kOfxImageEffectPropComponents);
1579     _pixelComponents = mapStrToPixelComponentEnum(str);
1580 
1581     switch (_pixelComponents) {
1582       case ePixelComponentAlpha:
1583         _pixelComponentCount = 1;
1584         break;
1585       case ePixelComponentNone:
1586         _pixelComponentCount = 0;
1587         break;
1588 #ifdef OFX_EXTENSIONS_NUKE
1589       case ePixelComponentMotionVectors:
1590       case ePixelComponentStereoDisparity:
1591         _pixelComponentCount = 2;
1592         break;
1593 #endif
1594       case ePixelComponentRGB:
1595         _pixelComponentCount = 3;
1596         break;
1597       case ePixelComponentRGBA:
1598         _pixelComponentCount = 4;
1599         break;
1600 #ifdef OFX_EXTENSIONS_NATRON
1601       case ePixelComponentXY:
1602         _pixelComponentCount = 2;
1603         break;
1604 #endif
1605         case ePixelComponentCustom: {
1606 #ifdef OFX_EXTENSIONS_NATRON
1607 
1608         std::string planeName, planeLabel, channelsLabel;
1609         std::vector<std::string> channels;
1610         extractCustomPlane(str, &planeName, &planeLabel, &channelsLabel, &channels);
1611         _pixelComponentCount = (int)channels.size();
1612 #else
1613         _pixelComponentCount = 0;
1614 #endif
1615         }   break;
1616       default:
1617         _pixelComponentCount = 0;
1618         break;
1619     }
1620 
1621     str = _imageProps.propGetString(kOfxImageEffectPropPixelDepth);
1622     _pixelDepth = mapStrToBitDepthEnum(str);
1623 
1624     // compute bytes per pixel
1625     _pixelBytes = _pixelComponentCount;
1626 
1627     switch(_pixelDepth)
1628     {
1629     case eBitDepthNone   : _pixelBytes *= 0; break;
1630     case eBitDepthUByte  : _pixelBytes *= 1; break;
1631     case eBitDepthUShort : _pixelBytes *= 2; break;
1632     case eBitDepthHalf   : _pixelBytes *= 2; break;
1633     case eBitDepthFloat  : _pixelBytes *= 4; break;
1634 #ifdef OFX_EXTENSIONS_VEGAS
1635     case eBitDepthUByteBGRA  : _pixelBytes *= 1; break;
1636     case eBitDepthUShortBGRA : _pixelBytes *= 2; break;
1637     case eBitDepthFloatBGRA  : _pixelBytes *= 4; break;
1638 #endif
1639     case eBitDepthCustom : _pixelBytes *= 0; break;
1640     }
1641 
1642     str = _imageProps.propGetString(kOfxImageEffectPropPreMultiplication);
1643     _preMultiplication =  mapStrToPreMultiplicationEnum(str);
1644 
1645     _regionOfDefinition.x1 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 0);
1646     _regionOfDefinition.y1 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 1);
1647     _regionOfDefinition.x2 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 2);
1648     _regionOfDefinition.y2 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 3);
1649 
1650     _bounds.x1 = _imageProps.propGetInt(kOfxImagePropBounds, 0);
1651     _bounds.y1 = _imageProps.propGetInt(kOfxImagePropBounds, 1);
1652     _bounds.x2 = _imageProps.propGetInt(kOfxImagePropBounds, 2);
1653     _bounds.y2 = _imageProps.propGetInt(kOfxImagePropBounds, 3);
1654 
1655     str = _imageProps.propGetString(kOfxImagePropField);
1656     if(str == kOfxImageFieldNone) {
1657       _field = eFieldNone;
1658     }
1659     else if(str == kOfxImageFieldBoth) {
1660       _field = eFieldBoth;
1661     }
1662     else if(str == kOfxImageFieldLower) {
1663       _field = eFieldLower;
1664     }
1665     else if(str == kOfxImageFieldUpper) {
1666       _field = eFieldLower;
1667     }
1668     else {
1669       OFX::Log::error(true, "Unknown field state '%s' reported on an image", str.c_str());
1670       _field = eFieldNone;
1671     }
1672 
1673     _uniqueID = _imageProps.propGetString(kOfxImagePropUniqueIdentifier);
1674 
1675     _renderScale.x = _renderScale.y = 1.;
1676     _imageProps.propGetDoubleN(kOfxImageEffectPropRenderScale, &_renderScale.x, 2, false);
1677 
1678 #if defined(OFX_EXTENSIONS_NATRON) || defined(OFX_EXTENSIONS_NUKE)
1679     bool gotDistortion = false;
1680 #endif
1681 #ifdef OFX_EXTENSIONS_NATRON
1682 
1683     // Check for distortion function
1684     _inverseDistortionFunction = (OfxInverseDistortionFunctionV1)_imageProps.propGetPointer(kOfxPropInverseDistortionFunction, false);
1685     _inverseDistortionFunctionData = _imageProps.propGetPointer(kOfxPropInverseDistortionFunctionData, false);
1686     if (_inverseDistortionFunction) {
1687       gotDistortion = true;
1688     }
1689 
1690     _transformIsIdentity = true;
1691 
1692     if (!gotDistortion) {
1693       // Check for canonical transform matrix
1694       if (_imageProps.propGetDimension(kOfxPropMatrix3x3, false) != 0) {
1695         gotDistortion = true;
1696 
1697         std::fill(_transform, _transform + 9, 0.);
1698         _imageProps.propGetDoubleN(kOfxPropMatrix3x3, _transform, 9);
1699         // check if the transform is identity (a zero matrix is considered identity)
1700         _transformIsIdentity = (_transform[1] == 0. && _transform[2] == 0. &&
1701                                 _transform[3] == 0. && _transform[5] == 0. &&
1702                                 _transform[6] == 0. && _transform[7] == 0. &&
1703                                 _transform[0] == _transform[2] && _transform[0] == _transform[8]);
1704       }
1705     }
1706 #endif
1707 #ifdef OFX_EXTENSIONS_NUKE
1708     if (!gotDistortion) {
1709       std::fill(_transform, _transform + 9, 0.);
1710       if (_imageProps.propGetDimension(kFnOfxPropMatrix2D, false) != 0) {
1711         // Check for deprecated pixel transform matrix
1712 
1713         std::fill(_transform, _transform + 9, 0.);
1714         _imageProps.propGetDoubleN(kFnOfxPropMatrix2D, _transform, 9);
1715         // check if the transform is identity (a zero matrix is considered identity)
1716         _transformIsIdentity = (_transform[1] == 0. && _transform[2] == 0. &&
1717                                 _transform[3] == 0. && _transform[5] == 0. &&
1718                                 _transform[6] == 0. && _transform[7] == 0. &&
1719                                 _transform[0] == _transform[2] && _transform[0] == _transform[8]);
1720       }
1721     }
1722 #endif
1723   }
1724 
~ImageBase()1725   ImageBase::~ImageBase()
1726   {
1727   }
1728 
1729   ////////////////////////////////////////////////////////////////////////////////
1730   // wraps up an image
Image(OfxPropertySetHandle props)1731   Image::Image(OfxPropertySetHandle props)
1732     : ImageBase(props)
1733   {
1734     OFX::Validation::validateImageProperties(props);
1735 
1736     // and fetch all the properties
1737     // should throw if it is not an image
1738     _pixelData = _imageProps.propGetPointer(kOfxImagePropData);
1739   }
1740 
~Image()1741   Image::~Image()
1742   {
1743     // error are ignored: don't throw in a destructor
1744     OFX::Private::gEffectSuite->clipReleaseImage(_imageProps.propSetHandle());
1745   }
1746 
1747 #ifdef OFX_SUPPORTS_OPENGLRENDER
1748   ////////////////////////////////////////////////////////////////////////////////
1749   // wraps up an OpenGL texture
Texture(OfxPropertySetHandle props)1750   Texture::Texture(OfxPropertySetHandle props)
1751     : ImageBase(props)
1752   {
1753     OFX::Validation::validateTextureProperties(props);
1754 
1755     // should throw if it is not a texture
1756     _index = _imageProps.propGetInt(kOfxImageEffectPropOpenGLTextureIndex);
1757     _target = _imageProps.propGetInt(kOfxImageEffectPropOpenGLTextureTarget);
1758   }
1759 
~Texture()1760   Texture::~Texture()
1761   {
1762     OfxStatus stat = OFX::Private::gOpenGLRenderSuite->clipFreeTexture(_imageProps.propSetHandle());
1763     // ignore status code for exception purposes
1764     (void)stat;
1765   }
1766 #endif
1767 
1768   /** @brief return a pixel pointer
1769 
1770   No attempt made to be uber efficient here.
1771   */
getPixelAddress(int x,int y)1772   void *Image::getPixelAddress(int x, int y)
1773   {
1774     // are we in the image bounds
1775     if(x < _bounds.x1 || x >= _bounds.x2 || y < _bounds.y1 || y >= _bounds.y2 || _pixelBytes == 0)
1776       return 0;
1777 
1778     char *pix = ((char *) _pixelData) + (size_t)(y - _bounds.y1) * _rowBytes;
1779     pix += (x - _bounds.x1) * _pixelBytes;
1780     return (void *) pix;
1781   }
1782 
getPixelAddress(int x,int y) const1783   const void *Image::getPixelAddress(int x, int y) const
1784   {
1785     // are we in the image bounds
1786     if(x < _bounds.x1 || x >= _bounds.x2 || y < _bounds.y1 || y >= _bounds.y2 || _pixelBytes == 0)
1787       return 0;
1788 
1789     const char *pix = ((const char *) _pixelData) + (size_t)(y - _bounds.y1) * _rowBytes;
1790     pix += (x - _bounds.x1) * _pixelBytes;
1791     return (const void *) pix;
1792   }
1793 
getPixelAddressNearest(int x,int y)1794   void *Image::getPixelAddressNearest(int x, int y)
1795   {
1796     x = std::max(_bounds.x1, std::min(x, _bounds.x2 - 1));
1797     y = std::max(_bounds.y1, std::min(y, _bounds.y2 - 1));
1798 
1799     char *pix = ((char *) _pixelData) + (size_t)(y - _bounds.y1) * _rowBytes;
1800     pix += (x - _bounds.x1) * _pixelBytes;
1801     return (void *) pix;
1802   }
1803 
getPixelAddressNearest(int x,int y) const1804   const void *Image::getPixelAddressNearest(int x, int y) const
1805   {
1806     x = std::max(_bounds.x1, std::min(x, _bounds.x2 - 1));
1807     y = std::max(_bounds.y1, std::min(y, _bounds.y2 - 1));
1808 
1809     const char *pix = ((const char *) _pixelData) + (size_t)(y - _bounds.y1) * _rowBytes;
1810     pix += (x - _bounds.x1) * _pixelBytes;
1811     return (const void *) pix;
1812   }
1813 
1814   ////////////////////////////////////////////////////////////////////////////////
1815   // clip instance
1816 
1817   /** @brief hidden constructor */
Clip(ImageEffect * effect,const std::string & name,OfxImageClipHandle handle,OfxPropertySetHandle props)1818   Clip::Clip(ImageEffect *effect, const std::string &name, OfxImageClipHandle handle, OfxPropertySetHandle props)
1819     : _clipName(name)
1820     , _clipProps(props)
1821     , _clipHandle(handle)
1822     , _effect(effect)
1823 #ifdef OFX_EXTENSIONS_VEGAS
1824     , _pixelOrder(ePixelOrderRGBA)
1825 #endif
1826   {
1827     OFX::Validation::validateClipInstanceProperties(_clipProps);
1828   }
1829 
1830 #ifdef OFX_EXTENSIONS_NATRON
1831   /** @brief set the label property */
1832   void
setLabel(const std::string & label)1833     Clip::setLabel(const std::string &label)
1834   {
1835     _clipProps.propSetString(kOfxPropLabel, label, false);
1836   }
1837 
1838   /** @brief set the label properties */
1839   void
setLabels(const std::string & label,const std::string & shortLabel,const std::string & longLabel)1840     Clip::setLabels(const std::string &label, const std::string &shortLabel, const std::string &longLabel)
1841   {
1842     setLabel(label);
1843     _clipProps.propSetString(kOfxPropShortLabel, shortLabel, false);
1844     _clipProps.propSetString(kOfxPropLongLabel, longLabel, false);
1845   }
1846 
1847   /** @brief set the secretness of the param, defaults to false */
setIsSecret(bool v)1848   void Clip::setIsSecret(bool v)
1849   {
1850     _clipProps.propSetInt(kOfxParamPropSecret, v, false);
1851   }
1852 
1853   /** @brief set the clip hint */
1854   void
setHint(const std::string & v)1855     Clip::setHint(const std::string &v)
1856   {
1857     _clipProps.propSetString(kOfxParamPropHint, v, false);
1858   }
1859 
1860   /** @brief set the clip label and hint */
1861   void
setLabelAndHint(const std::string & label,const std::string & hint)1862     Clip::setLabelAndHint(const std::string &label, const std::string &hint)
1863   {
1864     setLabel(label);
1865     setHint(hint);
1866   }
1867 
1868   void
getFormat(OfxRectI & format) const1869     Clip::getFormat(OfxRectI &format) const
1870   {
1871     format.x1 = format.y1 = format.x2 = format.y2 = 0; // default value
1872     _clipProps.propGetIntN(kOfxImageClipPropFormat, &format.x1, 4, false);
1873   }
1874 #endif
1875 
1876   /** @brief fetch the label */
getLabel(std::string & label) const1877   void Clip::getLabel(std::string &label) const
1878   {
1879     label      = _clipProps.propGetString(kOfxPropLabel);
1880   }
1881 
1882   /** @brief fetch the labels */
getLabels(std::string & label,std::string & shortLabel,std::string & longLabel) const1883   void Clip::getLabels(std::string &label, std::string &shortLabel, std::string &longLabel) const
1884   {
1885     getLabel(label);
1886     shortLabel = _clipProps.propGetString(kOfxPropShortLabel, false);
1887     longLabel  = _clipProps.propGetString(kOfxPropLongLabel, false);
1888   }
1889 
1890   /** @brief get the pixel depth */
getPixelDepth(void) const1891   BitDepthEnum Clip::getPixelDepth(void) const
1892   {
1893     std::string str = _clipProps.propGetString(kOfxImageEffectPropPixelDepth);
1894     BitDepthEnum e;
1895     try {
1896       e = mapStrToBitDepthEnum(str);
1897       if(e == eBitDepthNone && isConnected()) {
1898         OFX::Log::error(true, "Clip %s is connected and has no pixel depth.", _clipName.c_str());
1899       }
1900     }
1901     // gone wrong ?
1902     catch(std::invalid_argument) {
1903       OFX::Log::error(true, "Unknown pixel depth property '%s' reported on clip '%s'", str.c_str(), _clipName.c_str());
1904       e = eBitDepthNone;
1905     }
1906     return e;
1907   }
1908 
1909   /** @brief get the components in the image */
getPixelComponents(void) const1910   PixelComponentEnum Clip::getPixelComponents(void) const
1911   {
1912     std::string str = _clipProps.propGetString(kOfxImageEffectPropComponents);
1913     PixelComponentEnum e;
1914     try {
1915       e = mapStrToPixelComponentEnum(str);
1916       if(e == ePixelComponentNone && isConnected()) {
1917         OFX::Log::error(true, "Clip %s is connected and has no pixel component type!", _clipName.c_str());
1918       }
1919     }
1920     // gone wrong ?
1921     catch(std::invalid_argument) {
1922       OFX::Log::error(true, "Unknown  pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str());
1923       e = ePixelComponentNone;
1924     }
1925     return e;
1926   }
1927 
1928   /** @brief get the number of components in the image */
getPixelComponentCount(void) const1929   int Clip::getPixelComponentCount(void) const
1930   {
1931     std::string str = _clipProps.propGetString(kOfxImageEffectPropComponents);
1932     PixelComponentEnum e;
1933     try {
1934       e = mapStrToPixelComponentEnum(str);
1935       if(e == ePixelComponentNone && isConnected()) {
1936         OFX::Log::error(true, "Clip %s is connected and has no pixel component type!", _clipName.c_str());
1937       }
1938     }
1939     // gone wrong ?
1940     catch(std::invalid_argument) {
1941       OFX::Log::error(true, "Unknown  pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str());
1942       e = ePixelComponentNone;
1943     }
1944 
1945     switch (e) {
1946       case ePixelComponentAlpha:
1947         return 1;
1948       case ePixelComponentNone:
1949         return 0;
1950 #ifdef OFX_EXTENSIONS_NUKE
1951       case ePixelComponentMotionVectors:
1952       case ePixelComponentStereoDisparity:
1953         return 2;
1954 #endif
1955       case ePixelComponentRGB:
1956         return 3;
1957       case ePixelComponentRGBA:
1958         return 4;
1959 #ifdef OFX_EXTENSIONS_NATRON
1960       case ePixelComponentXY:
1961         return 2;
1962 #endif
1963         case ePixelComponentCustom: {
1964 #ifdef OFX_EXTENSIONS_NATRON
1965         std::string planeName, planeLabel, channelsLabel;
1966         std::vector<std::string> channels;
1967         extractCustomPlane(str, &planeName, &planeLabel, &channelsLabel, &channels);
1968         return (int)channels.size();
1969 #else
1970         return 0;
1971 #endif
1972         }
1973       default:
1974         return 0;
1975     }
1976   }
1977 
1978   /** @brief what is the actual pixel depth of the clip */
getUnmappedPixelDepth(void) const1979   BitDepthEnum Clip::getUnmappedPixelDepth(void) const
1980   {
1981     std::string str = _clipProps.propGetString(kOfxImageClipPropUnmappedPixelDepth);
1982     BitDepthEnum e;
1983     try {
1984       e = mapStrToBitDepthEnum(str);
1985       if(e == eBitDepthNone && !isConnected()) {
1986         OFX::Log::error(true, "Clip %s is connected and has no unmapped pixel depth.", _clipName.c_str());
1987       }
1988     }
1989     // gone wrong ?
1990     catch(std::invalid_argument) {
1991       OFX::Log::error(true, "Unknown unmapped pixel depth property '%s' reported on clip '%s'", str.c_str(), _clipName.c_str());
1992       e = eBitDepthNone;
1993     }
1994     return e;
1995   }
1996 
1997   /** @brief what is the component type of the clip */
getUnmappedPixelComponents(void) const1998   PixelComponentEnum Clip::getUnmappedPixelComponents(void) const
1999   {
2000     std::string str = _clipProps.propGetString(kOfxImageClipPropUnmappedComponents);
2001     PixelComponentEnum e;
2002     try {
2003       e = mapStrToPixelComponentEnum(str);
2004       if(e == ePixelComponentNone && !isConnected()) {
2005         OFX::Log::error(true, "Clip %s is connected and has no unmapped pixel component type!", _clipName.c_str());
2006       }
2007     }
2008     // gone wrong ?
2009     catch(std::invalid_argument) {
2010       OFX::Log::error(true, "Unknown unmapped pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str());
2011       e = ePixelComponentNone;
2012     }
2013     return e;
2014   }
2015 
2016   /** @brief get the components in the image */
getPreMultiplication(void) const2017   PreMultiplicationEnum Clip::getPreMultiplication(void) const
2018   {
2019     std::string str = _clipProps.propGetString(kOfxImageEffectPropPreMultiplication);
2020     PreMultiplicationEnum e;
2021     try {
2022       e = mapStrToPreMultiplicationEnum(str);
2023     }
2024     // gone wrong ?
2025     catch(std::invalid_argument) {
2026       OFX::Log::error(true, "Unknown premultiplication type '%s' reported on clip %s!", str.c_str(), _clipName.c_str());
2027       e = eImageOpaque;
2028     }
2029     return e;
2030   }
2031 
2032   /** @brief which spatial field comes first temporally */
getFieldOrder(void) const2033   FieldEnum Clip::getFieldOrder(void) const
2034   {
2035     std::string str = _clipProps.propGetString(kOfxImageClipPropFieldOrder);
2036     FieldEnum e;
2037     try {
2038       e = mapStrToFieldEnum(str);
2039       OFX::Log::error(e != eFieldNone && e != eFieldLower && e != eFieldUpper,
2040         "Field order '%s' reported on a clip %s is invalid, it must be none, lower or upper.", str.c_str(), _clipName.c_str());
2041     }
2042     // gone wrong ?
2043     catch(std::invalid_argument) {
2044       OFX::Log::error(true, "Unknown field order '%s' reported on a clip %s.", str.c_str(), _clipName.c_str());
2045       e = eFieldNone;
2046     }
2047     return e;
2048   }
2049 
2050 #ifdef OFX_EXTENSIONS_VEGAS
2051   /** @brief get the pixel order of this image */
getPixelOrder(void) const2052   PixelOrderEnum Clip::getPixelOrder(void) const
2053   {
2054     // only vegas supports this so far, so ignore it if it doesn't work
2055     std::string str = _clipProps.propGetString(kOfxImagePropPixelOrder, false);
2056     PixelOrderEnum e;
2057     if(str == kOfxImagePixelOrderRGBA) {
2058       e = ePixelOrderRGBA;
2059     }
2060     else if(str == kOfxImagePixelOrderBGRA) {
2061       e = ePixelOrderBGRA;
2062     }
2063     else {
2064       e = ePixelOrderRGBA;
2065     }
2066     return e;
2067   }
2068 #endif
2069 
2070   /** @brief is the clip connected */
isConnected(void) const2071   bool Clip::isConnected(void) const
2072   {
2073     return _clipProps.propGetInt(kOfxImageClipPropConnected) != 0;
2074   }
2075 
2076   /** @brief can the clip be continuously sampled */
hasContinuousSamples(void) const2077   bool Clip::hasContinuousSamples(void) const
2078   {
2079     return _clipProps.propGetInt(kOfxImageClipPropContinuousSamples) != 0;
2080   }
2081 
2082   /** @brief get the scale factor that has been applied to this clip */
getPixelAspectRatio(void) const2083   double Clip::getPixelAspectRatio(void) const
2084   {
2085     double par = _clipProps.propGetDouble(kOfxImagePropPixelAspectRatio, false);
2086     if (par != 0.) {
2087       return par;
2088     }
2089     return 1.;  // This error could happen in Eyeon Fusion.
2090   }
2091 
2092   /** @brief get the frame rate, in frames per second on this clip, after any clip preferences have been applied */
getFrameRate(void) const2093   double Clip::getFrameRate(void) const
2094   {
2095     return _clipProps.propGetDouble(kOfxImageEffectPropFrameRate);
2096   }
2097 
2098   /** @brief return the range of frames over which this clip has images, after any clip preferences have been applied */
getFrameRange(void) const2099   OfxRangeD Clip::getFrameRange(void) const
2100   {
2101     OfxRangeD v = {0., 0.};
2102     _clipProps.propGetDoubleN(kOfxImageEffectPropFrameRange, &v.min, 2);
2103     return v;
2104   }
2105 
2106   /** @brief get the frame rate, in frames per second on this clip, before any clip preferences have been applied */
getUnmappedFrameRate(void) const2107   double Clip::getUnmappedFrameRate(void) const
2108   {
2109     return _clipProps.propGetDouble(kOfxImageEffectPropUnmappedFrameRate);
2110   }
2111 
2112   /** @brief return the range of frames over which this clip has images, before any clip preferences have been applied */
getUnmappedFrameRange(void) const2113   OfxRangeD Clip::getUnmappedFrameRange(void) const
2114   {
2115     OfxRangeD v = {0., 0.};
2116     _clipProps.propGetDoubleN(kOfxImageEffectPropUnmappedFrameRange, &v.min, 2);
2117     return v;
2118   }
2119 
2120   /** @brief get the RoD for this clip in the cannonical coordinate system */
getRegionOfDefinition(double t)2121   OfxRectD Clip::getRegionOfDefinition(double t)
2122   {
2123     if ( OFX::IsNaN(t) ) {
2124       throwSuiteStatusException(kOfxStatErrValue);
2125     }
2126     OfxRectD bounds;
2127     OfxStatus stat = OFX::Private::gEffectSuite->clipGetRegionOfDefinition(_clipHandle, t, &bounds);
2128     if(stat == kOfxStatFailed) {
2129       bounds.x1 = bounds.x2 = bounds.y1 = bounds.y2 = 0;
2130     }
2131     throwSuiteStatusException(stat);
2132     return bounds;
2133   }
2134 
2135 #ifdef OFX_EXTENSIONS_RESOLVE
2136   /** @brief is the clip for thumbnail */
isForThumbnail(void) const2137   bool Clip::isForThumbnail(void) const
2138   {
2139       return (_clipProps.propGetInt(kOfxImageClipPropThumbnail, false) != 0);
2140   }
2141 #endif
2142 
2143   /** @brief fetch an image */
fetchImage(double t)2144   Image *Clip::fetchImage(double t)
2145   {
2146     if ( OFX::IsNaN(t) ) {
2147       throwSuiteStatusException(kOfxStatErrValue);
2148     }
2149     OfxPropertySetHandle imageHandle;
2150     OfxStatus stat = OFX::Private::gEffectSuite->clipGetImage(_clipHandle, t, NULL, &imageHandle);
2151     if(stat == kOfxStatFailed) {
2152       return NULL; // not an error, fetched images out of range/region, assume black and transparent
2153     }
2154     else
2155       throwSuiteStatusException(stat);
2156 
2157     return new Image(imageHandle);
2158   }
2159 
2160   /** @brief fetch an image, with a specific region in cannonical coordinates */
fetchImage(double t,const OfxRectD & bounds)2161   Image *Clip::fetchImage(double t, const OfxRectD &bounds)
2162   {
2163     if ( OFX::IsNaN(t) ) {
2164       throwSuiteStatusException(kOfxStatErrValue);
2165     }
2166     OfxPropertySetHandle imageHandle;
2167     OfxStatus stat = OFX::Private::gEffectSuite->clipGetImage(_clipHandle, t, &bounds, &imageHandle);
2168     if(stat == kOfxStatFailed) {
2169       return NULL; // not an error, fetched images out of range/region, assume black and transparent
2170     }
2171     else
2172       throwSuiteStatusException(stat);
2173 
2174     return new Image(imageHandle);
2175   }
2176 
2177 #ifdef OFX_EXTENSIONS_NUKE
getRegionOfDefinition(double t,int view)2178   OfxRectD Clip::getRegionOfDefinition(double t, int view)
2179   {
2180     if ( OFX::IsNaN(t) ) {
2181       throwSuiteStatusException(kOfxStatErrValue);
2182     }
2183     if (!OFX::Private::gImageEffectPlaneSuiteV2) {
2184       return getRegionOfDefinition(t);
2185     }
2186     OfxRectD bounds;
2187     OfxStatus stat = OFX::Private::gImageEffectPlaneSuiteV2->clipGetRegionOfDefinition(_clipHandle, t, view,  &bounds);
2188     if(stat == kOfxStatFailed) {
2189       bounds.x1 = bounds.x2 = bounds.y1 = bounds.y2 = 0;
2190     }
2191     throwSuiteStatusException(stat);
2192     return bounds;
2193   }
2194 
fetchImagePlane(double t,int view,const char * plane)2195   Image* Clip::fetchImagePlane(double t, int view, const char* plane)
2196   {
2197     if ( OFX::IsNaN(t) ) {
2198       throwSuiteStatusException(kOfxStatErrValue);
2199     }
2200     if (!OFX::Private::gImageEffectPlaneSuiteV2 || !OFX::Private::gImageEffectPlaneSuiteV2->clipGetImagePlane) {
2201       throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite"V2");
2202     }
2203     OfxPropertySetHandle imageHandle;
2204     OfxStatus stat = OFX::Private::gImageEffectPlaneSuiteV2->clipGetImagePlane(_clipHandle, t, view, plane, NULL, &imageHandle);
2205     if(stat == kOfxStatFailed) {
2206       return NULL; // not an error, fetched images out of range/region, assume black and transparent
2207     }
2208     else
2209       throwSuiteStatusException(stat);
2210 
2211     return new Image(imageHandle);
2212   }
2213 
fetchImagePlane(double t,const char * plane)2214   Image* Clip::fetchImagePlane(double t, const char* plane)
2215   {
2216     if ( OFX::IsNaN(t) ) {
2217       throwSuiteStatusException(kOfxStatErrValue);
2218     }
2219     if (!OFX::Private::gImageEffectPlaneSuiteV1 || !OFX::Private::gImageEffectPlaneSuiteV1->clipGetImagePlane) {
2220       if (std::string(plane) == kFnOfxImagePlaneColour) {
2221         return fetchImage(t);
2222       }
2223       throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite"V1");
2224       return NULL;
2225     }
2226     OfxPropertySetHandle imageHandle;
2227     OfxStatus stat = OFX::Private::gImageEffectPlaneSuiteV1->clipGetImagePlane(_clipHandle, t, plane, NULL, &imageHandle);
2228     if(stat == kOfxStatFailed) {
2229       return NULL; // not an error, fetched images out of range/region, assume black and transparent
2230     }
2231     else
2232       throwSuiteStatusException(stat);
2233 
2234     return new Image(imageHandle);
2235   }
2236 
fetchImagePlane(double t,int view,const char * plane,const OfxRectD & bounds)2237   Image* Clip::fetchImagePlane(double t, int view, const char* plane, const OfxRectD& bounds)
2238   {
2239     if ( OFX::IsNaN(t) ) {
2240       throwSuiteStatusException(kOfxStatErrValue);
2241     }
2242     if (!OFX::Private::gImageEffectPlaneSuiteV2 || !OFX::Private::gImageEffectPlaneSuiteV2->clipGetImagePlane) {
2243       throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite"V2");
2244     }
2245     OfxPropertySetHandle imageHandle;
2246 
2247     OfxStatus stat = OFX::Private::gImageEffectPlaneSuiteV2->clipGetImagePlane(_clipHandle, t, view, plane, &bounds, &imageHandle);
2248     if(stat == kOfxStatFailed) {
2249       return NULL; // not an error, fetched images out of range/region, assume black and transparent
2250     }
2251     else
2252       throwSuiteStatusException(stat);
2253 
2254     return new Image(imageHandle);
2255   }
2256 
fetchImagePlane(double t,const char * plane,const OfxRectD & bounds)2257   Image* Clip::fetchImagePlane(double t, const char* plane, const OfxRectD& bounds)
2258   {
2259     if ( OFX::IsNaN(t) ) {
2260       throwSuiteStatusException(kOfxStatErrValue);
2261     }
2262     if (!OFX::Private::gImageEffectPlaneSuiteV1 || !OFX::Private::gImageEffectPlaneSuiteV1->clipGetImagePlane) {
2263       if (std::string(plane) == kFnOfxImagePlaneColour) {
2264         return fetchImage(t, bounds);
2265       }
2266       throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite"V1");
2267       return NULL;
2268     }
2269     OfxPropertySetHandle imageHandle;
2270 
2271     OfxStatus stat = OFX::Private::gImageEffectPlaneSuiteV1->clipGetImagePlane(_clipHandle, t, plane, &bounds, &imageHandle);
2272     if(stat == kOfxStatFailed) {
2273       return NULL; // not an error, fetched images out of range/region, assume black and transparent
2274     }
2275     else
2276       throwSuiteStatusException(stat);
2277 
2278     return new Image(imageHandle);
2279   }
2280 
getPlanesPresent(std::vector<std::string> * components) const2281   void Clip::getPlanesPresent(std::vector<std::string>* components) const
2282   {
2283     _clipProps.propGetStringN(kFnOfxImageEffectPropComponentsPresent, components, false);
2284   }
2285 #endif // OFX_EXTENSIONS_NUKE
2286 
2287 #if defined(OFX_EXTENSIONS_VEGAS) || defined(OFX_EXTENSIONS_NUKE)
2288   /** @brief fetch an image */
fetchStereoscopicImage(double t,int view)2289   Image *Clip::fetchStereoscopicImage(double t, int view)
2290   {
2291     if ( OFX::IsNaN(t) ) {
2292       throwSuiteStatusException(kOfxStatErrValue);
2293     }
2294 #ifdef OFX_EXTENSIONS_NUKE
2295     if (OFX::Private::gImageEffectPlaneSuiteV2 && OFX::Private::gImageEffectPlaneSuiteV2->clipGetImagePlane) {
2296       return fetchImagePlane(t, view, kFnOfxImagePlaneColour);
2297     }
2298     else
2299 #endif
2300 #ifdef OFX_EXTENSIONS_VEGAS
2301     if (OFX::Private::gVegasStereoscopicImageSuite && OFX::Private::gVegasStereoscopicImageSuite->clipGetStereoscopicImage) {
2302       OfxPropertySetHandle imageHandle;
2303       OfxStatus stat = OFX::Private::gVegasStereoscopicImageSuite->clipGetStereoscopicImage(_clipHandle, t, view, NULL, &imageHandle);
2304       if (stat == kOfxStatFailed) {
2305         return NULL; // not an error, fetched images out of range/region, assume black and transparent
2306       } else {
2307         throwSuiteStatusException(stat);
2308       }
2309       return new Image(imageHandle);
2310     }
2311     else
2312 #endif
2313     {
2314 #if defined(OFX_EXTENSIONS_VEGAS) && defined(OFX_EXTENSIONS_NUKE)
2315       throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite "V2 & " kOfxVegasStereoscopicImageEffectSuite);
2316 #else
2317 #ifdef OFX_EXTENSIONS_VEGAS
2318       throwHostMissingSuiteException(kOfxVegasStereoscopicImageEffectSuite);
2319 #else
2320       throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite"V2");
2321 #endif
2322 #endif
2323     }
2324     return NULL;
2325   }
2326 #endif
2327 
2328 #ifdef OFX_SUPPORTS_OPENGLRENDER
loadTexture(double t,BitDepthEnum format,const OfxRectD * region)2329   Texture *Clip::loadTexture(double t, BitDepthEnum format, const OfxRectD *region)
2330   {
2331     if ( OFX::IsNaN(t) ) {
2332       throwSuiteStatusException(kOfxStatErrValue);
2333     }
2334     if (!gHostDescription.supportsOpenGLRender) {
2335       throwHostMissingSuiteException("loadTexture");
2336     }
2337     OfxPropertySetHandle hTex;
2338     OfxStatus stat = Private::gOpenGLRenderSuite->clipLoadTexture(_clipHandle, t, format == eBitDepthNone ? NULL : mapBitDepthEnumToStr(format), region, &hTex);
2339     if (stat != kOfxStatOK) {
2340       throwSuiteStatusException(stat);
2341     }
2342     return new Texture(hTex);
2343   }
2344 #endif
2345 
2346 #ifdef OFX_EXTENSIONS_NUKE
2347   ////////////////////////////////////////////////////////////////////////////////
2348   // camera instance
2349 
2350   /** @brief hidden constructor */
Camera(ImageEffect * effect,const std::string & name,NukeOfxCameraHandle handle,OfxPropertySetHandle props)2351   Camera::Camera(ImageEffect *effect, const std::string &name, NukeOfxCameraHandle handle, OfxPropertySetHandle props)
2352     : _cameraName(name)
2353     , _cameraProps(props)
2354     , _cameraHandle(handle)
2355     , _effect(effect)
2356   {
2357     OFX::Validation::validateCameraInstanceProperties(_cameraProps);
2358   }
2359 
2360 #ifdef OFX_EXTENSIONS_NATRON
2361   /** @brief set the label property */
2362   void
setLabel(const std::string & label)2363     Camera::setLabel(const std::string &label)
2364   {
2365     _cameraProps.propSetString(kOfxPropLabel, label, false);
2366   }
2367 
2368   /** @brief set the label properties */
2369   void
setLabels(const std::string & label,const std::string & shortLabel,const std::string & longLabel)2370     Camera::setLabels(const std::string &label, const std::string &shortLabel, const std::string &longLabel)
2371   {
2372     setLabel(label);
2373     _cameraProps.propSetString(kOfxPropShortLabel, shortLabel, false);
2374     _cameraProps.propSetString(kOfxPropLongLabel, longLabel, false);
2375   }
2376 
2377   /** @brief set the secretness of the param, defaults to false */
setIsSecret(bool v)2378   void Camera::setIsSecret(bool v)
2379   {
2380     _cameraProps.propSetInt(kOfxParamPropSecret, v, false);
2381   }
2382 
2383   /** @brief set the camera hint */
2384   void
setHint(const std::string & v)2385     Camera::setHint(const std::string &v)
2386   {
2387     _cameraProps.propSetString(kOfxParamPropHint, v, false);
2388   }
2389 
2390   /** @brief set the camera label and hint */
2391   void
setLabelAndHint(const std::string & label,const std::string & hint)2392     Camera::setLabelAndHint(const std::string &label, const std::string &hint)
2393   {
2394     setLabel(label);
2395     setHint(hint);
2396   }
2397 #endif
2398 
2399   /** @brief fetch the label */
getLabel(std::string & label) const2400   void Camera::getLabel(std::string &label) const
2401   {
2402     label      = _cameraProps.propGetString(kOfxPropLabel);
2403   }
2404 
2405   /** @brief fetch the labels */
getLabels(std::string & label,std::string & shortLabel,std::string & longLabel) const2406   void Camera::getLabels(std::string &label, std::string &shortLabel, std::string &longLabel) const
2407   {
2408     getLabel(label);
2409     shortLabel = _cameraProps.propGetString(kOfxPropShortLabel, false);
2410     longLabel  = _cameraProps.propGetString(kOfxPropLongLabel, false);
2411   }
2412 
2413 
2414   /** @brief is the camera connected */
isConnected(void) const2415   bool Camera::isConnected(void) const
2416   {
2417     return _cameraProps.propGetInt(kOfxImageClipPropConnected) != 0;
2418   }
2419 
2420   /** @brief Get an arbitrary camera parameter for a given time and view */
getParameter(const char * paramName,double time,int view,double * baseReturnAddress,int returnSize) const2421   void Camera::getParameter(const char* paramName, double time, int view, double* baseReturnAddress, int returnSize) const
2422   {
2423     OfxStatus stat = OFX::Private::gCameraSuite->cameraGetParameter(_cameraHandle, paramName, time, view, baseReturnAddress, returnSize);
2424     throwSuiteStatusException(stat);
2425   }
2426 
2427 #endif // OFX_EXTENSIONS_NUKE
2428 
2429 
2430   ////////////////////////////////////////////////////////////////////////////////
2431   /// image effect
2432 
2433   /** @brief ctor */
ImageEffect(OfxImageEffectHandle handle)2434   ImageEffect::ImageEffect(OfxImageEffectHandle handle)
2435     : _effectHandle(handle)
2436     , _effectProps(NULL)
2437     , _context(eContextNone)
2438     , _progressStartSuccess(false)
2439   {
2440     // get the property handle
2441     _effectProps = OFX::Private::fetchEffectProps(handle);
2442 
2443     // Set this as the instance data pointer on the effect handle
2444     _effectProps.propSetPointer(kOfxPropInstanceData, this);
2445 
2446     // validate the plugin instance
2447     OFX::Validation::validatePluginInstanceProperties(_effectProps);
2448 
2449     // fetch the context
2450     std::string ctxt = _effectProps.propGetString(kOfxImageEffectPropContext);
2451     _context = mapToContextEnum(ctxt);
2452 
2453     // the param set daddy-oh
2454     OfxParamSetHandle paramSet;
2455     OfxStatus stat = OFX::Private::gEffectSuite->getParamSet(handle, &paramSet);
2456     throwSuiteStatusException(stat);
2457     setParamSetHandle(paramSet);
2458 
2459   }
2460 
2461   /** @brief dtor */
~ImageEffect()2462   ImageEffect::~ImageEffect()
2463   {
2464     // clobber the instance data property on the effect handle
2465     _effectProps.propSetPointer(kOfxPropInstanceData, 0, false);
2466 
2467     // delete any clip instances we may have constructed
2468     std::map<std::string, Clip *>::iterator iter;
2469     for(iter = _fetchedClips.begin(); iter != _fetchedClips.end(); ++iter) {
2470       if(iter->second) {
2471         delete iter->second;
2472         iter->second = NULL;
2473       }
2474     }
2475   }
2476 
2477   /** @brief the context this effect was instantiate in */
getContext(void) const2478   ContextEnum ImageEffect::getContext(void) const
2479   {
2480     return _context;
2481   }
2482 
2483 #ifdef OFX_EXTENSIONS_VEGAS
2484   /** @brief the Vegas context this effect exists in */
getVegasContext(void)2485   VegasContextEnum ImageEffect::getVegasContext(void)
2486   {
2487     // fetch the context
2488     std::string ctxt = _effectProps.propGetString(kOfxImageEffectPropVegasContext, false);
2489     return mapToVegasContextEnum(ctxt);
2490   }
2491 #endif
2492 
2493   /** @brief size of the project */
getProjectSize(void) const2494   OfxPointD ImageEffect::getProjectSize(void) const
2495   {
2496     OfxPointD v = {0., 0.};
2497     _effectProps.propGetDoubleN(kOfxImageEffectPropProjectSize, &v.x, 2);
2498     return v;
2499   }
2500 
2501   /** @brief origin of the project */
getProjectOffset(void) const2502   OfxPointD ImageEffect::getProjectOffset(void) const
2503   {
2504     OfxPointD v = {0., 0.};
2505     _effectProps.propGetDoubleN(kOfxImageEffectPropProjectOffset, &v.x, 2);
2506     return v;
2507   }
2508 
2509   /** @brief extent of the project */
getProjectExtent(void) const2510   OfxPointD ImageEffect::getProjectExtent(void) const
2511   {
2512     OfxPointD v = {0., 0.};
2513     _effectProps.propGetDoubleN(kOfxImageEffectPropProjectExtent, &v.x, 2);
2514     return v;
2515   }
2516 
2517   /** @brief pixel aspect ratio of the project */
getProjectPixelAspectRatio(void) const2518   double ImageEffect::getProjectPixelAspectRatio(void) const
2519   {
2520     return _effectProps.propGetDouble(kOfxImageEffectPropProjectPixelAspectRatio, 0);
2521   }
2522 
2523   /** @brief how long does the effect last */
getEffectDuration(void) const2524   double ImageEffect::getEffectDuration(void) const
2525   {
2526     return _effectProps.propGetDouble(kOfxImageEffectInstancePropEffectDuration, 0);
2527   }
2528 
2529   /** @brief the frame rate of the project */
getFrameRate(void) const2530   double ImageEffect::getFrameRate(void) const
2531   {
2532     return _effectProps.propGetDouble(kOfxImageEffectPropFrameRate, 0);
2533   }
2534 
2535   /** @brief is the instance currently being interacted with */
isInteractive(void) const2536   bool ImageEffect::isInteractive(void) const
2537   {
2538     return _effectProps.propGetInt(kOfxPropIsInteractive) != 0;
2539   }
2540 
2541   /** @brief set the instance to be sequentially renderred, this should have been part of clip preferences! */
setSequentialRender(bool v)2542   void ImageEffect::setSequentialRender(bool v)
2543   {
2544     _effectProps.propSetInt(kOfxImageEffectInstancePropSequentialRender, int(v));
2545   }
2546 
2547   /** @brief Have we informed the host we want to be seqentially renderred ? */
getSequentialRender(void) const2548   bool ImageEffect::getSequentialRender(void) const
2549   {
2550     return _effectProps.propGetInt(kOfxImageEffectInstancePropSequentialRender) != 0;
2551   }
2552 
2553   /** @brief Does the plugin support image tiling ? Can only be called from changedParam or changedClip. */
setSupportsTiles(bool v)2554   void ImageEffect::setSupportsTiles(bool v)
2555   {
2556     _effectProps.propSetInt(kOfxImageEffectPropSupportsTiles, int(v), false); // read/write from OFX 1.4
2557   }
2558 
2559   /** @brief Have we informed the host we support image tiling ? */
getSupportsTiles(void) const2560   bool ImageEffect::getSupportsTiles(void) const
2561   {
2562     return _effectProps.propGetInt(kOfxImageEffectPropSupportsTiles) != 0;
2563   }
2564 
2565 #ifdef OFX_EXTENSIONS_NATRON
2566   void
setCanDistort(bool v)2567   ImageEffect::setCanDistort(bool v)
2568   {
2569     _effectProps.propSetInt(kOfxImageEffectPropCanDistort, int(v), false);
2570   }
2571 
2572   bool
getCanDistort() const2573   ImageEffect::getCanDistort() const
2574   {
2575     return _effectProps.propGetInt(kOfxImageEffectPropCanDistort, false) != 0;
2576   }
2577 
getExtraneousPlanesCreated(std::vector<std::string> * planes) const2578   void ImageEffect::getExtraneousPlanesCreated(std::vector<std::string>* planes) const
2579   {
2580       _effectProps.propGetStringN(kNatronOfxExtraCreatedPlanes, planes, false);
2581   }
2582 #endif
2583 
2584 #ifdef OFX_EXTENSIONS_NUKE
2585   void
setCanTransform(bool v)2586   ImageEffect::setCanTransform(bool v)
2587   {
2588     _effectProps.propSetInt(kFnOfxImageEffectCanTransform, int(v), false);
2589   }
2590 
2591   bool
getCanTransform() const2592   ImageEffect::getCanTransform() const
2593   {
2594     return _effectProps.propGetInt(kFnOfxImageEffectCanTransform, false) != 0;
2595   }
2596 #endif
2597 
2598 #ifdef OFX_SUPPORTS_OPENGLRENDER
2599   /** @brief Does the plugin support OpenGL accelerated rendering (but is also capable of CPU rendering) ? Can only be called from changedParam or changedClip (OFX 1.4). */
setSupportsOpenGLRender(bool v)2600   void ImageEffect::setSupportsOpenGLRender(bool v) {
2601     if (gHostDescription.supportsOpenGLRender) {
2602       _effectProps.propSetString(kOfxImageEffectPropOpenGLRenderSupported, (v ? "true" : "false"), false); // read/write from OFX 1.4
2603     }
2604   }
2605 #endif
2606 
2607 
2608   /** @brief notify host that the internal data structures need syncing back to parameters for persistence and so on.  This is reset by the host after calling SyncPrivateData. */
setParamSetNeedsSyncing()2609   void ImageEffect::setParamSetNeedsSyncing()
2610   {
2611     _effectProps.propSetInt(kOfxPropParamSetNeedsSyncing, 1, false); // introduced in OFX 1.2
2612   }
2613 
sendMessage(OFX::Message::MessageTypeEnum type,const std::string & id,const std::string & msg,bool throwIfMissing)2614   OFX::Message::MessageReplyEnum ImageEffect::sendMessage(OFX::Message::MessageTypeEnum type, const std::string& id, const std::string& msg, bool throwIfMissing)
2615   {
2616     if (!OFX::Private::gMessageSuite || !OFX::Private::gMessageSuite->message) {
2617       if (throwIfMissing) {
2618         OFX::Log::error(true, "OfxMessageSuiteV1::message() not available, could not send message \"%s\"/\"%s\"", id.c_str(), msg.c_str());
2619         throwHostMissingSuiteException("message");
2620       } else {
2621         OFX::Log::warning(true, "OfxMessageSuiteV1::message() not available, could not send message \"%s\"/\"%s\"", id.c_str(), msg.c_str());
2622       }
2623       return OFX::Message::eMessageReplyFailed;
2624     }
2625     OfxStatus stat = OFX::Private::gMessageSuite->message(_effectHandle, mapMessageTypeEnumToStr(type), id.c_str(), msg.c_str());
2626     return mapToMessageReplyEnum(stat);
2627   }
2628 
setPersistentMessage(OFX::Message::MessageTypeEnum type,const std::string & id,const std::string & msg,bool throwIfMissing)2629   OFX::Message::MessageReplyEnum ImageEffect::setPersistentMessage(OFX::Message::MessageTypeEnum type, const std::string& id, const std::string& msg, bool throwIfMissing)
2630   {
2631     if (!OFX::Private::gMessageSuiteV2 || !OFX::Private::gMessageSuiteV2->setPersistentMessage) {
2632       if (throwIfMissing) {
2633         OFX::Log::error(true, "OfxMessageSuiteV2::setPersistentMessage() not available, could not set message \"%s\"/\"%s\"", id.c_str(), msg.c_str());
2634         throwHostMissingSuiteException("setPersistentMessage");
2635       } else {
2636         OFX::Log::warning(true, "OfxMessageSuiteV2::setPersistentMessage() not available, could not set message \"%s\"/\"%s\"", id.c_str(), msg.c_str());
2637       }
2638       return OFX::Message::eMessageReplyFailed;
2639     }
2640     OfxStatus stat = OFX::Private::gMessageSuiteV2->setPersistentMessage(_effectHandle, mapMessageTypeEnumToStr(type), id.c_str(), msg.c_str());
2641     return mapToMessageReplyEnum(stat);
2642   }
2643 
clearPersistentMessage(bool throwIfMissing)2644   OFX::Message::MessageReplyEnum ImageEffect::clearPersistentMessage(bool throwIfMissing)
2645   {
2646     if (!OFX::Private::gMessageSuiteV2 || !OFX::Private::gMessageSuiteV2->clearPersistentMessage) {
2647       if (throwIfMissing) {
2648         OFX::Log::error(true, "OfxMessageSuiteV2::clearPersistentMessage() not available");
2649         throwHostMissingSuiteException("clearPersistentMessage");
2650       } else {
2651         OFX::Log::warning(true, "OfxMessageSuiteV2::clearPersistentMessage() not available");
2652       }
2653       return OFX::Message::eMessageReplyFailed;
2654     }
2655     OfxStatus stat = OFX::Private::gMessageSuiteV2->clearPersistentMessage(_effectHandle);
2656     return mapToMessageReplyEnum(stat);
2657   }
2658 
2659 #ifdef OFX_SUPPORTS_DIALOG
requestDialog(OfxPropertySetHandle inArgs,void * instanceData)2660   void ImageEffect::requestDialog(OfxPropertySetHandle inArgs, void *instanceData)
2661   {
2662     if(!(OFX::Private::gDialogSuiteV1 && OFX::Private::gDialogSuiteV1->requestDialog) &&
2663        !(OFX::Private::gDialogSuiteV2 && OFX::Private::gDialogSuiteV2->requestDialog)){ throwHostMissingSuiteException("requestDialog"); }
2664     OfxStatus stat;
2665     if (OFX::Private::gDialogSuiteV2) {
2666       stat = OFX::Private::gDialogSuiteV2->requestDialog(_effectHandle, inArgs, instanceData);
2667     } else {
2668       stat = OFX::Private::gDialogSuiteV1->requestDialog(instanceData);
2669     }
2670     throwSuiteStatusException(stat);
2671   }
2672 
notifyRedrawPending(OfxPropertySetHandle inArgs)2673   void ImageEffect::notifyRedrawPending(OfxPropertySetHandle inArgs)
2674   {
2675     if(!(OFX::Private::gDialogSuiteV1 && OFX::Private::gDialogSuiteV1->notifyRedrawPending) &&
2676        !(OFX::Private::gDialogSuiteV2 && OFX::Private::gDialogSuiteV2->notifyRedrawPending))
2677       { throwHostMissingSuiteException("notifyRedrawPending"); }
2678     OfxStatus stat;
2679     if (OFX::Private::gDialogSuiteV2) {
2680       stat = OFX::Private::gDialogSuiteV2->notifyRedrawPending(_effectHandle, inArgs);
2681     } else {
2682       stat = OFX::Private::gDialogSuiteV1->notifyRedrawPending();
2683     }
2684     throwSuiteStatusException(stat);
2685   }
2686 #endif
2687 
2688   /** @brief Fetch the named clip from this instance */
fetchClip(const std::string & name)2689   Clip *ImageEffect::fetchClip(const std::string &name)
2690   {
2691     // do we have the clip already
2692     std::map<std::string, Clip *>::const_iterator search;
2693     search = _fetchedClips.find(name);
2694     if(search != _fetchedClips.end())
2695       return search->second;
2696 
2697     // fetch the property set handle of the effect
2698     OfxImageClipHandle clipHandle = 0;
2699     OfxPropertySetHandle propHandle = 0;
2700     OfxStatus stat = OFX::Private::gEffectSuite->clipGetHandle(_effectHandle, name.c_str(), &clipHandle, &propHandle);
2701     throwSuiteStatusException(stat);
2702 
2703     // and make one
2704     Clip *newClip = new Clip(this, name, clipHandle, propHandle);
2705 
2706     // add it in
2707     std::map<std::string, Clip *>::iterator it = _fetchedClips.find(name);
2708     if (it != _fetchedClips.end()) {
2709       delete it->second;
2710       it->second = newClip;
2711     } else {
2712       _fetchedClips[name] = newClip;
2713     }
2714 
2715     // return it
2716     return newClip;
2717   }
2718 
2719 #ifdef OFX_EXTENSIONS_NUKE
2720   /** @brief Fetch the named camera from this instance */
fetchCamera(const std::string & name)2721   Camera* ImageEffect::fetchCamera(const std::string& name)
2722   {
2723     if (!OFX::Private::gCameraSuite) {
2724       throwHostMissingSuiteException(kNukeOfxCameraSuite);
2725     }
2726     // do we have the camera already
2727     std::map<std::string, Camera *>::const_iterator search;
2728     search = _fetchedCameras.find(name);
2729     if(search != _fetchedCameras.end())
2730       return search->second;
2731 
2732     // fetch the property set handle of the effect
2733     NukeOfxCameraHandle cameraHandle = 0;
2734     OfxPropertySetHandle propHandle = 0;
2735     OfxStatus stat = OFX::Private::gCameraSuite->cameraGetHandle(_effectHandle, name.c_str(), &cameraHandle, &propHandle);
2736     throwSuiteStatusException(stat);
2737 
2738     // and make one
2739     Camera *newCamera = new Camera(this, name, cameraHandle, propHandle);
2740 
2741     // add it in
2742     std::map<std::string, Camera *>::iterator it = _fetchedCameras.find(name);
2743     if (it != _fetchedCameras.end()) {
2744       delete it->second;
2745       it->second = newCamera;
2746     } else {
2747       _fetchedCameras[name] = newCamera;
2748     }
2749 
2750     // return it
2751     return newCamera;
2752   }
2753 #endif
2754 
2755   /** @brief does the host want us to abort rendering? */
abort(void) const2756   bool ImageEffect::abort(void) const
2757   {
2758     return OFX::Private::gEffectSuite->abort(_effectHandle) != 0;
2759   }
2760 
2761   /** @brief adds a new interact to the set of interacts open on this effect */
addOverlayInteract(OverlayInteract * interact)2762   void ImageEffect::addOverlayInteract(OverlayInteract *interact)
2763   {
2764     // do we have it already ?
2765     std::list<OverlayInteract *>::iterator i;
2766     i = std::find(_overlayInteracts.begin(), _overlayInteracts.end(), interact);
2767 
2768     // we don't, put it in there
2769     if(i == _overlayInteracts.end()) {
2770       // we have a new one to add in here
2771       _overlayInteracts.push_back(interact);
2772     }
2773   }
2774 
2775   /** @brief removes an interact to the set of interacts open on this effect */
removeOverlayInteract(OverlayInteract * interact)2776   void ImageEffect::removeOverlayInteract(OverlayInteract *interact)
2777   {
2778     // find it
2779     std::list<OverlayInteract *>::iterator i;
2780     i = std::find(_overlayInteracts.begin(), _overlayInteracts.end(), interact);
2781 
2782     // and remove it
2783     if(i != _overlayInteracts.end()) {
2784       _overlayInteracts.erase(i);
2785     }
2786   }
2787 
2788 #ifdef OFX_SUPPORTS_OPENGLRENDER
flushOpenGLResources(void)2789     bool ImageEffect::flushOpenGLResources(void)
2790     {
2791       if (!gHostDescription.supportsOpenGLRender) {
2792         return false;
2793       }
2794       return Private::gOpenGLRenderSuite->flushResources() == kOfxStatOK;
2795     }
2796 #endif
2797 
2798   ////////////////////////////////////////////////////////////////////////////////
2799   // below are the default members for the base image effect
2800 
2801 
2802   /** @brief client is identity function, returns the clip and time for the identity function
2803   */
isIdentity(const IsIdentityArguments &,Clip * &,double &,int &,std::string &)2804   bool ImageEffect::isIdentity(const IsIdentityArguments &/*args*/, Clip * &/*identityClip*/, double &/*identityTime*/
2805 #ifdef OFX_EXTENSIONS_NUKE
2806                                , int& /*view*/
2807                                , std::string& /*plane*/
2808 #endif
2809   )
2810   {
2811     return false; // by default, we are not an identity operation
2812   }
2813 
2814   /** @brief The get RoD action */
getRegionOfDefinition(const RegionOfDefinitionArguments &,OfxRectD &)2815   bool ImageEffect::getRegionOfDefinition(const RegionOfDefinitionArguments &/*args*/, OfxRectD &/*rod*/)
2816   {
2817     return false; // by default, we are not setting the RoD
2818   }
2819 
2820   /** @brief the get RoI action */
getRegionsOfInterest(const RegionsOfInterestArguments &,RegionOfInterestSetter &)2821   void ImageEffect::getRegionsOfInterest(const RegionsOfInterestArguments &/*args*/, RegionOfInterestSetter &/*rois*/)
2822   {
2823     // fa niente
2824   }
2825 
2826   /** @brief the get frames needed action */
getFramesNeeded(const FramesNeededArguments &,FramesNeededSetter &)2827   void ImageEffect::getFramesNeeded(const FramesNeededArguments &/*args*/, FramesNeededSetter &/*frames*/)
2828   {
2829     // fa niente
2830   }
2831 
2832   /** @brief client begin sequence render function */
beginSequenceRender(const BeginSequenceRenderArguments &)2833   void ImageEffect::beginSequenceRender(const BeginSequenceRenderArguments &/*args*/)
2834   {
2835     // fa niente
2836   }
2837 
2838   /** @brief client end sequence render function, this is one of the few that must be set */
endSequenceRender(const EndSequenceRenderArguments &)2839   void ImageEffect::endSequenceRender(const EndSequenceRenderArguments &/*args*/)
2840   {
2841     // fa niente
2842   }
2843 
2844   /** @brief The purge caches action, a request for an instance to free up as much memory as possible in low memory situations */
purgeCaches(void)2845   void ImageEffect::purgeCaches(void)
2846   {
2847     // fa niente
2848   }
2849 
2850   /** @brief The sync private data action, called when the effect needs to sync any private data to persistent parameters */
syncPrivateData(void)2851   void ImageEffect::syncPrivateData(void)
2852   {
2853     // fa niente
2854   }
2855 
2856   /** @brief get the clip preferences */
getClipPreferences(ClipPreferencesSetter &)2857   void ImageEffect::getClipPreferences(ClipPreferencesSetter &/*clipPreferences*/)
2858   {
2859     // fa niente
2860   }
2861 
isChromaticComponent(const std::string & str)2862   static bool isChromaticComponent(const std::string &str)
2863   {
2864     if(str == kOfxImageComponentRGBA)
2865       return true;
2866     if(str == kOfxImageComponentRGB)
2867       return true;
2868     if(str == kOfxImageComponentAlpha)
2869       return true;
2870 #ifdef OFX_EXTENSIONS_NATRON
2871     if(str == kNatronOfxImageComponentXY)
2872       return true;
2873 #endif
2874     return false;
2875   }
2876 
findMostChromaticComponents(const std::string & a,const std::string & b)2877   static const std::string &findMostChromaticComponents(const std::string &a, const std::string &b)
2878   {
2879     if(a == kOfxImageComponentNone)
2880       return b;
2881     if(a == kOfxImageComponentRGBA)
2882       return a;
2883     if(b == kOfxImageComponentRGBA)
2884       return b;
2885     if(a == kOfxImageComponentRGB)
2886       return a;
2887     if(b == kOfxImageComponentRGB)
2888       return b;
2889     return a;
2890   }
2891 
getSupportedComponents(const PropertySet & props,std::set<std::string> * supportedComponents)2892   static void getSupportedComponents(const PropertySet& props, std::set<std::string>* supportedComponents)
2893   {
2894     int nDims = props.propGetDimension(kOfxImageEffectPropSupportedComponents);
2895     for (int i = 0; i < nDims; ++i) {
2896       supportedComponents->insert(props.propGetString(kOfxImageEffectPropSupportedComponents, i));
2897     }
2898   }
2899 
isSupportedComponent(const std::set<std::string> & supportedComponents,const std::string & p)2900   static bool isSupportedComponent(const std::set<std::string>& supportedComponents, const std::string& p) {
2901     return supportedComponents.find(p) != supportedComponents.end();
2902   }
2903 
getSupportedPixelDepths(const PropertySet & props,std::set<std::string> * supportedPixelDepths)2904   static void getSupportedPixelDepths(const PropertySet& props, std::set<std::string>* supportedPixelDepths)
2905   {
2906     int nDims = props.propGetDimension(kOfxImageEffectPropSupportedPixelDepths);
2907     for (int i = 0; i < nDims; ++i) {
2908       supportedPixelDepths->insert(props.propGetString(kOfxImageEffectPropSupportedPixelDepths, i));
2909     }
2910   }
2911 
isSupportedPixelDepth(const std::set<std::string> & supportedPixelDepths,const std::string & p)2912   static bool isSupportedPixelDepth(const std::set<std::string>& supportedPixelDepths, const std::string& p) {
2913     return supportedPixelDepths.find(p) != supportedPixelDepths.end();
2914   }
2915 
findBestSupportedComponent(const std::set<std::string> & supportedComponents,const std::string & s)2916   static const std::string& findBestSupportedComponent(const  std::set<std::string>& supportedComponents, const std::string &s)
2917   {
2918     static const std::string none(kOfxImageComponentNone);
2919     static const std::string rgba(kOfxImageComponentRGBA);
2920     static const std::string rgb(kOfxImageComponentRGB);
2921     static const std::string alpha(kOfxImageComponentAlpha);
2922 #ifdef OFX_EXTENSIONS_NATRON
2923     static const std::string xy(kNatronOfxImageComponentXY);
2924 #endif
2925     /// is it there
2926     if(isSupportedComponent(supportedComponents, s))
2927       return s;
2928 
2929 #ifdef OFX_EXTENSIONS_NATRON
2930     if (s == xy) {
2931       if (isSupportedComponent(supportedComponents, rgb)) {
2932         return rgb;
2933       } else if (isSupportedComponent(supportedComponents, rgba)) {
2934         return rgba;
2935       } else if (isSupportedComponent(supportedComponents, alpha)) {
2936         return alpha;
2937       }
2938     }
2939 #endif
2940 
2941     /// were we fed some custom non chromatic component by getUnmappedComponents? Return it.
2942     /// we should never be here mind, so a bit weird
2943     if(!isChromaticComponent(s))
2944       return s;
2945 
2946     /// Means we have RGBA or Alpha being passed in and the clip
2947     /// only supports the other one, so return that
2948     if(s == rgba) {
2949       if(isSupportedComponent(supportedComponents, rgb))
2950         return rgb;
2951       if(isSupportedComponent(supportedComponents, alpha))
2952         return alpha;
2953     } else if(s == alpha) {
2954       if(isSupportedComponent(supportedComponents, rgba))
2955         return rgba;
2956       if(isSupportedComponent(supportedComponents, rgb))
2957         return rgb;
2958     }
2959 
2960     /// wierd, must be some custom bit , if only one, choose that, otherwise no idea
2961     /// how to map, you need to derive to do so.
2962     return none;
2963   }
2964 
FindDeepestBitDepth(const std::string & s1,const std::string & s2)2965   static std::string FindDeepestBitDepth(const std::string &s1, const std::string &s2)
2966   {
2967     if(s1 == kOfxBitDepthNone) {
2968       return s2;
2969     }
2970     else if(s1 == kOfxBitDepthByte) {
2971       if(s2 == kOfxBitDepthShort || s2 == kOfxBitDepthFloat)
2972         return s2;
2973       return s1;
2974     }
2975     else if(s1 == kOfxBitDepthShort) {
2976       if(s2 == kOfxBitDepthFloat)
2977         return s2;
2978       return s1;
2979     }
2980     else if(s1 == kOfxBitDepthHalf) {
2981       if(s2 == kOfxBitDepthFloat)
2982         return s2;
2983       return s1;
2984     }
2985     else if(s1 == kOfxBitDepthFloat) {
2986       return s1;
2987     }
2988     else {
2989       return s2; // oooh this might be bad dad.
2990     }
2991   }
2992 
findBestSupportedPixelDepth(const std::set<std::string> & supportedPixelDepths,const std::string & depth)2993   static const std::string& findBestSupportedPixelDepth(const std::set<std::string>& supportedPixelDepths, const std::string &depth)
2994   {
2995     static const std::string none(kOfxBitDepthNone);
2996     static const std::string bytes(kOfxBitDepthByte);
2997     static const std::string shorts(kOfxBitDepthShort);
2998     static const std::string halfs(kOfxBitDepthHalf);
2999     static const std::string floats(kOfxBitDepthFloat);
3000 
3001     if(depth == none)
3002       return none;
3003 
3004     if(isSupportedPixelDepth(supportedPixelDepths, depth))
3005       return depth;
3006 
3007     if(depth == floats) {
3008       if(isSupportedPixelDepth(supportedPixelDepths, halfs))
3009         return halfs;
3010       if(isSupportedPixelDepth(supportedPixelDepths, shorts))
3011         return shorts;
3012       if(isSupportedPixelDepth(supportedPixelDepths, bytes))
3013         return bytes;
3014     }
3015 
3016     if(depth == halfs) {
3017       if(isSupportedPixelDepth(supportedPixelDepths, floats))
3018         return floats;
3019       if(isSupportedPixelDepth(supportedPixelDepths, shorts))
3020         return shorts;
3021       if(isSupportedPixelDepth(supportedPixelDepths, bytes))
3022         return bytes;
3023     }
3024 
3025     if(depth == shorts) {
3026       if(isSupportedPixelDepth(supportedPixelDepths, floats))
3027         return floats;
3028       if(isSupportedPixelDepth(supportedPixelDepths, halfs))
3029         return halfs;
3030       if(isSupportedPixelDepth(supportedPixelDepths, bytes))
3031         return bytes;
3032     }
3033 
3034     if(depth == bytes) {
3035       if(isSupportedPixelDepth(supportedPixelDepths, shorts))
3036         return shorts;
3037       if(isSupportedPixelDepth(supportedPixelDepths, halfs))
3038         return halfs;
3039       if(isSupportedPixelDepth(supportedPixelDepths, floats))
3040         return floats;
3041     }
3042 
3043     return none;
3044   }
3045 
getDefaultOutputClipComponents()3046   PixelComponentEnum ImageEffect::getDefaultOutputClipComponents()
3047   {
3048     bool hasSetComps = false;
3049     std::string mostComponents  = kOfxImageComponentNone;
3050 
3051     Clip* outputClip = 0;
3052     for (std::map<std::string, Clip *>::const_iterator it = _fetchedClips.begin(); it != _fetchedClips.end(); ++it) {
3053       Clip *clip = it->second;
3054 
3055       if(clip->getName() == kOfxImageEffectOutputClipName) {
3056         outputClip = clip;
3057       } else {
3058         bool connected = clip->isConnected();
3059 
3060         if(connected) {
3061           std::string rawComp  = it->second->getUnmappedPixelComponentsProperty();
3062           std::set<std::string> supportedComponents;
3063           getSupportedComponents(clip->getPropertySet(), &supportedComponents);
3064           rawComp = findBestSupportedComponent(supportedComponents, rawComp); // turn that into a comp the plugin expects on that clip
3065 
3066           if(isChromaticComponent(rawComp)) {
3067             //Update deepest bitdepth and most components only if the infos are relevant, i.e: only if the clip is connected
3068             hasSetComps = true;
3069             bool mostIsAlpha = (mostComponents == kOfxImageComponentAlpha);
3070             bool rawIsAlpha = (rawComp == kOfxImageComponentAlpha);
3071             if (mostIsAlpha != rawIsAlpha && mostComponents != kOfxImageComponentNone) {
3072               // one is alpha, the other is anything else than just alpha: the union is RGBA
3073               mostComponents = kOfxImageComponentRGBA;
3074             }
3075             mostComponents  = findMostChromaticComponents(mostComponents, rawComp);
3076           }
3077         }
3078       }
3079     }
3080     if (!outputClip) {
3081       return mapStrToPixelComponentEnum(mostComponents);
3082     }
3083     if (!hasSetComps) {
3084       mostComponents = kOfxImageComponentRGBA;
3085     }
3086 
3087     // "Optional input clips can always have their component types remapped"
3088     // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id482755
3089     std::set<std::string> supportedComponents;
3090     getSupportedComponents(outputClip->getPropertySet(), &supportedComponents);
3091     std::string comp = findBestSupportedComponent(supportedComponents, mostComponents);
3092     return mapStrToPixelComponentEnum(comp);
3093   } // getDefaultOutputClipComponents
3094 
getDefaultBitdepth()3095   BitDepthEnum ImageEffect::getDefaultBitdepth()
3096   {
3097     bool hasSetDepth = false;
3098     std::string deepestBitDepth = kOfxBitDepthNone;
3099 
3100     Clip* outputClip = 0;
3101     for (std::map<std::string, Clip *>::const_iterator it = _fetchedClips.begin(); it != _fetchedClips.end(); ++it) {
3102       Clip *clip = it->second;
3103       if(clip->getName() == kOfxImageEffectOutputClipName) {
3104         outputClip = clip;
3105       } else {
3106         bool connected = clip->isConnected();
3107         if(connected) {
3108           //Update deepest bitdepth and most components only if the infos are relevant, i.e: only if the clip is connected
3109           hasSetDepth = true;
3110           std::string rawDepth = clip->getPropertySet().propGetString(kOfxImageClipPropUnmappedPixelDepth, 0);
3111 
3112           std::set<std::string> supportedPixelDepths;
3113           getSupportedPixelDepths(clip->getPropertySet(), &supportedPixelDepths);
3114           rawDepth = findBestSupportedPixelDepth(supportedPixelDepths, rawDepth);
3115           deepestBitDepth = FindDeepestBitDepth(deepestBitDepth, rawDepth);
3116         }
3117 
3118       }
3119     }
3120     if (!outputClip) {
3121       return mapStrToBitDepthEnum(deepestBitDepth);
3122     }
3123     if (!hasSetDepth) {
3124       deepestBitDepth = kOfxBitDepthFloat;
3125     }
3126 
3127     std::set<std::string> supportedPixelDepths;
3128     getSupportedComponents(outputClip->getPropertySet(), &supportedPixelDepths);
3129     deepestBitDepth = findBestSupportedPixelDepth(supportedPixelDepths, deepestBitDepth);
3130 
3131     return mapStrToBitDepthEnum(deepestBitDepth);
3132   }
3133 
3134   /** @brief the effect is about to be actively edited by a user, called when the first user interface is opened on an instance */
beginEdit(void)3135   void ImageEffect::beginEdit(void)
3136   {
3137     // fa niente
3138   }
3139 
3140   /** @brief the effect is no longer being edited by a user, called when the last user interface is closed on an instance */
endEdit(void)3141   void ImageEffect::endEdit(void)
3142   {
3143     // fa niente
3144   }
3145 
3146   /** @brief the effect is about to have some values changed */
beginChanged(InstanceChangeReason)3147   void ImageEffect::beginChanged(InstanceChangeReason /*reason*/)
3148   {
3149   }
3150 
3151   /** @brief called when a param has just had its value changed */
changedParam(const InstanceChangedArgs &,const std::string &)3152   void ImageEffect::changedParam(const InstanceChangedArgs &/*args*/, const std::string &/*paramName*/)
3153   {
3154   }
3155 
3156   /** @brief called when a clip has just been changed in some way (a rewire maybe) */
changedClip(const InstanceChangedArgs &,const std::string &)3157   void ImageEffect::changedClip(const InstanceChangedArgs &/*args*/, const std::string &/*clipName*/)
3158   {
3159   }
3160 
3161   /** @brief the effect has just had some values changed */
endChanged(InstanceChangeReason)3162   void ImageEffect::endChanged(InstanceChangeReason /*reason*/)
3163   {
3164   }
3165 
3166 #ifdef OFX_SUPPORTS_DIALOG
3167   /** @brief called in the host's UI thread after a plugin has requested a dialog @see requestDialog() */
dialog(void *)3168   void ImageEffect::dialog(void */*instanceData*/)
3169   {
3170   }
3171 #endif
3172 
3173   /** @brief get the time domain */
getTimeDomain(OfxRangeD &)3174   bool ImageEffect::getTimeDomain(OfxRangeD &/*range*/)
3175   {
3176     // by default, do the default
3177     return false;
3178   }
3179 
3180 #ifdef OFX_SUPPORTS_OPENGLRENDER
3181   /** @brief OpenGL context attached (returns context-specific data or NULL if the plugin does not support multiple contexts) */
contextAttached(bool)3182   void* ImageEffect::contextAttached(bool)
3183   {
3184     // fa niente
3185     return NULL;
3186   }
3187 
3188   /** @brief OpenGL context detached */
contextDetached(void *)3189   void ImageEffect::contextDetached(void*)
3190   {
3191     // fa niente
3192   }
3193 #endif
3194 
3195 #ifdef OFX_EXTENSIONS_VEGAS
3196   /** @brief Vegas requires conversion of keyframe data */
upliftVegasKeyframes(const SonyVegasUpliftArguments &)3197   void ImageEffect::upliftVegasKeyframes(const SonyVegasUpliftArguments &/*upliftInfo*/)
3198   {
3199     // fa niente
3200   }
3201 
3202   /** @brief Vegas requests custom about dialog */
invokeAbout()3203   bool ImageEffect::invokeAbout()
3204   {
3205     // by default, do nothing
3206     return false;
3207   }
3208 
3209   /** @brief Vegas requests custom help dialog */
invokeHelp()3210   bool ImageEffect::invokeHelp()
3211   {
3212     // by default, do nothing
3213     return false;
3214   }
3215 #endif
3216 
3217 #ifdef OFX_EXTENSIONS_NATRON
3218   /** @brief get the distortion function */
getInverseDistortion(const DistortionArguments &,Clip * &,double[9],OfxInverseDistortionFunctionV1 *,void **,int *,OfxInverseDistortionDataFreeFunctionV1 *)3219   bool ImageEffect::getInverseDistortion(const DistortionArguments &/*args*/, Clip * &/*transformClip*/, double /*transformMatrix*/[9],
3220                                   OfxInverseDistortionFunctionV1* /*distortionFunction*/,
3221                                   void** /*distortionFunctionData*/,
3222                                   int* /*distortionFunctionDataSizeHintInBytes*/,
3223                                   OfxInverseDistortionDataFreeFunctionV1* /*freeDataFunction*/)
3224   {
3225     // by default, do the default
3226     return false;
3227   }
3228 #endif
3229 
3230 #ifdef OFX_EXTENSIONS_NUKE
getClipComponents(const ClipComponentsArguments &,ClipComponentsSetter &)3231   OfxStatus ImageEffect::getClipComponents(const ClipComponentsArguments& /*args*/, ClipComponentsSetter& /*clipComponents*/)
3232   {
3233       return kOfxStatReplyDefault;
3234         // pass
3235   }
3236 
getFrameViewsNeeded(const FrameViewsNeededArguments &,FrameViewsNeededSetter &)3237   void ImageEffect::getFrameViewsNeeded(const FrameViewsNeededArguments & /*args*/, FrameViewsNeededSetter & /*frameViews*/)
3238   {
3239         // pass
3240   }
3241 
3242   /** @brief recover a transform matrix from an effect */
getTransform(const TransformArguments &,Clip * &,double[9])3243   bool ImageEffect::getTransform(const TransformArguments &/*args*/, Clip * &/*transformClip*/, double /*transformMatrix*/[9])
3244   {
3245     // by default, do the default
3246     return false;
3247   }
3248 
3249 
getViewName(int viewIndex) const3250   std::string ImageEffect::getViewName(int viewIndex) const
3251   {
3252       const char* viewName;
3253       OfxStatus stat = OFX::Private::gImageEffectPlaneSuiteV2->getViewName(_effectHandle, viewIndex, &viewName);
3254       if(stat == kOfxStatFailed || !viewName) {
3255           return std::string();
3256       }
3257       throwSuiteStatusException(stat);
3258       return std::string(viewName);
3259   }
3260 
getViewCount() const3261   int ImageEffect::getViewCount() const
3262   {
3263       int viewCount;
3264       OfxStatus stat = OFX::Private::gImageEffectPlaneSuiteV2->getViewCount(_effectHandle, &viewCount);
3265       if(stat == kOfxStatFailed) {
3266           return 0;
3267       }
3268       throwSuiteStatusException(stat);
3269       return viewCount;
3270   }
3271 
3272 #endif
3273 
3274   /** @brief called when a custom param needs to be interpolated */
interpolateCustomParam(const InterpolateCustomArgs & args,const std::string &)3275   std::string ImageEffect::interpolateCustomParam(const InterpolateCustomArgs &args, const std::string &/*paramName*/)
3276   {
3277       return args.value1;
3278   }
3279 
3280   /// Start doing progress.
progressStart(const std::string & message,const std::string & messageid)3281   void ImageEffect::progressStart(const std::string &message, const std::string &messageid)
3282   {
3283     if(OFX::Private::gProgressSuiteV2) {
3284       OfxStatus stat = OFX::Private::gProgressSuiteV2->progressStart((void *) _effectHandle, message.c_str(), messageid.c_str());
3285       _progressStartSuccess = ( stat == kOfxStatOK );
3286 #ifdef OFX_EXTENSIONS_VEGAS
3287     } else if(OFX::Private::gVegasProgressSuite) {
3288         OfxStatus stat = OFX::Private::gVegasProgressSuite->progressStart((void *) _effectHandle, message.c_str(), messageid.c_str());
3289         _progressStartSuccess = ( stat == kOfxStatOK );
3290 #endif
3291     } else if(OFX::Private::gProgressSuiteV1) {
3292       OfxStatus stat = OFX::Private::gProgressSuiteV1->progressStart((void *) _effectHandle, message.c_str());
3293       _progressStartSuccess = ( stat == kOfxStatOK );
3294     }
3295 
3296   }
3297 
3298   /// finish yer progress
progressEnd()3299   void ImageEffect::progressEnd()
3300   {
3301     if(_progressStartSuccess) {
3302       if(OFX::Private::gProgressSuiteV2) {
3303         OFX::Private::gProgressSuiteV2->progressEnd((void *) _effectHandle);
3304       } else if(OFX::Private::gProgressSuiteV1) {
3305         OFX::Private::gProgressSuiteV1->progressEnd((void *) _effectHandle);
3306       }
3307     }
3308   }
3309 
3310   /// set the progress to some level of completion, returns
3311   /// false if you should abandon processing, true to continue
progressUpdate(double t)3312   bool ImageEffect::progressUpdate(double t)
3313   {
3314     if ( OFX::IsNaN(t) ) {
3315       throwSuiteStatusException(kOfxStatErrValue);
3316     }
3317     if(_progressStartSuccess) {
3318       if(OFX::Private::gProgressSuiteV2) {
3319         OfxStatus stat = OFX::Private::gProgressSuiteV2->progressUpdate((void *) _effectHandle, t);
3320         if(stat == kOfxStatReplyNo)
3321           return false;
3322 #ifdef OFX_EXTENSIONS_VEGAS
3323       } else if(OFX::Private::gVegasProgressSuite) {
3324           OfxStatus stat = OFX::Private::gVegasProgressSuite->progressUpdate((void *) _effectHandle, t);
3325           if(stat == kOfxStatReplyNo)
3326               return false;
3327 #endif
3328       } else if(OFX::Private::gProgressSuiteV1) {
3329         OfxStatus stat = OFX::Private::gProgressSuiteV1->progressUpdate((void *) _effectHandle, t);
3330         if(stat == kOfxStatReplyNo)
3331           return false;
3332       }
3333     }
3334     return true;
3335   }
3336 
3337   /// get the current time on the timeline. This is not necessarily the same
3338   /// time as being passed to an action (eg render)
timeLineGetTime()3339   double ImageEffect::timeLineGetTime()
3340   {
3341     if(OFX::Private::gTimeLineSuite) {
3342       double time;
3343       if(OFX::Private::gTimeLineSuite->getTime((void *) _effectHandle, &time) == kOfxStatOK)
3344         return time;
3345     }
3346     return 0;
3347   }
3348 
3349   /// set the timeline to a specific time
timeLineGotoTime(double t)3350   void ImageEffect::timeLineGotoTime(double t)
3351   {
3352     if ( OFX::IsNaN(t) ) {
3353       throwSuiteStatusException(kOfxStatErrValue);
3354     }
3355     if(OFX::Private::gTimeLineSuite) {
3356       OFX::Private::gTimeLineSuite->gotoTime((void *) _effectHandle, t);
3357     }
3358   }
3359 
3360   /// get the first and last times available on the effect's timeline
timeLineGetBounds(double & t1,double & t2)3361   void ImageEffect:: timeLineGetBounds(double &t1, double &t2)
3362   {
3363     if(OFX::Private::gTimeLineSuite) {
3364       OFX::Private::gTimeLineSuite->getTimeBounds((void *) _effectHandle, &t1, &t2);
3365       return;
3366     }
3367     t1 = t2 = 0;
3368   }
3369 
3370 #ifdef OFX_EXTENSIONS_VEGAS
3371   ////////////////////////////////////////////////////////////////////////////////
3372   // Class used to uplift previous vegas keyframe data of the effect. */
SonyVegasUpliftArguments(PropertySet args)3373   SonyVegasUpliftArguments::SonyVegasUpliftArguments(PropertySet args)
3374     : guidUplift()
3375     , keyframeCount(0)
3376     , commonData(NULL)
3377     , commonDataSize(0)
3378   {
3379       _argProps = args;
3380   }
3381 
getKeyframeData(int keyframeIndex) const3382   void*  SonyVegasUpliftArguments::getKeyframeData     (int keyframeIndex) const
3383   {
3384       return _argProps.propGetPointer(kOfxPropVegasUpliftKeyframeData, keyframeIndex);
3385   }
3386 
getKeyframeDataSize(int keyframeIndex) const3387   int    SonyVegasUpliftArguments::getKeyframeDataSize (int keyframeIndex) const
3388   {
3389       return _argProps.propGetInt(kOfxPropVegasUpliftKeyframeDataLength, keyframeIndex);
3390   }
3391 
getKeyframeTime(int keyframeIndex) const3392   double SonyVegasUpliftArguments::getKeyframeTime     (int keyframeIndex) const
3393   {
3394       return _argProps.propGetDouble(kOfxPropVegasUpliftKeyframeTime, keyframeIndex);
3395   }
3396 
getKeyframeInterpolation(int keyframeIndex) const3397   VegasInterpolationEnum SonyVegasUpliftArguments::getKeyframeInterpolation (int keyframeIndex) const
3398   {
3399       return mapToInterpolationEnum(_argProps.propGetString(kOfxPropVegasUpliftKeyframeInterpolation, keyframeIndex));
3400   }
3401 #endif
3402 
3403 #ifdef OFX_EXTENSIONS_NUKE
extractValueForName(const StringStringMap & m,const std::string & name)3404   const std::string& ClipComponentsSetter::extractValueForName(const StringStringMap& m, const std::string& name)
3405   {
3406       StringStringMap::const_iterator it = m.find(name);
3407       if(it==m.end())
3408           throw(Exception::PropertyUnknownToHost(name.c_str()));
3409       return it->second;
3410   }
3411 
setOutProperties()3412   bool ClipComponentsSetter::setOutProperties()
3413   {
3414       for (std::map<std::string,std::vector<std::string> >::iterator it = _clipPlanes.begin(); it!=_clipPlanes.end(); ++it) {
3415           const std::string& propName = extractValueForName(_clipPlanesPropNames, it->first);
3416           for (std::size_t i = 0; i < it->second.size(); ++i) {
3417               _outArgs.propSetString(propName.c_str(), it->second[i], (int)i, true);
3418           }
3419       }
3420       return _doneSomething;
3421   }
3422 
addClipPlane(Clip & clip,const std::string & comps)3423   void ClipComponentsSetter::addClipPlane(Clip& clip, const std::string& comps)
3424   {
3425      _doneSomething = true;
3426      _clipPlanes[clip.name()].push_back(comps);
3427   }
3428 
setPassThroughClip(const Clip * clip,double time,int view)3429   void ClipComponentsSetter::setPassThroughClip(const Clip* clip, double time, int view)
3430   {
3431     if ( OFX::IsNaN(time) ) {
3432           throwSuiteStatusException(kOfxStatErrValue);
3433       }
3434       _doneSomething = true;
3435       if (clip) {
3436         _outArgs.propSetString(kFnOfxImageEffectPropPassThroughClip, clip->name(), 0);
3437       } else {
3438         _outArgs.propSetString(kFnOfxImageEffectPropPassThroughClip, "", 0);
3439       }
3440       _outArgs.propSetDouble(kFnOfxImageEffectPropPassThroughTime, time, 0);
3441       _outArgs.propSetInt(kFnOfxImageEffectPropPassThroughView, view, 0);
3442   }
3443 
3444 
extractValueForName(const StringStringMap & m,const std::string & name)3445   const std::string& FrameViewsNeededSetter::extractValueForName(const StringStringMap& m, const std::string& name)
3446   {
3447         StringStringMap::const_iterator it = m.find(name);
3448         if(it==m.end())
3449             throw(Exception::PropertyUnknownToHost(name.c_str()));
3450         return it->second;
3451   }
3452 
setOutProperties()3453   bool FrameViewsNeededSetter::setOutProperties()
3454   {
3455       for (std::map<std::string, std::map<int, std::vector<OfxRangeD> > >::iterator it = _frameViews.begin(); it!=_frameViews.end(); ++it) {
3456           int dimIndex = 0;
3457           const std::string& propName = extractValueForName(_clipFrameViewsPropnames, it->first);
3458           for (std::map<int, std::vector<OfxRangeD> >::iterator it2 = it->second.begin(); it2!=it->second.end(); ++it2) {
3459               for (std::vector<OfxRangeD>::iterator it3 = it2->second.begin(); it3 != it2->second.end(); ++it3, dimIndex += 3) {
3460                   _outArgs.propSetDouble(propName.c_str(), it3->min, dimIndex);
3461                   _outArgs.propSetDouble(propName.c_str(), it3->max, dimIndex + 1);
3462                   _outArgs.propSetDouble(propName.c_str(), it2->first, dimIndex + 2);
3463               }
3464           }
3465       }
3466       return _doneSomething;
3467   }
3468 
addFrameViewsNeeded(const Clip & clip,const OfxRangeD & range,int view)3469   void FrameViewsNeededSetter::addFrameViewsNeeded(const Clip& clip,const OfxRangeD &range, int view)
3470   {
3471       _doneSomething = true;
3472       std::map<int, std::vector<OfxRangeD> >& frameViews = _frameViews[clip.name()];
3473       std::vector<OfxRangeD>& ranges = frameViews[view];
3474       ranges.push_back(range);
3475   }
3476 #endif
3477 
3478   ////////////////////////////////////////////////////////////////////////////////
3479   // Class used to set the clip preferences of the effect. */
3480 
extractValueForName(const StringStringMap & m,const std::string & name)3481   const std::string& ClipPreferencesSetter::extractValueForName(const StringStringMap& m, const std::string& name)
3482   {
3483     StringStringMap::const_iterator it = m.find(name);
3484     if(it==m.end())
3485       throw(Exception::PropertyUnknownToHost(name.c_str()));
3486     return it->second;
3487   }
3488 
3489   /** @brief, force the host to set a clip's mapped component type to be \em comps.  */
setClipComponents(Clip & clip,PixelComponentEnum comps)3490   void ClipPreferencesSetter::setClipComponents(Clip &clip, PixelComponentEnum comps)
3491   {
3492     doneSomething_ = true;
3493     const std::string& propName = extractValueForName(clipComponentPropNames_, clip.name());
3494 
3495     switch(comps)
3496     {
3497     case ePixelComponentNone :
3498       outArgs_.propSetString(propName.c_str(), kOfxImageComponentNone);
3499       break;
3500     case ePixelComponentRGBA :
3501       outArgs_.propSetString(propName.c_str(), kOfxImageComponentRGBA);
3502       break;
3503     case ePixelComponentRGB :
3504       outArgs_.propSetString(propName.c_str(), kOfxImageComponentRGB);
3505       break;
3506     case ePixelComponentAlpha :
3507       outArgs_.propSetString(propName.c_str(), kOfxImageComponentAlpha);
3508       break;
3509 #ifdef OFX_EXTENSIONS_NUKE
3510     case ePixelComponentMotionVectors :
3511       outArgs_.propSetString(propName.c_str(), kFnOfxImageComponentMotionVectors);
3512       break;
3513     case ePixelComponentStereoDisparity :
3514       outArgs_.propSetString(propName.c_str(), kFnOfxImageComponentStereoDisparity);
3515       break;
3516 #endif
3517 #ifdef OFX_EXTENSIONS_NATRON
3518     case ePixelComponentXY:
3519       outArgs_.propSetString(propName.c_str(), kNatronOfxImageComponentXY);
3520       break;
3521 #endif
3522     case ePixelComponentCustom :
3523       break;
3524     }
3525   }
3526 
3527   /** @brief, force the host to set a clip's mapped bit depth be \em bitDepth */
setClipBitDepth(Clip & clip,BitDepthEnum bitDepth)3528   void ClipPreferencesSetter::setClipBitDepth(Clip &clip, BitDepthEnum bitDepth)
3529   {
3530     doneSomething_ = true;
3531     const std::string& propName = extractValueForName(clipDepthPropNames_, clip.name());
3532 
3533     switch(bitDepth)
3534     {
3535     case eBitDepthNone :
3536       outArgs_.propSetString(propName.c_str(), kOfxBitDepthNone);
3537       break;
3538     case eBitDepthUByte :
3539       outArgs_.propSetString(propName.c_str(), kOfxBitDepthByte);
3540       break;
3541     case eBitDepthUShort :
3542       outArgs_.propSetString(propName.c_str(), kOfxBitDepthShort);
3543       break;
3544     case eBitDepthHalf :
3545       outArgs_.propSetString(propName.c_str(), kOfxBitDepthHalf);
3546       break;
3547     case eBitDepthFloat :
3548       outArgs_.propSetString(propName.c_str(), kOfxBitDepthFloat);
3549       break;
3550 #ifdef OFX_EXTENSIONS_VEGAS
3551     case eBitDepthUByteBGRA :
3552       outArgs_.propSetString(propName.c_str(), kOfxBitDepthByteBGR);
3553       break;
3554     case eBitDepthUShortBGRA :
3555       outArgs_.propSetString(propName.c_str(), kOfxBitDepthShortBGR);
3556       break;
3557     case eBitDepthFloatBGRA :
3558       outArgs_.propSetString(propName.c_str(), kOfxBitDepthFloatBGR);
3559       break;
3560 #endif
3561     case eBitDepthCustom :
3562       break;
3563     }
3564   }
3565 
3566   /** @brief, force the host to set a clip's mapped Pixel Aspect Ratio to be \em PAR */
setPixelAspectRatio(Clip & clip,double PAR)3567   void ClipPreferencesSetter::setPixelAspectRatio(Clip &clip, double PAR)
3568   {
3569     doneSomething_ = true;
3570     const std::string& propName = extractValueForName(clipPARPropNames_, clip.name());
3571     outArgs_.propSetDouble(propName.c_str(), PAR);
3572   }
3573 
3574   /** @brief Allows an effect to change the output frame rate */
setOutputFrameRate(double v)3575   void ClipPreferencesSetter::setOutputFrameRate(double v)
3576   {
3577     doneSomething_ = true;
3578     outArgs_.propSetDouble(kOfxImageEffectPropFrameRate, v);
3579   }
3580 
3581   /** @brief Set the premultiplication state of the output clip. */
setOutputPremultiplication(PreMultiplicationEnum v)3582   void ClipPreferencesSetter::setOutputPremultiplication(PreMultiplicationEnum v)
3583   {
3584     doneSomething_ = true;
3585     switch(v)
3586     {
3587     case eImageOpaque :
3588       outArgs_.propSetString(kOfxImageEffectPropPreMultiplication, kOfxImageOpaque);
3589       break;
3590     case eImagePreMultiplied:
3591       outArgs_.propSetString(kOfxImageEffectPropPreMultiplication, kOfxImagePreMultiplied);
3592       break;
3593     case eImageUnPreMultiplied:
3594       outArgs_.propSetString(kOfxImageEffectPropPreMultiplication, kOfxImageUnPreMultiplied);
3595       break;
3596     }
3597   }
3598 
3599   /** @brief Set whether the effect can be continuously sampled. */
setOutputHasContinuousSamples(bool v)3600   void ClipPreferencesSetter::setOutputHasContinuousSamples(bool v)
3601   {
3602     doneSomething_ = true;
3603     outArgs_.propSetInt(kOfxImageClipPropContinuousSamples, int(v));
3604   }
3605 
3606   /** @brief Sets whether the effect will produce different images in all frames, even if the no params or input images are varying (eg: a noise generator). */
setOutputFrameVarying(bool v)3607   void ClipPreferencesSetter::setOutputFrameVarying(bool v)
3608   {
3609     doneSomething_ = true;
3610     outArgs_.propSetInt(kOfxImageEffectFrameVarying, int(v));
3611   }
3612 
3613 
setOutputFielding(FieldEnum v)3614   void  ClipPreferencesSetter::setOutputFielding(FieldEnum v)
3615   {
3616     doneSomething_ = true;
3617     switch(v)
3618     {
3619     case eFieldNone : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldNone, 0, false); break;
3620     case eFieldLower : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldLower, 0, false); break;
3621     case eFieldUpper : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldUpper, 0, false); break;
3622     case eFieldBoth : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldBoth, 0, false); break;
3623     case eFieldSingle : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldSingle, 0, false); break;
3624     case eFieldDoubled : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldDoubled, 0, false); break;
3625     }
3626   }
3627 
3628 #ifdef OFX_EXTENSIONS_NATRON
3629   /** @brief Sets the output format in pixel coordinates.
3630    Default to first non optional input clip format
3631    */
setOutputFormat(const OfxRectI & format)3632   void ClipPreferencesSetter::setOutputFormat(const OfxRectI& format)
3633   {
3634     doneSomething_ = true;
3635     std::vector<int> v(4);
3636     v[0] = format.x1;
3637     v[1] = format.y1;
3638     v[2] = format.x2;
3639     v[3] = format.y2;
3640     outArgs_.propSetIntN(kOfxImageClipPropFormat, v, false);
3641   }
3642 #endif
3643 
3644   ////////////////////////////////////////////////////////////////////////////////
3645   /** @brief Class that skins image memory allocation */
3646 
3647   /** @brief ctor */
ImageMemory(size_t nBytes,ImageEffect * associatedEffect)3648   ImageMemory::ImageMemory(size_t nBytes, ImageEffect *associatedEffect)
3649     : _handle(NULL)
3650   {
3651     OfxImageEffectHandle effectHandle = 0;
3652     if(associatedEffect != 0) {
3653       effectHandle = associatedEffect->_effectHandle;
3654     }
3655 
3656     OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryAlloc(effectHandle, nBytes, &_handle);
3657     if(stat == kOfxStatErrMemory)
3658       throw std::bad_alloc();
3659     throwSuiteStatusException(stat);
3660   }
3661 
3662   /** @brief dtor */
~ImageMemory()3663   ImageMemory::~ImageMemory()
3664   {
3665     OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryFree(_handle);
3666     // ignore status code for exception purposes
3667     (void)stat;
3668   }
3669 
3670   /** @brief lock the memory and return a pointer to it */
lock(void)3671   void *ImageMemory::lock(void)
3672   {
3673     void *ptr;
3674     OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryLock(_handle, &ptr);
3675     if(stat == kOfxStatErrMemory)
3676       throw std::bad_alloc();
3677     throwSuiteStatusException(stat);
3678     return ptr;
3679   }
3680 
3681   /** @brief unlock the memory */
unlock(void)3682   void ImageMemory::unlock(void)
3683   {
3684     OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryUnlock(_handle);
3685     (void)stat;
3686   }
3687 
3688 
3689 
3690   /** @brief OFX::Private namespace, for things private to the support library code here generally calls image effect class members */
3691   namespace Private {
3692 
3693 #ifdef OFX_EXTENSIONS_NATRON
3694     static
3695     bool
decodeNativeOverlayHandle(const std::string & str,ImageEffectHostDescription::NativeOverlayHandle & handle)3696     decodeNativeOverlayHandle(const std::string& str, ImageEffectHostDescription::NativeOverlayHandle& handle)
3697     {
3698       static const std::string idToken(kNatronNativeOverlayType);
3699       static const std::string paramHintToken(kNatronNativeOverlayParameterHint);
3700       static const std::string paramTypeToken(kNatronNativeOverlayParameterHint);
3701       std::size_t foundName = str.find_first_of(idToken);
3702       if (foundName == std::string::npos) {
3703         return false;
3704       }
3705 
3706       // Position the cursor to the start of the identifier string. +1 to ignore the '_' character.
3707       std::size_t identifierStart = foundName + idToken.size() + 1;
3708 
3709       std::size_t foundParamHint = str.find_first_of(paramHintToken);
3710       if (foundParamHint == std::string::npos) {
3711         return false;
3712       }
3713 
3714       std::size_t identifierEnd = foundParamHint - 1;
3715       handle.identifier = str.substr(identifierStart, identifierEnd - identifierStart);
3716 
3717       while (foundParamHint != std::string::npos) {
3718         // Position the cursor to the start of the first parameter string. +1 to ignore the '_' character.
3719         std::size_t paramHintStart = foundParamHint + paramHintToken.size() + 1;
3720 
3721 
3722         // Find the param type string token
3723         std::size_t foundParamType = str.find_first_of(paramTypeToken, paramHintStart);
3724 
3725         // huh badly encoded...
3726         if (foundParamType == std::string::npos) {
3727           return false;
3728         }
3729 
3730         std::size_t paramHintEnd = foundParamType - 1;
3731         std::string paramHint = str.substr(paramHintStart, paramHintEnd - paramHintStart);
3732 
3733 
3734         std::size_t paramTypeStart = foundParamType + paramTypeToken.size() + 1;
3735 
3736         // Find the next parameter description if any
3737         foundParamHint = str.find_first_of(paramHintToken, paramTypeStart);
3738 
3739         std::size_t paramTypeEnd;
3740         if (foundParamHint != std::string::npos) {
3741           // there's a parameter after this one, use it to know where to stop for the type string
3742           paramTypeEnd = foundParamHint - 1;
3743         } else {
3744           // we reached the end!
3745           paramTypeEnd = std::string::npos;
3746         }
3747         std::string paramType = str.substr(paramTypeStart, paramTypeEnd - paramTypeStart);
3748         handle.parameters.push_back(std::make_pair(paramHint, paramType));
3749 
3750       }
3751 
3752       return true;
3753 
3754     }
3755 #endif
3756     /** @brief Creates the global host description and sets its properties */
3757     static
3758     void
fetchHostDescription(OfxHost * host)3759       fetchHostDescription(OfxHost *host)
3760     {
3761       OFX::Log::error(OFX::gHostDescriptionHasInit, "Tried to create host description when we already have one.");
3762       if(!OFX::gHostDescriptionHasInit) {
3763         OFX::gHostDescriptionHasInit = true;
3764         // wrap the property handle up with a property set
3765         PropertySet hostProps(host->host);
3766 
3767         // and get some properties
3768         gHostDescription.APIVersionMajor            = hostProps.propGetInt(kOfxPropAPIVersion, 0, false); // OFX 1.2
3769         if (gHostDescription.APIVersionMajor == 0) {
3770           // assume OFX 1.0
3771           gHostDescription.APIVersionMajor = 1;
3772         }
3773         gHostDescription.APIVersionMinor            = hostProps.propGetInt(kOfxPropAPIVersion, 1, false); // OFX 1.2
3774         gHostDescription.hostName                   = hostProps.propGetString(kOfxPropName);
3775         gHostDescription.hostLabel                  = hostProps.propGetString(kOfxPropLabel);
3776         gHostDescription.versionMajor               = hostProps.propGetInt(kOfxPropVersion, 0, false); // OFX 1.2
3777         gHostDescription.versionMinor               = hostProps.propGetInt(kOfxPropVersion, 1, false); // OFX 1.2
3778         gHostDescription.versionMicro               = hostProps.propGetInt(kOfxPropVersion, 2, false); // OFX 1.2
3779         gHostDescription.versionLabel               = hostProps.propGetString(kOfxPropVersionLabel, false); // OFX 1.2
3780         gHostDescription.hostIsBackground           = hostProps.propGetInt(kOfxImageEffectHostPropIsBackground) != 0;
3781         gHostDescription.supportsOverlays           = hostProps.propGetInt(kOfxImageEffectPropSupportsOverlays) != 0;
3782         gHostDescription.supportsMultiResolution    = hostProps.propGetInt(kOfxImageEffectPropSupportsMultiResolution) != 0;
3783         gHostDescription.supportsTiles              = hostProps.propGetInt(kOfxImageEffectPropSupportsTiles) != 0;
3784         gHostDescription.temporalClipAccess         = hostProps.propGetInt(kOfxImageEffectPropTemporalClipAccess) != 0;
3785         gHostDescription.supportsMultipleClipDepths = hostProps.propGetInt(kOfxImageEffectPropSupportsMultipleClipDepths) != 0;
3786         gHostDescription.supportsMultipleClipPARs   = hostProps.propGetInt(kOfxImageEffectPropSupportsMultipleClipPARs) != 0;
3787         gHostDescription.supportsSetableFrameRate   = hostProps.propGetInt(kOfxImageEffectPropSetableFrameRate) != 0;
3788         gHostDescription.supportsSetableFielding    = hostProps.propGetInt(kOfxImageEffectPropSetableFielding) != 0;
3789         gHostDescription.sequentialRender           = hostProps.propGetInt(kOfxImageEffectInstancePropSequentialRender, false); // appeared in OFX 1.2
3790         gHostDescription.supportsStringAnimation    = hostProps.propGetInt(kOfxParamHostPropSupportsStringAnimation) != 0;
3791         gHostDescription.supportsCustomInteract     = hostProps.propGetInt(kOfxParamHostPropSupportsCustomInteract) != 0;
3792         gHostDescription.supportsChoiceAnimation    = hostProps.propGetInt(kOfxParamHostPropSupportsChoiceAnimation) != 0;
3793 #ifdef OFX_EXTENSIONS_RESOLVE
3794         gHostDescription.supportsStrChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStrChoiceAnimation, false) != 0;
3795 #endif
3796         gHostDescription.supportsBooleanAnimation   = hostProps.propGetInt(kOfxParamHostPropSupportsBooleanAnimation) != 0;
3797         gHostDescription.supportsCustomAnimation    = hostProps.propGetInt(kOfxParamHostPropSupportsCustomAnimation) != 0;
3798         gHostDescription.osHandle                   = hostProps.propGetPointer(kOfxPropHostOSHandle, false);
3799         gHostDescription.supportsParametricParameter = gParametricParameterSuite != 0;
3800         gHostDescription.supportsParametricAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsParametricAnimation, false) != 0;
3801 #ifdef OFX_EXTENSIONS_RESOLVE
3802         gHostDescription.supportsOpenCLRender        = hostProps.propGetString(kOfxImageEffectPropOpenCLRenderSupported, 0, false) == "true";
3803         gHostDescription.supportsCudaRender          = hostProps.propGetString(kOfxImageEffectPropCudaRenderSupported, 0, false) == "true";
3804 #endif
3805         gHostDescription.supportsRenderQualityDraft = hostProps.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0; // appeared in OFX 1.4
3806         {
3807             std::string originStr = hostProps.propGetString(kOfxImageEffectHostPropNativeOrigin, false); // appeared in OFX 1.4
3808           if (originStr.empty()) {
3809             // from http://openeffects.org/standard_changes/host-origin-hints :
3810             // "All this hint does is tell plugin that the host world is different
3811             // than OFX. Historically the first two hosts that exhibited this issue
3812             // could be Fusion (upper left is 0,0 natively) and Toxic (Center is 0,0)."
3813             if (gHostDescription.hostName == "com.eyeonline.Fusion" ||
3814                 ends_with(gHostDescription.hostName, "Fusion")) {
3815               // if host is Fusion, set to TopLeft
3816               gHostDescription.nativeOrigin = eNativeOriginTopLeft;
3817             } else if (starts_with(gHostDescription.hostName, "Autodesk Toxik") ||
3818                        ends_with(gHostDescription.hostName, "Toxik")) {
3819               // if host is Toxic, set to Center
3820               gHostDescription.nativeOrigin = eNativeOriginCenter;
3821             } else {
3822               gHostDescription.nativeOrigin = eNativeOriginBottomLeft;
3823             }
3824           } else if (originStr == kOfxHostNativeOriginBottomLeft) {
3825             gHostDescription.nativeOrigin = eNativeOriginBottomLeft;
3826           } else if (originStr == kOfxHostNativeOriginTopLeft) {
3827             gHostDescription.nativeOrigin = eNativeOriginTopLeft;
3828           } else if (originStr == kOfxHostNativeOriginCenter) {
3829             gHostDescription.nativeOrigin = eNativeOriginCenter;
3830           }
3831         }
3832 #ifdef OFX_SUPPORTS_OPENGLRENDER
3833         gHostDescription.supportsOpenGLRender = gOpenGLRenderSuite != 0 && hostProps.propGetString(kOfxImageEffectPropOpenGLRenderSupported, 0, false) == "true";
3834 #endif
3835 #ifdef OFX_EXTENSIONS_NUKE
3836         gHostDescription.supportsCamera    = gCameraSuite != 0;
3837         gHostDescription.canTransform               = hostProps.propGetInt(kFnOfxImageEffectCanTransform, false) != 0;
3838         gHostDescription.isMultiPlanar              = hostProps.propGetInt(kFnOfxImageEffectPropMultiPlanar, false) != 0;
3839 #endif
3840         gHostDescription.maxParameters              = hostProps.propGetInt(kOfxParamHostPropMaxParameters);
3841         gHostDescription.maxPages                   = hostProps.propGetInt(kOfxParamHostPropMaxPages);
3842         gHostDescription.pageRowCount               = hostProps.propGetInt(kOfxParamHostPropPageRowColumnCount, 0);
3843         gHostDescription.pageColumnCount            = hostProps.propGetInt(kOfxParamHostPropPageRowColumnCount, 1);
3844 #ifdef OFX_EXTENSIONS_NATRON
3845         gHostDescription.isNatron                   = hostProps.propGetInt(kNatronOfxHostIsNatron, false) != 0;
3846         gHostDescription.supportsDynamicChoices     = hostProps.propGetInt(kNatronOfxParamHostPropSupportsDynamicChoices, false) != 0;
3847         gHostDescription.supportsCascadingChoices   = hostProps.propGetInt(kNatronOfxParamPropChoiceCascading, false) != 0;
3848         gHostDescription.supportsChannelSelector    = hostProps.propGetString(kNatronOfxImageEffectPropChannelSelector, false) == kOfxImageComponentRGBA;
3849         gHostDescription.canDistort                 = hostProps.propGetInt(kOfxImageEffectPropCanDistort, false) != 0;
3850 
3851         int nOverlayHandles = hostProps.propGetDimension(kNatronOfxPropNativeOverlays, false);
3852         for (int i = 0; i < nOverlayHandles; ++i) {
3853           std::string overlayHandleEncoded = hostProps.propGetString(kNatronOfxPropNativeOverlays, i, false);
3854           ImageEffectHostDescription::NativeOverlayHandle h;
3855           if (decodeNativeOverlayHandle(overlayHandleEncoded, h)) {
3856             gHostDescription.nativeInteracts.push_back(h);
3857           }
3858 
3859         }
3860 
3861 #endif
3862 
3863         int numComponents = hostProps.propGetDimension(kOfxImageEffectPropSupportedComponents);
3864         for(int i=0; i<numComponents; ++i)
3865           gHostDescription._supportedComponents.push_back(mapStrToPixelComponentEnum(hostProps.propGetString(kOfxImageEffectPropSupportedComponents, i)));
3866 
3867         int numContexts = hostProps.propGetDimension(kOfxImageEffectPropSupportedContexts);
3868         for(int i=0; i<numContexts; ++i)
3869           gHostDescription._supportedContexts.push_back(mapToContextEnum(hostProps.propGetString(kOfxImageEffectPropSupportedContexts, i)));
3870 
3871         int numPixelDepths = hostProps.propGetDimension(kOfxImageEffectPropSupportedPixelDepths);
3872         for(int i=0; i<numPixelDepths; ++i)
3873           gHostDescription._supportedPixelDepths.push_back(mapStrToBitDepthEnum(hostProps.propGetString(kOfxImageEffectPropSupportedPixelDepths, i)));
3874       }
3875 
3876     }
3877 
3878     /** @brief fetch the effect property set from the ImageEffectHandle */
3879     OFX::PropertySet
fetchEffectProps(OfxImageEffectHandle handle)3880       fetchEffectProps(OfxImageEffectHandle handle)
3881     {
3882       // get the property handle
3883       OfxPropertySetHandle propHandle;
3884       OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet(handle, &propHandle);
3885       throwSuiteStatusException(stat);
3886       return OFX::PropertySet(propHandle);
3887     }
3888 
3889     /** @brief Keeps count of how many times load/unload have been called */
3890     int gLoadCount = 0;
3891 
3892     /** @brief Library side load action, this fetches all the suite pointers */
loadAction(void)3893     void loadAction(void)
3894     {
3895       gLoadCount++;
3896 
3897       //OfxStatus status = kOfxStatOK;
3898 
3899       // fetch the suites
3900       OFX::Log::error(gHost == 0, "Host pointer has not been set.");
3901       if(!gHost) throw OFX::Exception::Suite(kOfxStatErrBadHandle);
3902 
3903       if(gLoadCount == 1) {
3904         gEffectSuite    = (OfxImageEffectSuiteV1 *) fetchSuite(kOfxImageEffectSuite, 1);
3905         gPropSuite      = (OfxPropertySuiteV1 *)    fetchSuite(kOfxPropertySuite, 1);
3906         gParamSuite     = (OfxParameterSuiteV1 *)   fetchSuite(kOfxParameterSuite, 1);
3907         gMemorySuite    = (OfxMemorySuiteV1 *)      fetchSuite(kOfxMemorySuite, 1);
3908         gThreadSuite    = (OfxMultiThreadSuiteV1 *) fetchSuite(kOfxMultiThreadSuite, 1);
3909         gMessageSuite   = (OfxMessageSuiteV1 *)     fetchSuite(kOfxMessageSuite, 1);
3910         gMessageSuiteV2 = (OfxMessageSuiteV2 *)     fetchSuite(kOfxMessageSuite, 2, true);
3911         gProgressSuiteV1 = (OfxProgressSuiteV1 *)     fetchSuite(kOfxProgressSuite, 1, true);
3912         gProgressSuiteV2 = (OfxProgressSuiteV2 *)     fetchSuite(kOfxProgressSuite, 2, true);
3913         gTimeLineSuite   = (OfxTimeLineSuiteV1 *)     fetchSuite(kOfxTimeLineSuite, 1, true);
3914         gParametricParameterSuite = (OfxParametricParameterSuiteV1*) fetchSuite(kOfxParametricParameterSuite, 1, true);
3915 #ifdef OFX_SUPPORTS_OPENGLRENDER
3916         gOpenGLRenderSuite = (OfxImageEffectOpenGLRenderSuiteV1*) fetchSuite(kOfxOpenGLRenderSuite, 1, true);
3917 #endif
3918 #ifdef OFX_EXTENSIONS_NUKE
3919         gCameraSuite = (NukeOfxCameraSuiteV1*) fetchSuite(kNukeOfxCameraSuite, 1, true );
3920         gImageEffectPlaneSuiteV1 = (FnOfxImageEffectPlaneSuiteV1*) fetchSuite(kFnOfxImageEffectPlaneSuite, 1, true );
3921         gImageEffectPlaneSuiteV2 = (FnOfxImageEffectPlaneSuiteV2*) fetchSuite(kFnOfxImageEffectPlaneSuite, 2, true );
3922 #endif
3923 #ifdef OFX_EXTENSIONS_VEGAS
3924         gVegasProgressSuite   = (OfxVegasProgressSuiteV1 *)     fetchSuite(kOfxVegasProgressSuite, 1, true);
3925         gVegasStereoscopicImageSuite  = (OfxVegasStereoscopicImageSuiteV1 *) fetchSuite(kOfxVegasStereoscopicImageEffectSuite, 1, true);
3926         gVegasKeyframeSuite   = (OfxVegasKeyframeSuiteV1 *)     fetchSuite(kOfxVegasKeyframeSuite, 1, true);
3927 #endif
3928 
3929         // OK check and fetch host information
3930         fetchHostDescription(gHost);
3931 
3932         /// and set some dendent flags
3933         OFX::gHostDescription.supportsMessageSuiteV2 = gMessageSuiteV2 != NULL;
3934 #ifdef OFX_EXTENSIONS_VEGAS
3935         OFX::gHostDescription.supportsProgressSuite = (gProgressSuiteV1 != NULL || gProgressSuiteV2 != NULL || gVegasProgressSuite != NULL);
3936 #else
3937         OFX::gHostDescription.supportsProgressSuite = (gProgressSuiteV1 != NULL || gProgressSuiteV2 != NULL);
3938 #endif
3939         OFX::gHostDescription.supportsTimeLineSuite = gTimeLineSuite != NULL;
3940 
3941         // fetch the interact suite if the host supports interaction
3942         if(OFX::gHostDescription.supportsOverlays || OFX::gHostDescription.supportsCustomInteract)
3943           gInteractSuite  = (OfxInteractSuiteV1 *)    fetchSuite(kOfxInteractSuite, 1);
3944 
3945 #ifdef OFX_EXTENSIONS_VEGAS
3946 #if defined(WIN32) || defined(WIN64)
3947         gHWNDInteractSuite  = (OfxHWNDInteractSuiteV1 *)    fetchSuite(kOfxHWndInteractSuite, 1, true);
3948 #endif // #if defined(WIN32) || defined(WIN64)
3949 #endif
3950       }
3951 
3952       // initialise the validation code
3953       OFX::Validation::initialise();
3954 
3955       // validate the host
3956       OFX::Validation::validateHostProperties(gHost);
3957 
3958     }
3959 
3960     /** @brief Library side unload action, this fetches all the suite pointers */
3961     static
unloadAction(const char * id,unsigned int majorVersion,unsigned int minorVersion)3962     void unloadAction(const char* id, unsigned int majorVersion, unsigned int minorVersion)
3963     {
3964       gLoadCount--;
3965       if (gLoadCount<0) {
3966         OFX::Log::warning(true, "OFX Plugin '%s' is already unloaded.", id);
3967         return;
3968       }
3969 
3970       if(gLoadCount==0)
3971       {
3972         // force these to null
3973         gEffectSuite = 0;
3974         gPropSuite = 0;
3975         gParamSuite = 0;
3976         gMemorySuite = 0;
3977         gThreadSuite = 0;
3978         gMessageSuite = 0;
3979         gMessageSuiteV2 = 0;
3980         gInteractSuite = 0;
3981         gParametricParameterSuite = 0;
3982 #ifdef OFX_EXTENSIONS_NUKE
3983         gCameraSuite = 0;
3984         gImageEffectPlaneSuiteV1 = 0;
3985         gImageEffectPlaneSuiteV2 = 0;
3986 #endif
3987 #ifdef OFX_EXTENSIONS_VEGAS
3988 #if defined(WIN32) || defined(WIN64)
3989         gHWNDInteractSuite  = 0;
3990 #endif // #if defined(WIN32) || defined(WIN64)
3991         gVegasStereoscopicImageSuite  = 0;
3992         gVegasKeyframeSuite  = 0;
3993 #endif
3994       }
3995 
3996       {
3997         OFX::Private::VersionIDKey key;
3998         key.id = id;
3999         key.majorVersion = majorVersion;
4000         key.minorVersion = minorVersion;
4001         EffectDescriptorMap::iterator it = gEffectDescriptors.find(key);
4002         EffectContextMap& toBeDeleted = it->second;
4003         for(EffectContextMap::iterator it2 = toBeDeleted.begin(); it2 != toBeDeleted.end(); ++it2)
4004         {
4005           OFX::ImageEffectDescriptor* desc = it2->second;
4006           delete desc;
4007         }
4008         gEffectDescriptors.erase(it);
4009       }
4010       {
4011         OFX::OfxPlugInfoMap::iterator it = OFX::plugInfoMap.find(id);
4012         OfxPlugin* plug = it->second._plug;
4013         OFX::OfxPluginArray::iterator it2 = std::find(ofxPlugs.begin(), ofxPlugs.end(), plug);
4014         if (it2 != ofxPlugs.end()) {
4015           (*it2) = 0;
4016         }
4017         delete plug;
4018         OFX::plugInfoMap.erase(it);
4019       }
4020     }
4021 
4022 
4023     /** @brief fetches our pointer out of the props on the handle */
retrieveImageEffectPointer(OfxImageEffectHandle handle)4024     ImageEffect *retrieveImageEffectPointer(OfxImageEffectHandle handle)
4025     {
4026       ImageEffect *instance;
4027 
4028       // get the prop set on the handle
4029       OfxPropertySetHandle propHandle;
4030       OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet(handle, &propHandle);
4031       throwSuiteStatusException(stat);
4032 
4033       // make our wrapper object
4034       PropertySet props(propHandle);
4035 
4036       // fetch the instance data out of the properties
4037       instance = (ImageEffect *) props.propGetPointer(kOfxPropInstanceData);
4038 
4039       OFX::Log::error(instance == 0, "Instance data handle in effect instance properties is NULL!");
4040 
4041       // need to throw something here
4042       if (!instance) {
4043         throwSuiteStatusException(kOfxStatErrBadHandle);
4044       }
4045       // and dance to the music
4046       return instance;
4047     }
4048 
4049     /** @brief Checks the handles passed into the plugin's main entry point */
4050     static
4051     void
checkMainHandles(const std::string & action,const void * handle,OfxPropertySetHandle inArgsHandle,OfxPropertySetHandle outArgsHandle,bool handleCanBeNull,bool inArgsCanBeNull,bool outArgsCanBeNull)4052       checkMainHandles(const std::string &action,  const void *handle,
4053       OfxPropertySetHandle inArgsHandle,  OfxPropertySetHandle outArgsHandle,
4054       bool handleCanBeNull, bool inArgsCanBeNull, bool outArgsCanBeNull)
4055     {
4056       if(handleCanBeNull)
4057         OFX::Log::warning(handle != 0, "Handle passed to '%s' is not null.", action.c_str());
4058       else
4059         OFX::Log::error(handle == 0, "'Handle passed to '%s' is null.", action.c_str());
4060 
4061       if(inArgsCanBeNull)
4062         OFX::Log::warning(inArgsHandle != 0, "'inArgs' Handle passed to '%s' is not null.", action.c_str());
4063       else
4064         OFX::Log::error(inArgsHandle == 0, "'inArgs' handle passed to '%s' is null.", action.c_str());
4065 
4066       if(outArgsCanBeNull)
4067         OFX::Log::warning(outArgsHandle != 0, "'outArgs' Handle passed to '%s' is not null.", action.c_str());
4068       else
4069         OFX::Log::error(outArgsHandle == 0, "'outArgs' handle passed to '%s' is null.", action.c_str());
4070 
4071       // validate the property sets on the arguments
4072       OFX::Validation::validateActionArgumentsProperties(action, inArgsHandle, outArgsHandle);
4073 
4074       // throw exceptions if null when not meant to be null
4075       if(!handleCanBeNull && !handle)         throwSuiteStatusException(kOfxStatErrBadHandle);
4076       if(!inArgsCanBeNull && !inArgsHandle)   throwSuiteStatusException(kOfxStatErrBadHandle);
4077       if(!outArgsCanBeNull && !outArgsHandle) throwSuiteStatusException(kOfxStatErrBadHandle);
4078     }
4079 
4080 
4081     /** @brief Fetches the arguments used in a render action 'inargs' property set into a POD struct */
4082     static void
getRenderActionArguments(RenderArguments & args,OFX::PropertySet inArgs)4083       getRenderActionArguments(RenderArguments &args,  OFX::PropertySet inArgs)
4084     {
4085       args.time = inArgs.propGetDouble(kOfxPropTime);
4086 
4087       args.renderScale.x = args.renderScale.y = 1.;
4088       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4089 
4090       args.renderWindow.x1 = args.renderWindow.y1 = args.renderWindow.x2 = args.renderWindow.y2 = 0;
4091       inArgs.propGetIntN(kOfxImageEffectPropRenderWindow, &args.renderWindow.x1, 4);
4092 
4093 #ifdef OFX_EXTENSIONS_RESOLVE
4094       args.isEnabledOpenCLRender = inArgs.propGetInt(kOfxImageEffectPropOpenCLEnabled, false) != 0;
4095       args.isEnabledCudaRender   = inArgs.propGetInt(kOfxImageEffectPropCudaEnabled, false) != 0;
4096       args.pOpenCLCmdQ           = inArgs.propGetPointer(kOfxImageEffectPropOpenCLCommandQueue, false);
4097 #endif
4098 
4099 #ifdef OFX_SUPPORTS_OPENGLRENDER
4100       // Don't throw an exception if the following inArgs are not present.
4101       // OpenGL rendering appeared in OFX 1.3
4102       args.openGLEnabled = inArgs.propGetInt(kOfxImageEffectPropOpenGLEnabled, false) != 0;
4103 #ifdef OFX_EXTENSIONS_NATRON
4104       args.openGLContextData = args.openGLEnabled ? inArgs.propGetPointer(kNatronOfxImageEffectPropOpenGLContextData, false) : NULL;
4105 #endif
4106 #endif
4107 
4108       // Don't throw an exception if the following inArgs are not present:
4109       // They appeared in OFX 1.2.
4110       args.sequentialRenderStatus = inArgs.propGetInt(kOfxImageEffectPropSequentialRenderStatus, false) != 0;
4111       args.interactiveRenderStatus = inArgs.propGetInt(kOfxImageEffectPropInteractiveRenderStatus, false) != 0;
4112 
4113       // kOfxImageEffectPropRenderQualityDraft appeared in OFX 1.4
4114       args.renderQualityDraft = inArgs.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0;
4115 
4116 #ifdef OFX_EXTENSIONS_NUKE
4117       args.renderView = 0; // to support both Nuke and Vegas
4118 #endif
4119 
4120 #ifdef OFX_EXTENSIONS_VEGAS
4121       args.viewsToRender = inArgs.propGetInt(kOfxImageEffectPropViewsToRender, 0, false);
4122       args.renderView = inArgs.propGetInt(kOfxImageEffectPropRenderView, 0, false);
4123 #endif
4124 
4125 #ifdef OFX_EXTENSIONS_NUKE
4126       if (args.renderView == 0) {
4127         args.renderView = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4128       }
4129       int numPlanes = inArgs.propGetDimension(kOfxImageEffectPropRenderPlanes, false);
4130       for (int i = 0; i < numPlanes; ++i) {
4131         args.planes.push_back(inArgs.propGetString(kOfxImageEffectPropRenderPlanes, i, false));
4132       }
4133 #endif
4134 
4135       args.fieldToRender = eFieldNone;
4136       std::string str = inArgs.propGetString(kOfxImageEffectPropFieldToRender);
4137       try {
4138         args.fieldToRender = mapStrToFieldEnum(str);
4139       }
4140       catch (std::invalid_argument) {
4141         // dud field?
4142         OFX::Log::error(true, "Unknown field to render '%s'", str.c_str());
4143 
4144         // HACK need to throw something to cause a failure
4145       }
4146 
4147 #ifdef OFX_EXTENSIONS_VEGAS
4148       if (args.renderQualityDraft) { // OFX 1.4 property wins over Vegas extension
4149         args.renderQuality = eVegasRenderQualityDraft;
4150       } else {
4151         args.renderQuality = eVegasRenderQualityBest;
4152         std::string strQuality = inArgs.propGetString(kOfxImageEffectPropRenderQuality, /*throwOnFailure*/false);
4153         try {
4154           args.renderQuality = mapToVegasRenderQualityEnum(strQuality);
4155         } catch (std::invalid_argument) {
4156           // dud field?
4157           OFX::Log::error(true, "Unknown render quality '%s'", str.c_str());
4158         }
4159       }
4160 #endif
4161     }
4162 
4163     /** @brief Library side render action, fetches relevant properties and calls the client code */
4164     static
4165     void
renderAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4166       renderAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4167     {
4168       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4169       RenderArguments args;
4170 
4171       // get the arguments
4172       getRenderActionArguments(args, inArgs);
4173 
4174       // and call the plugin client render code
4175       effectInstance->render(args);
4176     }
4177 
4178     /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */
4179     static
4180     void
beginSequenceRenderAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4181       beginSequenceRenderAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4182     {
4183       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4184 
4185       BeginSequenceRenderArguments args;
4186 
4187       args.frameRange.min = args.frameRange.max = 0.;
4188       inArgs.propGetDoubleN(kOfxImageEffectPropFrameRange, &args.frameRange.min, 2);
4189 
4190       args.frameStep      = inArgs.propGetDouble(kOfxImageEffectPropFrameStep, 0);
4191 
4192       args.renderScale.x = args.renderScale.y = 1.;
4193       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4194 
4195 #ifdef OFX_EXTENSIONS_RESOLVE
4196       args.isEnabledOpenCLRender = inArgs.propGetInt(kOfxImageEffectPropOpenCLEnabled, false) != 0;
4197       args.isEnabledCudaRender   = inArgs.propGetInt(kOfxImageEffectPropCudaEnabled, false) != 0;
4198       args.pOpenCLCmdQ           = inArgs.propGetPointer(kOfxImageEffectPropOpenCLCommandQueue, false);
4199 #endif
4200 
4201 #ifdef OFX_SUPPORTS_OPENGLRENDER
4202       // Don't throw an exception if the following inArgs are not present.
4203       // OpenGL rendering appeared in OFX 1.3
4204       args.openGLEnabled = inArgs.propGetInt(kOfxImageEffectPropOpenGLEnabled, false) != 0;
4205 #ifdef OFX_EXTENSIONS_NATRON
4206       args.openGLContextData = args.openGLEnabled ? inArgs.propGetPointer(kNatronOfxImageEffectPropOpenGLContextData, false) : NULL;
4207 #endif
4208 #endif
4209       args.isInteractive = inArgs.propGetInt(kOfxPropIsInteractive) != 0;
4210       // Don't throw an exception if the following inArgs are not present:
4211       // They appeared in OFX 1.2
4212       args.sequentialRenderStatus = inArgs.propGetInt(kOfxImageEffectPropSequentialRenderStatus, false) != 0;
4213       args.interactiveRenderStatus = inArgs.propGetInt(kOfxImageEffectPropInteractiveRenderStatus, false) != 0;
4214 
4215       // kOfxImageEffectPropRenderQualityDraft appeared in OFX 1.4
4216       args.renderQualityDraft = inArgs.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0;
4217 
4218 #ifdef OFX_EXTENSIONS_NUKE
4219       args.view = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4220 #endif
4221 
4222       // and call the plugin client render code
4223       effectInstance->beginSequenceRender(args);
4224     }
4225 
4226     /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */
4227     static
4228     void
endSequenceRenderAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4229       endSequenceRenderAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4230     {
4231       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4232 
4233       EndSequenceRenderArguments args;
4234 
4235       args.renderScale.x = args.renderScale.y = 1.;
4236       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4237 
4238 #ifdef OFX_SUPPORTS_OPENGLRENDER
4239       // Don't throw an exception if the following inArgs are not present.
4240       // OpenGL rendering appeared in OFX 1.3
4241       args.openGLEnabled = inArgs.propGetInt(kOfxImageEffectPropOpenGLEnabled, false) != 0;
4242 #ifdef OFX_EXTENSIONS_NATRON
4243       args.openGLContextData = args.openGLEnabled ? inArgs.propGetPointer(kNatronOfxImageEffectPropOpenGLContextData, false) : NULL;
4244 #endif
4245 #endif
4246       args.isInteractive = inArgs.propGetInt(kOfxPropIsInteractive) != 0;
4247       // Don't throw an exception if the following inArgs are not present:
4248       // They appeared in OFX 1.2
4249       args.sequentialRenderStatus = inArgs.propGetInt(kOfxImageEffectPropSequentialRenderStatus, false) != 0;
4250       args.interactiveRenderStatus = inArgs.propGetInt(kOfxImageEffectPropInteractiveRenderStatus, false) != 0;
4251 
4252       // kOfxImageEffectPropRenderQualityDraft appeared in OFX 1.4
4253       args.renderQualityDraft = inArgs.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0;
4254 
4255 #ifdef OFX_EXTENSIONS_NUKE
4256       args.view = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4257 #endif
4258 
4259       // and call the plugin client render code
4260       effectInstance->endSequenceRender(args);
4261     }
4262 
4263 
4264     /** @brief Fetches the arguments used in a isIdentity action 'inargs' property set into a POD struct */
4265     static void
getIsIdentityActionArguments(IsIdentityArguments & args,OFX::PropertySet inArgs)4266       getIsIdentityActionArguments(IsIdentityArguments &args,  OFX::PropertySet inArgs)
4267     {
4268       args.time = inArgs.propGetDouble(kOfxPropTime);
4269 
4270       args.renderScale.x = args.renderScale.y = 1.;
4271       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4272 
4273       args.renderWindow.x1 = args.renderWindow.y1 = args.renderWindow.x2 = args.renderWindow.y2 = 0;
4274       inArgs.propGetIntN(kOfxImageEffectPropRenderWindow, &args.renderWindow.x1, 4);
4275 
4276 #ifdef OFX_EXTENSIONS_NUKE
4277       args.view = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4278       args.plane = inArgs.propGetString(kOfxImageEffectPropIdentityPlane, 0, false);
4279 #endif
4280 
4281       std::string str = inArgs.propGetString(kOfxImageEffectPropFieldToRender);
4282       try {
4283         args.fieldToRender = mapStrToFieldEnum(str);
4284       }
4285       catch (std::invalid_argument) {
4286         // dud field?
4287         OFX::Log::error(true, "Unknown field to render '%s'", str.c_str());
4288 
4289         // HACK need to throw something to cause a failure
4290       }
4291     }
4292 
4293     /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */
4294     static
4295     bool
isIdentityAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs)4296       isIdentityAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs)
4297     {
4298       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4299       IsIdentityArguments args;
4300 
4301       // get the arguments
4302       getIsIdentityActionArguments(args, inArgs);
4303 
4304       // and call the plugin client isIdentity code
4305       Clip *identityClip = 0;
4306       double identityTime = args.time;
4307 #ifdef OFX_EXTENSIONS_NUKE
4308       int identityView = args.view;
4309       std::string identityPlane = args.plane;
4310 #endif
4311       bool v = effectInstance->isIdentity(args, identityClip, identityTime
4312 #ifdef OFX_EXTENSIONS_NUKE
4313                                           , identityView, identityPlane
4314 #endif
4315                                           );
4316 
4317       if(v && identityClip) {
4318         outArgs.propSetString(kOfxPropName, identityClip->name());
4319         outArgs.propSetDouble(kOfxPropTime, identityTime);
4320 #ifdef OFX_EXTENSIONS_NUKE
4321         outArgs.propSetInt(kFnOfxImageEffectPropView, identityView, 0, false);
4322         outArgs.propSetString(kOfxImageEffectPropIdentityPlane, identityPlane, 0, false);
4323 #endif
4324         return true;
4325       }
4326       return false;
4327     }
4328 
4329     /** @brief Library side get region of definition function */
4330     static
4331     bool
regionOfDefinitionAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs)4332       regionOfDefinitionAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs)
4333     {
4334       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4335       RegionOfDefinitionArguments args;
4336 
4337       args.renderScale.x = args.renderScale.y = 1.;
4338       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4339 
4340       args.time = inArgs.propGetDouble(kOfxPropTime);
4341 
4342 #ifdef OFX_EXTENSIONS_NUKE
4343       args.view = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4344 #endif
4345 
4346       // and call the plugin client code
4347       OfxRectD rod;
4348       bool v = effectInstance->getRegionOfDefinition(args, rod);
4349 
4350       if(v) {
4351         outArgs.propSetDoubleN(kOfxImageEffectPropRegionOfDefinition, &rod.x1, 4);
4352         return true;
4353       }
4354       return false;
4355     }
4356 
4357     /** @brief Library side get regions of interest function */
4358     static
4359     bool
regionsOfInterestAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs,const char * plugname,unsigned int majorVersion,unsigned int minorVersion)4360       regionsOfInterestAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs, const char* plugname, unsigned int majorVersion, unsigned int minorVersion)
4361     {
4362       /** @brief local class to set the roi of a clip */
4363       class LOCAL ActualROISetter : public OFX::RegionOfInterestSetter {
4364         OFX::PropertySet &outArgs_;
4365         bool doneSomething_;
4366         const std::map<std::string, std::string>& clipROIPropNames_;
4367       public :
4368         /** @brief ctor */
4369         ActualROISetter(OFX::PropertySet &args, const std::map<std::string, std::string>& clipROIPropNames)
4370           : outArgs_(args)
4371           , doneSomething_(false)
4372           , clipROIPropNames_(clipROIPropNames)
4373         { }
4374 
4375         /** @brief did we set something ? */
4376         bool didSomething(void) const {return doneSomething_;}
4377 
4378         /** @brief set the RoI of the clip */
4379         virtual void setRegionOfInterest(const Clip &clip, const OfxRectD &roi)
4380         {
4381           std::map<std::string, std::string>::const_iterator it = clipROIPropNames_.find(clip.name());
4382           if(it==clipROIPropNames_.end())
4383             throw(Exception::PropertyUnknownToHost(clip.name().c_str()));
4384 
4385           // construct the name of the property
4386           const std::string& propName = it->second;
4387 
4388           // and set it
4389           outArgs_.propSetDoubleN(propName.c_str(), &roi.x1, 4);
4390 
4391           // and record the face we have done something
4392           doneSomething_ = true;
4393         }
4394       }; // end of local class
4395 
4396       // fetch our effect pointer
4397       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4398       RegionsOfInterestArguments args;
4399 
4400       // fetch in arguments from the prop handle
4401       args.renderScale.x = args.renderScale.y = 1.;
4402       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4403 
4404       args.regionOfInterest.x1 = args.regionOfInterest.y1 = args.regionOfInterest.x2 = args.regionOfInterest.y2 = 0.;
4405       inArgs.propGetDoubleN(kOfxImageEffectPropRegionOfInterest, &args.regionOfInterest.x1, 4);
4406 
4407       args.time = inArgs.propGetDouble(kOfxPropTime);
4408 
4409 #ifdef OFX_EXTENSIONS_NUKE
4410       args.view = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4411 #endif
4412 
4413       // make a roi setter object
4414       OFX::Private::VersionIDKey key;
4415       key.id = plugname;
4416       key.majorVersion = majorVersion;
4417       key.minorVersion = minorVersion;
4418       ActualROISetter setRoIs(outArgs, gEffectDescriptors[key][effectInstance->getContext()]->getClipROIPropNames());
4419 
4420       // and call the plugin client code
4421       effectInstance->getRegionsOfInterest(args, setRoIs);
4422 
4423       // did we do anything ?
4424       if(setRoIs.didSomething())
4425         return true;
4426       return false;
4427     }
4428 
4429     /** @brief Library side frames needed action */
4430     static
4431     bool
framesNeededAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs,const char * plugname,unsigned int majorVersion,unsigned int minorVersion)4432       framesNeededAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs, const char* plugname,  unsigned int majorVersion, unsigned int minorVersion)
4433     {
4434       /** @brief local class to set the frames needed from a clip */
4435       class LOCAL ActualSetter : public OFX::FramesNeededSetter {
4436         OFX::PropertySet &outArgs_;                                  /**< @brief property set to set values in */
4437         std::map<std::string, std::vector<OfxRangeD> > frameRanges_;  /**< @brief map holding a bunch of frame ranges, one for each clip */
4438         const std::map<std::string, std::string>& _clipFrameRangePropNames;
4439       public :
4440         /** @brief ctor */
4441         ActualSetter(OFX::PropertySet &args, const std::map<std::string, std::string>& clipFrameRangePropNames)
4442           : outArgs_(args), _clipFrameRangePropNames(clipFrameRangePropNames)
4443         { }
4444 
4445         /** @brief set the RoI of the clip */
4446         virtual void setFramesNeeded(const Clip &clip, const OfxRangeD &range)
4447         {
4448           // insert this into the vector which is in the map
4449           frameRanges_[clip.name()].push_back(range);
4450         }
4451 
4452         /** @brief write frameRanges_ back to the property set */
4453         bool setOutProperties(void)
4454         {
4455           bool didSomething = false;
4456 
4457           std::map<std::string, std::vector<OfxRangeD> >::iterator i;
4458 
4459           for(i = frameRanges_.begin(); i != frameRanges_.end(); ++i) {
4460             if(i->first != kOfxImageEffectOutputClipName) {
4461               didSomething = true;
4462 
4463               // Make the property name we are setting
4464               const std::map<std::string, std::string>::const_iterator it = _clipFrameRangePropNames.find(i->first);
4465               if(it==_clipFrameRangePropNames.end())
4466                 throw(Exception::PropertyUnknownToHost(i->first.c_str()));
4467 
4468               const std::string& propName = it->second;
4469 
4470               // fetch the list of frame ranges
4471               std::vector<OfxRangeD> &clipRange = i->second;
4472               std::vector<OfxRangeD>::iterator j;
4473               int n = 0;
4474 
4475               // The host may not have the property if the clip is not connected (Resolve).
4476               // Just proceed to the next clip.
4477               if (outArgs_.propExists(propName.c_str())) {
4478               // and set 'em
4479               for(j = clipRange.begin(); j < clipRange.end(); ++j) {
4480                 outArgs_.propSetDouble(propName.c_str(), j->min, n++);
4481                 outArgs_.propSetDouble(propName.c_str(), j->max, n++);
4482               }
4483               }
4484             }
4485           }
4486 
4487           return didSomething;
4488         }
4489 
4490       }; // end of local class
4491 
4492       // fetch our effect pointer
4493       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4494       FramesNeededArguments args;
4495 
4496       // fetch in arguments from the prop handle
4497       args.time = inArgs.propGetDouble(kOfxPropTime);
4498 
4499       // make a roi setter object
4500       OFX::Private::VersionIDKey key;
4501       key.id = plugname;
4502       key.majorVersion = majorVersion;
4503       key.minorVersion = minorVersion;
4504       ActualSetter setFrames(outArgs, gEffectDescriptors[key][effectInstance->getContext()]->getClipFrameRangePropNames());
4505 
4506       // and call the plugin client code
4507       effectInstance->getFramesNeeded(args, setFrames);
4508 
4509       // Write it back to the properties and see if we set anything
4510       if(setFrames.setOutProperties())
4511         return true;
4512       return false;
4513     }
4514 
4515     /** @brief Library side get regions of interest function */
4516     static
4517     bool
getTimeDomainAction(OfxImageEffectHandle handle,OFX::PropertySet & outArgs)4518       getTimeDomainAction(OfxImageEffectHandle handle, OFX::PropertySet &outArgs)
4519     {
4520       // fetch our effect pointer
4521       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4522 
4523       // we can only be a general context effect, so check that this is true
4524 #ifdef OFX_EXTENSIONS_TUTTLE
4525       OFX::Log::error(effectInstance->getContext() != eContextGeneral &&
4526                       effectInstance->getContext() != eContextReader &&
4527                       effectInstance->getContext() != eContextGenerator, "Calling kOfxImageEffectActionGetTimeDomain on an effect that is not a 'general', 'reader' or 'generator' context effect.");
4528 #else
4529       OFX::Log::error(effectInstance->getContext() != eContextGeneral, "Calling kOfxImageEffectActionGetTimeDomain on an effect that is not a general context effect.");
4530 #endif
4531 
4532       OfxRangeD timeDomain;
4533 
4534       // and call the plugin client code
4535       bool v = effectInstance->getTimeDomain(timeDomain);
4536 
4537       if(v) {
4538         outArgs.propSetDoubleN(kOfxImageEffectPropFrameRange, &timeDomain.min, 2);
4539       }
4540 
4541       return v;
4542     }
4543 
4544 #ifdef OFX_SUPPORTS_OPENGLRENDER
4545     /** @brief Library side context attached function */
4546     static
4547     void
contextAttachedAction(OfxImageEffectHandle handle,OFX::PropertySet & outArgs)4548       contextAttachedAction(OfxImageEffectHandle handle, OFX::PropertySet &outArgs)
4549     {
4550       // fetch our effect pointer
4551       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4552 
4553       bool createContextData = false;
4554 #ifdef OFX_EXTENSIONS_NATRON
4555       createContextData = outArgs.propGetDimension(kNatronOfxImageEffectPropOpenGLContextData, false) > 0; // don't throw if the host does not support it
4556 #endif
4557 
4558       // and call the plugin client code
4559       void* v = effectInstance->contextAttached(createContextData);
4560 
4561 #ifdef OFX_EXTENSIONS_NATRON
4562       if(v) {
4563         outArgs.propSetPointer(kNatronOfxImageEffectPropOpenGLContextData, v, false); // don't throw if the host does not support it
4564       }
4565 #endif
4566     }
4567 
4568     /** @brief Library side context attached function */
4569     static
4570     void
contextDetachedAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4571       contextDetachedAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4572     {
4573       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4574 
4575 #ifdef OFX_EXTENSIONS_NATRON
4576       void* contextData = inArgs.propGetPointer(kNatronOfxImageEffectPropOpenGLContextData, false);
4577 #else
4578       void* contextData = NULL;
4579 #endif
4580 
4581       // and call the plugin client code
4582       effectInstance->contextDetached(contextData);
4583     }
4584 #endif
4585 
4586     /** @brief Library side get regions of interest function */
4587     static
4588     bool
clipPreferencesAction(OfxImageEffectHandle handle,OFX::PropertySet & outArgs,const char * plugname,unsigned int majorVersion,unsigned int minorVersion)4589       clipPreferencesAction(OfxImageEffectHandle handle, OFX::PropertySet &outArgs, const char* plugname, unsigned int majorVersion, unsigned int minorVersion)
4590     {
4591       // fetch our effect pointer
4592       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4593 
4594       // set up our clip preferences setter
4595       OFX::Private::VersionIDKey key;
4596       key.id = plugname;
4597       key.majorVersion = majorVersion;
4598       key.minorVersion = minorVersion;
4599       ImageEffectDescriptor* desc = gEffectDescriptors[key][effectInstance->getContext()];
4600       ClipPreferencesSetter prefs(outArgs, desc->getClipDepthPropNames(), desc->getClipComponentPropNames(), desc->getClipPARPropNames());
4601 
4602       // and call the plug-in client code
4603       effectInstance->getClipPreferences(prefs);
4604 
4605       // did we do anything ?
4606       if(prefs.didSomething())
4607         return true;
4608       return false;
4609     }
4610 
4611     /** @brief Library side begin instance changed action */
4612     static
4613     void
beginInstanceChangedAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4614       beginInstanceChangedAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4615     {
4616       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4617 
4618       std::string reasonStr = inArgs.propGetString(kOfxPropChangeReason);
4619       InstanceChangeReason reason = mapToInstanceChangedReason(reasonStr);
4620 
4621       // and call the plugin client code
4622       effectInstance->beginChanged(reason);
4623     }
4624 
4625     /** @brief Library side instance changed action */
4626     static
4627     void
instanceChangedAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4628       instanceChangedAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4629     {
4630       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4631 
4632       InstanceChangedArgs args;
4633 
4634       // why did it change
4635       std::string reasonStr = inArgs.propGetString(kOfxPropChangeReason);
4636       args.reason = mapToInstanceChangedReason(reasonStr);
4637       args.time = inArgs.propGetDouble(kOfxPropTime);
4638       args.renderScale.x = args.renderScale.y = 1.;
4639       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4640 
4641       // what changed
4642       std::string changedType = inArgs.propGetString(kOfxPropType);
4643       std::string changedName = inArgs.propGetString(kOfxPropName);
4644 
4645       if(changedType == kOfxTypeParameter) {
4646         // and call the plugin client code
4647         effectInstance->changedParam(args, changedName);
4648       }
4649       else if(changedType == kOfxTypeClip) {
4650         // and call the plugin client code
4651         effectInstance->changedClip(args, changedName);
4652       }
4653       else {
4654         OFX::Log::error(true, "Instance Changed called with unknown type '%s' of object '%s'", changedType.c_str(), changedName.c_str());
4655       }
4656     }
4657 
4658     /** @brief Library side end instance changed action */
4659     static
4660     void
endInstanceChangedAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4661       endInstanceChangedAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4662     {
4663       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4664 
4665       std::string reasonStr = inArgs.propGetString(kOfxPropChangeReason);
4666       InstanceChangeReason reason = mapToInstanceChangedReason(reasonStr);
4667 
4668       // and call the plugin client code
4669       effectInstance->endChanged(reason);
4670     }
4671 
4672 #ifdef OFX_EXTENSIONS_VEGAS
4673     /** @brief Library side uplift vegas keyframe action */
4674     static
4675     void
upliftVegasKeyframeAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs)4676       upliftVegasKeyframeAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs)
4677     {
4678       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4679 
4680       SonyVegasUpliftArguments upliftArgs(inArgs);
4681 
4682       upliftArgs.keyframeCount = inArgs.propGetDimension(kOfxPropVegasUpliftKeyframeData);
4683       upliftArgs.guidUplift = inArgs.propGetString(kOfxImageEffectPropVegasUpliftGUID);
4684 
4685       upliftArgs.commonData = inArgs.propGetPointer(kOfxPropVegasUpliftData, false);
4686       upliftArgs.commonDataSize = inArgs.propGetInt(kOfxPropVegasUpliftDataLength, false);
4687 
4688       // and call the plugin client code
4689       effectInstance->upliftVegasKeyframes(upliftArgs);
4690     }
4691 
4692     /** @brief Library side invoke About function */
4693     static
4694     bool
invokeAbout(OfxImageEffectHandle handle,const char *)4695       invokeAbout(OfxImageEffectHandle handle, const char* /*plugname*/)
4696     {
4697       // fetch our effect pointer
4698       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4699 
4700       // and call the plug-in client code
4701       return effectInstance->invokeAbout();
4702     }
4703 
4704     /** @brief Library side invoke Help function */
4705     static
4706     bool
invokeHelp(OfxImageEffectHandle handle,const char *)4707       invokeHelp(OfxImageEffectHandle handle, const char* /*plugname*/)
4708     {
4709       // fetch our effect pointer
4710       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4711 
4712       // and call the plug-in client code
4713       return effectInstance->invokeHelp();
4714     }
4715 #endif
4716 #ifdef OFX_EXTENSIONS_NUKE
4717 
4718     static
4719     bool
getFrameViewsNeededAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs,const char * plugname,unsigned int majorVersion,unsigned int minorVersion)4720     getFrameViewsNeededAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs, const char* plugname, unsigned int majorVersion, unsigned int minorVersion)
4721     {
4722         ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4723         FrameViewsNeededArguments args;
4724         args.time = inArgs.propGetDouble(kOfxPropTime);
4725         args.view = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4726 
4727 
4728         OFX::Private::VersionIDKey key;
4729         key.id = plugname;
4730         key.majorVersion = majorVersion;
4731         key.minorVersion = minorVersion;
4732         ImageEffectDescriptor* desc = gEffectDescriptors[key][effectInstance->getContext()];
4733         FrameViewsNeededSetter setter(outArgs,desc->getClipFrameViewsPropNames());
4734         effectInstance->getFrameViewsNeeded(args,setter);
4735         if (setter.setOutProperties()) {
4736             return true;
4737         }
4738         return false;
4739     }
4740 
4741     static
4742     OfxStatus
getClipComponentsAction(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs,const char * plugname,unsigned int majorVersion,unsigned int minorVersion)4743     getClipComponentsAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs, const char* plugname, unsigned int majorVersion, unsigned int minorVersion)
4744     {
4745         ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4746         ClipComponentsArguments args;
4747         args.time = inArgs.propGetDouble(kOfxPropTime);
4748         args.view = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4749 
4750         OFX::Private::VersionIDKey key;
4751         key.id = plugname;
4752         key.majorVersion = majorVersion;
4753         key.minorVersion = minorVersion;
4754         ImageEffectDescriptor* desc = gEffectDescriptors[key][effectInstance->getContext()];
4755         ClipComponentsSetter setter(outArgs,desc->getClipPlanesPropNames());
4756         OfxStatus stat = effectInstance->getClipComponents(args,setter);
4757         if (!setter.setOutProperties()) {
4758             return kOfxStatReplyDefault;
4759         }
4760         return stat;
4761     }
4762 
4763     /** @brief Action called in place of a render to recover a transform matrix from an effect. */
4764     static
4765     bool
getTransform(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs)4766       getTransform(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs)
4767     {
4768       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4769       TransformArguments args;
4770 
4771       // get the arguments
4772       args.time = inArgs.propGetDouble(kOfxPropTime);
4773 
4774       args.renderScale.x = args.renderScale.y = 1.;
4775       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4776 
4777       // kOfxImageEffectPropRenderQualityDraft appeared in OFX 1.4
4778       args.renderQualityDraft = inArgs.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0;
4779 
4780       args.renderView = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4781 
4782       std::string str = inArgs.propGetString(kOfxImageEffectPropFieldToRender);
4783       try {
4784         args.fieldToRender = eFieldBoth;
4785         args.fieldToRender = mapStrToFieldEnum(str);
4786       } catch (std::invalid_argument) {
4787         // dud field?
4788         OFX::Log::error(true, "Unknown field to render '%s'", str.c_str());
4789 
4790         // HACK need to throw something to cause a failure
4791       }
4792 
4793       // and call the plugin client getTransform code
4794       Clip *transformClip = 0;
4795       double transformMatrix[9];
4796       bool v = effectInstance->getTransform(args, transformClip, transformMatrix);
4797 
4798       if(v && transformClip) {
4799         outArgs.propSetString(kOfxPropName, transformClip->name());
4800         outArgs.propSetDoubleN(kFnOfxPropMatrix2D, transformMatrix, 9);
4801         return true; // the transfrom and clip name were set and can be used to modify the named image appropriately
4802       }
4803       return false; // don't attempt to use the transform matrix, but render the image as per normal
4804     }
4805 #endif
4806 
4807 #ifdef OFX_EXTENSIONS_NATRON
4808     /** @brief Action called in place of a render to recover a distortion function from an effect. */
4809     static
4810     bool
getInverseDistortion(OfxImageEffectHandle handle,OFX::PropertySet inArgs,OFX::PropertySet & outArgs)4811       getInverseDistortion(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs)
4812     {
4813       ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
4814       DistortionArguments args;
4815 
4816       // get the arguments
4817       args.time = inArgs.propGetDouble(kOfxPropTime);
4818 
4819       args.renderScale.x = args.renderScale.y = 1.;
4820       inArgs.propGetDoubleN(kOfxImageEffectPropRenderScale, &args.renderScale.x, 2);
4821 
4822       // kOfxImageEffectPropRenderQualityDraft appeared in OFX 1.4
4823       args.renderQualityDraft = inArgs.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0;
4824 
4825       args.renderView = inArgs.propGetInt(kFnOfxImageEffectPropView, 0, false);
4826 
4827       std::string str = inArgs.propGetString(kOfxImageEffectPropFieldToRender);
4828       try {
4829         args.fieldToRender = eFieldBoth;
4830         args.fieldToRender = mapStrToFieldEnum(str);
4831       } catch (std::invalid_argument) {
4832         // dud field?
4833         OFX::Log::error(true, "Unknown field to render '%s'", str.c_str());
4834 
4835         // HACK need to throw something to cause a failure
4836       }
4837 
4838       // and call the plugin client getTransform code
4839       Clip *transformClip = 0;
4840       double transformMatrix[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
4841       if ( effectInstance->getCanDistort() ) {
4842         OfxInverseDistortionFunctionV1 distortionFunc = NULL;
4843         void* distortionFunctionData = NULL;
4844         int distortionFunctionDataSize = 0;
4845         OfxInverseDistortionDataFreeFunctionV1 freeDataFunction = NULL;
4846         bool v = effectInstance->getInverseDistortion(args, transformClip, transformMatrix, &distortionFunc, &distortionFunctionData, &distortionFunctionDataSize, &freeDataFunction);
4847 
4848         if(v && transformClip) {
4849           outArgs.propSetString(kOfxPropName, transformClip->name());
4850           if (distortionFunc == NULL) {
4851             outArgs.propSetDoubleN(kOfxPropMatrix3x3, transformMatrix, 9);
4852           } else {
4853             outArgs.propSetPointer(kOfxPropInverseDistortionFunction, (void*)distortionFunc);
4854             outArgs.propSetPointer(kOfxPropInverseDistortionFunctionData, distortionFunctionData);
4855             outArgs.propSetInt(kOfxPropInverseDistortionFunctionDataSize, distortionFunctionDataSize);
4856             outArgs.propSetPointer(kOfxPropInverseDistortionDataFreeFunction, (void*)freeDataFunction);
4857           }
4858           return true; // the transfrom and clip name were set and can be used to modify the named image appropriately
4859         }
4860       } else {
4861         // effect can not distort, maybe it can transform?
4862 
4863         TransformArguments argsTransform = {
4864           args.time,
4865           args.renderScale,
4866           args.fieldToRender,
4867           args.renderQualityDraft,
4868           args.renderView
4869         };
4870 
4871         // get the transform in pixel coords using the legacy getTransform function, then convert it to canonical coords
4872         bool v = effectInstance->getTransform(argsTransform, transformClip, transformMatrix);
4873 
4874         if(v && transformClip) {
4875           outArgs.propSetString(kOfxPropName, transformClip->name());
4876           // transform from pixel to canonical
4877           double par = transformClip->getPixelAspectRatio();
4878           const bool fielded = args.fieldToRender == eFieldLower || args.fieldToRender == eFieldUpper;
4879           // FS = fielded ? 0.5 : 1.
4880           // canonical to pixel:
4881           // X' = (X * SX)/PAR -> multiply first column by SX/PAR
4882           // Y' = Y * SY * FS -> multiply second column by SY*FS
4883           // pixel to canonical:
4884           // X' = (X * PAR)/SX -> divide first line by SX/PAR
4885           // Y' = Y/(SY * FS) -> divide second line by SY*FS
4886           double fx = args.renderScale.x / par;
4887           double fy = args.renderScale.y * (fielded ? 0.5 : 1.);
4888           //transformMatrix[0] *= 1.;
4889           transformMatrix[1] *= fy/fx;
4890           transformMatrix[2] *= 1./fx;
4891           transformMatrix[3] *= fx/fy;
4892           //transformMatrix[4] *= 1.;
4893           transformMatrix[5] *= 1./fy;
4894           transformMatrix[6] *= fx;
4895           transformMatrix[7] *= fy;
4896           //transformMatrix[8] *= 1.;
4897 
4898           outArgs.propSetDoubleN(kOfxPropMatrix3x3, transformMatrix, 9);
4899           return true; // the transfrom and clip name were set and can be used to modify the named image appropriately
4900         }
4901       }
4902       return false; // don't attempt to use the distortion function, but render the image as per normal
4903     }
4904 #endif
4905 
4906     /** @brief The main entry point for the plugin
4907     */
mainEntryStr(const char * actionRaw,const void * handleRaw,OfxPropertySetHandle inArgsRaw,OfxPropertySetHandle outArgsRaw,const char * plugname)4908     OfxStatus mainEntryStr(const char    *actionRaw,
4909       const void    *handleRaw,
4910       OfxPropertySetHandle   inArgsRaw,
4911       OfxPropertySetHandle   outArgsRaw,
4912       const char* plugname)
4913     {
4914       OFX::Log::print("********************************************************************************");
4915       OFX::Log::print("START mainEntry (%s)", actionRaw);
4916       OFX::Log::indent();
4917       OfxStatus stat = kOfxStatReplyDefault;
4918       try {
4919 
4920         OfxPlugInfoMap::iterator it = plugInfoMap.find(plugname);
4921         if(it==plugInfoMap.end())
4922           throw;
4923 
4924         OFX::PluginFactory* factory = it->second._factory;
4925 
4926         // Cast the raw handle to be an image effect handle, because that is what it is
4927         OfxImageEffectHandle handle = (OfxImageEffectHandle) handleRaw;
4928 
4929         // Turn the arguments into wrapper objects to make our lives easier
4930         OFX::PropertySet inArgs(inArgsRaw);
4931         OFX::PropertySet outArgs(outArgsRaw);
4932 
4933         // turn the action into a std::string
4934         std::string action(actionRaw);
4935 
4936         // figure the actions
4937         if (action == kOfxActionLoad) {
4938           // call the support load function, param-less
4939           OFX::Private::loadAction();
4940 
4941           // call the plugin side load action, param-less
4942           factory->load();
4943 
4944           // got here, must be good
4945           stat = kOfxStatOK;
4946         }
4947 
4948         // figure the actions
4949         else if (action == kOfxActionUnload) {
4950           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, true, true, true);
4951 
4952           // call the plugin side unload action, param-less, should be called, eve if the stat above failed!
4953           factory->unload();
4954 
4955           // call the support unload function, param-less
4956           OFX::Private::unloadAction(plugname, it->second._plug->pluginVersionMajor, it->second._plug->pluginVersionMinor);
4957 
4958           // got here, must be good
4959           stat = kOfxStatOK;
4960         }
4961 
4962         else if(action == kOfxActionDescribe) {
4963           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
4964 
4965           // make the plugin descriptor
4966           ImageEffectDescriptor *desc = new ImageEffectDescriptor(handle);
4967 
4968           // validate the host
4969           OFX::Validation::validatePluginDescriptorProperties(fetchEffectProps(handle));
4970 
4971           //  and pass it to the plugin to do something with it
4972 
4973           factory->describe(*desc);
4974 
4975           // add it to our map
4976           OFX::Private::VersionIDKey key;
4977           key.id = it->first;
4978           key.majorVersion = it->second._plug->pluginVersionMajor;
4979           key.minorVersion = it->second._plug->pluginVersionMinor;
4980           EffectDescriptorMap::iterator it = gEffectDescriptors.find(key);
4981           if (it != gEffectDescriptors.end()) {
4982             EffectContextMap& contextMap = it->second;
4983             EffectContextMap::iterator it2 = contextMap.find(eContextNone);
4984             if (it2 != contextMap.end()) {
4985               delete it2->second;
4986             }
4987           }
4988           gEffectDescriptors[key][eContextNone] = desc;
4989 
4990           // got here, must be good
4991           stat = kOfxStatOK;
4992         }
4993         else if(action == kOfxImageEffectActionDescribeInContext) {
4994           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
4995 
4996           // make the plugin descriptor and pass it to the plugin to do something with it
4997           ImageEffectDescriptor *desc = new ImageEffectDescriptor(handle);
4998 
4999           // figure the context and map it to an enum
5000           std::string contextStr = inArgs.propGetString(kOfxImageEffectPropContext);
5001           ContextEnum context = mapToContextEnum(contextStr);
5002 
5003           // validate the host
5004           OFX::Validation::validatePluginDescriptorProperties(fetchEffectProps(handle));
5005 
5006           // call plugin describe in context
5007           factory->describeInContext(*desc, context);
5008 
5009           // add it to our map
5010           OFX::Private::VersionIDKey key;
5011           key.id = it->first;
5012           key.majorVersion = it->second._plug->pluginVersionMajor;
5013           key.minorVersion = it->second._plug->pluginVersionMinor;
5014           EffectDescriptorMap::iterator it = gEffectDescriptors.find(key);
5015           if (it != gEffectDescriptors.end()) {
5016             EffectContextMap& contextMap = it->second;
5017             EffectContextMap::iterator it2 = contextMap.find(context);
5018             if (it2 != contextMap.end()) {
5019               delete it2->second;
5020             }
5021           }
5022           gEffectDescriptors[key][context] = desc;
5023 
5024           // got here, must be good
5025           stat = kOfxStatOK;
5026         }
5027         else if(action == kOfxActionCreateInstance) {
5028           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5029 
5030           // fetch the effect props to figure the context
5031           PropertySet effectProps = fetchEffectProps(handle);
5032 
5033           // get the context and turn it into an enum
5034           std::string str = effectProps.propGetString(kOfxImageEffectPropContext);
5035           ContextEnum context = mapToContextEnum(str);
5036 
5037           // make the image effect instance for this context
5038           ImageEffect *instance = factory->createInstance(handle, context);
5039           (void)instance;
5040 
5041           // validate the plugin handle's properties
5042           OFX::Validation::validatePluginInstanceProperties(fetchEffectProps(handle));
5043 
5044           // got here, must be good
5045           stat = kOfxStatOK;
5046         }
5047         else if(action == kOfxActionDestroyInstance) {
5048           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5049 
5050           // fetch our pointer out of the props on the handle
5051           ImageEffect *instance = retrieveImageEffectPointer(handle);
5052 
5053           // kill it
5054           delete instance;
5055 
5056           // got here, must be good
5057           stat = kOfxStatOK;
5058         }
5059         else if(action == kOfxImageEffectActionRender) {
5060           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5061 
5062           // call the render action skin
5063           renderAction(handle, inArgs);
5064 
5065           // got here, must be good
5066           stat = kOfxStatOK;
5067         }
5068         else if(action == kOfxImageEffectActionBeginSequenceRender) {
5069           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5070 
5071           // call the begin render action skin
5072           beginSequenceRenderAction(handle, inArgs);
5073         }
5074         else if(action == kOfxImageEffectActionEndSequenceRender) {
5075           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5076 
5077           // call the begin render action skin
5078           endSequenceRenderAction(handle, inArgs);
5079         }
5080         else if(action == kOfxImageEffectActionIsIdentity) {
5081           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5082 
5083           // call the identity action, if it is, return OK
5084           if(isIdentityAction(handle, inArgs, outArgs))
5085             stat = kOfxStatOK;
5086         }
5087         else if(action == kOfxImageEffectActionGetRegionOfDefinition) {
5088           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5089 
5090           // call the rod action, return OK if it does something
5091           if(regionOfDefinitionAction(handle, inArgs, outArgs))
5092             stat = kOfxStatOK;
5093         }
5094         else if(action == kOfxImageEffectActionGetRegionsOfInterest) {
5095           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5096 
5097           // call the RoI action, return OK if it does something
5098           if(regionsOfInterestAction(handle, inArgs, outArgs, plugname, it->second._plug->pluginVersionMajor, it->second._plug->pluginVersionMinor))
5099             stat = kOfxStatOK;
5100         }
5101         else if(action == kOfxImageEffectActionGetFramesNeeded) {
5102           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5103 
5104           // call the frames needed action, return OK if it does something
5105           if(framesNeededAction(handle, inArgs, outArgs, plugname, it->second._plug->pluginVersionMajor, it->second._plug->pluginVersionMinor))
5106             stat = kOfxStatOK;
5107         }
5108         else if(action == kOfxImageEffectActionGetClipPreferences) {
5109           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, false);
5110 
5111           // call the frames needed action, return OK if it does something
5112           if(clipPreferencesAction(handle, outArgs, plugname, it->second._plug->pluginVersionMajor, it->second._plug->pluginVersionMinor))
5113             stat = kOfxStatOK;
5114         }
5115         else if(action == kOfxActionPurgeCaches) {
5116           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5117 
5118           // fetch our pointer out of the props on the handle
5119           ImageEffect *instance = retrieveImageEffectPointer(handle);
5120 
5121           // purge 'em
5122           instance->purgeCaches();
5123         }
5124         else if(action == kOfxActionSyncPrivateData) {
5125           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5126 
5127           // fetch our pointer out of the props on the handle
5128           ImageEffect *instance = retrieveImageEffectPointer(handle);
5129 
5130           // and sync it
5131           instance->syncPrivateData();
5132         }
5133         else if(action == kOfxImageEffectActionGetTimeDomain) {
5134           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, false);
5135 
5136           // call the get time domain action
5137           if(getTimeDomainAction(handle, outArgs))
5138             stat = kOfxStatOK;
5139         }
5140         else if(action == kOfxActionBeginInstanceChanged) {
5141           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5142 
5143           // call the begin instance changed action
5144           beginInstanceChangedAction(handle, inArgs);
5145         }
5146         else if(action == kOfxActionInstanceChanged) {
5147           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5148 
5149           // call the instance changed action
5150           instanceChangedAction(handle, inArgs);
5151         }
5152         else if(action == kOfxActionEndInstanceChanged) {
5153           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5154 
5155           // call the end instance changed action
5156           endInstanceChangedAction(handle, inArgs);
5157         }
5158         else if(action == kOfxActionBeginInstanceEdit) {
5159           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5160 
5161           // fetch our pointer out of the props on the handle
5162           ImageEffect *instance = retrieveImageEffectPointer(handle);
5163 
5164           // call the begin edit function
5165           instance->beginEdit();
5166         }
5167         else if(action == kOfxActionEndInstanceEdit) {
5168           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5169 
5170           // fetch our pointer out of the props on the handle
5171           ImageEffect *instance = retrieveImageEffectPointer(handle);
5172 
5173           // call the end edit function
5174           instance->endEdit();
5175         }
5176 #ifdef OFX_SUPPORTS_OPENGLRENDER
5177         else if(action == kOfxActionOpenGLContextAttached) {
5178           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5179 
5180           // call the context attached action
5181           contextAttachedAction(handle, outArgs);
5182         }
5183         else if(action == kOfxActionOpenGLContextDetached) {
5184           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5185 
5186           // call the context detached action
5187           contextDetachedAction(handle, inArgs);
5188         }
5189 #endif
5190 #ifdef OFX_SUPPORTS_DIALOG
5191         else if(action == kOfxActionDialog) {
5192           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5193 
5194           // fetch our pointer out of the props on the handle
5195           ImageEffect *instance = retrieveImageEffectPointer(handle);
5196 
5197           void* instanceData = inArgs.propGetPointer(kOfxPropInstanceData);
5198 
5199           // need to throw something here
5200           if (!instanceData) {
5201             throwSuiteStatusException(kOfxStatErrBadHandle);
5202           }
5203           // call the dialog action (user_data is in the raw handle)
5204           instance->dialog(instanceData);
5205         }
5206 #endif
5207 #ifdef OFX_EXTENSIONS_VEGAS
5208         else if(action == kOfxImageEffectActionVegasKeyframeUplift) {
5209           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true);
5210 
5211           // call the uplift vegas keyframes function
5212           upliftVegasKeyframeAction(handle, inArgs);
5213         }
5214         else if(action == kOfxImageEffectActionInvokeHelp) {
5215           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5216 
5217           // call the invoke help function
5218           if(invokeHelp(handle, plugname))
5219             stat = kOfxStatOK;
5220         }
5221         else if(action == kOfxImageEffectActionInvokeAbout) {
5222           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true);
5223 
5224           // call the invoke help function
5225           if(invokeAbout(handle, plugname))
5226             stat = kOfxStatOK;
5227         }
5228 #endif
5229 #ifdef OFX_EXTENSIONS_NUKE
5230         else if(action == kFnOfxImageEffectActionGetClipComponents) {
5231           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5232 
5233           // call the clip components function, return OK if it does something
5234           // the spec is not clear as to whether it is allowed to do nothing but
5235           // this action should always be implemented for multi-planes effects.
5236           stat = getClipComponentsAction(handle, inArgs, outArgs, plugname, it->second._plug->pluginVersionMajor, it->second._plug->pluginVersionMinor);
5237         }
5238         else if(action == kFnOfxImageEffectActionGetFrameViewsNeeded) {
5239           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5240 
5241           // call the frames views needed action, return OK if it does something
5242           if (getFrameViewsNeededAction(handle, inArgs, outArgs, plugname, it->second._plug->pluginVersionMajor, it->second._plug->pluginVersionMinor)) {
5243               stat = kOfxStatOK;
5244           }
5245         }
5246         else if(action == kFnOfxImageEffectActionGetTransform) {
5247           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5248 
5249           // call the get transform function
5250           if(getTransform(handle, inArgs, outArgs))
5251             stat = kOfxStatOK;
5252         }
5253 #endif
5254 #ifdef OFX_EXTENSIONS_NATRON
5255         else if(action == kOfxImageEffectActionGetInverseDistortion) {
5256           checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false);
5257 
5258           // call the get transform function
5259           if(getInverseDistortion(handle, inArgs, outArgs))
5260             stat = kOfxStatOK;
5261         }
5262 #endif
5263 
5264         else if(actionRaw) {
5265           OFX::Log::error(true, "Unknown action '%s'.", actionRaw);
5266         }
5267         else {
5268           OFX::Log::error(true, "Requested action was a null pointer.");
5269         }
5270       }
5271 
5272       // catch suite exceptions
5273       catch (const OFX::Exception::Suite &ex)
5274       {
5275 #      ifdef DEBUG
5276         std::cout << "Caught OFX::Exception::Suite: " << ex.what() << std::endl;
5277 #      endif
5278         stat = ex.status();
5279       }
5280 
5281       // catch host inadequate exceptions
5282       catch (const OFX::Exception::HostInadequate &e)
5283       {
5284 #      ifdef DEBUG
5285         std::cout << "Caught OFX::Exception::HostInadequate: " << e.what() << std::endl;
5286 #      endif
5287         unused(e);
5288         stat = kOfxStatErrMissingHostFeature;
5289       }
5290 
5291       // catch exception due to a property being unknown to the host, implies something wrong with host if not caught further down
5292       catch (const OFX::Exception::PropertyUnknownToHost &e)
5293       {
5294 #      ifdef DEBUG
5295         std::cout << "Caught OFX::Exception::PropertyUnknownToHost: " << e.what() << std::endl;
5296 #      endif
5297         unused(e);
5298         stat = kOfxStatErrMissingHostFeature;
5299       }
5300 
5301       // catch memory
5302       catch (std::bad_alloc)
5303       {
5304         stat = kOfxStatErrMemory;
5305       }
5306 
5307       // catch a custom client exception, if defined
5308 #ifdef OFX_CLIENT_EXCEPTION_TYPE
5309       catch (OFX_CLIENT_EXCEPTION_TYPE &ex)
5310       {
5311         stat = OFX_CLIENT_EXCEPTION_HANDLER(ex, plugname);
5312       }
5313 #endif
5314       // Catch anything else, unknown
5315       catch (const std::exception &e)
5316       {
5317 #      ifdef DEBUG
5318         std::cout << "Caught exception: " << e.what() << std::endl;
5319 #      endif
5320         unused(e);
5321         stat = kOfxStatFailed;
5322       }
5323       catch (...)
5324       {
5325 #      ifdef DEBUG
5326         std::cout << "Caught Unknown exception" << std::endl;
5327 #      endif
5328         stat = kOfxStatFailed;
5329       }
5330 
5331       OFX::Log::outdent();
5332       OFX::Log::print("STOP mainEntry (%s)\n", actionRaw);
5333       return stat;
5334     }
5335 
5336 
customParamInterpolationV1Entry(const void * handleRaw,OfxPropertySetHandle inArgsRaw,OfxPropertySetHandle outArgsRaw)5337     OfxStatus customParamInterpolationV1Entry(
5338       const void*            handleRaw,
5339       OfxPropertySetHandle   inArgsRaw,
5340       OfxPropertySetHandle   outArgsRaw)
5341     {
5342       OFX::Log::print("********************************************************************************");
5343       OFX::Log::print("START customParamInterpolationV1Entry");
5344       OFX::Log::indent();
5345       OfxStatus stat = kOfxStatReplyDefault;
5346       try {
5347         // Cast the raw handle to be an image effect handle, because that is what it is
5348         OfxImageEffectHandle handle = (OfxImageEffectHandle) handleRaw;
5349 
5350         // Turn the arguments into wrapper objects to make our lives easier
5351         OFX::PropertySet inArgs(inArgsRaw);
5352         OFX::PropertySet outArgs(outArgsRaw);
5353 
5354         ImageEffect *effectInstance = retrieveImageEffectPointer(handle);
5355 
5356         InterpolateCustomArgs interpArgs;
5357 
5358         interpArgs.time     = inArgs.propGetDouble(kOfxPropTime);
5359         interpArgs.value1   = inArgs.propGetString(kOfxParamPropCustomValue, 0);
5360         interpArgs.value2   = inArgs.propGetString(kOfxParamPropCustomValue, 1);
5361         interpArgs.keytime1 = inArgs.propGetDouble(kOfxParamPropInterpolationTime, 0);
5362         interpArgs.keytime2 = inArgs.propGetDouble(kOfxParamPropInterpolationTime, 1);
5363         interpArgs.amount   = inArgs.propGetDouble(kOfxParamPropInterpolationAmount);
5364 
5365         std::string paramName = inArgs.propGetString(kOfxPropName);
5366 
5367         // and call the plugin client code
5368         std::string output = effectInstance->interpolateCustomParam(interpArgs, paramName);
5369 
5370         outArgs.propSetString(kOfxParamPropCustomValue, output);
5371       }
5372 
5373       // catch suite exceptions
5374       catch (OFX::Exception::Suite &ex)
5375       {
5376 #      ifdef DEBUG
5377         std::cout << "Caught OFX::Exception::Suite" << std::endl;
5378 #      endif
5379         stat = ex.status();
5380       }
5381 
5382       // catch host inadequate exceptions
5383       catch (OFX::Exception::HostInadequate)
5384       {
5385 #      ifdef DEBUG
5386         std::cout << "Caught OFX::Exception::HostInadequate" << std::endl;
5387 #      endif
5388         stat = kOfxStatErrMissingHostFeature;
5389       }
5390 
5391       // catch exception due to a property being unknown to the host, implies something wrong with host if not caught further down
5392       catch (OFX::Exception::PropertyUnknownToHost)
5393       {
5394 #      ifdef DEBUG
5395         std::cout << "Caught OFX::Exception::PropertyUnknownToHost" << std::endl;
5396 #      endif
5397         stat = kOfxStatErrMissingHostFeature;
5398       }
5399 
5400       // catch memory
5401       catch (std::bad_alloc)
5402       {
5403         stat = kOfxStatErrMemory;
5404       }
5405 
5406       // catch a custom client exception, if defined
5407 #ifdef OFX_CLIENT_EXCEPTION_TYPE
5408       catch (OFX_CLIENT_EXCEPTION_TYPE &ex)
5409       {
5410         stat = OFX_CLIENT_EXCEPTION_HANDLER(ex, plugname);
5411       }
5412 #endif
5413       // Catch anything else, unknown
5414       catch (...)
5415       {
5416 #      ifdef DEBUG
5417         std::cout << "Caught Unknown exception" << std::endl;
5418 #      endif
5419         stat = kOfxStatFailed;
5420       }
5421 
5422       OFX::Log::outdent();
5423       OFX::Log::print("STOP customParamInterpolationV1Entry\n");
5424       return stat;
5425     }
5426 
5427     /** @brief The plugin function that gets passed the host structure. */
setHost(OfxHost * host)5428     void setHost(OfxHost *host)
5429     {
5430       gHost = host;
5431     }
5432 
5433   }; // namespace Private
5434 
5435   /** @brief Fetch's a suite from the host and logs errors */
fetchSuite(const char * suiteName,int suiteVersion,bool optional)5436   const void * fetchSuite(const char *suiteName, int suiteVersion, bool optional)
5437   {
5438     const void *suite = Private::gHost->fetchSuite(Private::gHost->host, suiteName, suiteVersion);
5439     if(suite==0)
5440     {
5441       if(optional)
5442         OFX::Log::warning(suite == 0, "Could not fetch the optional suite '%s' version %d.", suiteName, suiteVersion);
5443       else
5444         OFX::Log::error(suite == 0, "Could not fetch the mandatory suite '%s' version %d.", suiteName, suiteVersion);
5445     }
5446     if(!optional && suite == 0) throw OFX::Exception::HostInadequate(suiteName);
5447     return suite;
5448   }
5449 
5450 #ifndef OFXS_MANUAL_REGISTRATION
5451   ////////////////////////////////////////////////////////////////////////////////
5452   /** @brief The OFX::Plugin namespace. All the functions in here needs to be defined by each plugin that uses the support libs.
5453    */
5454   namespace Plugin {
5455     /** @brief Plugin side function used to identify the plugin to the support library.
5456 
5457      This was obsoleted by automatic plugin registration, using the macro mRegisterPluginFactoryInstance:
5458      static BasicExamplePluginFactory p("net.sf.openfx.basicPlugin", 1, 0);
5459      mRegisterPluginFactoryInstance(p)
5460      */
getPluginIDs(OFX::PluginFactoryArray &)5461     void getPluginIDs(OFX::PluginFactoryArray &/*id*/)
5462     {
5463       // an empty definition is enough to prevent old code from compiling.
5464     }
5465   };
5466 #endif // OFXS_MANUAL_REGISTRATION
5467 }; // namespace OFX
5468 
5469 static
generatePlugInfo(OFX::PluginFactory * factory,std::string & newID)5470 OFX::OfxPlugInfo generatePlugInfo(OFX::PluginFactory* factory, std::string& newID)
5471 {
5472   validateXMLString(factory->getID(), true);
5473   newID = factory->getUID();
5474   OFX::auto_ptr<OfxPlugin> ofxPlugin(new OfxPlugin());
5475   ofxPlugin->pluginApi  = kOfxImageEffectPluginApi;
5476   ofxPlugin->apiVersion = 1;
5477   ofxPlugin->pluginIdentifier   = factory->getID().c_str();
5478   ofxPlugin->pluginVersionMajor = factory->getMajorVersion();
5479   ofxPlugin->pluginVersionMinor = factory->getMinorVersion();
5480   ofxPlugin->setHost    = OFX::Private::setHost;
5481   ofxPlugin->mainEntry  = factory->getMainEntry();
5482   return OFX::OfxPlugInfo(factory, ofxPlugin.release());
5483 }
5484 
5485 bool gHasInit = false;
5486 
5487 static
init()5488 void init()
5489 {
5490   if(gHasInit)
5491     return;
5492 
5493 #ifdef OFXS_MANUAL_REGISTRATION
5494   // Call OFX::Plugin::getPluginIDs if the plugin uses manual plugin registration.
5495   // Note that manual registration was obsoleted by automatic registratic using mRegisterPluginFactoryInstance().
5496   OFX::Plugin::getPluginIDs(OFX::PluginFactories::plugIDs());
5497 #endif // OFXS_MANUAL_REGISTRATION
5498   const OFX::PluginFactoryArray& plugIDs = OFX::PluginFactories::plugIDs();
5499   if(OFX::ofxPlugs.empty())
5500     OFX::ofxPlugs.resize(plugIDs.size());
5501 
5502   int counter = 0;
5503   for (OFX::PluginFactoryArray::const_iterator it = plugIDs.begin(); it != plugIDs.end(); ++it, ++counter)
5504   {
5505     std::string newID;
5506     OFX::OfxPlugInfo info = generatePlugInfo(*it, newID);
5507     OFX::plugInfoMap[newID] = info;
5508     OFX::ofxPlugs[counter] = info._plug;
5509   }
5510   gHasInit = true;
5511 }
5512 
5513 /** @brief, mandated function returning the number of plugins, which is always 1 */
OfxGetNumberOfPlugins(void)5514 EXPORT int OfxGetNumberOfPlugins(void)
5515 {
5516   init();
5517   return (int)OFX::PluginFactories::plugIDs().size();
5518 }
5519 
5520 /** @brief, mandated function returning the nth plugin
5521 
5522 We call the plugin side defined OFX::Plugin::getPluginIDs function to find out what to set.
5523 */
5524 
OfxGetPlugin(int nth)5525 EXPORT OfxPlugin* OfxGetPlugin(int nth)
5526 {
5527   init();
5528   int numPlugs = (int)OFX::plugInfoMap.size();
5529   OFX::Log::error(nth >= numPlugs, "Host attempted to get plugin %d, when there is only %d plugin(s), so it should have asked for 0.", nth, numPlugs);
5530   if(OFX::ofxPlugs[nth] == 0)
5531   {
5532     std::string newID;
5533     OFX::OfxPlugInfo info = generatePlugInfo(OFX::PluginFactories::plugIDs()[nth], newID);
5534     OFX::plugInfoMap[newID] = info;
5535     OFX::ofxPlugs[nth] = info._plug;
5536   }
5537   return OFX::ofxPlugs[nth];
5538 }
5539