ReturnType operator()( T *dataContainer ) { assert( dataContainer ); const typename T::ValueType &data = dataContainer->readable(); ScaledDataConversion<typename T::ValueType::value_type, float> converter; typedef boost::multi_array_ref< const typename T::ValueType::value_type, 2 > SourceArray2D; typedef boost::multi_array_ref< unsigned int, 2 > TargetArray2D; // Grab the display and data windows to avoid dereferencing pointers during the tight loop later... const Box2i srcDisplayWindow = m_image->getDisplayWindow(); const Box2i srcDataWindow = m_image->getDataWindow(); const SourceArray2D sourceData( &data[0], extents[ srcDataWindow.size().y + 1 ][ srcDataWindow.size().x + 1 ] ); TargetArray2D targetData( &m_imageBuffer[0], extents[ srcDisplayWindow.size().y + 1 ][ srcDisplayWindow.size().x + 1 ] ); const Box2i copyRegion = boxIntersection( m_dataWindow, boxIntersection( srcDisplayWindow, srcDataWindow ) ); const unsigned int boxOffsetX = copyRegion.min.x - srcDisplayWindow.min.x; const unsigned int boxOffsetY = copyRegion.min.y - srcDisplayWindow.min.y; for ( int y = copyRegion.min.y; y <= copyRegion.max.y ; y++ ) { for ( int x = copyRegion.min.x; x <= copyRegion.max.x ; x++ ) { targetData[ (y - copyRegion.min.y) + boxOffsetY ][ (x - copyRegion.min.x) + boxOffsetX ] |= std::min((unsigned int)1023, (unsigned int)(converter( sourceData[ y - srcDataWindow.min.y ][ x - srcDataWindow.min.x ] ) * 1023 )) << m_bitShift; } } };
IECore::ImagePrimitivePtr ImagePlug::image() const { Format format = formatPlug()->getValue(); Box2i dataWindow = dataWindowPlug()->getValue(); Box2i newDataWindow( Imath::V2i(0) ); if( dataWindow.isEmpty() ) { dataWindow = Box2i( Imath::V2i(0) ); } else { newDataWindow = format.yDownToFormatSpace( dataWindow ); } ImagePrimitivePtr result = new ImagePrimitive( newDataWindow, format.getDisplayWindow() ); ConstStringVectorDataPtr channelNamesData = channelNamesPlug()->getValue(); const vector<string> &channelNames = channelNamesData->readable(); vector<float *> imageChannelData; for( vector<string>::const_iterator it = channelNames.begin(), eIt = channelNames.end(); it!=eIt; it++ ) { FloatVectorDataPtr cd = new FloatVectorData; vector<float> &c = cd->writable(); c.resize( result->variableSize( PrimitiveVariable::Vertex ), 0.0f ); result->variables[*it] = PrimitiveVariable( PrimitiveVariable::Vertex, cd ); imageChannelData.push_back( &(c[0]) ); } parallel_for( blocked_range2d<size_t>( 0, dataWindow.size().x+1, tileSize(), 0, dataWindow.size().y+1, tileSize() ), GafferImage::Detail::CopyTiles( imageChannelData, channelNames, channelDataPlug(), dataWindow, Context::current(), tileSize()) ); return result; }
void DisplayIop::engine( int y, int x, int r, const DD::Image::ChannelSet &channels, DD::Image::Row &row ) { Channel outputChannels[4] = { Chan_Red, Chan_Green, Chan_Blue, Chan_Alpha }; const char *inputChannels[] = { "R", "G", "B", "A", nullptr }; const ImagePrimitive *image = nullptr; Box2i inputDataWindow; Box2i inputDisplayWindow; if( firstDisplayIop()->m_driver ) { image = firstDisplayIop()->m_driver->image().get(); inputDataWindow = image->getDataWindow(); inputDisplayWindow = image->getDisplayWindow(); } int i = 0; while( inputChannels[i] ) { const FloatVectorData *inputData = image ? image->getChannel<float>( inputChannels[i] ) : nullptr; if( inputData ) { float *output = row.writable( outputChannels[i] ) + x; // remap coordinate relative to our data window. nuke images have pixel origin at bottom, // cortex images have pixel origin at top. V2i pDataWindow = V2i( x, inputDisplayWindow.max.y - y ) - inputDataWindow.min; const float *input = &((inputData->readable())[pDataWindow.y*(inputDataWindow.size().x+1) + pDataWindow.x]); memcpy( output, input, (r-x) * sizeof( float ) ); } else { row.erase( outputChannels[i] ); } i++; } }
DataPtr TIFFImageReader::readTypedChannel( const std::string &name, const Box2i &dataWindow ) { typedef TypedData< std::vector< V > > TargetVector; typename TargetVector::Ptr dataContainer = new TargetVector(); typename TargetVector::ValueType &data = dataContainer->writable(); std::vector<std::string> names; channelNames( names ); std::vector<std::string>::iterator it = find( names.begin(), names.end(), name ); if ( it == names.end() ) { throw IOException( (boost::format( "TIFFImageReader: Could not find channel \"%s\" while reading %s") % name % fileName() ).str() ); } int channelOffset = std::distance( names.begin(), it ); assert( channelOffset >= 0 ); assert( channelOffset < (int)names.size() ); if ( channelOffset >= m_samplesPerPixel ) { throw IOException( (boost::format( "TIFFImageReader: Insufficient samples-per-pixel (%d) for reading channel \"%s\"") % m_samplesPerPixel % name).str() ); } int area = ( dataWindow.size().x + 1 ) * ( dataWindow.size().y + 1 ); assert( area >= 0 ); data.resize( area ); int dataWidth = 1 + dataWindow.size().x; int bufferDataWidth = 1 + m_dataWindow.size().x; ScaledDataConversion<T, V> converter; int dataY = 0; for ( int y = dataWindow.min.y - m_dataWindow.min.y ; y <= dataWindow.max.y - m_dataWindow.min.y ; ++y, ++dataY ) { int dataX = 0; for ( int x = dataWindow.min.x - m_dataWindow.min.x; x <= dataWindow.max.x - m_dataWindow.min.x ; ++x, ++dataX ) { const T* buf = reinterpret_cast< T* >( & m_buffer[0] ); assert( buf ); // \todo Currently, we only support PLANARCONFIG_CONTIG for TIFFTAG_PLANARCONFIG. assert( m_planarConfig == PLANARCONFIG_CONTIG ); typename TargetVector::ValueType::size_type dataOffset = dataY * dataWidth + dataX; assert( dataOffset < data.size() ); data[dataOffset] = converter( buf[ m_samplesPerPixel * ( y * bufferDataWidth + x ) + channelOffset ] ); } } return dataContainer; }
void OSLImage::hashChannelNames( const GafferImage::ImagePlug *output, const Gaffer::Context *context, IECore::MurmurHash &h ) const { ImageProcessor::hashChannelNames( output, context, h ); inPlug()->channelNamesPlug()->hash( h ); const Box2i dataWindow = inPlug()->dataWindowPlug()->getValue(); if( !dataWindow.isEmpty() ) { ContextPtr c = new Context( *context, Context::Borrowed ); c->set( ImagePlug::tileOriginContextName, ImagePlug::tileOrigin( dataWindow.min ) ); Context::Scope s( c.get() ); shadingPlug()->hash( h ); } }
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 ); }
IECore::ImagePrimitivePtr ImagePlug::image() const { Format format = formatPlug()->getValue(); Box2i dataWindow = dataWindowPlug()->getValue(); Box2i newDataWindow( Imath::V2i(0) ); if( dataWindow.isEmpty() ) { dataWindow = Box2i( Imath::V2i(0) ); } else { newDataWindow = format.yDownToFormatSpace( dataWindow ); } // use the default format if we don't have an explicit one. /// \todo: remove this once FormatPlug is handling it for /// us during ExecutableNode::execute (see issue #887). if( format.getDisplayWindow().isEmpty() ) { format = Context::current()->get<Format>( Format::defaultFormatContextName, Format() ); } ImagePrimitivePtr result = new ImagePrimitive( newDataWindow, format.getDisplayWindow() ); ConstCompoundObjectPtr metadata = metadataPlug()->getValue(); compoundObjectToCompoundData( metadata.get(), result->blindData() ); ConstStringVectorDataPtr channelNamesData = channelNamesPlug()->getValue(); const vector<string> &channelNames = channelNamesData->readable(); vector<float *> imageChannelData; for( vector<string>::const_iterator it = channelNames.begin(), eIt = channelNames.end(); it!=eIt; it++ ) { FloatVectorDataPtr cd = new FloatVectorData; vector<float> &c = cd->writable(); c.resize( result->variableSize( PrimitiveVariable::Vertex ), 0.0f ); result->variables[*it] = PrimitiveVariable( PrimitiveVariable::Vertex, cd ); imageChannelData.push_back( &(c[0]) ); } parallel_for( blocked_range3d<size_t>( 0, imageChannelData.size(), 1, 0, dataWindow.size().x+1, tileSize(), 0, dataWindow.size().y+1, tileSize() ), GafferImage::Detail::CopyTiles( imageChannelData, channelNames, channelDataPlug(), dataWindow, Context::current(), tileSize()) ); return result; }
void ImagePrimitive::setDisplayWindow( const Box2i &displayWindow ) { if ( displayWindow.isEmpty() ) { throw InvalidArgumentException( "ImagePrimitive: Cannot set displayWindow to the empty window" ); } m_displayWindow = displayWindow; }
void ImageDisplayDriver::imageData( const Box2i &box, const float *data, size_t dataSize ) { Box2i tmpBox = box; Box2i dataWindow = m_image->getDataWindow(); tmpBox.extendBy( dataWindow ); if ( tmpBox != dataWindow ) { throw Exception("The box is outside image data window."); } int pixelSize = channelNames().size(); if ( dataSize != (box.max.x - box.min.x + 1) * (box.max.y - box.min.y + 1) * channelNames().size() ) { throw Exception("Invalid dataSize value."); } int channel, targetX, targetY, sourceWidth, sourceHeight, targetWidth; sourceWidth = box.max.x - box.min.x + 1; sourceHeight = box.max.y - box.min.y + 1; targetWidth = dataWindow.max.x - dataWindow.min.x + 1; channel = 0; targetX = box.min.x - dataWindow.min.x; targetY = box.min.y - dataWindow.min.y; for ( vector<string>::const_iterator it = channelNames().begin(); it != channelNames().end(); it++, channel++ ) { vector< float > &target = boost::static_pointer_cast< FloatVectorData >(m_image->variables[ *it ].data)->writable(); vector< float >::iterator targetIt; const float *sourceIt = data+channel; targetIt = target.begin()+targetWidth*targetY+targetX; for ( int y = 0; y < sourceHeight; y++ ) { for ( int x = 0; x < sourceWidth; x++ ) { *targetIt = *sourceIt; sourceIt += pixelSize; targetIt++; } targetIt += targetWidth - sourceWidth; } } }
void iterateTiles(std::array<QuadSetup, N> quads, Intersector intersector) const { Box2i bounds; for (const auto &q : quads) bounds.grow(q.bounds); if (bounds.empty()) return; uint32 minX = uint32(bounds.min().x()) & ~TileMask, maxX = bounds.max().x(); uint32 minY = uint32(bounds.min().y()) & ~TileMask, maxY = bounds.max().y(); for (auto &q : quads) { q.start(minX + TileSize*0.5f, minY + TileSize*0.5f); for (int k = 0; k < 3; ++k) { q.stepX[k] *= float(TileSize); q.stepY[k] *= float(TileSize); } } for (uint32 y = minY; y < maxY; y += TileSize) { for (auto &q : quads) q.beginRow(); for (uint32 x = minX; x < maxX; x += TileSize) { float wMin = -1.0f; for (auto &q : quads) wMin = max(wMin, q.reduce()); if (wMin >= 0.0f) { uint32 xjMax = min(x + TileSize, _res.x()); uint32 yjMax = min(y + TileSize, _res.y()); for (uint32 yj = y; yj < yjMax; ++yj) for (uint32 xj = x; xj < xjMax; ++xj) intersector(xj, yj, xj + yj*_res.x()); } for (auto &q : quads) q.stepCol(); } for (auto &q : quads) q.endRow(); } }
void ImageSampler::hash( const Gaffer::ValuePlug *output, const Gaffer::Context *context, IECore::MurmurHash &h ) const { ComputeNode::hash( output, context, h ); if( output->parent<Plug>() == colorPlug() ) { std::string channel = channelName( output ); if( channel.size() ) { V2f pixel = pixelPlug()->getValue(); Box2i sampleWindow; sampleWindow.extendBy( V2i( pixel ) - V2i( 1 ) ); sampleWindow.extendBy( V2i( pixel ) + V2i( 1 ) ); Sampler sampler( imagePlug(), channel, sampleWindow ); sampler.hash( h ); h.append( pixel ); } } }
IECore::ConstStringVectorDataPtr OSLImage::computeChannelNames( const Gaffer::Context *context, const GafferImage::ImagePlug *parent ) const { ConstStringVectorDataPtr channelNamesData = inPlug()->channelNamesPlug()->getValue(); set<string> result( channelNamesData->readable().begin(), channelNamesData->readable().end() ); const Box2i dataWindow = inPlug()->dataWindowPlug()->getValue(); if( !dataWindow.isEmpty() ) { ContextPtr c = new Context( *context, Context::Borrowed ); c->set( ImagePlug::tileOriginContextName, ImagePlug::tileOrigin( dataWindow.min ) ); Context::Scope s( c.get() ); ConstCompoundDataPtr shading = runTimeCast<const CompoundData>( shadingPlug()->getValue() ); for( CompoundDataMap::const_iterator it = shading->readable().begin(), eIt = shading->readable().end(); it != eIt; ++it ) { result.insert( it->first ); } } return new StringVectorData( vector<string>( result.begin(), result.end() ) ); }
void operator()( const blocked_range3d<size_t>& r ) const { ContextPtr context = new Context( *m_parentContext, Context::Borrowed ); Context::Scope scope( context.get() ); const Box2i operationWindow( V2i( r.rows().begin()+m_dataWindow.min.x, r.cols().begin()+m_dataWindow.min.y ), V2i( r.rows().end()+m_dataWindow.min.x-1, r.cols().end()+m_dataWindow.min.y-1 ) ); V2i minTileOrigin = ImagePlug::tileOrigin( operationWindow.min ); V2i maxTileOrigin = ImagePlug::tileOrigin( operationWindow.max ); size_t imageStride = m_dataWindow.size().x + 1; for( size_t channelIndex = r.pages().begin(); channelIndex < r.pages().end(); ++channelIndex ) { context->set( ImagePlug::channelNameContextName, m_channelNames[channelIndex] ); float *channelBegin = m_imageChannelData[channelIndex]; for( int tileOriginY = minTileOrigin.y; tileOriginY <= maxTileOrigin.y; tileOriginY += m_tileSize ) { for( int tileOriginX = minTileOrigin.x; tileOriginX <= maxTileOrigin.x; tileOriginX += m_tileSize ) { context->set( ImagePlug::tileOriginContextName, V2i( tileOriginX, tileOriginY ) ); Box2i tileBound( V2i( tileOriginX, tileOriginY ), V2i( tileOriginX + m_tileSize - 1, tileOriginY + m_tileSize - 1 ) ); Box2i b = boxIntersection( tileBound, operationWindow ); size_t tileStrideSize = sizeof(float) * ( b.size().x + 1 ); ConstFloatVectorDataPtr tileData = m_channelDataPlug->getValue(); const float *tileDataBegin = &(tileData->readable()[0]); for( int y = b.min.y; y<=b.max.y; y++ ) { const float *tilePtr = tileDataBegin + (y - tileOriginY) * m_tileSize + (b.min.x - tileOriginX); float *channelPtr = channelBegin + ( m_dataWindow.size().y - ( y - m_dataWindow.min.y ) ) * imageStride + (b.min.x - m_dataWindow.min.x); std::memcpy( channelPtr, tilePtr, tileStrideSize ); } } } } }
void ImageSampler::compute( Gaffer::ValuePlug *output, const Gaffer::Context *context ) const { if( output->parent<Plug>() == colorPlug() ) { float sample = 0; std::string channel = channelName( output ); if( channel.size() ) { V2f pixel = pixelPlug()->getValue(); Box2i sampleWindow; sampleWindow.extendBy( V2i( pixel ) - V2i( 1 ) ); sampleWindow.extendBy( V2i( pixel ) + V2i( 1 ) ); Sampler sampler( imagePlug(), channel, sampleWindow, Filter::create( filterPlug()->getValue() ) ); sample = sampler.sample( pixel.x, pixel.y ); } static_cast<FloatPlug *>( output )->setValue( sample ); return; } ComputeNode::compute( output, context ); }
static void medianCut( const Array2D &luminance, const Array2D &summedLuminance, MedianCutSampler::Projection projection, const Box2i &area, vector<Box2i> &areas, vector<V2f> ¢roids, int depth, int maxDepth ) { float radiansPerPixel = M_PI / (luminance.shape()[1]); if( depth==maxDepth ) { float totalEnergy = 0.0f; V2f position( 0.0f ); for( int y=area.min.y; y<=area.max.y; y++ ) { for( int x=area.min.x; x<=area.max.x; x++ ) { float e = luminance[x][y]; position += V2f( x, y ) * e; totalEnergy += e; } } position /= totalEnergy; centroids.push_back( position ); areas.push_back( area ); } else { // find cut dimension V2f size = area.size(); if( projection==MedianCutSampler::LatLong ) { float centreY = (area.max.y + area.min.y) / 2.0f; float centreAngle = (M_PI - radiansPerPixel) / 2.0f - centreY * radiansPerPixel; size.x *= cosf( centreAngle ); } int cutAxis = size.x > size.y ? 0 : 1; float e = energy( summedLuminance, area ); float halfE = e / 2.0f; Box2i lowArea = area; while( e > halfE ) { lowArea.max[cutAxis] -= 1; e = energy( summedLuminance, lowArea ); } Box2i highArea = area; highArea.min[cutAxis] = lowArea.max[cutAxis] + 1; medianCut( luminance, summedLuminance, projection, lowArea, areas, centroids, depth + 1, maxDepth ); medianCut( luminance, summedLuminance, projection, highArea, areas, centroids, depth + 1, maxDepth ); } }
void DPXImageWriter::writeImage( const vector<string> &names, const ImagePrimitive *image, const Box2i &dataWindow ) const { // write the dpx in the standard 10bit log format ofstream out; out.open(fileName().c_str()); if ( !out.is_open() ) { throw IOException( "DPXImageWriter: Error writing to " + fileName() ); } /// We'd like RGB to be at the front, in that order, because it seems that not all readers support the channel identifiers! vector<string> desiredChannelOrder; desiredChannelOrder.push_back( "R" ); desiredChannelOrder.push_back( "G" ); desiredChannelOrder.push_back( "B" ); vector<string> namesCopy = names; vector<string> filteredNames; for ( vector<string>::const_iterator it = desiredChannelOrder.begin(); it != desiredChannelOrder.end(); ++it ) { vector<string>::iterator res = find( namesCopy.begin(), namesCopy.end(), *it ); if ( res != namesCopy.end() ) { namesCopy.erase( res ); filteredNames.push_back( *it ); } } for ( vector<string>::const_iterator it = namesCopy.begin(); it != namesCopy.end(); ++it ) { filteredNames.push_back( *it ); } assert( names.size() == filteredNames.size() ); Box2i displayWindow = image->getDisplayWindow(); int displayWidth = 1 + displayWindow.size().x; int displayHeight = 1 + displayWindow.size().y; // build the header DPXFileInformation fi; memset(&fi, 0, sizeof(fi)); DPXImageInformation ii; memset(&ii, 0, sizeof(ii)); DPXImageOrientation ioi; memset(&ioi, 0, sizeof(ioi)); DPXMotionPictureFilm mpf; memset(&mpf, 0, sizeof(mpf)); DPXTelevisionHeader th; memset(&th, 0, sizeof(th)); fi.magic = asBigEndian<>( 0x53445058 ); // compute data offsets fi.gen_hdr_size = sizeof(fi) + sizeof(ii) + sizeof(ioi); fi.gen_hdr_size = asBigEndian<>(fi.gen_hdr_size); fi.ind_hdr_size = sizeof(mpf) + sizeof(th); fi.ind_hdr_size = asBigEndian<>(fi.ind_hdr_size); int header_size = sizeof(fi) + sizeof(ii) + sizeof(ioi) + sizeof(mpf) + sizeof(th); fi.image_data_offset = header_size; fi.image_data_offset = asBigEndian<>(fi.image_data_offset); strcpy((char *) fi.vers, "V2.0"); strncpy( (char *) fi.file_name, fileName().c_str(), sizeof( fi.file_name ) ); // compute the current date and time boost::posix_time::ptime utc = boost::posix_time::second_clock::universal_time(); boost::gregorian::date date = utc.date(); boost::posix_time::time_duration time = utc.time_of_day(); snprintf((char *) fi.create_time, sizeof( fi.create_time ), "%04d:%02d:%02d:%02d:%02d:%02d:%s", static_cast<int>( date.year() ), static_cast<int>( date.month() ), static_cast<int>( date.day() ), time.hours(), time.minutes(), time.seconds(), "UTC" ); snprintf((char *) fi.creator, sizeof( fi.creator ), "cortex"); snprintf((char *) fi.project, sizeof( fi.project ), "cortex"); snprintf((char *) fi.copyright, sizeof( fi.copyright ), "Unknown"); ii.orientation = 0; // left-to-right, top-to-bottom ii.element_number = 1; ii.pixels_per_line = displayWidth; ii.lines_per_image_ele = displayHeight; ii.element_number = asBigEndian<>(ii.element_number); ii.pixels_per_line = asBigEndian<>(ii.pixels_per_line); ii.lines_per_image_ele = asBigEndian<>(ii.lines_per_image_ele); for (int c = 0; c < 8; ++c) { DPXImageInformation::_image_element &ie = ii.image_element[c]; ie.data_sign = 0; /// \todo Dcoument these constants ie.ref_low_data = 0; ie.ref_low_quantity = 0.0; ie.ref_high_data = 1023; ie.ref_high_quantity = 2.046; ie.ref_low_data = asBigEndian<>(ie.ref_low_data); ie.ref_low_quantity = asBigEndian<>(ie.ref_low_quantity); ie.ref_high_data = asBigEndian<>(ie.ref_high_data); ie.ref_high_quantity = asBigEndian<>(ie.ref_high_quantity); /// \todo Dcoument these constants ie.transfer = 1; ie.packing = asBigEndian<short>( 1 ); ie.bit_size = 10; ie.descriptor = 50; ie.data_offset = fi.image_data_offset; } ioi.x_offset = 0; ioi.y_offset = 0; // Write the header int image_data_size = sizeof( unsigned int ) * displayWidth * displayHeight; fi.file_size = header_size + image_data_size; fi.file_size = asBigEndian<>(fi.file_size); out.write(reinterpret_cast<char *>(&fi), sizeof(fi)); if ( out.fail() ) { throw IOException( "DPXImageWriter: Error writing to " + fileName() ); } out.write(reinterpret_cast<char *>(&ii), sizeof(ii)); if ( out.fail() ) { throw IOException( "DPXImageWriter: Error writing to " + fileName() ); } out.write(reinterpret_cast<char *>(&ioi), sizeof(ioi)); if ( out.fail() ) { throw IOException( "DPXImageWriter: Error writing to " + fileName() ); } out.write(reinterpret_cast<char *>(&mpf), sizeof(mpf)); if ( out.fail() ) { throw IOException( "DPXImageWriter: Error writing to " + fileName() ); } out.write(reinterpret_cast<char *>(&th), sizeof(th)); if ( out.fail() ) { throw IOException( "DPXImageWriter: Error writing to " + fileName() ); } // write the data vector<unsigned int> imageBuffer( displayWidth*displayHeight, 0 ); int offset = 0; vector<string>::const_iterator i = filteredNames.begin(); while (i != filteredNames.end()) { if (!(*i == "R" || *i == "G" || *i == "B")) { msg( Msg::Warning, "DPXImageWriter::write", format( "Channel \"%s\" was not encoded." ) % *i ); ++i; continue; } int bpp = 10; unsigned int shift = (32 - bpp) - (offset*bpp); assert( image->variables.find( *i ) != image->variables.end() ); DataPtr dataContainer = image->variables.find( *i )->second.data; assert( dataContainer ); ChannelConverter converter( *i, image, dataWindow, shift, imageBuffer ); despatchTypedData< ChannelConverter, TypeTraits::IsNumericVectorTypedData, ChannelConverter::ErrorHandler >( dataContainer.get(), converter ); ++i; ++offset; } // write the buffer for (int i = 0; i < displayWidth * displayHeight; ++i) { imageBuffer[i] = asBigEndian<>(imageBuffer[i]); out.write( (const char *) (&imageBuffer[i]), sizeof(unsigned int) ); if ( out.fail() ) { throw IOException( "DPXImageWriter: Error writing to " + fileName() ); } } }
ObjectPtr EnvMapSampler::doOperation( const CompoundObject * operands ) { ImagePrimitivePtr image = static_cast<ImagePrimitive *>( imageParameter()->getValue() )->copy(); Box2i dataWindow = image->getDataWindow(); // find the rgb channels ConstFloatVectorDataPtr redData = image->getChannel<float>( "R" ); ConstFloatVectorDataPtr greenData = image->getChannel<float>( "G" ); ConstFloatVectorDataPtr blueData = image->getChannel<float>( "B" ); if( !(redData && greenData && blueData) ) { throw Exception( "Image does not contain valid RGB float channels." ); } const vector<float> &red = redData->readable(); const vector<float> &green = greenData->readable(); const vector<float> &blue = blueData->readable(); // get a luminance channel LuminanceOpPtr luminanceOp = new LuminanceOp(); luminanceOp->inputParameter()->setValue( image ); luminanceOp->copyParameter()->getTypedValue() = false; luminanceOp->removeColorPrimVarsParameter()->getTypedValue() = false; luminanceOp->operate(); // do the median cut thing to get some samples MedianCutSamplerPtr sampler = new MedianCutSampler; sampler->imageParameter()->setValue( image ); sampler->subdivisionDepthParameter()->setNumericValue( subdivisionDepthParameter()->getNumericValue() ); ConstCompoundObjectPtr samples = boost::static_pointer_cast<CompoundObject>( sampler->operate() ); const vector<V2f> ¢roids = boost::static_pointer_cast<V2fVectorData>( samples->members().find( "centroids" )->second )->readable(); const vector<Box2i> &areas = boost::static_pointer_cast<Box2iVectorData>( samples->members().find( "areas" )->second )->readable(); // get light directions and colors from the samples V3fVectorDataPtr directionsData = new V3fVectorData; Color3fVectorDataPtr colorsData = new Color3fVectorData; vector<V3f> &directions = directionsData->writable(); vector<Color3f> &colors = colorsData->writable(); float radiansPerPixel = M_PI / (dataWindow.size().y + 1); float angleAtTop = ( M_PI - radiansPerPixel ) / 2.0f; for( unsigned i=0; i<centroids.size(); i++ ) { const Box2i &area = areas[i]; Color3f color( 0 ); for( int y=area.min.y; y<=area.max.y; y++ ) { int yRel = y - dataWindow.min.y; float angle = angleAtTop - yRel * radiansPerPixel; float weight = cosf( angle ); int index = (area.min.x - dataWindow.min.x) + (dataWindow.size().x + 1 ) * yRel; for( int x=area.min.x; x<=area.max.x; x++ ) { color[0] += weight * red[index]; color[1] += weight * green[index]; color[2] += weight * blue[index]; index++; } } color /= red.size(); colors.push_back( color ); float phi = angleAtTop - (centroids[i].y - dataWindow.min.y) * radiansPerPixel; V3f direction; direction.y = sinf( phi ); float r = cosf( phi ); float theta = 2 * M_PI * lerpfactor( (float)centroids[i].x, (float)dataWindow.min.x, (float)dataWindow.max.x ); direction.x = r * cosf( theta ); direction.z = r * sinf( theta ); directions.push_back( -direction ); // negated so we output the direction the light shines in } // return the result CompoundObjectPtr result = new CompoundObject; result->members()["directions"] = directionsData; result->members()["colors"] = colorsData; return result; }
ObjectPtr MedianCutSampler::doOperation( const CompoundObject * operands ) { ImagePrimitivePtr image = static_cast<ImagePrimitive *>( imageParameter()->getValue() )->copy(); Box2i dataWindow = image->getDataWindow(); // find the right channel const std::string &channelName = m_channelNameParameter->getTypedValue(); FloatVectorDataPtr luminance = image->getChannel<float>( channelName ); if( !luminance ) { throw Exception( str( format( "No FloatVectorData channel named \"%s\"." ) % channelName ) ); } // if the projection requires it, weight the luminances so they're less // important towards the poles of the sphere Projection projection = (Projection)m_projectionParameter->getNumericValue(); if( projection==LatLong ) { float radiansPerPixel = M_PI / (dataWindow.size().y + 1); float angle = ( M_PI - radiansPerPixel ) / 2.0f; float *p = &(luminance->writable()[0]); for( int y=dataWindow.min.y; y<=dataWindow.max.y; y++ ) { float *pEnd = p + dataWindow.size().x + 1; float w = cosf( angle ); while( p < pEnd ) { *p *= w; p++; } angle -= radiansPerPixel; } } // make a summed area table for speed FloatVectorDataPtr summedLuminance = luminance; luminance = luminance->copy(); // we need this for the centroid computation SummedAreaOpPtr summedAreaOp = new SummedAreaOp(); summedAreaOp->inputParameter()->setValue( image ); summedAreaOp->copyParameter()->setTypedValue( false ); summedAreaOp->channelNamesParameter()->getTypedValue().clear(); summedAreaOp->channelNamesParameter()->getTypedValue().push_back( "Y" ); summedAreaOp->operate(); // do the median cut thing CompoundObjectPtr result = new CompoundObject; V2fVectorDataPtr centroids = new V2fVectorData; Box2iVectorDataPtr areas = new Box2iVectorData; result->members()["centroids"] = centroids; result->members()["areas"] = areas; dataWindow.max -= dataWindow.min; dataWindow.min -= dataWindow.min; // let's start indexing from 0 shall we? Array2D array( &(luminance->writable()[0]), extents[dataWindow.size().x+1][dataWindow.size().y+1], fortran_storage_order() ); Array2D summedArray( &(summedLuminance->writable()[0]), extents[dataWindow.size().x+1][dataWindow.size().y+1], fortran_storage_order() ); medianCut( array, summedArray, projection, dataWindow, areas->writable(), centroids->writable(), 0, subdivisionDepthParameter()->getNumericValue() ); return result; }
static bool triangleRender(const Triangle2i& triangle,SmartPointer<Texture> _rgb,SmartPointer<Texture> _alpha,Color4f color,const Box2i& box, bool bWrite) { XgeDebugAssert(_rgb->bpp==24 && _alpha->bpp==8 && _rgb->width==_alpha->width && _rgb->height==_alpha->height); int x1=triangle.p0.x,y1=triangle.p0.y; int x2=triangle.p1.x,y2=triangle.p1.y; int x3=triangle.p2.x,y3=triangle.p2.y; unsigned char* rgb =_rgb ->buffer; unsigned char* alpha =_alpha->buffer; int W =_rgb ->width; int minx = min3(x1, x2, x3) , maxx = max3(x1, x2, x3); int miny = min3(y1, y2, y3) , maxy = max3(y1, y2, y3); //must be completely be contained in the current area if (!(minx>=box.left() && maxx<=box.right() && miny>=box.bottom() && maxy<=box.top())) { XgeDebugAssert(!bWrite); return false; } int Sign=(((x2-x1)*(y3-y1)-(x3-x1)*(y2-y1))>=0)?(+1):(-1); for(int y = miny; y <= maxy; y++) for(int x = minx; x <= maxx; x++) { // if the pixel is inside.... if ( (Sign*((x2 - x1) * (y - y1) - (y2 - y1) * (x - x1)) >= 0) && (Sign*((x3 - x2) * (y - y2) - (y3 - y2) * (x - x2)) >= 0) && (Sign*((x1 - x3) * (y - y3) - (y1 - y3) * (x - x3)) >= 0) ) { int idx=y*W+x; //test if it has already been written //note: for each pixel consider also the pixel (-1,0) (+1,0) (0,-1) (0,+1) if (!bWrite && (alpha[idx] || alpha[idx-1] || alpha[idx+1] ||alpha[idx-W] || alpha[idx+W])) return false; //if write.... if (bWrite) { unsigned char R=(unsigned char)(255.0f*color.r); unsigned char G=(unsigned char)(255.0f*color.g); unsigned char B=(unsigned char)(255.0f*color.b); rgb[idx*3+0] = R; rgb[idx*3+1] = G; rgb[idx*3+2] = B; rgb[(idx-1)*3+0] = rgb[(idx+1)*3+0] = rgb[(idx-W)*3+0] = rgb[(idx+W)*3+0] =R ; rgb[(idx-1)*3+1] = rgb[(idx+1)*3+1] = rgb[(idx-W)*3+1] = rgb[(idx+W)*3+1] =G ; rgb[(idx-1)*3+2] = rgb[(idx+1)*3+2] = rgb[(idx-W)*3+2] =rgb[(idx+W)*3+2] =B ; //sign as drawn alpha [idx ] = 0x01; alpha [idx-1] = 0x01; alpha [idx+1] = 0x01; alpha [idx-W] = 0x01; alpha [idx+W] = 0x01; } } } return true; }
DataPtr SGIImageReader::readTypedChannel( const std::string &name, const Box2i &dataWindow ) { typedef TypedData< std::vector< V > > TargetVector; assert( open() ); assert( m_header ); typename TypedData< std::vector<T > >::ConstPtr dataBuffer = assertedStaticCast< TypedData< std::vector<T > > >( m_buffer ); assert( dataBuffer ); const typename TypedData< std::vector<T > >::ValueType &buffer = dataBuffer->readable(); assert( m_header->m_channelOffsets.find( name ) != m_header->m_channelOffsets.end() ); int channelOffset = m_header->m_channelOffsets[name]; 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(); int wholeDataHeight = 1 + wholeDataWindow.size().y; int wholeDataWidth = 1 + wholeDataWindow.size().x; 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; ScaledDataConversion<T, V> converter; if ( m_header->m_fileHeader.m_storageFormat >= 1 ) /// RLE { int dataY = 0; for ( int y = yMin ; y <= yMax ; ++y, ++dataY ) { assert( yMin >= 0 ); assert( yMin < wholeDataHeight ); /// Images are unencoded "upside down" (flipped in Y), for our purposes uint32_t rowOffset = m_header->m_offsetTable[ channelOffset * wholeDataHeight + ( wholeDataHeight - 1 - y ) ]; if ( rowOffset >= buffer.size() ) { throw IOException( "SGIImageReader: Invalid RLE row offset found while reading " + fileName() ); } std::vector<T> scanline( wholeDataWidth ); uint32_t scanlineOffset = 0; bool done = false; while ( !done ) { T pixel = buffer[ rowOffset ++ ]; T count = pixel & 0x7f; if ( count == 0 ) { done = true; } else { if ( pixel & 0x80 ) { if ( scanlineOffset + count - 1 >= scanline.size() || rowOffset + count - 1 >= buffer.size() ) { throw IOException( "SGIImageReader: Invalid RLE data found while reading " + fileName() ); } while ( count -- ) { assert( scanlineOffset < scanline.size() ); assert( rowOffset < buffer.size() ); scanline[ scanlineOffset++ ] = buffer[ rowOffset ++ ]; } } else { if ( scanlineOffset + count - 1 >= scanline.size() || rowOffset - 1 >= buffer.size() ) { throw IOException( "SGIImageReader: Invalid RLE data found while reading " + fileName() ); } assert( rowOffset < buffer.size() ); pixel = buffer[ rowOffset ++ ]; while ( count -- ) { assert( scanlineOffset < scanline.size() ); scanline[ scanlineOffset++ ] = pixel; } } } } if ( scanlineOffset != scanline.size() ) { throw IOException( "SGIImageReader: Error occurred during RLE decode while reading " + fileName() ); } int dataOffset = dataY * dataWidth; for ( int x = xMin; x <= xMax ; ++x, ++dataOffset ) { data[dataOffset] = converter( scanline[x] ); } } } else /// Not RLE { int dataY = 0; 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() ); /// Images are unencoded "upside down" (flipped in Y), for our purposes data[dataOffset] = converter( buffer[ ( channelOffset * wholeDataHeight * wholeDataWidth ) + ( wholeDataHeight - 1 - y ) * wholeDataWidth + x ] ); } } } return dataContainer; }
void TGAImageWriter::writeImage( const vector<string> &names, const ImagePrimitive * image, const Box2i &dataWindow ) const { vector<string>::const_iterator rIt = std::find( names.begin(), names.end(), "R" ); vector<string>::const_iterator gIt = std::find( names.begin(), names.end(), "G" ); vector<string>::const_iterator bIt = std::find( names.begin(), names.end(), "B" ); vector<string>::const_iterator aIt = std::find( names.begin(), names.end(), "A" ); int numChannels = 0; if ( rIt != names.end() && gIt != names.end() && bIt != names.end() ) { numChannels = 3; } if ( aIt != names.end() ) { numChannels ++; } if ( numChannels != 3 && numChannels != 4 ) { throw IOException( "TGAImageWriter: Unsupported channel names specified." ); } std::ofstream out; out.open( fileName().c_str() ); if ( !out.is_open() ) { throw IOException( "TGAImageWriter: Could not open " + fileName() ); } vector<string> desiredChannelOrder; desiredChannelOrder.push_back( "B" ); desiredChannelOrder.push_back( "G" ); desiredChannelOrder.push_back( "R" ); desiredChannelOrder.push_back( "A" ); vector<string> namesCopy = names; vector<string> filteredNames; for ( vector<string>::const_iterator it = desiredChannelOrder.begin(); it != desiredChannelOrder.end(); ++it ) { vector<string>::iterator res = find( namesCopy.begin(), namesCopy.end(), *it ); if ( res != namesCopy.end() ) { namesCopy.erase( res ); filteredNames.push_back( *it ); } } for ( vector<string>::const_iterator it = namesCopy.begin(); it != namesCopy.end(); ++it ) { filteredNames.push_back( *it ); } assert( names.size() == filteredNames.size() ); Box2i displayWindow = image->getDisplayWindow(); int displayWidth = 1 + displayWindow.size().x; int displayHeight = 1 + displayWindow.size().y; // Write the header /// ID Length writeLittleEndian<uint8_t>( out, 0 ); /// Color Map Type writeLittleEndian<uint8_t>( out, 0 ); /// Image Type writeLittleEndian<uint8_t>( out, 2 ); /// Color Map Specification writeLittleEndian<uint16_t>( out, 0 ); writeLittleEndian<uint16_t>( out, 0 ); writeLittleEndian<uint8_t>( out, 0 ); /// Image Specification writeLittleEndian<uint16_t>( out, 0 ); writeLittleEndian<uint16_t>( out, 0 ); writeLittleEndian<uint16_t>( out, displayWidth ); writeLittleEndian<uint16_t>( out, displayHeight ); writeLittleEndian<uint8_t>( out, numChannels * 8 ); writeLittleEndian<uint8_t>( out, ( numChannels == 4 ? 8 : 0 ) + 32 ); // Encode the image buffer std::vector<uint8_t> imageBuffer( displayWidth*displayHeight*numChannels, 0 ); int offset = 0; for ( vector<string>::const_iterator it = filteredNames.begin(); it != filteredNames.end(); ++it ) { const std::string &name = *it; if ( !( name == "R" || name == "G" || name == "B" || name == "A" ) ) { msg( Msg::Warning, "TGAImageWriter::write", format( "Channel \"%s\" was not encoded." ) % name ); } else { assert( image->variables.find( name ) != image->variables.end() ); DataPtr dataContainer = image->variables.find( name )->second.data; assert( dataContainer ); ChannelConverter converter( name, image, dataWindow, numChannels, offset, imageBuffer ); despatchTypedData< ChannelConverter, TypeTraits::IsNumericVectorTypedData, ChannelConverter::ErrorHandler >( dataContainer, converter ); ++offset; } } /// Write the Buffer for ( int i = 0; i < displayWidth*displayHeight*numChannels; ++i ) { assert( i < ( int )imageBuffer.size() ); writeLittleEndian( out, imageBuffer[i] ); if ( out.fail() ) { throw IOException( "TGAImageWriter: Error writing to " + fileName() ); } } }
void makeMultiView (const vector <string> &viewNames, const vector <const char *> &inFileNames, const char *outFileName, Compression compression, bool verbose) { Header header; Image image; FrameBuffer outFb; // // Find the size of the dataWindow, check files // Box2i d; for (int i = 0; i < viewNames.size(); ++i) { InputFile in (inFileNames[i]); if (verbose) { cout << "reading file " << inFileNames[i] << " " "for " << viewNames[i] << " view" << endl; } if (hasMultiView (in.header())) { THROW (IEX_NAMESPACE::NoImplExc, "The image in file " << inFileNames[i] << " is already a " "multi-view image. Cannot combine multiple multi-view " "images."); } header = in.header(); if (i == 0) { d=header.dataWindow(); }else{ d.extendBy(header.dataWindow()); } } image.resize (d); header.dataWindow()=d; // blow away channels; we'll rebuild them header.channels()=ChannelList(); // // Read the input image files // for (int i = 0; i < viewNames.size(); ++i) { InputFile in (inFileNames[i]); if (verbose) { cout << "reading file " << inFileNames[i] << " " "for " << viewNames[i] << " view" << endl; } FrameBuffer inFb; for (ChannelList::ConstIterator j = in.header().channels().begin(); j != in.header().channels().end(); ++j) { const Channel &inChannel = j.channel(); string inChanName = j.name(); string outChanName = insertViewName (inChanName, viewNames, i); image.addChannel (outChanName, inChannel); image.channel(outChanName).black(); header.channels().insert (outChanName, inChannel); inFb.insert (inChanName, image.channel(outChanName).slice()); outFb.insert (outChanName, image.channel(outChanName).slice()); } in.setFrameBuffer (inFb); in.readPixels (in.header().dataWindow().min.y, in.header().dataWindow().max.y); } // // Write the output image file // { header.compression() = compression; addMultiView (header, viewNames); OutputFile out (outFileName, header); if (verbose) cout << "writing file " << outFileName << endl; out.setFrameBuffer (outFb); out.writePixels (header.dataWindow().max.y - header.dataWindow().min.y + 1); } }