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