TuttleOFX  1
TuttleOFX/libraries/tuttle/src/tuttle/host/ofx/OfxhImageEffectNode.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License :
00003  *
00004  * Copyright (c) 2007-2009, The Open Effects Association Ltd. All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *
00009  * Redistributions of source code must retain the above copyright notice,
00010  *    this list of conditions and the following disclaimer.
00011  * Redistributions in binary form must reproduce the above copyright notice,
00012  *    this list of conditions and the following disclaimer in the documentation
00013  *    and/or other materials provided with the distribution.
00014  * Neither the name The Open Effects Association Ltd, nor the names of its
00015  *    contributors may be used to endorse or promote products derived from this
00016  *    software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00019  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00020  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00021  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
00022  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00023  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00024  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00025  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00026  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00027  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 // ofx host
00031 #include "OfxhBinary.hpp"
00032 #include "OfxhMemory.hpp"
00033 #include "OfxhImageEffectNode.hpp"
00034 #include "OfxhPluginAPICache.hpp"
00035 #include "OfxhPluginCache.hpp"
00036 #include "OfxhHost.hpp"
00037 #include "OfxhImageEffectPlugin.hpp"
00038 #include "OfxhUtilities.hpp"
00039 #include "attribute/OfxhClip.hpp"
00040 #include "attribute/OfxhParam.hpp"
00041 #include "attribute/OfxhParamGroup.hpp"
00042 #include "attribute/OfxhParamDouble.hpp"
00043 #include "property/OfxhSet.hpp"
00044 
00045 #include <tuttle/host/Core.hpp>
00046 
00047 // ofx
00048 #include <ofxCore.h>
00049 #include <ofxImageEffect.h>
00050 
00051 #include <cstring>
00052 #include <cstdarg>
00053 #include <cmath>
00054 
00055 namespace tuttle {
00056 namespace host {
00057 namespace ofx {
00058 namespace imageEffect {
00059 
00060 static const property::OfxhPropSpec effectInstanceStuff[] = {
00061         /* name                                 type                   dim.   r/o    default value */
00062         { kOfxPropType, property::ePropTypeString, 1, true, kOfxTypeImageEffectInstance },
00063         { kOfxPropName, property::ePropTypeString, 1, false, "UNIQUE_NAME_NOT_SET" },
00064         { kOfxImageEffectPropContext, property::ePropTypeString, 1, true, "" },
00065         { kOfxPropInstanceData, property::ePropTypePointer, 1, false, NULL },
00066         { kOfxImageEffectPropPluginHandle, property::ePropTypePointer, 1, false, NULL },
00067         { kOfxImageEffectPropProjectSize, property::ePropTypeDouble, 2, true, "0" },
00068         { kOfxImageEffectPropProjectOffset, property::ePropTypeDouble, 2, true, "0" },
00069         { kOfxImageEffectPropProjectExtent, property::ePropTypeDouble, 2, true, "0" },
00070         { kOfxImageEffectPropProjectPixelAspectRatio, property::ePropTypeDouble, 1, true, "0" },
00071         { kOfxImageEffectInstancePropEffectDuration, property::ePropTypeDouble, 1, true, "0" },
00072         { kOfxImageEffectInstancePropSequentialRender, property::ePropTypeInt, 1, false, "0" },
00073         { kOfxImageEffectPropFrameRate, property::ePropTypeDouble, 1, true, "0" },
00074         { kOfxPropIsInteractive, property::ePropTypeInt, 1, true, "0" },
00075         { 0 }
00076 };
00077 
00078 OfxhImageEffectNode::OfxhImageEffectNode( const OfxhImageEffectPlugin&         plugin,
00079                                           const OfxhImageEffectNodeDescriptor& descriptor,
00080                                           const std::string&                   context,
00081                                           bool                                 interactive )
00082         : OfxhImageEffectNodeBase( effectInstanceStuff )
00083         , _plugin( plugin )
00084         , _context( context )
00085         , _descriptor( descriptor )
00086         , _interactive( interactive )
00087         , _created( false )
00088         , _continuousSamples( false )
00089         , _frameVarying( false )
00090         , _outputFrameRate( 0 )
00091 {
00092         _properties.setChainedSet( &descriptor.getProperties() );
00093 
00094         _properties.setPointerProperty( kOfxImageEffectPropPluginHandle, const_cast<OfxPlugin*>( _plugin.getPluginLoadGuardPtr()->getOfxPlugin() ) );
00095 
00096         _properties.setStringProperty( kOfxImageEffectPropContext, context );
00097         _properties.setIntProperty( kOfxPropIsInteractive, interactive );
00098 
00099         // copy is sequential over
00100         bool sequential = descriptor.getProperties().getIntProperty( kOfxImageEffectInstancePropSequentialRender ) != 0;
00101         _properties.setIntProperty( kOfxImageEffectInstancePropSequentialRender, sequential );
00102 
00103         initHook();
00104 }
00105 
00106 void OfxhImageEffectNode::initHook()
00107 {
00108         int i = 0;
00109         
00110         while( effectInstanceStuff[i].name )
00111         {
00112                 // don't set hooks for context or isinteractive
00113                 if( strcmp( effectInstanceStuff[i].name, kOfxImageEffectPropContext ) &&
00114                     strcmp( effectInstanceStuff[i].name, kOfxPropIsInteractive ) &&
00115                     strcmp( effectInstanceStuff[i].name, kOfxImageEffectInstancePropSequentialRender ) )
00116                 {
00117                         const property::OfxhPropSpec& spec = effectInstanceStuff[i];
00118 
00119                         switch( spec.type )
00120                         {
00121                                 case property::ePropTypeDouble:
00122                                         _properties.setGetHook( spec.name, this );
00123                                         break;
00124                                 default:
00125                                         break;
00126                         }
00127                 }
00128                 ++i;
00129         }
00130 }
00131 
00132 OfxhImageEffectNode::OfxhImageEffectNode( const OfxhImageEffectNode& other )
00133         : OfxhImageEffectNodeBase( other.getProperties() )
00134         //, attribute::OfxhParamSet( other ) // done in populate function
00135         //, attribute::OfxhClipImageSet( other ) // done in populate function
00136         , _plugin( other.getPlugin() )
00137         , _context( other.getContext() )
00138         , _descriptor( other.getDescriptor() )
00139         , _interactive( other._interactive )
00140         , _created( false )
00141         , _continuousSamples( other._continuousSamples )
00142         , _frameVarying( other._frameVarying )
00143         , _outputFrameRate( other._outputFrameRate )
00144 {
00145         _properties.setChainedSet( &_descriptor.getProperties() );
00146         initHook();
00147 }
00148 
00149 /**
00150  * called after construction to populate clips and params
00151  */
00152 void OfxhImageEffectNode::populate()
00153 {
00154         populateClips( _descriptor );
00155         populateParams( _descriptor );
00156 }
00157 
00158 /**
00159  * @todo tuttle: move this in ParamSet !
00160  */
00161 void OfxhImageEffectNode::populateParams( const imageEffect::OfxhImageEffectNodeDescriptor& descriptor )
00162 {
00163         const attribute::OfxhParamSetDescriptor::ParamDescriptorList& paramDescriptors = _descriptor.getParamList();
00164 
00165         this->reserveParameters( paramDescriptors.size() );
00166 
00167         // Create parameters on their own groups
00168         for( attribute::OfxhParamSetDescriptor::ParamDescriptorList::const_iterator it = paramDescriptors.begin(), itEnd = paramDescriptors.end();
00169              it != itEnd;
00170              ++it )
00171         {
00172                 // SetInstance where the childrens param instances will be added
00173                 const attribute::OfxhParamDescriptor& descriptor = *it;
00174 
00175                 // get a param instance from a param descriptor.
00176                 attribute::OfxhParam* paramInstance = newParam( descriptor );
00177 
00178                 // This param instance is added into the right parent ParamSet.
00179                 const std::string parentName = descriptor.getParentName();
00180                 attribute::OfxhParamSet* parentParamSet = this;
00181                 // Find the parent group, by default it's the node itself
00182                 if( parentName != "" )
00183                 {
00184                         try
00185                         {
00186                                 attribute::OfxhParamGroup& parentGroup = dynamic_cast<attribute::OfxhParamGroup&>( getParam(parentName) );
00187                                 parentParamSet = &parentGroup;
00188                         }
00189                         catch(...)
00190                         {
00191                                 TUTTLE_LOG_WARNING(
00192                                         "The parameter " << quotes(descriptor.getName()) <<
00193                                         " is declared inside the group parameter " << quotes(parentName) <<
00194                                         ", but this group parameter is not recognized. "
00195                                         "This is probably an error in the plugin."
00196                                         );
00197                         }
00198                 }
00199                 // declare the new param instance to the corresponding paramGroup.
00200                 parentParamSet->declareChildParam( *paramInstance );
00201         }
00202 }
00203 
00204 /**
00205  * @todo tuttle check clip ? check hash ? etc.
00206  */
00207 bool OfxhImageEffectNode::operator==( const OfxhImageEffectNode& other ) const
00208 {
00209         if( OfxhImageEffectNodeBase::operator!=( other ) )
00210                 return false;
00211         if( attribute::OfxhParamSet::operator!=( other ) )
00212                 return false;
00213         if( attribute::OfxhClipImageSet::operator!=( other ) )
00214                 return false;
00215         return true;
00216 }
00217 
00218 /**
00219  * implemented for Param::SetDescriptor
00220  */
00221 property::OfxhSet& OfxhImageEffectNode::getParamSetProps()
00222 {
00223         return _properties;
00224 }
00225 
00226 // do nothing
00227 size_t OfxhImageEffectNode::getDimension( const std::string& name ) const OFX_EXCEPTION_SPEC
00228 {
00229         TUTTLE_LOG_ERROR( "failing in " << __PRETTY_FUNCTION__ << " with name=" << name );
00230         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrMissingHostFeature ) );
00231         return 0;
00232 }
00233 
00234 size_t OfxhImageEffectNode::upperGetDimension( const std::string& name )
00235 {
00236         return _properties.getDimension( name );
00237 }
00238 
00239 void OfxhImageEffectNode::notify( const std::string& name, bool singleValue, int indexOrN ) OFX_EXCEPTION_SPEC
00240 {
00241         TUTTLE_LOG_ERROR( "failing in " << __PRETTY_FUNCTION__ );
00242 }
00243 
00244 /**
00245  * don't know what to do
00246  */
00247 void OfxhImageEffectNode::reset( const std::string& name ) OFX_EXCEPTION_SPEC
00248 {
00249         TUTTLE_LOG_ERROR( "failing in " << __PRETTY_FUNCTION__ );
00250         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrMissingHostFeature ) );
00251 }
00252 
00253 /**
00254  * get the virtuals for viewport size, pixel scale, background colour
00255  */
00256 double OfxhImageEffectNode::getDoubleProperty( const std::string& name, int index ) const OFX_EXCEPTION_SPEC
00257 {
00258         if( name == kOfxImageEffectPropProjectSize )
00259         {
00260                 if( index >= 2 )
00261                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00262                 double values[2];
00263                 getProjectSize( values[0], values[1] );
00264                 return values[index];
00265         }
00266         else if( name == kOfxImageEffectPropProjectOffset )
00267         {
00268                 if( index >= 2 )
00269                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00270                 double values[2];
00271                 getProjectOffset( values[0], values[1] );
00272                 return values[index];
00273         }
00274         else if( name == kOfxImageEffectPropProjectExtent )
00275         {
00276                 if( index >= 2 )
00277                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00278                 double values[2];
00279                 getProjectExtent( values[0], values[1] );
00280                 return values[index];
00281         }
00282         else if( name == kOfxImageEffectPropProjectPixelAspectRatio )
00283         {
00284                 if( index >= 1 )
00285                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00286                 return getOutputPixelAspectRatio();
00287         }
00288         else if( name == kOfxImageEffectInstancePropEffectDuration )
00289         {
00290                 if( index >= 1 )
00291                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00292                 return getEffectDuration();
00293         }
00294         else if( name == kOfxImageEffectPropFrameRate )
00295         {
00296                 if( index >= 1 )
00297                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00298                 return getOutputFrameRate();
00299         }
00300         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrUnknown ) );
00301         return 0.0;
00302 }
00303 
00304 void OfxhImageEffectNode::getDoublePropertyN( const std::string& name, double* first, int n ) const OFX_EXCEPTION_SPEC
00305 {
00306         if( name == kOfxImageEffectPropProjectSize )
00307         {
00308                 if( n > 2 )
00309                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00310                 getProjectSize( first[0], first[1] );
00311         }
00312         else if( name == kOfxImageEffectPropProjectOffset )
00313         {
00314                 if( n > 2 )
00315                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00316                 getProjectOffset( first[0], first[1] );
00317         }
00318         else if( name == kOfxImageEffectPropProjectExtent )
00319         {
00320                 if( n > 2 )
00321                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00322                 getProjectExtent( first[0], first[1] );
00323         }
00324         else if( name == kOfxImageEffectPropProjectPixelAspectRatio )
00325         {
00326                 if( n > 1 )
00327                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00328                 *first = getOutputPixelAspectRatio();
00329         }
00330         else if( name == kOfxImageEffectInstancePropEffectDuration )
00331         {
00332                 if( n > 1 )
00333                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00334                 *first = getEffectDuration();
00335         }
00336         else if( name == kOfxImageEffectPropFrameRate )
00337         {
00338                 if( n > 1 )
00339                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrBadIndex ) );
00340                 *first = getOutputFrameRate();
00341         }
00342         else
00343                 BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrUnknown ) );
00344 }
00345 
00346 OfxhImageEffectNode::~OfxhImageEffectNode()
00347 {
00348         // destroy the instance, only if succesfully created
00349         if( _created )
00350         {
00351                 OfxStatus status = mainEntry( kOfxActionDestroyInstance, this->getHandle(), 0, 0 );
00352                 if( status != kOfxStatOK &&
00353                         status != kOfxStatReplyDefault )
00354                 {
00355                         TUTTLE_LOG_TRACE( "OFXh: Failed to destroy the effect instance. Status is " << mapStatusToString(status) << "." );
00356                 }
00357         }
00358 }
00359 
00360 /**
00361  * this is used to populate with any extra action in arguments that may be needed
00362  */
00363 void OfxhImageEffectNode::setCustomInArgs( const std::string& action, property::OfxhSet& inArgs ) const {}
00364 
00365 /**
00366  * this is used to populate with any extra action out arguments that may be needed
00367  */
00368 void OfxhImageEffectNode::setCustomOutArgs( const std::string& action, property::OfxhSet& outArgs ) const {}
00369 
00370 /**
00371  * this is used to populate with any extra action out arguments that may be needed
00372  */
00373 void OfxhImageEffectNode::examineOutArgs( const std::string& action, OfxStatus, const property::OfxhSet& outArgs ) const {}
00374 
00375 /**
00376  * check for connection
00377  */
00378 bool OfxhImageEffectNode::checkClipConnectionStatus() const
00379 {
00380         std::map<std::string, attribute::OfxhClipImage*>::const_iterator i;
00381         for( i = _clipImages.begin(); i != _clipImages.end(); ++i )
00382         {
00383                 if( !i->second->isOptional() && !i->second->isConnected() )
00384                 {
00385                         return false;
00386                 }
00387         }
00388         return true;
00389 }
00390 
00391 /**
00392  * override this to make processing abort, return 1 to abort processing
00393  *
00394    int OfxhImageEffectNode::abort()
00395    {
00396     return 0;
00397    }
00398  */
00399 
00400 /**
00401  * override this to use your own memory instance - must inherrit from memory::instance
00402  *
00403    OfxhMemory* OfxhImageEffectNode::newMemoryInstance( size_t nBytes )
00404    {
00405     OfxhMemory* instance = new OfxhMemory();
00406     instance->alloc( nBytes );
00407     return instance;
00408    }
00409  */
00410 
00411 /**
00412  * @return an memory::instance calls makeMemoryInstance that can be overriden
00413  */
00414 OfxhMemory* OfxhImageEffectNode::imageMemoryAlloc( size_t nBytes )
00415 {
00416         return newMemoryInstance( nBytes );
00417 }
00418 
00419 /**
00420  * call the effect entry point
00421  */
00422 OfxStatus OfxhImageEffectNode::mainEntry( const char*        action,
00423                                           const void*        handle,
00424                                           property::OfxhSet* inArgs,
00425                                           property::OfxhSet* outArgs ) const
00426 {
00427         const OfxhPluginLoadGuard* pHandle = _plugin.getPluginLoadGuardPtr();
00428 
00429         if( !pHandle )
00430                 return kOfxStatFailed;
00431 
00432         const OfxPlugin* ofxPlugin = pHandle->getOfxPlugin();
00433         if( !ofxPlugin )
00434                 return kOfxStatFailed;
00435 
00436         OfxPropertySetHandle inHandle = 0;
00437         if( inArgs )
00438         {
00439                 setCustomInArgs( action, *inArgs );
00440                 inHandle = inArgs->getHandle();
00441         }
00442 
00443         OfxPropertySetHandle outHandle = 0;
00444         if( outArgs )
00445         {
00446                 setCustomOutArgs( action, *outArgs );
00447                 outHandle = outArgs->getHandle();
00448         }
00449 
00450         OfxStatus stat = ofxPlugin->mainEntry( action, handle, inHandle, outHandle );
00451 
00452         if( outArgs )
00453                 examineOutArgs( action, stat, *outArgs );
00454 
00455         return stat;
00456 }
00457 
00458 /**
00459  * create a clip instance
00460  */
00461 void OfxhImageEffectNode::createInstanceAction() OFX_EXCEPTION_SPEC
00462 {
00463         /// we need to init the clips before we call create instance incase
00464         /// they try and fetch something in create instance, which they are allowed
00465         setDefaultClipPreferences();
00466 
00467         // now tell the plug-in to create instance
00468         OfxStatus status = mainEntry( kOfxActionCreateInstance, this->getHandle(), 0, 0 );
00469 
00470         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00471                 BOOST_THROW_EXCEPTION( OfxhException( status, "Create action failed on plugin " + getName() ) );
00472 
00473         _created = true;
00474 }
00475 
00476 /**
00477  * begin/change/end instance changed
00478  */
00479 void OfxhImageEffectNode::beginInstanceChangedAction( const std::string& why ) OFX_EXCEPTION_SPEC
00480 {
00481         property::OfxhPropSpec stuff[] = {
00482                 { kOfxPropChangeReason, property::ePropTypeString, 1, true, why.c_str() },
00483                 { 0 }
00484         };
00485 
00486         property::OfxhSet inArgs( stuff );
00487 
00488         OfxStatus status = mainEntry( kOfxActionBeginInstanceChanged, this->getHandle(), &inArgs, 0 );
00489 
00490         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00491                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00492 }
00493 
00494 void OfxhImageEffectNode::paramInstanceChangedAction( const std::string& paramName,
00495                                                       const std::string& why,
00496                                                       OfxTime            time,
00497                                                       OfxPointD          renderScale ) OFX_EXCEPTION_SPEC
00498 {
00499         if( getParamsByName()[paramName]->changedActionInProgress() )
00500         {
00501                 return;
00502         }
00503         getParamsByName()[paramName]->changedActionBegin();
00504         /*attribute::OfxhParam& param = */ getParam( paramName );
00505 
00506         if( isClipPreferencesSlaveParam( paramName ) )
00507                 _clipPrefsDirty = true;
00508 
00509         property::OfxhPropSpec stuff[] = {
00510                 { kOfxPropType, property::ePropTypeString, 1, true, kOfxTypeParameter },
00511                 { kOfxPropName, property::ePropTypeString, 1, true, paramName.c_str() },
00512                 { kOfxPropChangeReason, property::ePropTypeString, 1, true, why.c_str() },
00513                 { kOfxPropTime, property::ePropTypeDouble, 1, true, "0" },
00514                 { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
00515                 { 0 }
00516         };
00517 
00518         property::OfxhSet inArgs( stuff );
00519 
00520         // add the second dimension of the render scale
00521         inArgs.setDoubleProperty( kOfxPropTime, time );
00522 
00523         inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
00524 
00525         OfxStatus status = mainEntry( kOfxActionInstanceChanged, this->getHandle(), &inArgs, 0 );
00526 
00527         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00528                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00529 
00530         getParamsByName()[paramName]->changedActionEnd();
00531 
00532 }
00533 
00534 void OfxhImageEffectNode::clipInstanceChangedAction( const std::string& clipName,
00535                                                      const std::string& why,
00536                                                      OfxTime            time,
00537                                                      OfxPointD          renderScale ) OFX_EXCEPTION_SPEC
00538 {
00539         _clipPrefsDirty = true;
00540         ClipImageMap::iterator it = _clipImages.find( clipName );
00541         if( it == _clipImages.end() )
00542                 BOOST_THROW_EXCEPTION( OfxhException( kOfxStatFailed ) );
00543 
00544         property::OfxhPropSpec stuff[] = {
00545                 { kOfxPropType, property::ePropTypeString, 1, true, kOfxTypeClip },
00546                 { kOfxPropName, property::ePropTypeString, 1, true, it->second->getName().c_str() },
00547                 { kOfxPropChangeReason, property::ePropTypeString, 1, true, why.c_str() },
00548                 { kOfxPropTime, property::ePropTypeDouble, 1, true, "0" },
00549                 { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
00550                 { 0 }
00551         };
00552 
00553         property::OfxhSet inArgs( stuff );
00554 
00555         // add the second dimension of the render scale
00556         inArgs.setDoubleProperty( kOfxPropTime, time );
00557         inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
00558 
00559         OfxStatus status = mainEntry( kOfxActionInstanceChanged, getHandle(), &inArgs, 0 );
00560 
00561         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00562                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00563 }
00564 
00565 void OfxhImageEffectNode::endInstanceChangedAction( const std::string& why ) OFX_EXCEPTION_SPEC
00566 {
00567         property::OfxhPropSpec whyStuff[] = {
00568                 { kOfxPropChangeReason, property::ePropTypeString, 1, true, why.c_str() },
00569                 { 0 }
00570         };
00571 
00572         property::OfxhSet inArgs( whyStuff );
00573 
00574         OfxStatus status = mainEntry( kOfxActionEndInstanceChanged, this->getHandle(), &inArgs, 0 );
00575 
00576         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00577                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00578 }
00579 
00580 /**
00581  * purge plugin caches
00582  */
00583 void OfxhImageEffectNode::purgeCachesAction() OFX_EXCEPTION_SPEC
00584 {
00585         OfxStatus status = mainEntry( kOfxActionPurgeCaches, this->getHandle(), 0, 0 );
00586 
00587         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00588                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00589 }
00590 
00591 /**
00592  * sync plugin private data
00593  */
00594 void OfxhImageEffectNode::syncPrivateDataAction() OFX_EXCEPTION_SPEC
00595 {
00596         OfxStatus status = mainEntry( kOfxActionSyncPrivateData, this->getHandle(), 0, 0 );
00597 
00598         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00599                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00600 }
00601 
00602 /**
00603  * end edit instance
00604  */
00605 void OfxhImageEffectNode::beginInstanceEditAction() OFX_EXCEPTION_SPEC
00606 {
00607         OfxStatus status = mainEntry( kOfxActionBeginInstanceEdit, this->getHandle(), 0, 0 );
00608 
00609         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00610                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00611 }
00612 
00613 /**
00614  * end edit instance
00615  */
00616 void OfxhImageEffectNode::endInstanceEditAction() OFX_EXCEPTION_SPEC
00617 {
00618         OfxStatus status = mainEntry( kOfxActionEndInstanceEdit, this->getHandle(), 0, 0 );
00619 
00620         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00621                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00622 }
00623 
00624 void OfxhImageEffectNode::beginSequenceRenderAction( OfxTime   startFrame,
00625                                              OfxTime   endFrame,
00626                                              double    step,
00627                                              bool      interactive,
00628                                              OfxPointD renderScale ) OFX_EXCEPTION_SPEC
00629 {
00630         property::OfxhPropSpec stuff[] = {
00631                 { kOfxImageEffectPropFrameRange, property::ePropTypeDouble, 2, true, "0" },
00632                 { kOfxImageEffectPropFrameStep, property::ePropTypeDouble, 1, true, "0" },
00633                 { kOfxPropIsInteractive, property::ePropTypeInt, 1, true, "0" },
00634                 { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
00635                 { 0 }
00636         };
00637 
00638         property::OfxhSet inArgs( stuff );
00639 
00640         // set up second dimension for frame range and render scale
00641         inArgs.setDoubleProperty( kOfxImageEffectPropFrameRange, startFrame, 0 );
00642         inArgs.setDoubleProperty( kOfxImageEffectPropFrameRange, endFrame, 1 );
00643 
00644         inArgs.setDoubleProperty( kOfxImageEffectPropFrameStep, step );
00645 
00646         inArgs.setIntProperty( kOfxPropIsInteractive, interactive );
00647 
00648         inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
00649 
00650         OfxStatus status = mainEntry( kOfxImageEffectActionBeginSequenceRender, this->getHandle(), &inArgs, 0 );
00651 
00652         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00653                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00654 }
00655 
00656 void OfxhImageEffectNode::renderAction( OfxTime            time,
00657                                         const std::string& field,
00658                                         const OfxRectI&    renderWindow,
00659                                         OfxPointD          renderScale ) OFX_EXCEPTION_SPEC
00660 {
00661         static const property::OfxhPropSpec stuff[] = {
00662                 { kOfxPropTime, property::ePropTypeDouble, 1, true, "0" },
00663                 { kOfxImageEffectPropFieldToRender, property::ePropTypeString, 1, true, "" },
00664                 { kOfxImageEffectPropRenderWindow, property::ePropTypeInt, 4, true, "0" },
00665                 { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
00666                 { 0 }
00667         };
00668 
00669         property::OfxhSet inArgs( stuff );
00670 
00671         inArgs.setDoubleProperty( kOfxPropTime, time );
00672         inArgs.setStringProperty( kOfxImageEffectPropFieldToRender, field );
00673         inArgs.setIntPropertyN( kOfxImageEffectPropRenderWindow, &renderWindow.x1, 4 );
00674         inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
00675 
00676         //TUTTLE_TLOG( TUTTLE_INFO, "OfxhImageEffect::renderAction inArgs=" << inArgs );
00677 
00678         OfxStatus status = mainEntry( kOfxImageEffectActionRender, this->getHandle(), &inArgs, 0 );
00679 
00680         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00681                 BOOST_THROW_EXCEPTION( OfxhException( status, "Error in ActionRender on node \"" + this->getName() + "\" at time " + boost::lexical_cast<std::string>( time ) + "." ) );
00682 }
00683 
00684 void OfxhImageEffectNode::endSequenceRenderAction( OfxTime   startFrame,
00685                                            OfxTime   endFrame,
00686                                            double    step,
00687                                            bool      interactive,
00688                                            OfxPointD renderScale ) OFX_EXCEPTION_SPEC
00689 {
00690         property::OfxhPropSpec stuff[] = {
00691                 { kOfxImageEffectPropFrameRange, property::ePropTypeDouble, 2, true, "0" },
00692                 { kOfxImageEffectPropFrameStep, property::ePropTypeDouble, 1, true, "0" },
00693                 { kOfxPropIsInteractive, property::ePropTypeInt, 1, true, "0" },
00694                 { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
00695                 { 0 }
00696         };
00697 
00698         property::OfxhSet inArgs( stuff );
00699 
00700         inArgs.setDoubleProperty( kOfxImageEffectPropFrameStep, step );
00701 
00702         inArgs.setDoubleProperty( kOfxImageEffectPropFrameRange, startFrame, 0 );
00703         inArgs.setDoubleProperty( kOfxImageEffectPropFrameRange, endFrame, 1 );
00704         inArgs.setIntProperty( kOfxPropIsInteractive, interactive );
00705         inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
00706 
00707         OfxStatus status = mainEntry( kOfxImageEffectActionEndSequenceRender, this->getHandle(), &inArgs, 0 );
00708 
00709         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00710                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
00711 }
00712 
00713 OfxRectD OfxhImageEffectNode::computeClipRodUnionAtTimes( const attribute::OfxhClipImage& clip, const TimesSet& timesSet ) const
00714 {       
00715         OfxRectD rod = { 0, 0, 0, 0 };
00716         bool gotOne = false;
00717         BOOST_FOREACH( const TimesSet::value_type& inTime, timesSet )
00718         {
00719                 const OfxRectD localRod = clip.fetchRegionOfDefinition( inTime );
00720                 if( !gotOne )
00721                 {
00722                         rod = localRod;
00723                         gotOne = true;
00724                 }
00725                 else
00726                 {
00727                         rod = rectUnion( rod, localRod );
00728                 }
00729         }
00730         return rod;
00731 }
00732 
00733 
00734 /**
00735  * calculate the default rod for this effect instance
00736  */
00737 OfxRectD OfxhImageEffectNode::calcDefaultRegionOfDefinition( OfxTime   time,
00738                                                              OfxPointD renderScale ) const
00739 {
00740         ClipTimesSetMap timesSetMap = getFramesNeeded( time ); /// @todo: do not recompute this here
00741         
00742 //      TUTTLE_TLOG( TUTTLE_INFO, "calcDefaultRegionOfDefinition" );
00743 //      TUTTLE_TLOG_VAR( TUTTLE_INFO, _context );
00744         OfxRectD rod = { 0, 0, 0, 0 };
00745         try
00746         {
00747                 // figure out the default contexts
00748                 if( _context == kOfxImageEffectContextGenerator ||
00749                         _context == kOfxImageEffectContextReader )
00750                 {
00751                         // generator is the extent
00752                         rod.x1 = rod.y1 = 0;
00753                         getProjectExtent( rod.x2, rod.y2 );
00754                         const attribute::OfxhClipImage& clip = getClip( kOfxImageEffectOutputClipName );
00755                         rod = clip.fetchRegionOfDefinition( time );
00756                         /// @todo tuttle: maybe RoD problems with Generator and Read here... to check !
00757                 }
00758                 else if( _context == kOfxImageEffectContextFilter ||
00759                         _context == kOfxImageEffectContextPaint  ||
00760                         _context == kOfxImageEffectContextWriter )
00761                 {
00762                         // filter and paint default to the input clip
00763                         const attribute::OfxhClipImage& clip = getClip( kOfxImageEffectSimpleSourceClipName );
00764 
00765                         // depending on framesNeeded
00766                         rod = computeClipRodUnionAtTimes( clip, timesSetMap[clip.getName()] );
00767                 }
00768                 else if( _context == kOfxImageEffectContextTransition )
00769                 {
00770                         // transition is the union of the two clips
00771                         const attribute::OfxhClipImage& clipFrom = getClip( kOfxImageEffectTransitionSourceFromClipName );
00772                         const attribute::OfxhClipImage& clipTo   = getClip( kOfxImageEffectTransitionSourceToClipName );
00773 
00774                         rod = computeClipRodUnionAtTimes( clipFrom, timesSetMap[clipFrom.getName()] );
00775                         rod = rectUnion( rod, computeClipRodUnionAtTimes( clipTo, timesSetMap[clipTo.getName()] ) );
00776                 }
00777                 else if( _context == kOfxImageEffectContextGeneral )
00778                 {
00779                         // general context is the union of all the non optional clips
00780                         bool gotOne = false;
00781                         BOOST_FOREACH( const ClipImageMap::value_type& clipPair, _clipImages )
00782                         {
00783                                 attribute::OfxhClipImage* clip = clipPair.second;
00784                                 if( ! clip->isOutput() && clip->isConnected() )
00785                                 {
00786                                         // depending on framesNeeded
00787                                         const TimesSet& timesSet = timesSetMap[clip->getName()];
00788                                         if( timesSet.size() == 0 )
00789                                                 continue; // the plugin don't use this input (is it allowed by the standard?)
00790                                         BOOST_FOREACH( const TimesSet::value_type& inTime, timesSet )
00791                                         {
00792                                                 const OfxRectD localRod = clip->fetchRegionOfDefinition( inTime );
00793                                                 if( !gotOne )
00794                                                 {
00795                                                         rod = localRod;
00796                                                         gotOne = true;
00797                                                 }
00798                                                 else
00799                                                 {
00800                                                         rod = rectUnion( rod, localRod );
00801                                                 }
00802                                         }
00803                                 }
00804                         }
00805 
00806                         if( !gotOne )
00807                         {
00808                                 // no non optionals? then be the extent
00809                                 rod.x1 = rod.y1 = 0;
00810                                 getProjectExtent( rod.x2, rod.y2 );
00811                         }
00812 
00813                 }
00814                 else if( _context == kOfxImageEffectContextRetimer )
00815                 {
00816                         // retimer
00817                         const attribute::OfxhClipImage& clip = getClip( kOfxImageEffectSimpleSourceClipName );
00818                         /*attribute::ParamDoubleInstance& retimerParam = */ dynamic_cast<const attribute::OfxhParamDouble&>( getParam( kOfxImageEffectRetimerParamName ) );
00819                         rod = computeClipRodUnionAtTimes( clip, timesSetMap[clip.getName()] );
00820                 }
00821         }
00822         catch( boost::exception& e )
00823         {
00824                 if( ! boost::get_error_info<exception::dev>( e ) )
00825                         e << exception::dev() + "Error while computing the default ROD.";
00826                 e << exception::ofxContext( _context );
00827                 e << exception::pluginIdentifier( getPlugin().getIdentifier() );
00828                 throw;
00829         }
00830         return rod;
00831 }
00832 
00833 /**
00834  * RoD call
00835  */
00836 void OfxhImageEffectNode::getRegionOfDefinitionAction( OfxTime   time,
00837                                                        OfxPointD renderScale,
00838                                                        OfxRectD& rod ) const OFX_EXCEPTION_SPEC
00839 {
00840         property::OfxhPropSpec inStuff[] = {
00841                 { kOfxPropTime, property::ePropTypeDouble, 1, true, "0" },
00842                 { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
00843                 { 0 }
00844         };
00845 
00846         property::OfxhPropSpec outStuff[] = {
00847                 { kOfxImageEffectPropRegionOfDefinition, property::ePropTypeDouble, 4, false, "0" },
00848                 { 0 }
00849         };
00850 
00851         property::OfxhSet inArgs( inStuff );
00852         property::OfxhSet outArgs( outStuff );
00853 
00854         inArgs.setDoubleProperty( kOfxPropTime, time );
00855 
00856         inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
00857 
00858         OfxStatus stat = mainEntry( kOfxImageEffectActionGetRegionOfDefinition,
00859                                     this->getHandle(),
00860                                     &inArgs,
00861                                     &outArgs );
00862 
00863         if( stat == kOfxStatOK )
00864         {
00865                 outArgs.getDoublePropertyN( kOfxImageEffectPropRegionOfDefinition, &rod.x1, 4 );
00866         }
00867         else if( stat == kOfxStatReplyDefault )
00868         {
00869                 rod = calcDefaultRegionOfDefinition( time, renderScale );
00870         }
00871         else
00872         {
00873                 // defined to process sequences with hole
00874                 // find a best way to do this ??
00875                 if( _context == kOfxImageEffectContextReader )
00876                         BOOST_THROW_EXCEPTION( tuttle::exception::FileInSequenceNotExist() );
00877                 
00878                 BOOST_THROW_EXCEPTION( OfxhException( stat, "getRegionOfDefinitionAction error." ) );
00879         }
00880 }
00881 
00882 /**
00883  * get the region of interest for each input and return it in the given std::map
00884  */
00885 void OfxhImageEffectNode::getRegionOfInterestAction( OfxTime time,
00886                                                      OfxPointD renderScale,
00887                                                      const OfxRectD& roi,
00888                                                      std::map<attribute::OfxhClipImage*, OfxRectD>& rois ) const OFX_EXCEPTION_SPEC
00889 {
00890         // reset the map
00891         rois.clear();
00892 
00893         ClipTimesSetMap timesSetMap = getFramesNeeded( time );
00894         
00895         if( !supportsTiles() )
00896         {
00897                 /// No tiling support on the effect at all. So set the roi of each input clip to be the RoD of that clip.
00898                 BOOST_FOREACH( const ClipImageMap::value_type& clip, _clipImages )
00899                 {
00900                         if( ! clip.second->isOutput() ||
00901                             getContext() == kOfxImageEffectContextGenerator ||
00902                             getContext() == kOfxImageEffectContextReader )
00903                         {
00904                                 if( clip.second->isOutput() || clip.second->isConnected() ) // needed to be able to fetch the RoD
00905                                 {
00906                                         /// @todo tuttle: how to support size on generators... check if this is correct in all cases.
00907                                         OfxRectD roi = clip.second->fetchRegionOfDefinition( time );
00908                                         rois[clip.second] = roi;
00909                                 }
00910                         }
00911                 }
00912         }
00913         else
00914         {
00915                 /// set up the in args
00916                 static property::OfxhPropSpec inStuff[] = {
00917                         { kOfxPropTime, property::ePropTypeDouble, 1, true, "0" },
00918                         { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
00919                         { kOfxImageEffectPropRegionOfInterest, property::ePropTypeDouble, 4, true, 0 },
00920                         { 0 }
00921                 };
00922                 property::OfxhSet inArgs( inStuff );
00923 
00924                 inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
00925                 inArgs.setDoubleProperty( kOfxPropTime, time );
00926                 inArgs.setDoublePropertyN( kOfxImageEffectPropRegionOfInterest, &roi.x1, 4 );
00927 
00928                 property::OfxhSet outArgs;
00929                 std::list<std::string> keepPropNamesOwnership;
00930                 
00931                 BOOST_FOREACH( const ClipImageMap::value_type& clip, _clipImages )
00932                 {
00933                         if( ! clip.second->isOutput() ||
00934                             getContext() == kOfxImageEffectContextGenerator ||
00935                             getContext() == kOfxImageEffectContextReader )
00936                         {
00937                                 keepPropNamesOwnership.push_back( "OfxImageClipPropRoI_" + clip.first );
00938                                 const std::string& name = keepPropNamesOwnership.back();
00939 
00940                                 property::OfxhPropSpec s;
00941                                 s.name         = name.c_str();
00942                                 s.type         = property::ePropTypeDouble;
00943                                 s.dimension    = 4;
00944                                 s.readonly     = false;
00945                                 s.defaultValue = "";
00946                                 outArgs.createProperty( s );
00947 
00948                                 /// initialise to the default
00949                                 outArgs.setDoublePropertyN( s.name, &roi.x1, 4 );
00950                         }
00951                 }
00952 
00953                 /// call the action
00954                 const OfxStatus status = mainEntry( kOfxImageEffectActionGetRegionsOfInterest,
00955                                               this->getHandle(),
00956                                               &inArgs,
00957                                               &outArgs );
00958 
00959                 if( status != kOfxStatOK && status != kOfxStatReplyDefault )
00960                         BOOST_THROW_EXCEPTION( OfxhException( status ) );
00961 
00962                 /// set the thing up
00963                 BOOST_FOREACH( const ClipImageMap::value_type& clip, _clipImages )
00964                 {
00965                         if( ! clip.second->isOutput() ||
00966                             getContext() == kOfxImageEffectContextGenerator ||
00967                             getContext() == kOfxImageEffectContextReader )
00968                         {
00969                                 if( clip.second->isOutput() || clip.second->isConnected() ) // needed to be able to fetch the RoD
00970                                 {
00971                                         // depending on framesNeeded !
00972                                         const TimesSet& timesSet = timesSetMap[clip.second->getName()];
00973                                         if( timesSet.size() == 0 )
00974                                                 continue; // the plugin don't use this input (is it allowed by the standard?)
00975                                         // use intersection?
00976                                         OfxRectD inputsRodIntersection = clip.second->fetchRegionOfDefinition( *(timesSet.begin()) );
00977                                         BOOST_FOREACH( const TimesSet::value_type& inTime, timesSet )
00978                                         {
00979                                                 const OfxRectD timeRod = clip.second->fetchRegionOfDefinition( inTime );
00980                                                 inputsRodIntersection = clamp( timeRod, inputsRodIntersection );
00981                                         }
00982                                         if( clip.second->supportsTiles() )
00983                                         {
00984                                                 const std::string name = "OfxImageClipPropRoI_" + clip.first;
00985                                                 OfxRectD thisRoi;
00986                                                 thisRoi.x1 = outArgs.getDoubleProperty( name, 0 );
00987                                                 thisRoi.y1 = outArgs.getDoubleProperty( name, 1 );
00988                                                 thisRoi.x2 = outArgs.getDoubleProperty( name, 2 );
00989                                                 thisRoi.y2 = outArgs.getDoubleProperty( name, 3 );
00990 
00991                                                 // clamp it to the clip's rod
00992                                                 thisRoi          = clamp( thisRoi, inputsRodIntersection );
00993                                                 rois[clip.second] = thisRoi;
00994                                         }
00995                                         else
00996                                         {
00997                                                 /// not supporting tiles on this input, so set it to the rod
00998                                                 rois[clip.second] = inputsRodIntersection;
00999                                         }
01000                                 }
01001                         }
01002                 }
01003         }
01004 }
01005 
01006 /**
01007  * see how many frames are needed from each clip to render the indicated frame
01008  */
01009 void OfxhImageEffectNode::getFramesNeededAction( OfxTime   time,
01010                                                 ClipRangeMap& rangeMap ) const OFX_EXCEPTION_SPEC
01011 {
01012         OfxStatus status = kOfxStatReplyDefault;
01013         property::OfxhSet outArgs;
01014         std::list<std::string> keepPropNamesOwnership;
01015 
01016         if( temporalAccess() )
01017         {
01018                 property::OfxhPropSpec inStuff[] = {
01019                         { kOfxPropTime, property::ePropTypeDouble, 1, true, "0" },
01020                         { 0 }
01021                 };
01022                 property::OfxhSet inArgs( inStuff );
01023                 inArgs.setDoubleProperty( kOfxPropTime, time );
01024 
01025                 for( ClipImageMap::const_iterator it = _clipImages.begin(), itEnd = _clipImages.end();
01026                      it != itEnd;
01027                      ++it )
01028                 {
01029                         if( !it->second->isOutput() )
01030                         {
01031                                 keepPropNamesOwnership.push_back( "OfxImageClipPropFrameRange_" + it->first );
01032                                 const std::string& name = keepPropNamesOwnership.back();
01033 
01034                                 property::OfxhPropSpec s;
01035                                 s.name         = name.c_str();
01036                                 s.type         = property::ePropTypeDouble;
01037                                 s.dimension    = 0;
01038                                 s.readonly     = false;
01039                                 s.defaultValue = "";
01040                                 outArgs.createProperty( s );
01041                                 /// intialise it to the current frame
01042                                 outArgs.setDoubleProperty( name, time, 0 );
01043                                 outArgs.setDoubleProperty( name, time, 1 );
01044                         }
01045                 }
01046 
01047                 status = mainEntry( kOfxImageEffectActionGetFramesNeeded,
01048                                     this->getHandle(),
01049                                     &inArgs,
01050                                     &outArgs );
01051 
01052                 if( status != kOfxStatOK && status != kOfxStatReplyDefault )
01053                         BOOST_THROW_EXCEPTION( OfxhException( status ) );
01054         }
01055 
01056         OfxRangeD defaultRange;
01057         defaultRange.min = defaultRange.max = time;
01058 
01059         for( ClipImageMap::const_iterator it = _clipImages.begin(), itEnd = _clipImages.end();
01060              it != itEnd;
01061              ++it )
01062         {
01063                 attribute::OfxhClipImage* clip = it->second;
01064 
01065                 if( !clip->isOutput() )
01066                 {
01067                         if( status == kOfxStatReplyDefault )
01068                         {
01069                                 rangeMap[clip].push_back( defaultRange );
01070                         }
01071                         else
01072                         {
01073                                 const std::string name = "OfxImageClipPropFrameRange_" + it->first;
01074 
01075                                 int nRanges = outArgs.getDimension( name );
01076                                 if( nRanges % 2 != 0 )
01077                                         BOOST_THROW_EXCEPTION( OfxhException( kOfxStatErrValue, "Frame range needs to be divisible by 2." ) );
01078 
01079                                 if( nRanges == 0 )
01080                                 {
01081                                         rangeMap[clip].push_back( defaultRange );
01082                                 }
01083                                 else
01084                                 {
01085                                         for( int r = 0; r < nRanges; )
01086                                         {
01087                                                 double min = outArgs.getDoubleProperty( name, r );
01088                                                 double max = outArgs.getDoubleProperty( name, r + 1 );
01089                                                 r += 2;
01090 
01091                                                 OfxRangeD range;
01092                                                 range.min = min;
01093                                                 range.max = max;
01094                                                 rangeMap[clip].push_back( range );
01095                                         }
01096                                 }
01097                         }
01098                 }
01099         }
01100 }
01101 
01102 OfxhImageEffectNode::ClipTimesSetMap OfxhImageEffectNode::getFramesNeeded( const OfxTime time ) const
01103 {
01104         const bool temporalClipAccess = this->getProperties().fetchIntProperty( kOfxImageEffectPropTemporalClipAccess ).getValue();
01105         ClipTimesSetMap result;
01106         if( temporalClipAccess )
01107         {
01108                 ClipRangeMap clipMap;
01109                 getFramesNeededAction( time, clipMap );
01110                 BOOST_FOREACH( const ClipRangeMap::value_type& v, clipMap )
01111                 {
01112                         BOOST_FOREACH( const ClipRangeMap::value_type::second_type::value_type& range, v.second )
01113                         {
01114                                 for( OfxTime t = range.min; t <= range.max; t += 1.0 )
01115                                 {
01116                                         result[ v.first->getName() ].insert(t);
01117                                 }
01118                         }
01119                 }
01120         }
01121         else
01122         {
01123                 BOOST_FOREACH( ClipImageVector::const_reference v, _clipsByOrder )
01124                 {
01125                         result[v.getName()].insert(time);
01126                 }
01127         }
01128         return result;
01129 }
01130 
01131 bool OfxhImageEffectNode::isIdentityAction( OfxTime&           time,
01132                                             const std::string& field,
01133                                             const OfxRectI&    renderWindow,
01134                                             OfxPointD          renderScale,
01135                                             std::string&       clip ) const OFX_EXCEPTION_SPEC
01136 {
01137         static property::OfxhPropSpec inStuff[] = {
01138                 { kOfxPropTime, property::ePropTypeDouble, 1, true, "0" },
01139                 { kOfxImageEffectPropFieldToRender, property::ePropTypeString, 1, true, "" },
01140                 { kOfxImageEffectPropRenderWindow, property::ePropTypeInt, 4, true, "0" },
01141                 { kOfxImageEffectPropRenderScale, property::ePropTypeDouble, 2, true, "0" },
01142                 { 0 }
01143         };
01144 
01145         static property::OfxhPropSpec outStuff[] = {
01146                 { kOfxPropTime, property::ePropTypeDouble, 1, false, "0.0" },
01147                 { kOfxPropName, property::ePropTypeString, 1, false, "" },
01148                 { 0 }
01149         };
01150 
01151         property::OfxhSet inArgs( inStuff );
01152 
01153         inArgs.setStringProperty( kOfxImageEffectPropFieldToRender, field );
01154         inArgs.setDoubleProperty( kOfxPropTime, time );
01155         inArgs.setIntPropertyN( kOfxImageEffectPropRenderWindow, &renderWindow.x1, 4 );
01156         inArgs.setDoublePropertyN( kOfxImageEffectPropRenderScale, &renderScale.x, 2 );
01157 
01158         property::OfxhSet outArgs( outStuff );
01159 
01160         outArgs.setDoubleProperty( kOfxPropTime, time );
01161 
01162         OfxStatus status = mainEntry( kOfxImageEffectActionIsIdentity,
01163                                       this->getHandle(),
01164                                       &inArgs,
01165                                       &outArgs );
01166 
01167         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
01168                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
01169 
01170         time = outArgs.getDoubleProperty( kOfxPropTime );
01171         clip = outArgs.getStringProperty( kOfxPropName );
01172 
01173         return status == kOfxStatOK;
01174 }
01175 
01176 /**
01177  * Get whether the component is a supported 'chromatic' component (RGBA or alpha) in
01178  * the base API.
01179  * Override this if you have extended your chromatic colour types (eg RGB) and want
01180  * the clip preferences logic to still work
01181  */
01182 bool OfxhImageEffectNode::isChromaticComponent( const std::string& str ) const
01183 {
01184         if( str == kOfxImageComponentRGBA )
01185                 return true;
01186         if( str == kOfxImageComponentRGB )
01187                 return true;
01188         if( str == kOfxImageComponentAlpha )
01189                 return true;
01190         return false;
01191 }
01192 
01193 /**
01194  * function to check for multiple bit depth support
01195  * The answer will depend on host, plugin and context
01196  */
01197 bool OfxhImageEffectNode::canCurrentlyHandleMultipleClipDepths() const
01198 {
01199         /// does the host support 'em
01200         bool hostSupports = tuttle::host::core().getHost().getProperties().getIntProperty( kOfxImageEffectPropSupportsMultipleClipDepths ) != 0;
01201 
01202         /// does the plug-in support 'em
01203         bool pluginSupports = supportsMultipleClipDepths();
01204 
01205         /// no support, so no
01206         if( !hostSupports || !pluginSupports )
01207                 return false;
01208 
01209         // in the standard, it's written that only general context can handle multiple clip depth...
01210         // but we remove this restriction for filters and generators (to allow input bitdepth != output bitdepth)
01211         //      if( _context == kOfxImageEffectContextFilter || _context == kOfxImageEffectContextGenerator )
01212         //              return false;
01213         if(
01214             _context == kOfxImageEffectContextTransition ||
01215             _context == kOfxImageEffectContextPaint ||
01216             _context == kOfxImageEffectContextRetimer )
01217                 return false;
01218 
01219         return true;
01220 }
01221 
01222 /**
01223  * Setup the default clip preferences on the clips
01224  */
01225 void OfxhImageEffectNode::setDefaultClipPreferences()
01226 {
01227         /// is there multiple bit depth support? Depends on host, plugin and context
01228         bool multiBitDepth = canCurrentlyHandleMultipleClipDepths();
01229 
01230         /// OK find the deepest chromatic component on our input clips and the one with the
01231         /// most components
01232         std::string deepestBitDepth = kOfxBitDepthNone;
01233         std::string mostComponents  = kOfxImageComponentNone;
01234         double frameRate            = 0.0;
01235         double defaultPixelAspectRatio = 0.0;
01236         std::string premult         = kOfxImageOpaque;
01237 
01238         for( std::map<std::string, attribute::OfxhClipImage*>::iterator it = _clipImages.begin();
01239              it != _clipImages.end();
01240              it++ )
01241         {
01242                 attribute::OfxhClipImage* clip = it->second;
01243 
01244                 // If input clip
01245                 if( !clip->isOutput() )
01246                 {
01247                         if( clip->isConnected() )
01248                         {
01249                                 frameRate = maximum( frameRate, clip->getFrameRate() );
01250                         }
01251                         defaultPixelAspectRatio = std::max( defaultPixelAspectRatio, clip->getPixelAspectRatio() );
01252 
01253                         std::string rawComp = clip->getUnmappedComponents();
01254                         rawComp = clip->findSupportedComp( rawComp ); // turn that into a comp the plugin expects on that clip
01255 
01256                         const std::string& rawDepth   = clip->getUnmappedBitDepth();
01257                         const std::string& rawPreMult = clip->getPremult();
01258 
01259                         if( isChromaticComponent( rawComp ) )
01260                         {
01261                                 if( rawPreMult == kOfxImagePreMultiplied )
01262                                         premult = kOfxImagePreMultiplied;
01263                                 else if( rawPreMult == kOfxImageUnPreMultiplied && premult != kOfxImagePreMultiplied )
01264                                         premult = kOfxImageUnPreMultiplied;
01265                                 deepestBitDepth = findDeepestBitDepth( deepestBitDepth, rawDepth );
01266                                 mostComponents  = findMostChromaticComponents( mostComponents, rawComp );
01267                         }
01268                 }
01269         }
01270 
01271         // default value if the generator don't set the framerate
01272         if( frameRate == 0.0 )
01273                 frameRate = 25.0;
01274         if( defaultPixelAspectRatio == 0.0 )
01275                 defaultPixelAspectRatio = 1.0;
01276         
01277         /// set some stuff up
01278         _outputFrameRate         = frameRate;
01279         _outputFielding          = getDefaultOutputFielding();
01280         _outputPreMultiplication = premult;
01281         _continuousSamples       = false;
01282         _frameVarying            = false;
01283         getOutputOfxhClip().setPixelAspectRatio( defaultPixelAspectRatio, property::eModifiedByHost );
01284 
01285         /// now find the best depth that the plugin supports
01286         deepestBitDepth = bestSupportedBitDepth( deepestBitDepth );
01287 
01288         /// now add the clip gubbins to the out args
01289         for( std::map<std::string, attribute::OfxhClipImage*>::iterator it = _clipImages.begin();
01290              it != _clipImages.end();
01291              ++it )
01292         {
01293                 attribute::OfxhClipImage* clip = it->second;
01294 
01295                 std::string rawComp = clip->getUnmappedComponents();
01296                 rawComp = clip->findSupportedComp( rawComp ); // turn that into a comp the plugin expects on that clip
01297                 const std::string& rawDepth = clip->getUnmappedBitDepth();
01298                 if( isChromaticComponent( rawComp ) )
01299                 {
01300                         if( clip->isOutput() )
01301                         {
01302                                 std::string depth = deepestBitDepth;
01303                                 std::string comp  = clip->findSupportedComp( mostComponents );
01304                                 clip->setBitDepthString( depth );
01305                                 clip->setComponentsString( comp );
01306                         }
01307                         else
01308                         {
01309                                 std::string comp  = rawComp;
01310                                 std::string depth = multiBitDepth ? bestSupportedBitDepth( rawDepth ) : deepestBitDepth;
01311 
01312                                 clip->setBitDepthString( depth );
01313                                 clip->setComponentsString( comp );
01314                         }
01315                 }
01316                 else
01317                 {
01318                         /// hmm custom component type, don't touch it and pass it through
01319                         clip->setBitDepthString( rawDepth );
01320                         clip->setComponentsString( rawComp );
01321                 }
01322         }
01323 }
01324 
01325 /**
01326  * Initialise the clip preferences arguments, override this to do
01327  * stuff with wierd components etc...
01328  */
01329 void OfxhImageEffectNode::setupClipPreferencesArgs( property::OfxhSet& outArgs, std::list<std::string>& outKeepPropNamesOwnership )
01330 {
01331         // http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html#ImageEffectClipPreferences
01332         
01333         /// reset all the clip prefs stuff to their defaults
01334         setDefaultClipPreferences();
01335 
01336         static property::OfxhPropSpec clipPrefsStuffs [] = {
01337                 { kOfxImageEffectPropFrameRate, property::ePropTypeDouble, 1, false, "1" },
01338                 { kOfxImageEffectPropPreMultiplication, property::ePropTypeString, 1, false, "" },
01339                 { kOfxImageClipPropFieldOrder, property::ePropTypeString, 1, false, "" },
01340                 { kOfxImageClipPropContinuousSamples, property::ePropTypeInt, 1, false, "0" },
01341                 { kOfxImageEffectFrameVarying, property::ePropTypeInt, 1, false, "0" },
01342                 { 0 }
01343         };
01344 
01345         outArgs.addProperties( clipPrefsStuffs );
01346 
01347         /// set the default for those
01348 
01349         /// is there multiple bit depth support? Depends on host, plugin and context
01350         bool multiBitDepth = canCurrentlyHandleMultipleClipDepths();
01351 
01352         outArgs.setStringProperty( kOfxImageClipPropFieldOrder, _outputFielding );
01353         outArgs.setStringProperty( kOfxImageEffectPropPreMultiplication, _outputPreMultiplication );
01354         outArgs.setDoubleProperty( kOfxImageEffectPropFrameRate, _outputFrameRate );
01355 
01356         /// now add the clip gubbins to the out args
01357         for( std::map<std::string, attribute::OfxhClipImage*>::iterator it = _clipImages.begin();
01358              it != _clipImages.end();
01359              ++it )
01360         {
01361                 attribute::OfxhClipImage* clip = it->second;
01362 
01363                 outKeepPropNamesOwnership.push_back( "OfxImageClipPropComponents_" + it->first );
01364                 const std::string& componentParamName = outKeepPropNamesOwnership.back();
01365                 property::OfxhPropSpec specComp = { componentParamName.c_str(), property::ePropTypeString, 0, false, "" }; // note the support for multi-planar clips
01366                 outArgs.createProperty( specComp );
01367                 // as it is variable dimension, there is no default value, so we have to set it explicitly
01368                 outArgs.setStringProperty( componentParamName, clip->getComponentsString() );
01369 
01370                 outKeepPropNamesOwnership.push_back( "OfxImageClipPropDepth_" + it->first );
01371                 const std::string& depthParamName = outKeepPropNamesOwnership.back();
01372                 property::OfxhPropSpec specDep = { depthParamName.c_str(), property::ePropTypeString, 1, !multiBitDepth, clip->getBitDepthString().c_str() };
01373                 outArgs.createProperty( specDep );
01374                 outArgs.setStringProperty( depthParamName, clip->getBitDepthString() );
01375 
01376                 outKeepPropNamesOwnership.push_back( "OfxImageClipPropPAR_" + it->first );
01377                 const std::string& parParamName = outKeepPropNamesOwnership.back();
01378                 property::OfxhPropSpec specPAR = { parParamName.c_str(), property::ePropTypeDouble, 1, false, "1" };
01379                 outArgs.createProperty( specPAR );
01380                 outArgs.setDoubleProperty( parParamName, clip->getPixelAspectRatio() );
01381         }
01382 }
01383 
01384 bool OfxhImageEffectNode::isLeafNode() const
01385 {
01386         for( ClipImageMap::const_iterator it = _clipImages.begin();
01387              it != _clipImages.end();
01388              ++it )
01389         {
01390                 attribute::OfxhClipImage* clip = it->second;
01391 
01392                 if( ! clip->isOutput() && clip->isConnected() )
01393                         return false;
01394         }
01395         return true;
01396 }
01397 
01398 void OfxhImageEffectNode::setupClipInstancePreferences( property::OfxhSet& outArgs )
01399 {
01400         const bool isLeaf = isLeafNode();
01401 
01402         for( ClipImageMap::iterator it = _clipImages.begin();
01403              it != _clipImages.end();
01404              ++it )
01405         {
01406                 attribute::OfxhClipImage* clip = it->second;
01407 
01408                 // Properties setup
01409                 const std::string componentParamName = "OfxImageClipPropComponents_" + it->first;
01410                 const std::string depthParamName     = "OfxImageClipPropDepth_" + it->first;
01411                 const std::string parParamName       = "OfxImageClipPropPAR_" + it->first;
01412 
01413                 const property::String& propPixelDepth = outArgs.fetchStringProperty( depthParamName );
01414                 clip->setBitDepthString( propPixelDepth.getValue(), propPixelDepth.getModifiedBy() );
01415 
01416                 const property::String& propComponent = outArgs.fetchStringProperty( componentParamName );
01417                 clip->setComponentsString( propComponent.getValue(), propComponent.getModifiedBy() );
01418 
01419                 const property::Double& propPixelAspectRatio = outArgs.fetchDoubleProperty( parParamName );
01420                 clip->setPixelAspectRatio( propPixelAspectRatio.getValue(), propPixelAspectRatio.getModifiedBy() );
01421 
01422                 if( isLeaf &&
01423                         it->first == kOfxImageEffectOutputClipName &&
01424                         propPixelDepth.getModifiedBy() != property::eModifiedByPlugin &&
01425                         propPixelDepth.getValue() == kOfxBitDepthNone
01426                   )
01427                 {
01428                         // If a generator node (even if declared in the general context) don't set the
01429                         // bitdepth... we set an arbitrary default value.
01430                         clip->setBitDepthString( kOfxBitDepthFloat, property::eModifiedByHost );
01431                 }
01432         }
01433         
01434         _outputFrameRate         = outArgs.getDoubleProperty( kOfxImageEffectPropFrameRate );
01435         _outputFielding          = outArgs.getStringProperty( kOfxImageClipPropFieldOrder );
01436         _outputPreMultiplication = outArgs.getStringProperty( kOfxImageEffectPropPreMultiplication );
01437         _continuousSamples       = outArgs.getIntProperty( kOfxImageClipPropContinuousSamples ) != 0;
01438         _frameVarying            = outArgs.getIntProperty( kOfxImageEffectFrameVarying ) != 0;
01439 }
01440 
01441 /**
01442  * the idea here is the clip prefs live as active props on the effect
01443  * and are set up by clip preferences. The action manages the clip
01444  * preferences bits. We also monitor clip and param changes and
01445  * flag when clip prefs is dirty.
01446  * call the clip preferences action
01447  */
01448 void OfxhImageEffectNode::getClipPreferencesAction() OFX_EXCEPTION_SPEC
01449 {
01450         /// create the out args with the stuff that does not depend on individual clips
01451         property::OfxhSet outArgs;
01452         std::list<std::string> keepPropNamesOwnership;
01453 
01454         
01455         setupClipPreferencesArgs( outArgs, keepPropNamesOwnership );
01456 
01457         const OfxStatus status = mainEntry( kOfxImageEffectActionGetClipPreferences,
01458                                       this->getHandle(),
01459                                       0,
01460                                       &outArgs );
01461 
01462         if( status != kOfxStatOK &&
01463             status != kOfxStatReplyDefault )
01464         {
01465                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
01466         }
01467         
01468         // Setup members data from loaded properties
01469         setupClipInstancePreferences( outArgs );
01470 
01471         _clipPrefsDirty = false;
01472 }
01473 
01474 /**
01475  * find the most chromatic components out of the two. Override this if you define
01476  * more chromatic components
01477  */
01478 const std::string& OfxhImageEffectNode::findMostChromaticComponents( const std::string& a, const std::string& b ) const
01479 {
01480         if( a == kOfxImageComponentNone )
01481                 return b;
01482         if( a == kOfxImageComponentRGBA )
01483                 return a;
01484         if( b == kOfxImageComponentRGBA )
01485                 return b;
01486         if( a == kOfxImageComponentRGB )
01487                 return a;
01488         if( b == kOfxImageComponentRGB )
01489                 return b;
01490         return a;
01491 }
01492 
01493 /**
01494  * given the bit depth, find the best match for it.
01495  */
01496 const std::string& OfxhImageEffectNode::bestSupportedBitDepth( const std::string& depth ) const
01497 {
01498         static const std::string none( kOfxBitDepthNone );
01499         static const std::string bytes( kOfxBitDepthByte );
01500         static const std::string shorts( kOfxBitDepthShort );
01501         static const std::string floats( kOfxBitDepthFloat );
01502 
01503         if( depth == none )
01504                 return none;
01505 
01506         if( isBitDepthSupported( depth ) )
01507                 return depth;
01508 
01509         if( depth == floats )
01510         {
01511                 if( isBitDepthSupported( shorts ) )
01512                         return shorts;
01513                 if( isBitDepthSupported( bytes ) )
01514                         return bytes;
01515         }
01516 
01517         if( depth == shorts )
01518         {
01519                 if( isBitDepthSupported( floats ) )
01520                         return floats;
01521                 if( isBitDepthSupported( bytes ) )
01522                         return bytes;
01523         }
01524 
01525         if( depth == bytes )
01526         {
01527                 if( isBitDepthSupported( shorts ) )
01528                         return shorts;
01529                 if( isBitDepthSupported( floats ) )
01530                         return floats;
01531         }
01532 
01533         /// Something wrong here
01534         return none;
01535 }
01536 
01537 bool OfxhImageEffectNode::getTimeDomainAction( OfxRangeD& range ) const OFX_EXCEPTION_SPEC
01538 {
01539         property::OfxhPropSpec outStuff[] = {
01540                 { kOfxImageEffectPropFrameRange, property::ePropTypeDouble, 2, false, "0.0" },
01541                 { 0 }
01542         };
01543 
01544         property::OfxhSet outArgs( outStuff );
01545         outArgs.setDoubleProperty( kOfxImageEffectPropFrameRange, range.min, 0 );
01546         outArgs.setDoubleProperty( kOfxImageEffectPropFrameRange, range.max, 1 );
01547 
01548         OfxStatus status = mainEntry( kOfxImageEffectActionGetTimeDomain,
01549                                       this->getHandle(),
01550                                       0,
01551                                       &outArgs );
01552 
01553         if( status != kOfxStatOK && status != kOfxStatReplyDefault )
01554                 BOOST_THROW_EXCEPTION( OfxhException( status ) );
01555 
01556         range.min = outArgs.getDoubleProperty( kOfxImageEffectPropFrameRange, 0 );
01557         range.max = outArgs.getDoubleProperty( kOfxImageEffectPropFrameRange, 1 );
01558 
01559         if( status == kOfxStatOK )
01560                 return true;
01561 
01562         return false;
01563 }
01564 
01565 /**
01566  * implemented for Param::SetInstance
01567  */
01568 void OfxhImageEffectNode::paramChanged( const attribute::OfxhParam& param, const attribute::EChange change )
01569 {
01570         if( change == attribute::eChangeNone )
01571                 return;
01572 
01573         const std::string changeStr = attribute::mapChangeEnumToString( change );
01574         const double frame          = getFrameRecursive();
01575         OfxPointD renderScale;
01576 
01577         getRenderScaleRecursive( renderScale.x, renderScale.y );
01578 
01579         beginInstanceChangedAction( changeStr );
01580         paramInstanceChangedAction( param.getName(), changeStr, frame, renderScale );
01581         endInstanceChangedAction( changeStr );
01582 }
01583 
01584 }
01585 }
01586 }
01587 }