DataPtr JPEGImageReader::readTypedChannel( const std::string &name, const Imath::Box2i &dataWindow, int channelOffset ) { int area = ( dataWindow.size().x + 1 ) * ( dataWindow.size().y + 1 ); assert( area >= 0 ); int dataWidth = 1 + dataWindow.size().x; int dataY = 0; typedef TypedData< std::vector< V > > TargetVector; typename TargetVector::Ptr dataContainer = new TargetVector(); typename TargetVector::ValueType &data = dataContainer->writable(); data.resize( area ); ScaledDataConversion< unsigned char, V> converter; for ( int y = dataWindow.min.y; y <= dataWindow.max.y; ++y, ++dataY ) { typename TargetVector::ValueType::size_type dataOffset = dataY * dataWidth; for ( int x = dataWindow.min.x; x <= dataWindow.max.x; ++x, ++dataOffset ) { assert( dataOffset < data.size() ); data[dataOffset] = converter( m_buffer[ m_numChannels * ( y * m_bufferWidth + x ) + channelOffset ] ); } } return dataContainer; }
void exposure_node_t::do_process( const image::const_image_view_t& src, const image::image_view_t& dst, const render::render_context_t& context) { image::const_image_view_t src_view; image::image_view_t dst_view; Imath::Box2i area; if( input(1)) { boost::gil::copy_pixels( src, dst); area = intersect( input(1)->defined(), defined()); if( area.isEmpty()) return; src_view = input(0)->const_subimage_view( area); dst_view = subimage_view( area); } else { src_view = src; dst_view = dst; } boost::gil::tbb_transform_pixels( src_view, dst_view, exposure_fun( get_value<float>( param( "exp")))); if( input(1)) image::key_mix( src_view, dst_view, boost::gil::nth_channel_view( input(1)->const_subimage_view( area), 3), dst_view); }
void layer_node_t::do_calc_bounds( const render::context_t& context) { Imath::Box2i bbox; float opacity = get_value<float>( param( "opacity")); if( opacity == 1.0f) { switch( get_value<int>( param( "layer_mode"))) { case comp_mult: case comp_min: case comp_mix: bbox = ImathExt::intersect( input_as<image_node_t>( 0)->bounds(), input_as<image_node_t>( 1)->bounds()); break; case comp_sub: bbox = input_as<image_node_t>( 0)->bounds(); break; default: bbox = input_as<image_node_t>( 0)->bounds(); bbox.extendBy( input_as<image_node_t>( 1)->bounds()); } } else { bbox = input_as<image_node_t>( 0)->bounds(); bbox.extendBy( input_as<image_node_t>( 1)->bounds()); } set_bounds( bbox); }
void expand_node_t::do_process( const render::context_t& context) { Imath::Box2i area = ImathExt::intersect( input_as<image_node_t>()->defined(), defined()); if( area.isEmpty()) return; boost::gil::copy_pixels( input_as<image_node_t>()->const_subimage_view( area), subimage_view( area)); }
void crop_node_t::do_process( const render::render_context_t& context) { Imath::Box2i area = intersect( input()->defined(), defined()); if( area.isEmpty()) return; boost::gil::copy_pixels( input()->const_subimage_view( area), subimage_view( area)); }
Imath::Box2i CopyChannels::computeDataWindow( const Gaffer::Context *context, const ImagePlug *parent ) const { Imath::Box2i dataWindow; for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it ) { dataWindow.extendBy( (*it)->dataWindowPlug()->getValue() ); } return dataWindow; }
Imath::Box2i Merge::computeDataWindow( const Gaffer::Context *context, const ImagePlug *parent ) const { Imath::Box2i dataWindow; for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it ) { // We don't need to check that the plug is connected here as unconnected plugs don't have data windows. dataWindow.extendBy( (*it)->dataWindowPlug()->getValue() ); } return dataWindow; }
void ImageStats::compute( ValuePlug *output, const Context *context ) const { const int colorIndex = ::colorIndex( output ); if( colorIndex == -1 ) { // Not a plug we know about ComputeNode::compute( output, context ); return; } const std::string channelName = this->channelName( colorIndex ); const Imath::Box2i area = areaPlug()->getValue(); if( channelName.empty() || BufferAlgo::empty( area ) ) { output->setToDefault(); return; } // Loop over the ROI and compute the min, max and average channel values and then set our outputs. Sampler s( inPlug(), channelName, area ); float min = Imath::limits<float>::max(); float max = Imath::limits<float>::min(); double sum = 0.; for( int y = area.min.y; y < area.max.y; ++y ) { for( int x = area.min.x; x < area.max.x; ++x ) { float v = s.sample( x, y ); min = std::min( v, min ); max = std::max( v, max ); sum += v; } } if( output->parent<Plug>() == minPlug() ) { static_cast<FloatPlug *>( output )->setValue( min ); } else if( output->parent<Plug>() == maxPlug() ) { static_cast<FloatPlug *>( output )->setValue( max ); } else if( output->parent<Plug>() == averagePlug() ) { static_cast<FloatPlug *>( output )->setValue( sum / double( (area.size().x) * (area.size().y) ) ); } }
void make_color_bars( const image_view_t& view, const Imath::Box2i& domain, const Imath::Box2i& defined) { typedef detail::color_bars_fn deref_t; typedef deref_t::point_t point_t; typedef boost::gil::virtual_2d_locator<deref_t,false> locator_t; typedef boost::gil::image_view<locator_t> my_virt_view_t; point_t dims( domain.size().x+1, domain.size().y+1); my_virt_view_t bars( dims, locator_t( point_t(0,0), point_t(1,1), deref_t( dims))); boost::gil::copy_pixels( boost::gil::subimage_view( bars, defined.min.x - domain.min.x, defined.min.y - domain.min.y, defined.size().x+1, defined.size().y+1), view); }
image_t *clip_t::get_output_image( OfxTime time, OfxRectD *optionalBounds) { RAMEN_ASSERT( node()); RAMEN_ASSERT( node()->composition()); RAMEN_ASSERT( !node()->image_empty()); RAMEN_ASSERT( time == node()->composition()->frame()); RAMEN_ASSERT( getComponents() == kOfxImageComponentRGBA); RAMEN_ASSERT( getPixelDepth() == kOfxBitDepthFloat); Imath::Box2i area; if( optionalBounds) { area = Imath::Box2i( Imath::V2i( optionalBounds->x1, optionalBounds->y1), Imath::V2i( optionalBounds->x2 - 1, optionalBounds->y2 - 1)); area = Imath::scale( area, 1.0f / node()->render_context().subsample); area = node()->vertical_flip( area); } else area = node()->defined(); if( area.isEmpty()) { #ifndef NDEBUG DLOG( INFO) << "clip_t::getOutputImage, node = " << node()->name() << ", area == empty"; #endif return 0; } image::const_image_view_t view( node()->const_subimage_view( area)); int rowbytes; void *ptr = view_get_ptr_and_stride( view, rowbytes); // convert to OFX coordinate sys area = node()->vertical_flip( area); OfxRectI bounds; bounds.x1 = area.min.x; bounds.y1 = area.min.y; bounds.x2 = area.max.x + 1; bounds.y2 = area.max.y + 1; #ifndef NDEBUG DLOG( INFO) << "clip_t::getOutputImage, node = " << node()->name(); #endif return new image_t( *this, node()->image(), 1.0 / node()->render_context().subsample, ptr, bounds, bounds, rowbytes, std::string( "")); }
Imath::M33f Transform2DPlug::matrix( const Imath::Box2i &displayWindow, double pixelAspect ) const { // We need to transform from image space (with 0x0 being the bottom left) // to Gadget space (where 0x0 is the top left). To do this, we need to know the // size of the Format. ///\todo: We don't handle the pixel aspect of the format here but we should! float formatHeight = displayWindow.size().y + 1; M33f p; V2f pivotVec = pivotPlug()->getValue(); pivotVec.y = formatHeight - pivotVec.y; p.translate( pivotVec ); M33f t; V2f translateVec = translatePlug()->getValue(); translateVec.y *= -1.; t.translate( translateVec ); M33f r; r.rotate( IECore::degreesToRadians( rotatePlug()->getValue() ) ); M33f s; s.scale( scalePlug()->getValue() ); M33f pi; pi.translate( pivotVec*Imath::V2f(-1.f) ); M33f result = pi * s * r * t * p; return result; }
TiledRgbaOutputFile::TiledRgbaOutputFile (const char name[], int tileXSize, int tileYSize, LevelMode mode, LevelRoundingMode rmode, const Imath::Box2i &displayWindow, const Imath::Box2i &dataWindow, RgbaChannels rgbaChannels, float pixelAspectRatio, const Imath::V2f screenWindowCenter, float screenWindowWidth, LineOrder lineOrder, Compression compression, int numThreads) : _outputFile (0), _toYa (0) { Header hd (displayWindow, dataWindow.isEmpty()? displayWindow: dataWindow, pixelAspectRatio, screenWindowCenter, screenWindowWidth, lineOrder, compression); insertChannels (hd, rgbaChannels, name); hd.setTileDescription (TileDescription (tileXSize, tileYSize, mode, rmode)); _outputFile = new TiledOutputFile (name, hd, numThreads); if (rgbaChannels & WRITE_Y) _toYa = new ToYa (*_outputFile, rgbaChannels); }
void image_node_renderer_t::render( const Imath::Box2i& roi) { RAMEN_ASSERT( has_context_); RAMEN_ASSERT( !roi.isEmpty()); n_->set_interest( roi); breadth_first_inputs_apply( *n_, boost::bind( &image_node_t::calc_inputs_interest_fun, _1, new_context_)); depth_first_inputs_search( *n_, boost::bind( &image_node_t::calc_defined_fun, _1, new_context_)); depth_first_inputs_search( *n_, boost::bind( &image_node_t::subsample_areas_fun, _1, new_context_)); if( do_log) depth_first_inputs_search( *n_, test_empty_images()); depth_first_inputs_search( *n_, boost::bind( &node_t::clear_hash, _1)); depth_first_inputs_search( *n_, boost::bind( &node_t::calc_hash_str, _1, new_context_)); if( do_log) depth_first_inputs_search( *n_, print_areas()); try { n_->recursive_process( new_context_); render_done_ = true; } catch( std::bad_alloc&) { throw boost::enable_error_info( std::bad_alloc()); } }
RgbaOutputFile::RgbaOutputFile (const char name[], const Imath::Box2i &displayWindow, const Imath::Box2i &dataWindow, RgbaChannels rgbaChannels, float pixelAspectRatio, const Imath::V2f screenWindowCenter, float screenWindowWidth, LineOrder lineOrder, Compression compression): _outputFile (0) { Header hd (displayWindow, dataWindow.isEmpty()? displayWindow: dataWindow, pixelAspectRatio, screenWindowCenter, screenWindowWidth, lineOrder, compression); ChannelList ch; if (rgbaChannels & WRITE_R) ch.insert ("R", Channel (HALF, 1, 1)); if (rgbaChannels & WRITE_G) ch.insert ("G", Channel (HALF, 1, 1)); if (rgbaChannels & WRITE_B) ch.insert ("B", Channel (HALF, 1, 1)); if (rgbaChannels & WRITE_A) ch.insert ("A", Channel (HALF, 1, 1)); hd.channels() = ch; _outputFile = new OutputFile (name, hd); }
AcesOutputFile::AcesOutputFile (const std::string &name, const Imath::Box2i &displayWindow, const Imath::Box2i &dataWindow, RgbaChannels rgbaChannels, float pixelAspectRatio, const Imath::V2f screenWindowCenter, float screenWindowWidth, LineOrder lineOrder, Compression compression, int numThreads) : _data (new Data) { checkCompression (compression); Header newHeader (displayWindow, dataWindow.isEmpty()? displayWindow: dataWindow, pixelAspectRatio, screenWindowCenter, screenWindowWidth, lineOrder, compression); addChromaticities (newHeader, acesChromaticities()); addAdoptedNeutral (newHeader, acesChromaticities().white); _data->rgbaFile = new RgbaOutputFile (name.c_str(), newHeader, rgbaChannels, numThreads); _data->rgbaFile->setYCRounding (7, 6); }
void flipbook_t::set_format( const Imath::Box2i& f, float aspect, int subsample) { RAMEN_ASSERT( !f.isEmpty()); format_ = ImathExt::scale( f, 1.0f / subsample); buffer_ = image::buffer_t( format_, 4); aspect_ = aspect; }
void set_matte_node_t::do_process( const render::context_t& context) { if( defined().isEmpty()) return; boost::gil::tbb_transform_pixels( input_as<image_node_t>( 0)->const_subimage_view( defined()), image_view(), copy_rgb_and_clear_alpha()); Imath::Box2i area = ImathExt::intersect( defined(), input_as<image_node_t>( 1)->defined()); if( !area.isEmpty()) boost::gil::tbb_copy_pixels( boost::gil::nth_channel_view( input_as<image_node_t>( 1)->const_subimage_view( area), 3), boost::gil::nth_channel_view( subimage_view( area), 3)); if( get_value<bool>( param( "premultiply"))) image::premultiply( image_view(), image_view()); }
Imath::Box2i Mix::computeDataWindow( const Gaffer::Context *context, const ImagePlug *parent ) const { const float mix = mixPlug()->getValue(); if( mix == 0.0f ) { return inPlugs()->getChild< ImagePlug>( 0 )->dataWindowPlug()->getValue(); } else if( mix == 1.0f && !maskPlug()->getInput<ValuePlug>() ) { return inPlugs()->getChild< ImagePlug >( 1 )->dataWindowPlug()->getValue(); } Imath::Box2i dataWindow; for( ImagePlugIterator it( inPlugs() ); !it.done(); ++it ) { // We don't need to check that the plug is connected here as unconnected plugs don't have data windows. dataWindow.extendBy( (*it)->dataWindowPlug()->getValue() ); } return dataWindow; }
void keyer_node_t::sample_input( const Imath::Box2i& area, std::vector<Imath::Color3f>& colors) const { if( input_pixels_.empty()) return; Imath::Box2i subarea = Imath::intersect( area, input_data_window_); if( subarea.isEmpty()) return; Imath::V2i p; for( p.y = subarea.min.y; p.y <= subarea.max.y; ++p.y) { for( p.x = subarea.min.x; p.x <= subarea.max.x; ++p.x) { image::pixel_t px( input_pixels_.const_rgba_view()( p.x - input_data_window_.min.x, p.y - input_data_window_.min.y)); colors.push_back( Imath::Color3f( boost::gil::get_color( px, boost::gil::red_t()), boost::gil::get_color( px, boost::gil::green_t()), boost::gil::get_color( px, boost::gil::blue_t()))); } } }
void copy_channels_node_t::do_calc_bounds( const render::render_context_t& context) { Imath::Box2i rod1 = input(0)->bounds(); Imath::Box2i rod2 = input(1)->bounds(); int ch_r = get_value<int>( param( "red")); int ch_g = get_value<int>( param( "green")); int ch_b = get_value<int>( param( "blue")); int ch_a = get_value<int>( param( "alpha")); // use alpha from input 1 if( ch_a == copy_source) { set_bounds( rod1); return; } // alpha comes from input2 if( ch_a != set_one && ch_a != set_zero) { set_bounds( rod2); return; } // alpha is zero or one, look at the other channels Imath::Box2i rod; if( ch_r == copy_source) rod = rod1; else { if( ch_r != set_zero && ch_r != set_one) rod = rod2; } if( ch_g == copy_source) rod.extendBy( rod1); else { if( ch_g != set_zero && ch_r != set_one) rod.extendBy( rod2); } if( ch_b == copy_source) rod.extendBy( rod1); else { if( ch_b != set_zero && ch_r != set_one) rod.extendBy( rod2); } if( rod.isEmpty()) rod = rod1; set_bounds( rod); }
void copy_channels_node_t::do_calc_defined( const render::render_context_t& context) { Imath::Box2i def1 = input(0)->defined(); Imath::Box2i def2 = input(1)->defined(); int ch_r = get_value<int>( param( "red")); int ch_g = get_value<int>( param( "green")); int ch_b = get_value<int>( param( "blue")); int ch_a = get_value<int>( param( "alpha")); // use alpha from input 1 if( ch_a == copy_source) { set_defined( def1); return; } // alpha comes from input2 if( ch_a != set_one && ch_a != set_zero) { set_defined( def2); return; } // alpha is zero or one, look at the other channels Imath::Box2i def; if( ch_r == copy_source) def = def1; else { if( ch_r != set_zero && ch_r != set_one) def = def2; } if( ch_g == copy_source) def.extendBy( def1); else { if( ch_g != set_zero && ch_r != set_one) def.extendBy( def2); } if( ch_b == copy_source) def.extendBy( def1); else { if( ch_b != set_zero && ch_r != set_one) def.extendBy( def2); } if( def.isEmpty()) def = def1; set_defined( def); }
virtual void imageData( const Imath::Box2i &box, const float *data, size_t dataSize ) { Box2i yUpBox = m_gafferFormat.yDownToFormatSpace( box ); const V2i boxMinTileOrigin = ImagePlug::tileOrigin( yUpBox.min ); const V2i boxMaxTileOrigin = ImagePlug::tileOrigin( yUpBox.max ); for( int tileOriginY = boxMinTileOrigin.y; tileOriginY <= boxMaxTileOrigin.y; tileOriginY += ImagePlug::tileSize() ) { for( int tileOriginX = boxMinTileOrigin.x; tileOriginX <= boxMaxTileOrigin.x; tileOriginX += ImagePlug::tileSize() ) { for( int channelIndex = 0, numChannels = channelNames().size(); channelIndex < numChannels; ++channelIndex ) { const V2i tileOrigin( tileOriginX, tileOriginY ); ConstFloatVectorDataPtr tileData = getTile( tileOrigin, channelIndex ); if( !tileData ) { // we've been sent data outside of the data window continue; } // we must create a new object to hold the updated tile data, // because the old one might well have been returned from // computeChannelData and be being held in the cache. FloatVectorDataPtr updatedTileData = tileData->copy(); vector<float> &updatedTile = updatedTileData->writable(); const Box2i tileBound( tileOrigin, tileOrigin + Imath::V2i( GafferImage::ImagePlug::tileSize() - 1 ) ); const Box2i transferBound = IECore::boxIntersection( tileBound, yUpBox ); for( int y = transferBound.min.y; y<=transferBound.max.y; ++y ) { int srcY = m_gafferFormat.formatToYDownSpace( y ); size_t srcIndex = ( ( srcY - box.min.y ) * ( box.size().x + 1 ) * numChannels ) + ( transferBound.min.x - box.min.x ) + channelIndex; size_t dstIndex = ( y - tileBound.min.y ) * ImagePlug::tileSize() + transferBound.min.x - tileBound.min.x; const size_t srcEndIndex = srcIndex + transferBound.size().x * numChannels; while( srcIndex <= srcEndIndex ) { updatedTile[dstIndex] = data[srcIndex]; srcIndex += numChannels; dstIndex++; } } setTile( tileOrigin, channelIndex, updatedTileData ); } } } dataReceivedSignal()( this, box ); }
// All the work is done here int joinEXRs( int tilesX, int tilesY, const char* baseName, bool deleteTiles, bool Verbose ) { int exitCode = 0; // Expand names if( Verbose ) printf("Image file name = '%s', tilesX=%d, tilesY=%d\n", baseName, tilesX, tilesY); int numTiles = tilesX * tilesY; // Allocate memory: char ** tileNames = new char * [numTiles]; Imf::InputFile ** iFiles = new Imf::InputFile * [numTiles]; for( int i = 0; i < numTiles; i++) { tileNames[i] = new char[FILENAME_MAXLEN]; iFiles[i] = 0; } // Insert tile info and check if files exist int nonEmptyTile = -1; struct stat stFileInfo; for( int i = 0; i < numTiles; i++) { sprintf( tileNames[i], "%s.tile_%d.exr", baseName, i); if( Verbose ) printf("Tile name %d = '%s'\n", i, tileNames[i]); if( stat( tileNames[i], &stFileInfo ) == 0 ) { // File exists - so open it and check for validness iFiles[i] = new Imf::InputFile( tileNames[i]); if( false == iFiles[i]->isComplete()) { fprintf( stderr, "Error: File '%s' is incomplete or is not an OpenEXR file.\n", tileNames[i]); fflush( stderr); delete iFiles[i]; iFiles[i] = 0; exitCode = 1; } else if( nonEmptyTile == -1 ) { nonEmptyTile = i; } } else { fprintf( stderr, "Error: File '%s' not founded.\n", tileNames[i]); fflush( stderr); exitCode = 1; } } if( nonEmptyTile < 0) // All tiles were empty { fprintf( stderr, "Error: No tile files founded.\n"); fflush( stderr); } else { // Gather info from a non-empty tile file Imf::Header inHeader = iFiles[nonEmptyTile]->header(); Imath::Box2i imageBox = inHeader.displayWindow(); // size of the resulting image int imageWidth = imageBox.max.x - imageBox.min.x + 1; int imageHeight = imageBox.max.y - imageBox.min.y + 1; // Iterate through all the channels and reserve mem for the whole display window // also add channels to the header of the output file Imf::Header outHeader( imageWidth, imageHeight); std::map< Imf::Name, ChannelInfo* > chInfos; // this will hold pixel data and stride for each channel in input files Imf::ChannelList channels = inHeader.channels(); Imf::ChannelList::ConstIterator itCh; for( itCh = channels.begin(); itCh != channels.end(); itCh++ ) { chInfos[itCh.name()] = new ChannelInfo( typeSize( itCh.channel().type), imageHeight, imageWidth ); outHeader.channels().insert( itCh.name(), Imf::Channel( itCh.channel().type)); if( Verbose) printf("Channel: '%s' | stride: %d\n", itCh.name(), typeSize( itCh.channel().type)); } // Collect data from files Imath::Box2i tileBox; // each tile's data window Imath::Box2i resultBox; // resulting data window (should be sum of all tiles' data windows) Imf::FrameBuffer fb; for( int i = 0; i < numTiles; i++) { if( iFiles[i] == 0) // no file for this tile continue; tileBox = iFiles[i]->header().dataWindow(); resultBox.extendBy( tileBox ); if( Verbose) printf("Data win: xmin=%d xmax=%d ymin=%d ymax=%d\n", tileBox.min.x, tileBox.max.x, tileBox.min.y, tileBox.max.y); channels = iFiles[i]->header().channels(); for( itCh = channels.begin(); itCh != channels.end(); itCh++ ) fb.insert( itCh.name(), Imf::Slice( itCh.channel().type, // pixel type (char*)&chInfos[itCh.name()]->array2d[0][0], // base chInfos[itCh.name()]->stride, // x stride chInfos[itCh.name()]->stride * imageWidth, // y stride 1, 1, 0.0 ) ); // x,y sampling, fill value iFiles[i]->setFrameBuffer(fb); iFiles[i]->readPixels( tileBox.min.y, tileBox.max.y); } // Write out everything: outHeader.dataWindow() = resultBox; Imf::OutputFile imageFile( baseName, outHeader ); imageFile.setFrameBuffer(fb); imageFile.writePixels( resultBox.max.y-resultBox.min.y+1 ); printf("Joined EXR image successfully written.\n"); // Free files: for( int i = 0; i < numTiles; i++) if( iFiles[i] != 0 ) delete iFiles[i]; delete [] iFiles; if( deleteTiles ) { for( int i = 0; i < numTiles; i++) if( unlink( tileNames[i]) != 0) { perror("Remove"); printf("Can't remove file '%s'\n", tileNames[i]); } } } // Free names: for( int i = 0; i < numTiles; i++) delete [] tileNames[i]; delete [] tileNames; return exitCode; }
DataPtr CINImageReader::readTypedChannel( const std::string &name, const Imath::Box2i &dataWindow ) { typedef TypedData< std::vector< V > > TargetVector; // figure out the offset into the bitstream for the given channel assert( m_header->m_channelOffsets.find( name ) != m_header->m_channelOffsets.end() ); int channelOffset = m_header->m_channelOffsets[name]; assert( (int)m_header->m_imageInformation.channel_information[channelOffset].bpp == 10 ); int bpp = m_header->m_imageInformation.channel_information[channelOffset].bpp; // todo: make sure ushort is enough for the current bpp int ushortShift = sizeof(unsigned short)*8 - bpp; // form the mask for this channel unsigned int mask = 0; for (int pi = 0; pi < bpp; ++pi) { mask = 1 + (mask << 1); } mask <<= ((32 - bpp) - channelOffset * bpp); ScaledDataConversion< unsigned short, V> converter; typename TargetVector::Ptr dataContainer = new TargetVector(); typename TargetVector::ValueType &data = dataContainer->writable(); int area = ( dataWindow.size().x + 1 ) * ( dataWindow.size().y + 1 ); assert( area >= 0 ); data.resize( area ); int dataWidth = 1 + dataWindow.size().x; Box2i wholeDataWindow = this->dataWindow(); const int yMin = dataWindow.min.y - wholeDataWindow.min.y; const int yMax = dataWindow.max.y - wholeDataWindow.min.y; const int xMin = dataWindow.min.x - wholeDataWindow.min.x; const int xMax = dataWindow.max.x - wholeDataWindow.min.x; int dataY = 0; for ( int y = yMin ; y <= yMax ; ++y, ++dataY ) { typename TargetVector::ValueType::size_type dataOffset = dataY * dataWidth; std::vector<unsigned int>::size_type bufferOffset = y * m_bufferWidth + xMin; for ( int x = xMin; x <= xMax ; ++x, ++dataOffset, ++bufferOffset ) { assert( dataOffset < data.size() ); unsigned int cell = m_buffer[ bufferOffset ]; if ( m_reverseBytes ) { cell = reverseBytes( cell ); } // assume we have 10bit log, two wasted bits aligning to 32 longword unsigned short cv = (unsigned short) ( ( mask & cell ) >> ( 2 + ( 2 - channelOffset ) * bpp ) ); assert( cv < 1024 ); data[dataOffset] = converter( (cv << ushortShift) + ((1 << ushortShift) - 1) ); } } return dataContainer; }
image_t *clip_t::get_input_image( OfxTime time, OfxRectD *optionalBounds) { RAMEN_ASSERT( node()); RAMEN_ASSERT( node()->composition()); RAMEN_ASSERT( port_ != -1); RAMEN_ASSERT( getPixelDepth() == kOfxBitDepthFloat); image_node_t *in = node()->input_as<image_node_t>( port()); if( !in) { #ifndef NDEBUG DLOG( INFO) << "clip_t::getImage, node = " << node()->name() << ", port = " << port() << ", result = 0"; #endif return 0; } render::context_t context = node()->render_context(); context.composition = node()->composition(); context.result_node = in; context.frame = time; render::context_guard_t guard( node()->composition()->current_context(), in); render::image_node_renderer_t r( context); Imath::Box2i area; if( optionalBounds) { // TODO: is this correct if the effect does not support tiles? area = Imath::Box2i( Imath::V2i( optionalBounds->x1, optionalBounds->y1), Imath::V2i( optionalBounds->x2 - 1, optionalBounds->y2 - 1)); area = node()->vertical_flip( area); r.render( area); area.min.x = optionalBounds->x1; area.min.y = optionalBounds->y1; area.max.x = optionalBounds->x2 - 1; area.max.y = optionalBounds->y2 - 1; area = node()->vertical_flip( area); area = intersect( in->defined(), area); } else { r.render(); area = in->defined(); } if( area.isEmpty()) { #ifndef NDEBUG DLOG( INFO) << "clip_t::getImage, node = " << node()->name() << ", port = " << port() << ", area == empty"; #endif return 0; } image::buffer_t pixels = in->image(); image::const_image_view_t view( in->const_subimage_view( area)); area = node()->vertical_flip( area); OfxRectI bounds; bounds.x1 = area.min.x; bounds.y1 = area.min.y; bounds.x2 = area.max.x + 1; bounds.y2 = area.max.y + 1; std::stringstream s; for( int i = 0; i < 16; ++i) s << (int) in->digest()[i]; #ifndef NDEBUG if( optionalBounds) DLOG( INFO) << "clip_t::getImage, node = " << node()->name() << ", port = " << port() << ", bounds = " << *optionalBounds; else DLOG( INFO) << "clip_t::getImage, node = " << node()->name() << ", port = " << port(); #endif image_t *result = 0; if( getComponents() == kOfxImageComponentRGBA) { int rowbytes; void *ptr = view_get_ptr_and_stride( view, rowbytes); result = new image_t( *this, pixels, 1.0 / node()->render_context().subsample, ptr, bounds, bounds, rowbytes, s.str()); } else { // TODO: create an gray scale image and copy the alpha channel RAMEN_ASSERT( 0); } in->release_image(); return result; }
OfxRectD clip_t::getRegionOfDefinition( OfxTime time) const { RAMEN_ASSERT( node()); const image_node_t *in = 0; bool use_default = false; bool restore_time = false; float saved_frame; // calling getRoD for the output clip does not make a lot of sense, but some plugins do... if( port_ == -1) in = node(); else // normal case in = node()->input_as<const image_node_t>( port_); if( !in) { in = node(); use_default = true; } if( node()->composition()) { saved_frame = node()->composition()->frame(); if( saved_frame != time) { restore_time = true; composition_t *c = const_cast<composition_t*>( node()->composition()); c->set_frame( time); } } Imath::Box2i b; if( use_default) { for( int i = 0; i < in->num_inputs(); ++i) { if( const image_node_t *src = in->input_as<const image_node_t>( i)) b.extendBy( src->bounds()); } if( b.isEmpty()) { if( in->composition()) b = node()->composition()->default_format().area(); else b = preferences_t::Instance().default_format().area(); } } else b = in->bounds(); b = node()->vertical_flip( b); OfxRectD v; v.x1 = b.min.x; v.y1 = b.min.y; v.x2 = b.max.x + 1; v.y2 = b.max.y + 1; if( restore_time) { composition_t *c = const_cast<composition_t*>( node()->composition()); c->set_frame( saved_frame); } #ifndef NDEBUG DLOG( INFO) << "clip_t::getRoD, node = " << node()->name() << ", port = " << port() << ", result = " << v; #endif return v; }
void Crop::compute( Gaffer::ValuePlug *output, const Gaffer::Context *context ) const { if ( output == cropWindowPlug() ) { int areaSource = areaSourcePlug()->getValue(); Imath::Box2i cropWindow; switch ( areaSource ) { case Crop::DataWindow: { cropWindow = inPlug()->dataWindowPlug()->getValue(); break; } case Crop::DisplayWindow: { cropWindow = inPlug()->formatPlug()->getValue().getDisplayWindow(); break; } case Crop::Format: { cropWindow = formatPlug()->getValue().getDisplayWindow(); if( formatCenterPlug()->getValue() ) { const Imath::Box2i displayWindow = inPlug()->formatPlug()->getValue().getDisplayWindow(); Imath::V2i centerOffset( cropWindow.center() - displayWindow.center() ); cropWindow.min -= centerOffset; cropWindow.max -= centerOffset; } break; } default: { cropWindow = areaPlug()->getValue(); break; } } static_cast<Gaffer::AtomicBox2iPlug *>( output )->setValue( cropWindow ); } else if( output->parent<Plug>() == offsetPlug() ) { ImagePlug::GlobalScope c( context ); V2i offset( 0 ); if( affectDisplayWindowPlug()->getValue() ) { if( resetOriginPlug()->getValue() ) { offset -= cropWindowPlug()->getValue().min; } else if( areaSourcePlug()->getValue() == Crop::Format && formatCenterPlug()->getValue() ) { offset -= cropWindowPlug()->getValue().min - formatPlug()->getValue().getDisplayWindow().min; } } static_cast<IntPlug *>( output )->setValue( output == offsetPlug()->getChild( 0 ) ? offset[0] : offset[1] ); } else { ImageProcessor::compute( output, context ); } }
gray_image_view_t buffer_t::gray_subimage_view( const Imath::Box2i& area) const { check_area_inside_image( area); return boost::gil::subimage_view( gray_view(), area.min.x - bounds_.min.x, area.min.y - bounds_.min.y, area.size().x+1, area.size().y+1); }
DataPtr EXRImageReader::readTypedChannel( const std::string &name, const Imath::Box2i &dataWindow, const Imf::Channel *channel ) { assert( channel ); Imath::V2i pixelDimensions = dataWindow.size() + Imath::V2i( 1 ); unsigned numPixels = pixelDimensions.x * pixelDimensions.y; typedef TypedData<vector<T> > DataType; typename DataType::Ptr data = new DataType; data->writable().resize( numPixels ); Imath::Box2i fullDataWindow = this->dataWindow(); if( fullDataWindow.min.x==dataWindow.min.x && fullDataWindow.max.x==dataWindow.max.x ) { // the width we want to read matches the width in the file, so we can read straight // into the result buffer FrameBuffer frameBuffer; T *buffer00 = data->baseWritable() - dataWindow.min.y * pixelDimensions.x - fullDataWindow.min.x; Slice slice( channel->type, (char *)buffer00, sizeof(T), sizeof(T) * pixelDimensions.x ); frameBuffer.insert( name.c_str(), slice ); m_inputFile->setFrameBuffer( frameBuffer ); // exr library will choose the best order to read scanlines automatically (increasing or decreasing) try { m_inputFile->readPixels( dataWindow.min.y, dataWindow.max.y ); } catch( Iex::InputExc &e ) { // so we can read incomplete files msg( Msg::Warning, "EXRImageReader::readChannel", e.what() ); return data; } } else { // widths don't match, we need to read into a temporary buffer and then transfer just // the bits we need into the result buffer. int numTmpPixels = fullDataWindow.size().x + 1; vector<T> tmpBuffer( numTmpPixels ); T *tmpBufferTransferStart = &(tmpBuffer[0]) + dataWindow.min.x - fullDataWindow.min.x; size_t tmpBufferTransferLength = pixelDimensions.x * sizeof( T ); T *transferDestination = &(data->writable()[0]); // slice has yStride of 0 so each successive scanline just overwrites the previous one Slice slice( channel->type, (char *)(&(tmpBuffer[0]) - fullDataWindow.min.x), sizeof(T), 0 ); FrameBuffer frameBuffer; frameBuffer.insert( name.c_str(), slice ); m_inputFile->setFrameBuffer( frameBuffer ); int yStart = dataWindow.min.y; int yEnd = dataWindow.max.y; int yStep = 1; try { for( int y=yStart; y!=(yEnd+yStep); y+=yStep ) { m_inputFile->readPixels( y ); memcpy( (char *)transferDestination, (const char *)tmpBufferTransferStart, tmpBufferTransferLength ); transferDestination += pixelDimensions.x; } } catch( Iex::InputExc &e ) { // so we can read incomplete files msg( Msg::Warning, "EXRImageReader::readChannel", e.what() ); return data; } } #ifndef NDEBUG for ( typename DataType::ValueType::const_iterator it = data->readable().begin(); it != data->readable().end(); ++it ) { assert( *it == *it ); // Will fail iff NaN } #endif return data; }
///\todo: It seems that if a JPG is written with RGBA channels the output is wrong but it should be supported. Find out why and fix it. /// There is a test case in ImageWriterTest which checks the output of the jpg writer against an incorrect image and it will fail if it is equal to the writer output. void ImageWriter::execute( const Contexts &contexts ) const { if( !inPlug()->getInput<ImagePlug>() ) { throw IECore::Exception( "No input image." ); } // Loop over the execution contexts... for( Contexts::const_iterator it = contexts.begin(), eIt = contexts.end(); it != eIt; it++ ) { Context::Scope scopedContext( it->get() ); std::string fileName = fileNamePlug()->getValue(); fileName = (*it)->substitute( fileName ); boost::shared_ptr< ImageOutput > out( ImageOutput::create( fileName.c_str() ) ); if ( !out ) { throw IECore::Exception( boost::str( boost::format( "Invalid filename: %s" ) % fileName ) ); } // Grab the intersection of the channels from the "channels" plug and the image input to see which channels we are to write out. IECore::ConstStringVectorDataPtr channelNamesData = inPlug()->channelNamesPlug()->getValue(); std::vector<std::string> maskChannels = channelNamesData->readable(); channelsPlug()->maskChannels( maskChannels ); const int nChannels = maskChannels.size(); // Get the image channel data. IECore::ImagePrimitivePtr imagePtr( inPlug()->image() ); // Get the image's display window. const Imath::Box2i displayWindow( imagePtr->getDisplayWindow() ); const int displayWindowWidth = displayWindow.size().x+1; const int displayWindowHeight = displayWindow.size().y+1; // Get the image's data window and if it then set a flag. bool imageIsBlack = false; Imath::Box2i dataWindow( imagePtr->getDataWindow() ); if ( inPlug()->dataWindowPlug()->getValue().isEmpty() ) { dataWindow = displayWindow; imageIsBlack = true; } int dataWindowWidth = dataWindow.size().x+1; int dataWindowHeight = dataWindow.size().y+1; // Create the image header. ImageSpec spec( dataWindowWidth, dataWindowHeight, nChannels, TypeDesc::FLOAT ); // Add the channel names to the header whilst getting pointers to the channel data. std::vector<const float*> channelPtrs; spec.channelnames.clear(); for ( std::vector<std::string>::iterator channelIt( maskChannels.begin() ); channelIt != maskChannels.end(); channelIt++ ) { spec.channelnames.push_back( *channelIt ); IECore::FloatVectorDataPtr dataPtr = imagePtr->getChannel<float>( *channelIt ); channelPtrs.push_back( &(dataPtr->readable()[0]) ); // OIIO has a special attribute for the Alpha and Z channels. If we find some, we should tag them... if ( *channelIt == "A" ) { spec.alpha_channel = channelIt-maskChannels.begin(); } else if ( *channelIt == "Z" ) { spec.z_channel = channelIt-maskChannels.begin(); } } // Specify the display window. spec.full_x = displayWindow.min.x; spec.full_y = displayWindow.min.y; spec.full_width = displayWindowWidth; spec.full_height = displayWindowHeight; spec.x = dataWindow.min.x; spec.y = dataWindow.min.y; if ( !out->open( fileName, spec ) ) { throw IECore::Exception( boost::str( boost::format( "Could not open \"%s\", error = %s" ) % fileName % out->geterror() ) ); } // Only allow tiled output if our file format supports it. int writeMode = writeModePlug()->getValue() & out->supports( "tile" ); if ( writeMode == Scanline ) { // Create a buffer for the scanline. float scanline[ nChannels*dataWindowWidth ]; if ( imageIsBlack ) { memset( scanline, 0, sizeof(float) * nChannels*dataWindowWidth ); for ( int y = spec.y; y < spec.y + dataWindowHeight; ++y ) { if ( !out->write_scanline( y, 0, TypeDesc::FLOAT, &scanline[0] ) ) { throw IECore::Exception( boost::str( boost::format( "Could not write scanline to \"%s\", error = %s" ) % fileName % out->geterror() ) ); } } } else { // Interleave the channel data and write it by scanline to the file. for ( int y = spec.y; y < spec.y + dataWindowHeight; ++y ) { for ( std::vector<const float *>::iterator channelDataIt( channelPtrs.begin() ); channelDataIt != channelPtrs.end(); channelDataIt++ ) { float *outPtr = &scanline[0] + (channelDataIt - channelPtrs.begin()); // The pointer that we are writing to. // The row that we are reading from is flipped (in the Y) as we use a different image space internally to OpenEXR and OpenImageIO. const float *inRowPtr = (*channelDataIt) + ( y - spec.y ) * dataWindowWidth; const int inc = channelPtrs.size(); for ( int x = 0; x < dataWindowWidth; ++x, outPtr += inc ) { *outPtr = *inRowPtr++; } } if ( !out->write_scanline( y, 0, TypeDesc::FLOAT, &scanline[0] ) ) { throw IECore::Exception( boost::str( boost::format( "Could not write scanline to \"%s\", error = %s" ) % fileName % out->geterror() ) ); } } } } // Tiled output else { // Create a buffer for the tile. const int tileSize = ImagePlug::tileSize(); float tile[ nChannels*tileSize*tileSize ]; if ( imageIsBlack ) { memset( tile, 0, sizeof(float) * nChannels*tileSize*tileSize ); for ( int tileY = 0; tileY < dataWindowHeight; tileY += tileSize ) { for ( int tileX = 0; tileX < dataWindowWidth; tileX += tileSize ) { if ( !out->write_tile( tileX+dataWindow.min.x, tileY+spec.y, 0, TypeDesc::FLOAT, &tile[0] ) ) { throw IECore::Exception( boost::str( boost::format( "Could not write tile to \"%s\", error = %s" ) % fileName % out->geterror() ) ); } } } } else { // Interleave the channel data and write it to the file tile-by-tile. for ( int tileY = 0; tileY < dataWindowHeight; tileY += tileSize ) { for ( int tileX = 0; tileX < dataWindowWidth; tileX += tileSize ) { float *outPtr = &tile[0]; const int r = std::min( tileSize+tileX, dataWindowWidth ); const int t = std::min( tileSize+tileY, dataWindowHeight ); for ( int y = 0; y < t; ++y ) { for ( std::vector<const float *>::iterator channelDataIt( channelPtrs.begin() ); channelDataIt != channelPtrs.end(); channelDataIt++ ) { const int inc = channelPtrs.size(); const float *inRowPtr = (*channelDataIt) + ( tileY + t - y - 1 ) * dataWindowWidth; for ( int x = 0; x < r; ++x, outPtr += inc ) { *outPtr = *inRowPtr+(tileX+x); } } } if ( !out->write_tile( tileX+dataWindow.min.x, tileY+spec.y, 0, TypeDesc::FLOAT, &tile[0] ) ) { throw IECore::Exception( boost::str( boost::format( "Could not write tile to \"%s\", error = %s" ) % fileName % out->geterror() ) ); } } } } } out->close(); } }