/** @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 #include "ofxGPURender.h" #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() = default; OfxPlugInfo(OFX::PluginFactory* f, std::unique_ptr p):_factory(f), _plug(std::move(p)){} OFX::PluginFactory* _factory = nullptr; std::unique_ptr _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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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; } } /** @brief Does the plugin support OpenCL Buffers Render */ void ImageEffectDescriptor::setSupportsOpenCLBuffersRender(bool v) { try { _effectProps.propSetString(kOfxImageEffectPropOpenCLRenderSupported, (v ? "true" : "false")); } catch(OFX::Exception::PropertyUnknownToHost) { OFX::Log::warning(true, "Host does not have kOfxImageEffectPropOpenCLRenderSupported property"); } } /** @brief Does the plugin support OpenCL Images Render */ void ImageEffectDescriptor::setSupportsOpenCLImagesRender(bool v) { try { _effectProps.propSetString(kOfxImageEffectPropOpenCLSupported, (v ? "true" : "false")); } catch(OFX::Exception::PropertyUnknownToHost) { OFX::Log::warning(true, "Host does not have kOfxImageEffectPropOpenCLSupported property"); } } /** @brief Does the plugin support CUDA Render */ void ImageEffectDescriptor::setSupportsCudaRender(bool v) { try { _effectProps.propSetString(kOfxImageEffectPropCudaRenderSupported, (v ? "true" : "false")); } catch(OFX::Exception::PropertyUnknownToHost) { OFX::Log::warning(true, "Host does not have kOfxImageEffectPropCudaRenderSupported property"); } } /** @brief Does the plugin support CUDA Stream */ void ImageEffectDescriptor::setSupportsCudaStream(bool v) { try { _effectProps.propSetString(kOfxImageEffectPropCudaStreamSupported, (v ? "true" : "false")); } catch(OFX::Exception::PropertyUnknownToHost) { OFX::Log::warning(true, "Host does not have kOfxImageEffectPropCudaStreamSupported property"); } } /** @brief Does the plugin support Metal Render */ void ImageEffectDescriptor::setSupportsMetalRender(bool v) { try { _effectProps.propSetString(kOfxImageEffectPropMetalRenderSupported, (v ? "true" : "false")); } catch(OFX::Exception::PropertyUnknownToHost) { OFX::Log::warning(true, "Host does not have kOfxImageEffectPropMetalRenderSupported property"); } } #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, /*throwOnFailure*/false); // not required for OpenCL Images _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 _OpenCLImage = nullptr; _OpenCLImage = _imageProps.propGetPointer(kOfxImageEffectPropOpenCLImage, /*throwOnFailure*/false); // should throw if it is not an image _pixelData = _imageProps.propGetPointer(kOfxImagePropData, /*throwOnFailure*/!_OpenCLImage); } 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; // As of 1.5 gHostDescription.supportsStrChoice = hostProps.propGetInt(kOfxParamHostPropSupportsStrChoice, false) != 0; // As of 1.5 gHostDescription.supportsStrChoiceAnimation = hostProps.propGetInt(kOfxParamHostPropSupportsStrChoiceAnimation, false) != 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.supportsOpenCLRender = hostProps.propGetString(kOfxImageEffectPropOpenCLRenderSupported, 0, false) == "true"; gHostDescription.supportsCudaRender = hostProps.propGetString(kOfxImageEffectPropCudaRenderSupported, 0, false) == "true"; gHostDescription.supportsCudaStream = hostProps.propGetString(kOfxImageEffectPropCudaStreamSupported, 0, false) == "true"; gHostDescription.supportsMetalRender = hostProps.propGetString(kOfxImageEffectPropMetalRenderSupported, 0, false) == "true"; 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); OFX::OfxPluginArray::iterator it2 = std::find(ofxPlugs.begin(), ofxPlugs.end(), it->second._plug.get()); if (it2 != ofxPlugs.end()) { (*it2) = nullptr; } OFX::plugInfoMap.erase(it); } } /** @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); args.isEnabledOpenCLRender = inArgs.propGetInt(kOfxImageEffectPropOpenCLEnabled, false) != 0; args.isEnabledCudaRender = inArgs.propGetInt(kOfxImageEffectPropCudaEnabled, false) != 0; args.isEnabledMetalRender = inArgs.propGetInt(kOfxImageEffectPropMetalEnabled, false) != 0; args.pOpenCLCmdQ = inArgs.propGetPointer(kOfxImageEffectPropOpenCLCommandQueue, false); args.pCudaStream = inArgs.propGetPointer(kOfxImageEffectPropCudaStream, false); args.pMetalCmdQ = inArgs.propGetPointer(kOfxImageEffectPropMetalCommandQueue, false); #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); args.isEnabledOpenCLRender = inArgs.propGetInt(kOfxImageEffectPropOpenCLEnabled, false) != 0; args.isEnabledCudaRender = inArgs.propGetInt(kOfxImageEffectPropCudaEnabled, false) != 0; args.isEnabledMetalRender = inArgs.propGetInt(kOfxImageEffectPropMetalEnabled, false) != 0; args.pOpenCLCmdQ = inArgs.propGetPointer(kOfxImageEffectPropOpenCLCommandQueue, false); args.pCudaStream = inArgs.propGetPointer(kOfxImageEffectPropCudaStream, false); args.pMetalCmdQ = inArgs.propGetPointer(kOfxImageEffectPropMetalCommandQueue, false); #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); args.isEnabledOpenCLRender = inArgs.propGetInt(kOfxImageEffectPropOpenCLEnabled, false) != 0; args.isEnabledCudaRender = inArgs.propGetInt(kOfxImageEffectPropCudaEnabled, false) != 0; args.isEnabledMetalRender = inArgs.propGetInt(kOfxImageEffectPropMetalEnabled, false) != 0; args.pOpenCLCmdQ = inArgs.propGetPointer(kOfxImageEffectPropOpenCLCommandQueue, false); args.pCudaStream = inArgs.propGetPointer(kOfxImageEffectPropCudaStream, false); args.pMetalCmdQ = inArgs.propGetPointer(kOfxImageEffectPropMetalCommandQueue, false); #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 for %s)", actionRaw, plugname); 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) { OFX::Log::error(true, "Caught OFX::Exception::Suite: %s", ex.what()); # 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) { OFX::Log::error(true, "Caught OFX::Exception::HostInadequate: %s", e.what()); # 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) { OFX::Log::error(true, "Caught OFX::Exception::PropertyUnknownToHost: %s", e.what()); # 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) { OFX::Log::error(true, "Caught std::exception: %s", e.what()); # ifdef DEBUG std::cout << "Caught std::exception: " << e.what() << std::endl; # endif stat = kOfxStatFailed; } catch (...) { OFX::Log::error(true, "Caught unknown exception"); # ifdef DEBUG std::cout << "Caught Unknown exception" << std::endl; # endif stat = kOfxStatFailed; } OFX::Log::outdent(); OFX::Log::print("STOP mainEntry (%s for %s, returning %d=%s)\n", actionRaw, plugname, stat, mapStatusToString(stat)); 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::unique_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, std::move(ofxPlugin)); } 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::ofxPlugs[counter] = info._plug.get(); OFX::plugInfoMap[newID] = std::move(info); } 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] == nullptr) { std::string newID; OFX::OfxPlugInfo info = generatePlugInfo(OFX::plugIDs[nth], newID); OFX::ofxPlugs[nth] = info._plug.get(); OFX::plugInfoMap[newID] = std::move(info); } return OFX::ofxPlugs[nth]; }