TuttleOFX  1
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                 _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