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