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.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 }