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 _renderArgs.renderWindow.x1 = _renderArgs.renderWindow.y1 = _renderArgs.renderWindow.x2 = _renderArgs.renderWindow.y2 = 0; 00054 _renderArgs.renderScale.x = _renderArgs.renderScale.y = 0; 00055 _renderArgs.time = -1; 00056 _renderArgs.fieldToRender = OFX::eFieldNone; 00057 00058 _clipDst = effect.fetchClip( kOfxImageEffectOutputClipName ); 00059 } 00060 00061 virtual ~ImageProcessor() 00062 {} 00063 00064 void setNoMultiThreading() { _nbThreads = 1; } 00065 void setNbThreads( const unsigned int nbThreads ) { _nbThreads = nbThreads; } 00066 void setNbThreadsAuto() { _nbThreads = 0; } 00067 00068 /** @brief called before any MP is done */ 00069 virtual void preProcess() { progressBegin( _renderWindowSize.y * _renderWindowSize.x ); } 00070 00071 /** @brief called before any MP is done */ 00072 virtual void postProcess() { progressEnd(); } 00073 00074 virtual void setup( const OFX::RenderArguments& args ) 00075 { 00076 // destination view 00077 // TUTTLE_LOG_INFOS; 00078 // TUTTLE_LOG_VAR( TUTTLE_INFO, "dst - fetchImage " << time ); 00079 _dst.reset( _clipDst->fetchImage( args.time ) ); 00080 if( !_dst.get() ) 00081 BOOST_THROW_EXCEPTION( exception::ImageNotReady() 00082 << exception::dev() + "Error on clip " + quotes(_clipDst->name()) 00083 << exception::time( args.time ) ); 00084 if( _dst->getRowDistanceBytes() == 0 ) 00085 BOOST_THROW_EXCEPTION( exception::WrongRowBytes() 00086 << exception::dev() + "Error on clip " + quotes(_clipDst->name()) 00087 << exception::time( args.time ) ); 00088 00089 if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" ) 00090 { 00091 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds 00092 _dstPixelRod = _clipDst->getPixelRod( args.time, args.renderScale ); 00093 } 00094 else 00095 { 00096 _dstPixelRod = _dst->getRegionOfDefinition(); 00097 } 00098 _dstPixelRodSize.x = ( this->_dstPixelRod.x2 - this->_dstPixelRod.x1 ); 00099 _dstPixelRodSize.y = ( this->_dstPixelRod.y2 - this->_dstPixelRod.y1 ); 00100 } 00101 00102 /** @brief fetch output and inputs clips */ 00103 virtual void setupAndProcess( const OFX::RenderArguments& args ) 00104 { 00105 _renderArgs = args; 00106 _renderWindowSize.x = ( _renderArgs.renderWindow.x2 - _renderArgs.renderWindow.x1 ); 00107 _renderWindowSize.y = ( _renderArgs.renderWindow.y2 - _renderArgs.renderWindow.y1 ); 00108 try 00109 { 00110 setup( args ); 00111 } 00112 catch( exception::Common& ) 00113 { 00114 progressEnd(); 00115 00116 // if the host is trying to abort the rendering return without error 00117 if( _effect.abort() ) 00118 { 00119 return; 00120 } 00121 throw; 00122 } 00123 catch(...) 00124 { 00125 progressEnd(); 00126 00127 // if the host is trying to abort the rendering return without error 00128 if( _effect.abort() ) 00129 { 00130 return; 00131 } 00132 throw; 00133 } 00134 00135 // Call the base class process member 00136 this->process(); 00137 } 00138 00139 /** @brief overridden from OFX::MultiThread::Processor. This function is called once on each SMP thread by the base class */ 00140 void multiThreadFunction( const unsigned int threadId, const unsigned int nThreads ) 00141 { 00142 // slice the y range into the number of threads it has 00143 const int dy = std::abs( _renderArgs.renderWindow.y2 - _renderArgs.renderWindow.y1 ); 00144 const int y1 = _renderArgs.renderWindow.y1 + threadId * dy / nThreads; 00145 const int step = ( threadId + 1 ) * dy / nThreads; 00146 const int y2 = _renderArgs.renderWindow.y1 + ( step < dy ? step : dy ); 00147 00148 OfxRectI winRoW = _renderArgs.renderWindow; 00149 winRoW.y1 = y1; 00150 winRoW.y2 = y2; 00151 00152 // and render that thread on each 00153 multiThreadProcessImages( winRoW ); 00154 } 00155 00156 /** @brief this is called by multiThreadFunction to actually process images, override in derived classes */ 00157 virtual void multiThreadProcessImages( const OfxRectI& windowRoW ) = 0; 00158 00159 // to output clip coordinates 00160 OfxRectI translateRoWToOutputClipCoordinates( const OfxRectI& windowRoW ) const 00161 { 00162 return translateRegion( windowRoW, _dstPixelRod ); 00163 } 00164 00165 /** @brief called to process everything */ 00166 virtual void process() 00167 { 00168 // is it OK ? 00169 if( _renderArgs.renderWindow.x2 - _renderArgs.renderWindow.x1 == 0 || 00170 _renderArgs.renderWindow.y2 - _renderArgs.renderWindow.y1 == 0 ) 00171 { 00172 BOOST_THROW_EXCEPTION( exception::ImageFormat() << exception::user( "RenderWindow empty !" ) ); 00173 } 00174 // call the pre MP pass 00175 preProcess(); 00176 00177 // call the base multi threading code, should put a pre & post thread calls in too 00178 multiThread( _nbThreads ); 00179 00180 // call the post MP pass 00181 postProcess(); 00182 } 00183 }; 00184 00185 00186 } 00187 } 00188 00189 #endif 00190