TuttleOFX
1
|
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 }