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