TuttleOFX
1
|
00001 #include "EXRReaderDefinitions.hpp" 00002 #include "EXRReaderPlugin.hpp" 00003 00004 #include <tuttle/plugin/context/ReaderPlugin.hpp> 00005 00006 #include <tuttle/plugin/ImageGilProcessor.hpp> 00007 #include <tuttle/plugin/exceptions.hpp> 00008 00009 #include <terry/algorithm/transform_pixels.hpp> 00010 #include <terry/numeric/assign.hpp> 00011 00012 #include <terry/globals.hpp> 00013 #include <terry/basic_colors.hpp> 00014 #include <terry/openexr/half.hpp> 00015 00016 #include <ofxsImageEffect.h> 00017 #include <ofxsMultiThread.h> 00018 00019 #include <ImfChannelList.h> 00020 #include <ImfArray.h> 00021 #include <ImathVec.h> 00022 00023 #include <boost/gil/gil_all.hpp> 00024 #include <boost/gil/packed_pixel.hpp> 00025 00026 #include <boost/integer.hpp> // for boost::uint_t 00027 #include <boost/cstdint.hpp> 00028 #include <boost/mpl/vector.hpp> 00029 #include <boost/scoped_ptr.hpp> 00030 #include <boost/assert.hpp> 00031 #include <boost/filesystem/fstream.hpp> 00032 00033 #include <algorithm> 00034 00035 namespace tuttle { 00036 namespace plugin { 00037 namespace exr { 00038 namespace reader { 00039 00040 namespace bfs = boost::filesystem; 00041 00042 template<class View> 00043 EXRReaderProcess<View>::EXRReaderProcess( EXRReaderPlugin& instance ) 00044 : ImageGilProcessor<View>( instance, eImageOrientationFromTopToBottom ) 00045 , _plugin( instance ) 00046 { 00047 this->setNoMultiThreading(); 00048 } 00049 00050 template<class View> 00051 void EXRReaderProcess<View>::setup( const OFX::RenderArguments& args ) 00052 { 00053 ImageGilProcessor<View>::setup( args ); 00054 00055 _params = _plugin.getProcessParams( args.time ); 00056 00057 try 00058 { 00059 _exrImage.reset( new Imf::InputFile( _params._filepath.c_str() ) ); 00060 } 00061 catch( ... ) 00062 { 00063 BOOST_THROW_EXCEPTION( exception::File() 00064 << exception::user( "EXR: Error when reading header." ) 00065 << exception::filename( _params._filepath.c_str() ) ); 00066 } 00067 } 00068 00069 /** 00070 * @brief Function called by rendering thread each time a process must be done. 00071 * @param[in] procWindowRoW Processing window in RoW 00072 */ 00073 template<class View> 00074 void EXRReaderProcess<View>::multiThreadProcessImages( const OfxRectI& procWindowRoW ) 00075 { 00076 using namespace terry; 00077 BOOST_ASSERT( procWindowRoW == this->_dstPixelRod ); 00078 00079 try 00080 { 00081 View dst = this->_dstView; 00082 00083 //TUTTLE_LOG_VAR( TUTTLE_INFO, _params._fileComponents ); 00084 switch( _params._fileComponents ) 00085 { 00086 case 1: 00087 { 00088 gray32f_image_t img( this->_dstView.width(), this->_dstView.height() ); 00089 typename gray32f_image_t::view_t dv( view( img ) ); 00090 terry::algorithm::transform_pixels( dv, terry::numeric::pixel_assigns_color_t<gray32f_pixel_t>( gray32f_pixel_t( 0.0 ) ) ); 00091 readImage( dv, _params._filepath ); 00092 copy_and_convert_pixels( dv, dst ); 00093 break; 00094 } 00095 case 3: 00096 { 00097 rgb32f_image_t img( this->_dstView.width(), this->_dstView.height() ); 00098 typename rgb32f_image_t::view_t dv( view( img ) ); 00099 terry::algorithm::transform_pixels( dv, terry::numeric::pixel_assigns_color_t<rgb32f_pixel_t>( rgb32f_pixel_t( 0.0, 0.0, 0.0 ) ) ); 00100 readImage( dv, _params._filepath ); 00101 copy_and_convert_pixels( dv, dst ); 00102 break; 00103 } 00104 case 4: 00105 { 00106 rgba32f_image_t img( this->_dstView.width(), this->_dstView.height() ); 00107 typename rgba32f_image_t::view_t dv( view( img ) ); 00108 terry::algorithm::transform_pixels( dv, terry::numeric::pixel_assigns_color_t<rgba32f_pixel_t>( rgba32f_pixel_t( 0.0, 0.0, 0.0, 0.0 ) ) ); 00109 readImage( dv, _params._filepath ); 00110 copy_and_convert_pixels( dv, dst ); 00111 break; 00112 } 00113 default: 00114 { 00115 BOOST_THROW_EXCEPTION( exception::Unsupported() 00116 << exception::user( "ExrWriter: file channel not supported" ) ); 00117 } 00118 } 00119 } 00120 catch( boost::exception& e ) 00121 { 00122 e << exception::filename( _params._filepath ); 00123 TUTTLE_TLOG( TUTTLE_ERROR, boost::diagnostic_information( e ) ); 00124 throw; 00125 } 00126 catch( ... ) 00127 { 00128 BOOST_THROW_EXCEPTION( exception::Unknown() 00129 << exception::user( "Unable to read image") 00130 << exception::dev( boost::current_exception_diagnostic_information() ) 00131 << exception::filename( _params._filepath ) ); 00132 } 00133 } 00134 00135 /** 00136 */ 00137 template<class View> 00138 template<class DView> 00139 void EXRReaderProcess<View>::readImage( DView dst, const std::string& filepath ) 00140 { 00141 using namespace boost; 00142 using namespace mpl; 00143 using namespace boost::gil; 00144 using namespace Imf; 00145 00146 EXRReaderProcessParams params = _plugin.getProcessParams( this->_renderArgs.time ); 00147 Imf::InputFile in( filepath.c_str() ); 00148 Imf::FrameBuffer frameBuffer; 00149 const Imf::Header& header = in.header(); 00150 const Imath::Box2i& dw = header.dataWindow(); 00151 typename Imath::V2i imageDims = dw.size(); 00152 imageDims.x++; // Width 00153 imageDims.y++; // Height 00154 00155 // Get number of output components 00156 switch( (EParamReaderChannel)params._outComponents ) 00157 { 00158 case eParamReaderChannelGray: 00159 { 00160 // Copy 1 channel seletected by alpha channel ( index 3 ) 00161 channelCopy( in, frameBuffer, params, dst, imageDims.x, imageDims.y, 1 ); 00162 break; 00163 } 00164 case eParamReaderChannelRGB: 00165 { 00166 // Copy 3 channels starting by the first channel (0, 1, 2) 00167 channelCopy( in, frameBuffer, params, dst, imageDims.x, imageDims.y, 3 ); 00168 break; 00169 } 00170 case eParamReaderChannelRGBA: 00171 { 00172 // Copy 4 channels starting by the first channel (0, 1, 2, 3) 00173 channelCopy( in, frameBuffer, params, dst, imageDims.x, imageDims.y, 4 ); 00174 break; 00175 } 00176 case eParamReaderChannelAuto: 00177 { 00178 if( ! ( _params._fileComponents == 1 || _params._fileComponents == 3 || _params._fileComponents == 4 ) ) 00179 { 00180 std::string msg = "EXR: not support "; 00181 msg += _params._fileComponents; 00182 msg += " channels."; 00183 BOOST_THROW_EXCEPTION( exception::FileNotExist() 00184 << exception::user( msg ) ); 00185 } 00186 00187 channelCopy( in, frameBuffer, params, dst, imageDims.x, imageDims.y, _params._fileComponents ); 00188 } 00189 } 00190 } 00191 00192 template<class View> 00193 template< typename PixelType > 00194 void EXRReaderProcess<View>::initExrChannel( DataVector& data, Imf::Slice& slice, Imf::FrameBuffer& frameBuffer, Imf::PixelType pixelType, std::string channelID, const Imath::Box2i& dw, int w, int h ) 00195 { 00196 data.resize( sizeof( PixelType ) * w * h ); 00197 00198 slice.type = pixelType; 00199 slice.base = (char*) (&data[0] - sizeof( PixelType ) * ( dw.min.x + dw.min.y * w ) ); 00200 slice.xStride = sizeof( PixelType ) * 1; 00201 slice.yStride = sizeof( PixelType ) * w; 00202 slice.xSampling = 1; 00203 slice.ySampling = 1; 00204 slice.fillValue = 1.0; 00205 00206 frameBuffer.insert( channelID.c_str(), slice ); 00207 } 00208 00209 template<class View> 00210 template<class DView> 00211 void EXRReaderProcess<View>::channelCopy( Imf::InputFile& input, Imf::FrameBuffer& frameBuffer, const EXRReaderProcessParams& params, 00212 DView& dst, int w, int h, size_t nc ) 00213 { 00214 using namespace boost::gil; 00215 const Imf::Header& header = input.header(); 00216 const Imath::Box2i& dw = header.dataWindow(); 00217 00218 DataVector *data = new DataVector[nc]; 00219 Imf::Slice *slices = new Imf::Slice[nc]; 00220 00221 for( size_t layer = 0; layer < nc; ++layer ) 00222 { 00223 const Imf::ChannelList& cl( header.channels() ); 00224 const Imf::Channel& ch = cl[ getChannelName( layer ).c_str() ]; 00225 switch( ch.type ) 00226 { 00227 case Imf::HALF: 00228 { 00229 initExrChannel<half>( data[layer], slices[layer], frameBuffer, ch.type, getChannelName( layer ), dw, w, h ); 00230 break; 00231 } 00232 case Imf::FLOAT: 00233 { 00234 initExrChannel<float>( data[layer], slices[layer], frameBuffer, ch.type, getChannelName( layer ), dw, w, h ); 00235 break; 00236 } 00237 case Imf::UINT: 00238 { 00239 initExrChannel<boost::uint32_t>( data[layer], slices[layer], frameBuffer, ch.type, getChannelName( layer ), dw, w, h ); 00240 break; 00241 } 00242 case Imf::NUM_PIXELTYPES: 00243 { 00244 BOOST_THROW_EXCEPTION( exception::Value() 00245 << exception::user( "Pixel type not supported." ) ); 00246 break; 00247 } 00248 } 00249 } 00250 00251 input.setFrameBuffer( frameBuffer ); 00252 input.readPixels( dw.min.y, dw.max.y ); 00253 00254 for( size_t layer = 0; layer < nc; ++layer ) 00255 { 00256 switch( slices[layer].type ) 00257 { 00258 case Imf::HALF: 00259 { 00260 sliceCopy<DView, gray16h_view_t>( input, &slices[layer], dst, params, w, h, layer ); 00261 break; 00262 } 00263 case Imf::FLOAT: 00264 { 00265 sliceCopy<DView, gray32f_view_t>( input, &slices[layer], dst, params, w, h, layer ); 00266 break; 00267 } 00268 case Imf::UINT: 00269 { 00270 sliceCopy<DView, gray32_view_t>( input, &slices[layer], dst, params, w, h, layer ); 00271 break; 00272 } 00273 case Imf::NUM_PIXELTYPES: 00274 { 00275 BOOST_THROW_EXCEPTION( exception::Value() 00276 << exception::user( "Pixel type not supported." ) ); 00277 break; 00278 } 00279 } 00280 } 00281 delete []data; 00282 delete []slices; 00283 } 00284 00285 template<class View> 00286 template<class DView, typename workingView> 00287 void EXRReaderProcess<View>::sliceCopy( Imf::InputFile& input, const Imf::Slice* slice, DView& dst, const EXRReaderProcessParams& params, int w, int h, int n ) 00288 { 00289 using namespace terry; 00290 const Imath::Box2i& dataw = input.header().dataWindow(); 00291 const Imath::Box2i& dispw = input.header().displayWindow(); 00292 00293 workingView vw( interleaved_view( w, h, ( typename workingView::value_type*)slice->base, w * sizeof( typename workingView::value_type ) ) ); 00294 workingView subView; 00295 00296 if( params._displayWindow ) 00297 { 00298 size_t xoffsetData = dataw.min.x + std::max( 0, dispw.min.x ); 00299 size_t yoffsetData = dataw.min.y + std::max( 0, dispw.min.y ); 00300 size_t xoffsetDisp = dataw.min.x - std::min( 0, dispw.min.x ); 00301 size_t yoffsetDisp = dataw.min.y - std::min( 0, dispw.min.y ); 00302 00303 size_t wView = std::min( dataw.max.x, dispw.max.x ) - std::max( dataw.min.x, dispw.min.x ) + 1; 00304 size_t hView = std::min( dataw.max.y, dispw.max.y ) - std::max( dataw.min.y, dispw.min.y ) + 1; 00305 00306 TUTTLE_TLOG_VAR4( TUTTLE_WARNING, dataw.min.x, dataw.min.y, dataw.max.x, dataw.max.y ); 00307 TUTTLE_TLOG_VAR4( TUTTLE_WARNING, dispw.min.x, dispw.min.y, dispw.max.x, dispw.max.y ); 00308 TUTTLE_TLOG_VAR4( TUTTLE_WARNING, xoffsetData, yoffsetData, xoffsetDisp, yoffsetDisp ); 00309 TUTTLE_TLOG_VAR2( TUTTLE_WARNING, wView, hView ); 00310 00311 subView = subimage_view( vw, 00312 xoffsetData, 00313 yoffsetData, 00314 wView, 00315 hView 00316 ); 00317 00318 DView dstView = subimage_view( dst, 00319 xoffsetDisp, 00320 yoffsetDisp, 00321 wView, 00322 hView 00323 ); 00324 00325 if( wView > 0 && hView > 0 ) 00326 { 00327 copy_and_convert_pixels( subView, nth_channel_view( dstView, n ) ); 00328 } 00329 } 00330 else 00331 { 00332 subView = subimage_view( vw, dataw.min.x, dataw.min.y, w, h ); 00333 copy_and_convert_pixels( subView, nth_channel_view( dst, n ) ); 00334 } 00335 } 00336 00337 template<class View> 00338 std::string EXRReaderProcess<View>::getChannelName( size_t index ) 00339 { 00340 return _plugin.channelNames()[ _plugin.channelChoice()[index]->getValue() ]; 00341 } 00342 00343 } 00344 } 00345 } 00346 }