/* OFX Support Library, a library that skins the OFX plug-in API with C++ classes. Copyright (C) 2004-2007 The Open Effects Association Ltd Author Bruno Nicoletti bruno@thefoundry.co.uk Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name The Open Effects Association Ltd, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The Open Effects Association Ltd 1 Wardour St London W1D 6PA England */ /** @brief This file contains code that skins the ofx effect suite */ #include "ofxsSupportPrivate.h" #include // for find #include // for strlen #ifdef DEBUG #include #endif #include #ifdef OFX_SUPPORTS_OPENGLRENDER #include "ofxOpenGLRender.h" #endif #include "ofxsCore.h" #if defined __APPLE__ || defined linux || defined __FreeBSD__ # if __GNUC__ >= 4 # define EXPORT __attribute__((visibility("default"))) # define LOCAL __attribute__((visibility("hidden"))) # else # define EXPORT # define LOCAL # endif #elif defined _WIN32 # define EXPORT OfxExport # define LOCAL #else # error Not building on your operating system quite yet #endif // string utility functions static bool ends_with(std::string const & value, std::string const & ending) { if (ending.size() > value.size()) return false; return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } static bool starts_with(std::string const & value, std::string const & beginning) { if (beginning.size() > value.size()) return false; return std::equal(beginning.begin(), beginning.end(), value.begin()); } /** @brief The core 'OFX Support' namespace, used by plugin implementations. All code for these are defined in the common support libraries. */ namespace OFX { // globals to keep consistent data structures around. OFX::PluginFactoryArray plugIDs; //Put it all into a map, so we know when to delete what! struct OfxPlugInfo { OfxPlugInfo():_factory(0), _plug(0){} OfxPlugInfo(OFX::PluginFactory* f, OfxPlugin* p):_factory(f), _plug(p){} OFX::PluginFactory* _factory; OfxPlugin* _plug; }; typedef std::map OfxPlugInfoMap; OfxPlugInfoMap plugInfoMap; typedef std::vector OfxPluginArray; OfxPluginArray ofxPlugs; /** @brief the global host description */ ImageEffectHostDescription gHostDescription; bool gHostDescriptionHasInit = false; bool ImageEffectHostDescription::supportsPixelComponent(const PixelComponentEnum component) const { return std::find(_supportedComponents.begin(), _supportedComponents.end(), component) != _supportedComponents.end(); } bool ImageEffectHostDescription::supportsBitDepth( const BitDepthEnum bitDepth) const { return std::find(_supportedPixelDepths.begin(), _supportedPixelDepths.end(), bitDepth) != _supportedPixelDepths.end(); } bool ImageEffectHostDescription::supportsContext(const ContextEnum context) const { return std::find(_supportedContexts.begin(), _supportedContexts.end(), context) != _supportedContexts.end(); } /** @return default pixel depth supported by host application. */ BitDepthEnum ImageEffectHostDescription::getDefaultPixelDepth() const { if(!_supportedPixelDepths.empty()) { return _supportedPixelDepths[0]; } else { OFX::Log::warning(true, "The host doesn't define supported pixel depth. (size: %d)", (int)_supportedPixelDepths.size()); return eBitDepthFloat; } } /** @return default pixel component supported by host application. */ PixelComponentEnum ImageEffectHostDescription::getDefaultPixelComponent() const { if(! _supportedComponents.empty()) { return _supportedComponents[0]; } else { OFX::Log::warning(true, "The host doesn't define supported pixel component. (size: %d)", _supportedComponents.size()); return ePixelComponentRGBA; } } ImageEffectHostDescription* getImageEffectHostDescription() { if(gHostDescriptionHasInit) return &gHostDescription; return NULL; } namespace Private { // Suite and host pointers OfxHost *gHost = 0; OfxImageEffectSuiteV1 *gEffectSuite = 0; OfxPropertySuiteV1 *gPropSuite = 0; OfxInteractSuiteV1 *gInteractSuite = 0; OfxParameterSuiteV1 *gParamSuite = 0; OfxMemorySuiteV1 *gMemorySuite = 0; OfxMultiThreadSuiteV1 *gThreadSuite = 0; OfxMessageSuiteV1 *gMessageSuite = 0; OfxMessageSuiteV2 *gMessageSuiteV2 = 0; OfxProgressSuiteV1 *gProgressSuiteV1 = 0; OfxProgressSuiteV2 *gProgressSuiteV2 = 0; OfxTimeLineSuiteV1 *gTimeLineSuite = 0; OfxParametricParameterSuiteV1 *gParametricParameterSuite = 0; #ifdef OFX_SUPPORTS_OPENGLRENDER OfxImageEffectOpenGLRenderSuiteV1 *gOpenGLRenderSuite = 0; #endif // @brief the set of descriptors, one per context used by kOfxActionDescribeInContext, //'eContextNone' is the one used by the kOfxActionDescribe EffectDescriptorMap gEffectDescriptors; }; /** @brief map a std::string to a context */ ContextEnum mapToContextEnum(const std::string &s) throw(std::invalid_argument) { if(s == kOfxImageEffectContextGenerator) return eContextGenerator; if(s == kOfxImageEffectContextFilter) return eContextFilter; if(s == kOfxImageEffectContextTransition) return eContextTransition; if(s == kOfxImageEffectContextPaint) return eContextPaint; if(s == kOfxImageEffectContextGeneral) return eContextGeneral; if(s == kOfxImageEffectContextRetimer) return eContextRetimer; OFX::Log::error(true, "Unknown image effect context '%s'", s.c_str()); throw std::invalid_argument(s); } const char* mapContextEnumToStr(ContextEnum context) throw(std::invalid_argument) { switch (context) { case eContextGenerator: return kOfxImageEffectContextGenerator; case eContextFilter: return kOfxImageEffectContextFilter; case eContextTransition: return kOfxImageEffectContextTransition; case eContextPaint: return kOfxImageEffectContextPaint; case eContextGeneral: return kOfxImageEffectContextGeneral; case eContextRetimer: return kOfxImageEffectContextRetimer; default: OFX::Log::error(true, "Unknown context enum '%d'", (int)context); throw std::invalid_argument("unknown ContextEnum"); } } const char* mapMessageTypeEnumToStr(OFX::Message::MessageTypeEnum type) { if(type == OFX::Message::eMessageFatal) return kOfxMessageFatal; else if(type == OFX::Message::eMessageError) return kOfxMessageError; else if(type == OFX::Message::eMessageMessage) return kOfxMessageMessage; else if(type == OFX::Message::eMessageWarning) return kOfxMessageWarning; else if(type == OFX::Message::eMessageLog) return kOfxMessageLog; else if(type == OFX::Message::eMessageQuestion) return kOfxMessageQuestion; OFX::Log::error(true, "Unknown message type enum '%d'", type); return 0; } OFX::Message::MessageReplyEnum mapToMessageReplyEnum(OfxStatus stat) { if(stat == kOfxStatOK) return OFX::Message::eMessageReplyOK; else if(stat == kOfxStatReplyYes) return OFX::Message::eMessageReplyYes; else if(stat == kOfxStatReplyNo) return OFX::Message::eMessageReplyNo; else if(stat == kOfxStatFailed) return OFX::Message::eMessageReplyFailed; OFX::Log::error(true, "Unknown message reply status enum '%d'", stat); return OFX::Message::eMessageReplyFailed; } /** @brief map a std::string to a context */ InstanceChangeReason mapToInstanceChangedReason(const std::string &s) throw(std::invalid_argument) { if(s == kOfxChangePluginEdited) return eChangePluginEdit; if(s == kOfxChangeUserEdited) return eChangeUserEdit; if(s == kOfxChangeTime) return eChangeTime; OFX::Log::error(true, "Unknown instance changed reason '%s'", s.c_str()); throw std::invalid_argument(s); } /** @brief turns a bit depth string into and enum */ BitDepthEnum mapStrToBitDepthEnum(const std::string &str) throw(std::invalid_argument) { if(str == kOfxBitDepthByte) { return eBitDepthUByte; } else if(str == kOfxBitDepthShort) { return eBitDepthUShort; } else if(str == kOfxBitDepthHalf) { return eBitDepthHalf; } else if(str == kOfxBitDepthFloat) { return eBitDepthFloat; } else if(str == kOfxBitDepthNone) { return eBitDepthNone; } else { return eBitDepthCustom; } } /** @brief turns a bit depth string into and enum */ const char* mapBitDepthEnumToStr(BitDepthEnum bitDepth) throw(std::invalid_argument) { switch (bitDepth) { case eBitDepthUByte: return kOfxBitDepthByte; case eBitDepthUShort: return kOfxBitDepthShort; case eBitDepthHalf: return kOfxBitDepthHalf; case eBitDepthFloat: return kOfxBitDepthFloat; case eBitDepthNone: return kOfxBitDepthNone; case eBitDepthCustom: return "OfxBitDepthCustom"; default: OFX::Log::error(true, "Unknown bit depth enum '%d'", (int)bitDepth); throw std::invalid_argument("unknown BitDepthEnum"); } } /** @brief turns a pixel component string into and enum */ PixelComponentEnum mapStrToPixelComponentEnum(const std::string &str) throw(std::invalid_argument) { if(str == kOfxImageComponentRGBA) { return ePixelComponentRGBA; } else if(str == kOfxImageComponentRGB) { return ePixelComponentRGB; } else if(str == kOfxImageComponentAlpha) { return ePixelComponentAlpha; } else if(str == kOfxImageComponentNone) { return ePixelComponentNone; } else { return ePixelComponentCustom; } } /** @brief turns a pixel component string into and enum */ const char* mapPixelComponentEnumToStr(PixelComponentEnum pixelComponent) throw(std::invalid_argument) { switch (pixelComponent) { case ePixelComponentRGBA: return kOfxImageComponentRGBA; case ePixelComponentRGB: return kOfxImageComponentRGB; case ePixelComponentAlpha: return kOfxImageComponentAlpha; case ePixelComponentCustom: return "OfxImageComponentCustom"; default: OFX::Log::error(true, "Unknown pixel component enum '%d'", (int)pixelComponent); throw std::invalid_argument("unknown PixelComponentEnum"); } } /** @brief turns a premultiplication string into and enum */ static PreMultiplicationEnum mapStrToPreMultiplicationEnum(const std::string &str) throw(std::invalid_argument) { if(str == kOfxImageOpaque) { return eImageOpaque; } else if(str == kOfxImagePreMultiplied) { return eImagePreMultiplied; } else if(str == kOfxImageUnPreMultiplied) { return eImageUnPreMultiplied; } else { throw std::invalid_argument(""); } } /** @brief turns a field string into and enum */ FieldEnum mapStrToFieldEnum(const std::string &str) throw(std::invalid_argument) { if(str == kOfxImageFieldNone) { return eFieldNone; } else if(str == kOfxImageFieldBoth) { return eFieldBoth; } else if(str == kOfxImageFieldLower) { return eFieldLower; } else if(str == kOfxImageFieldUpper) { return eFieldUpper; } else { throw std::invalid_argument(""); } } //////////////////////////////////////////////////////////////////////////////// // clip descriptor /** @brief hidden constructor */ ClipDescriptor::ClipDescriptor(const std::string &name, OfxPropertySetHandle props) : _clipName(name) , _clipProps(props) { OFX::Validation::validateClipDescriptorProperties(props); } /** @brief set the label properties */ void ClipDescriptor::setLabel(const std::string &label) { _clipProps.propSetString(kOfxPropLabel, label); } /** @brief set the label properties */ void ClipDescriptor::setLabels(const std::string &label, const std::string &shortLabel, const std::string &longLabel) { setLabel(label); _clipProps.propSetString(kOfxPropShortLabel, shortLabel, false); _clipProps.propSetString(kOfxPropLongLabel, longLabel, false); } /** @brief set how fielded images are extracted from the clip defaults to eFieldExtractDoubled */ void ClipDescriptor::setFieldExtraction(FieldExtractionEnum v) { switch(v) { case eFieldExtractBoth : _clipProps.propSetString(kOfxImageClipPropFieldExtraction, kOfxImageFieldBoth); break; case eFieldExtractSingle : _clipProps.propSetString(kOfxImageClipPropFieldExtraction, kOfxImageFieldSingle); break; case eFieldExtractDoubled : _clipProps.propSetString(kOfxImageClipPropFieldExtraction, kOfxImageFieldDoubled); break; } } /** @brief set which components are supported, defaults to none set, this must be called at least once! */ void ClipDescriptor::addSupportedComponent(PixelComponentEnum v) { int n = _clipProps.propGetDimension(kOfxImageEffectPropSupportedComponents); switch(v) { case ePixelComponentNone : _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentNone, n); break; case ePixelComponentRGBA : _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentRGBA, n); break; case ePixelComponentRGB : _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentRGB, n); break; case ePixelComponentAlpha : _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, kOfxImageComponentAlpha, n); break; case ePixelComponentCustom : break; } } /** @brief set which components are supported, defaults to none set, this must be called at least once! */ void ClipDescriptor::addSupportedComponent(const std::string &comp) { int n = _clipProps.propGetDimension(kOfxImageEffectPropSupportedComponents); _clipProps.propSetString(kOfxImageEffectPropSupportedComponents, comp, n); } /** @brief say whether we are going to do random temporal access on this clip, defaults to false */ void ClipDescriptor::setTemporalClipAccess(bool v) { _clipProps.propSetInt(kOfxImageEffectPropTemporalClipAccess, int(v)); } /** @brief say whether if the clip is optional, defaults to false */ void ClipDescriptor::setOptional(bool v) { _clipProps.propSetInt(kOfxImageClipPropOptional, int(v)); } /** @brief say whether this clip supports tiling, defaults to true */ void ClipDescriptor::setSupportsTiles(bool v) { _clipProps.propSetInt(kOfxImageEffectPropSupportsTiles, int(v)); } /** @brief say whether this clip is a 'mask', so the host can know to replace with a roto or similar, defaults to false */ void ClipDescriptor::setIsMask(bool v) { _clipProps.propSetInt(kOfxImageClipPropIsMask, int(v)); } //////////////////////////////////////////////////////////////////////////////// // image effect descriptor /** @brief effect descriptor ctor */ ImageEffectDescriptor::ImageEffectDescriptor(OfxImageEffectHandle handle) : _effectHandle(handle) { // fetch the property set handle of the effect OfxPropertySetHandle props; OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet(handle, &props); throwSuiteStatusException(stat); _effectProps.propSetHandle(props); OFX::Validation::validatePluginDescriptorProperties(props); // fetch the param set handle and set it in our ParamSetDescriptor base OfxParamSetHandle paramSetHandle; stat = OFX::Private::gEffectSuite->getParamSet(handle, ¶mSetHandle); throwSuiteStatusException(stat); setParamSetHandle(paramSetHandle); } /** @brief dtor */ ImageEffectDescriptor::~ImageEffectDescriptor() { // delete any clip descriptors we may have constructed std::map::iterator iter; for(iter = _definedClips.begin(); iter != _definedClips.end(); ++iter) { if(iter->second) { delete iter->second; iter->second = NULL; } } } /** @brief, set the label properties in a plugin */ void ImageEffectDescriptor::setLabel(const std::string &label) { _effectProps.propSetString(kOfxPropLabel, label); } /** @brief, set the label properties in a plugin */ void ImageEffectDescriptor::setLabels(const std::string &label, const std::string &shortLabel, const std::string &longLabel) { setLabel(label); _effectProps.propSetString(kOfxPropShortLabel, shortLabel, false); _effectProps.propSetString(kOfxPropLongLabel, longLabel, false); } /** @brief, set the version properties in a plugin */ void ImageEffectDescriptor::setVersion(int major, int minor, int micro, int build, const std::string &versionLabel) { _effectProps.propSetInt(kOfxPropVersion, major, 0, false); // introduced in OFX 1.2 if (minor || micro || build) { _effectProps.propSetInt(kOfxPropVersion, minor, 1, false); // introduced in OFX 1.2 if (micro || build) { _effectProps.propSetInt(kOfxPropVersion, micro, 2, false); // introduced in OFX 1.2 if (build) { _effectProps.propSetInt(kOfxPropVersion, build, 3, false); // introduced in OFX 1.2 } } } if (!versionLabel.empty()) { _effectProps.propSetString(kOfxPropVersionLabel, versionLabel, false); } } /** @brief Set the plugin grouping */ void ImageEffectDescriptor::setPluginGrouping(const std::string &group) { _effectProps.propSetString(kOfxImageEffectPluginPropGrouping, group); } /** @brief Set the plugin description, defaults to "" */ void ImageEffectDescriptor::setPluginDescription(const std::string &description) { _effectProps.propSetString(kOfxPropPluginDescription, description, false); // introduced in OFX 1.2 } /** @brief Add a context to those supported */ void ImageEffectDescriptor::addSupportedContext(ContextEnum v) { int n = _effectProps.propGetDimension(kOfxImageEffectPropSupportedContexts); switch (v) { case eContextNone : break; case eContextGenerator : _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextGenerator, n); break; case eContextFilter : _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextFilter, n); break; case eContextTransition : _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextTransition, n); break; case eContextPaint : _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextPaint, n); break; case eContextGeneral : _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextGeneral, n); break; case eContextRetimer : _effectProps.propSetString(kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextRetimer, n); break; } } void ImageEffectDescriptor::setOverlayInteractDescriptor(EffectOverlayDescriptor* desc) { _overlayDescriptor.reset(desc); if(OFX::gHostDescription.supportsOverlays && desc->getMainEntry()) _effectProps.propSetPointer(kOfxImageEffectPluginPropOverlayInteractV1, (void*)desc->getMainEntry()); } /** @brief Add a pixel depth to those supported */ void ImageEffectDescriptor::addSupportedBitDepth(BitDepthEnum v) { int n = _effectProps.propGetDimension(kOfxImageEffectPropSupportedPixelDepths); switch(v) { case eBitDepthNone : _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthNone , n); break; case eBitDepthUByte : _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthByte , n); break; case eBitDepthUShort : _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthShort , n); break; case eBitDepthHalf : _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthHalf , n); break; case eBitDepthFloat : _effectProps.propSetString(kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthFloat , n); break; case eBitDepthCustom : break; } } #ifdef OFX_SUPPORTS_OPENGLRENDER /** @brief Add a pixel depth to those supported */ void ImageEffectDescriptor::addSupportedOpenGLBitDepth(BitDepthEnum v) { int n = _effectProps.propGetDimension(kOfxOpenGLPropPixelDepth); switch(v) { case eBitDepthNone : _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthNone , n); break; case eBitDepthUByte : _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthByte , n); break; case eBitDepthUShort : _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthShort , n); break; case eBitDepthHalf : _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthHalf , n); break; case eBitDepthFloat : _effectProps.propSetString(kOfxOpenGLPropPixelDepth, kOfxBitDepthFloat , n); break; default: break; } } #endif /** @brief Is the plugin single instance only ? */ void ImageEffectDescriptor::setSingleInstance(bool v) { _effectProps.propSetInt(kOfxImageEffectPluginPropSingleInstance, int(v)); } /** @brief Does the plugin expect the host to perform per frame SMP threading */ void ImageEffectDescriptor::setHostFrameThreading(bool v) { _effectProps.propSetInt(kOfxImageEffectPluginPropHostFrameThreading, int(v)); } /** @brief Does the plugin support multi resolution images */ void ImageEffectDescriptor::setSupportsMultiResolution(bool v) { _effectProps.propSetInt(kOfxImageEffectPropSupportsMultiResolution, int(v)); } /** @brief Does the plugin support image tiling */ void ImageEffectDescriptor::setSupportsTiles(bool v) { _effectProps.propSetInt(kOfxImageEffectPropSupportsTiles, int(v)); } /** @brief Does the plugin perform temporal clip access */ void ImageEffectDescriptor::setTemporalClipAccess(bool v) { _effectProps.propSetInt(kOfxImageEffectPropTemporalClipAccess, int(v)); } /** @brief Does the plugin want to have render called twice per frame in all circumanstances for fielded images ? */ void ImageEffectDescriptor::setRenderTwiceAlways(bool v) { _effectProps.propSetInt(kOfxImageEffectPluginPropFieldRenderTwiceAlways, int(v)); } /** @brief Does the plugin support inputs and output clips of differing depths */ void ImageEffectDescriptor::setSupportsMultipleClipDepths(bool v) { _effectProps.propSetInt(kOfxImageEffectPropSupportsMultipleClipDepths, int(v)); } /** @brief Does the plugin support inputs and output clips of pixel aspect ratios */ void ImageEffectDescriptor::setSupportsMultipleClipPARs(bool v) { _effectProps.propSetInt(kOfxImageEffectPropSupportsMultipleClipPARs, int(v)); } /** @brief What kind of thread safety does the plugin have */ void ImageEffectDescriptor::setRenderThreadSafety(RenderSafetyEnum v) { switch(v) { case eRenderUnsafe : _effectProps.propSetString(kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderUnsafe); break; case eRenderInstanceSafe : _effectProps.propSetString(kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderInstanceSafe); break; case eRenderFullySafe : _effectProps.propSetString(kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderFullySafe); break; } } #ifdef OFX_SUPPORTS_OPENGLRENDER /** @brief Does the plugin support OpenGL accelerated rendering (but is also capable of CPU rendering) ? */ void ImageEffectDescriptor::setSupportsOpenGLRender(bool v) { if (gHostDescription.supportsOpenGLRender) { _effectProps.propSetString(kOfxImageEffectPropOpenGLRenderSupported, (v ? "true" : "false")); } } /** @brief Does the plugin require OpenGL accelerated rendering ? */ void ImageEffectDescriptor::setNeedsOpenGLRender(bool v) { if (gHostDescription.supportsOpenGLRender) { _effectProps.propSetString(kOfxImageEffectPropOpenGLRenderSupported, (v ? "needed" : "false")); } } void ImageEffectDescriptor::addOpenGLBitDepth(BitDepthEnum v) { int n = _effectProps.propGetDimension(kOfxImageEffectPropSupportedPixelDepths); std::string value = mapBitDepthEnumToStr(v); if (!value.empty()) { _effectProps.propSetString(kOfxOpenGLPropPixelDepth, value, n); } } #endif /** @brief If the slave param changes the clip preferences need to be re-evaluated */ void ImageEffectDescriptor::addClipPreferencesSlaveParam(ParamDescriptor &p) { int n = _effectProps.propGetDimension(kOfxImageEffectPropClipPreferencesSlaveParam); _effectProps.propSetString(kOfxImageEffectPropClipPreferencesSlaveParam, p.getName(), n); } /** @brief Create a clip, only callable from describe in context */ ClipDescriptor *ImageEffectDescriptor::defineClip(const std::string &name) { // do we have the clip already std::map::const_iterator search; search = _definedClips.find(name); if(search != _definedClips.end()) return search->second; // no, so make it OfxPropertySetHandle propSet; OfxStatus stat = OFX::Private::gEffectSuite->clipDefine(_effectHandle, name.c_str(), &propSet); (void)stat; ClipDescriptor *clip = new ClipDescriptor(name, propSet); _definedClips[name] = clip; _clipComponentsPropNames[name] = std::string("OfxImageClipPropComponents_") + name; _clipDepthPropNames[name] = std::string("OfxImageClipPropDepth_") + name; _clipPARPropNames[name] = std::string("OfxImageClipPropPAR_") + name; _clipROIPropNames[name] = std::string("OfxImageClipPropRoI_") + name; _clipFrameRangePropNames[name] = std::string("OfxImageClipPropFrameRange_") + name; return clip; } //////////////////////////////////////////////////////////////////////////////// // wraps up an image ImageBase::ImageBase(OfxPropertySetHandle props) : _imageProps(props) { OFX::Validation::validateImageBaseProperties(props); // and fetch all the properties _rowBytes = _imageProps.propGetInt(kOfxImagePropRowBytes); _pixelAspectRatio = _imageProps.propGetDouble(kOfxImagePropPixelAspectRatio);; std::string str = _imageProps.propGetString(kOfxImageEffectPropComponents); _pixelComponents = mapStrToPixelComponentEnum(str); switch (_pixelComponents) { case ePixelComponentAlpha: _pixelComponentCount = 1; break; case ePixelComponentNone: _pixelComponentCount = 0; break; case ePixelComponentRGB: _pixelComponentCount = 3; break; case ePixelComponentRGBA: _pixelComponentCount = 4; break; case ePixelComponentCustom: default: _pixelComponentCount = 0; break; } str = _imageProps.propGetString(kOfxImageEffectPropPixelDepth); _pixelDepth = mapStrToBitDepthEnum(str); // compute bytes per pixel _pixelBytes = _pixelComponentCount; switch(_pixelDepth) { case eBitDepthNone : _pixelBytes *= 0; break; case eBitDepthUByte : _pixelBytes *= 1; break; case eBitDepthUShort : _pixelBytes *= 2; break; case eBitDepthHalf : _pixelBytes *= 2; break; case eBitDepthFloat : _pixelBytes *= 4; break; case eBitDepthCustom : _pixelBytes *= 0; break; } str = _imageProps.propGetString(kOfxImageEffectPropPreMultiplication); _preMultiplication = mapStrToPreMultiplicationEnum(str); _regionOfDefinition.x1 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 0); _regionOfDefinition.y1 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 1); _regionOfDefinition.x2 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 2); _regionOfDefinition.y2 = _imageProps.propGetInt(kOfxImagePropRegionOfDefinition, 3); _bounds.x1 = _imageProps.propGetInt(kOfxImagePropBounds, 0); _bounds.y1 = _imageProps.propGetInt(kOfxImagePropBounds, 1); _bounds.x2 = _imageProps.propGetInt(kOfxImagePropBounds, 2); _bounds.y2 = _imageProps.propGetInt(kOfxImagePropBounds, 3); str = _imageProps.propGetString(kOfxImagePropField); if(str == kOfxImageFieldNone) { _field = eFieldNone; } else if(str == kOfxImageFieldBoth) { _field = eFieldBoth; } else if(str == kOfxImageFieldLower) { _field = eFieldLower; } else if(str == kOfxImageFieldUpper) { _field = eFieldLower; } else { OFX::Log::error(true, "Unknown field state '%s' reported on an image", str.c_str()); _field = eFieldNone; } _uniqueID = _imageProps.propGetString(kOfxImagePropUniqueIdentifier); _renderScale.x = _imageProps.propGetDouble(kOfxImageEffectPropRenderScale, 0); _renderScale.y = _imageProps.propGetDouble(kOfxImageEffectPropRenderScale, 1); } ImageBase::~ImageBase() { } //////////////////////////////////////////////////////////////////////////////// // wraps up an image Image::Image(OfxPropertySetHandle props) : ImageBase(props) { OFX::Validation::validateImageProperties(props); // and fetch all the properties // should throw if it is not an image _pixelData = _imageProps.propGetPointer(kOfxImagePropData); } Image::~Image() { OFX::Private::gEffectSuite->clipReleaseImage(_imageProps.propSetHandle()); } #ifdef OFX_SUPPORTS_OPENGLRENDER //////////////////////////////////////////////////////////////////////////////// // wraps up an OpenGL texture Texture::Texture(OfxPropertySetHandle props) : ImageBase(props) { OFX::Validation::validateTextureProperties(props); // should throw if it is not a texture _index = _imageProps.propGetInt(kOfxImageEffectPropOpenGLTextureIndex); _target = _imageProps.propGetInt(kOfxImageEffectPropOpenGLTextureTarget); } Texture::~Texture() { OfxStatus stat = OFX::Private::gOpenGLRenderSuite->clipFreeTexture(_imageProps.propSetHandle()); if (stat != kOfxStatOK) { throwSuiteStatusException(stat); } } #endif /** @brief return a pixel pointer No attempt made to be uber efficient here. */ void *Image::getPixelAddress(int x, int y) { // are we in the image bounds if(x < _bounds.x1 || x >= _bounds.x2 || y < _bounds.y1 || y >= _bounds.y2 || _pixelBytes == 0) return 0; char *pix = ((char *) _pixelData) + (size_t)(y - _bounds.y1) * _rowBytes; pix += (x - _bounds.x1) * _pixelBytes; return (void *) pix; } const void *Image::getPixelAddress(int x, int y) const { // are we in the image bounds if(x < _bounds.x1 || x >= _bounds.x2 || y < _bounds.y1 || y >= _bounds.y2 || _pixelBytes == 0) return 0; const char *pix = ((const char *) _pixelData) + (size_t)(y - _bounds.y1) * _rowBytes; pix += (x - _bounds.x1) * _pixelBytes; return (const void *) pix; } //////////////////////////////////////////////////////////////////////////////// // clip instance /** @brief hidden constructor */ Clip::Clip(ImageEffect *effect, const std::string &name, OfxImageClipHandle handle, OfxPropertySetHandle props) : _clipName(name) , _clipProps(props) , _clipHandle(handle) , _effect(effect) { OFX::Validation::validateClipInstanceProperties(_clipProps); } /** @brief fetch the label */ void Clip::getLabel(std::string &label) const { label = _clipProps.propGetString(kOfxPropLabel); } /** @brief fetch the labels */ void Clip::getLabels(std::string &label, std::string &shortLabel, std::string &longLabel) const { getLabel(label); shortLabel = _clipProps.propGetString(kOfxPropShortLabel, false); longLabel = _clipProps.propGetString(kOfxPropLongLabel, false); } /** @brief get the pixel depth */ BitDepthEnum Clip::getPixelDepth(void) const { std::string str = _clipProps.propGetString(kOfxImageEffectPropPixelDepth); BitDepthEnum e; try { e = mapStrToBitDepthEnum(str); if(e == eBitDepthNone && isConnected()) { OFX::Log::error(true, "Clip %s is connected and has no pixel depth.", _clipName.c_str()); } } // gone wrong ? catch(std::invalid_argument) { OFX::Log::error(true, "Unknown pixel depth property '%s' reported on clip '%s'", str.c_str(), _clipName.c_str()); e = eBitDepthNone; } return e; } /** @brief get the components in the image */ PixelComponentEnum Clip::getPixelComponents(void) const { std::string str = _clipProps.propGetString(kOfxImageEffectPropComponents); PixelComponentEnum e; try { e = mapStrToPixelComponentEnum(str); if(e == ePixelComponentNone && isConnected()) { OFX::Log::error(true, "Clip %s is connected and has no pixel component type!", _clipName.c_str()); } } // gone wrong ? catch(std::invalid_argument) { OFX::Log::error(true, "Unknown pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str()); e = ePixelComponentNone; } return e; } /** @brief get the number of components in the image */ int Clip::getPixelComponentCount(void) const { std::string str = _clipProps.propGetString(kOfxImageEffectPropComponents); PixelComponentEnum e; try { e = mapStrToPixelComponentEnum(str); if(e == ePixelComponentNone && isConnected()) { OFX::Log::error(true, "Clip %s is connected and has no pixel component type!", _clipName.c_str()); } } // gone wrong ? catch(std::invalid_argument) { OFX::Log::error(true, "Unknown pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str()); e = ePixelComponentNone; } switch (e) { case ePixelComponentAlpha: return 1; case ePixelComponentNone: return 0; case ePixelComponentRGB: return 3; case ePixelComponentRGBA: return 4; case ePixelComponentCustom: default: return 0; } } /** @brief what is the actual pixel depth of the clip */ BitDepthEnum Clip::getUnmappedPixelDepth(void) const { std::string str = _clipProps.propGetString(kOfxImageClipPropUnmappedPixelDepth); BitDepthEnum e; try { e = mapStrToBitDepthEnum(str); if(e == eBitDepthNone && !isConnected()) { OFX::Log::error(true, "Clip %s is connected and has no unmapped pixel depth.", _clipName.c_str()); } } // gone wrong ? catch(std::invalid_argument) { OFX::Log::error(true, "Unknown unmapped pixel depth property '%s' reported on clip '%s'", str.c_str(), _clipName.c_str()); e = eBitDepthNone; } return e; } /** @brief what is the component type of the clip */ PixelComponentEnum Clip::getUnmappedPixelComponents(void) const { std::string str = _clipProps.propGetString(kOfxImageClipPropUnmappedComponents); PixelComponentEnum e; try { e = mapStrToPixelComponentEnum(str); if(e == ePixelComponentNone && !isConnected()) { OFX::Log::error(true, "Clip %s is connected and has no unmapped pixel component type!", _clipName.c_str()); } } // gone wrong ? catch(std::invalid_argument) { OFX::Log::error(true, "Unknown unmapped pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str()); e = ePixelComponentNone; } return e; } /** @brief get the components in the image */ PreMultiplicationEnum Clip::getPreMultiplication(void) const { std::string str = _clipProps.propGetString(kOfxImageEffectPropPreMultiplication); PreMultiplicationEnum e; try { e = mapStrToPreMultiplicationEnum(str); } // gone wrong ? catch(std::invalid_argument) { OFX::Log::error(true, "Unknown premultiplication type '%s' reported on clip %s!", str.c_str(), _clipName.c_str()); e = eImageOpaque; } return e; } /** @brief which spatial field comes first temporally */ FieldEnum Clip::getFieldOrder(void) const { std::string str = _clipProps.propGetString(kOfxImageClipPropFieldOrder); FieldEnum e; try { e = mapStrToFieldEnum(str); OFX::Log::error(e != eFieldNone && e != eFieldLower && e != eFieldUpper, "Field order '%s' reported on a clip %s is invalid, it must be none, lower or upper.", str.c_str(), _clipName.c_str()); } // gone wrong ? catch(std::invalid_argument) { OFX::Log::error(true, "Unknown field order '%s' reported on a clip %s.", str.c_str(), _clipName.c_str()); e = eFieldNone; } return e; } /** @brief is the clip connected */ bool Clip::isConnected(void) const { return _clipProps.propGetInt(kOfxImageClipPropConnected) != 0; } /** @brief can the clip be continuously sampled */ bool Clip::hasContinuousSamples(void) const { return _clipProps.propGetInt(kOfxImageClipPropContinuousSamples) != 0; } /** @brief get the scale factor that has been applied to this clip */ double Clip::getPixelAspectRatio(void) const { try { return _clipProps.propGetDouble(kOfxImagePropPixelAspectRatio); } catch(...) { return 1.0; // This error could happen in Eyeon Fusion. } } /** @brief get the frame rate, in frames per second on this clip, after any clip preferences have been applied */ double Clip::getFrameRate(void) const { return _clipProps.propGetDouble(kOfxImageEffectPropFrameRate); } /** @brief return the range of frames over which this clip has images, after any clip preferences have been applied */ OfxRangeD Clip::getFrameRange(void) const { OfxRangeD v; v.min = _clipProps.propGetDouble(kOfxImageEffectPropFrameRange, 0); v.max = _clipProps.propGetDouble(kOfxImageEffectPropFrameRange, 1); return v; } /** @brief get the frame rate, in frames per second on this clip, before any clip preferences have been applied */ double Clip::getUnmappedFrameRate(void) const { return _clipProps.propGetDouble(kOfxImageEffectPropUnmappedFrameRate); } /** @brief return the range of frames over which this clip has images, before any clip preferences have been applied */ OfxRangeD Clip::getUnmappedFrameRange(void) const { OfxRangeD v; v.min = _clipProps.propGetDouble(kOfxImageEffectPropUnmappedFrameRange, 0); v.max = _clipProps.propGetDouble(kOfxImageEffectPropUnmappedFrameRange, 1); return v; } /** @brief get the RoD for this clip in the cannonical coordinate system */ OfxRectD Clip::getRegionOfDefinition(double t) { OfxRectD bounds; OfxStatus stat = OFX::Private::gEffectSuite->clipGetRegionOfDefinition(_clipHandle, t, &bounds); if(stat == kOfxStatFailed) { bounds.x1 = bounds.x2 = bounds.y1 = bounds.y2 = 0; } throwSuiteStatusException(stat); return bounds; } /** @brief fetch an image */ Image *Clip::fetchImage(double t) { OfxPropertySetHandle imageHandle; OfxStatus stat = OFX::Private::gEffectSuite->clipGetImage(_clipHandle, t, NULL, &imageHandle); if(stat == kOfxStatFailed) { return NULL; // not an error, fetched images out of range/region, assume black and transparent } else throwSuiteStatusException(stat); return new Image(imageHandle); } /** @brief fetch an image, with a specific region in cannonical coordinates */ Image *Clip::fetchImage(double t, const OfxRectD &bounds) { OfxPropertySetHandle imageHandle; OfxStatus stat = OFX::Private::gEffectSuite->clipGetImage(_clipHandle, t, &bounds, &imageHandle); if(stat == kOfxStatFailed) { return NULL; // not an error, fetched images out of range/region, assume black and transparent } else throwSuiteStatusException(stat); return new Image(imageHandle); } #ifdef OFX_SUPPORTS_OPENGLRENDER Texture *Clip::loadTexture(double t, BitDepthEnum format, const OfxRectD *region) { if (!gHostDescription.supportsOpenGLRender) { throwHostMissingSuiteException("loadTexture"); } OfxPropertySetHandle hTex; OfxStatus stat = Private::gOpenGLRenderSuite->clipLoadTexture(_clipHandle, t, format == eBitDepthNone ? NULL : mapBitDepthEnumToStr(format), region, &hTex); if (stat != kOfxStatOK) { throwSuiteStatusException(stat); } return new Texture(hTex); } #endif //////////////////////////////////////////////////////////////////////////////// /// image effect /** @brief ctor */ ImageEffect::ImageEffect(OfxImageEffectHandle handle) : _effectHandle(handle) , _effectProps(0) , _context(eContextNone) , _progressStartSuccess(false) { // get the property handle _effectProps = OFX::Private::fetchEffectProps(handle); // Set this as the instance data pointer on the effect handle _effectProps.propSetPointer(kOfxPropInstanceData, this); // validate the plugin instance OFX::Validation::validatePluginInstanceProperties(_effectProps); // fetch the context std::string ctxt = _effectProps.propGetString(kOfxImageEffectPropContext); _context = mapToContextEnum(ctxt); // the param set daddy-oh OfxParamSetHandle paramSet; OfxStatus stat = OFX::Private::gEffectSuite->getParamSet(handle, ¶mSet); throwSuiteStatusException(stat); setParamSetHandle(paramSet); } /** @brief dtor */ ImageEffect::~ImageEffect() { // clobber the instance data property on the effect handle _effectProps.propSetPointer(kOfxPropInstanceData, 0); // delete any clip instances we may have constructed std::map::iterator iter; for(iter = _fetchedClips.begin(); iter != _fetchedClips.end(); ++iter) { if(iter->second) { delete iter->second; iter->second = NULL; } } } /** @brief the context this effect was instantiate in */ ContextEnum ImageEffect::getContext(void) const { return _context; } /** @brief size of the project */ OfxPointD ImageEffect::getProjectSize(void) const { OfxPointD v; v.x = _effectProps.propGetDouble(kOfxImageEffectPropProjectSize, 0); v.y = _effectProps.propGetDouble(kOfxImageEffectPropProjectSize, 1); return v; } /** @brief origin of the project */ OfxPointD ImageEffect::getProjectOffset(void) const { OfxPointD v; v.x = _effectProps.propGetDouble(kOfxImageEffectPropProjectOffset, 0); v.y = _effectProps.propGetDouble(kOfxImageEffectPropProjectOffset, 1); return v; } /** @brief extent of the project */ OfxPointD ImageEffect::getProjectExtent(void) const { OfxPointD v; v.x = _effectProps.propGetDouble(kOfxImageEffectPropProjectExtent, 0); v.y = _effectProps.propGetDouble(kOfxImageEffectPropProjectExtent, 1); return v; } /** @brief pixel aspect ratio of the project */ double ImageEffect::getProjectPixelAspectRatio(void) const { return _effectProps.propGetDouble(kOfxImageEffectPropProjectPixelAspectRatio, 0); } /** @brief how long does the effect last */ double ImageEffect::getEffectDuration(void) const { return _effectProps.propGetDouble(kOfxImageEffectInstancePropEffectDuration, 0); } /** @brief the frame rate of the project */ double ImageEffect::getFrameRate(void) const { return _effectProps.propGetDouble(kOfxImageEffectPropFrameRate, 0); } /** @brief is the instance currently being interacted with */ bool ImageEffect::isInteractive(void) const { return _effectProps.propGetInt(kOfxPropIsInteractive) != 0; } /** @brief set the instance to be sequentially renderred, this should have been part of clip preferences! */ void ImageEffect::setSequentialRender(bool v) { _effectProps.propSetInt(kOfxImageEffectInstancePropSequentialRender, int(v)); } /** @brief Have we informed the host we want to be seqentially renderred ? */ bool ImageEffect::getSequentialRender(void) const { return _effectProps.propGetInt(kOfxImageEffectInstancePropSequentialRender) != 0; } /** @brief Does the plugin support image tiling ? Can only be called from changedParam or changedClip. */ void ImageEffect::setSupportsTiles(bool v) { _effectProps.propSetInt(kOfxImageEffectPropSupportsTiles, int(v), false); // read/write from OFX 1.4 } /** @brief Have we informed the host we support image tiling ? */ bool ImageEffect::getSupportsTiles(void) const { return _effectProps.propGetInt(kOfxImageEffectPropSupportsTiles) != 0; } #ifdef OFX_SUPPORTS_OPENGLRENDER /** @brief Does the plugin support OpenGL accelerated rendering (but is also capable of CPU rendering) ? Can only be called from changedParam or changedClip. */ void ImageEffect::setSupportsOpenGLRender(bool v) { if (gHostDescription.supportsOpenGLRender) { _effectProps.propSetString(kOfxImageEffectPropOpenGLRenderSupported, (v ? "true" : "false"), false); // read/write from OFX 1.4 } } /** @brief Does the plugin require OpenGL accelerated rendering ? Can only be called from changedParam or changedClip. */ void ImageEffect::setNeedsOpenGLRender(bool v) { if (gHostDescription.supportsOpenGLRender) { _effectProps.propSetString(kOfxImageEffectPropOpenGLRenderSupported, (v ? "needed" : "false"), false); // read/write from OFX 1.4 } } #endif /** @brief notify host that the internal data structures need syncing back to parameters for persistance and so on. This is reset by the host after calling SyncPrivateData. */ void ImageEffect::setParamSetNeedsSyncing() { _effectProps.propSetInt(kOfxPropParamSetNeedsSyncing, 1, false); // introduced in OFX 1.2 } OFX::Message::MessageReplyEnum ImageEffect::sendMessage(OFX::Message::MessageTypeEnum type, const std::string& id, const std::string& msg) { if(!OFX::Private::gMessageSuite){ throwHostMissingSuiteException("message"); } if(!OFX::Private::gMessageSuite->message){ throwHostMissingSuiteException("message"); } OfxStatus stat = OFX::Private::gMessageSuite->message(_effectHandle, mapMessageTypeEnumToStr(type), id.c_str(), msg.c_str()); return mapToMessageReplyEnum(stat); } OFX::Message::MessageReplyEnum ImageEffect::setPersistentMessage(OFX::Message::MessageTypeEnum type, const std::string& id, const std::string& msg) { if(!OFX::Private::gMessageSuiteV2){ throwHostMissingSuiteException("setPersistentMessage"); } if(!OFX::Private::gMessageSuiteV2->setPersistentMessage){ throwHostMissingSuiteException("setPersistentMessage"); } OfxStatus stat = OFX::Private::gMessageSuiteV2->setPersistentMessage(_effectHandle, mapMessageTypeEnumToStr(type), id.c_str(), msg.c_str()); return mapToMessageReplyEnum(stat); } OFX::Message::MessageReplyEnum ImageEffect::clearPersistentMessage() { if(!OFX::Private::gMessageSuiteV2){ throwHostMissingSuiteException("clearPersistentMessage"); } if(!OFX::Private::gMessageSuiteV2->clearPersistentMessage){ throwHostMissingSuiteException("clearPersistentMessage"); } OfxStatus stat = OFX::Private::gMessageSuiteV2->clearPersistentMessage(_effectHandle); return mapToMessageReplyEnum(stat); } /** @brief Fetch the named clip from this instance */ Clip *ImageEffect::fetchClip(const std::string &name) { // do we have the clip already std::map::const_iterator search; search = _fetchedClips.find(name); if(search != _fetchedClips.end()) return search->second; // fetch the property set handle of the effect OfxImageClipHandle clipHandle = 0; OfxPropertySetHandle propHandle = 0; OfxStatus stat = OFX::Private::gEffectSuite->clipGetHandle(_effectHandle, name.c_str(), &clipHandle, &propHandle); throwSuiteStatusException(stat); // and make one Clip *newClip = new Clip(this, name, clipHandle, propHandle); // add it in _fetchedClips[name] = newClip; // return it return newClip; } /** @brief does the host want us to abort rendering? */ bool ImageEffect::abort(void) const { return OFX::Private::gEffectSuite->abort(_effectHandle) != 0; } /** @brief adds a new interact to the set of interacts open on this effect */ void ImageEffect::addOverlayInteract(OverlayInteract *interact) { // do we have it already ? std::list::iterator i; i = std::find(_overlayInteracts.begin(), _overlayInteracts.end(), interact); // we don't, put it in there if(i == _overlayInteracts.end()) { // we have a new one to add in here _overlayInteracts.push_back(interact); } } /** @brief removes an interact to the set of interacts open on this effect */ void ImageEffect::removeOverlayInteract(OverlayInteract *interact) { // find it std::list::iterator i; i = std::find(_overlayInteracts.begin(), _overlayInteracts.end(), interact); // and remove it if(i != _overlayInteracts.end()) { _overlayInteracts.erase(i); } } /** @brief force all overlays on this interact to be redrawn */ void ImageEffect::redrawOverlays(void) { // find it std::list::iterator i; for(i = _overlayInteracts.begin(); i != _overlayInteracts.end(); ++i) { (*i)->requestRedraw(); } } #ifdef OFX_SUPPORTS_OPENGLRENDER bool ImageEffect::flushOpenGLResources(void) { if (!gHostDescription.supportsOpenGLRender) { return false; } return Private::gOpenGLRenderSuite->flushResources() == kOfxStatOK; } #endif //////////////////////////////////////////////////////////////////////////////// // below are the default members for the base image effect /** @brief client is identity function, returns the clip and time for the identity function */ bool ImageEffect::isIdentity(const IsIdentityArguments &/*args*/, Clip * &/*identityClip*/, double &/*identityTime*/) { return false; // by default, we are not an identity operation } /** @brief The get RoD action */ bool ImageEffect::getRegionOfDefinition(const RegionOfDefinitionArguments &/*args*/, OfxRectD &/*rod*/) { return false; // by default, we are not setting the RoD } /** @brief the get RoI action */ void ImageEffect::getRegionsOfInterest(const RegionsOfInterestArguments &/*args*/, RegionOfInterestSetter &/*rois*/) { // fa niente } /** @brief the get frames needed action */ void ImageEffect::getFramesNeeded(const FramesNeededArguments &/*args*/, FramesNeededSetter &/*frames*/) { // fa niente } /** @brief client begin sequence render function */ void ImageEffect::beginSequenceRender(const BeginSequenceRenderArguments &/*args*/) { // fa niente } /** @brief client end sequence render function, this is one of the few that must be set */ void ImageEffect::endSequenceRender(const EndSequenceRenderArguments &/*args*/) { // fa niente } /** @brief The purge caches action, a request for an instance to free up as much memory as possible in low memory situations */ void ImageEffect::purgeCaches(void) { // fa niente } /** @brief The sync private data action, called when the effect needs to sync any private data to persistant parameters */ void ImageEffect::syncPrivateData(void) { // fa niente } /** @brief get the clip preferences */ void ImageEffect::getClipPreferences(ClipPreferencesSetter &/*clipPreferences*/) { // fa niente } /** @brief the effect is about to be actively edited by a user, called when the first user interface is opened on an instance */ void ImageEffect::beginEdit(void) { // fa niente } /** @brief the effect is no longer being edited by a user, called when the last user interface is closed on an instance */ void ImageEffect::endEdit(void) { // fa niente } /** @brief the effect is about to have some values changed */ void ImageEffect::beginChanged(InstanceChangeReason /*reason*/) { } /** @brief called when a param has just had its value changed */ void ImageEffect::changedParam(const InstanceChangedArgs &/*args*/, const std::string &/*paramName*/) { } /** @brief called when a clip has just been changed in some way (a rewire maybe) */ void ImageEffect::changedClip(const InstanceChangedArgs &/*args*/, const std::string &/*clipName*/) { } /** @brief the effect has just had some values changed */ void ImageEffect::endChanged(InstanceChangeReason /*reason*/) { } /** @brief get the time domain */ bool ImageEffect::getTimeDomain(OfxRangeD &/*range*/) { // by default, do the default return false; } #ifdef OFX_SUPPORTS_OPENGLRENDER /** @brief OpenGL context attached */ void ImageEffect::contextAttached(void) { // fa niente } /** @brief OpenGL context detached */ void ImageEffect::contextDetached(void) { // fa niente } #endif /** @brief called when a custom param needs to be interpolated */ std::string ImageEffect::interpolateCustomParam(const InterpolateCustomArgs &args, const std::string &/*paramName*/) { return args.value1; } /// Start doing progress. void ImageEffect::progressStart(const std::string &message, const std::string &messageid) { if(OFX::Private::gProgressSuiteV2) { OfxStatus stat = OFX::Private::gProgressSuiteV2->progressStart((void *) _effectHandle, message.c_str(), messageid.c_str()); _progressStartSuccess = ( stat == kOfxStatOK ); } else if(OFX::Private::gProgressSuiteV1) { OfxStatus stat = OFX::Private::gProgressSuiteV1->progressStart((void *) _effectHandle, message.c_str()); _progressStartSuccess = ( stat == kOfxStatOK ); } } /// finish yer progress void ImageEffect::progressEnd() { if(_progressStartSuccess) { if(OFX::Private::gProgressSuiteV2) { OFX::Private::gProgressSuiteV2->progressEnd((void *) _effectHandle); } else if(OFX::Private::gProgressSuiteV1) { OFX::Private::gProgressSuiteV1->progressEnd((void *) _effectHandle); } } } /// set the progress to some level of completion, returns /// false if you should abandon processing, true to continue bool ImageEffect::progressUpdate(double t) { if(_progressStartSuccess) { if(OFX::Private::gProgressSuiteV2) { OfxStatus stat = OFX::Private::gProgressSuiteV2->progressUpdate((void *) _effectHandle, t); if(stat == kOfxStatReplyNo) return false; } else if(OFX::Private::gProgressSuiteV1) { OfxStatus stat = OFX::Private::gProgressSuiteV1->progressUpdate((void *) _effectHandle, t); if(stat == kOfxStatReplyNo) return false; } } return true; } /// get the current time on the timeline. This is not necessarily the same /// time as being passed to an action (eg render) double ImageEffect::timeLineGetTime() { if(OFX::Private::gTimeLineSuite) { double time; if(OFX::Private::gTimeLineSuite->getTime((void *) _effectHandle, &time) == kOfxStatOK) return time; } return 0; } /// set the timeline to a specific time void ImageEffect::timeLineGotoTime(double t) { if(OFX::Private::gTimeLineSuite) { OFX::Private::gTimeLineSuite->gotoTime((void *) _effectHandle, t); } } /// get the first and last times available on the effect's timeline void ImageEffect:: timeLineGetBounds(double &t1, double &t2) { if(OFX::Private::gTimeLineSuite) { OFX::Private::gTimeLineSuite->getTimeBounds((void *) _effectHandle, &t1, &t2); return; } t1 = t2 = 0; } //////////////////////////////////////////////////////////////////////////////// // Class used to set the clip preferences of the effect. */ const std::string& ClipPreferencesSetter::extractValueForName(const StringStringMap& m, const std::string& name) { StringStringMap::const_iterator it = m.find(name); if(it==m.end()) throw(Exception::PropertyUnknownToHost(name.c_str())); return it->second; } /** @brief, force the host to set a clip's mapped component type to be \em comps. */ void ClipPreferencesSetter::setClipComponents(Clip &clip, PixelComponentEnum comps) { doneSomething_ = true; const std::string& propName = extractValueForName(clipComponentPropNames_, clip.name()); switch(comps) { case ePixelComponentNone : outArgs_.propSetString(propName.c_str(), kOfxImageComponentNone); break; case ePixelComponentRGBA : outArgs_.propSetString(propName.c_str(), kOfxImageComponentRGBA); break; case ePixelComponentRGB : outArgs_.propSetString(propName.c_str(), kOfxImageComponentRGB); break; case ePixelComponentAlpha : outArgs_.propSetString(propName.c_str(), kOfxImageComponentAlpha); break; case ePixelComponentCustom : break; } } /** @brief, force the host to set a clip's mapped bit depth be \em bitDepth */ void ClipPreferencesSetter::setClipBitDepth(Clip &clip, BitDepthEnum bitDepth) { doneSomething_ = true; const std::string& propName = extractValueForName(clipDepthPropNames_, clip.name()); switch(bitDepth) { case eBitDepthNone : outArgs_.propSetString(propName.c_str(), kOfxBitDepthNone); break; case eBitDepthUByte : outArgs_.propSetString(propName.c_str(), kOfxBitDepthByte); break; case eBitDepthUShort : outArgs_.propSetString(propName.c_str(), kOfxBitDepthShort); break; case eBitDepthHalf : outArgs_.propSetString(propName.c_str(), kOfxBitDepthHalf); break; case eBitDepthFloat : outArgs_.propSetString(propName.c_str(), kOfxBitDepthFloat); break; case eBitDepthCustom : break; } } /** @brief, force the host to set a clip's mapped Pixel Aspect Ratio to be \em PAR */ void ClipPreferencesSetter::setPixelAspectRatio(Clip &clip, double PAR) { doneSomething_ = true; const std::string& propName = extractValueForName(clipPARPropNames_, clip.name()); outArgs_.propSetDouble(propName.c_str(), PAR); } /** @brief Allows an effect to change the output frame rate */ void ClipPreferencesSetter::setOutputFrameRate(double v) { doneSomething_ = true; outArgs_.propSetDouble(kOfxImageEffectPropFrameRate, v); } /** @brief Set the premultiplication state of the output clip. */ void ClipPreferencesSetter::setOutputPremultiplication(PreMultiplicationEnum v) { doneSomething_ = true; switch(v) { case eImageOpaque : outArgs_.propSetString(kOfxImageEffectPropPreMultiplication, kOfxImageOpaque); break; case eImagePreMultiplied: outArgs_.propSetString(kOfxImageEffectPropPreMultiplication, kOfxImagePreMultiplied); break; case eImageUnPreMultiplied: outArgs_.propSetString(kOfxImageEffectPropPreMultiplication, kOfxImageUnPreMultiplied); break; } } /** @brief Set whether the effect can be continously sampled. */ void ClipPreferencesSetter::setOutputHasContinousSamples(bool v) { doneSomething_ = true; outArgs_.propSetInt(kOfxImageClipPropContinuousSamples, int(v)); } /** @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). */ void ClipPreferencesSetter::setOutputFrameVarying(bool v) { doneSomething_ = true; outArgs_.propSetInt(kOfxImageEffectFrameVarying, int(v)); } void ClipPreferencesSetter::setOutputFielding(FieldEnum v) { doneSomething_ = true; switch(v) { case eFieldNone : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldNone, 0, false); break; case eFieldLower : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldLower, 0, false); break; case eFieldUpper : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldUpper, 0, false); break; case eFieldBoth : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldBoth, 0, false); break; case eFieldSingle : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldSingle, 0, false); break; case eFieldDoubled : outArgs_.propSetString(kOfxImageClipPropFieldOrder, kOfxImageFieldDoubled, 0, false); break; } } //////////////////////////////////////////////////////////////////////////////// /** @brief Class that skins image memory allocation */ /** @brief ctor */ ImageMemory::ImageMemory(size_t nBytes, ImageEffect *associatedEffect) : _handle(0) { OfxImageEffectHandle effectHandle = 0; if(associatedEffect != 0) { effectHandle = associatedEffect->_effectHandle; } OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryAlloc(effectHandle, nBytes, &_handle); if(stat == kOfxStatErrMemory) throw std::bad_alloc(); throwSuiteStatusException(stat); } /** @brief dtor */ ImageMemory::~ImageMemory() { OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryFree(_handle); // ignore status code for exception purposes (void)stat; } /** @brief lock the memory and return a pointer to it */ void *ImageMemory::lock(void) { void *ptr; OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryLock(_handle, &ptr); if(stat == kOfxStatErrMemory) throw std::bad_alloc(); throwSuiteStatusException(stat); return ptr; } /** @brief unlock the memory */ void ImageMemory::unlock(void) { OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryUnlock(_handle); (void)stat; } /** @brief OFX::Private namespace, for things private to the support library code here generally calls image effect class members */ namespace Private { /** @brief Creates the global host description and sets its properties */ static void fetchHostDescription(OfxHost *host) { OFX::Log::error(OFX::gHostDescriptionHasInit, "Tried to create host description when we already have one."); if(!OFX::gHostDescriptionHasInit) { OFX::gHostDescriptionHasInit = true; // wrap the property handle up with a property set PropertySet hostProps(host->host); // and get some properties gHostDescription.APIVersionMajor = hostProps.propGetInt(kOfxPropAPIVersion, 0, false); // OFX 1.2 if (gHostDescription.APIVersionMajor == 0) { // assume OFX 1.0 gHostDescription.APIVersionMajor = 1; } gHostDescription.APIVersionMinor = hostProps.propGetInt(kOfxPropAPIVersion, 1, false); // OFX 1.2 gHostDescription.hostName = hostProps.propGetString(kOfxPropName); gHostDescription.hostLabel = hostProps.propGetString(kOfxPropLabel); gHostDescription.versionMajor = hostProps.propGetInt(kOfxPropVersion, 0, false); // OFX 1.2 gHostDescription.versionMinor = hostProps.propGetInt(kOfxPropVersion, 1, false); // OFX 1.2 gHostDescription.versionMicro = hostProps.propGetInt(kOfxPropVersion, 2, false); // OFX 1.2 gHostDescription.versionLabel = hostProps.propGetString(kOfxPropVersionLabel, false); // OFX 1.2 gHostDescription.hostIsBackground = hostProps.propGetInt(kOfxImageEffectHostPropIsBackground) != 0; gHostDescription.supportsOverlays = hostProps.propGetInt(kOfxImageEffectPropSupportsOverlays) != 0; gHostDescription.supportsMultiResolution = hostProps.propGetInt(kOfxImageEffectPropSupportsMultiResolution) != 0; gHostDescription.supportsTiles = hostProps.propGetInt(kOfxImageEffectPropSupportsTiles) != 0; gHostDescription.temporalClipAccess = hostProps.propGetInt(kOfxImageEffectPropTemporalClipAccess) != 0; gHostDescription.supportsMultipleClipDepths = hostProps.propGetInt(kOfxImageEffectPropSupportsMultipleClipDepths) != 0; gHostDescription.supportsMultipleClipPARs = hostProps.propGetInt(kOfxImageEffectPropSupportsMultipleClipPARs) != 0; gHostDescription.supportsSetableFrameRate = hostProps.propGetInt(kOfxImageEffectPropSetableFrameRate) != 0; gHostDescription.supportsSetableFielding = hostProps.propGetInt(kOfxImageEffectPropSetableFielding) != 0; gHostDescription.sequentialRender = hostProps.propGetInt(kOfxImageEffectInstancePropSequentialRender, false); // appeared in OFX 1.2 gHostDescription.supportsStringAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStringAnimation) != 0; gHostDescription.supportsCustomInteract = hostProps.propGetInt(kOfxParamHostPropSupportsCustomInteract) != 0; gHostDescription.supportsChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsChoiceAnimation) != 0; gHostDescription.supportsBooleanAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsBooleanAnimation) != 0; gHostDescription.supportsCustomAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsCustomAnimation) != 0; gHostDescription.osHandle = hostProps.propGetPointer(kOfxPropHostOSHandle, false); gHostDescription.supportsParametricParameter = gParametricParameterSuite != 0; gHostDescription.supportsParametricAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsParametricAnimation, false) != 0; gHostDescription.supportsRenderQualityDraft = hostProps.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0; // appeared in OFX 1.4 { std::string originStr = hostProps.propGetString(kOfxImageEffectHostPropNativeOrigin, false); // appeared in OFX 1.4 if (originStr.empty()) { // from http://openeffects.org/standard_changes/host-origin-hints : // "All this hint does is tell plugin that the host world is different // than OFX. Historically the first two hosts that exhibited this issue // could be Fusion (upper left is 0,0 natively) and Toxic (Center is 0,0)." if (gHostDescription.hostName == "com.eyeonline.Fusion" || ends_with(gHostDescription.hostName, "Fusion")) { // if host is Fusion, set to TopLeft gHostDescription.nativeOrigin = eNativeOriginTopLeft; } else if (starts_with(gHostDescription.hostName, "Autodesk Toxik") || ends_with(gHostDescription.hostName, "Toxik")) { // if host is Toxic, set to Center gHostDescription.nativeOrigin = eNativeOriginCenter; } else { gHostDescription.nativeOrigin = eNativeOriginBottomLeft; } } else if (originStr == kOfxHostNativeOriginBottomLeft) { gHostDescription.nativeOrigin = eNativeOriginBottomLeft; } else if (originStr == kOfxHostNativeOriginTopLeft) { gHostDescription.nativeOrigin = eNativeOriginTopLeft; } else if (originStr == kOfxHostNativeOriginCenter) { gHostDescription.nativeOrigin = eNativeOriginCenter; } } #ifdef OFX_SUPPORTS_OPENGLRENDER gHostDescription.supportsOpenGLRender = gOpenGLRenderSuite != 0 && hostProps.propGetString(kOfxImageEffectPropOpenGLRenderSupported, 0, false) == "true"; #endif gHostDescription.maxParameters = hostProps.propGetInt(kOfxParamHostPropMaxParameters); gHostDescription.maxPages = hostProps.propGetInt(kOfxParamHostPropMaxPages); gHostDescription.pageRowCount = hostProps.propGetInt(kOfxParamHostPropPageRowColumnCount, 0); gHostDescription.pageColumnCount = hostProps.propGetInt(kOfxParamHostPropPageRowColumnCount, 1); int numComponents = hostProps.propGetDimension(kOfxImageEffectPropSupportedComponents); for(int i=0; igetPropertySet(handle, &propHandle); throwSuiteStatusException(stat); return OFX::PropertySet(propHandle); } /** @brief Keeps count of how many times load/unload have been called */ int gLoadCount = 0; /** @brief Library side load action, this fetches all the suite pointers */ void loadAction(void) { gLoadCount++; //OfxStatus status = kOfxStatOK; // fetch the suites OFX::Log::error(gHost == 0, "Host pointer has not been set."); if(!gHost) throw OFX::Exception::Suite(kOfxStatErrBadHandle); if(gLoadCount == 1) { gEffectSuite = (OfxImageEffectSuiteV1 *) fetchSuite(kOfxImageEffectSuite, 1); gPropSuite = (OfxPropertySuiteV1 *) fetchSuite(kOfxPropertySuite, 1); gParamSuite = (OfxParameterSuiteV1 *) fetchSuite(kOfxParameterSuite, 1); gMemorySuite = (OfxMemorySuiteV1 *) fetchSuite(kOfxMemorySuite, 1); gThreadSuite = (OfxMultiThreadSuiteV1 *) fetchSuite(kOfxMultiThreadSuite, 1); gMessageSuite = (OfxMessageSuiteV1 *) fetchSuite(kOfxMessageSuite, 1); gMessageSuiteV2 = (OfxMessageSuiteV2 *) fetchSuite(kOfxMessageSuite, 2, true); gProgressSuiteV1 = (OfxProgressSuiteV1 *) fetchSuite(kOfxProgressSuite, 1, true); gProgressSuiteV2 = (OfxProgressSuiteV2 *) fetchSuite(kOfxProgressSuite, 2, true); gTimeLineSuite = (OfxTimeLineSuiteV1 *) fetchSuite(kOfxTimeLineSuite, 1, true); gParametricParameterSuite = (OfxParametricParameterSuiteV1*) fetchSuite(kOfxParametricParameterSuite, 1, true); #ifdef OFX_SUPPORTS_OPENGLRENDER gOpenGLRenderSuite = (OfxImageEffectOpenGLRenderSuiteV1*) fetchSuite(kOfxOpenGLRenderSuite, 1, true); #endif // OK check and fetch host information fetchHostDescription(gHost); /// and set some dendent flags OFX::gHostDescription.supportsMessageSuiteV2 = gMessageSuiteV2 != NULL; OFX::gHostDescription.supportsProgressSuite = (gProgressSuiteV1 != NULL || gProgressSuiteV2 != NULL); OFX::gHostDescription.supportsTimeLineSuite = gTimeLineSuite != NULL; // fetch the interact suite if the host supports interaction if(OFX::gHostDescription.supportsOverlays || OFX::gHostDescription.supportsCustomInteract) gInteractSuite = (OfxInteractSuiteV1 *) fetchSuite(kOfxInteractSuite, 1); } // initialise the validation code OFX::Validation::initialise(); // validate the host OFX::Validation::validateHostProperties(gHost); } /** @brief Library side unload action, this fetches all the suite pointers */ static void unloadAction(const char* id) { gLoadCount--; if (gLoadCount<0) { OFX::Log::warning(true, "OFX Plugin '%s' is already unloaded.", id); return; } if(gLoadCount==0) { // force these to null gEffectSuite = 0; gPropSuite = 0; gParamSuite = 0; gMemorySuite = 0; gThreadSuite = 0; gMessageSuite = 0; gMessageSuiteV2 = 0; gInteractSuite = 0; gParametricParameterSuite = 0; } { EffectDescriptorMap::iterator it = gEffectDescriptors.find(id); EffectContextMap& toBeDeleted = it->second; for(EffectContextMap::iterator it2 = toBeDeleted.begin(); it2 != toBeDeleted.end(); ++it2) { OFX::ImageEffectDescriptor* desc = it2->second; delete desc; } toBeDeleted.clear(); } { OFX::OfxPlugInfoMap::iterator it = OFX::plugInfoMap.find(id); OfxPlugin* plug = it->second._plug; OFX::OfxPluginArray::iterator it2 = std::find(ofxPlugs.begin(), ofxPlugs.end(), plug); if (it2 != ofxPlugs.end()) { (*it2) = 0; } delete plug; } } /** @brief fetches our pointer out of the props on the handle */ ImageEffect *retrieveImageEffectPointer(OfxImageEffectHandle handle) { ImageEffect *instance; // get the prop set on the handle OfxPropertySetHandle propHandle; OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet(handle, &propHandle); throwSuiteStatusException(stat); // make our wrapper object PropertySet props(propHandle); // fetch the instance data out of the properties instance = (ImageEffect *) props.propGetPointer(kOfxPropInstanceData); OFX::Log::error(instance == 0, "Instance data handle in effect instance properties is NULL!"); // need to throw something here // and dance to the music return instance; } /** @brief Checks the handles passed into the plugin's main entry point */ static void checkMainHandles(const std::string &action, const void *handle, OfxPropertySetHandle inArgsHandle, OfxPropertySetHandle outArgsHandle, bool handleCanBeNull, bool inArgsCanBeNull, bool outArgsCanBeNull) { if(handleCanBeNull) OFX::Log::warning(handle != 0, "Handle passed to '%s' is not null.", action.c_str()); else OFX::Log::error(handle == 0, "'Handle passed to '%s' is null.", action.c_str()); if(inArgsCanBeNull) OFX::Log::warning(inArgsHandle != 0, "'inArgs' Handle passed to '%s' is not null.", action.c_str()); else OFX::Log::error(inArgsHandle == 0, "'inArgs' handle passed to '%s' is null.", action.c_str()); if(outArgsCanBeNull) OFX::Log::warning(outArgsHandle != 0, "'outArgs' Handle passed to '%s' is not null.", action.c_str()); else OFX::Log::error(outArgsHandle == 0, "'outArgs' handle passed to '%s' is null.", action.c_str()); // validate the property sets on the arguments OFX::Validation::validateActionArgumentsProperties(action, inArgsHandle, outArgsHandle); // throw exceptions if null when not meant to be null if(!handleCanBeNull && !handle) throwSuiteStatusException(kOfxStatErrBadHandle); if(!inArgsCanBeNull && !inArgsHandle) throwSuiteStatusException(kOfxStatErrBadHandle); if(!outArgsCanBeNull && !outArgsHandle) throwSuiteStatusException(kOfxStatErrBadHandle); } /** @brief Fetches the arguments used in a render action 'inargs' property set into a POD struct */ static void getRenderActionArguments(RenderArguments &args, OFX::PropertySet inArgs) { args.time = inArgs.propGetDouble(kOfxPropTime); args.renderScale.x = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 0); args.renderScale.y = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 1); args.renderWindow.x1 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 0); args.renderWindow.y1 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 1); args.renderWindow.x2 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 2); args.renderWindow.y2 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 3); #ifdef OFX_SUPPORTS_OPENGLRENDER // Don't throw an exception if the following inArgs are not present. // OpenGL rendering appeared in OFX 1.3 args.openGLEnabled = inArgs.propGetInt(kOfxImageEffectPropOpenGLEnabled, false) != 0; #endif // Don't throw an exception if the following inArgs are not present: // They appeared in OFX 1.2. args.sequentialRenderStatus = inArgs.propGetInt(kOfxImageEffectPropSequentialRenderStatus, false) != 0; args.interactiveRenderStatus = inArgs.propGetInt(kOfxImageEffectPropInteractiveRenderStatus, false) != 0; // kOfxImageEffectPropRenderQualityDraft appeared in OFX 1.4 args.renderQualityDraft = inArgs.propGetInt(kOfxImageEffectPropRenderQualityDraft, false) != 0; args.fieldToRender = eFieldNone; std::string str = inArgs.propGetString(kOfxImageEffectPropFieldToRender); try { args.fieldToRender = mapStrToFieldEnum(str); } catch (std::invalid_argument) { // dud field? OFX::Log::error(true, "Unknown field to render '%s'", str.c_str()); // HACK need to throw something to cause a failure } } /** @brief Library side render action, fetches relevant properties and calls the client code */ static void renderAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); RenderArguments args; // get the arguments getRenderActionArguments(args, inArgs); // and call the plugin client render code effectInstance->render(args); } /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */ static void beginSequenceRenderAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); BeginSequenceRenderArguments args; args.frameRange.min = inArgs.propGetDouble(kOfxImageEffectPropFrameRange, 0); args.frameRange.max = inArgs.propGetDouble(kOfxImageEffectPropFrameRange, 1); args.frameStep = inArgs.propGetDouble(kOfxImageEffectPropFrameStep, 0); args.renderScale.x = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 0); args.renderScale.y = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 1); #ifdef OFX_SUPPORTS_OPENGLRENDER // Don't throw an exception if the following inArgs are not present. // OpenGL rendering appeared in OFX 1.3 args.openGLEnabled = inArgs.propGetInt(kOfxImageEffectPropOpenGLEnabled, false) != 0; #endif args.isInteractive = inArgs.propGetInt(kOfxPropIsInteractive) != 0; // Don't throw an exception if the following inArgs are not present: // They appeared in OFX 1.2 args.sequentialRenderStatus = inArgs.propGetInt(kOfxImageEffectPropSequentialRenderStatus, false) != 0; args.interactiveRenderStatus = inArgs.propGetInt(kOfxImageEffectPropInteractiveRenderStatus, false) != 0; // and call the plugin client render code effectInstance->beginSequenceRender(args); } /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */ static void endSequenceRenderAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); EndSequenceRenderArguments args; args.renderScale.x = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 0); args.renderScale.y = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 1); #ifdef OFX_SUPPORTS_OPENGLRENDER // Don't throw an exception if the following inArgs are not present. // OpenGL rendering appeared in OFX 1.3 args.openGLEnabled = inArgs.propGetInt(kOfxImageEffectPropOpenGLEnabled, false) != 0; #endif args.isInteractive = inArgs.propGetInt(kOfxPropIsInteractive) != 0; // Don't throw an exception if the following inArgs are not present: // They appeared in OFX 1.2 args.sequentialRenderStatus = inArgs.propGetInt(kOfxImageEffectPropSequentialRenderStatus, false) != 0; args.interactiveRenderStatus = inArgs.propGetInt(kOfxImageEffectPropInteractiveRenderStatus, false) != 0; // and call the plugin client render code effectInstance->endSequenceRender(args); } /** @brief Fetches the arguments used in a isIdentity action 'inargs' property set into a POD struct */ static void getIsIdentityActionArguments(IsIdentityArguments &args, OFX::PropertySet inArgs) { args.time = inArgs.propGetDouble(kOfxPropTime); args.renderScale.x = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 0); args.renderScale.y = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 1); args.renderWindow.x1 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 0); args.renderWindow.y1 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 1); args.renderWindow.x2 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 2); args.renderWindow.y2 = inArgs.propGetInt(kOfxImageEffectPropRenderWindow, 3); std::string str = inArgs.propGetString(kOfxImageEffectPropFieldToRender); try { args.fieldToRender = mapStrToFieldEnum(str); } catch (std::invalid_argument) { // dud field? OFX::Log::error(true, "Unknown field to render '%s'", str.c_str()); // HACK need to throw something to cause a failure } } /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */ static bool isIdentityAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); IsIdentityArguments args; // get the arguments getIsIdentityActionArguments(args, inArgs); // and call the plugin client isIdentity code Clip *identityClip = 0; double identityTime = args.time; bool v = effectInstance->isIdentity(args, identityClip, identityTime); if(v && identityClip) { outArgs.propSetString(kOfxPropName, identityClip->name()); outArgs.propSetDouble(kOfxPropTime, identityTime); return true; } return false; } /** @brief Library side get region of definition function */ static bool regionOfDefinitionAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); RegionOfDefinitionArguments args; args.renderScale.x = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 0); args.renderScale.y = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 1); args.time = inArgs.propGetDouble(kOfxPropTime); // and call the plugin client code OfxRectD rod; bool v = effectInstance->getRegionOfDefinition(args, rod); if(v) { outArgs.propSetDouble(kOfxImageEffectPropRegionOfDefinition, rod.x1, 0); outArgs.propSetDouble(kOfxImageEffectPropRegionOfDefinition, rod.y1, 1); outArgs.propSetDouble(kOfxImageEffectPropRegionOfDefinition, rod.x2, 2); outArgs.propSetDouble(kOfxImageEffectPropRegionOfDefinition, rod.y2, 3); return true; } return false; } /** @brief Library side get regions of interest function */ static bool regionsOfInterestAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs, const char* plugname) { /** @brief local class to set the roi of a clip */ class LOCAL ActualROISetter : public OFX::RegionOfInterestSetter { OFX::PropertySet &outArgs_; bool doneSomething_; const std::map& clipROIPropNames_; public : /** @brief ctor */ ActualROISetter(OFX::PropertySet &args, const std::map& clipROIPropNames) : outArgs_(args) , doneSomething_(false) , clipROIPropNames_(clipROIPropNames) { } /** @brief did we set something ? */ bool didSomething(void) const {return doneSomething_;} /** @brief set the RoI of the clip */ virtual void setRegionOfInterest(const Clip &clip, const OfxRectD &roi) { std::map::const_iterator it = clipROIPropNames_.find(clip.name()); if(it==clipROIPropNames_.end()) throw(Exception::PropertyUnknownToHost(clip.name().c_str())); // construct the name of the property const std::string& propName = it->second; // and set it outArgs_.propSetDouble(propName.c_str(), roi.x1, 0); outArgs_.propSetDouble(propName.c_str(), roi.y1, 1); outArgs_.propSetDouble(propName.c_str(), roi.x2, 2); outArgs_.propSetDouble(propName.c_str(), roi.y2, 3); // and record the face we have done something doneSomething_ = true; } }; // end of local class // fetch our effect pointer ImageEffect *effectInstance = retrieveImageEffectPointer(handle); RegionsOfInterestArguments args; // fetch in arguments from the prop handle args.renderScale.x = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 0); args.renderScale.y = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 1); args.regionOfInterest.x1 = inArgs.propGetDouble(kOfxImageEffectPropRegionOfInterest, 0); args.regionOfInterest.y1 = inArgs.propGetDouble(kOfxImageEffectPropRegionOfInterest, 1); args.regionOfInterest.x2 = inArgs.propGetDouble(kOfxImageEffectPropRegionOfInterest, 2); args.regionOfInterest.y2 = inArgs.propGetDouble(kOfxImageEffectPropRegionOfInterest, 3); args.time = inArgs.propGetDouble(kOfxPropTime); // make a roi setter object ActualROISetter setRoIs(outArgs, gEffectDescriptors[plugname][effectInstance->getContext()]->getClipROIPropNames()); // and call the plugin client code effectInstance->getRegionsOfInterest(args, setRoIs); // did we do anything ? if(setRoIs.didSomething()) return true; return false; } /** @brief Library side frames needed action */ static bool framesNeededAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet &outArgs, const char* plugname) { /** @brief local class to set the frames needed from a clip */ class LOCAL ActualSetter : public OFX::FramesNeededSetter { OFX::PropertySet &outArgs_; /**< @brief property set to set values in */ std::map > frameRanges_; /**< @brief map holding a bunch of frame ranges, one for each clip */ const std::map& _clipFrameRangePropNames; public : /** @brief ctor */ ActualSetter(OFX::PropertySet &args, const std::map& clipFrameRangePropNames) : outArgs_(args), _clipFrameRangePropNames(clipFrameRangePropNames) { } /** @brief set the RoI of the clip */ virtual void setFramesNeeded(const Clip &clip, const OfxRangeD &range) { // insert this into the vector which is in the map frameRanges_[clip.name()].push_back(range); } /** @brief write frameRanges_ back to the property set */ bool setOutProperties(void) { bool didSomething = false; std::map >::iterator i; for(i = frameRanges_.begin(); i != frameRanges_.end(); ++i) { if(i->first != kOfxImageEffectOutputClipName) { didSomething = true; // Make the property name we are setting const std::map::const_iterator it = _clipFrameRangePropNames.find(i->first); if(it==_clipFrameRangePropNames.end()) throw(Exception::PropertyUnknownToHost(i->first.c_str())); const std::string& propName = it->second; // fetch the list of frame ranges std::vector &clipRange = i->second; std::vector::iterator j; int n = 0; // and set 'em for(j = clipRange.begin(); j < clipRange.end(); ++j) { outArgs_.propSetDouble(propName.c_str(), j->min, n++); outArgs_.propSetDouble(propName.c_str(), j->max, n++); } } } return didSomething; } }; // end of local class // fetch our effect pointer ImageEffect *effectInstance = retrieveImageEffectPointer(handle); FramesNeededArguments args; // fetch in arguments from the prop handle args.time = inArgs.propGetDouble(kOfxPropTime); // make a roi setter object ActualSetter setFrames(outArgs, gEffectDescriptors[plugname][effectInstance->getContext()]->getClipFrameRangePropNames()); // and call the plugin client code effectInstance->getFramesNeeded(args, setFrames); // Write it back to the properties and see if we set anything if(setFrames.setOutProperties()) return true; return false; } /** @brief Library side get regions of interest function */ static bool getTimeDomainAction(OfxImageEffectHandle handle, OFX::PropertySet &outArgs) { // fetch our effect pointer ImageEffect *effectInstance = retrieveImageEffectPointer(handle); // we can only be a general context effect, so check that this is true OFX::Log::error(effectInstance->getContext() != eContextGeneral, "Calling kOfxImageEffectActionGetTimeDomain on an effect that is not a general context effect."); OfxRangeD timeDomain; // and call the plugin client code bool v = effectInstance->getTimeDomain(timeDomain); if(v) { outArgs.propSetDouble(kOfxImageEffectPropFrameRange, timeDomain.min, 0); outArgs.propSetDouble(kOfxImageEffectPropFrameRange, timeDomain.max, 1); } return v; } /** @brief Library side get regions of interest function */ static bool clipPreferencesAction(OfxImageEffectHandle handle, OFX::PropertySet &outArgs, const char* plugname) { // fetch our effect pointer ImageEffect *effectInstance = retrieveImageEffectPointer(handle); // set up our clip preferences setter ImageEffectDescriptor* desc = gEffectDescriptors[plugname][effectInstance->getContext()]; ClipPreferencesSetter prefs(outArgs, desc->getClipDepthPropNames(), desc->getClipComponentPropNames(), desc->getClipPARPropNames()); // and call the plug-in client code effectInstance->getClipPreferences(prefs); // did we do anything ? if(prefs.didSomething()) return true; return false; } /** @brief Library side begin instance changed action */ static void beginInstanceChangedAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); std::string reasonStr = inArgs.propGetString(kOfxPropChangeReason); InstanceChangeReason reason = mapToInstanceChangedReason(reasonStr); // and call the plugin client code effectInstance->beginChanged(reason); } /** @brief Library side instance changed action */ static void instanceChangedAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); InstanceChangedArgs args; // why did it change std::string reasonStr = inArgs.propGetString(kOfxPropChangeReason); args.reason = mapToInstanceChangedReason(reasonStr); args.time = inArgs.propGetDouble(kOfxPropTime); args.renderScale.x = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 0); args.renderScale.y = inArgs.propGetDouble(kOfxImageEffectPropRenderScale, 1); // what changed std::string changedType = inArgs.propGetString(kOfxPropType); std::string changedName = inArgs.propGetString(kOfxPropName); if(changedType == kOfxTypeParameter) { // and call the plugin client code effectInstance->changedParam(args, changedName); } else if(changedType == kOfxTypeClip) { // and call the plugin client code effectInstance->changedClip(args, changedName); } else { OFX::Log::error(true, "Instance Changed called with unknown type '%s' of object '%s'", changedType.c_str(), changedName.c_str()); } } /** @brief Library side end instance changed action */ static void endInstanceChangedAction(OfxImageEffectHandle handle, OFX::PropertySet inArgs) { ImageEffect *effectInstance = retrieveImageEffectPointer(handle); std::string reasonStr = inArgs.propGetString(kOfxPropChangeReason); InstanceChangeReason reason = mapToInstanceChangedReason(reasonStr); // and call the plugin client code effectInstance->endChanged(reason); } /** @brief The main entry point for the plugin */ OfxStatus mainEntryStr(const char *actionRaw, const void *handleRaw, OfxPropertySetHandle inArgsRaw, OfxPropertySetHandle outArgsRaw, const char* plugname) { OFX::Log::print("********************************************************************************"); OFX::Log::print("START mainEntry (%s)", actionRaw); OFX::Log::indent(); OfxStatus stat = kOfxStatReplyDefault; try { OfxPlugInfoMap::iterator it = plugInfoMap.find(plugname); if(it==plugInfoMap.end()) throw; OFX::PluginFactory* factory = it->second._factory; // Cast the raw handle to be an image effect handle, because that is what it is OfxImageEffectHandle handle = (OfxImageEffectHandle) handleRaw; // Turn the arguments into wrapper objects to make our lives easier OFX::PropertySet inArgs(inArgsRaw); OFX::PropertySet outArgs(outArgsRaw); // turn the action into a std::string std::string action(actionRaw); // figure the actions if (action == kOfxActionLoad) { // call the support load function, param-less OFX::Private::loadAction(); // call the plugin side load action, param-less factory->load(); // got here, must be good stat = kOfxStatOK; } // figure the actions else if (action == kOfxActionUnload) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, true, true, true); // call the plugin side unload action, param-less, should be called, eve if the stat above failed! factory->unload(); // call the support unload function, param-less OFX::Private::unloadAction(plugname); // got here, must be good stat = kOfxStatOK; } else if(action == kOfxActionDescribe) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // make the plugin descriptor ImageEffectDescriptor *desc = new ImageEffectDescriptor(handle); // validate the host OFX::Validation::validatePluginDescriptorProperties(fetchEffectProps(handle)); // and pass it to the plugin to do something with it factory->describe(*desc); // add it to our map gEffectDescriptors[plugname][eContextNone] = desc; // got here, must be good stat = kOfxStatOK; } else if(action == kOfxImageEffectActionDescribeInContext) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true); // make the plugin descriptor and pass it to the plugin to do something with it ImageEffectDescriptor *desc = new ImageEffectDescriptor(handle); // figure the context and map it to an enum std::string contextStr = inArgs.propGetString(kOfxImageEffectPropContext); ContextEnum context = mapToContextEnum(contextStr); // validate the host OFX::Validation::validatePluginDescriptorProperties(fetchEffectProps(handle)); // call plugin describe in context factory->describeInContext(*desc, context); // add it to our map gEffectDescriptors[plugname][context] = desc; // got here, must be good stat = kOfxStatOK; } else if(action == kOfxActionCreateInstance) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch the effect props to figure the context PropertySet effectProps = fetchEffectProps(handle); // get the context and turn it into an enum std::string str = effectProps.propGetString(kOfxImageEffectPropContext); ContextEnum context = mapToContextEnum(str); // make the image effect instance for this context ImageEffect *instance = factory->createInstance(handle, context); (void)instance; // validate the plugin handle's properties OFX::Validation::validatePluginInstanceProperties(fetchEffectProps(handle)); // got here, must be good stat = kOfxStatOK; } else if(action == kOfxActionDestroyInstance) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch our pointer out of the props on the handle ImageEffect *instance = retrieveImageEffectPointer(handle); // kill it delete instance; // got here, must be good stat = kOfxStatOK; } else if(action == kOfxImageEffectActionRender) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true); // call the render action skin renderAction(handle, inArgs); // got here, must be good stat = kOfxStatOK; } else if(action == kOfxImageEffectActionBeginSequenceRender) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true); // call the begin render action skin beginSequenceRenderAction(handle, inArgs); } else if(action == kOfxImageEffectActionEndSequenceRender) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true); // call the begin render action skin endSequenceRenderAction(handle, inArgs); } else if(action == kOfxImageEffectActionIsIdentity) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false); // call the identity action, if it is, return OK if(isIdentityAction(handle, inArgs, outArgs)) stat = kOfxStatOK; } else if(action == kOfxImageEffectActionGetRegionOfDefinition) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false); // call the rod action, return OK if it does something if(regionOfDefinitionAction(handle, inArgs, outArgs)) stat = kOfxStatOK; } else if(action == kOfxImageEffectActionGetRegionsOfInterest) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false); // call the RoI action, return OK if it does something if(regionsOfInterestAction(handle, inArgs, outArgs, plugname)) stat = kOfxStatOK; } else if(action == kOfxImageEffectActionGetFramesNeeded) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false); // call the frames needed action, return OK if it does something if(framesNeededAction(handle, inArgs, outArgs, plugname)) stat = kOfxStatOK; } else if(action == kOfxImageEffectActionGetClipPreferences) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, false); // call the frames needed action, return OK if it does something if(clipPreferencesAction(handle, outArgs, plugname)) stat = kOfxStatOK; } else if(action == kOfxActionPurgeCaches) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch our pointer out of the props on the handle ImageEffect *instance = retrieveImageEffectPointer(handle); // purge 'em instance->purgeCaches(); } else if(action == kOfxActionSyncPrivateData) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch our pointer out of the props on the handle ImageEffect *instance = retrieveImageEffectPointer(handle); // and sync it instance->syncPrivateData(); } else if(action == kOfxImageEffectActionGetTimeDomain) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, false); // call the instance changed action if(getTimeDomainAction(handle, outArgs)) stat = kOfxStatOK; } else if(action == kOfxActionBeginInstanceChanged) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true); // call the instance changed action beginInstanceChangedAction(handle, inArgs); } else if(action == kOfxActionInstanceChanged) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true); // call the instance changed action instanceChangedAction(handle, inArgs); } else if(action == kOfxActionEndInstanceChanged) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true); // call the instance changed action endInstanceChangedAction(handle, inArgs); } else if(action == kOfxActionBeginInstanceEdit) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch our pointer out of the props on the handle ImageEffect *instance = retrieveImageEffectPointer(handle); // call the begin edit function instance->beginEdit(); } else if(action == kOfxActionEndInstanceEdit) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch our pointer out of the props on the handle ImageEffect *instance = retrieveImageEffectPointer(handle); // call the end edit function instance->endEdit(); } #ifdef OFX_SUPPORTS_OPENGLRENDER else if(action == kOfxActionOpenGLContextAttached) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch our pointer out of the props on the handle ImageEffect *instance = retrieveImageEffectPointer(handle); // call the context attached function instance->contextAttached(); } else if(action == kOfxActionOpenGLContextDetached) { checkMainHandles(actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true); // fetch our pointer out of the props on the handle ImageEffect *instance = retrieveImageEffectPointer(handle); // call the context detached function instance->contextDetached(); } #endif else if(actionRaw) { OFX::Log::error(true, "Unknown action '%s'.", actionRaw); } else { OFX::Log::error(true, "Requested action was a null pointer."); } } // catch suite exceptions catch (const OFX::Exception::Suite &ex) { # ifdef DEBUG std::cout << "Caught OFX::Exception::Suite: " << ex.what() << std::endl; # endif stat = ex.status(); } // catch host inadequate exceptions catch (const OFX::Exception::HostInadequate &e) { # ifdef DEBUG std::cout << "Caught OFX::Exception::HostInadequate: " << e.what() << std::endl; # endif stat = kOfxStatErrMissingHostFeature; } // catch exception due to a property being unknown to the host, implies something wrong with host if not caught further down catch (const OFX::Exception::PropertyUnknownToHost &e) { # ifdef DEBUG std::cout << "Caught OFX::Exception::PropertyUnknownToHost: " << e.what() << std::endl; # endif stat = kOfxStatErrMissingHostFeature; } // catch memory catch (std::bad_alloc) { stat = kOfxStatErrMemory; } // catch a custom client exception, if defined #ifdef OFX_CLIENT_EXCEPTION_TYPE catch (OFX_CLIENT_EXCEPTION_TYPE &ex) { stat = OFX_CLIENT_EXCEPTION_HANDLER(ex, plugname); } #endif // Catch anything else, unknown catch (const std::exception &e) { # ifdef DEBUG std::cout << "Caught exception: " << e.what() << std::endl; # endif stat = kOfxStatFailed; } catch (...) { # ifdef DEBUG std::cout << "Caught Unknown exception" << std::endl; # endif stat = kOfxStatFailed; } OFX::Log::outdent(); OFX::Log::print("STOP mainEntry (%s)\n", actionRaw); return stat; } OfxStatus customParamInterpolationV1Entry( const void* handleRaw, OfxPropertySetHandle inArgsRaw, OfxPropertySetHandle outArgsRaw) { OFX::Log::print("********************************************************************************"); OFX::Log::print("START customParamInterpolationV1Entry"); OFX::Log::indent(); OfxStatus stat = kOfxStatReplyDefault; try { // Cast the raw handle to be an image effect handle, because that is what it is OfxImageEffectHandle handle = (OfxImageEffectHandle) handleRaw; // Turn the arguments into wrapper objects to make our lives easier OFX::PropertySet inArgs(inArgsRaw); OFX::PropertySet outArgs(outArgsRaw); ImageEffect *effectInstance = retrieveImageEffectPointer(handle); InterpolateCustomArgs interpArgs; interpArgs.time = inArgs.propGetDouble(kOfxPropTime); interpArgs.value1 = inArgs.propGetString(kOfxParamPropCustomValue, 0); interpArgs.value2 = inArgs.propGetString(kOfxParamPropCustomValue, 1); interpArgs.keytime1 = inArgs.propGetDouble(kOfxParamPropInterpolationTime, 0); interpArgs.keytime2 = inArgs.propGetDouble(kOfxParamPropInterpolationTime, 1); interpArgs.amount = inArgs.propGetDouble(kOfxParamPropInterpolationAmount); std::string paramName = inArgs.propGetString(kOfxPropName); // and call the plugin client code std::string output = effectInstance->interpolateCustomParam(interpArgs, paramName); outArgs.propSetString(kOfxParamPropCustomValue, output); } // catch suite exceptions catch (OFX::Exception::Suite &ex) { # ifdef DEBUG std::cout << "Caught OFX::Exception::Suite" << std::endl; # endif stat = ex.status(); } // catch host inadequate exceptions catch (OFX::Exception::HostInadequate) { # ifdef DEBUG std::cout << "Caught OFX::Exception::HostInadequate" << std::endl; # endif stat = kOfxStatErrMissingHostFeature; } // catch exception due to a property being unknown to the host, implies something wrong with host if not caught further down catch (OFX::Exception::PropertyUnknownToHost) { # ifdef DEBUG std::cout << "Caught OFX::Exception::PropertyUnknownToHost" << std::endl; # endif stat = kOfxStatErrMissingHostFeature; } // catch memory catch (std::bad_alloc) { stat = kOfxStatErrMemory; } // catch a custom client exception, if defined #ifdef OFX_CLIENT_EXCEPTION_TYPE catch (OFX_CLIENT_EXCEPTION_TYPE &ex) { stat = OFX_CLIENT_EXCEPTION_HANDLER(ex, plugname); } #endif // Catch anything else, unknown catch (...) { # ifdef DEBUG std::cout << "Caught Unknown exception" << std::endl; # endif stat = kOfxStatFailed; } OFX::Log::outdent(); OFX::Log::print("STOP customParamInterpolationV1Entry\n"); return stat; } /** @brief The plugin function that gets passed the host structure. */ void setHost(OfxHost *host) { gHost = host; } }; // namespace Private /** @brief Fetch's a suite from the host and logs errors */ const void * fetchSuite(const char *suiteName, int suiteVersion, bool optional) { const void *suite = Private::gHost->fetchSuite(Private::gHost->host, suiteName, suiteVersion); if(suite==0) { if(optional) OFX::Log::warning(suite == 0, "Could not fetch the optional suite '%s' version %d.", suiteName, suiteVersion); else OFX::Log::error(suite == 0, "Could not fetch the mandatory suite '%s' version %d.", suiteName, suiteVersion); } if(!optional && suite == 0) throw OFX::Exception::HostInadequate(suiteName); return suite; } }; // namespace OFX static OFX::OfxPlugInfo generatePlugInfo(OFX::PluginFactory* factory, std::string& newID) { newID = factory->getUID(); std::auto_ptr ofxPlugin(new OfxPlugin()); ofxPlugin->pluginApi = kOfxImageEffectPluginApi; ofxPlugin->apiVersion = 1; ofxPlugin->pluginIdentifier = factory->getID().c_str(); ofxPlugin->pluginVersionMajor = factory->getMajorVersion(); ofxPlugin->pluginVersionMinor = factory->getMinorVersion(); ofxPlugin->setHost = OFX::Private::setHost; ofxPlugin->mainEntry = factory->getMainEntry(); return OFX::OfxPlugInfo(factory, ofxPlugin.release()); } bool gHasInit = false; static void init() { if(gHasInit) return; OFX::Plugin::getPluginIDs(OFX::plugIDs); if(OFX::ofxPlugs.empty()) OFX::ofxPlugs.resize(OFX::plugIDs.size()); int counter = 0; for (OFX::PluginFactoryArray::const_iterator it = OFX::plugIDs.begin(); it != OFX::plugIDs.end(); ++it, ++counter) { std::string newID; OFX::OfxPlugInfo info = generatePlugInfo(*it, newID); OFX::plugInfoMap[newID] = info; OFX::ofxPlugs[counter] = info._plug; } gHasInit = true; } /** @brief, mandated function returning the number of plugins, which is always 1 */ EXPORT int OfxGetNumberOfPlugins(void) { init(); return (int)OFX::plugIDs.size(); } /** @brief, mandated function returning the nth plugin We call the plugin side defined OFX::Plugin::getPluginIDs function to find out what to set. */ EXPORT OfxPlugin* OfxGetPlugin(int nth) { init(); int numPlugs = (int)OFX::plugInfoMap.size(); 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); if(OFX::ofxPlugs[nth] == 0) { std::string newID; OFX::OfxPlugInfo info = generatePlugInfo(OFX::plugIDs[nth], newID); OFX::plugInfoMap[newID] = info; OFX::ofxPlugs[nth] = info._plug; } return OFX::ofxPlugs[nth]; }