TuttleOFX  1
TuttleOFX/libraries/tuttle/src/tuttle/plugin/ImageProcessor.hpp
Go to the documentation of this file.
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