This commit resolves several categories of OFX plugin failures that
manifested as magenta (pink) render output or crashes:
1. Param default-value initialization
- IntegerInstance, DoubleInstance, BooleanInstance, ChoiceInstance,
and StringInstance now read kOfxParamPropDefault from the descriptor
at construction time. Previously, when no PluginNode was attached
(integration-test mode), get() returned 0/0.0/false, causing
generator plugins to receive invalid extent/format/PAR values and
crash in coordinate assertions.
- IntegerInstance also fixed uninitialized `id` that caused
kOfxStatErrBadHandle in CImg plugins.
2. Clip property initialization
- newClipInstance() now seeds pixelDepth and components from the
host VideoParams instead of leaving them as None. This prevents
Transform3x3Plugin and similar plugins from asserting on
getPixelComponentCount() during fetchClip inside createInstance.
- getAspectRatio() and getProjectPixelAspectRatio() now fall back
to 1.0 when the project's PAR is not yet set, avoiding division-
by-zero in coordinate conversion.
3. Frame-rate and time-base preservation
- setInputTexture() no longer overwrites the clip's frame_rate or
time_base with the input texture's values. Multi-input plugins
were crashing because setupClipPreferencesArgs throws when inputs
have mismatched rates.
4. Render loop hardening
- getClipPreferences() is now wrapped in try/catch so that frame-
rate mismatch exceptions mark render failure instead of aborting
the render thread.
- getRegionOfInterestAction() treats kOfxStatErrBadHandle as non-
fatal and falls back to default RoI.
- RenderPlugin syncs all clip instances after setVideoParam so that
getAspectRatio/getFrameRate return valid values before
createInstanceAction queries them.
5. Test suite updates
- All PluginMisc tests now use F32 input to match the host pipeline
default.
- CreateGradientTexture fixed to support F32 pixel format.
- Added CImgBilateral and CImgGuided_MultiInput tests.
- Secret parameters are now registered as hidden Node inputs so that
getClipPreferences can read them (fixes generator pink screen).
6. Debug logging in HostSupport
- clipGetImage and clipGetRegionOfDefinition now catch exceptions
and log the failing clip name for easier debugging.
2839 lines
108 KiB
C++
2839 lines
108 KiB
C++
// Copyright OpenFX and contributors to the OpenFX project.
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#include <math.h>
|
|
|
|
// ofx
|
|
#include "ofxCore.h"
|
|
#include "ofxImageEffect.h"
|
|
|
|
// ofx host
|
|
#include "ofxhBinary.h"
|
|
#include "ofxhPropertySuite.h"
|
|
#include "ofxhClip.h"
|
|
#include "ofxhParam.h"
|
|
#include "ofxhMemory.h"
|
|
#include "ofxhImageEffect.h"
|
|
#include "ofxhPluginAPICache.h"
|
|
#include "ofxhPluginCache.h"
|
|
#include "ofxhHost.h"
|
|
#include "ofxhImageEffectAPI.h"
|
|
#include "ofxhUtilities.h"
|
|
#ifdef OFX_SUPPORTS_PARAMETRIC
|
|
#include "ofxhParametricParam.h"
|
|
#endif
|
|
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
|
#include "ofxGPURender.h"
|
|
#endif
|
|
#include "ofxOld.h" // old plugins may rely on deprecated properties being present
|
|
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <iostream>
|
|
|
|
namespace OFX {
|
|
|
|
namespace Host {
|
|
|
|
namespace ImageEffect {
|
|
|
|
/// properties common on an effect and a descriptor
|
|
static const Property::PropSpec effectDescriptorStuff[] = {
|
|
/* name type dim. r/o default value */
|
|
{ kOfxPropType, Property::eString, 1, true, kOfxTypeImageEffect },
|
|
{ kOfxPropLabel, Property::eString, 1, false, "" },
|
|
{ kOfxPropShortLabel, Property::eString, 1, false, "" },
|
|
{ kOfxPropLongLabel, Property::eString, 1, false, "" },
|
|
{ kOfxPropVersion, Property::eInt, 0, false, "0" },
|
|
{ kOfxPropVersionLabel, Property::eString, 1, false, "" },
|
|
{ kOfxPropPluginDescription, Property::eString, 1, false, "" },
|
|
{ kOfxImageEffectPropSupportedContexts, Property::eString, 0, false, "" },
|
|
{ kOfxImageEffectPluginPropGrouping, Property::eString, 1, false, "" },
|
|
{ kOfxImageEffectPluginPropSingleInstance, Property::eInt, 1, false, "0" },
|
|
{ kOfxImageEffectPluginRenderThreadSafety, Property::eString, 1, false, kOfxImageEffectRenderInstanceSafe },
|
|
{ kOfxImageEffectPluginPropHostFrameThreading, Property::eInt, 1, false, "1" },
|
|
{ kOfxImageEffectPluginPropOverlayInteractV1, Property::ePointer, 1, false, NULL },
|
|
{ kOfxImageEffectPropSupportsMultiResolution, Property::eInt, 1, false, "1" } ,
|
|
{ kOfxImageEffectPropSupportsTiles, Property::eInt, 1, false, "1" },
|
|
{ kOfxImageEffectPropTemporalClipAccess, Property::eInt, 1, false, "0" },
|
|
{ kOfxImageEffectPropSupportedPixelDepths, Property::eString, 0, false, "" },
|
|
{ kOfxImageEffectPluginPropFieldRenderTwiceAlways, Property::eInt, 1, false, "1" } ,
|
|
{ kOfxImageEffectPropSupportsMultipleClipDepths, Property::eInt, 1, false, "0" },
|
|
{ kOfxImageEffectPropSupportsMultipleClipPARs, Property::eInt, 1, false, "0" },
|
|
{ kOfxImageEffectPropClipPreferencesSlaveParam, Property::eString, 0, false, "" },
|
|
{ kOfxImageEffectInstancePropSequentialRender, Property::eInt, 1, false, "0" },
|
|
{ kOfxPluginPropFilePath, Property::eString, 1, true, ""},
|
|
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
|
{ kOfxImageEffectPropOpenGLRenderSupported, Property::eString, 1, false, "false"}, // OFX 1.3
|
|
{ kOfxImageEffectPropCudaRenderSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropCudaStreamSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropMetalRenderSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropOpenCLRenderSupported, Property::eString, 1, false, "false" },
|
|
#endif
|
|
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
//
|
|
// Base
|
|
//
|
|
|
|
Base::Base(const Property::Set &set)
|
|
: _properties(set)
|
|
{}
|
|
|
|
Base::Base(const Property::PropSpec * propSpec)
|
|
: _properties(propSpec)
|
|
{}
|
|
|
|
Base::~Base() {}
|
|
|
|
/// obtain a handle on this for passing to the C api
|
|
OfxImageEffectHandle Base::getHandle() const {
|
|
return (OfxImageEffectHandle)this;
|
|
}
|
|
|
|
/// get the properties set
|
|
Property::Set &Base::getProps() {
|
|
return _properties;
|
|
}
|
|
|
|
/// get the properties set, const version
|
|
const Property::Set &Base::getProps() const {
|
|
return _properties;
|
|
}
|
|
|
|
/// name of the clip
|
|
const std::string &Base::getShortLabel() const
|
|
{
|
|
const std::string &s = _properties.getStringProperty(kOfxPropShortLabel);
|
|
if(s == "") {
|
|
const std::string &s2 = _properties.getStringProperty(kOfxPropLabel);
|
|
if(s2 == "") {
|
|
return _properties.getStringProperty(kOfxPropName);
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/// name of the clip
|
|
const std::string &Base::getLabel() const
|
|
{
|
|
const std::string &s = _properties.getStringProperty(kOfxPropLabel);
|
|
if(s == "") {
|
|
return _properties.getStringProperty(kOfxPropName);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/// name of the clip
|
|
const std::string &Base::getLongLabel() const
|
|
{
|
|
const std::string &s = _properties.getStringProperty(kOfxPropLongLabel);
|
|
if(s == "") {
|
|
const std::string &s2 = _properties.getStringProperty(kOfxPropLabel);
|
|
if(s2 == "") {
|
|
return _properties.getStringProperty(kOfxPropName);
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/// is the given context supported
|
|
bool Base::isContextSupported(const std::string &s) const
|
|
{
|
|
return _properties.findStringPropValueIndex(kOfxImageEffectPropSupportedContexts, s) != -1;
|
|
}
|
|
|
|
/// what is the name of the group the plug-in belongs to
|
|
const std::string &Base::getPluginGrouping() const
|
|
{
|
|
return _properties.getStringProperty(kOfxImageEffectPluginPropGrouping);
|
|
}
|
|
|
|
/// is the effect single instance
|
|
bool Base::isSingleInstance() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPluginPropSingleInstance) != 0;
|
|
}
|
|
|
|
/// what is the thread safety on this effect
|
|
const std::string &Base::getRenderThreadSafety() const
|
|
{
|
|
return _properties.getStringProperty(kOfxImageEffectPluginRenderThreadSafety);
|
|
}
|
|
|
|
/// should the host attempt to managed multi-threaded rendering if it can
|
|
/// via tiling or some such
|
|
bool Base::getHostFrameThreading() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPluginPropHostFrameThreading) != 0;
|
|
}
|
|
|
|
/// get the overlay interact main entry if it exists
|
|
OfxPluginEntryPoint *Base::getOverlayInteractMainEntry() const
|
|
{
|
|
return (OfxPluginEntryPoint *)(_properties.getPointerProperty(kOfxImageEffectPluginPropOverlayInteractV1));
|
|
}
|
|
|
|
/// does the effect support images of differing sizes
|
|
bool Base::supportsMultiResolution() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPropSupportsMultiResolution) != 0;
|
|
}
|
|
|
|
/// does the effect support tiled rendering
|
|
bool Base::supportsTiles() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPropSupportsTiles) != 0;
|
|
}
|
|
|
|
/// does this effect need random temporal access
|
|
bool Base::temporalAccess() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPropTemporalClipAccess) != 0;
|
|
}
|
|
|
|
/// is the given RGBA/A pixel depth supported by the effect
|
|
bool Base::isPixelDepthSupported(const std::string &s) const
|
|
{
|
|
return _properties.findStringPropValueIndex(kOfxImageEffectPropSupportedPixelDepths, s) != -1;
|
|
}
|
|
|
|
/// when field rendering, does the effect need to be called
|
|
/// twice to render a frame in all Base::circumstances (with different fields)
|
|
bool Base::fieldRenderTwiceAlways() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPluginPropFieldRenderTwiceAlways) != 0;
|
|
}
|
|
|
|
/// does the effect support multiple clip depths
|
|
bool Base::supportsMultipleClipDepths() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPropSupportsMultipleClipDepths) != 0;
|
|
}
|
|
|
|
/// does the effect support multiple clip pixel aspect ratios
|
|
bool Base::supportsMultipleClipPARs() const
|
|
{
|
|
return _properties.getIntProperty(kOfxImageEffectPropSupportsMultipleClipPARs) != 0;
|
|
}
|
|
|
|
/// does changing the named param re-tigger a clip preferences action
|
|
bool Base::isClipPreferencesSlaveParam(const std::string &s) const
|
|
{
|
|
return _properties.findStringPropValueIndex(kOfxImageEffectPropClipPreferencesSlaveParam, s) != -1;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// descriptor
|
|
|
|
Descriptor::Descriptor(Plugin *plug)
|
|
: Base(effectDescriptorStuff)
|
|
, _plugin(plug)
|
|
{
|
|
_properties.setStringProperty(kOfxPluginPropFilePath, plug->getBinary()->getBundlePath());
|
|
gImageEffectHost->initDescriptor(this);
|
|
}
|
|
|
|
Descriptor::Descriptor(const Descriptor &other, Plugin *plug)
|
|
: Base(other._properties)
|
|
, _plugin(plug)
|
|
{
|
|
_properties.setStringProperty(kOfxPluginPropFilePath, plug->getBinary()->getBundlePath());
|
|
gImageEffectHost->initDescriptor(this);
|
|
}
|
|
|
|
Descriptor::Descriptor(const std::string &bundlePath, Plugin *plug)
|
|
: Base(effectDescriptorStuff)
|
|
, _plugin(plug)
|
|
{
|
|
_properties.setStringProperty(kOfxPluginPropFilePath, bundlePath);
|
|
gImageEffectHost->initDescriptor(this);
|
|
}
|
|
|
|
Descriptor::~Descriptor()
|
|
{
|
|
for(std::map<std::string, ClipDescriptor*>::iterator it = _clips.begin(); it != _clips.end(); ++it)
|
|
delete it->second;
|
|
_clips.clear();
|
|
}
|
|
|
|
|
|
/// create a new clip and add this to the clip map
|
|
ClipDescriptor *Descriptor::defineClip(const std::string &name) {
|
|
ClipDescriptor *c = new ClipDescriptor(name);
|
|
_clips[name] = c;
|
|
_clipsByOrder.push_back(c);
|
|
return c;
|
|
}
|
|
|
|
/// implemented for Param::SetDescriptor
|
|
Property::Set &Descriptor::getParamSetProps()
|
|
{
|
|
return _properties;
|
|
}
|
|
|
|
/// get the interact description, this will also call describe on the interact
|
|
Interact::Descriptor &Descriptor::getOverlayDescriptor(int bitDepthPerComponent, bool hasAlpha)
|
|
{
|
|
if(_overlayDescriptor.getState() == Interact::eUninitialised) {
|
|
// OK, we need to describe it, set the entry point and describe away
|
|
_overlayDescriptor.setEntryPoint(getOverlayInteractMainEntry());
|
|
_overlayDescriptor.describe(bitDepthPerComponent, hasAlpha);
|
|
}
|
|
|
|
return _overlayDescriptor;
|
|
}
|
|
|
|
/// get the clips
|
|
const std::map<std::string, ClipDescriptor*> &Descriptor::getClips() const {
|
|
return _clips;
|
|
}
|
|
|
|
void Descriptor::addClip(const std::string &name, ClipDescriptor *clip) {
|
|
_clips[name] = clip;
|
|
_clipsByOrder.push_back(clip);
|
|
}
|
|
|
|
//
|
|
// Instance
|
|
//
|
|
|
|
static const Property::PropSpec effectInstanceStuff[] = {
|
|
/* name type dim. r/o default value */
|
|
{ kOfxPropType, Property::eString, 1, true, kOfxTypeImageEffectInstance },
|
|
{ kOfxImageEffectPropContext, Property::eString, 1, true, "" },
|
|
{ kOfxPropInstanceData, Property::ePointer, 1, false, NULL },
|
|
{ kOfxImageEffectPropPluginHandle, Property::ePointer, 1, false, NULL },
|
|
{ kOfxImageEffectPropProjectSize, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropProjectOffset, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropProjectExtent, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropProjectPixelAspectRatio, Property::eDouble, 1, true, "0" },
|
|
{ kOfxImageEffectInstancePropEffectDuration, Property::eDouble, 1, true, "0" },
|
|
{ kOfxImageEffectInstancePropSequentialRender, Property::eInt, 1, false, "0" },
|
|
{ kOfxImageEffectPropFrameRate , Property::eDouble, 1, true, "0" },
|
|
{ kOfxPropIsInteractive, Property::eInt, 1, true, "0" },
|
|
# ifdef kOfxImageEffectPropInAnalysis
|
|
{ kOfxImageEffectPropInAnalysis, Property::eInt, 1, false, "0" }, // removed in OFX 1.4
|
|
# endif
|
|
{ kOfxImageEffectPropSupportsTiles, Property::eInt, 1, false, "1" }, // OFX 1.4
|
|
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
|
{ kOfxImageEffectPropOpenGLRenderSupported, Property::eString, 1, false, "false"}, // OFX 1.4
|
|
{ kOfxImageEffectPropCudaRenderSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropCudaStreamSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropMetalRenderSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropOpenCLRenderSupported, Property::eString, 1, false, "false" },
|
|
#endif
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Instance::Instance(ImageEffectPlugin* plugin,
|
|
Descriptor &other,
|
|
const std::string &context,
|
|
bool interactive)
|
|
: Base(effectInstanceStuff)
|
|
, _plugin(plugin)
|
|
, _context(context)
|
|
, _descriptor(&other)
|
|
, _interactive(interactive)
|
|
, _created(false)
|
|
, _clipPrefsDirty(true)
|
|
, _continuousSamples(false)
|
|
, _frameVarying(false)
|
|
, _outputFrameRate(24)
|
|
{
|
|
int i = 0;
|
|
_properties.setChainedSet(&other.getProps());
|
|
|
|
_properties.setPointerProperty(kOfxImageEffectPropPluginHandle, _plugin->getPluginHandle()->getOfxPlugin());
|
|
|
|
_properties.setStringProperty(kOfxImageEffectPropContext,context);
|
|
_properties.setIntProperty(kOfxPropIsInteractive,interactive);
|
|
|
|
// copy is sequential over
|
|
bool sequential = other.getProps().getIntProperty(kOfxImageEffectInstancePropSequentialRender) != 0;
|
|
_properties.setIntProperty(kOfxImageEffectInstancePropSequentialRender,sequential);
|
|
|
|
while(effectInstanceStuff[i].name) {
|
|
|
|
// don't set hooks for context or isinteractive
|
|
|
|
if(strcmp(effectInstanceStuff[i].name,kOfxImageEffectPropContext) &&
|
|
strcmp(effectInstanceStuff[i].name,kOfxPropIsInteractive) &&
|
|
strcmp(effectInstanceStuff[i].name,kOfxImageEffectInstancePropSequentialRender) )
|
|
{
|
|
const Property::PropSpec& spec = effectInstanceStuff[i];
|
|
|
|
switch (spec.type) {
|
|
case Property::eDouble:
|
|
_properties.setGetHook(spec.name, this);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/// implemented for Param::SetDescriptor
|
|
Property::Set &Instance::getParamSetProps()
|
|
{
|
|
return _properties;
|
|
}
|
|
|
|
/// called after construction to populate clips and params
|
|
OfxStatus Instance::populate()
|
|
{
|
|
const std::vector<ClipDescriptor*>& clips = _descriptor->getClipsByOrder();
|
|
|
|
int counter = 0;
|
|
for(std::vector<ClipDescriptor*>::const_iterator it=clips.begin();
|
|
it!=clips.end();
|
|
++it, ++counter) {
|
|
const std::string &name = (*it)->getName();
|
|
// foreach clip descriptor make a clip instance
|
|
ClipInstance* instance = newClipInstance(this, *it, counter);
|
|
if(!instance) return kOfxStatFailed;
|
|
|
|
_clips[name] = instance;
|
|
}
|
|
|
|
const std::list<Param::Descriptor*>& map = _descriptor->getParamList();
|
|
|
|
std::map<std::string,std::vector<Param::Instance*> > parameters;
|
|
std::map<std::string, Param::Instance*> groups;
|
|
|
|
for(std::list<Param::Descriptor*>::const_iterator it=map.begin();
|
|
it!=map.end();
|
|
++it) {
|
|
Param::Descriptor* descriptor = (*it);
|
|
// get the param descriptor
|
|
if(!descriptor) return kOfxStatErrValue;
|
|
|
|
// name of the parameter
|
|
std::string name = descriptor->getName();
|
|
|
|
// get a param instance from a param descriptor
|
|
Param::Instance* instance = newParam(name,*descriptor);
|
|
if(!instance) return kOfxStatFailed;
|
|
|
|
// add the value into the param set instance
|
|
OfxStatus st = addParam(name,instance);
|
|
if(st != kOfxStatOK) return st;
|
|
|
|
std::string parent = instance->getParentName();
|
|
|
|
if(parent!="")
|
|
parameters[parent].push_back(instance);
|
|
|
|
if(instance->getType()==kOfxParamTypeGroup){
|
|
groups[instance->getName()]=instance;
|
|
}
|
|
}
|
|
|
|
// for each group parameter made
|
|
for(std::map<std::string, Param::Instance*>::iterator it=groups.begin();
|
|
it!=groups.end();
|
|
++it) {
|
|
// cast to a group instance
|
|
Param::GroupInstance* group = dynamic_cast<Param::GroupInstance*>(it->second);
|
|
|
|
// if cast ok
|
|
if(group){
|
|
// find the parameters whose parent was this group
|
|
std::map<std::string,std::vector<Param::Instance*> >::iterator it2 = parameters.find(group->getName());
|
|
if(it2!=parameters.end()){
|
|
// associate the group with its children, and the children with its parent group
|
|
group->setChildren(it2->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
// do nothing
|
|
int Instance::getDimension(const std::string &name) const {
|
|
printf("failing in %s with name=%s\n", __PRETTY_FUNCTION__, name.c_str());
|
|
throw Property::Exception(kOfxStatErrMissingHostFeature);
|
|
}
|
|
|
|
int Instance::upperGetDimension(const std::string &name) {
|
|
return _properties.getDimension(name);
|
|
}
|
|
|
|
void Instance::notify(const std::string &/*name*/, bool /*singleValue*/, int /*indexOrN*/)
|
|
{
|
|
printf("failing in %s\n", __PRETTY_FUNCTION__);
|
|
}
|
|
|
|
// don't know what to do
|
|
void Instance::reset(const std::string &/*name*/) {
|
|
printf("failing in %s\n", __PRETTY_FUNCTION__);
|
|
throw Property::Exception(kOfxStatErrMissingHostFeature);
|
|
}
|
|
|
|
// get the virutals for viewport size, pixel scale, background colour
|
|
double Instance::getDoubleProperty(const std::string &name, int index) const
|
|
{
|
|
if(name==kOfxImageEffectPropProjectSize){
|
|
if(index>=2) throw Property::Exception(kOfxStatErrBadIndex);
|
|
double values[2];
|
|
getProjectSize(values[0],values[1]);
|
|
return values[index];
|
|
}
|
|
else if(name==kOfxImageEffectPropProjectOffset){
|
|
if(index>=2) throw Property::Exception(kOfxStatErrBadIndex);
|
|
double values[2];
|
|
getProjectOffset(values[0],values[1]);
|
|
return values[index];
|
|
}
|
|
else if(name==kOfxImageEffectPropProjectExtent){
|
|
if(index>=2) throw Property::Exception(kOfxStatErrBadIndex);
|
|
double values[2];
|
|
getProjectExtent(values[0],values[1]);
|
|
return values[index];
|
|
}
|
|
else if(name==kOfxImageEffectPropProjectPixelAspectRatio){
|
|
if(index>=1) throw Property::Exception(kOfxStatErrBadIndex);
|
|
return getProjectPixelAspectRatio();
|
|
}
|
|
else if(name==kOfxImageEffectInstancePropEffectDuration){
|
|
if(index>=1) throw Property::Exception(kOfxStatErrBadIndex);
|
|
return getEffectDuration();
|
|
}
|
|
else if(name==kOfxImageEffectPropFrameRate){
|
|
if(index>=1) throw Property::Exception(kOfxStatErrBadIndex);
|
|
return getFrameRate();
|
|
}
|
|
else
|
|
throw Property::Exception(kOfxStatErrUnknown);
|
|
}
|
|
|
|
void Instance::getDoublePropertyN(const std::string &name, double* first, int n) const
|
|
{
|
|
if(name==kOfxImageEffectPropProjectSize){
|
|
if(n>2) throw Property::Exception(kOfxStatErrBadIndex);
|
|
getProjectSize(first[0],first[1]);
|
|
}
|
|
else if(name==kOfxImageEffectPropProjectOffset){
|
|
if(n>2) throw Property::Exception(kOfxStatErrBadIndex);
|
|
getProjectOffset(first[0],first[1]);
|
|
}
|
|
else if(name==kOfxImageEffectPropProjectExtent){
|
|
if(n>2) throw Property::Exception(kOfxStatErrBadIndex);
|
|
getProjectExtent(first[0],first[1]);
|
|
}
|
|
else if(name==kOfxImageEffectPropProjectPixelAspectRatio){
|
|
if(n>1) throw Property::Exception(kOfxStatErrBadIndex);
|
|
*first = getProjectPixelAspectRatio();
|
|
}
|
|
else if(name==kOfxImageEffectInstancePropEffectDuration){
|
|
if(n>1) throw Property::Exception(kOfxStatErrBadIndex);
|
|
*first = getEffectDuration();
|
|
}
|
|
else if(name==kOfxImageEffectPropFrameRate){
|
|
if(n>1) throw Property::Exception(kOfxStatErrBadIndex);
|
|
*first = getFrameRate();
|
|
}
|
|
else
|
|
throw Property::Exception(kOfxStatErrUnknown);
|
|
}
|
|
|
|
Instance::~Instance(){
|
|
// destroy the instance, only if succesfully created
|
|
if (_created) {
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionDestroyInstance<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxActionDestroyInstance,this->getHandle(),0,0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionDestroyInstance<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
(void)st;
|
|
}
|
|
|
|
/// clobber my clips
|
|
std::map<std::string, ClipInstance*>::iterator i;
|
|
for(i = _clips.begin(); i != _clips.end(); ++i) {
|
|
if(i->second)
|
|
delete i->second;
|
|
i->second = NULL;
|
|
}
|
|
}
|
|
|
|
/// this is used to populate with any extra action in argumnents that may be needed
|
|
void Instance::setCustomInArgs(const std::string &/*action*/, Property::Set &/*inArgs*/)
|
|
{
|
|
}
|
|
|
|
/// this is used to populate with any extra action out argumnents that may be needed
|
|
void Instance::setCustomOutArgs(const std::string &/*action*/, Property::Set &/*outArgs*/)
|
|
{
|
|
}
|
|
|
|
/// this is used to populate with any extra action out argumnents that may be needed
|
|
void Instance::examineOutArgs(const std::string &/*action*/, OfxStatus, const Property::Set &/*outArgs*/)
|
|
{
|
|
}
|
|
|
|
/// check for connection
|
|
bool Instance::checkClipConnectionStatus() const
|
|
{
|
|
std::map<std::string, ClipInstance*>::const_iterator i;
|
|
for(i = _clips.begin(); i != _clips.end(); ++i) {
|
|
if(!i->second->isOptional() && !i->second->getConnected()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// override this to make processing abort, return 1 to abort processing
|
|
int Instance::abort() {
|
|
return 0;
|
|
}
|
|
|
|
// override this to use your own memory instance - must inherrit from memory::instance
|
|
Memory::Instance* Instance::newMemoryInstance(size_t /*nBytes*/) {
|
|
return 0;
|
|
}
|
|
|
|
// return an memory::instance calls makeMemoryInstance that can be overriden
|
|
Memory::Instance* Instance::imageMemoryAlloc(size_t nBytes){
|
|
Memory::Instance* instance = newMemoryInstance(nBytes);
|
|
if(instance)
|
|
return instance;
|
|
else{
|
|
Memory::Instance* instance = new Memory::Instance;
|
|
instance->alloc(nBytes);
|
|
return instance;
|
|
}
|
|
}
|
|
|
|
// call the effect entry point
|
|
OfxStatus Instance::mainEntry(const char *action,
|
|
const void *handle,
|
|
Property::Set *inArgs,
|
|
Property::Set *outArgs)
|
|
{
|
|
if(_plugin){
|
|
PluginHandle* pHandle = _plugin->getPluginHandle();
|
|
if(pHandle){
|
|
OfxPlugin* ofxPlugin = pHandle->getOfxPlugin();
|
|
if(ofxPlugin){
|
|
|
|
OfxPropertySetHandle inHandle = 0;
|
|
if(inArgs) {
|
|
setCustomInArgs(action, *inArgs);
|
|
inHandle = inArgs->getHandle();
|
|
}
|
|
|
|
OfxPropertySetHandle outHandle = 0;
|
|
if(outArgs) {
|
|
setCustomOutArgs(action, *outArgs);
|
|
outHandle = outArgs->getHandle();
|
|
}
|
|
|
|
OfxStatus stat;
|
|
try {
|
|
stat = ofxPlugin->mainEntry(action, handle, inHandle, outHandle);
|
|
} CatchAllSetStatus(stat, gImageEffectHost, ofxPlugin, action);
|
|
|
|
if(outArgs)
|
|
examineOutArgs(action, stat, *outArgs);
|
|
|
|
return stat;
|
|
}
|
|
return kOfxStatFailed;
|
|
}
|
|
return kOfxStatFailed;
|
|
}
|
|
return kOfxStatFailed;
|
|
}
|
|
|
|
// get the nth clip, in order of declaration
|
|
ClipInstance* Instance::getNthClip(int index)
|
|
{
|
|
const std::string name = _descriptor->getClipsByOrder()[index]->getName();
|
|
return _clips[name];
|
|
}
|
|
|
|
ClipInstance* Instance::getClip(const std::string& name) const {
|
|
std::map<std::string,ClipInstance*>::const_iterator it = _clips.find(name);
|
|
if(it!=_clips.end()){
|
|
return it->second;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// create a clip instance
|
|
OfxStatus Instance::createInstanceAction()
|
|
{
|
|
/// we need to init the clips before we call create instance incase
|
|
/// they try and fetch something in create instance, which they are allowed
|
|
setDefaultClipPreferences();
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionCreateInstance<<"()"<<std::endl;
|
|
# endif
|
|
// now tell the plug-in to create instance
|
|
OfxStatus st = mainEntry(kOfxActionCreateInstance,this->getHandle(),0,0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionCreateInstance<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
|
|
if (st == kOfxStatOK) {
|
|
_created = true;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
// begin/change/end instance changed
|
|
OfxStatus Instance::beginInstanceChangedAction(const std::string & why)
|
|
{
|
|
Property::PropSpec stuff[] = {
|
|
{ kOfxPropChangeReason, Property::eString, 1, true, why.c_str() },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(stuff);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionBeginInstanceChanged<<"("<<why<<")"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxActionBeginInstanceChanged,this->getHandle(), &inArgs, 0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionBeginInstanceChanged<<"("<<why<<")->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
OfxStatus Instance::paramInstanceChangedAction(const std::string & paramName,
|
|
const std::string & why,
|
|
OfxTime time,
|
|
OfxPointD renderScale)
|
|
{
|
|
Param::Instance* param = getParam(paramName);
|
|
|
|
if(isClipPreferencesSlaveParam(paramName))
|
|
_clipPrefsDirty = true;
|
|
|
|
if (!param) {
|
|
return kOfxStatFailed;
|
|
}
|
|
|
|
Property::PropSpec stuff[] = {
|
|
{ kOfxPropType, Property::eString, 1, true, kOfxTypeParameter },
|
|
{ kOfxPropName, Property::eString, 1, true, paramName.c_str() },
|
|
{ kOfxPropChangeReason, Property::eString, 1, true, why.c_str() },
|
|
{ kOfxPropTime, Property::eDouble, 1, true, "0" },
|
|
{ kOfxImageEffectPropRenderScale, Property::eDouble, 2, true, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(stuff);
|
|
|
|
// add the second dimension of the render scale
|
|
inArgs.setDoubleProperty(kOfxPropTime,time);
|
|
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRenderScale, &renderScale.x, 2);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionInstanceChanged<<"("<<kOfxTypeParameter<<","<<paramName<<","<<why<<","<<time<<",("<<renderScale.x<<","<<renderScale.y<<"))"<<std::endl;
|
|
# endif
|
|
|
|
OfxStatus st = mainEntry(kOfxActionInstanceChanged,this->getHandle(), &inArgs, 0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionInstanceChanged<<"("<<kOfxTypeParameter<<","<<paramName<<","<<why<<","<<time<<",("<<renderScale.x<<","<<renderScale.y<<"))->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
OfxStatus Instance::clipInstanceChangedAction(const std::string & clipName,
|
|
const std::string & why,
|
|
OfxTime time,
|
|
OfxPointD renderScale)
|
|
{
|
|
_clipPrefsDirty = true;
|
|
std::map<std::string,ClipInstance*>::iterator it=_clips.find(clipName);
|
|
if(it!=_clips.end())
|
|
return (it->second)->instanceChangedAction(why,time,renderScale);
|
|
else
|
|
return kOfxStatFailed;
|
|
}
|
|
|
|
OfxStatus Instance::endInstanceChangedAction(const std::string & why)
|
|
{
|
|
Property::PropSpec whyStuff[] = {
|
|
{ kOfxPropChangeReason, Property::eString, 1, true, why.c_str() },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(whyStuff);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionEndInstanceChanged<<"("<<why<<")"<<std::endl;
|
|
# endif
|
|
|
|
OfxStatus st = mainEntry(kOfxActionEndInstanceChanged,this->getHandle(), &inArgs, 0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionEndInstanceChanged<<"("<<why<<")->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
// purge your caches
|
|
OfxStatus Instance::purgeCachesAction(){
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionPurgeCaches<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxActionPurgeCaches ,this->getHandle(),0,0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionPurgeCaches<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
// sync your private data
|
|
OfxStatus Instance::syncPrivateDataAction(){
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionSyncPrivateData<<"()"<<std::endl;
|
|
# endif
|
|
// Only call kOfxActionSyncPrivateData if kOfxPropParamSetNeedsSyncing is not set,
|
|
// or if it is set to 1.
|
|
// see http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxPropParamSetNeedsSyncing
|
|
Property::Int* s = _properties.fetchIntProperty(kOfxPropParamSetNeedsSyncing);
|
|
bool needsSyncing = s ? s->getValue() : true;
|
|
OfxStatus st = kOfxStatReplyDefault;
|
|
if (needsSyncing) {
|
|
st = mainEntry(kOfxActionSyncPrivateData,this->getHandle(),0,0);
|
|
if (s) {
|
|
s->setValue(0);
|
|
}
|
|
}
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionSyncPrivateData<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
// begin/end edit instance
|
|
OfxStatus Instance::beginInstanceEditAction(){
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionBeginInstanceEdit<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxActionBeginInstanceEdit,this->getHandle(),0,0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionBeginInstanceEdit<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
OfxStatus Instance::endInstanceEditAction(){
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionEndInstanceEdit<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxActionEndInstanceEdit,this->getHandle(),0,0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionEndInstanceEdit<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
# ifdef OFX_SUPPORTS_OPENGLRENDER
|
|
// attach/detach OpenGL context
|
|
OfxStatus Instance::contextAttachedAction(){
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionOpenGLContextAttached<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxActionOpenGLContextAttached,this->getHandle(),0,0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionOpenGLContextAttached<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
OfxStatus Instance::contextDetachedAction(){
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionOpenGLContextDetached<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxActionOpenGLContextDetached,this->getHandle(),0,0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxActionOpenGLContextDetached<<"()->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
# endif
|
|
|
|
OfxStatus Instance::beginRenderAction(OfxTime startFrame,
|
|
OfxTime endFrame,
|
|
OfxTime step,
|
|
bool interactive,
|
|
OfxPointD renderScale,
|
|
bool sequentialRender,
|
|
bool interactiveRender
|
|
)
|
|
{
|
|
Property::PropSpec stuff[] = {
|
|
{ kOfxImageEffectPropFrameRange, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropFrameStep, Property::eDouble, 1, true, "0" },
|
|
{ kOfxPropIsInteractive, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropRenderScale, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropSequentialRenderStatus, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropInteractiveRenderStatus, Property::eInt, 1, true, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(stuff);
|
|
|
|
// set up second dimension for frame range and render scale
|
|
inArgs.setDoubleProperty(kOfxImageEffectPropFrameRange,startFrame, 0);
|
|
inArgs.setDoubleProperty(kOfxImageEffectPropFrameRange,endFrame, 1);
|
|
|
|
inArgs.setDoubleProperty(kOfxImageEffectPropFrameStep,step);
|
|
|
|
inArgs.setIntProperty(kOfxPropIsInteractive,interactive);
|
|
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRenderScale, &renderScale.x, 2);
|
|
|
|
inArgs.setIntProperty(kOfxImageEffectPropSequentialRenderStatus,sequentialRender);
|
|
inArgs.setIntProperty(kOfxImageEffectPropInteractiveRenderStatus,interactiveRender);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionBeginSequenceRender<<"(("<<startFrame<<","<<endFrame<<"),"<<step<<","<<interactive<<",("<<renderScale.x<<","<<renderScale.y<<"),"<<sequentialRender<<","<<interactiveRender
|
|
<<")"<<std::endl;
|
|
# endif
|
|
|
|
OfxStatus st = mainEntry(kOfxImageEffectActionBeginSequenceRender, this->getHandle(), &inArgs, 0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionBeginSequenceRender<<"(("<<startFrame<<","<<endFrame<<"),"<<step<<","<<interactive<<",("<<renderScale.x<<","<<renderScale.y<<"),"<<sequentialRender<<","<<interactiveRender
|
|
<<")->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
OfxStatus Instance::renderAction(OfxTime time,
|
|
const std::string & field,
|
|
const OfxRectI &renderRoI,
|
|
OfxPointD renderScale,
|
|
bool sequentialRender,
|
|
bool interactiveRender,
|
|
bool draftRender
|
|
)
|
|
{
|
|
static const Property::PropSpec inStuff[] = {
|
|
{ kOfxPropTime, Property::eDouble, 1, true, "0" },
|
|
{ kOfxImageEffectPropFieldToRender, Property::eString, 1, true, "" },
|
|
{ kOfxImageEffectPropRenderWindow, Property::eInt, 4, true, "0" },
|
|
{ kOfxImageEffectPropRenderScale, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropSequentialRenderStatus, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropInteractiveRenderStatus, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropRenderQualityDraft, Property::eInt, 1, true, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(inStuff);
|
|
|
|
inArgs.setStringProperty(kOfxImageEffectPropFieldToRender,field);
|
|
inArgs.setDoubleProperty(kOfxPropTime,time);
|
|
inArgs.setIntPropertyN(kOfxImageEffectPropRenderWindow, &renderRoI.x1, 4);
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRenderScale, &renderScale.x, 2);
|
|
inArgs.setIntProperty(kOfxImageEffectPropSequentialRenderStatus,sequentialRender);
|
|
inArgs.setIntProperty(kOfxImageEffectPropInteractiveRenderStatus,interactiveRender);
|
|
inArgs.setIntProperty(kOfxImageEffectPropRenderQualityDraft,draftRender);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionRender<<"("<<time<<","<<field<<",("<<renderRoI.x1<<","<<renderRoI.y1<<","<<renderRoI.x2<<","<<renderRoI.y2<<"),("<<renderScale.x<<","<<renderScale.y<<"),"<<sequentialRender<<","<<interactiveRender
|
|
<<")"<<std::endl;
|
|
# endif
|
|
|
|
OfxStatus st = mainEntry(kOfxImageEffectActionRender,this->getHandle(), &inArgs, 0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionRender<<"("<<time<<","<<field<<",("<<renderRoI.x1<<","<<renderRoI.y1<<","<<renderRoI.x2<<","<<renderRoI.y2<<"),("<<renderScale.x<<","<<renderScale.y<<"),"<<sequentialRender<<","<<interactiveRender
|
|
<<")->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
OfxStatus Instance::endRenderAction(OfxTime startFrame,
|
|
OfxTime endFrame,
|
|
OfxTime step,
|
|
bool interactive,
|
|
OfxPointD renderScale,
|
|
bool sequentialRender,
|
|
bool interactiveRender
|
|
)
|
|
{
|
|
static const Property::PropSpec inStuff[] = {
|
|
{ kOfxImageEffectPropFrameRange, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropFrameStep, Property::eDouble, 1, true, "0" },
|
|
{ kOfxPropIsInteractive, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropRenderScale, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropSequentialRenderStatus, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropInteractiveRenderStatus, Property::eInt, 1, true, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(inStuff);
|
|
|
|
inArgs.setDoubleProperty(kOfxImageEffectPropFrameStep,step);
|
|
|
|
inArgs.setDoubleProperty(kOfxImageEffectPropFrameRange,startFrame, 0);
|
|
inArgs.setDoubleProperty(kOfxImageEffectPropFrameRange,endFrame, 1);
|
|
inArgs.setIntProperty(kOfxPropIsInteractive,interactive);
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRenderScale, &renderScale.x, 2);
|
|
inArgs.setIntProperty(kOfxImageEffectPropSequentialRenderStatus,sequentialRender);
|
|
inArgs.setIntProperty(kOfxImageEffectPropInteractiveRenderStatus,interactiveRender);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionEndSequenceRender<<"(("<<startFrame<<","<<endFrame<<"),"<<step<<","<<interactive<<",("<<renderScale.x<<","<<renderScale.y<<"),"<<sequentialRender<<","<<interactiveRender
|
|
<<")"<<std::endl;
|
|
# endif
|
|
|
|
OfxStatus st = mainEntry(kOfxImageEffectActionEndSequenceRender,this->getHandle(), &inArgs, 0);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionEndSequenceRender<<"(("<<startFrame<<","<<endFrame<<"),"<<step<<","<<interactive<<",("<<renderScale.x<<","<<renderScale.y<<"),"<<sequentialRender<<","<<interactiveRender
|
|
<<")->"<<StatStr(st)<<std::endl;
|
|
# endif
|
|
return st;
|
|
}
|
|
|
|
/// calculate the default rod for this effect instance
|
|
OfxRectD Instance::calcDefaultRegionOfDefinition(OfxTime time,
|
|
OfxPointD /*renderScale*/) const
|
|
{
|
|
OfxRectD rod;
|
|
|
|
// figure out the default contexts
|
|
if(
|
|
_context == kOfxImageEffectContextGenerator) {
|
|
// generator is the extent
|
|
rod.x1 = rod.y1 = 0;
|
|
getProjectExtent(rod.x2, rod.y2);
|
|
}
|
|
else if(_context == kOfxImageEffectContextFilter ||
|
|
_context == kOfxImageEffectContextPaint) {
|
|
// filter and paint default to the input clip
|
|
ClipInstance *clip = getClip(kOfxImageEffectSimpleSourceClipName);
|
|
if(clip) {
|
|
rod = clip->getRegionOfDefinition(time);
|
|
} else {
|
|
throw Property::Exception(kOfxStatFailed);
|
|
}
|
|
}
|
|
else if(_context == kOfxImageEffectContextTransition) {
|
|
// transition is the union of the two clips
|
|
ClipInstance *clipFrom = getClip(kOfxImageEffectTransitionSourceFromClipName);
|
|
ClipInstance *clipTo = getClip(kOfxImageEffectTransitionSourceToClipName);
|
|
if(clipFrom && clipTo) {
|
|
rod = clipFrom->getRegionOfDefinition(time);
|
|
rod = Union(rod, clipTo->getRegionOfDefinition(time));
|
|
} else {
|
|
throw Property::Exception(kOfxStatFailed);
|
|
}
|
|
}
|
|
else if(_context == kOfxImageEffectContextGeneral
|
|
) {
|
|
// general context is the union of all the non optional clips
|
|
bool gotOne = false;
|
|
for(std::map<std::string, ClipInstance*>::const_iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
ClipInstance *clip = it->second;
|
|
if(!clip->isOutput() && (!clip->isOptional() || (clip->getConnected() && clip->getName() == kOfxImageEffectSimpleSourceClipName))) {
|
|
if(!gotOne)
|
|
rod = clip->getRegionOfDefinition(time);
|
|
else
|
|
rod = Union(rod, clip->getRegionOfDefinition(time));
|
|
gotOne = true;
|
|
}
|
|
}
|
|
|
|
if(!gotOne) {
|
|
/// no non optionals? then be the extent
|
|
rod.x1 = rod.y1 = 0;
|
|
getProjectExtent(rod.x2, rod.y2);
|
|
}
|
|
|
|
}
|
|
else if(_context == kOfxImageEffectContextRetimer) {
|
|
// retimer
|
|
ClipInstance *clip = getClip(kOfxImageEffectSimpleSourceClipName);
|
|
if(clip) {
|
|
Param::DoubleInstance *param = dynamic_cast<Param::DoubleInstance *>(getParam(kOfxImageEffectRetimerParamName));
|
|
if(param) {
|
|
rod = clip->getRegionOfDefinition(floor(time));
|
|
rod = Union(rod, clip->getRegionOfDefinition(floor(time) + 1));
|
|
} else {
|
|
throw Property::Exception(kOfxStatFailed);
|
|
}
|
|
} else {
|
|
throw Property::Exception(kOfxStatFailed);
|
|
}
|
|
}
|
|
else {
|
|
// unknown context
|
|
throw Property::Exception(kOfxStatErrMissingHostFeature);
|
|
}
|
|
|
|
return rod;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// RoD call
|
|
OfxStatus Instance::getRegionOfDefinitionAction(OfxTime time,
|
|
OfxPointD renderScale,
|
|
OfxRectD &rod)
|
|
{
|
|
static const Property::PropSpec inStuff[] = {
|
|
{ kOfxPropTime, Property::eDouble, 1, true, "0" },
|
|
{ kOfxImageEffectPropRenderScale, Property::eDouble, 2, true, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
static const Property::PropSpec outStuff[] = {
|
|
{ kOfxImageEffectPropRegionOfDefinition , Property::eDouble, 4, false, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(inStuff);
|
|
Property::Set outArgs(outStuff);
|
|
|
|
inArgs.setDoubleProperty(kOfxPropTime,time);
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRenderScale, &renderScale.x, 2);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetRegionOfDefinition<<"("<<time<<",("<<renderScale.x<<","<<renderScale.y<<"))"<<std::endl;
|
|
# endif
|
|
OfxStatus stat = mainEntry(kOfxImageEffectActionGetRegionOfDefinition,
|
|
this->getHandle(),
|
|
&inArgs,
|
|
&outArgs);
|
|
if(stat == kOfxStatOK) {
|
|
outArgs.getDoublePropertyN(kOfxImageEffectPropRegionOfDefinition, &rod.x1, 4);
|
|
}
|
|
else if(stat == kOfxStatReplyDefault) {
|
|
rod = calcDefaultRegionOfDefinition(time, renderScale);
|
|
}
|
|
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetRegionOfDefinition<<"("<<time<<",("<<renderScale.x<<","<<renderScale.y<<"))->"<<StatStr(stat);
|
|
if(stat == kOfxStatOK) {
|
|
std::cout << ": ("<<rod.x1<<","<<rod.y1<<","<<rod.x2<<","<<rod.y2<<")";
|
|
}
|
|
std::cout << std::endl;
|
|
# endif
|
|
|
|
return stat;
|
|
}
|
|
|
|
/// get the region of interest for each input and return it in the given std::map
|
|
OfxStatus Instance::getRegionOfInterestAction(OfxTime time,
|
|
OfxPointD renderScale,
|
|
const OfxRectD &roi,
|
|
std::map<ClipInstance *, OfxRectD>& rois)
|
|
{
|
|
OfxStatus stat = kOfxStatReplyDefault;
|
|
|
|
// reset the map
|
|
rois.clear();
|
|
|
|
if(!supportsTiles()) {
|
|
/// No tiling support on the effect at all. So set the roi of each input clip to be the RoD of that clip.
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
if(!it->second->isOutput() ||
|
|
getContext() == kOfxImageEffectContextGenerator) {
|
|
if (it->second->isOutput() || it->second->getConnected()) {// needed to be able to fetch the RoD
|
|
/// @todo tuttle: how to support size on generators... check if this is correct in all cases.
|
|
OfxRectD roi = it->second->getRegionOfDefinition(time);
|
|
rois[it->second] = roi;
|
|
}
|
|
}
|
|
}
|
|
stat = kOfxStatOK;
|
|
}
|
|
else {
|
|
/// set up the in args
|
|
static const Property::PropSpec inStuff[] = {
|
|
{ kOfxPropTime, Property::eDouble, 1, true, "0" },
|
|
{ kOfxImageEffectPropRenderScale, Property::eDouble, 2, true, "0" },
|
|
{ kOfxImageEffectPropRegionOfInterest , Property::eDouble, 4, true, 0 },
|
|
Property::propSpecEnd
|
|
};
|
|
Property::Set inArgs(inStuff);
|
|
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRenderScale, &renderScale.x, 2);
|
|
inArgs.setDoubleProperty(kOfxPropTime,time);
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRegionOfInterest, &roi.x1, 4);
|
|
|
|
Property::Set outArgs;
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
if(!it->second->isOutput() ||
|
|
getContext() == kOfxImageEffectContextGenerator) {
|
|
Property::PropSpec s;
|
|
std::string name = "OfxImageClipPropRoI_"+it->first;
|
|
|
|
s.name = name.c_str();
|
|
s.type = Property::eDouble;
|
|
s.dimension = 4;
|
|
s.readonly = false;
|
|
s.defaultValue = "";
|
|
outArgs.createProperty(s);
|
|
|
|
/// initialise to the default
|
|
outArgs.setDoublePropertyN(s.name, &roi.x1, 4);
|
|
}
|
|
}
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetRegionsOfInterest<<"("<<time<<",("<<renderScale.x<<","<<renderScale.y<<"),("<<roi.x1<<","<<roi.y1<<","<<roi.x2<<","<<roi.y2<<"))"<<std::endl;
|
|
# endif
|
|
/// call the action
|
|
stat = mainEntry(kOfxImageEffectActionGetRegionsOfInterest,
|
|
this->getHandle(),
|
|
&inArgs,
|
|
&outArgs);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetRegionsOfInterest<<"("<<time<<",("<<renderScale.x<<","<<renderScale.y<<"),("<<roi.x1<<","<<roi.y1<<","<<roi.x2<<","<<roi.y2<<"))->"<<StatStr(stat);
|
|
if (stat == kOfxStatOK) {
|
|
std::cout << ": ";
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
std::string name = "OfxImageClipPropRoI_"+it->first;
|
|
OfxRectD thisRoi;
|
|
thisRoi.x1 = outArgs.getDoubleProperty(name,0);
|
|
thisRoi.y1 = outArgs.getDoubleProperty(name,1);
|
|
thisRoi.x2 = outArgs.getDoubleProperty(name,2);
|
|
thisRoi.y2 = outArgs.getDoubleProperty(name,3);
|
|
std::cout << it->first << "->("<<thisRoi.x1<<","<<thisRoi.y1<<","<<thisRoi.x2<<","<<thisRoi.y2<<") ";
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
# endif
|
|
/// set the thing up
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
if(!it->second->isOutput() ||
|
|
getContext() == kOfxImageEffectContextGenerator) {
|
|
if (it->second->isOutput() || it->second->getConnected()) { // needed to be able to fetch the RoD
|
|
|
|
if(it->second->supportsTiles()) {
|
|
std::string name = "OfxImageClipPropRoI_"+it->first;
|
|
OfxRectD thisRoi;
|
|
thisRoi.x1 = outArgs.getDoubleProperty(name,0);
|
|
thisRoi.y1 = outArgs.getDoubleProperty(name,1);
|
|
thisRoi.x2 = outArgs.getDoubleProperty(name,2);
|
|
thisRoi.y2 = outArgs.getDoubleProperty(name,3);
|
|
|
|
// and DON'T clamp it to the clip's rod
|
|
// We cannot clip it against the RoD because the RoI may be used for frames
|
|
// at different a time or view than the current time and view passed to this action
|
|
// which would result in a wrong clipping. Unfortunately only the implementation of
|
|
// the host can do the correct clipping.
|
|
//thisRoi = Clamp(thisRoi, rod);
|
|
rois[it->second] = thisRoi;
|
|
}
|
|
else {
|
|
/// not supporting tiles on this input, so set it to the rod
|
|
OfxRectD rod = it->second->getRegionOfDefinition(time
|
|
);
|
|
rois[it->second] = rod;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return stat;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// see how many frames are needed from each clip to render the indicated frame
|
|
OfxStatus Instance::getFrameNeededAction(OfxTime time,
|
|
RangeMap &rangeMap)
|
|
{
|
|
OfxStatus stat = kOfxStatReplyDefault;
|
|
Property::Set outArgs;
|
|
|
|
if(temporalAccess()) {
|
|
static const Property::PropSpec inStuff[] = {
|
|
{ kOfxPropTime, Property::eDouble, 1, true, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
Property::Set inArgs(inStuff);
|
|
inArgs.setDoubleProperty(kOfxPropTime,time);
|
|
|
|
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
if(!it->second->isOutput()) {
|
|
Property::PropSpec s;
|
|
std::string name = "OfxImageClipPropFrameRange_"+it->first;
|
|
|
|
s.name = name.c_str();
|
|
s.type = Property::eDouble;
|
|
s.dimension = 0;
|
|
s.readonly = false;
|
|
s.defaultValue = "";
|
|
outArgs.createProperty(s);
|
|
/// intialise it to the current frame
|
|
outArgs.setDoubleProperty(name, time, 0);
|
|
outArgs.setDoubleProperty(name, time, 1);
|
|
}
|
|
}
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetFramesNeeded<<"("<<time<<")"<<std::endl;
|
|
# endif
|
|
stat = mainEntry(kOfxImageEffectActionGetFramesNeeded,
|
|
this->getHandle(),
|
|
&inArgs,
|
|
&outArgs);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetFramesNeeded<<"("<<time<<")->"<<StatStr(stat);
|
|
if (stat == kOfxStatOK) {
|
|
std::cout << ": ";
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
ClipInstance *clip = it->second;
|
|
|
|
if(!clip->isOutput()) {
|
|
std::string name = "OfxImageClipPropFrameRange_"+it->first;
|
|
std::cout << it->first << "->[";
|
|
|
|
int nRanges = outArgs.getDimension(name);
|
|
for(int r=0;r<nRanges;){
|
|
double min = outArgs.getDoubleProperty(name,r);
|
|
double max = outArgs.getDoubleProperty(name,r+1);
|
|
r += 2;
|
|
std::cout <<"("<<min<<","<<max<<")";
|
|
if (r < nRanges-1) {
|
|
std::cout << ",";
|
|
}
|
|
}
|
|
std::cout << "]";
|
|
}
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
# endif
|
|
}
|
|
|
|
OfxRangeD defaultRange;
|
|
defaultRange.min =
|
|
defaultRange.max = time;
|
|
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
ClipInstance *clip = it->second;
|
|
|
|
if(!clip->isOutput()) {
|
|
if(stat != kOfxStatOK) {
|
|
rangeMap[clip].push_back(defaultRange);
|
|
}
|
|
else {
|
|
std::string name = "OfxImageClipPropFrameRange_"+it->first;
|
|
|
|
int nRanges = outArgs.getDimension(name);
|
|
if(nRanges%2 != 0)
|
|
return kOfxStatFailed; // bad! needs to be divisible by 2
|
|
|
|
if(nRanges == 0) {
|
|
rangeMap[clip].push_back(defaultRange);
|
|
}
|
|
else {
|
|
for(int r=0;r<nRanges;){
|
|
double min = outArgs.getDoubleProperty(name,r);
|
|
double max = outArgs.getDoubleProperty(name,r+1);
|
|
r += 2;
|
|
|
|
OfxRangeD range;
|
|
range.min = min;
|
|
range.max = max;
|
|
rangeMap[clip].push_back(range);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return stat;
|
|
}
|
|
|
|
OfxStatus Instance::isIdentityAction(OfxTime &time,
|
|
const std::string & field,
|
|
const OfxRectI &renderRoI,
|
|
OfxPointD renderScale,
|
|
std::string &clip)
|
|
{
|
|
static const Property::PropSpec inStuff[] = {
|
|
{ kOfxPropTime, Property::eDouble, 1, true, "0" },
|
|
{ kOfxImageEffectPropFieldToRender, Property::eString, 1, true, "" },
|
|
{ kOfxImageEffectPropRenderWindow, Property::eInt, 4, true, "0" },
|
|
{ kOfxImageEffectPropRenderScale, Property::eDouble, 2, true, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
static const Property::PropSpec outStuff[] = {
|
|
{ kOfxPropTime, Property::eDouble, 1, false, "0.0" },
|
|
{ kOfxPropName, Property::eString, 1, false, "" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set inArgs(inStuff);
|
|
|
|
inArgs.setStringProperty(kOfxImageEffectPropFieldToRender,field);
|
|
inArgs.setDoubleProperty(kOfxPropTime,time);
|
|
inArgs.setIntPropertyN(kOfxImageEffectPropRenderWindow, &renderRoI.x1, 4);
|
|
inArgs.setDoublePropertyN(kOfxImageEffectPropRenderScale, &renderScale.x, 2);
|
|
|
|
Property::Set outArgs(outStuff);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionIsIdentity<<"("<<time<<","<<field<<",("<<renderRoI.x1<<","<<renderRoI.y1<<","<<renderRoI.x2<<","<<renderRoI.y2<<"),("<<renderScale.x<<","<<renderScale.y<<"))"<<std::endl;
|
|
# endif
|
|
outArgs.setDoubleProperty(kOfxPropTime,time);
|
|
|
|
OfxStatus st = mainEntry(kOfxImageEffectActionIsIdentity,
|
|
this->getHandle(),
|
|
&inArgs,
|
|
&outArgs);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionIsIdentity<<"("<<time<<","<<field<<",("<<renderRoI.x1<<","<<renderRoI.y1<<","<<renderRoI.x2<<","<<renderRoI.y2<<"),("<<renderScale.x<<","<<renderScale.y<<"))->"<<StatStr(st);
|
|
if(st==kOfxStatOK){
|
|
std::cout << ": "<<outArgs.getDoubleProperty(kOfxPropTime)<<","<<outArgs.getStringProperty(kOfxPropName);
|
|
}
|
|
std::cout<<std::endl;
|
|
# endif
|
|
|
|
if(st==kOfxStatOK){
|
|
time = outArgs.getDoubleProperty(kOfxPropTime);
|
|
clip = outArgs.getStringProperty(kOfxPropName);
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
/// Get whether the component is a supported 'chromatic' component (RGBA or alpha) in
|
|
/// the base API.
|
|
/// Override this if you have extended your chromatic colour types (eg RGB) and want
|
|
/// the clip preferences logic to still work
|
|
bool Instance::isChromaticComponent(const std::string &str) const
|
|
{
|
|
if(str == kOfxImageComponentRGBA)
|
|
return true;
|
|
if(str == kOfxImageComponentRGB)
|
|
return true;
|
|
if(str == kOfxImageComponentAlpha)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// function to check for multiple bit depth support
|
|
/// The answer will depend on host, plugin and context
|
|
bool Instance::canCurrentlyHandleMultipleClipDepths() const
|
|
{
|
|
/// does the host support 'em
|
|
bool hostSupports = gImageEffectHost->getProperties().getIntProperty(kOfxImageEffectPropSupportsMultipleClipDepths) != 0;;
|
|
|
|
/// does the plug-in support 'em
|
|
bool pluginSupports = supportsMultipleClipDepths();
|
|
|
|
/// no support, so no
|
|
if(!hostSupports || !pluginSupports)
|
|
return false;
|
|
|
|
/// if filter context, no support, tempted to change this though
|
|
if(_context == kOfxImageEffectContextFilter)
|
|
return false;
|
|
|
|
/// if filter context, no support
|
|
if(_context == kOfxImageEffectContextGenerator ||
|
|
_context == kOfxImageEffectContextTransition ||
|
|
_context == kOfxImageEffectContextPaint ||
|
|
_context == kOfxImageEffectContextRetimer)
|
|
return false;
|
|
|
|
/// OK we're cool
|
|
return true;
|
|
}
|
|
|
|
/// Setup the default clip preferences on the clips
|
|
void Instance::setDefaultClipPreferences()
|
|
{
|
|
/// is there multiple bit depth support? Depends on host, plugin and context
|
|
bool multiBitDepth = canCurrentlyHandleMultipleClipDepths();
|
|
|
|
/// OK find the deepest chromatic component on our input clips and the one with the
|
|
/// most components
|
|
bool hasSetCompsAndDepth = false;
|
|
std::string deepestBitDepth = kOfxBitDepthNone;
|
|
std::string mostComponents = kOfxImageComponentNone;
|
|
double frameRate = getFrameRate(); //< default to the project frame rate
|
|
std::string premult = kOfxImageOpaque;
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
ClipInstance *clip = it->second;
|
|
|
|
if(!clip->isOutput()) {
|
|
bool connected = clip->getConnected();
|
|
|
|
if (connected) {
|
|
frameRate = Maximum(frameRate, clip->getFrameRate());
|
|
}
|
|
|
|
std::string rawComp = clip->getUnmappedComponents();
|
|
rawComp = clip->findSupportedComp(rawComp); // turn that into a comp the plugin expects on that clip
|
|
|
|
const std::string &rawDepth = clip->getUnmappedBitDepth();
|
|
const std::string &rawPreMult = clip->getPremult();
|
|
|
|
if(isChromaticComponent(rawComp)) {
|
|
if(connected) {
|
|
if(rawPreMult == kOfxImagePreMultiplied)
|
|
premult = kOfxImagePreMultiplied;
|
|
else if(rawPreMult == kOfxImageUnPreMultiplied && premult != kOfxImagePreMultiplied)
|
|
premult = kOfxImageUnPreMultiplied;
|
|
}
|
|
|
|
if(connected) {
|
|
//Update deepest bitdepth and most components only if the infos are relevant, i.e: only if the clip is connected
|
|
hasSetCompsAndDepth = true;
|
|
deepestBitDepth = FindDeepestBitDepth(deepestBitDepth, rawDepth);
|
|
mostComponents = findMostChromaticComponents(mostComponents, rawComp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// default to a reasonable value if there is no input
|
|
if (!hasSetCompsAndDepth) {
|
|
mostComponents = kOfxImageComponentRGBA;
|
|
deepestBitDepth = kOfxBitDepthFloat;
|
|
}
|
|
|
|
/// set some stuff up
|
|
_outputFrameRate = frameRate;
|
|
_outputFielding = getDefaultOutputFielding();
|
|
_outputPreMultiplication = premult;
|
|
_continuousSamples = false;
|
|
_frameVarying = false;
|
|
|
|
/// now find the best depth that the plugin supports
|
|
deepestBitDepth = bestSupportedDepth(deepestBitDepth);
|
|
|
|
/// now add the clip gubbins to the out args
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
ClipInstance *clip = it->second;
|
|
|
|
std::string comp, depth;
|
|
|
|
std::string rawComp = clip->getUnmappedComponents();
|
|
rawComp = clip->findSupportedComp(rawComp); // turn that into a comp the plugin expects on that clip
|
|
const std::string &rawDepth = clip->getUnmappedBitDepth();
|
|
|
|
if(isChromaticComponent(rawComp)) {
|
|
|
|
if(clip->isOutput() || clip->isOptional()) {
|
|
// "Optional input clips can always have their component types remapped"
|
|
// http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#id482755
|
|
depth = deepestBitDepth;
|
|
comp = clip->findSupportedComp(mostComponents);
|
|
|
|
clip->setPixelDepth(depth);
|
|
clip->setComponents(comp);
|
|
}
|
|
else {
|
|
comp = rawComp;
|
|
depth = multiBitDepth ? bestSupportedDepth(rawDepth) : deepestBitDepth;
|
|
|
|
clip->setPixelDepth(depth);
|
|
clip->setComponents(comp);
|
|
}
|
|
}
|
|
else {
|
|
/// hmm custom component type, don't touch it and pass it through
|
|
clip->setPixelDepth(rawDepth);
|
|
clip->setComponents(rawComp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Initialise the clip preferences arguments, override this to do
|
|
/// stuff with wierd components etc...
|
|
void Instance::setupClipPreferencesArgs(Property::Set &outArgs)
|
|
{
|
|
/// reset all the clip prefs stuff to their defaults
|
|
setDefaultClipPreferences();
|
|
|
|
static const Property::PropSpec clipPrefsStuffs []=
|
|
{
|
|
{ kOfxImageEffectPropFrameRate, Property::eDouble, 1, false, "1" },
|
|
{ kOfxImageEffectPropPreMultiplication, Property::eString, 1, false, "" },
|
|
{ kOfxImageClipPropFieldOrder, Property::eString, 1, false, "" },
|
|
{ kOfxImageClipPropContinuousSamples, Property::eInt, 1, false, "0" },
|
|
{ kOfxImageEffectFrameVarying, Property::eInt, 1, false, "0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
outArgs.addProperties(clipPrefsStuffs);
|
|
|
|
/// set the default for those
|
|
|
|
/// is there multiple bit depth support? Depends on host, plugin and context
|
|
bool multiBitDepth = canCurrentlyHandleMultipleClipDepths();
|
|
|
|
outArgs.setStringProperty(kOfxImageClipPropFieldOrder, _outputFielding);
|
|
outArgs.setStringProperty(kOfxImageEffectPropPreMultiplication, _outputPreMultiplication);
|
|
|
|
/// now add the clip gubbins to the out args
|
|
double projectPAR = getProjectPixelAspectRatio();
|
|
bool multipleClipsPAR = supportsMultipleClipPARs();
|
|
/// get the PAR of inputs, if it has different PARs and the effect does not support multiple clips PAR, throw an exception
|
|
double inputPar;
|
|
bool inputParSet = false;
|
|
for (std::map<std::string, ClipInstance*>::iterator it2 = _clips.begin(); it2 != _clips.end(); ++it2) {
|
|
if (!it2->second->isOutput() && it2->second->getConnected()) {
|
|
if (!inputParSet) {
|
|
inputPar = it2->second->getAspectRatio();
|
|
inputParSet = true;
|
|
} else if (!multipleClipsPAR && inputPar != it2->second->getAspectRatio()) {
|
|
// We have several inputs with different aspect ratio, which should be forbidden by the host.
|
|
throw Property::Exception(kOfxStatErrValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
ClipInstance *clip = it->second;
|
|
|
|
std::string componentParamName = "OfxImageClipPropComponents_"+it->first;
|
|
std::string depthParamName = "OfxImageClipPropDepth_"+it->first;
|
|
std::string parParamName = "OfxImageClipPropPAR_"+it->first;
|
|
|
|
Property::PropSpec specComp = {componentParamName.c_str(), Property::eString, 0, false, ""}; // note the support for multi-planar clips
|
|
outArgs.createProperty(specComp);
|
|
outArgs.setStringProperty(componentParamName.c_str(), clip->getComponents().c_str()); // as it is variable dimension, there is no default value, so we have to set it explicitly
|
|
|
|
Property::PropSpec specDep = {depthParamName.c_str(), Property::eString, 1, !multiBitDepth, clip->getPixelDepth().c_str()};
|
|
outArgs.createProperty(specDep);
|
|
|
|
Property::PropSpec specPAR = {parParamName.c_str(), Property::eDouble, 1, false, "1"};
|
|
outArgs.createProperty(specPAR);
|
|
if (!clip->isOutput()) {
|
|
// If the clip is input, use the same par for all inputs unless the plug-in supports multiple clips PAR
|
|
double par;
|
|
if (!multipleClipsPAR && inputParSet) {
|
|
par = inputPar;
|
|
} else if (multipleClipsPAR) {
|
|
par = clip->getAspectRatio();
|
|
} else {
|
|
par = projectPAR;
|
|
}
|
|
outArgs.setDoubleProperty(parParamName, par);
|
|
} else {
|
|
// If the clip is output we should propagate the pixel aspect ratio of the inputs
|
|
outArgs.setDoubleProperty(parParamName, inputParSet ? inputPar : projectPAR);
|
|
}
|
|
}
|
|
|
|
//Set the output frame rate according to what input clips have. Several inputs with different frame rates should be
|
|
//forbidden by the host.
|
|
bool outputFrameRateSet = false;
|
|
double outputFrameRate = _outputFrameRate;
|
|
for (std::map<std::string, ClipInstance*>::iterator it2 = _clips.begin(); it2 != _clips.end(); ++it2) {
|
|
if (!it2->second->isOutput() && it2->second->getConnected()) {
|
|
if (!outputFrameRateSet) {
|
|
outputFrameRate = it2->second->getFrameRate();
|
|
outputFrameRateSet = true;
|
|
} else if (outputFrameRate != it2->second->getFrameRate()) {
|
|
// We have several inputs with different frame rates
|
|
throw Property::Exception(kOfxStatErrValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
outArgs.setDoubleProperty(kOfxImageEffectPropFrameRate, outputFrameRate);
|
|
|
|
}
|
|
|
|
/// the idea here is the clip prefs live as active props on the effect
|
|
/// and are set up by clip preferences. The action manages the clip
|
|
/// preferences bits. We also monitor clip and param changes and
|
|
/// flag when clip prefs is dirty.
|
|
/// call the clip preferences action
|
|
bool Instance::getClipPreferences()
|
|
{
|
|
/// create the out args with the stuff that does not depend on individual clips
|
|
Property::Set outArgs;
|
|
|
|
setupClipPreferencesArgs(outArgs);
|
|
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetClipPreferences<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxImageEffectActionGetClipPreferences,
|
|
this->getHandle(),
|
|
0,
|
|
&outArgs);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetClipPreferences<<"()->"<<StatStr(st);
|
|
# endif
|
|
|
|
if(st!=kOfxStatOK && st!=kOfxStatReplyDefault) {
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << std::endl;
|
|
# endif
|
|
/// ouch
|
|
return false;
|
|
}
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << ": ";
|
|
# endif
|
|
/// OK, go pump the components/depths back into the clips themselves
|
|
for(std::map<std::string, ClipInstance*>::iterator it=_clips.begin();
|
|
it!=_clips.end();
|
|
++it) {
|
|
ClipInstance *clip = it->second;
|
|
|
|
std::string componentParamName = "OfxImageClipPropComponents_"+it->first;
|
|
std::string depthParamName = "OfxImageClipPropDepth_"+it->first;
|
|
std::string parParamName = "OfxImageClipPropPAR_"+it->first;
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << it->first<<"->"<<outArgs.getStringProperty(depthParamName)<<","<<outArgs.getStringProperty(componentParamName)<<","<<outArgs.getDoubleProperty(parParamName)<<" ";
|
|
# endif
|
|
|
|
clip->setPixelDepth(outArgs.getStringProperty(depthParamName));
|
|
clip->setComponents(outArgs.getStringProperty(componentParamName));
|
|
//clip->setPixelAspect(outArgs.getDoubleProperty(parParamName));
|
|
}
|
|
|
|
_outputFrameRate = outArgs.getDoubleProperty(kOfxImageEffectPropFrameRate);
|
|
_outputFielding = outArgs.getStringProperty(kOfxImageClipPropFieldOrder);
|
|
_outputPreMultiplication = outArgs.getStringProperty(kOfxImageEffectPropPreMultiplication);
|
|
_continuousSamples = outArgs.getIntProperty(kOfxImageClipPropContinuousSamples) != 0;
|
|
_frameVarying = outArgs.getIntProperty(kOfxImageEffectFrameVarying) != 0;
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << _outputFrameRate<<","<<_outputFielding<<","<<_outputPreMultiplication<<","<<_continuousSamples<<","<<_frameVarying<<std::endl;
|
|
# endif
|
|
|
|
_clipPrefsDirty = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// find the most chromatic components out of the two. Override this if you define
|
|
/// more chromatic components
|
|
const std::string &Instance::findMostChromaticComponents(const std::string &a, const std::string &b) const
|
|
{
|
|
if(a == kOfxImageComponentNone)
|
|
return b;
|
|
if(a == kOfxImageComponentRGBA)
|
|
return a;
|
|
if(b == kOfxImageComponentRGBA)
|
|
return b;
|
|
if(a == kOfxImageComponentRGB)
|
|
return a;
|
|
if(b == kOfxImageComponentRGB)
|
|
return b;
|
|
return a;
|
|
}
|
|
|
|
/// given the bit depth, find the best match for it.
|
|
const std::string &Instance::bestSupportedDepth(const std::string &depth) const
|
|
{
|
|
static const std::string none(kOfxBitDepthNone);
|
|
static const std::string bytes(kOfxBitDepthByte);
|
|
static const std::string shorts(kOfxBitDepthShort);
|
|
static const std::string halfs(kOfxBitDepthHalf);
|
|
static const std::string floats(kOfxBitDepthFloat);
|
|
|
|
if(depth == none)
|
|
return none;
|
|
|
|
if(isPixelDepthSupported(depth))
|
|
return depth;
|
|
|
|
if(depth == floats) {
|
|
if(isPixelDepthSupported(shorts))
|
|
return shorts;
|
|
if(isPixelDepthSupported(bytes))
|
|
return bytes;
|
|
}
|
|
|
|
if(depth == halfs) {
|
|
if(isPixelDepthSupported(floats))
|
|
return floats;
|
|
if(isPixelDepthSupported(shorts))
|
|
return shorts;
|
|
if(isPixelDepthSupported(bytes))
|
|
return bytes;
|
|
}
|
|
|
|
if(depth == shorts) {
|
|
if(isPixelDepthSupported(floats))
|
|
return floats;
|
|
if(isPixelDepthSupported(bytes))
|
|
return bytes;
|
|
}
|
|
|
|
if(depth == bytes) {
|
|
if(isPixelDepthSupported(shorts))
|
|
return shorts;
|
|
if(isPixelDepthSupported(floats))
|
|
return floats;
|
|
}
|
|
|
|
/// WTF? Something wrong here
|
|
return none;
|
|
}
|
|
|
|
|
|
/// get the interact description, this will also call describe on the interact
|
|
Interact::Descriptor &Instance::getOverlayDescriptor(int bitDepthPerComponent, bool hasAlpha)
|
|
{
|
|
return _descriptor->getOverlayDescriptor(bitDepthPerComponent, hasAlpha);
|
|
}
|
|
|
|
OfxStatus Instance::getTimeDomainAction(OfxRangeD& range)
|
|
{
|
|
static const Property::PropSpec outStuff[] = {
|
|
{ kOfxImageEffectPropFrameRange , Property::eDouble, 2, false, "0.0" },
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
Property::Set outArgs(outStuff);
|
|
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetTimeDomain<<"()"<<std::endl;
|
|
# endif
|
|
OfxStatus st = mainEntry(kOfxImageEffectActionGetTimeDomain,
|
|
this->getHandle(),
|
|
0,
|
|
&outArgs);
|
|
# ifdef OFX_DEBUG_ACTIONS
|
|
std::cout << "OFX: "<<(void*)this<<"->"<<kOfxImageEffectActionGetTimeDomain<<"()->"<<StatStr(st);
|
|
if (st == kOfxStatOK) {
|
|
std::cout << ": ("<<outArgs.getDoubleProperty(kOfxImageEffectPropFrameRange,0)<<","<<outArgs.getDoubleProperty(kOfxImageEffectPropFrameRange,1)<<")";
|
|
}
|
|
std::cout << std::endl;
|
|
# endif
|
|
if(st!=kOfxStatOK) return st;
|
|
|
|
range.min = outArgs.getDoubleProperty(kOfxImageEffectPropFrameRange,0);
|
|
range.max = outArgs.getDoubleProperty(kOfxImageEffectPropFrameRange,1);
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
|
|
/// implemented for Param::SetInstance
|
|
void Instance::paramChangedByPlugin(Param::Instance *param)
|
|
{
|
|
if (!_created) {
|
|
// setValue() was probably called from kOfxActionCreateInstance
|
|
// this is legal according to http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#SettingParams
|
|
// but kOfxActionInstanceChanged should not be called according to the preconditions of http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#kOfxActionInstanceChanged
|
|
return;
|
|
}
|
|
double frame = getFrameRecursive();
|
|
OfxPointD renderScale; getRenderScaleRecursive(renderScale.x, renderScale.y);
|
|
|
|
beginInstanceChangedAction(kOfxChangePluginEdited);
|
|
paramInstanceChangedAction(param->getName(), kOfxChangePluginEdited, frame, renderScale);
|
|
endInstanceChangedAction(kOfxChangePluginEdited);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// The image effect suite functions
|
|
static OfxStatus getPropertySet(OfxImageEffectHandle h1,
|
|
OfxPropertySetHandle *h2)
|
|
{
|
|
try {
|
|
if (!h2) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
Base *effectBase = reinterpret_cast<Base*>(h1);
|
|
|
|
if (!effectBase || !effectBase->verifyMagic()) {
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
*h2 = effectBase->getProps().getHandle();
|
|
|
|
return kOfxStatOK;
|
|
} catch (...) {
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus getParamSet(OfxImageEffectHandle h1,
|
|
OfxParamSetHandle *h2)
|
|
{
|
|
try {
|
|
if (!h2) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Base *effectBase = reinterpret_cast<ImageEffect::Base*>(h1);
|
|
|
|
if (!effectBase || !effectBase->verifyMagic()) {
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Descriptor *effectDescriptor = dynamic_cast<ImageEffect::Descriptor*>(effectBase);
|
|
|
|
if(effectDescriptor) {
|
|
*h2 = effectDescriptor->getParamSetHandle();
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
ImageEffect::Instance *effectInstance = dynamic_cast<ImageEffect::Instance*>(effectBase);
|
|
|
|
if(effectInstance) {
|
|
*h2 = effectInstance->getParamSetHandle();
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clipDefine(OfxImageEffectHandle h1,
|
|
const char *name,
|
|
OfxPropertySetHandle *h2)
|
|
{
|
|
try {
|
|
if (!h2) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Base *effectBase = reinterpret_cast<ImageEffect::Base*>(h1);
|
|
|
|
if (!effectBase || !effectBase->verifyMagic()) {
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Descriptor *effectDescriptor = dynamic_cast<ImageEffect::Descriptor*>(effectBase);
|
|
|
|
if(effectDescriptor){
|
|
ClipDescriptor *clip = effectDescriptor->defineClip(name);
|
|
*h2 = clip->getPropHandle();
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
*h2 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clipGetPropertySet(OfxImageClipHandle clip,
|
|
OfxPropertySetHandle *propHandle){
|
|
try {
|
|
if (!propHandle) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ClipInstance *clipInstance = reinterpret_cast<ClipInstance*>(clip);
|
|
|
|
if (!clipInstance || !clipInstance->verifyMagic()) {
|
|
*propHandle = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
*propHandle = clipInstance->getPropHandle();
|
|
return kOfxStatOK;
|
|
} catch (...) {
|
|
*propHandle = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clipGetImage(OfxImageClipHandle h1,
|
|
OfxTime time,
|
|
const OfxRectD *h2,
|
|
OfxPropertySetHandle *h3)
|
|
{
|
|
ClipInstance *clipInstance = nullptr;
|
|
try {
|
|
if (!h3) {
|
|
std::cerr << "[clipGetImage] h3 is null" << std::endl;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
clipInstance = reinterpret_cast<ClipInstance*>(h1);
|
|
|
|
if (!clipInstance) {
|
|
std::cerr << "[clipGetImage] clipInstance is null" << std::endl;
|
|
*h3 = NULL;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
if (!clipInstance->verifyMagic()) {
|
|
std::cerr << "[clipGetImage] verifyMagic failed for clip=" << clipInstance->getName() << std::endl;
|
|
*h3 = NULL;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
Image* image = clipInstance->getImage(time,h2);
|
|
if(!image) {
|
|
std::cerr << "[clipGetImage] getImage returned null for clip=" << clipInstance->getName() << " time=" << time << std::endl;
|
|
*h3 = NULL;
|
|
return kOfxStatFailed;
|
|
}
|
|
|
|
*h3 = image->getPropHandle();
|
|
if (!*h3) {
|
|
std::cerr << "[clipGetImage] getPropHandle returned null for clip=" << clipInstance->getName() << std::endl;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
return kOfxStatOK;
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[clipGetImage] exception: " << e.what() << " clip=" << (clipInstance ? clipInstance->getName() : "null") << std::endl;
|
|
*h3 = NULL;
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
std::cerr << "[clipGetImage] unknown exception clip=" << (clipInstance ? clipInstance->getName() : "null") << std::endl;
|
|
*h3 = NULL;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clipReleaseImage(OfxPropertySetHandle h1)
|
|
{
|
|
try {
|
|
Property::Set *pset = reinterpret_cast<Property::Set*>(h1);
|
|
|
|
if (!pset || !pset->verifyMagic()) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
Image *image = dynamic_cast<Image*>(pset);
|
|
|
|
if(image){
|
|
// clip::image has a virtual destructor for derived classes
|
|
image->releaseReference();
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clipGetHandle(OfxImageEffectHandle imageEffect,
|
|
const char *name,
|
|
OfxImageClipHandle *clip,
|
|
OfxPropertySetHandle *propertySet)
|
|
{
|
|
try {
|
|
if (!clip) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Base *effectBase = reinterpret_cast<ImageEffect::Base*>(imageEffect);
|
|
|
|
if (!effectBase || !effectBase->verifyMagic()) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Instance *effectInstance = reinterpret_cast<ImageEffect::Instance*>(effectBase);
|
|
|
|
if(effectInstance){
|
|
ClipInstance* instance = effectInstance->getClip(name);
|
|
if(!instance)
|
|
return kOfxStatErrBadHandle;
|
|
*clip = instance->getHandle();
|
|
if(propertySet)
|
|
*propertySet = instance->getPropHandle();
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clipGetRegionOfDefinition(OfxImageClipHandle clip,
|
|
OfxTime time,
|
|
OfxRectD *bounds)
|
|
{
|
|
ClipInstance *clipInstance = nullptr;
|
|
try {
|
|
if (!bounds) {
|
|
std::cerr << "[clipGetRegionOfDefinition] bounds is null" << std::endl;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
clipInstance = reinterpret_cast<ClipInstance*>(clip);
|
|
|
|
if (!clipInstance) {
|
|
std::cerr << "[clipGetRegionOfDefinition] clipInstance is null" << std::endl;
|
|
bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
if (!clipInstance->verifyMagic()) {
|
|
std::cerr << "[clipGetRegionOfDefinition] verifyMagic failed for clip=" << clipInstance->getName() << std::endl;
|
|
bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
*bounds = clipInstance->getRegionOfDefinition(time);
|
|
if (bounds->x2 < bounds->x1 || bounds->y2 < bounds->y1) {
|
|
std::cerr << "[clipGetRegionOfDefinition] invalid RoD for clip=" << clipInstance->getName() << std::endl;
|
|
return kOfxStatFailed;
|
|
}
|
|
|
|
return kOfxStatOK;
|
|
} catch (const std::exception &e) {
|
|
std::cerr << "[clipGetRegionOfDefinition] exception: " << e.what() << " clip=" << (clipInstance ? clipInstance->getName() : "null") << std::endl;
|
|
bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.;
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
std::cerr << "[clipGetRegionOfDefinition] unknown exception clip=" << (clipInstance ? clipInstance->getName() : "null") << std::endl;
|
|
bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
// should processing be aborted?
|
|
static int abort(OfxImageEffectHandle imageEffect)
|
|
{
|
|
try {
|
|
ImageEffect::Base *effectBase = reinterpret_cast<ImageEffect::Base*>(imageEffect);
|
|
|
|
if (!effectBase || !effectBase->verifyMagic()) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Instance *effectInstance = dynamic_cast<ImageEffect::Instance*>(effectBase);
|
|
|
|
if(effectInstance)
|
|
return effectInstance->abort();
|
|
else
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus imageMemoryAlloc(OfxImageEffectHandle instanceHandle,
|
|
size_t nBytes,
|
|
OfxImageMemoryHandle *memoryHandle)
|
|
{
|
|
try {
|
|
if (!memoryHandle) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ImageEffect::Base *effectBase = reinterpret_cast<ImageEffect::Base*>(instanceHandle);
|
|
ImageEffect::Instance *effectInstance = reinterpret_cast<ImageEffect::Instance*>(effectBase);
|
|
Memory::Instance* memory;
|
|
|
|
if(effectInstance){
|
|
|
|
if (!effectInstance->verifyMagic()) {
|
|
*memoryHandle = NULL;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
memory = effectInstance->imageMemoryAlloc(nBytes);
|
|
}
|
|
else {
|
|
memory = gImageEffectHost->imageMemoryAlloc(nBytes);
|
|
}
|
|
|
|
if (memory) {
|
|
*memoryHandle = memory->getHandle();
|
|
return kOfxStatOK;
|
|
} else {
|
|
*memoryHandle = NULL;
|
|
return kOfxStatErrMemory;
|
|
}
|
|
} catch (std::bad_alloc&) {
|
|
*memoryHandle = NULL;
|
|
return kOfxStatErrMemory;
|
|
} catch (...) {
|
|
*memoryHandle = NULL;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus imageMemoryFree(OfxImageMemoryHandle memoryHandle){
|
|
try {
|
|
Memory::Instance *memoryInstance = reinterpret_cast<Memory::Instance*>(memoryHandle);
|
|
|
|
if(memoryInstance && memoryInstance->verifyMagic()) {
|
|
memoryInstance->freeMem();
|
|
delete memoryInstance;
|
|
return kOfxStatOK;
|
|
}
|
|
else
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static
|
|
OfxStatus imageMemoryLock(OfxImageMemoryHandle memoryHandle,
|
|
void **returnedPtr){
|
|
try {
|
|
if (!returnedPtr) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
Memory::Instance *memoryInstance = reinterpret_cast<Memory::Instance*>(memoryHandle);
|
|
|
|
if(memoryInstance && memoryInstance->verifyMagic()) {
|
|
memoryInstance->lock();
|
|
*returnedPtr = memoryInstance->getPtr();
|
|
|
|
return (*returnedPtr) ? kOfxStatOK : kOfxStatErrMemory;
|
|
}
|
|
|
|
*returnedPtr = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
*returnedPtr = NULL;
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus imageMemoryUnlock(OfxImageMemoryHandle memoryHandle){
|
|
try {
|
|
Memory::Instance *memoryInstance = reinterpret_cast<Memory::Instance*>(memoryHandle);
|
|
|
|
if(memoryInstance && memoryInstance->verifyMagic()){
|
|
memoryInstance->unlock();
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static const struct OfxImageEffectSuiteV1 gImageEffectSuite = {
|
|
getPropertySet,
|
|
getParamSet,
|
|
clipDefine,
|
|
clipGetHandle,
|
|
clipGetPropertySet,
|
|
clipGetImage,
|
|
clipReleaseImage,
|
|
clipGetRegionOfDefinition,
|
|
abort,
|
|
imageMemoryAlloc,
|
|
imageMemoryFree,
|
|
imageMemoryLock,
|
|
imageMemoryUnlock
|
|
};
|
|
|
|
# ifdef OFX_SUPPORTS_OPENGLRENDER
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// The OpenGL render suite functions
|
|
|
|
static OfxStatus clipLoadTexture(OfxImageClipHandle h1,
|
|
OfxTime time,
|
|
const char *format,
|
|
const OfxRectD *h2,
|
|
OfxPropertySetHandle *h3)
|
|
{
|
|
try {
|
|
if (!h3) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
ClipInstance *clipInstance = reinterpret_cast<ClipInstance*>(h1);
|
|
|
|
if (!clipInstance || !clipInstance->verifyMagic()) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
if(clipInstance){
|
|
Texture* texture = clipInstance->loadTexture(time,format,h2);
|
|
if(!texture) {
|
|
*h3 = NULL;
|
|
|
|
return kOfxStatFailed;
|
|
}
|
|
|
|
*h3 = texture->getPropHandle();
|
|
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
*h3 = NULL;
|
|
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clipFreeTexture(OfxPropertySetHandle h1)
|
|
{
|
|
try {
|
|
Property::Set *pset = reinterpret_cast<Property::Set*>(h1);
|
|
|
|
if (!pset || !pset->verifyMagic()) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
|
|
Texture *texture = dynamic_cast<Texture*>(pset);
|
|
|
|
if(texture){
|
|
// clip::texture has a virtual destructor for derived classes
|
|
texture->releaseReference();
|
|
return kOfxStatOK;
|
|
}
|
|
else
|
|
return kOfxStatErrBadHandle;
|
|
} catch (...) {
|
|
return kOfxStatErrBadHandle;
|
|
}
|
|
}
|
|
|
|
static OfxStatus flushResources( )
|
|
{
|
|
return gImageEffectHost->flushOpenGLResources();
|
|
}
|
|
|
|
static const struct OfxImageEffectOpenGLRenderSuiteV1 gOpenGLRenderSuite = {
|
|
clipLoadTexture,
|
|
clipFreeTexture,
|
|
flushResources
|
|
};
|
|
# endif
|
|
|
|
/// message suite function for an image effect
|
|
static OfxStatus message(void *handle, const char *type, const char *id, const char *format, ...)
|
|
{
|
|
try {
|
|
ImageEffect::Instance *effectInstance = reinterpret_cast<ImageEffect::Instance*>(handle);
|
|
OfxStatus stat;
|
|
if(effectInstance){
|
|
va_list args;
|
|
va_start(args,format);
|
|
stat = effectInstance->vmessage(type,id,format,args);
|
|
va_end(args);
|
|
}
|
|
else{
|
|
va_list args;
|
|
va_start(args,format);
|
|
vprintf(format,args);
|
|
va_end(args);
|
|
stat = kOfxStatErrBadHandle;
|
|
}
|
|
return stat;
|
|
} catch (...) {
|
|
return kOfxStatFailed;
|
|
}
|
|
}
|
|
|
|
static OfxStatus setPersistentMessage(void *handle, const char *type, const char *id, const char *format, ...)
|
|
{
|
|
try {
|
|
ImageEffect::Instance *effectInstance = reinterpret_cast<ImageEffect::Instance*>(handle);
|
|
OfxStatus stat;
|
|
if(effectInstance){
|
|
va_list args;
|
|
va_start(args,format);
|
|
stat = effectInstance->setPersistentMessage(type,id,format,args);
|
|
va_end(args);
|
|
}
|
|
else{
|
|
va_list args;
|
|
va_start(args,format);
|
|
vprintf(format,args);
|
|
va_end(args);
|
|
stat = kOfxStatErrBadHandle;
|
|
}
|
|
return stat;
|
|
} catch (...) {
|
|
return kOfxStatFailed;
|
|
}
|
|
}
|
|
|
|
static OfxStatus clearPersistentMessage(void *handle)
|
|
{
|
|
try {
|
|
ImageEffect::Instance *effectInstance = reinterpret_cast<ImageEffect::Instance*>(handle);
|
|
OfxStatus stat;
|
|
if(effectInstance){
|
|
stat = effectInstance->clearPersistentMessage();
|
|
}
|
|
else{
|
|
stat = kOfxStatErrBadHandle;
|
|
}
|
|
return stat;
|
|
} catch (...) {
|
|
return kOfxStatFailed;
|
|
}
|
|
}
|
|
|
|
/// message suite for an image effect plugin (backward-compatible with OfxMessageSuiteV1)
|
|
static const struct OfxMessageSuiteV2 gMessageSuite = {
|
|
message,
|
|
setPersistentMessage,
|
|
clearPersistentMessage
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// make an overlay interact for an image effect
|
|
OverlayInteract::OverlayInteract(ImageEffect::Instance &effect, int bitDepthPerComponent, bool hasAlpha)
|
|
: Interact::Instance(effect.getOverlayDescriptor(bitDepthPerComponent, hasAlpha),
|
|
(void *)(effect.getHandle()))
|
|
, _instance(effect)
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Progress suite functions
|
|
|
|
/// begin progressing
|
|
static OfxStatus ProgressStartV1(void *effectInstance,
|
|
const char *label)
|
|
{
|
|
if (!effectInstance)
|
|
return kOfxStatErrBadHandle;
|
|
Instance *me = reinterpret_cast<Instance *>(effectInstance);
|
|
me->progressStart(label, "");
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
|
|
/// begin progressing
|
|
static OfxStatus ProgressStart(void *effectInstance,
|
|
const char *message,
|
|
const char *messageid)
|
|
{
|
|
if (!effectInstance)
|
|
return kOfxStatErrBadHandle;
|
|
Instance *me = reinterpret_cast<Instance *>(effectInstance);
|
|
me->progressStart(message, messageid);
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
/// finish progressing
|
|
static OfxStatus ProgressEnd(void *effectInstance)
|
|
{
|
|
if (!effectInstance)
|
|
return kOfxStatErrBadHandle;
|
|
Instance *me = reinterpret_cast<Instance *>(effectInstance);
|
|
me->progressEnd();
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
/// update progressing
|
|
static OfxStatus ProgressUpdate(void *effectInstance, double progress)
|
|
{
|
|
if (!effectInstance)
|
|
return kOfxStatErrBadHandle;
|
|
Instance *me = reinterpret_cast<Instance *>(effectInstance);
|
|
bool v = me->progressUpdate(progress);
|
|
return v ? kOfxStatOK : kOfxStatReplyNo;
|
|
}
|
|
|
|
/// our progress suite
|
|
struct OfxProgressSuiteV1 gProgressSuiteV1 = {
|
|
ProgressStartV1,
|
|
ProgressUpdate,
|
|
ProgressEnd
|
|
};
|
|
|
|
struct OfxProgressSuiteV2 gProgressSuiteV2 = {
|
|
ProgressStart,
|
|
ProgressUpdate,
|
|
ProgressEnd
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// timeline suite functions
|
|
|
|
/// timeline suite function
|
|
static OfxStatus TimeLineGetTime(void *effectInstance, double *time)
|
|
{
|
|
if (!effectInstance)
|
|
return kOfxStatErrBadHandle;
|
|
Instance *me = reinterpret_cast<Instance *>(effectInstance);
|
|
*time = me->timeLineGetTime();
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
/// timeline suite function
|
|
static OfxStatus TimeLineGotoTime(void *effectInstance, double time)
|
|
{
|
|
if (!effectInstance)
|
|
return kOfxStatErrBadHandle;
|
|
Instance *me = reinterpret_cast<Instance *>(effectInstance);
|
|
me->timeLineGotoTime(time);
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
/// timeline suite function
|
|
static OfxStatus TimeLineGetBounds(void *effectInstance, double *firstTime, double *lastTime)
|
|
{
|
|
if (!effectInstance)
|
|
return kOfxStatErrBadHandle;
|
|
Instance *me = reinterpret_cast<Instance *>(effectInstance);
|
|
me->timeLineGetBounds(*firstTime, *lastTime);
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
/// our progress suite
|
|
struct OfxTimeLineSuiteV1 gTimelineSuite = {
|
|
TimeLineGetTime,
|
|
TimeLineGotoTime,
|
|
TimeLineGetBounds
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#ifdef OFX_SUPPORTS_MULTITHREAD
|
|
// Forward all multithread suite calls to the host implementation.
|
|
|
|
static OfxStatus multiThread(OfxThreadFunctionV1 func,
|
|
unsigned int nThreads,
|
|
void *customArg)
|
|
{
|
|
return gImageEffectHost->multiThread(func, nThreads, customArg);
|
|
}
|
|
|
|
static OfxStatus multiThreadNumCPUs(unsigned int *nCPUs)
|
|
{
|
|
return gImageEffectHost->multiThreadNumCPUS(nCPUs);
|
|
}
|
|
|
|
static OfxStatus multiThreadIndex(unsigned int *threadIndex){
|
|
return gImageEffectHost->multiThreadIndex(threadIndex);
|
|
}
|
|
|
|
static int multiThreadIsSpawnedThread(void){
|
|
return gImageEffectHost->multiThreadIsSpawnedThread();
|
|
}
|
|
|
|
static OfxStatus mutexCreate(OfxMutexHandle *mutex, int lockCount)
|
|
{
|
|
return gImageEffectHost->mutexCreate(mutex, lockCount);
|
|
}
|
|
|
|
static OfxStatus mutexDestroy(const OfxMutexHandle mutex)
|
|
{
|
|
return gImageEffectHost->mutexDestroy(mutex);
|
|
}
|
|
|
|
static OfxStatus mutexLock(const OfxMutexHandle mutex){
|
|
return gImageEffectHost->mutexLock(mutex);
|
|
}
|
|
|
|
static OfxStatus mutexUnLock(const OfxMutexHandle mutex){
|
|
return gImageEffectHost->mutexUnLock(mutex);
|
|
}
|
|
|
|
static OfxStatus mutexTryLock(const OfxMutexHandle mutex){
|
|
return gImageEffectHost->mutexTryLock(mutex);
|
|
}
|
|
#else // !OFX_SUPPORTS_MULTITHREAD
|
|
/// a simple multithread suite
|
|
static OfxStatus multiThread(OfxThreadFunctionV1 func,
|
|
unsigned int /*nThreads*/,
|
|
void *customArg)
|
|
{
|
|
if (!func)
|
|
return kOfxStatFailed;
|
|
func(0,1,customArg);
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
static OfxStatus multiThreadNumCPUs(unsigned int *nCPUs)
|
|
{
|
|
if (!nCPUs)
|
|
return kOfxStatFailed;
|
|
*nCPUs = 1;
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
static OfxStatus multiThreadIndex(unsigned int *threadIndex){
|
|
if (!threadIndex)
|
|
return kOfxStatFailed;
|
|
*threadIndex = 0;
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
static int multiThreadIsSpawnedThread(void){
|
|
return false;
|
|
}
|
|
|
|
static OfxStatus mutexCreate(OfxMutexHandle *mutex, int /*lockCount*/)
|
|
{
|
|
if (!mutex)
|
|
return kOfxStatFailed;
|
|
// do nothing single threaded
|
|
*mutex = 0;
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
static OfxStatus mutexDestroy(const OfxMutexHandle mutex)
|
|
{
|
|
if (mutex != 0)
|
|
return kOfxStatErrBadHandle;
|
|
// do nothing single threaded
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
static OfxStatus mutexLock(const OfxMutexHandle mutex){
|
|
if (mutex != 0)
|
|
return kOfxStatErrBadHandle;
|
|
// do nothing single threaded
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
static OfxStatus mutexUnLock(const OfxMutexHandle mutex){
|
|
if (mutex != 0)
|
|
return kOfxStatErrBadHandle;
|
|
// do nothing single threaded
|
|
return kOfxStatOK;
|
|
}
|
|
|
|
static OfxStatus mutexTryLock(const OfxMutexHandle mutex){
|
|
if (mutex != 0)
|
|
return kOfxStatErrBadHandle;
|
|
// do nothing single threaded
|
|
return kOfxStatOK;
|
|
}
|
|
#endif // !OFX_SUPPORTS_MULTITHREAD
|
|
|
|
static const struct OfxMultiThreadSuiteV1 gMultiThreadSuite = {
|
|
multiThread,
|
|
multiThreadNumCPUs,
|
|
multiThreadIndex,
|
|
multiThreadIsSpawnedThread,
|
|
mutexCreate,
|
|
mutexDestroy,
|
|
mutexLock,
|
|
mutexUnLock,
|
|
mutexTryLock
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// The image effect host
|
|
|
|
/// properties for the image effect host
|
|
static const Property::PropSpec hostStuffs[] = {
|
|
{ kOfxImageEffectHostPropIsBackground, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropSupportsOverlays, Property::eInt, 1, true, "1" },
|
|
{ kOfxImageEffectPropSupportsMultiResolution, Property::eInt, 1, true, "1" },
|
|
{ kOfxImageEffectPropSupportsTiles, Property::eInt, 1, true, "1" },
|
|
{ kOfxImageEffectPropTemporalClipAccess, Property::eInt, 1, true, "1" },
|
|
|
|
|
|
|
|
/// xxx this needs defaulting manually
|
|
{ kOfxImageEffectPropSupportedComponents, Property::eString, 0, true, "" },
|
|
/// xxx this needs defaulting manually
|
|
|
|
{ kOfxImageEffectPropSupportedPixelDepths, Property::eString, 0, true, "" },
|
|
|
|
/// xxx this needs defaulting manually
|
|
|
|
{ kOfxImageEffectPropSupportedContexts, Property::eString, 0, true, "" },
|
|
/// xxx this needs defaulting manually
|
|
|
|
{ kOfxImageEffectPropSupportsMultipleClipDepths, Property::eInt, 1, true, "1" },
|
|
{ kOfxImageEffectPropSupportsMultipleClipPARs, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropSetableFrameRate, Property::eInt, 1, true, "0" },
|
|
{ kOfxImageEffectPropSetableFielding, Property::eInt, 1, true, "0" },
|
|
{ kOfxParamHostPropSupportsCustomInteract, Property::eInt, 1, true, "0" },
|
|
{ kOfxParamHostPropSupportsStringAnimation, Property::eInt, 1, true, "0" },
|
|
{ kOfxParamHostPropSupportsChoiceAnimation, Property::eInt, 1, true, "0" },
|
|
{ kOfxParamHostPropSupportsBooleanAnimation, Property::eInt, 1, true, "0" },
|
|
{ kOfxParamHostPropSupportsCustomAnimation, Property::eInt, 1, true, "0" },
|
|
{ kOfxPropHostOSHandle, Property::ePointer, 1, true, NULL },
|
|
#ifdef OFX_SUPPORTS_PARAMETRIC
|
|
{ kOfxParamHostPropSupportsParametricAnimation, Property::eInt, 1, true, "0"},
|
|
#endif
|
|
{ kOfxParamHostPropMaxParameters, Property::eInt, 1, true, "-1" },
|
|
{ kOfxParamHostPropMaxPages, Property::eInt, 1, true, "0" },
|
|
{ kOfxParamHostPropPageRowColumnCount, Property::eInt, 2, true, "0" },
|
|
{ kOfxImageEffectInstancePropSequentialRender, Property::eInt, 1, true, "0" }, // OFX 1.2
|
|
#ifdef OFX_SUPPORTS_OPENGLRENDER
|
|
{ kOfxImageEffectPropOpenGLRenderSupported, Property::eString, 1, true, "false"}, // OFX 1.3
|
|
{ kOfxImageEffectPropCudaRenderSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropCudaStreamSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropMetalRenderSupported, Property::eString, 1, false, "false" },
|
|
{ kOfxImageEffectPropOpenCLRenderSupported, Property::eString, 1, false, "false" },
|
|
#endif
|
|
{ kOfxImageEffectPropRenderQualityDraft, Property::eInt, 1, true, "0" }, // OFX 1.4
|
|
{ kOfxImageEffectHostPropNativeOrigin, Property::eString, 0, true, kOfxHostNativeOriginBottomLeft }, // OFX 1.4
|
|
Property::propSpecEnd
|
|
};
|
|
|
|
/// ctor
|
|
Host::Host()
|
|
{
|
|
/// add the properties for an image effect host, derived classs to set most of them
|
|
_properties.addProperties(hostStuffs);
|
|
}
|
|
|
|
/// optionally over-ridden function to register the creation of a new descriptor in the host app
|
|
void Host::initDescriptor(Descriptor* /*desc*/)
|
|
{
|
|
}
|
|
|
|
/// Use this in any dialogue etc... showing progress
|
|
void Host::loadingStatus(const std::string &)
|
|
{
|
|
}
|
|
|
|
bool Host::pluginSupported(ImageEffectPlugin */*plugin*/, std::string &/*reason*/) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// override this to use your own memory instance - must inherrit from memory::instance
|
|
Memory::Instance* Host::newMemoryInstance(size_t /*nBytes*/) {
|
|
return 0;
|
|
}
|
|
|
|
// return an memory::instance calls makeMemoryInstance that can be overriden
|
|
Memory::Instance* Host::imageMemoryAlloc(size_t nBytes){
|
|
Memory::Instance* instance = newMemoryInstance(nBytes);
|
|
if(instance)
|
|
return instance;
|
|
else{
|
|
Memory::Instance* instance = new Memory::Instance;
|
|
instance->alloc(nBytes);
|
|
return instance;
|
|
}
|
|
}
|
|
|
|
/// our suite fetcher
|
|
const void *Host::fetchSuite(const char *suiteName, int suiteVersion)
|
|
{
|
|
if (strcmp(suiteName, kOfxImageEffectSuite)==0) {
|
|
if(suiteVersion==1)
|
|
return (void *)&gImageEffectSuite;
|
|
else
|
|
return NULL;
|
|
}
|
|
else if (strcmp(suiteName, kOfxParameterSuite)==0) {
|
|
return Param::GetSuite(suiteVersion);
|
|
}
|
|
else if (strcmp(suiteName, kOfxMessageSuite)==0) {
|
|
// version 2 is backward-compatible
|
|
if(suiteVersion==1 || suiteVersion==2)
|
|
return (void *)&gMessageSuite;
|
|
else
|
|
return NULL;
|
|
}
|
|
else if (strcmp(suiteName, kOfxInteractSuite)==0) {
|
|
return Interact::GetSuite(suiteVersion);
|
|
}
|
|
else if (strcmp(suiteName, kOfxProgressSuite)==0) {
|
|
if(suiteVersion==1)
|
|
return (void*)&gProgressSuiteV1;
|
|
else if(suiteVersion==2)
|
|
return (void*)&gProgressSuiteV2;
|
|
else
|
|
return 0;
|
|
}
|
|
else if (strcmp(suiteName, kOfxTimeLineSuite)==0) {
|
|
if(suiteVersion==1)
|
|
return (void*)&gTimelineSuite;
|
|
else
|
|
return 0;
|
|
}
|
|
else if (strcmp(suiteName, kOfxMultiThreadSuite)==0) {
|
|
if(suiteVersion == 1)
|
|
return (void*)&gMultiThreadSuite;
|
|
else
|
|
return NULL;
|
|
}
|
|
# ifdef OFX_SUPPORTS_OPENGLRENDER
|
|
else if (strcmp(suiteName, kOfxOpenGLRenderSuite)==0) {
|
|
if(suiteVersion == 1)
|
|
return (void*)&gOpenGLRenderSuite;
|
|
else
|
|
return NULL;
|
|
}
|
|
# endif
|
|
# ifdef OFX_SUPPORTS_PARAMETRIC
|
|
else if (strcmp(suiteName, kOfxParametricParameterSuite)==0) {
|
|
return ParametricParam::GetSuite(suiteVersion);
|
|
}
|
|
# endif
|
|
else /// otherwise just grab the base class one, which is props and memory
|
|
return OFX::Host::Host::fetchSuite(suiteName, suiteVersion);
|
|
}
|
|
|
|
} // ImageEffect
|
|
|
|
} // Host
|
|
|
|
} // OFX
|