Files
oak-editor/third_party/openfx/Support/Library/ofxsImageEffect.cpp
T

3079 lines
114 KiB
C++

/** @brief This file contains code that skins the ofx effect suite */
#include "ofxsSupportPrivate.h"
#include <algorithm> // for find
#include <cstring> // for strlen
#ifdef DEBUG
#include <iostream>
#endif
#include <stdexcept>
#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<OfxPlugin> p):_factory(f), _plug(std::move(p)){}
OFX::PluginFactory* _factory = nullptr;
std::unique_ptr<OfxPlugin> _plug;
};
typedef std::map<std::string, OfxPlugInfo> OfxPlugInfoMap;
OfxPlugInfoMap plugInfoMap;
typedef std::vector<OfxPlugin*> 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, &paramSetHandle);
throwSuiteStatusException(stat);
setParamSetHandle(paramSetHandle);
}
/** @brief dtor */
ImageEffectDescriptor::~ImageEffectDescriptor()
{
// delete any clip descriptors we may have constructed
std::map<std::string, ClipDescriptor *>::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<std::string, ClipDescriptor *>::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, &paramSet);
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<std::string, Clip *>::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<std::string, Clip *>::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<OverlayInteract *>::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<OverlayInteract *>::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<OverlayInteract *>::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; i<numComponents; ++i)
gHostDescription._supportedComponents.push_back(mapStrToPixelComponentEnum(hostProps.propGetString(kOfxImageEffectPropSupportedComponents, i)));
int numContexts = hostProps.propGetDimension(kOfxImageEffectPropSupportedContexts);
for(int i=0; i<numContexts; ++i)
gHostDescription._supportedContexts.push_back(mapToContextEnum(hostProps.propGetString(kOfxImageEffectPropSupportedContexts, i)));
int numPixelDepths = hostProps.propGetDimension(kOfxImageEffectPropSupportedPixelDepths);
for(int i=0; i<numPixelDepths; ++i)
gHostDescription._supportedPixelDepths.push_back(mapStrToBitDepthEnum(hostProps.propGetString(kOfxImageEffectPropSupportedPixelDepths, i)));
}
}
/** @brief fetch the effect property set from the ImageEffectHandle */
OFX::PropertySet
fetchEffectProps(OfxImageEffectHandle handle)
{
// get the property handle
OfxPropertySetHandle propHandle;
OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet(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<std::string, std::string>& clipROIPropNames_;
public :
/** @brief ctor */
ActualROISetter(OFX::PropertySet &args, const std::map<std::string, std::string>& 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<std::string, std::string>::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<std::string, std::vector<OfxRangeD> > frameRanges_; /**< @brief map holding a bunch of frame ranges, one for each clip */
const std::map<std::string, std::string>& _clipFrameRangePropNames;
public :
/** @brief ctor */
ActualSetter(OFX::PropertySet &args, const std::map<std::string, std::string>& 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<std::string, std::vector<OfxRangeD> >::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<std::string, std::string>::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<OfxRangeD> &clipRange = i->second;
std::vector<OfxRangeD>::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> 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];
}