TuttleOFX
1
|
00001 #ifndef _TUTTLE_PLUGIN_IMAGEPROCESSOR_HPP_ 00002 #define _TUTTLE_PLUGIN_IMAGEPROCESSOR_HPP_ 00003 00004 #include "exceptions.hpp" 00005 #include "OfxProgress.hpp" 00006 00007 #include <tuttle/plugin/image.hpp> 00008 #include <tuttle/plugin/exceptions.hpp> 00009 #include <tuttle/common/math/rectOp.hpp> 00010 00011 #include <ofxsImageEffect.h> 00012 #include <ofxsMultiThread.h> 00013 #include <ofxsUtilities.h> 00014 00015 #include <boost/scoped_ptr.hpp> 00016 #include <boost/exception/info.hpp> 00017 #include <boost/exception/error_info.hpp> 00018 #include <boost/throw_exception.hpp> 00019 00020 #include <cstdlib> 00021 #include <vector> 00022 00023 namespace tuttle { 00024 namespace plugin { 00025 00026 /** 00027 * @brief Base class that can be used to process images of any type. 00028 */ 00029 class ImageProcessor : public OFX::MultiThread::Processor 00030 , public tuttle::plugin::OfxProgress 00031 { 00032 protected: 00033 OFX::ImageEffect& _effect; ///< effect to render with 00034 OFX::RenderArguments _renderArgs; ///< render arguments 00035 OFX::Clip* _clipDst; ///< Destination image clip 00036 boost::scoped_ptr<OFX::Image> _dst; 00037 OfxRectI _dstPixelRod; 00038 OfxPointI _dstPixelRodSize; 00039 OfxPointI _renderWindowSize; 00040 EImageOrientation _imageOrientation; 00041 00042 private: 00043 unsigned int _nbThreads; 00044 00045 public: 00046 /** @brief ctor */ 00047 ImageProcessor( OFX::ImageEffect& effect, const EImageOrientation imageOrientation ) 00048 : OfxProgress( effect ) 00049 , _effect( effect ) 00050 , _imageOrientation( imageOrientation ) 00051 , _nbThreads( 0 ) // auto, maximum allowable number of CPUs will be used 00052 { 00053 _dstPixelRod.x1 = _dstPixelRod.y1 = _dstPixelRod.x2 = _dstPixelRod.y2 = 0; 00054 _dstPixelRodSize.x = _dstPixelRodSize.y = 0; 00055 _renderWindowSize.x = _renderWindowSize.y = 0; 00056 _renderArgs.renderWindow.x1 = _renderArgs.renderWindow.y1 = _renderArgs.renderWindow.x2 = _renderArgs.renderWindow.y2 = 0; 00057 _renderArgs.renderScale.x = _renderArgs.renderScale.y = 0; 00058 _renderArgs.time = -1; 00059 _renderArgs.fieldToRender = OFX::eFieldNone; 00060 00061 _clipDst = effect.fetchClip( kOfxImageEffectOutputClipName ); 00062 } 00063 00064 virtual ~ImageProcessor() 00065 {} 00066 00067 void setNoMultiThreading() { _nbThreads = 1; } 00068 void setNbThreads( const unsigned int nbThreads ) { _nbThreads = nbThreads; } 00069 void setNbThreadsAuto() { _nbThreads = 0; } 00070 00071 /** @brief called before any MP is done */ 00072 virtual void preProcess() { progressBegin( _renderWindowSize.y * _renderWindowSize.x ); } 00073 00074 /** @brief called before any MP is done */ 00075 virtual void postProcess() { progressEnd(); } 00076 00077 virtual void setup( const OFX::RenderArguments& args ) 00078 { 00079 // destination view 00080 // TUTTLE_LOG_INFOS; 00081 // TUTTLE_LOG_VAR( TUTTLE_INFO, "dst - fetchImage " << time ); 00082 _dst.reset( _clipDst->fetchImage( args.time ) ); 00083 if( !_dst.get() ) 00084 BOOST_THROW_EXCEPTION( exception::ImageNotReady() 00085 << exception::dev() + "Error on clip " + quotes(_clipDst->name()) 00086 << exception::time( args.time ) ); 00087 if( _dst->getRowDistanceBytes() == 0 ) 00088 BOOST_THROW_EXCEPTION( exception::WrongRowBytes() 00089 << exception::dev() + "Error on clip " + quotes(_clipDst->name()) 00090 << exception::time( args.time ) ); 00091 00092 if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" ) 00093 { 00094 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds 00095 _dstPixelRod = _clipDst->getPixelRod( args.time, args.renderScale ); 00096 } 00097 else 00098 { 00099 _dstPixelRod = _dst->getRegionOfDefinition(); 00100 } 00101 _dstPixelRodSize.x = ( this->_dstPixelRod.x2 - this->_dstPixelRod.x1 ); 00102 _dstPixelRodSize.y = ( this->_dstPixelRod.y2 - this->_dstPixelRod.y1 ); 00103 } 00104 00105 /** @brief fetch output and inputs clips */ 00106 virtual void setupAndProcess( const OFX::RenderArguments& args ) 00107 { 00108 _renderArgs = args; 00109 _renderWindowSize.x = ( _renderArgs.renderWindow.x2 - _renderArgs.renderWindow.x1 ); 00110 _renderWindowSize.y = ( _renderArgs.renderWindow.y2 - _renderArgs.renderWindow.y1 ); 00111 try 00112 { 00113 setup( args ); 00114 } 00115 catch( exception::Common& ) 00116 { 00117 progressEnd(); 00118 00119 // if the host is trying to abort the rendering return without error 00120 if( _effect.abort() ) 00121 { 00122 return; 00123 } 00124 throw; 00125 } 00126 catch(...) 00127 { 00128 progressEnd(); 00129 00130 // if the host is trying to abort the rendering return without error 00131 if( _effect.abort() ) 00132 { 00133 return; 00134 } 00135 throw; 00136 } 00137 00138 // Call the base class process member 00139 this->process(); 00140 } 00141 00142 /** @brief overridden from OFX::MultiThread::Processor. This function is called once on each SMP thread by the base class */ 00143 void multiThreadFunction( const unsigned int threadId, const unsigned int nThreads ) 00144 { 00145 // slice the y range into the number of threads it has 00146 const int dy = std::abs( _renderArgs.renderWindow.y2 - _renderArgs.renderWindow.y1 ); 00147 const int y1 = _renderArgs.renderWindow.y1 + threadId * dy / nThreads; 00148 const int step = ( threadId + 1 ) * dy / nThreads; 00149 const int y2 = _renderArgs.renderWindow.y1 + ( step < dy ? step : dy ); 00150 00151 OfxRectI winRoW = _renderArgs.renderWindow; 00152 winRoW.y1 = y1; 00153 winRoW.y2 = y2; 00154 00155 // and render that thread on each 00156 multiThreadProcessImages( winRoW ); 00157 } 00158 00159 /** @brief this is called by multiThreadFunction to actually process images, override in derived classes */ 00160 virtual void multiThreadProcessImages( const OfxRectI& windowRoW ) = 0; 00161 00162 // to output clip coordinates 00163 OfxRectI translateRoWToOutputClipCoordinates( const OfxRectI& windowRoW ) const 00164 { 00165 return translateRegion( windowRoW, _dstPixelRod ); 00166 } 00167 00168 /** @brief called to process everything */ 00169 virtual void process() 00170 { 00171 // is it OK ? 00172 if( _renderArgs.renderWindow.x2 - _renderArgs.renderWindow.x1 == 0 || 00173 _renderArgs.renderWindow.y2 - _renderArgs.renderWindow.y1 == 0 ) 00174 { 00175 BOOST_THROW_EXCEPTION( exception::ImageFormat() << exception::user( "RenderWindow empty !" ) ); 00176 } 00177 // call the pre MP pass 00178 preProcess(); 00179 00180 // call the base multi threading code, should put a pre & post thread calls in too 00181 multiThread( _nbThreads ); 00182 00183 // call the post MP pass 00184 postProcess(); 00185 } 00186 }; 00187 00188 00189 } 00190 } 00191 00192 #endif 00193