TuttleOFX
1
|
00001 /* 00002 * OFX Support Library, a library that skins the OFX plug-in API with C++ classes. 00003 * Copyright (C) 2004-2007 The Open Effects Association Ltd 00004 * Author Bruno Nicoletti bruno@thefoundry.co.uk 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 * The Open Effects Association Ltd 00030 * 1 Wardour St 00031 * London W1D 6PA 00032 * England 00033 * 00034 * 00035 */ 00036 00037 /** @brief This file contains code that skins the ofx effect suite */ 00038 00039 #include "ofxsSupportPrivate.h" 00040 #include "ofxsUtilities.h" 00041 00042 #include <tuttle/common/utils/backtrace.hpp> 00043 #include <tuttle/common/exceptions.hpp> 00044 00045 #include <boost/numeric/conversion/cast.hpp> 00046 #include <boost/lexical_cast.hpp> 00047 #include <boost/foreach.hpp> 00048 #include <boost/exception/diagnostic_information.hpp> 00049 00050 #include <algorithm> // for find 00051 #include <iostream> 00052 #include <sstream> 00053 #include <string> 00054 00055 /** @brief The core 'OFX Support' namespace, used by plugin implementations. All code for these are defined in the common support libraries. */ 00056 namespace OFX { 00057 00058 FramesNeededSetter::~FramesNeededSetter() {} 00059 RegionOfInterestSetter::~RegionOfInterestSetter() {} 00060 00061 00062 // globals to keep consistent data structures around. 00063 OFX::PluginFactoryArray plugIDs; 00064 //Put it all into a map, so we know when to delete what! 00065 struct OfxPlugInfo 00066 { 00067 OfxPlugInfo() 00068 : _factory(NULL) 00069 , _plug(NULL) 00070 {} 00071 OfxPlugInfo( OFX::PluginFactory* f, OfxPlugin* p ) 00072 : _factory( f ) 00073 , _plug( p ) 00074 {} 00075 OFX::PluginFactory* _factory; 00076 OfxPlugin* _plug; 00077 }; 00078 00079 typedef std::map<std::string, OfxPlugInfo> OfxPlugInfoMap; 00080 typedef std::vector<OfxPlugin*> OfxPluginArray; 00081 00082 namespace Private { 00083 OfxPlugInfoMap plugInfoMap; 00084 OfxPluginArray ofxPlugs; 00085 00086 /** @brief the global host description */ 00087 ImageEffectHostDescription gHostDescription; 00088 bool gHostDescriptionHasInit = false; 00089 00090 00091 /** @brief Keeps count of how many times load/unload have been called */ 00092 int gLoadCount = 0; 00093 bool gHasInit = false; 00094 00095 // Suite and host pointers 00096 OfxHost* gHost = NULL; 00097 OfxImageEffectSuiteV1* gEffectSuite = NULL; 00098 OfxPropertySuiteV1* gPropSuite = NULL; 00099 OfxInteractSuiteV1* gInteractSuite = NULL; 00100 OfxParameterSuiteV1* gParamSuite = NULL; 00101 OfxMemorySuiteV1* gMemorySuite = NULL; 00102 OfxMultiThreadSuiteV1* gThreadSuite = NULL; 00103 OfxMessageSuiteV1* gMessageSuite = NULL; 00104 OfxProgressSuiteV1* gProgressSuite = NULL; 00105 OfxTimeLineSuiteV1* gTimeLineSuite = NULL; 00106 OfxParametricParameterSuiteV1* gParametricParameterSuite = NULL; 00107 NukeOfxCameraSuiteV1* gCameraParameterSuite = NULL; 00108 00109 // @brief the set of descriptors, one per context used by kOfxActionDescribeInContext, 00110 //'eContextNone' is the one used by the kOfxActionDescribe 00111 EffectDescriptorMap gEffectDescriptors; 00112 } // namespace Private 00113 00114 00115 ImageEffectHostDescription* getImageEffectHostDescription() 00116 { 00117 if( Private::gHostDescriptionHasInit ) 00118 return &Private::gHostDescription; 00119 return NULL; 00120 } 00121 00122 00123 /** @brief map a std::string to a context */ 00124 EContext mapContextStringToEnum( const std::string& s ) 00125 { 00126 if( s == kOfxImageEffectContextGenerator ) 00127 return eContextGenerator; 00128 if( s == kOfxImageEffectContextFilter ) 00129 return eContextFilter; 00130 if( s == kOfxImageEffectContextTransition ) 00131 return eContextTransition; 00132 if( s == kOfxImageEffectContextPaint ) 00133 return eContextPaint; 00134 if( s == kOfxImageEffectContextGeneral ) 00135 return eContextGeneral; 00136 if( s == kOfxImageEffectContextRetimer ) 00137 return eContextRetimer; 00138 if( s == kOfxImageEffectContextReader ) 00139 return eContextReader; 00140 if( s == kOfxImageEffectContextWriter ) 00141 return eContextWriter; 00142 OFX::Log::error( true, "Unknown image effect context '%s'", s.c_str() ); 00143 BOOST_THROW_EXCEPTION( std::invalid_argument( s ) ); 00144 return eContextGeneral; 00145 } 00146 00147 /** @brief map a std::string to a context */ 00148 const std::string mapContextEnumToString( const EContext s ) 00149 { 00150 switch( s ) 00151 { 00152 case eContextGenerator: 00153 return kOfxImageEffectContextGenerator; 00154 case eContextFilter: 00155 return kOfxImageEffectContextFilter; 00156 case eContextTransition: 00157 return kOfxImageEffectContextTransition; 00158 case eContextPaint: 00159 return kOfxImageEffectContextPaint; 00160 case eContextGeneral: 00161 return kOfxImageEffectContextGeneral; 00162 case eContextRetimer: 00163 return kOfxImageEffectContextRetimer; 00164 case eContextReader: 00165 return kOfxImageEffectContextReader; 00166 case eContextWriter: 00167 return kOfxImageEffectContextWriter; 00168 case eContextNone: 00169 return "ContextNone..."; 00170 } 00171 OFX::Log::error( true, "Unknown image effect context enum '%d'", (int)s ); 00172 BOOST_THROW_EXCEPTION( std::invalid_argument( "Unknown image effect context enum." ) ); 00173 return ""; 00174 } 00175 00176 const std::string mapMessageTypeEnumToString( const OFX::Message::EMessageType type ) 00177 { 00178 if( type == OFX::Message::eMessageFatal ) 00179 return kOfxMessageFatal; 00180 else if( type == OFX::Message::eMessageError ) 00181 return kOfxMessageError; 00182 else if( type == OFX::Message::eMessageMessage ) 00183 return kOfxMessageMessage; 00184 else if( type == OFX::Message::eMessageLog ) 00185 return kOfxMessageLog; 00186 else if( type == OFX::Message::eMessageQuestion ) 00187 return kOfxMessageQuestion; 00188 OFX::Log::error( true, "Unknown message type enum '%d'", type ); 00189 return 0; 00190 } 00191 00192 OFX::Message::EMessageReply mapMessageReplyStatusToEnum( const OfxStatus stat ) 00193 { 00194 if( stat == kOfxStatOK ) 00195 return OFX::Message::eMessageReplyOK; 00196 else if( stat == kOfxStatReplyYes ) 00197 return OFX::Message::eMessageReplyYes; 00198 else if( stat == kOfxStatReplyNo ) 00199 return OFX::Message::eMessageReplyNo; 00200 else if( stat == kOfxStatFailed ) 00201 return OFX::Message::eMessageReplyFailed; 00202 OFX::Log::error( true, "Unknown message reply status value '%d'", stat ); 00203 return OFX::Message::eMessageReplyFailed; 00204 } 00205 00206 OfxStatus mapMessageReplyEnumToStatus( const OFX::Message::EMessageReply stat ) 00207 { 00208 switch( stat ) 00209 { 00210 case OFX::Message::eMessageReplyOK: 00211 return kOfxStatOK; 00212 case OFX::Message::eMessageReplyYes: 00213 return kOfxStatReplyYes; 00214 case OFX::Message::eMessageReplyNo: 00215 return kOfxStatReplyNo; 00216 case OFX::Message::eMessageReplyFailed: 00217 return kOfxStatFailed; 00218 } 00219 OFX::Log::error( true, "Unknown message reply status enum '%d'", stat ); 00220 return kOfxStatFailed; 00221 } 00222 00223 /** @brief map a std::string to a context */ 00224 InstanceChangeReason mapInstanceChangedReasonStringToEnum( const std::string& s ) 00225 { 00226 if( s == kOfxChangePluginEdited ) 00227 return eChangePluginEdit; 00228 if( s == kOfxChangeUserEdited ) 00229 return eChangeUserEdit; 00230 if( s == kOfxChangeTime ) 00231 return eChangeTime; 00232 OFX::Log::error( true, "Unknown instance changed reason '%s'", s.c_str() ); 00233 BOOST_THROW_EXCEPTION( std::invalid_argument( s ) ); 00234 return eChangePluginEdit; 00235 } 00236 00237 /** @brief turns a bit depth string into and enum */ 00238 EBitDepth mapBitDepthStringToEnum( const std::string& str ) 00239 { 00240 if( str == kOfxBitDepthByte ) 00241 { 00242 return eBitDepthUByte; 00243 } 00244 else if( str == kOfxBitDepthShort ) 00245 { 00246 return eBitDepthUShort; 00247 } 00248 else if( str == kOfxBitDepthFloat ) 00249 { 00250 return eBitDepthFloat; 00251 } 00252 else if( str == kOfxBitDepthNone ) 00253 { 00254 return eBitDepthNone; 00255 } 00256 else 00257 { 00258 return eBitDepthCustom; 00259 } 00260 } 00261 00262 const std::string mapBitDepthEnumToString( const EBitDepth e ) 00263 { 00264 switch(e) 00265 { 00266 case eBitDepthUByte: 00267 return kOfxBitDepthByte; 00268 case eBitDepthUShort: 00269 return kOfxBitDepthShort; 00270 case eBitDepthFloat: 00271 return kOfxBitDepthFloat; 00272 case eBitDepthNone: 00273 return kOfxBitDepthNone; 00274 case eBitDepthCustom: 00275 return "eBitDepthCustom"; 00276 } 00277 BOOST_THROW_EXCEPTION( std::invalid_argument( "BitDepth enum: " + boost::lexical_cast<std::string>(e) ) ); 00278 return kOfxBitDepthNone; 00279 } 00280 00281 /** @brief turns a pixel component string into and enum */ 00282 EPixelComponent mapPixelComponentStringToEnum( const std::string& str ) 00283 { 00284 if( str == kOfxImageComponentRGBA ) 00285 { 00286 return ePixelComponentRGBA; 00287 } 00288 else if( str == kOfxImageComponentRGB ) 00289 { 00290 return ePixelComponentRGB; 00291 } 00292 else if( str == kOfxImageComponentAlpha ) 00293 { 00294 return ePixelComponentAlpha; 00295 } 00296 else if( str == kOfxImageComponentNone ) 00297 { 00298 return ePixelComponentNone; 00299 } 00300 else 00301 { 00302 return ePixelComponentCustom; 00303 } 00304 } 00305 00306 std::string mapPixelComponentEnumToString( const EPixelComponent e ) 00307 { 00308 switch(e) 00309 { 00310 case ePixelComponentRGBA: 00311 return kOfxImageComponentRGBA; 00312 case ePixelComponentRGB: 00313 return kOfxImageComponentRGB; 00314 case ePixelComponentAlpha: 00315 return kOfxImageComponentAlpha; 00316 case ePixelComponentNone: 00317 return kOfxImageComponentNone; 00318 case ePixelComponentCustom: 00319 return "ePixelComponentCustom"; 00320 } 00321 BOOST_THROW_EXCEPTION( std::invalid_argument( "EPixelComponent: " + boost::lexical_cast<std::string>(e) ) ); 00322 } 00323 00324 /** @brief turns a premultiplication string into and enum */ 00325 EPreMultiplication mapPreMultiplicationStringToEnum( const std::string& str ) 00326 { 00327 if( str == kOfxImageOpaque ) 00328 { 00329 return eImageOpaque; 00330 } 00331 else if( str == kOfxImagePreMultiplied ) 00332 { 00333 return eImagePreMultiplied; 00334 } 00335 else if( str == kOfxImageUnPreMultiplied ) 00336 { 00337 return eImageUnPreMultiplied; 00338 } 00339 BOOST_THROW_EXCEPTION( std::invalid_argument( str ) ); 00340 return eImageOpaque; 00341 } 00342 00343 std::string mapPreMultiplicationEnumToString( const EPreMultiplication e ) 00344 { 00345 switch( e ) 00346 { 00347 case eImageOpaque: 00348 return kOfxImageOpaque; 00349 case eImagePreMultiplied: 00350 return kOfxImagePreMultiplied; 00351 case eImageUnPreMultiplied: 00352 return kOfxImageUnPreMultiplied; 00353 } 00354 BOOST_THROW_EXCEPTION( std::invalid_argument( "PreMultiplicationEnum: " + boost::lexical_cast<std::string>(e) ) ); 00355 return ""; 00356 } 00357 00358 /** @brief turns a field string into and enum */ 00359 EField mapFieldStringToEnum( const std::string& str ) 00360 { 00361 if( str == kOfxImageFieldNone ) 00362 { 00363 return eFieldNone; 00364 } 00365 else if( str == kOfxImageFieldBoth ) 00366 { 00367 return eFieldBoth; 00368 } 00369 else if( str == kOfxImageFieldLower ) 00370 { 00371 return eFieldLower; 00372 } 00373 else if( str == kOfxImageFieldUpper ) 00374 { 00375 return eFieldUpper; 00376 } 00377 BOOST_THROW_EXCEPTION( std::invalid_argument( str ) ); 00378 return eFieldNone; 00379 } 00380 00381 std::string mapFieldEnumToString( const EField e ) 00382 { 00383 switch( e ) 00384 { 00385 case eFieldNone: 00386 return kOfxImageFieldNone; 00387 case eFieldBoth: 00388 return kOfxImageFieldBoth; 00389 case eFieldLower: 00390 return kOfxImageFieldLower; 00391 case eFieldUpper: 00392 return kOfxImageFieldUpper; 00393 } 00394 BOOST_THROW_EXCEPTION( std::invalid_argument( boost::lexical_cast<std::string>(e) ) ); 00395 return ""; 00396 } 00397 00398 //////////////////////////////////////////////////////////////////////////////// 00399 // clip descriptor 00400 00401 /** @brief hidden constructor */ 00402 ClipDescriptor::ClipDescriptor( const std::string& name, OfxPropertySetHandle props ) 00403 : _clipName( name ), 00404 _clipProps( props ) 00405 { 00406 OFX::Validation::validateClipDescriptorProperties( props ); 00407 } 00408 00409 /** @brief set the label properties */ 00410 void ClipDescriptor::setLabels( const std::string& label, const std::string& shortLabel, const std::string& longLabel ) 00411 { 00412 _clipProps.propSetString( kOfxPropLabel, label ); 00413 _clipProps.propSetString( kOfxPropShortLabel, shortLabel, false ); 00414 _clipProps.propSetString( kOfxPropLongLabel, longLabel, false ); 00415 } 00416 00417 /** @brief set how fielded images are extracted from the clip defaults to eFieldExtractDoubled */ 00418 void ClipDescriptor::setFieldExtraction( EFieldExtraction v ) 00419 { 00420 switch( v ) 00421 { 00422 case eFieldExtractBoth: 00423 _clipProps.propSetString( kOfxImageClipPropFieldExtraction, kOfxImageFieldBoth ); 00424 break; 00425 00426 case eFieldExtractSingle: 00427 _clipProps.propSetString( kOfxImageClipPropFieldExtraction, kOfxImageFieldSingle ); 00428 break; 00429 00430 case eFieldExtractDoubled: 00431 _clipProps.propSetString( kOfxImageClipPropFieldExtraction, kOfxImageFieldDoubled ); 00432 break; 00433 } 00434 } 00435 00436 /** @brief set which components are supported, defaults to none set, this must be called at least once! */ 00437 void ClipDescriptor::addSupportedComponent( EPixelComponent v ) 00438 { 00439 int n = _clipProps.propGetDimension( kOfxImageEffectPropSupportedComponents ); 00440 00441 switch( v ) 00442 { 00443 case ePixelComponentRGBA: 00444 _clipProps.propSetString( kOfxImageEffectPropSupportedComponents, kOfxImageComponentRGBA, n ); 00445 break; 00446 case ePixelComponentRGB: 00447 _clipProps.propSetString( kOfxImageEffectPropSupportedComponents, kOfxImageComponentRGB, n ); 00448 break; 00449 case ePixelComponentAlpha: 00450 _clipProps.propSetString( kOfxImageEffectPropSupportedComponents, kOfxImageComponentAlpha, n ); 00451 break; 00452 case ePixelComponentCustom: 00453 break; 00454 case ePixelComponentNone: 00455 _clipProps.propSetString( kOfxImageEffectPropSupportedComponents, kOfxImageComponentNone, n ); 00456 break; 00457 } 00458 } 00459 00460 /** @brief set which components are supported, defaults to none set, this must be called at least once! */ 00461 void ClipDescriptor::addSupportedComponent( const std::string& comp ) 00462 { 00463 int n = _clipProps.propGetDimension( kOfxImageEffectPropSupportedComponents ); 00464 00465 _clipProps.propSetString( kOfxImageEffectPropSupportedComponents, comp, n ); 00466 00467 } 00468 00469 /** @brief say whether we are going to do random temporal access on this clip, defaults to false */ 00470 void ClipDescriptor::setTemporalClipAccess( bool v ) 00471 { 00472 _clipProps.propSetInt( kOfxImageEffectPropTemporalClipAccess, int(v) ); 00473 } 00474 00475 /** @brief say whether if the clip is optional, defaults to false */ 00476 void ClipDescriptor::setOptional( bool v ) 00477 { 00478 _clipProps.propSetInt( kOfxImageClipPropOptional, int(v) ); 00479 } 00480 00481 /** @brief say whether this clip supports tiling, defaults to true */ 00482 void ClipDescriptor::setSupportsTiles( bool v ) 00483 { 00484 _clipProps.propSetInt( kOfxImageEffectPropSupportsTiles, int(v) ); 00485 } 00486 00487 /** @brief say whether this clip is a 'mask', so the host can know to replace with a roto or similar, defaults to false */ 00488 void ClipDescriptor::setIsMask( bool v ) 00489 { 00490 _clipProps.propSetInt( kOfxImageClipPropIsMask, int(v) ); 00491 } 00492 00493 //////////////////////////////////////////////////////////////////////////////// 00494 // image effect descriptor 00495 00496 /** @brief effect descriptor ctor */ 00497 ImageEffectDescriptor::ImageEffectDescriptor( OfxImageEffectHandle handle ) 00498 : _effectHandle( handle ) 00499 { 00500 // fetch the property set handle of the effect 00501 OfxPropertySetHandle props; 00502 OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet( handle, &props ); 00503 00504 throwSuiteStatusException( stat ); 00505 _effectProps.propSetHandle( props ); 00506 00507 OFX::Validation::validatePluginDescriptorProperties( props ); 00508 00509 // fetch the param set handle and set it in our ParamSetDescriptor base 00510 OfxParamSetHandle paramSetHandle; 00511 stat = OFX::Private::gEffectSuite->getParamSet( handle, ¶mSetHandle ); 00512 throwSuiteStatusException( stat ); 00513 setOfxParamSetHandle( paramSetHandle ); 00514 } 00515 00516 /** @brief dtor */ 00517 ImageEffectDescriptor::~ImageEffectDescriptor() 00518 { 00519 // delete any clip descriptors we may have constructed 00520 std::map<std::string, ClipDescriptor*>::iterator iter; 00521 for( iter = _definedClips.begin(); iter != _definedClips.end(); ++iter ) 00522 { 00523 if( iter->second ) 00524 { 00525 delete iter->second; 00526 iter->second = NULL; 00527 } 00528 } 00529 } 00530 00531 /** @brief, set the label properties in a plugin */ 00532 void ImageEffectDescriptor::setLabels( const std::string& label, const std::string& shortLabel, const std::string& longLabel ) 00533 { 00534 _effectProps.propSetString( kOfxPropLabel, label ); 00535 _effectProps.propSetString( kOfxPropShortLabel, shortLabel, false ); 00536 _effectProps.propSetString( kOfxPropLongLabel, longLabel, false ); 00537 } 00538 00539 void ImageEffectDescriptor::setDescription( const std::string& description ) 00540 { 00541 // added in openfx API 1.2, so do not throw if unknown by the host 00542 _effectProps.propSetString( kOfxPropPluginDescription, description, false ); 00543 } 00544 00545 /** @brief Set the plugin grouping */ 00546 void ImageEffectDescriptor::setPluginGrouping( const std::string& group ) 00547 { 00548 _effectProps.propSetString( kOfxImageEffectPluginPropGrouping, group ); 00549 } 00550 00551 /** @brief Add a context to those supported */ 00552 void ImageEffectDescriptor::addSupportedContext( EContext v ) 00553 { 00554 int n = _effectProps.propGetDimension( kOfxImageEffectPropSupportedContexts ); 00555 00556 switch( v ) 00557 { 00558 case eContextGenerator: 00559 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextGenerator, n ); 00560 break; 00561 case eContextFilter: 00562 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextFilter, n ); 00563 break; 00564 case eContextTransition: 00565 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextTransition, n ); 00566 break; 00567 case eContextPaint: 00568 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextPaint, n ); 00569 break; 00570 case eContextGeneral: 00571 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextGeneral, n ); 00572 break; 00573 case eContextRetimer: 00574 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextRetimer, n ); 00575 break; 00576 case eContextReader: 00577 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextReader, n ); 00578 break; 00579 case eContextWriter: 00580 _effectProps.propSetString( kOfxImageEffectPropSupportedContexts, kOfxImageEffectContextWriter, n ); 00581 break; 00582 case eContextNone: 00583 break; 00584 } 00585 } 00586 00587 void ImageEffectDescriptor::setOverlayInteractDescriptor(EffectInteractWrap* desc) 00588 { 00589 _overlayDescriptor.reset( desc ); 00590 if( OFX::Private::gHostDescription.supportsOverlays && desc->getMainEntry() ) 00591 _effectProps.propSetPointer( kOfxImageEffectPluginPropOverlayInteractV1, (void*)desc->getMainEntry() ); 00592 } 00593 00594 /** @brief Add a pixel depth to those supported */ 00595 void ImageEffectDescriptor::addSupportedBitDepth( EBitDepth v ) 00596 { 00597 int n = _effectProps.propGetDimension( kOfxImageEffectPropSupportedPixelDepths ); 00598 00599 switch( v ) 00600 { 00601 case eBitDepthUByte: 00602 _effectProps.propSetString( kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthByte, n ); 00603 break; 00604 case eBitDepthUShort: 00605 _effectProps.propSetString( kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthShort, n ); 00606 break; 00607 case eBitDepthFloat: 00608 _effectProps.propSetString( kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthFloat, n ); 00609 break; 00610 case eBitDepthNone: 00611 _effectProps.propSetString( kOfxImageEffectPropSupportedPixelDepths, kOfxBitDepthNone, n ); 00612 break; 00613 case eBitDepthCustom: 00614 // what the good way ? 00615 break; 00616 } 00617 } 00618 00619 /** @brief Add a file extension to those supported */ 00620 void ImageEffectDescriptor::addSupportedExtension( const std::string& extension ) 00621 { 00622 // only Tuttle support this property ( out of standard ) 00623 if( OFX::Private::gHostDescription.hostName == "TuttleOfx" ) 00624 { 00625 const int n = _effectProps.propGetDimension( kTuttleOfxImageEffectPropSupportedExtensions ); 00626 _effectProps.propSetString( kTuttleOfxImageEffectPropSupportedExtensions, extension, n ); 00627 } 00628 } 00629 00630 void ImageEffectDescriptor::addSupportedExtensions( const std::vector<std::string>& extensions ) 00631 { 00632 // only Tuttle support this property ( out of standard ) 00633 if( OFX::Private::gHostDescription.hostName == "TuttleOfx" ) 00634 { 00635 int n = _effectProps.propGetDimension( kTuttleOfxImageEffectPropSupportedExtensions ); 00636 00637 BOOST_FOREACH( const std::string& ext, extensions ) 00638 { 00639 _effectProps.propSetString( kTuttleOfxImageEffectPropSupportedExtensions, ext, n++ ); 00640 } 00641 } 00642 } 00643 00644 /** @brief Is the plugin single instance only ? */ 00645 void ImageEffectDescriptor::setSingleInstance( bool v ) 00646 { 00647 _effectProps.propSetInt( kOfxImageEffectPluginPropSingleInstance, int(v) ); 00648 } 00649 00650 /** @brief Does the plugin expect the host to perform per frame SMP threading */ 00651 void ImageEffectDescriptor::setHostFrameThreading( bool v ) 00652 { 00653 _effectProps.propSetInt( kOfxImageEffectPluginPropHostFrameThreading, int(v) ); 00654 } 00655 00656 /** @brief Does the plugin support multi resolution images */ 00657 void ImageEffectDescriptor::setSupportsMultiResolution( bool v ) 00658 { 00659 _effectProps.propSetInt( kOfxImageEffectPropSupportsMultiResolution, int(v) ); 00660 } 00661 00662 /** @brief Does the plugin support image tiling */ 00663 void ImageEffectDescriptor::setSupportsTiles( bool v ) 00664 { 00665 _effectProps.propSetInt( kOfxImageEffectPropSupportsTiles, int(v) ); 00666 } 00667 00668 /** @brief Does the plugin perform temporal clip access */ 00669 void ImageEffectDescriptor::setTemporalClipAccess( bool v ) 00670 { 00671 _effectProps.propSetInt( kOfxImageEffectPropTemporalClipAccess, int(v) ); 00672 } 00673 00674 /** @brief Does the plugin want to have render called twice per frame in all circumanstances for fielded images ? */ 00675 void ImageEffectDescriptor::setRenderTwiceAlways( bool v ) 00676 { 00677 _effectProps.propSetInt( kOfxImageEffectPluginPropFieldRenderTwiceAlways, int(v) ); 00678 } 00679 00680 /** @brief Does the plugin support inputs and output clips of differing depths */ 00681 void ImageEffectDescriptor::setSupportsMultipleClipDepths( bool v ) 00682 { 00683 _effectProps.propSetInt( kOfxImageEffectPropSupportsMultipleClipDepths, int(v) ); 00684 } 00685 00686 /** @brief Does the plugin support inputs and output clips of pixel aspect ratios */ 00687 void ImageEffectDescriptor::setSupportsMultipleClipPARs( bool v ) 00688 { 00689 _effectProps.propSetInt( kOfxImageEffectPropSupportsMultipleClipPARs, int(v) ); 00690 } 00691 00692 /** @brief What kind of thread safety does the plugin have */ 00693 void ImageEffectDescriptor::setRenderThreadSafety( ERenderSafety v ) 00694 { 00695 switch( v ) 00696 { 00697 case eRenderUnsafe: 00698 _effectProps.propSetString( kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderUnsafe ); 00699 break; 00700 case eRenderInstanceSafe: 00701 _effectProps.propSetString( kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderInstanceSafe ); 00702 break; 00703 case eRenderFullySafe: 00704 _effectProps.propSetString( kOfxImageEffectPluginRenderThreadSafety, kOfxImageEffectRenderFullySafe ); 00705 break; 00706 } 00707 } 00708 00709 /** @brief If the slave param changes the clip preferences need to be re-evaluated */ 00710 void ImageEffectDescriptor::addClipPreferencesSlaveParam( ParamDescriptor& p ) 00711 { 00712 int n = _effectProps.propGetDimension( kOfxImageEffectPropClipPreferencesSlaveParam ); 00713 00714 _effectProps.propSetString( kOfxImageEffectPropClipPreferencesSlaveParam, p.getName(), n ); 00715 } 00716 00717 /** @brief Create a clip, only callable from describe in context */ 00718 ClipDescriptor* ImageEffectDescriptor::defineClip( const std::string& name ) 00719 { 00720 // do we have the clip already 00721 std::map<std::string, ClipDescriptor*>::const_iterator search; 00722 search = _definedClips.find( name ); 00723 if( search != _definedClips.end() ) 00724 return search->second; 00725 00726 // no, so make it 00727 OfxPropertySetHandle propSet; 00728 OfxStatus stat = OFX::Private::gEffectSuite->clipDefine( _effectHandle, name.c_str(), &propSet ); 00729 if( stat == kOfxStatOK ) 00730 { 00731 ClipDescriptor* clip = new ClipDescriptor( name, propSet ); 00732 00733 _definedClips[name] = clip; 00734 _clipComponentsPropNames[name] = std::string( "OfxImageClipPropComponents_" ) + name; 00735 _clipDepthPropNames[name] = std::string( "OfxImageClipPropDepth_" ) + name; 00736 _clipPARPropNames[name] = std::string( "OfxImageClipPropPAR_" ) + name; 00737 _clipROIPropNames[name] = std::string( "OfxImageClipPropRoI_" ) + name; 00738 _clipFrameRangePropNames[name] = std::string( "OfxImageClipPropFrameRange_" ) + name; 00739 00740 return clip; 00741 } 00742 else 00743 return NULL; 00744 } 00745 00746 //////////////////////////////////////////////////////////////////////////////// 00747 // wraps up an image 00748 Image::Image( OfxPropertySetHandle props ) 00749 : _imageProps( props ) 00750 { 00751 OFX::Validation::validateImageProperties( props ); 00752 00753 // and fetch all the properties 00754 _pixelData = _imageProps.propGetPointer( kOfxImagePropData ); 00755 00756 _rowDistanceBytes = _imageProps.propGetInt( kOfxImagePropRowBytes ); 00757 _pixelAspectRatio = _imageProps.propGetDouble( kOfxImagePropPixelAspectRatio ); 00758 00759 std::string str = _imageProps.propGetString( kOfxImageEffectPropComponents ); 00760 _pixelComponents = mapPixelComponentStringToEnum( str ); 00761 00762 str = _imageProps.propGetString( kOfxImageEffectPropPixelDepth ); 00763 _pixelDepth = mapBitDepthStringToEnum( str ); 00764 00765 // compute bytes per pixel 00766 _pixelBytes = 0; 00767 switch( _pixelComponents ) 00768 { 00769 case ePixelComponentRGBA: _pixelBytes = 4; break; 00770 case ePixelComponentRGB: _pixelBytes = 3; break; 00771 case ePixelComponentAlpha: _pixelBytes = 1; break; 00772 case ePixelComponentCustom: _pixelBytes = 0; break; 00773 case ePixelComponentNone: _pixelBytes = 0; break; 00774 } 00775 00776 switch( _pixelDepth ) 00777 { 00778 case eBitDepthUByte: _pixelBytes *= 1; break; 00779 case eBitDepthUShort: _pixelBytes *= 2; break; 00780 case eBitDepthFloat: _pixelBytes *= 4; break; 00781 case eBitDepthCustom: /* what the good way ? */ break; 00782 case eBitDepthNone: _pixelBytes = 0; break; 00783 } 00784 00785 str = _imageProps.propGetString( kOfxImageEffectPropPreMultiplication ); 00786 _preMultiplication = mapPreMultiplicationStringToEnum( str ); 00787 00788 _regionOfDefinition.x1 = _imageProps.propGetInt( kOfxImagePropRegionOfDefinition, 0 ); 00789 _regionOfDefinition.y1 = _imageProps.propGetInt( kOfxImagePropRegionOfDefinition, 1 ); 00790 _regionOfDefinition.x2 = _imageProps.propGetInt( kOfxImagePropRegionOfDefinition, 2 ); 00791 _regionOfDefinition.y2 = _imageProps.propGetInt( kOfxImagePropRegionOfDefinition, 3 ); 00792 00793 _bounds.x1 = _imageProps.propGetInt( kOfxImagePropBounds, 0 ); 00794 _bounds.y1 = _imageProps.propGetInt( kOfxImagePropBounds, 1 ); 00795 _bounds.x2 = _imageProps.propGetInt( kOfxImagePropBounds, 2 ); 00796 _bounds.y2 = _imageProps.propGetInt( kOfxImagePropBounds, 3 ); 00797 00798 str = _imageProps.propGetString( kOfxImagePropField ); 00799 if( str == kOfxImageFieldNone ) 00800 { 00801 _field = eFieldNone; 00802 } 00803 else if( str == kOfxImageFieldBoth ) 00804 { 00805 _field = eFieldBoth; 00806 } 00807 else if( str == kOfxImageFieldLower ) 00808 { 00809 _field = eFieldLower; 00810 } 00811 else if( str == kOfxImageFieldUpper ) 00812 { 00813 _field = eFieldLower; 00814 } 00815 else 00816 { 00817 OFX::Log::error( true, "Unknown field state '%s' reported on an image", str.c_str() ); 00818 _field = eFieldNone; 00819 } 00820 00821 _uniqueID = _imageProps.propGetString( kOfxImagePropUniqueIdentifier ); 00822 00823 // std::string tuttleFullName = _imageProps.propGetString( "TuttleFullName" ); 00824 // TUTTLE_LOG_TRACE("tuttleFullName: " << tuttleFullName ); 00825 00826 _renderScale.x = _imageProps.propGetDouble( kOfxImageEffectPropRenderScale, 0 ); 00827 _renderScale.y = _imageProps.propGetDouble( kOfxImageEffectPropRenderScale, 1 ); 00828 00829 //std::cout << "IMAGE + " << _uniqueID << std::endl; 00830 } 00831 00832 Image::~Image() 00833 { 00834 //std::cout << "IMAGE - " << _uniqueID << std::endl; 00835 OFX::Private::gEffectSuite->clipReleaseImage( _imageProps.propSetHandle() ); 00836 } 00837 00838 std::size_t Image::getPixelBytes() const 00839 { 00840 std::size_t pixelBytes = 0; 00841 00842 switch( getPixelDepth() ) 00843 { 00844 case OFX::eBitDepthCustom: 00845 case OFX::eBitDepthNone: 00846 return 0; // Unknown 00847 case OFX::eBitDepthUByte: pixelBytes = 1; 00848 break; 00849 case OFX::eBitDepthUShort: pixelBytes = 2; 00850 break; 00851 case OFX::eBitDepthFloat: pixelBytes = 4; 00852 break; 00853 } 00854 00855 switch( getPixelComponents() ) 00856 { 00857 case OFX::ePixelComponentAlpha: break; // pixelSize * 1 00858 case OFX::ePixelComponentRGB: pixelBytes *= 3; 00859 break; 00860 case OFX::ePixelComponentRGBA: pixelBytes *= 4; 00861 break; 00862 default: break; 00863 } 00864 return pixelBytes; 00865 } 00866 00867 std::size_t Image::getBoundsRowDataBytes() const 00868 { 00869 return getPixelBytes() * getBoundsSize().x; 00870 } 00871 00872 std::size_t Image::getBoundsNbPixels() const 00873 { 00874 const OfxPointI s = getBoundsSize(); 00875 return s.x * s.y; 00876 } 00877 00878 std::size_t Image::getBoundsImageDataBytes() const 00879 { 00880 return getPixelBytes() * getBoundsNbPixels(); 00881 } 00882 00883 /** @brief return a pixel pointer 00884 * 00885 * No attempt made to be uber efficient here. 00886 */ 00887 void* Image::getPixelAddress( int x, int y ) 00888 { 00889 // are we in the image bounds 00890 BOOST_ASSERT( x >= _bounds.x1 && x < _bounds.x2 && y >= _bounds.y1 && y < _bounds.y2 && _pixelBytes != 0 ); 00891 00892 char* pix = ( char* )( ( (char*) _pixelData ) + ( y - _bounds.y1 ) * _rowDistanceBytes ); 00893 pix += ( x - _bounds.x1 ) * _pixelBytes; 00894 return (void*) pix; 00895 } 00896 00897 //////////////////////////////////////////////////////////////////////////////// 00898 // clip instance 00899 00900 /** @brief hidden constructor */ 00901 Clip::Clip( ImageEffect* effect, const std::string& name, OfxImageClipHandle handle, OfxPropertySetHandle props ) 00902 : _clipName( name ), 00903 _clipProps( props ), 00904 _clipHandle( handle ), 00905 _effect( effect ) 00906 { 00907 OFX::Validation::validateClipInstanceProperties( _clipProps ); 00908 } 00909 00910 /** @brief fetch the labels */ 00911 void Clip::getLabels( std::string& label, std::string& shortLabel, std::string& longLabel ) const 00912 { 00913 label = _clipProps.propGetString( kOfxPropLabel ); 00914 shortLabel = _clipProps.propGetString( kOfxPropShortLabel, false ); 00915 longLabel = _clipProps.propGetString( kOfxPropLongLabel, false ); 00916 } 00917 00918 /** @brief get the pixel depth */ 00919 EBitDepth Clip::getPixelDepth( void ) const 00920 { 00921 std::string str = _clipProps.propGetString( kOfxImageEffectPropPixelDepth ); 00922 EBitDepth bitDepth; 00923 00924 try 00925 { 00926 bitDepth = mapBitDepthStringToEnum( str ); 00927 if( bitDepth == eBitDepthNone && isConnected() ) 00928 { 00929 OFX::Log::error( true, "Clip %s is connected and has no pixel depth.", _clipName.c_str() ); 00930 } 00931 } 00932 // gone wrong ? 00933 catch( std::invalid_argument& ) 00934 { 00935 OFX::Log::error( true, "Unknown pixel depth property '%s' reported on clip '%s'", str.c_str(), _clipName.c_str() ); 00936 bitDepth = eBitDepthNone; 00937 } 00938 return bitDepth; 00939 } 00940 00941 /** @brief get the components in the image */ 00942 EPixelComponent Clip::getPixelComponents( void ) const 00943 { 00944 std::string str = _clipProps.propGetString( kOfxImageEffectPropComponents ); 00945 EPixelComponent pix; 00946 00947 try 00948 { 00949 pix = mapPixelComponentStringToEnum( str ); 00950 if( pix == ePixelComponentNone && isConnected() ) 00951 { 00952 OFX::Log::error( true, "Clip %s is connected and has no pixel component type!", _clipName.c_str() ); 00953 } 00954 } 00955 // gone wrong ? 00956 catch( std::invalid_argument& ) 00957 { 00958 OFX::Log::error( true, "Unknown pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str() ); 00959 pix = ePixelComponentNone; 00960 } 00961 return pix; 00962 } 00963 00964 /** @brief what is the actual pixel depth of the clip */ 00965 EBitDepth Clip::getUnmappedPixelDepth( void ) const 00966 { 00967 std::string str = _clipProps.propGetString( kOfxImageClipPropUnmappedPixelDepth ); 00968 EBitDepth bitDepth; 00969 00970 try 00971 { 00972 bitDepth = mapBitDepthStringToEnum( str ); 00973 if( bitDepth == eBitDepthNone && !isConnected() ) 00974 { 00975 OFX::Log::error( true, "Clip %s is connected and has no unmapped pixel depth.", _clipName.c_str() ); 00976 } 00977 } 00978 // gone wrong ? 00979 catch( std::invalid_argument& ) 00980 { 00981 OFX::Log::error( true, "Unknown unmapped pixel depth property '%s' reported on clip '%s'", str.c_str(), _clipName.c_str() ); 00982 bitDepth = eBitDepthNone; 00983 } 00984 return bitDepth; 00985 } 00986 00987 /** @brief what is the component type of the clip */ 00988 EPixelComponent Clip::getUnmappedPixelComponents( void ) const 00989 { 00990 std::string str = _clipProps.propGetString( kOfxImageClipPropUnmappedComponents ); 00991 EPixelComponent pix; 00992 00993 try 00994 { 00995 pix = mapPixelComponentStringToEnum( str ); 00996 if( pix == ePixelComponentNone && !isConnected() ) 00997 { 00998 OFX::Log::error( true, "Clip %s is connected and has no unmapped pixel component type!", _clipName.c_str() ); 00999 } 01000 } 01001 // gone wrong ? 01002 catch( std::invalid_argument& ) 01003 { 01004 OFX::Log::error( true, "Unknown unmapped pixel component type '%s' reported on clip '%s'", str.c_str(), _clipName.c_str() ); 01005 pix = ePixelComponentNone; 01006 } 01007 return pix; 01008 } 01009 01010 /** @brief get the components in the image */ 01011 EPreMultiplication Clip::getPreMultiplication( void ) const 01012 { 01013 std::string str = _clipProps.propGetString( kOfxImageEffectPropPreMultiplication ); 01014 EPreMultiplication premult; 01015 01016 try 01017 { 01018 premult = mapPreMultiplicationStringToEnum( str ); 01019 } 01020 // gone wrong ? 01021 catch( std::invalid_argument& ) 01022 { 01023 OFX::Log::error( true, "Unknown premultiplication type '%s' reported on clip %s!", str.c_str(), _clipName.c_str() ); 01024 premult = eImageOpaque; 01025 } 01026 return premult; 01027 } 01028 01029 /** @brief which spatial field comes first temporally */ 01030 EField Clip::getFieldOrder( void ) const 01031 { 01032 std::string str = _clipProps.propGetString( kOfxImageClipPropFieldOrder ); 01033 EField field; 01034 01035 try 01036 { 01037 field = mapFieldStringToEnum( str ); 01038 OFX::Log::error( field != eFieldNone && field != eFieldLower && field != eFieldUpper, 01039 "Field order '%s' reported on a clip %s is invalid, it must be none, lower or upper.", str.c_str(), _clipName.c_str() ); 01040 } 01041 // gone wrong ? 01042 catch( std::invalid_argument& ) 01043 { 01044 OFX::Log::error( true, "Unknown field order '%s' reported on a clip %s.", str.c_str(), _clipName.c_str() ); 01045 field = eFieldNone; 01046 } 01047 return field; 01048 } 01049 01050 /** @brief is the clip connected */ 01051 bool Clip::isConnected( void ) const 01052 { 01053 return _clipProps.propGetInt( kOfxImageClipPropConnected ) != 0; 01054 } 01055 01056 /** @brief can the clip be continuously sampled */ 01057 bool Clip::hasContinuousSamples( void ) const 01058 { 01059 return _clipProps.propGetInt( kOfxImageClipPropContinuousSamples ) != 0; 01060 } 01061 01062 /** @brief get the scale factor that has been applied to this clip */ 01063 double Clip::getPixelAspectRatio( void ) const 01064 { 01065 try { 01066 return _clipProps.propGetDouble( kOfxImagePropPixelAspectRatio ); 01067 } 01068 catch(...) 01069 { 01070 return 1.0; // This error could happen in Eyeon Fusion. 01071 } 01072 } 01073 01074 /** @brief get the frame rate, in frames per second on this clip, after any clip preferences have been applied */ 01075 double Clip::getFrameRate( void ) const 01076 { 01077 return _clipProps.propGetDouble( kOfxImageEffectPropFrameRate ); 01078 } 01079 01080 /** @brief return the range of frames over which this clip has images, after any clip preferences have been applied */ 01081 OfxRangeD Clip::getFrameRange( void ) const 01082 { 01083 OfxRangeD v; 01084 01085 v.min = _clipProps.propGetDouble( kOfxImageEffectPropFrameRange, 0 ); 01086 v.max = _clipProps.propGetDouble( kOfxImageEffectPropFrameRange, 1 ); 01087 return v; 01088 } 01089 01090 /** @brief get the frame rate, in frames per second on this clip, before any clip preferences have been applied */ 01091 double Clip::getUnmappedFrameRate( void ) const 01092 { 01093 return _clipProps.propGetDouble( kOfxImageEffectPropUnmappedFrameRate ); 01094 } 01095 01096 /** @brief return the range of frames over which this clip has images, before any clip preferences have been applied */ 01097 OfxRangeD Clip::getUnmappedFrameRange( void ) const 01098 { 01099 OfxRangeD v; 01100 01101 v.min = _clipProps.propGetDouble( kOfxImageEffectPropUnmappedFrameRange, 0 ); 01102 v.max = _clipProps.propGetDouble( kOfxImageEffectPropUnmappedFrameRange, 1 ); 01103 return v; 01104 } 01105 01106 /** @brief get the RoD for this clip in the cannonical coordinate system */ 01107 OfxRectD Clip::getCanonicalRod( const OfxTime t ) const 01108 { 01109 OfxRectD bounds; 01110 OfxStatus stat = OFX::Private::gEffectSuite->clipGetRegionOfDefinition( _clipHandle, t, &bounds ); 01111 01112 if( stat == kOfxStatFailed ) 01113 { 01114 bounds.x1 = bounds.x2 = bounds.y1 = bounds.y2 = 0; 01115 } 01116 throwSuiteStatusException( stat ); 01117 return bounds; 01118 } 01119 01120 OfxRectD Clip::getCanonicalRod( const OfxTime t, const OfxPointD& renderScale ) const 01121 { 01122 OfxRectD rod = getCanonicalRod( t ); 01123 rod.x1 *= renderScale.x; 01124 rod.y1 *= renderScale.y; 01125 rod.x2 *= renderScale.x; 01126 rod.y2 *= renderScale.y; 01127 return rod; 01128 } 01129 01130 /** @brief get the RoD for this clip in pixel space */ 01131 OfxRectI Clip::getPixelRod( const OfxTime t ) const 01132 { 01133 OfxRectD rod = getCanonicalRod(t); 01134 double ratio = getPixelAspectRatio(); 01135 if( ratio == 0.0 ) 01136 ratio = 1.0; 01137 01138 OfxRectI pixRod; 01139 pixRod.x1 = boost::numeric_cast<int>(rod.x1 / ratio); 01140 pixRod.y1 = boost::numeric_cast<int>(rod.y1); 01141 pixRod.x2 = boost::numeric_cast<int>(std::ceil(rod.x2 / ratio)); 01142 pixRod.y2 = boost::numeric_cast<int>(std::ceil(rod.y2)); 01143 01144 return pixRod; 01145 } 01146 01147 OfxRectI Clip::getPixelRod( const OfxTime t, const OfxPointD& renderScale ) const 01148 { 01149 OfxRectI rod = getPixelRod( t ); 01150 rod.x1 = static_cast<int>( rod.x1 * renderScale.x ); 01151 rod.y1 = static_cast<int>( rod.y1 * renderScale.y ); 01152 rod.x2 = static_cast<int>( rod.x2 * renderScale.x ); 01153 rod.y2 = static_cast<int>( rod.y2 * renderScale.y ); 01154 return rod; 01155 } 01156 01157 /** @brief fetch an image */ 01158 Image* Clip::fetchImage( OfxTime t ) 01159 { 01160 OfxPropertySetHandle imageHandle; 01161 OfxStatus stat = OFX::Private::gEffectSuite->clipGetImage( _clipHandle, t, NULL, &imageHandle ); 01162 01163 if( stat == kOfxStatFailed ) 01164 { 01165 return NULL; // not an error, fetched images out of range/region, assume black and transparent 01166 } 01167 else 01168 throwSuiteStatusException( stat ); 01169 01170 return new Image( imageHandle ); 01171 } 01172 01173 /** @brief fetch an image, with a specific region in cannonical coordinates */ 01174 Image* Clip::fetchImage( OfxTime t, OfxRectD bounds ) 01175 { 01176 OfxPropertySetHandle imageHandle; 01177 OfxStatus stat = OFX::Private::gEffectSuite->clipGetImage( _clipHandle, t, &bounds, &imageHandle ); 01178 01179 if( stat == kOfxStatFailed ) 01180 { 01181 return NULL; // not an error, fetched images out of range/region, assume black and transparent 01182 } 01183 else 01184 throwSuiteStatusException( stat ); 01185 01186 return new Image( imageHandle ); 01187 } 01188 01189 //////////////////////////////////////////////////////////////////////////////// 01190 /// image effect 01191 01192 /** @brief ctor */ 01193 ImageEffect::ImageEffect( OfxImageEffectHandle handle ) 01194 : _effectHandle( handle ), 01195 _effectProps( 0 ), 01196 _context( eContextNone ), 01197 _progressStartSuccess( false ) 01198 { 01199 // get the property handle 01200 _effectProps = OFX::Private::fetchEffectProps( handle ); 01201 01202 // Set this as the instance data pointer on the effect handle 01203 _effectProps.propSetPointer( kOfxPropInstanceData, this ); 01204 01205 // validate the plugin instance 01206 OFX::Validation::validatePluginInstanceProperties( _effectProps ); 01207 01208 // fetch the context 01209 std::string ctxt = _effectProps.propGetString( kOfxImageEffectPropContext ); 01210 _context = mapContextStringToEnum( ctxt ); 01211 01212 // the param set daddy-oh 01213 OfxParamSetHandle paramSet; 01214 OfxStatus stat = OFX::Private::gEffectSuite->getParamSet( handle, ¶mSet ); 01215 throwSuiteStatusException( stat ); 01216 setParamSetHandle( paramSet ); 01217 01218 } 01219 01220 /** @brief dtor */ 01221 ImageEffect::~ImageEffect() 01222 { 01223 // clobber the instance data property on the effect handle 01224 _effectProps.propSetPointer( kOfxPropInstanceData, 0 ); 01225 01226 // delete any clip instances we may have constructed 01227 std::map<std::string, Clip*>::iterator iter; 01228 for( iter = _fetchedClips.begin(); iter != _fetchedClips.end(); ++iter ) 01229 { 01230 if( iter->second ) 01231 { 01232 delete iter->second; 01233 iter->second = NULL; 01234 } 01235 } 01236 } 01237 01238 /** @brief the context this effect was instantiate in */ 01239 EContext ImageEffect::getContext( void ) const 01240 { 01241 //std::cout << "debug... getContext enum:" << (int)_context << " str:" << mapContextEnumToString(_context) << std::endl; 01242 return _context; 01243 } 01244 01245 /** @brief size of the project */ 01246 OfxPointD ImageEffect::getProjectSize( void ) const 01247 { 01248 OfxPointD v; 01249 01250 v.x = _effectProps.propGetDouble( kOfxImageEffectPropProjectSize, 0 ); 01251 v.y = _effectProps.propGetDouble( kOfxImageEffectPropProjectSize, 1 ); 01252 return v; 01253 } 01254 01255 /** @brief origin of the project */ 01256 OfxPointD ImageEffect::getProjectOffset( void ) const 01257 { 01258 OfxPointD v; 01259 01260 v.x = _effectProps.propGetDouble( kOfxImageEffectPropProjectOffset, 0 ); 01261 v.y = _effectProps.propGetDouble( kOfxImageEffectPropProjectOffset, 1 ); 01262 return v; 01263 } 01264 01265 /** @brief extent of the project */ 01266 OfxPointD ImageEffect::getProjectExtent( void ) const 01267 { 01268 OfxPointD v; 01269 01270 v.x = _effectProps.propGetDouble( kOfxImageEffectPropProjectExtent, 0 ); 01271 v.y = _effectProps.propGetDouble( kOfxImageEffectPropProjectExtent, 1 ); 01272 return v; 01273 } 01274 01275 /** @brief pixel aspect ratio of the project */ 01276 double ImageEffect::getProjectPixelAspectRatio( void ) const 01277 { 01278 return _effectProps.propGetDouble( kOfxImageEffectPropProjectPixelAspectRatio, 0 ); 01279 } 01280 01281 /** @brief how long does the effect last */ 01282 double ImageEffect::getEffectDuration( void ) const 01283 { 01284 return _effectProps.propGetDouble( kOfxImageEffectInstancePropEffectDuration, 0 ); 01285 } 01286 01287 /** @brief the frame rate of the project */ 01288 double ImageEffect::getFrameRate( void ) const 01289 { 01290 return _effectProps.propGetDouble( kOfxImageEffectPropFrameRate, 0 ); 01291 } 01292 01293 /** @brief is the instance currently being interacted with */ 01294 bool ImageEffect::isInteractive( void ) const 01295 { 01296 return _effectProps.propGetInt( kOfxPropIsInteractive ) != 0; 01297 } 01298 01299 /** @brief set the instance to be sequentially renderred, this should have been part of clip preferences! */ 01300 void ImageEffect::setSequentialRender( bool v ) 01301 { 01302 _effectProps.propSetInt( kOfxImageEffectInstancePropSequentialRender, int(v) ); 01303 } 01304 01305 /** @brief Have we informed the host we want to be seqentially renderred ? */ 01306 bool ImageEffect::getSequentialRender( void ) const 01307 { 01308 return _effectProps.propGetInt( kOfxImageEffectInstancePropSequentialRender ) != 0; 01309 } 01310 01311 OFX::Message::EMessageReply ImageEffect::sendMessage( OFX::Message::EMessageType type, const std::string& id, const std::string& msg ) 01312 { 01313 if( !OFX::Private::gMessageSuite->message ) 01314 { 01315 throwHostMissingSuiteException( "message" ); 01316 } 01317 OfxStatus stat = OFX::Private::gMessageSuite->message( _effectHandle, mapMessageTypeEnumToString( type ).c_str(), id.c_str(), msg.c_str() ); 01318 return mapMessageReplyStatusToEnum( stat ); 01319 } 01320 01321 /** @brief Fetch the named clip from this instance */ 01322 Clip* ImageEffect::fetchClip( const std::string& name ) 01323 { 01324 // do we have the clip already 01325 std::map<std::string, Clip*>::const_iterator search; 01326 search = _fetchedClips.find( name ); 01327 if( search != _fetchedClips.end() ) 01328 return search->second; 01329 01330 // fetch the property set handle of the effect 01331 OfxImageClipHandle clipHandle = 0; 01332 OfxPropertySetHandle propHandle = 0; 01333 OfxStatus stat = OFX::Private::gEffectSuite->clipGetHandle( getHandle(), name.c_str(), &clipHandle, &propHandle ); 01334 throwSuiteStatusException( stat ); 01335 01336 // and make one 01337 Clip* newClip = new Clip( this, name, clipHandle, propHandle ); 01338 01339 // add it in 01340 _fetchedClips[name] = newClip; 01341 01342 // return it 01343 return newClip; 01344 } 01345 01346 /** @brief Fetch a parametric param */ 01347 CameraParam* ImageEffect::fetchCameraParam( const std::string& name ) 01348 { 01349 return fetchAttribute<CameraParam>( getHandle(), name ); 01350 } 01351 01352 /** @brief does the host want us to abort rendering? */ 01353 bool ImageEffect::abort() const 01354 { 01355 return OFX::Private::gEffectSuite->abort( _effectHandle ) != 0; 01356 } 01357 01358 /** @brief adds a new interact to the set of interacts open on this effect */ 01359 void ImageEffect::addOverlayInteract( OverlayInteract* interact ) 01360 { 01361 // do we have it already ? 01362 std::list<OverlayInteract*>::iterator i; 01363 i = std::find( _overlayInteracts.begin(), _overlayInteracts.end(), interact ); 01364 01365 // we don't, put it in there 01366 if( i == _overlayInteracts.end() ) 01367 { 01368 // we have a new one to add in here 01369 _overlayInteracts.push_back( interact ); 01370 } 01371 } 01372 01373 /** @brief removes an interact to the set of interacts open on this effect */ 01374 void ImageEffect::removeOverlayInteract( OverlayInteract* interact ) 01375 { 01376 // find it 01377 std::list<OverlayInteract*>::iterator i; 01378 i = std::find( _overlayInteracts.begin(), _overlayInteracts.end(), interact ); 01379 01380 // and remove it 01381 if( i != _overlayInteracts.end() ) 01382 { 01383 _overlayInteracts.erase( i ); 01384 } 01385 } 01386 01387 /** @brief force all overlays on this interact to be redrawn */ 01388 void ImageEffect::redrawOverlays( void ) 01389 { 01390 // find it 01391 std::list<OverlayInteract*>::iterator i; 01392 for( i = _overlayInteracts.begin(); i != _overlayInteracts.end(); ++i ) 01393 { 01394 ( *i )->requestRedraw(); 01395 } 01396 } 01397 01398 //////////////////////////////////////////////////////////////////////////////// 01399 // below are the default members for the base image effect 01400 01401 /** @brief client is identity function, returns the clip and time for the identity function 01402 */ 01403 bool ImageEffect::isIdentity( const RenderArguments& args, Clip*& identityClip, double& identityTime ) 01404 { 01405 return false; // by default, we are not an identity operation 01406 } 01407 01408 /** @brief The get RoD action */ 01409 bool ImageEffect::getRegionOfDefinition( const RegionOfDefinitionArguments& args, OfxRectD& rod ) 01410 { 01411 return false; // by default, we are not setting the RoD 01412 } 01413 01414 /** @brief the get RoI action */ 01415 void ImageEffect::getRegionsOfInterest( const RegionsOfInterestArguments& args, RegionOfInterestSetter& rois ) 01416 { 01417 // fa niente 01418 } 01419 01420 /** @brief the get frames needed action */ 01421 void ImageEffect::getFramesNeeded( const FramesNeededArguments& args, FramesNeededSetter& frames ) 01422 { 01423 // fa niente 01424 } 01425 01426 /** @brief client begin sequence render function */ 01427 void ImageEffect::beginSequenceRender( const BeginSequenceRenderArguments& args ) 01428 { 01429 // fa niente 01430 } 01431 01432 /** @brief client end sequence render function, this is one of the few that must be set */ 01433 void ImageEffect::endSequenceRender( const EndSequenceRenderArguments& args ) 01434 { 01435 // fa niente 01436 } 01437 01438 /** @brief The purge caches action, a request for an instance to free up as much memory as possible in low memory situations */ 01439 void ImageEffect::purgeCaches( void ) 01440 { 01441 // fa niente 01442 } 01443 01444 /** @brief The sync private data action, called when the effect needs to sync any private data to persistant parameters */ 01445 void ImageEffect::syncPrivateData( void ) 01446 { 01447 // fa niente 01448 } 01449 01450 /** @brief get the clip preferences */ 01451 void ImageEffect::getClipPreferences( ClipPreferencesSetter& clipPreferences ) 01452 { 01453 // fa niente 01454 } 01455 01456 /** @brief the effect is about to be actively edited by a user, called when the first user interface is opened on an instance */ 01457 void ImageEffect::beginEdit( void ) 01458 { 01459 // fa niente 01460 } 01461 01462 /** @brief the effect is no longer being edited by a user, called when the last user interface is closed on an instance */ 01463 void ImageEffect::endEdit( void ) 01464 { 01465 // fa niente 01466 } 01467 01468 /** @brief the effect is about to have some values changed */ 01469 void ImageEffect::beginChanged( InstanceChangeReason reason ) 01470 {} 01471 01472 /** @brief called when a param has just had its value changed */ 01473 void ImageEffect::changedParam( const InstanceChangedArgs& args, const std::string& paramName ) 01474 {} 01475 01476 /** @brief called when a clip has just been changed in some way (a rewire maybe) */ 01477 void ImageEffect::changedClip( const InstanceChangedArgs& args, const std::string& clipName ) 01478 {} 01479 01480 /** @brief the effect has just had some values changed */ 01481 void ImageEffect::endChanged( InstanceChangeReason reason ) 01482 {} 01483 01484 /** @brief get the time domain */ 01485 bool ImageEffect::getTimeDomain( OfxRangeD& range ) 01486 { 01487 // by default, do the default 01488 return false; 01489 } 01490 01491 /// Start doing progress. 01492 void ImageEffect::progressStart( const std::string& message ) 01493 { 01494 if( OFX::Private::gProgressSuite ) 01495 { 01496 OfxStatus stat = OFX::Private::gProgressSuite->progressStart( (void*) _effectHandle, message.c_str() ); 01497 _progressStartSuccess = ( stat == kOfxStatOK ); 01498 } 01499 } 01500 01501 /// finish yer progress 01502 void ImageEffect::progressEnd() 01503 { 01504 if( OFX::Private::gProgressSuite && _progressStartSuccess ) 01505 { 01506 OFX::Private::gProgressSuite->progressEnd( (void*) _effectHandle ); 01507 } 01508 } 01509 01510 /// set the progress to some level of completion, 01511 /// returns true if you should abandon processing, false to continue 01512 bool ImageEffect::progressUpdate( const double t ) 01513 { 01514 if( OFX::Private::gProgressSuite && _progressStartSuccess ) 01515 { 01516 const OfxStatus stat = OFX::Private::gProgressSuite->progressUpdate( (void*) _effectHandle, t ); 01517 if( stat == kOfxStatReplyNo ) 01518 return true; 01519 } 01520 return false; 01521 } 01522 01523 /// get the current time on the timeline. This is not necessarily the same 01524 /// time as being passed to an action (eg render) 01525 double ImageEffect::timeLineGetTime() 01526 { 01527 if( OFX::Private::gTimeLineSuite ) 01528 { 01529 double time; 01530 if( OFX::Private::gTimeLineSuite->getTime( (void*) _effectHandle, &time ) == kOfxStatOK ) 01531 return time; 01532 } 01533 return 0; 01534 } 01535 01536 /// set the timeline to a specific time 01537 void ImageEffect::timeLineGotoTime( const double t ) 01538 { 01539 if( OFX::Private::gTimeLineSuite ) 01540 { 01541 OFX::Private::gTimeLineSuite->gotoTime( (void*) _effectHandle, t ); 01542 } 01543 } 01544 01545 /// get the first and last times available on the effect's timeline 01546 void ImageEffect:: timeLineGetBounds( double& t1, double& t2 ) 01547 { 01548 t1 = t2 = 0; 01549 if( OFX::Private::gTimeLineSuite ) 01550 { 01551 OFX::Private::gTimeLineSuite->getTimeBounds( (void*) _effectHandle, &t1, &t2 ); 01552 } 01553 } 01554 01555 //////////////////////////////////////////////////////////////////////////////// 01556 // Class used to set the clip preferences of the effect. */ 01557 01558 const std::string& ClipPreferencesSetter::extractValueForName( const StringStringMap& m, const std::string& name ) 01559 { 01560 StringStringMap::const_iterator it = m.find( name ); 01561 01562 if( it == m.end() ) 01563 BOOST_THROW_EXCEPTION( Exception::PropertyUnknownToHost( name ) ); 01564 return it->second; 01565 } 01566 01567 /** @brief, force the host to set a clip's mapped component type to be \em comps. */ 01568 void ClipPreferencesSetter::setClipComponents( Clip& clip, EPixelComponent comps ) 01569 { 01570 doneSomething_ = true; 01571 const std::string& propName = extractValueForName( clipComponentPropNames_, clip.name() ); 01572 01573 switch( comps ) 01574 { 01575 case ePixelComponentRGBA: 01576 outArgs_.propSetString( propName.c_str(), kOfxImageComponentRGBA ); 01577 break; 01578 case ePixelComponentRGB: 01579 outArgs_.propSetString( propName.c_str(), kOfxImageComponentRGB ); 01580 break; 01581 case ePixelComponentAlpha: 01582 outArgs_.propSetString( propName.c_str(), kOfxImageComponentAlpha ); 01583 break; 01584 case ePixelComponentCustom: 01585 break; 01586 case ePixelComponentNone: 01587 outArgs_.propSetString( propName.c_str(), kOfxImageComponentNone ); 01588 break; 01589 } 01590 } 01591 01592 /** @brief, force the host to set a clip's mapped bit depth be \em bitDepth */ 01593 void ClipPreferencesSetter::setClipBitDepth( Clip& clip, EBitDepth bitDepth ) 01594 { 01595 doneSomething_ = true; 01596 const std::string& propName = extractValueForName( clipDepthPropNames_, clip.name() ); 01597 01598 if( _imageEffectHostDescription->supportsMultipleClipDepths ) 01599 { 01600 switch( bitDepth ) 01601 { 01602 case eBitDepthUByte: 01603 outArgs_.propSetString( propName.c_str(), kOfxBitDepthByte ); 01604 break; 01605 case eBitDepthUShort: 01606 outArgs_.propSetString( propName.c_str(), kOfxBitDepthShort ); 01607 break; 01608 case eBitDepthFloat: 01609 outArgs_.propSetString( propName.c_str(), kOfxBitDepthFloat ); 01610 break; 01611 case eBitDepthNone: 01612 outArgs_.propSetString( propName.c_str(), kOfxBitDepthNone ); 01613 break; 01614 case eBitDepthCustom: 01615 // what the good way ? 01616 break; 01617 } 01618 } 01619 else 01620 { 01621 // BOOST_THROW_EXCEPTION( std::logic_error("Host doesn't support multiple bit depths.") ); 01622 // it's not clear what we need to to in this case... 01623 // set the value supported by the host or set nothing 01624 // by setting this value, we may have less problem depending on host implementations 01625 outArgs_.propSetString( propName.c_str(), mapBitDepthEnumToString( _imageEffectHostDescription->getPixelDepth() ) ); 01626 } 01627 } 01628 01629 /** @brief, force the host to set a clip's mapped Pixel Aspect Ratio to be \em PAR */ 01630 void ClipPreferencesSetter::setPixelAspectRatio( Clip& clip, double PAR ) 01631 { 01632 if( ! _imageEffectHostDescription->supportsMultipleClipPARs ) 01633 return; 01634 // BOOST_THROW_EXCEPTION( std::logic_error("Host doesn't support multiple Pixel Aspect Ratios.") ); 01635 01636 doneSomething_ = true; 01637 const std::string& propName = extractValueForName( clipPARPropNames_, clip.name() ); 01638 outArgs_.propSetDouble( propName.c_str(), PAR ); 01639 } 01640 01641 /** @brief Allows an effect to change the output frame rate */ 01642 void ClipPreferencesSetter::setOutputFrameRate( double v ) 01643 { 01644 if( ! _imageEffectHostDescription->supportsSetableFrameRate ) 01645 return; 01646 // BOOST_THROW_EXCEPTION( std::logic_error("Host doesn't support setable frame rate.") ); 01647 01648 doneSomething_ = true; 01649 outArgs_.propSetDouble( kOfxImageEffectPropFrameRate, v ); 01650 } 01651 01652 /** @brief Set the premultiplication state of the output clip. */ 01653 void ClipPreferencesSetter::setOutputPremultiplication( EPreMultiplication v ) 01654 { 01655 doneSomething_ = true; 01656 switch( v ) 01657 { 01658 case eImageOpaque: 01659 outArgs_.propSetString( kOfxImageEffectPropPreMultiplication, kOfxImageOpaque ); 01660 break; 01661 case eImagePreMultiplied: 01662 outArgs_.propSetString( kOfxImageEffectPropPreMultiplication, kOfxImagePreMultiplied ); 01663 break; 01664 case eImageUnPreMultiplied: 01665 outArgs_.propSetString( kOfxImageEffectPropPreMultiplication, kOfxImageUnPreMultiplied ); 01666 break; 01667 } 01668 } 01669 01670 /** @brief Set whether the effect can be continously sampled. */ 01671 void ClipPreferencesSetter::setOutputHasContinousSamples( bool v ) 01672 { 01673 doneSomething_ = true; 01674 outArgs_.propSetInt( kOfxImageClipPropContinuousSamples, int(v) ); 01675 } 01676 01677 /** @brief Sets whether the effect will produce different images in all frames, even if the no params or input images are varying (eg: a noise generator). */ 01678 void ClipPreferencesSetter::setOutputFrameVarying( bool v ) 01679 { 01680 doneSomething_ = true; 01681 outArgs_.propSetInt( kOfxImageEffectFrameVarying, int(v) ); 01682 } 01683 01684 void ClipPreferencesSetter::setOutputFielding( EField v ) 01685 { 01686 doneSomething_ = true; 01687 switch( v ) 01688 { 01689 case eFieldNone: outArgs_.propSetString( kOfxImageClipPropFieldOrder, kOfxImageFieldNone, 0, false ); break; 01690 case eFieldLower: outArgs_.propSetString( kOfxImageClipPropFieldOrder, kOfxImageFieldLower, 0, false ); break; 01691 case eFieldUpper: outArgs_.propSetString( kOfxImageClipPropFieldOrder, kOfxImageFieldUpper, 0, false ); break; 01692 case eFieldBoth: outArgs_.propSetString( kOfxImageClipPropFieldOrder, kOfxImageFieldBoth, 0, false ); break; 01693 // kOfxImageFieldSingle ? 01694 // kOfxImageFieldDoubled ? 01695 } 01696 } 01697 01698 //////////////////////////////////////////////////////////////////////////////// 01699 /** @brief Class that skins image memory allocation */ 01700 01701 ImageMemory::ImageMemory() 01702 : _handle( 0 ) 01703 , _alloc(false) 01704 { 01705 } 01706 01707 ImageMemory::ImageMemory( size_t nBytes, ImageEffect* associatedEffect ) 01708 : _handle( 0 ) 01709 , _alloc(false) 01710 { 01711 alloc( nBytes, associatedEffect ); 01712 } 01713 01714 /** @brief dtor */ 01715 ImageMemory::~ImageMemory() 01716 { 01717 if( _alloc ) 01718 { 01719 /*OfxStatus stat = */ OFX::Private::gEffectSuite->imageMemoryFree( _handle ); 01720 } 01721 // ignore status code for exception purposes 01722 } 01723 01724 void ImageMemory::alloc( size_t nBytes, ImageEffect* associatedEffect ) 01725 { 01726 BOOST_ASSERT( ! _alloc ); 01727 _alloc = true; 01728 OfxImageEffectHandle effectHandle = 0; 01729 01730 if( associatedEffect != 0 ) 01731 { 01732 effectHandle = associatedEffect->_effectHandle; 01733 } 01734 01735 OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryAlloc( effectHandle, nBytes, &_handle ); 01736 if( stat == kOfxStatErrMemory ) 01737 BOOST_THROW_EXCEPTION( std::bad_alloc() ); 01738 throwSuiteStatusException( stat ); 01739 } 01740 01741 /** @brief lock the memory and return a pointer to it */ 01742 void* ImageMemory::lock( void ) 01743 { 01744 void* ptr; 01745 OfxStatus stat = OFX::Private::gEffectSuite->imageMemoryLock( _handle, &ptr ); 01746 01747 if( stat == kOfxStatErrMemory ) 01748 BOOST_THROW_EXCEPTION( std::bad_alloc() ); 01749 throwSuiteStatusException( stat ); 01750 return ptr; 01751 } 01752 01753 /** @brief unlock the memory */ 01754 void ImageMemory::unlock( void ) 01755 { 01756 /*OfxStatus stat = */ OFX::Private::gEffectSuite->imageMemoryUnlock( _handle ); 01757 } 01758 01759 /** @brief OFX::Private namespace, for things private to the support library code here generally calls image effect class members */ 01760 namespace Private { 01761 01762 /** @brief Creates the global host description and sets its properties */ 01763 void fetchHostDescription( OfxHost* host ) 01764 { 01765 OFX::Log::error( OFX::Private::gHostDescriptionHasInit, "Tried to create host description when we already have one." ); 01766 if( !OFX::Private::gHostDescriptionHasInit ) 01767 { 01768 OFX::Private::gHostDescriptionHasInit = true; 01769 // wrap the property handle up with a property set 01770 PropertySet hostProps( host->host ); 01771 01772 // and get some properties 01773 gHostDescription.hostName = hostProps.propGetString( kOfxPropName ); 01774 gHostDescription.hostLabel = hostProps.propGetString( kOfxPropLabel ); 01775 gHostDescription.hostIsBackground = hostProps.propGetInt( kOfxImageEffectHostPropIsBackground ) != 0; 01776 gHostDescription.supportsOverlays = hostProps.propGetInt( kOfxImageEffectPropSupportsOverlays ) != 0; 01777 gHostDescription.supportsMultiResolution = hostProps.propGetInt( kOfxImageEffectPropSupportsMultiResolution ) != 0; 01778 gHostDescription.supportsTiles = hostProps.propGetInt( kOfxImageEffectPropSupportsTiles ) != 0; 01779 gHostDescription.temporalClipAccess = hostProps.propGetInt( kOfxImageEffectPropTemporalClipAccess ) != 0; 01780 gHostDescription.supportsMultipleClipDepths = hostProps.propGetInt( kOfxImageEffectPropSupportsMultipleClipDepths ) != 0; 01781 gHostDescription.supportsMultipleClipPARs = hostProps.propGetInt( kOfxImageEffectPropSupportsMultipleClipPARs ) != 0; 01782 gHostDescription.supportsSetableFrameRate = hostProps.propGetInt( kOfxImageEffectPropSetableFrameRate ) != 0; 01783 gHostDescription.supportsSetableFielding = hostProps.propGetInt( kOfxImageEffectPropSetableFielding ) != 0; 01784 gHostDescription.supportsStringAnimation = hostProps.propGetInt( kOfxParamHostPropSupportsStringAnimation ) != 0; 01785 gHostDescription.supportsCustomInteract = hostProps.propGetInt( kOfxParamHostPropSupportsCustomInteract ) != 0; 01786 gHostDescription.supportsChoiceAnimation = hostProps.propGetInt( kOfxParamHostPropSupportsChoiceAnimation ) != 0; 01787 gHostDescription.supportsBooleanAnimation = hostProps.propGetInt( kOfxParamHostPropSupportsBooleanAnimation ) != 0; 01788 gHostDescription.supportsCustomAnimation = hostProps.propGetInt( kOfxParamHostPropSupportsCustomAnimation ) != 0; 01789 gHostDescription.supportsParametricParameter = gParametricParameterSuite != NULL; 01790 gHostDescription.supportsCameraParameter = gCameraParameterSuite != NULL; 01791 gHostDescription.maxParameters = hostProps.propGetInt( kOfxParamHostPropMaxParameters ); 01792 gHostDescription.maxPages = hostProps.propGetInt( kOfxParamHostPropMaxPages ); 01793 gHostDescription.pageRowCount = hostProps.propGetInt( kOfxParamHostPropPageRowColumnCount, 0 ); 01794 gHostDescription.pageColumnCount = hostProps.propGetInt( kOfxParamHostPropPageRowColumnCount, 1 ); 01795 int numComponents = hostProps.propGetDimension( kOfxImageEffectPropSupportedComponents ); 01796 for( int i = 0; i < numComponents; ++i ) 01797 gHostDescription._supportedComponents.push_back( mapPixelComponentStringToEnum( hostProps.propGetString( kOfxImageEffectPropSupportedComponents, i ) ) ); 01798 01799 int numContexts = hostProps.propGetDimension( kOfxImageEffectPropSupportedContexts ); 01800 for( int i = 0; i < numContexts; ++i ) 01801 gHostDescription._supportedContexts.push_back( mapContextStringToEnum( hostProps.propGetString( kOfxImageEffectPropSupportedContexts, i ) ) ); 01802 01803 int numPixelDepths = hostProps.propGetDimension( kOfxImageEffectPropSupportedPixelDepths ); 01804 for( int i = 0; i < numPixelDepths; ++i ) 01805 gHostDescription._supportedPixelDepths.push_back( mapBitDepthStringToEnum( hostProps.propGetString( kOfxImageEffectPropSupportedPixelDepths, i ) ) ); 01806 } 01807 } 01808 01809 /** @brief fetch the effect property set from the ImageEffectHandle */ 01810 OFX::PropertySet fetchEffectProps( OfxImageEffectHandle handle ) 01811 { 01812 // get the property handle 01813 OfxPropertySetHandle propHandle; 01814 OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet( handle, &propHandle ); 01815 01816 throwSuiteStatusException( stat ); 01817 return OFX::PropertySet( propHandle ); 01818 } 01819 01820 /** @brief Library side load action, this fetches all the suite pointers */ 01821 void loadAction( void ) 01822 { 01823 ++Private::gLoadCount; 01824 01825 //OfxStatus status = kOfxStatOK; 01826 01827 // fetch the suites 01828 OFX::Log::error( gHost == 0, "Host pointer has not been set." ); 01829 if( !gHost ) 01830 BOOST_THROW_EXCEPTION( OFX::Exception::Suite( kOfxStatErrBadHandle ) ); 01831 01832 if( Private::gLoadCount == 1 ) 01833 { 01834 gEffectSuite = (OfxImageEffectSuiteV1*) fetchSuite( kOfxImageEffectSuite, 1 ); 01835 gPropSuite = (OfxPropertySuiteV1*) fetchSuite( kOfxPropertySuite, 1 ); 01836 gParamSuite = (OfxParameterSuiteV1*) fetchSuite( kOfxParameterSuite, 1 ); 01837 gMemorySuite = (OfxMemorySuiteV1*) fetchSuite( kOfxMemorySuite, 1 ); 01838 gThreadSuite = (OfxMultiThreadSuiteV1*) fetchSuite( kOfxMultiThreadSuite, 1 ); 01839 gMessageSuite = (OfxMessageSuiteV1*) fetchSuite( kOfxMessageSuite, 1 ); 01840 gProgressSuite = (OfxProgressSuiteV1*) fetchSuite( kOfxProgressSuite, 1, true ); 01841 gTimeLineSuite = (OfxTimeLineSuiteV1*) fetchSuite( kOfxTimeLineSuite, 1, true ); 01842 gParametricParameterSuite = static_cast<OfxParametricParameterSuiteV1*>( OFX::fetchSuite( kOfxParametricParameterSuite, 1, true ) ); 01843 gCameraParameterSuite = static_cast<NukeOfxCameraSuiteV1*>( OFX::fetchSuite( kNukeOfxCameraSuite, 1, true ) ); 01844 01845 // OK check and fetch host information 01846 fetchHostDescription( gHost ); 01847 01848 /// and set some dendent flags 01849 OFX::Private::gHostDescription.supportsProgressSuite = ( gProgressSuite != NULL ); 01850 OFX::Private::gHostDescription.supportsTimeLineSuite = ( gTimeLineSuite != NULL ); 01851 01852 // fetch the interact suite if the host supports interaction 01853 if( OFX::Private::gHostDescription.supportsOverlays || OFX::Private::gHostDescription.supportsCustomInteract ) 01854 gInteractSuite = (OfxInteractSuiteV1*) fetchSuite( kOfxInteractSuite, 1 ); 01855 } 01856 01857 // initialise the validation code 01858 OFX::Validation::initialise(); 01859 01860 // validate the host 01861 OFX::Validation::validateHostProperties( gHost ); 01862 01863 } 01864 01865 /** @brief Library side unload action, this fetches all the suite pointers */ 01866 void unloadAction( const char* id ) 01867 { 01868 --Private::gLoadCount; 01869 01870 if( Private::gLoadCount < 0 ) 01871 { 01872 std::cerr << "OFX Plugin \"" << id << "\" is already unloaded." << std::endl; 01873 return; 01874 } 01875 01876 { 01877 EffectDescriptorMap::iterator it = gEffectDescriptors.find( id ); 01878 EffectContextMap& toBeDeleted = it->second; 01879 for( EffectContextMap::iterator it2 = toBeDeleted.begin(); it2 != toBeDeleted.end(); ++it2 ) 01880 { 01881 OFX::ImageEffectDescriptor* desc = it2->second; 01882 delete desc; 01883 } 01884 toBeDeleted.clear(); 01885 } 01886 { 01887 OFX::OfxPlugInfoMap::iterator it = OFX::Private::plugInfoMap.find( id ); 01888 if( it == OFX::Private::plugInfoMap.end() ) 01889 BOOST_THROW_EXCEPTION( OFX::Exception::Suite( kOfxStatErrBadIndex, std::string("Can't unload the plugin \"") + id + "\".") ); 01890 OfxPlugin* plugin = it->second._plug; 01891 OFX::OfxPluginArray::iterator it2 = std::find( ofxPlugs.begin(), ofxPlugs.end(), plugin ); 01892 if( it2 == ofxPlugs.end() ) 01893 BOOST_THROW_EXCEPTION( OFX::Exception::Suite( kOfxStatErrBadIndex, std::string("Can't unload the plugin \"") + id + "\".") ); 01894 (*it2) = NULL; // set the plugin as unloaded but keep the index 01895 delete plugin; 01896 //it->second._plug = NULL; // delete the plugin but keep an entry into the map 01897 } 01898 01899 if( Private::gLoadCount == 0 ) 01900 { 01901 // force these to null 01902 gEffectSuite = NULL; 01903 gPropSuite = NULL; 01904 gParamSuite = NULL; 01905 gMemorySuite = NULL; 01906 gThreadSuite = NULL; 01907 gMessageSuite = NULL; 01908 gInteractSuite = NULL; 01909 gParametricParameterSuite = NULL; 01910 gCameraParameterSuite = NULL; 01911 01912 ofxPlugs.clear(); 01913 gHasInit = false; 01914 } 01915 } 01916 01917 /** @brief fetches our pointer out of the props on the handle */ 01918 ImageEffect* retrieveImageEffectPointer( OfxImageEffectHandle handle ) 01919 { 01920 ImageEffect* instance; 01921 01922 // get the prop set on the handle 01923 OfxPropertySetHandle propHandle; 01924 OfxStatus stat = OFX::Private::gEffectSuite->getPropertySet( handle, &propHandle ); 01925 01926 throwSuiteStatusException( stat ); 01927 01928 // make our wrapper object 01929 PropertySet props( propHandle ); 01930 01931 // fetch the instance data out of the properties 01932 instance = (ImageEffect*) props.propGetPointer( kOfxPropInstanceData ); 01933 01934 OFX::Log::error( instance == NULL, "Instance data handle in effect instance properties is NULL!" ); 01935 01936 if( instance == NULL ) 01937 { 01938 BOOST_THROW_EXCEPTION( OFX::Exception::Suite( kOfxStatErrBadHandle, std::string("Can't retrieve ImageEffect pointer from ofxImageEffectHandle. (property: ") + kOfxPropInstanceData + " is NULL).\nThe plugin will crash...") ); 01939 } 01940 // and dance to the music 01941 return instance; 01942 } 01943 01944 /** @brief Checks the handles passed into the plugin's main entry point */ 01945 void checkMainHandles( const std::string& action, const void* handle, 01946 OfxPropertySetHandle inArgsHandle, OfxPropertySetHandle outArgsHandle, 01947 bool handleCanBeNull, bool inArgsCanBeNull, bool outArgsCanBeNull ) 01948 { 01949 if( handleCanBeNull ) 01950 OFX::Log::warning( handle != 0, "Handle passed to '%s' is not null.", action.c_str() ); 01951 else 01952 OFX::Log::error( handle == 0, "'Handle passed to '%s' is null.", action.c_str() ); 01953 01954 if( inArgsCanBeNull ) 01955 OFX::Log::warning( inArgsHandle != 0, "'inArgs' Handle passed to '%s' is not null.", action.c_str() ); 01956 else 01957 OFX::Log::error( inArgsHandle == 0, "'inArgs' handle passed to '%s' is null.", action.c_str() ); 01958 01959 if( outArgsCanBeNull ) 01960 OFX::Log::warning( outArgsHandle != 0, "'outArgs' Handle passed to '%s' is not null.", action.c_str() ); 01961 else 01962 OFX::Log::error( outArgsHandle == 0, "'outArgs' handle passed to '%s' is null.", action.c_str() ); 01963 01964 // validate the property sets on the arguments 01965 OFX::Validation::validateActionArgumentsProperties( action, inArgsHandle, outArgsHandle ); 01966 01967 // throw exceptions if null when not meant to be null 01968 if( !handleCanBeNull && !handle ) 01969 throwSuiteStatusException( kOfxStatErrBadHandle ); 01970 if( !inArgsCanBeNull && !inArgsHandle ) 01971 throwSuiteStatusException( kOfxStatErrBadHandle ); 01972 if( !outArgsCanBeNull && !outArgsHandle ) 01973 throwSuiteStatusException( kOfxStatErrBadHandle ); 01974 } 01975 01976 /** @brief Fetches the arguments used in a render action 'inargs' property set into a POD struct */ 01977 void getRenderActionArguments( RenderArguments& args, OFX::PropertySet inArgs ) 01978 { 01979 args.time = inArgs.propGetDouble( kOfxPropTime ); 01980 01981 args.renderScale.x = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 0 ); 01982 args.renderScale.y = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 1 ); 01983 01984 args.renderWindow.x1 = inArgs.propGetInt( kOfxImageEffectPropRenderWindow, 0 ); 01985 args.renderWindow.y1 = inArgs.propGetInt( kOfxImageEffectPropRenderWindow, 1 ); 01986 args.renderWindow.x2 = inArgs.propGetInt( kOfxImageEffectPropRenderWindow, 2 ); 01987 args.renderWindow.y2 = inArgs.propGetInt( kOfxImageEffectPropRenderWindow, 3 ); 01988 01989 std::string str = inArgs.propGetString( kOfxImageEffectPropFieldToRender ); 01990 try 01991 { 01992 args.fieldToRender = mapFieldStringToEnum( str ); 01993 } 01994 catch( std::invalid_argument ) 01995 { 01996 // dud field? 01997 OFX::Log::error( true, "Unknown field to render '%s'", str.c_str() ); 01998 01999 // HACK need to throw something to cause a failure 02000 } 02001 } 02002 02003 /** @brief Library side render action, fetches relevant properties and calls the client code */ 02004 void renderAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs ) 02005 { 02006 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02007 RenderArguments args; 02008 02009 // get the arguments 02010 getRenderActionArguments( args, inArgs ); 02011 02012 // and call the plugin client render code 02013 effectInstance->render( args ); 02014 } 02015 02016 /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */ 02017 void beginSequenceRenderAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs ) 02018 { 02019 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02020 02021 BeginSequenceRenderArguments args; 02022 02023 args.frameRange.min = inArgs.propGetDouble( kOfxImageEffectPropFrameRange, 0 ); 02024 args.frameRange.max = inArgs.propGetDouble( kOfxImageEffectPropFrameRange, 1 ); 02025 02026 args.frameStep = inArgs.propGetDouble( kOfxImageEffectPropFrameStep, 0 ); 02027 02028 args.renderScale.x = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 0 ); 02029 args.renderScale.y = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 1 ); 02030 02031 args.isInteractive = inArgs.propGetInt( kOfxPropIsInteractive ) != 0; 02032 02033 // and call the plugin client render code 02034 effectInstance->beginSequenceRender( args ); 02035 } 02036 02037 /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */ 02038 void endSequenceRenderAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs ) 02039 { 02040 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02041 02042 EndSequenceRenderArguments args; 02043 02044 args.renderScale.x = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 0 ); 02045 args.renderScale.y = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 1 ); 02046 02047 args.isInteractive = inArgs.propGetInt( kOfxPropIsInteractive ) != 0; 02048 02049 // and call the plugin client render code 02050 effectInstance->endSequenceRender( args ); 02051 } 02052 02053 /** @brief Library side render begin sequence render action, fetches relevant properties and calls the client code */ 02054 bool isIdentityAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet& outArgs ) 02055 { 02056 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02057 RenderArguments args; 02058 02059 // get the arguments 02060 getRenderActionArguments( args, inArgs ); 02061 02062 // and call the plugin client isIdentity code 02063 Clip* identityClip = 0; 02064 double identityTime = args.time; 02065 bool v = effectInstance->isIdentity( args, identityClip, identityTime ); 02066 02067 if( v && identityClip ) 02068 { 02069 outArgs.propSetString( kOfxPropName, identityClip->name() ); 02070 outArgs.propSetDouble( kOfxPropTime, identityTime ); 02071 return true; 02072 } 02073 return false; 02074 } 02075 02076 /** @brief Library side get region of definition function */ 02077 bool regionOfDefinitionAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet& outArgs ) 02078 { 02079 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02080 RegionOfDefinitionArguments args; 02081 02082 args.renderScale.x = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 0 ); 02083 args.renderScale.y = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 1 ); 02084 02085 args.time = inArgs.propGetDouble( kOfxPropTime ); 02086 02087 // and call the plugin client code 02088 OfxRectD rod; 02089 rod.x1 = outArgs.propGetDouble(kOfxImageEffectPropRegionOfDefinition, 0); 02090 rod.y1 = outArgs.propGetDouble(kOfxImageEffectPropRegionOfDefinition, 1); 02091 rod.x2 = outArgs.propGetDouble(kOfxImageEffectPropRegionOfDefinition, 2); 02092 rod.y2 = outArgs.propGetDouble(kOfxImageEffectPropRegionOfDefinition, 3); 02093 bool v = effectInstance->getRegionOfDefinition( args, rod ); 02094 02095 if( v ) 02096 { 02097 outArgs.propSetDouble( kOfxImageEffectPropRegionOfDefinition, rod.x1, 0 ); 02098 outArgs.propSetDouble( kOfxImageEffectPropRegionOfDefinition, rod.y1, 1 ); 02099 outArgs.propSetDouble( kOfxImageEffectPropRegionOfDefinition, rod.x2, 2 ); 02100 outArgs.propSetDouble( kOfxImageEffectPropRegionOfDefinition, rod.y2, 3 ); 02101 return true; 02102 } 02103 return false; 02104 } 02105 02106 /** @brief Library side get regions of interest function */ 02107 bool regionsOfInterestAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet& outArgs, const char* plugname ) 02108 { 02109 /** @brief local class to set the roi of a clip */ 02110 class ActualROISetter : public OFX::RegionOfInterestSetter 02111 { 02112 bool doneSomething_; 02113 OFX::PropertySet& outArgs_; 02114 const std::map<std::string, std::string>& clipROIPropNames_; 02115 const char* _plugname; 02116 02117 public: 02118 /** @brief ctor */ 02119 ActualROISetter( OFX::PropertySet& args, const std::map<std::string, std::string>& clipROIPropNames ) 02120 : doneSomething_( false ), 02121 outArgs_( args ), 02122 clipROIPropNames_( clipROIPropNames ) 02123 {} 02124 02125 /** @brief did we set something ? */ 02126 bool didSomething( void ) const { return doneSomething_; } 02127 02128 /** @brief set the RoI of the clip */ 02129 virtual void setRegionOfInterest( const Clip& clip, const OfxRectD& roi ) 02130 { 02131 std::map<std::string, std::string>::const_iterator it = clipROIPropNames_.find( clip.name() ); 02132 if( it == clipROIPropNames_.end() ) 02133 BOOST_THROW_EXCEPTION( Exception::PropertyUnknownToHost( clip.name() ) ); 02134 02135 // construct the name of the property 02136 const std::string& propName = it->second; 02137 02138 // and set it 02139 outArgs_.propSetDouble( propName.c_str(), roi.x1, 0 ); 02140 outArgs_.propSetDouble( propName.c_str(), roi.y1, 1 ); 02141 outArgs_.propSetDouble( propName.c_str(), roi.x2, 2 ); 02142 outArgs_.propSetDouble( propName.c_str(), roi.y2, 3 ); 02143 02144 // and record the face we have done something 02145 doneSomething_ = true; 02146 } 02147 02148 }; // end of local class 02149 02150 // fetch our effect pointer 02151 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02152 RegionsOfInterestArguments args; 02153 02154 // fetch in arguments from the prop handle 02155 args.renderScale.x = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 0 ); 02156 args.renderScale.y = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 1 ); 02157 02158 args.regionOfInterest.x1 = inArgs.propGetDouble( kOfxImageEffectPropRegionOfInterest, 0 ); 02159 args.regionOfInterest.y1 = inArgs.propGetDouble( kOfxImageEffectPropRegionOfInterest, 1 ); 02160 args.regionOfInterest.x2 = inArgs.propGetDouble( kOfxImageEffectPropRegionOfInterest, 2 ); 02161 args.regionOfInterest.y2 = inArgs.propGetDouble( kOfxImageEffectPropRegionOfInterest, 3 ); 02162 02163 args.time = inArgs.propGetDouble( kOfxPropTime ); 02164 02165 // make a roi setter object 02166 ActualROISetter setRoIs( outArgs, gEffectDescriptors[plugname][effectInstance->getContext()]->getClipROIPropNames() ); 02167 02168 // and call the plugin client code 02169 effectInstance->getRegionsOfInterest( args, setRoIs ); 02170 02171 // did we do anything ? 02172 if( setRoIs.didSomething() ) 02173 return true; 02174 return false; 02175 } 02176 02177 /** @brief Library side frames needed action */ 02178 bool framesNeededAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs, OFX::PropertySet& outArgs, const char* plugname ) 02179 { 02180 /** @brief local class to set the frames needed from a clip */ 02181 class ActualSetter : public OFX::FramesNeededSetter 02182 { 02183 OFX::PropertySet& outArgs_; /**< @brief property set to set values in */ 02184 std::map<std::string, std::vector<OfxRangeD> > frameRanges_; /**< @brief map holding a bunch of frame ranges, one for each clip */ 02185 const std::map<std::string, std::string>& _clipFrameRangePropNames; 02186 02187 public: 02188 /** @brief ctor */ 02189 ActualSetter( OFX::PropertySet& args, const std::map<std::string, std::string>& clipFrameRangePropNames ) 02190 : outArgs_( args ), 02191 _clipFrameRangePropNames( clipFrameRangePropNames ) 02192 {} 02193 02194 /** @brief set the RoI of the clip */ 02195 virtual void setFramesNeeded( const Clip& clip, const OfxRangeD& range ) 02196 { 02197 // insert this into the vector which is in the map 02198 frameRanges_[clip.name()].push_back( range ); 02199 } 02200 02201 /** @brief write frameRanges_ back to the property set */ 02202 bool setOutProperties( void ) 02203 { 02204 bool didSomething = false; 02205 02206 std::map<std::string, std::vector<OfxRangeD> >::iterator i; 02207 02208 for( i = frameRanges_.begin(); i != frameRanges_.end(); ++i ) 02209 { 02210 if( i->first != kOfxImageEffectOutputClipName ) 02211 { 02212 didSomething = true; 02213 02214 // Make the property name we are setting 02215 const std::map<std::string, std::string>::const_iterator it = _clipFrameRangePropNames.find( i->first ); 02216 if( it == _clipFrameRangePropNames.end() ) 02217 BOOST_THROW_EXCEPTION( Exception::PropertyUnknownToHost( i->first ) ); 02218 02219 const std::string& propName = it->second; 02220 02221 // fetch the list of frame ranges 02222 std::vector<OfxRangeD>& clipRange = i->second; 02223 std::vector<OfxRangeD>::iterator j; 02224 int n = 0; 02225 02226 // and set 'em 02227 for( j = clipRange.begin(); j < clipRange.end(); ++j ) 02228 { 02229 outArgs_.propSetDouble( propName.c_str(), j->min, n++ ); 02230 outArgs_.propSetDouble( propName.c_str(), j->max, n++ ); 02231 } 02232 } 02233 } 02234 02235 return didSomething; 02236 } 02237 02238 }; // end of local class 02239 02240 // fetch our effect pointer 02241 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02242 FramesNeededArguments args; 02243 02244 // fetch in arguments from the prop handle 02245 args.time = inArgs.propGetDouble( kOfxPropTime ); 02246 02247 // make a roi setter object 02248 ActualSetter setFrames( outArgs, gEffectDescriptors[plugname][effectInstance->getContext()]->getClipFrameRangePropNames() ); 02249 02250 // and call the plugin client code 02251 effectInstance->getFramesNeeded( args, setFrames ); 02252 02253 // Write it back to the properties and see if we set anything 02254 if( setFrames.setOutProperties() ) 02255 return true; 02256 return false; 02257 } 02258 02259 /** @brief Library side get regions of interest function */ 02260 bool getTimeDomainAction( OfxImageEffectHandle handle, OFX::PropertySet& outArgs ) 02261 { 02262 // fetch our effect pointer 02263 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02264 02265 // we can only be a general context effect, so check that this is true 02266 OFX::Log::error( 02267 effectInstance->getContext() != eContextGeneral && 02268 effectInstance->getContext() != eContextReader && 02269 effectInstance->getContext() != eContextGenerator 02270 , 02271 "Calling kOfxImageEffectActionGetTimeDomain on an effect that is not a 'general', 'reader' or 'generator' context node." ); 02272 02273 OfxRangeD timeDomain; 02274 timeDomain.min = outArgs.propGetDouble( kOfxImageEffectPropFrameRange, 0 ); 02275 timeDomain.max = outArgs.propGetDouble( kOfxImageEffectPropFrameRange, 1 ); 02276 02277 // and call the plugin client code 02278 bool v = effectInstance->getTimeDomain( timeDomain ); 02279 02280 if( v ) 02281 { 02282 outArgs.propSetDouble( kOfxImageEffectPropFrameRange, timeDomain.min, 0 ); 02283 outArgs.propSetDouble( kOfxImageEffectPropFrameRange, timeDomain.max, 1 ); 02284 } 02285 02286 return v; 02287 } 02288 02289 /** @brief Library side get regions of interest function */ 02290 bool clipPreferencesAction( OfxImageEffectHandle handle, OFX::PropertySet& outArgs, const char* plugname ) 02291 { 02292 // fetch our effect pointer 02293 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02294 02295 // set up our clip preferences setter 02296 ImageEffectDescriptor* desc = gEffectDescriptors[plugname][effectInstance->getContext()]; 02297 ClipPreferencesSetter prefs( outArgs, desc->getClipDepthPropNames(), desc->getClipComponentPropNames(), desc->getClipPARPropNames() ); 02298 02299 // and call the plug-in client code 02300 effectInstance->getClipPreferences( prefs ); 02301 02302 // did we do anything ? 02303 if( prefs.didSomething() ) 02304 return true; 02305 return false; 02306 } 02307 02308 /** @brief Library side begin instance changed action */ 02309 void beginInstanceChangedAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs ) 02310 { 02311 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02312 02313 std::string reasonStr = inArgs.propGetString( kOfxPropChangeReason ); 02314 InstanceChangeReason reason = mapInstanceChangedReasonStringToEnum( reasonStr ); 02315 02316 // and call the plugin client code 02317 effectInstance->beginChanged( reason ); 02318 } 02319 02320 /** @brief Library side instance changed action */ 02321 void instanceChangedAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs ) 02322 { 02323 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02324 02325 InstanceChangedArgs args; 02326 02327 // why did it change 02328 std::string reasonStr = inArgs.propGetString( kOfxPropChangeReason ); 02329 02330 args.reason = mapInstanceChangedReasonStringToEnum( reasonStr ); 02331 args.time = inArgs.propGetDouble( kOfxPropTime ); 02332 args.renderScale.x = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 0 ); 02333 args.renderScale.y = inArgs.propGetDouble( kOfxImageEffectPropRenderScale, 1 ); 02334 02335 // what changed 02336 std::string changedType = inArgs.propGetString( kOfxPropType ); 02337 std::string changedName = inArgs.propGetString( kOfxPropName ); 02338 02339 if( changedType == kOfxTypeParameter ) 02340 { 02341 // and call the plugin client code 02342 effectInstance->changedParam( args, changedName ); 02343 } 02344 else if( changedType == kOfxTypeClip ) 02345 { 02346 // and call the plugin client code 02347 effectInstance->changedClip( args, changedName ); 02348 } 02349 else 02350 { 02351 OFX::Log::error( true, "Instance Changed called with unknown type '%s' of object '%s'", changedType.c_str(), changedName.c_str() ); 02352 } 02353 } 02354 02355 /** @brief Library side end instance changed action */ 02356 void endInstanceChangedAction( OfxImageEffectHandle handle, OFX::PropertySet inArgs ) 02357 { 02358 ImageEffect* effectInstance = retrieveImageEffectPointer( handle ); 02359 02360 std::string reasonStr = inArgs.propGetString( kOfxPropChangeReason ); 02361 InstanceChangeReason reason = mapInstanceChangedReasonStringToEnum( reasonStr ); 02362 02363 // and call the plugin client code 02364 effectInstance->endChanged( reason ); 02365 } 02366 02367 /** @brief The main entry point for the plugin 02368 */ 02369 OfxStatus mainEntryStr( const char* actionRaw, 02370 const void* handleRaw, 02371 OfxPropertySetHandle inArgsRaw, 02372 OfxPropertySetHandle outArgsRaw, 02373 const char* plugname ) 02374 { 02375 OFX::Log::print( "********************************************************************************" ); 02376 OFX::Log::print( "START mainEntry (%s)", actionRaw ); 02377 OFX::Log::indent(); 02378 OfxStatus stat = kOfxStatReplyDefault; 02379 02380 // Cast the raw handle to be an image effect handle, because that is what it is 02381 OfxImageEffectHandle handle = (OfxImageEffectHandle) handleRaw; 02382 02383 // Turn the arguments into wrapper objects to make our lives easier 02384 OFX::PropertySet inArgs( inArgsRaw ); 02385 OFX::PropertySet outArgs( outArgsRaw ); 02386 02387 try 02388 { 02389 // turn the action into a std::string 02390 std::string action( actionRaw ); 02391 OfxPlugInfoMap::iterator it = plugInfoMap.find( plugname ); 02392 if( it == plugInfoMap.end() ) 02393 BOOST_THROW_EXCEPTION( std::logic_error( "Action not recognized: " + action ) ); 02394 OFX::PluginFactory* factory = it->second._factory; 02395 02396 // figure the actions 02397 if( action == kOfxActionLoad ) 02398 { 02399 // call the support load function, param-less 02400 OFX::Private::loadAction(); 02401 02402 // call the plugin side load action, param-less 02403 factory->load(); 02404 02405 // got here, must be good 02406 stat = kOfxStatOK; 02407 } 02408 // figure the actions 02409 else if( action == kOfxActionUnload ) 02410 { 02411 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, true, true, true ); 02412 02413 // call the plugin side unload action, param-less, should be called, eve if the stat above failed! 02414 factory->unload(); 02415 02416 // call the support unload function, param-less 02417 OFX::Private::unloadAction( plugname ); 02418 02419 // got here, must be good 02420 stat = kOfxStatOK; 02421 } 02422 02423 else if( action == kOfxActionDescribe ) 02424 { 02425 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true ); 02426 02427 // make the plugin descriptor 02428 ImageEffectDescriptor* desc = new ImageEffectDescriptor( handle ); 02429 02430 // validate the host 02431 OFX::Validation::validatePluginDescriptorProperties( fetchEffectProps( handle ) ); 02432 02433 // and pass it to the plugin to do something with it 02434 02435 factory->describe( *desc ); 02436 02437 // add it to our map 02438 gEffectDescriptors[plugname][eContextNone] = desc; 02439 02440 // got here, must be good 02441 stat = kOfxStatOK; 02442 } 02443 else if( action == kOfxImageEffectActionDescribeInContext ) 02444 { 02445 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true ); 02446 02447 // make the plugin descriptor and pass it to the plugin to do something with it 02448 ImageEffectDescriptor* desc = new ImageEffectDescriptor( handle ); 02449 02450 // figure the context and map it to an enum 02451 std::string contextStr = inArgs.propGetString( kOfxImageEffectPropContext ); 02452 EContext context = mapContextStringToEnum( contextStr ); 02453 02454 // validate the host 02455 OFX::Validation::validatePluginDescriptorProperties( fetchEffectProps( handle ) ); 02456 02457 // call plugin descibe in context 02458 factory->describeInContext( *desc, context ); 02459 02460 // add it to our map 02461 gEffectDescriptors[plugname][context] = desc; 02462 02463 // got here, must be good 02464 stat = kOfxStatOK; 02465 } 02466 else if( action == kOfxActionCreateInstance ) 02467 { 02468 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true ); 02469 02470 // fetch the effect props to figure the context 02471 PropertySet effectProps = fetchEffectProps( handle ); 02472 02473 // get the context and turn it into an enum 02474 std::string str = effectProps.propGetString( kOfxImageEffectPropContext ); 02475 EContext context = mapContextStringToEnum( str ); 02476 02477 // make the image effect instance for this context 02478 /*ImageEffect *instance = */ factory->createInstance( handle, context ); 02479 02480 // validate the plugin handle's properties 02481 OFX::Validation::validatePluginInstanceProperties( fetchEffectProps( handle ) ); 02482 02483 // got here, must be good 02484 stat = kOfxStatOK; 02485 } 02486 else if( action == kOfxActionDestroyInstance ) 02487 { 02488 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true ); 02489 02490 // fetch our pointer out of the props on the handle 02491 ImageEffect* instance = retrieveImageEffectPointer( handle ); 02492 02493 // kill it 02494 delete instance; 02495 02496 // got here, must be good 02497 stat = kOfxStatOK; 02498 } 02499 else if( action == kOfxImageEffectActionRender ) 02500 { 02501 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true ); 02502 02503 // call the render action skin 02504 renderAction( handle, inArgs ); 02505 02506 // got here, must be good 02507 stat = kOfxStatOK; 02508 } 02509 else if( action == kOfxImageEffectActionBeginSequenceRender ) 02510 { 02511 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true ); 02512 02513 // call the begin render action skin 02514 beginSequenceRenderAction( handle, inArgs ); 02515 } 02516 else if( action == kOfxImageEffectActionEndSequenceRender ) 02517 { 02518 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true ); 02519 02520 // call the begin render action skin 02521 endSequenceRenderAction( handle, inArgs ); 02522 } 02523 else if( action == kOfxImageEffectActionIsIdentity ) 02524 { 02525 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false ); 02526 02527 // call the identity action, if it is, return OK 02528 if( isIdentityAction( handle, inArgs, outArgs ) ) 02529 stat = kOfxStatOK; 02530 } 02531 else if( action == kOfxImageEffectActionGetRegionOfDefinition ) 02532 { 02533 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false ); 02534 02535 // call the rod action, return OK if it does something 02536 if( regionOfDefinitionAction( handle, inArgs, outArgs ) ) 02537 stat = kOfxStatOK; 02538 } 02539 else if( action == kOfxImageEffectActionGetRegionsOfInterest ) 02540 { 02541 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false ); 02542 02543 // call the RoI action, return OK if it does something 02544 if( regionsOfInterestAction( handle, inArgs, outArgs, plugname ) ) 02545 stat = kOfxStatOK; 02546 } 02547 else if( action == kOfxImageEffectActionGetFramesNeeded ) 02548 { 02549 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, false ); 02550 02551 // call the frames needed action, return OK if it does something 02552 if( framesNeededAction( handle, inArgs, outArgs, plugname ) ) 02553 stat = kOfxStatOK; 02554 } 02555 else if( action == kOfxImageEffectActionGetClipPreferences ) 02556 { 02557 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, false ); 02558 02559 // call the frames needed action, return OK if it does something 02560 if( clipPreferencesAction( handle, outArgs, plugname ) ) 02561 stat = kOfxStatOK; 02562 } 02563 else if( action == kOfxActionPurgeCaches ) 02564 { 02565 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true ); 02566 02567 // fetch our pointer out of the props on the handle 02568 ImageEffect* instance = retrieveImageEffectPointer( handle ); 02569 02570 // purge 'em 02571 instance->purgeCaches(); 02572 } 02573 else if( action == kOfxActionSyncPrivateData ) 02574 { 02575 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true ); 02576 02577 // fetch our pointer out of the props on the handle 02578 ImageEffect* instance = retrieveImageEffectPointer( handle ); 02579 02580 // and sync it 02581 instance->syncPrivateData(); 02582 } 02583 else if( action == kOfxImageEffectActionGetTimeDomain ) 02584 { 02585 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, false ); 02586 02587 // call the instance changed action 02588 if( getTimeDomainAction( handle, outArgs ) ) 02589 stat = kOfxStatOK; 02590 02591 // fetch our pointer out of the props on the handle 02592 /*ImageEffect *instance = */ retrieveImageEffectPointer( handle ); 02593 02594 } 02595 else if( action == kOfxActionBeginInstanceChanged ) 02596 { 02597 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true ); 02598 02599 // call the instance changed action 02600 beginInstanceChangedAction( handle, inArgs ); 02601 } 02602 else if( action == kOfxActionInstanceChanged ) 02603 { 02604 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true ); 02605 02606 // call the instance changed action 02607 instanceChangedAction( handle, inArgs ); 02608 } 02609 else if( action == kOfxActionEndInstanceChanged ) 02610 { 02611 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, false, true ); 02612 02613 // call the instance changed action 02614 endInstanceChangedAction( handle, inArgs ); 02615 } 02616 else if( action == kOfxActionBeginInstanceEdit ) 02617 { 02618 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true ); 02619 02620 // fetch our pointer out of the props on the handle 02621 ImageEffect* instance = retrieveImageEffectPointer( handle ); 02622 02623 // call the begin edit function 02624 instance->beginEdit(); 02625 } 02626 else if( action == kOfxActionEndInstanceEdit ) 02627 { 02628 checkMainHandles( actionRaw, handleRaw, inArgsRaw, outArgsRaw, false, true, true ); 02629 02630 // fetch our pointer out of the props on the handle 02631 ImageEffect* instance = retrieveImageEffectPointer( handle ); 02632 02633 // call the end edit function 02634 instance->endEdit(); 02635 } 02636 else if( actionRaw ) 02637 { 02638 OFX::Log::error( true, "Unknown action '%s'.", actionRaw ); 02639 } 02640 else 02641 { 02642 OFX::Log::error( true, "Requested action was a null pointer." ); 02643 } 02644 } 02645 catch( boost::exception& e ) 02646 { 02647 std::cerr << tuttle::common::Color::get()->_error; 02648 std::cerr << "__________" << std::endl; 02649 /* 02650 [tuttle::exception::tag_devMessage*] = DPX: Unable to open file. 02651 [OFX::tag_ofxStatus*] = kOfxStatErrValue 02652 [boost::errinfo_file_name_*] = /datas/tmp/master32secsh01.0014.dpx 02653 */ 02654 if( const boost::error_info_sstream* const messageException = boost::get_error_info< tuttle::exception::user >(e) ) 02655 { 02656 std::cerr << "Error: " << *messageException << std::endl; 02657 } 02658 else 02659 { 02660 std::cerr << "Error: " "No message." << std::endl; 02661 } 02662 if( const std::string* const filenameException = boost::get_error_info< ::boost::errinfo_file_name >(e) ) 02663 { 02664 std::cerr << "filename: \"" << *filenameException << "\"" << std::endl; 02665 } 02666 02667 #ifndef TUTTLE_PRODUCTION 02668 std::cerr << "__________" << std::endl; 02669 std::cerr << "* Caught boost::exception on action " << actionRaw << std::endl; 02670 #ifndef BOOST_EXCEPTION_DISABLE 02671 std::cerr << boost::diagnostic_information(e); 02672 #endif 02673 // std::cerr << "* inArgs: " << inArgs << std::endl; 02674 std::cerr << "----------" << std::endl; 02675 std::cerr << "* Backtrace" << std::endl; 02676 std::cerr << boost::trace(e); 02677 #endif 02678 std::cerr << "__________" << std::endl; 02679 std::cerr << tuttle::common::Color::get()->_std; 02680 02681 /// @todo there is an assert in boost::get_error_info here. Why? 02682 if( const ::OfxStatus* status = boost::get_error_info< ::OFX::ofxStatus >( e ) ) 02683 { 02684 stat = *status; 02685 } 02686 else 02687 { 02688 stat = kOfxStatFailed; 02689 } 02690 } 02691 02692 // catch suite exceptions 02693 catch( OFX::Exception::Suite& e ) 02694 { 02695 std::cerr << "Caught OFX::Exception::Suite (" << e.what() << ")" << std::endl; 02696 stat = e.status(); 02697 } 02698 02699 // catch host inadequate exceptions 02700 catch( OFX::Exception::HostInadequate& e ) 02701 { 02702 std::cerr << "Caught OFX::Exception::HostInadequate (" << e.what() << ")" << std::endl; 02703 stat = kOfxStatErrMissingHostFeature; 02704 } 02705 02706 // catch exception due to a property being unknown to the host, implies something wrong with host if not caught further down 02707 catch( OFX::Exception::PropertyUnknownToHost& e ) 02708 { 02709 std::cerr << "Caught OFX::Exception::PropertyUnknownToHost (" << e.what() << ")" << std::endl; 02710 stat = kOfxStatErrMissingHostFeature; 02711 } 02712 02713 // catch memory 02714 catch( std::bad_alloc& e ) 02715 { 02716 std::cerr << "Caught std::bad_alloc (" << e.what() << ")" << std::endl; 02717 stat = kOfxStatErrMemory; 02718 } 02719 02720 // catch a custom client exception, if defined 02721 #ifdef OFX_CLIENT_EXCEPTION_TYPE 02722 catch( OFX_CLIENT_EXCEPTION_TYPE& e ) 02723 { 02724 std::cerr << "Caught OFX_CLIENT_EXCEPTION (" << e.what() << ")" << std::endl; 02725 stat = OFX_CLIENT_EXCEPTION_HANDLER( e, plugname ); 02726 } 02727 #endif 02728 02729 // catch all exceptions 02730 catch( std::exception& e ) 02731 { 02732 std::cerr << "Caught std::exception on action " << actionRaw << " (" << e.what() << ")" << std::endl; 02733 stat = kOfxStatFailed; 02734 } 02735 02736 // Catch anything else, unknown 02737 catch( ... ) 02738 { 02739 std::cerr << "Caught Unknown exception (file:" << __FILE__ << " line:" << __LINE__ << ")" << std::endl; 02740 stat = kOfxStatFailed; 02741 } 02742 02743 OFX::Log::outdent(); 02744 OFX::Log::print( "STOP mainEntry (%s)\n", actionRaw ); 02745 return stat; 02746 } 02747 02748 /** @brief The plugin function that gets passed the host structure. */ 02749 void setHost( OfxHost* host ) 02750 { 02751 gHost = host; 02752 } 02753 02754 OfxPlugInfo generatePlugInfo( PluginFactory* factory ) 02755 { 02756 std::auto_ptr<OfxPlugin> ofxPlugin( new OfxPlugin() ); 02757 ofxPlugin->pluginApi = kOfxImageEffectPluginApi; 02758 ofxPlugin->apiVersion = kOfxImageEffectPluginApiVersion; 02759 ofxPlugin->pluginIdentifier = factory->getID().c_str(); 02760 ofxPlugin->pluginVersionMajor = factory->getMajorVersion(); 02761 ofxPlugin->pluginVersionMinor = factory->getMinorVersion(); 02762 ofxPlugin->setHost = OFX::Private::setHost; 02763 ofxPlugin->mainEntry = factory->getMainEntry(); 02764 return OfxPlugInfo( factory, ofxPlugin.release() ); 02765 } 02766 02767 } // namespace Private 02768 02769 /** @brief Fetch's a suite from the host and logs errors */ 02770 void* fetchSuite( const char* suiteName, int suiteVersion, bool optional ) 02771 { 02772 void* suite = Private::gHost->fetchSuite( Private::gHost->host, suiteName, suiteVersion ); 02773 02774 if( suite == 0 ) 02775 { 02776 if( optional ) 02777 OFX::Log::warning( suite == 0, "Could not fetch the optional suite '%s' version %d.", suiteName, suiteVersion ); 02778 else 02779 OFX::Log::error( suite == 0, "Could not fetch the mandatory suite '%s' version %d.", suiteName, suiteVersion ); 02780 } 02781 if( !optional && suite == 0 ) 02782 BOOST_THROW_EXCEPTION( OFX::Exception::HostInadequate( suiteName ) ); 02783 return suite; 02784 } 02785 02786 } // namespace OFX 02787 02788 namespace OFX { 02789 namespace Plugin { 02790 void getPluginIDs( OFX::PluginFactoryArray& ids ); 02791 } 02792 } 02793 02794 02795 void init() 02796 { 02797 using namespace OFX; 02798 using namespace OFX::Private; 02799 if( gHasInit ) 02800 { 02801 return; 02802 } 02803 02804 ::OFX::Plugin::getPluginIDs( plugIDs ); 02805 ofxPlugs.reserve( plugIDs.size() ); 02806 02807 BOOST_FOREACH( PluginFactory* factory, plugIDs ) 02808 { 02809 OfxPlugInfo info = generatePlugInfo( factory ); 02810 const std::string newID = factory->getUID(); 02811 plugInfoMap[newID] = info; 02812 ofxPlugs.push_back( info._plug ); 02813 } 02814 gHasInit = true; 02815 } 02816 02817 02818 /** @brief, mandated function returning the number of plugins, which is always 1 */ 02819 OfxExport int OfxGetNumberOfPlugins( void ) 02820 { 02821 init(); 02822 return static_cast<int>( OFX::plugIDs.size() ); 02823 } 02824 02825 /** @brief, mandated function returning the nth plugin 02826 * 02827 * We call the plugin side defined OFX::Plugin::getPluginID function to find out what to set. 02828 */ 02829 OfxExport OfxPlugin* OfxGetPlugin( int nth ) 02830 { 02831 init(); 02832 const int numPlugs = static_cast<int>( OFX::Private::plugInfoMap.size() ); 02833 OFX::Log::error( nth >= numPlugs, "Host attempted to get plugin %d, when there is only %d plugin(s).", nth, numPlugs ); 02834 BOOST_ASSERT( nth < numPlugs ); 02835 OFX::Log::error( OFX::Private::ofxPlugs[nth] == NULL, "Host attempted to get plugin %d, but it is unloaded.", nth ); 02836 BOOST_ASSERT( OFX::Private::ofxPlugs[nth] != NULL ); 02837 return OFX::Private::ofxPlugs[nth]; 02838 } 02839