TuttleOFX  1
FadeProcess.tcc
Go to the documentation of this file.
00001 #include "FadeAlgorithm.hpp"
00002 
00003 #include <terry/merge/ViewsMerging.hpp>
00004 #include <terry/numeric/init.hpp>
00005 #include <terry/numeric/operations.hpp>
00006 #include <terry/numeric/assign.hpp>
00007 #include <terry/generator/constant.hpp>
00008 #include <terry/typedefs.hpp>
00009 #include <terry/algorithm/transform_pixels_progress.hpp>
00010 
00011 namespace tuttle {
00012 namespace plugin {
00013 namespace fade {
00014 
00015 
00016 template <typename Pixel>
00017 struct FunctorFade
00018         : public terry::merge_functor<Pixel, terry::merge_per_channel>
00019 {
00020         const double _t;
00021         
00022         FunctorFade( const double t )
00023         : _t(t)
00024         {}
00025         
00026         template<typename Channel>
00027         inline void operator()( const Channel& A, const Channel& B, Channel& dst )
00028         {
00029                 // (1-t)*A + t*B
00030                 dst = (Channel)( (1.0-_t)*A + _t*B );
00031         }
00032 };
00033 
00034 template <typename Pixel>
00035 struct FunctorFadeToColor
00036 {
00037         const Pixel _color;
00038         const double _t;
00039         const double _invt; // 1.0 - _t
00040         
00041         FunctorFadeToColor( const Pixel& color, const double t )
00042         : _color( color )
00043         , _t( t )
00044         , _invt( 1.0-t )
00045         {}
00046         
00047         GIL_FORCEINLINE
00048         Pixel operator()( const Pixel& a ) const
00049         {
00050                 using namespace terry::numeric;
00051                 // (1-t)*src + t*color
00052                 return pixel_plus_t<Pixel, Pixel, Pixel>()(
00053                         pixel_multiplies_scalar_t<Pixel, double>()( a, _invt ),
00054                         pixel_multiplies_scalar_t<Pixel, double>()( _color, _t )
00055                         );
00056         }
00057 };
00058 
00059 
00060 template<class View>
00061 FadeProcess<View>::FadeProcess( FadePlugin &effect )
00062 : ImageGilProcessor<View>( effect, eImageOrientationIndependant )
00063 , _plugin( effect )
00064 {
00065 }
00066 
00067 template<class View>
00068 void FadeProcess<View>::setup( const OFX::RenderArguments& args )
00069 {
00070         ImageGilProcessor<View>::setup( args );
00071 
00072         // sources view
00073         // clip A
00074         if( _plugin._clipSrcFrom->isConnected() )
00075         {
00076                 _srcA.reset( _plugin._clipSrcFrom->fetchImage( args.time ) );
00077                 if( !_srcA.get() )
00078                         BOOST_THROW_EXCEPTION( exception::ImageNotReady() );
00079                 if( _srcA->getRowDistanceBytes() == 0 )
00080                         BOOST_THROW_EXCEPTION( exception::WrongRowBytes() );
00081                 
00082                 if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00083                 {
00084                         // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00085                         _srcPixelRodA   = _plugin._clipSrcFrom->getPixelRod( args.time, args.renderScale );
00086                 }
00087                 else
00088                 {
00089                         _srcPixelRodA = _srcA->getRegionOfDefinition();
00090                 }
00091                 this->_srcViewA = this->getView( _srcA.get(), _srcPixelRodA );
00092         }
00093         // clip B
00094         if( _plugin._clipSrcTo->isConnected() )
00095         {
00096                 _srcB.reset( _plugin._clipSrcTo->fetchImage( args.time ) );
00097                 if( !_srcB.get() )
00098                         BOOST_THROW_EXCEPTION( exception::ImageNotReady() );
00099                 if( _srcB->getRowDistanceBytes() == 0 )
00100                         BOOST_THROW_EXCEPTION( exception::WrongRowBytes() );
00101                 
00102                 if( OFX::getImageEffectHostDescription()->hostName == "uk.co.thefoundry.nuke" )
00103                 {
00104                         // bug in nuke, getRegionOfDefinition() on OFX::Image returns bounds
00105                         _srcPixelRodB   = _plugin._clipSrcTo->getPixelRod( args.time, args.renderScale );
00106                 }
00107                 else
00108                 {
00109                         _srcPixelRodB = _srcB->getRegionOfDefinition();
00110                 }
00111                 this->_srcViewB = this->getView( _srcB.get(), _srcPixelRodB );
00112         }
00113         if( _plugin._clipSrcFrom->isConnected() && _plugin._clipSrcTo->isConnected() )
00114         {
00115                 // Make sure bit depths are the same
00116                 if( _srcA->getPixelDepth() != this->_dst->getPixelDepth() ||
00117                         _srcB->getPixelDepth() != this->_dst->getPixelDepth() ||
00118                         _srcA->getPixelComponents() != this->_dst->getPixelComponents() ||
00119                         _srcB->getPixelComponents() != this->_dst->getPixelComponents() )
00120                 {
00121                         BOOST_THROW_EXCEPTION( exception::BitDepthMismatch() );
00122                 }
00123         }
00124 
00125         _params = _plugin.getProcessParams();
00126 }
00127 
00128 /**
00129  * @brief Function called by rendering thread each time a process must be done.
00130  * @param[in] procWindowRoW  Processing window
00131  */
00132 template<class View>
00133 void FadeProcess<View>::multiThreadProcessImages( const OfxRectI& procWindowRoW )
00134 {
00135         using namespace terry;
00136         using namespace terry::numeric;
00137         
00138         const OfxPointI procWindowSize = {
00139                 procWindowRoW.x2 - procWindowRoW.x1,
00140                 procWindowRoW.y2 - procWindowRoW.y1
00141         };
00142         const OfxRectI procWindowOutput = translateRegion( procWindowRoW, this->_dstPixelRod );
00143         const OfxRectI srcRodA = _srcPixelRodA; //translateRegion( _srcPixelRodA, _params._offsetA );
00144         const OfxRectI srcRodB = _srcPixelRodB; //translateRegion( _srcPixelRodB, _params._offsetB );
00145         
00146         const bool fromConnected = _plugin._clipSrcFrom->isConnected();
00147         const bool toConnected = _plugin._clipSrcTo->isConnected();
00148         
00149         if( fromConnected && toConnected )
00150         {
00151 
00152                 const OfxRectI intersect = rectanglesIntersection( srcRodA, srcRodB );
00153                 const OfxRectI procIntersect = rectanglesIntersection( procWindowRoW, intersect );
00154                 const OfxPointI procIntersectSize = {
00155                         procIntersect.x2 - procIntersect.x1,
00156                         procIntersect.y2 - procIntersect.y1
00157                 };
00158 
00159                 /// @todo tuttle: fill only the good regions
00160                 switch( _params._rod )
00161                 {
00162                         case eParamRodIntersect:
00163                         {
00164                                 // nothing to do in this case
00165                                 // this is the standard case
00166                                 // we merge the intersection of the 2 images
00167                                 break;
00168                         }
00169                         case eParamRodUnion:
00170                         {
00171                                 /// @todo tuttle:
00172                                 ///  * add color choice
00173                                 ///  * fill A and B
00174                                 ///  * fill only the good regions
00175                                 {
00176                                         // fill color
00177                                         Pixel pixelZero; pixel_zeros_t<Pixel>()( pixelZero );
00178                                         View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1,
00179                                                                                                                         procWindowSize.x, procWindowSize.y );
00180                                         fill_pixels( dst, pixelZero );
00181                                 }
00182                                 {
00183                                         // fill with A
00184                                         const OfxRectI procWindowSrc = translateRegion( procWindowRoW, srcRodA );
00185 
00186                                         View src = subimage_view( this->_srcViewA, procWindowSrc.x1, procWindowSrc.y1,
00187                                                                                                                         procWindowSize.x, procWindowSize.y );
00188                                         View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1,
00189                                                                                                                         procWindowSize.x, procWindowSize.y );
00190                                         /// @todo tuttle: fill only the good regions
00191                                         copy_pixels( src, dst );
00192                                 }
00193                                 {
00194                                         // fill with B
00195                                         const OfxRectI procWindowSrc = translateRegion( procWindowRoW, srcRodB );
00196 
00197                                         View src = subimage_view( this->_srcViewB, procWindowSrc.x1, procWindowSrc.y1,
00198                                                                                                                         procWindowSize.x, procWindowSize.y );
00199                                         View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1,
00200                                                                                                                         procWindowSize.x, procWindowSize.y );
00201                                         /// @todo tuttle: fill only the good regions
00202                                         copy_pixels( src, dst );
00203                                 }
00204                                 break;
00205                         }
00206                         case eParamRodA:
00207                         {
00208                                 // fill with A
00209                                 const OfxRectI procWindowSrc = translateRegion( procWindowRoW, srcRodA );
00210 
00211                                 View src = subimage_view( this->_srcViewA, procWindowSrc.x1, procWindowSrc.y1,
00212                                                                                                                 procWindowSize.x, procWindowSize.y );
00213                                 View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1,
00214                                                                                                                 procWindowSize.x, procWindowSize.y );
00215                                 /// @todo tuttle: fill only the good regions
00216                                 copy_pixels( src, dst );
00217                                 break;
00218                         }
00219                         case eParamRodB:
00220                         {
00221                                 // fill with B
00222                                 const OfxRectI procWindowSrc = translateRegion( procWindowRoW, srcRodB );
00223 
00224                                 View src = subimage_view( this->_srcViewB, procWindowSrc.x1, procWindowSrc.y1,
00225                                                                                                                 procWindowSize.x, procWindowSize.y );
00226                                 View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1,
00227                                                                                                                 procWindowSize.x, procWindowSize.y );
00228                                 /// @todo tuttle: fill only the good regions
00229                                 copy_pixels( src, dst );
00230                                 break;
00231                         }
00232                 }
00233 
00234                 View srcViewA_inter = subimage_view(    this->_srcViewA,
00235                                                         procIntersect.x1 - srcRodA.x1,
00236                                                         procIntersect.y1 - srcRodA.y1,
00237                                                         procIntersectSize.x,
00238                                                         procIntersectSize.y );
00239                 View srcViewB_inter = subimage_view(    this->_srcViewB,
00240                                                         procIntersect.x1 - srcRodB.x1,
00241                                                         procIntersect.y1 - srcRodB.y1,
00242                                                         procIntersectSize.x,
00243                                                         procIntersectSize.y );
00244 
00245                 View dstView_inter = subimage_view(     this->_dstView,
00246                                                         procIntersect.x1 - this->_dstPixelRod.x1,
00247                                                         procIntersect.y1 - this->_dstPixelRod.y1,
00248                                                         procIntersectSize.x,
00249                                                         procIntersectSize.y );
00250 
00251                 merge_views( srcViewA_inter, srcViewB_inter, dstView_inter, FunctorFade<Pixel>(_params._transition) );
00252         }
00253         else if( fromConnected || toConnected )
00254         {
00255                 // Only one input clip connected: fade to color
00256                 View connectedSrc = fromConnected ? this->_srcViewA : this->_srcViewB;
00257                 
00258                 const OfxRectI procWindowSrc = translateRegion( procWindowRoW, fromConnected ? srcRodA : srcRodB );
00259 
00260                 View src = subimage_view( connectedSrc, procWindowSrc.x1, procWindowSrc.y1,
00261                                                                                                 procWindowSize.x, procWindowSize.y );
00262                 View dst = subimage_view( this->_dstView, procWindowOutput.x1, procWindowOutput.y1,
00263                                                                                                 procWindowSize.x, procWindowSize.y );
00264                 
00265                 // Fade to color
00266                 Pixel c;
00267                 color_convert( _params._color, c );
00268                 
00269                 terry::algorithm::transform_pixels_progress(
00270                         src,
00271                         dst,
00272                         FunctorFadeToColor<Pixel>( c, _params._transition ),
00273                         this->getOfxProgress()
00274                         );
00275         }
00276         else
00277         {
00278                 // No input clip, set a constant color
00279                 View dst = subimage_view( this->_dstView,
00280                                 procWindowOutput.x1, procWindowOutput.y1,
00281                                 procWindowSize.x, procWindowSize.y );
00282                 
00283                 Pixel c;
00284                 color_convert( _params._color, c );
00285                 
00286                 terry::algorithm::transform_pixels_progress(
00287                         dst,
00288                         pixel_assigns_color_t<Pixel>( c ),
00289                         this->getOfxProgress()
00290                         );
00291         }
00292 }
00293 
00294 }
00295 }
00296 }