TuttleOFX  1
MergeProcess.tcc
Go to the documentation of this file.
00001 #include "MergePlugin.hpp"
00002 #include "MergeDefinitions.hpp"
00003 #include <terry/merge/ViewsMerging.hpp>
00004 
00005 #include <tuttle/plugin/numeric/rectOp.hpp>
00006 #include <tuttle/plugin/ImageGilProcessor.hpp>
00007 #include <tuttle/plugin/exceptions.hpp>
00008 
00009 #include <ofxsImageEffect.h>
00010 #include <ofxsMultiThread.h>
00011 
00012 #include <terry/numeric/operations.hpp>
00013 #include <terry/numeric/init.hpp>
00014 
00015 #include <boost/gil/extension/color/hsl.hpp>
00016 #include <boost/gil/gil_all.hpp>
00017 
00018 namespace tuttle {
00019 namespace plugin {
00020 namespace merge {
00021 
00022 template<class View, class Functor>
00023 MergeProcess<View, Functor>::MergeProcess( MergePlugin& instance )
00024         : ImageGilProcessor<View>( instance, eImageOrientationIndependant )
00025         , _plugin( instance )
00026 {}
00027 
00028 template<class View, class Functor>
00029 void MergeProcess<View, Functor>::setup( const OFX::RenderArguments& args )
00030 {
00031         ImageGilProcessor<View>::setup( args );
00032 
00033         // sources view
00034         // clip A
00035         _srcA.reset( _plugin._clipSrcA->fetchImage( args.time ) );
00036         if( !_srcA.get() )
00037                 BOOST_THROW_EXCEPTION( exception::ImageNotReady() );
00038         if( _srcA->getRowDistanceBytes() == 0 )
00039                 BOOST_THROW_EXCEPTION( exception::WrongRowBytes() );
00040         
00041         if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00042         {
00043                 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00044                 _srcPixelRodA   = _plugin._clipSrcA->getPixelRod( args.time, args.renderScale );
00045         }
00046         else
00047         {
00048                 _srcPixelRodA = _srcA->getRegionOfDefinition();
00049         }
00050         this->_srcViewA = this->getView( _srcA.get(), _srcPixelRodA );
00051 
00052         // clip B
00053         _srcB.reset( _plugin._clipSrcB->fetchImage( args.time ) );
00054         if( !_srcB.get() )
00055                 BOOST_THROW_EXCEPTION( exception::ImageNotReady() );
00056         if( _srcB->getRowDistanceBytes() == 0 )
00057                 BOOST_THROW_EXCEPTION( exception::WrongRowBytes() );
00058         
00059         if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00060         {
00061                 // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00062                 _srcPixelRodB   = _plugin._clipSrcB->getPixelRod( args.time, args.renderScale );
00063         }
00064         else
00065         {
00066                 _srcPixelRodB = _srcB->getRegionOfDefinition();
00067         }
00068         this->_srcViewB = this->getView( _srcB.get(), _srcPixelRodB );
00069 
00070         // Make sure bit depths are the same
00071         if( _srcA->getPixelDepth() != this->_dst->getPixelDepth() ||
00072             _srcB->getPixelDepth() != this->_dst->getPixelDepth() ||
00073             _srcA->getPixelComponents() != this->_dst->getPixelComponents() ||
00074             _srcB->getPixelComponents() != this->_dst->getPixelComponents() )
00075         {
00076                 BOOST_THROW_EXCEPTION( exception::BitDepthMismatch() );
00077         }
00078 
00079         _params = _plugin.getProcessParams( args.renderScale );
00080 }
00081 
00082 template <typename View, typename Value> GIL_FORCEINLINE 
00083 void fill_pixels(const View& dstView, const Value& val, const OfxRectI& region )
00084 {
00085         View dst = subimage_view( dstView,
00086                         region.x1, region.y1,
00087                         region.x2-region.x1, region.y2-region.y1 );
00088         
00089         fill_pixels( dst, val );
00090 }
00091 
00092 
00093 template <typename View> GIL_FORCEINLINE
00094 void copy_pixels( const View& src, const OfxRectI& srcRegion, const View& dst, const OfxRectI& dstRegion )
00095 {
00096         const OfxPointI regionSize = {
00097                 srcRegion.x2 - srcRegion.x1,
00098                 srcRegion.y2 - srcRegion.y1
00099         };
00100         BOOST_ASSERT( regionSize.x == ( dstRegion.x2 - dstRegion.x1 ) );
00101         BOOST_ASSERT( regionSize.y == ( dstRegion.y2 - dstRegion.y1 ) );
00102         
00103         View srcCrop = subimage_view( src,
00104                         srcRegion.x1, srcRegion.y1,
00105                         regionSize.x, regionSize.y );
00106         View dstCrop = subimage_view( dst,
00107                         dstRegion.x1, dstRegion.y1,
00108                         regionSize.x, regionSize.y );
00109 
00110         copy_pixels( srcCrop, dstCrop );
00111         
00112 }
00113 
00114 template<class View>
00115 void fillAroundIntersection(
00116                 const View& viewA, const OfxRectI& srcAPixelRod,
00117                 const View& viewB, const OfxRectI& srcBPixelRod,
00118                 const View& dstView, const OfxRectI& dstPixelRod,
00119                 const OfxRectI& procWindowRoW
00120         )
00121 {
00122         using namespace terry;
00123         using namespace terry::numeric;
00124         using namespace boost::gil;
00125         /// @todo tuttle:
00126         ///  * fill only the good regions
00127         ///  * add color choice?
00128         
00129         ////////////////////////////////////////////////////
00130         // Schema of different cases:
00131         // x: fill with a color
00132         //
00133         ////////////////////////////////////////////////////
00134         // _____________
00135         // |            |xxxxxxxx
00136         // |  copy A    |xxxxxxxx
00137         // |      ______|_______
00138         // |     |      |       |
00139         // |     |Merge |       |
00140         // |_____|______|       |
00141         // xxxxxx|    copy B    |
00142         // xxxxxx|______________|
00143         // 
00144         ////////////////////////////////////////////////////
00145         //       _______
00146         // xxxxxx|      |xxxxxxxx
00147         // xxxxxx|      |xxxxxxxx
00148         // ______|______|_______
00149         // |     |      |       |
00150         // |     |Merge |       |
00151         // |_____|______|_______|
00152         // xxxxxx|      |xxxxxxxx
00153         // xxxxxx|______|xxxxxxxx
00154         //
00155         ////////////////////////////////////////////////////
00156         // _____________
00157         // |            |xxxxxxxx
00158         // |  copy A    |xxxxxxxx
00159         // |____________|xxxxxxxx
00160         // xxxxxxxxxxxxxxxxxxxxxx
00161         // xxxxxx________________
00162         // xxxxxx|    copy B    |
00163         // xxxxxx|______________|
00164         //
00165         ////////////////////////////////////////////////////
00166         // _____________________
00167         // |                    |
00168         // |_ _ _ ______ _ _ _ _|
00169         // |     |      |       |
00170         // |     |Merge |       |
00171         // |_ _ _|______|_ _ _ _|
00172         // |____________________|
00173         // 
00174         ////////////////////////////////////////////////////
00175         
00176         if( ! rectangleContainsAnother( srcAPixelRod, srcBPixelRod ) )
00177         {
00178                 // fill color
00179                 typedef typename View::value_type Pixel;
00180                 Pixel pixelZero; pixel_zeros_t<Pixel>()( pixelZero );
00181                 const OfxRectI procWindowOutput = translateRegion( procWindowRoW, dstPixelRod );
00182                 
00183                 fill_pixels( dstView, pixelZero, procWindowOutput );
00184         }
00185         {
00186                 // fill with A
00187                 const OfxRectI intersectRegionA = rectanglesIntersection( procWindowRoW, srcAPixelRod );
00188                 const OfxRectI procWindowSrcA = translateRegion( intersectRegionA, srcAPixelRod );
00189                 const OfxRectI procWindowOutputA = translateRegion( intersectRegionA, dstPixelRod );
00190                 //TUTTLE_TLOG_VAR( TUTTLE_INFO, intersectRegionA );
00191                 //TUTTLE_TLOG_VAR( TUTTLE_INFO, procWindowSrcA );
00192                 //TUTTLE_TLOG_VAR( TUTTLE_INFO, procWindowOutputA );
00193                 
00194                 /// @todo tuttle: fill only the good regions
00195                 copy_pixels( viewA, procWindowSrcA, dstView, procWindowOutputA );
00196         }
00197         {
00198                 // fill with B
00199                 const OfxRectI intersectRegionB = rectanglesIntersection( procWindowRoW, srcBPixelRod );
00200                 const OfxRectI procWindowSrcB = translateRegion( intersectRegionB, srcBPixelRod );
00201                 const OfxRectI procWindowOutputB = translateRegion( intersectRegionB, dstPixelRod );
00202                 //TUTTLE_TLOG_VAR( TUTTLE_INFO, intersectRegionB );
00203                 //TUTTLE_TLOG_VAR( TUTTLE_INFO, procWindowSrcB );
00204                 //TUTTLE_TLOG_VAR( TUTTLE_INFO, procWindowOutputB );
00205                 
00206                 /// @todo tuttle: fill only the good regions
00207                 copy_pixels( viewB, procWindowSrcB, dstView, procWindowOutputB );
00208         }
00209 }
00210 
00211 template<class View>
00212 void fillAroundIntersection(
00213                 const View& viewA, const OfxRectI& srcAPixelRod,
00214                 const OfxRectI& srcBPixelRod,
00215                 const View& dstView, const OfxRectI& dstPixelRod,
00216                 const OfxRectI& procWindowRoW
00217         )
00218 {
00219         // fill with A
00220         const OfxRectI procWindowSrc = translateRegion( procWindowRoW, srcAPixelRod );
00221         const OfxRectI procWindowOutput = translateRegion( procWindowRoW, dstPixelRod );
00222 
00223         /// @todo tuttle: fill only the good regions
00224         copy_pixels( viewA, procWindowSrc, dstView, procWindowOutput );
00225 }
00226 
00227 /**
00228  * @brief Function called by rendering thread each time a process must be done.
00229  * @param[in] procWindowRoW  Processing window in RoW
00230  */
00231 template<class View, class Functor>
00232 void MergeProcess<View, Functor>::multiThreadProcessImages( const OfxRectI& procWindowRoW )
00233 {
00234         using namespace terry;
00235         using namespace terry::numeric;
00236 
00237         const OfxRectI srcRodA = translateRegion( _srcPixelRodA, _params._offsetA );
00238         const OfxRectI srcRodB = translateRegion( _srcPixelRodB, _params._offsetB );
00239         
00240         const OfxRectI intersect = rectanglesIntersection( srcRodA, srcRodB );
00241         const OfxRectI procIntersect = rectanglesIntersection( procWindowRoW, intersect );
00242         const OfxPointI procIntersectSize = {
00243                 procIntersect.x2 - procIntersect.x1,
00244                 procIntersect.y2 - procIntersect.y1
00245         };
00246 
00247         /// @todo tuttle: fill only the good regions
00248         switch( _params._rod )
00249         {
00250                 case eParamRodIntersect:
00251                 {
00252                         // nothing to do in this case
00253                         // this is the standard case
00254                         // we merge the intersection of the 2 images
00255                         break;
00256                 }
00257                 case eParamRodUnion:
00258                 {
00259                         fillAroundIntersection(
00260                                         this->_srcViewA, srcRodA,
00261                                         this->_srcViewB, srcRodB,
00262                                         this->_dstView, this->_dstPixelRod,
00263                                         procWindowRoW
00264                                 );
00265                         break;
00266                 }
00267                 case eParamRodA:
00268                 {
00269                         fillAroundIntersection(
00270                                         this->_srcViewA, srcRodA,
00271                                         srcRodB,
00272                                         this->_dstView, this->_dstPixelRod,
00273                                         procWindowRoW
00274                                 );
00275                         break;
00276                 }
00277                 case eParamRodB:
00278                 {
00279                         fillAroundIntersection(
00280                                         this->_srcViewB, srcRodB,
00281                                         srcRodA,
00282                                         this->_dstView, this->_dstPixelRod,
00283                                         procWindowRoW
00284                                 );
00285                         break;
00286                 }
00287         }
00288 
00289         View srcViewA_inter = subimage_view(    this->_srcViewA,
00290                                                 procIntersect.x1 - srcRodA.x1,
00291                                                 procIntersect.y1 - srcRodA.y1,
00292                                                 procIntersectSize.x,
00293                                                 procIntersectSize.y );
00294         View srcViewB_inter = subimage_view(    this->_srcViewB,
00295                                                 procIntersect.x1 - srcRodB.x1,
00296                                                 procIntersect.y1 - srcRodB.y1,
00297                                                 procIntersectSize.x,
00298                                                 procIntersectSize.y );
00299 
00300         View dstView_inter = subimage_view(     this->_dstView,
00301                                                 procIntersect.x1 - this->_dstPixelRod.x1,
00302                                                 procIntersect.y1 - this->_dstPixelRod.y1,
00303                                                 procIntersectSize.x,
00304                                                 procIntersectSize.y );
00305 
00306         merge_views( srcViewA_inter, srcViewB_inter, dstView_inter, Functor() );
00307 }
00308 
00309 }
00310 }
00311 }