bool ImageIO::writeImage(QVector<float> &pixels, const QString &filePath, const LayerDesc &desc, int width, int height) { try { Imf::Header header (width, height); Imf::FrameBuffer frameBuffer; for (int chan = 0; chan < desc.numChannels(); chan++) { QString chan_name = QString("%1.%2").arg(desc._layer_name).arg(desc._channels[chan]); header.channels().insert(qPrintable(chan_name), Imf::Channel(Imf::FLOAT)); frameBuffer.insert(qPrintable(chan_name), Imf::Slice(Imf::FLOAT, (char *) pixels.data() + chan*sizeof(float), sizeof(float)*desc.numChannels(), sizeof(float)*width*desc.numChannels())); } Imf::OutputFile file(qPrintable(remapFilePath(filePath)), header); file.setFrameBuffer(frameBuffer); file.writePixels(height); } catch (const std::exception &e) { qWarning() << e.what(); return false; } return true; }
void write_half_exr( const boost::filesystem::path& p, Imf::Header& header, const image::const_image_view_t& view, bool write_alpha) { boost::gil::rgba16f_image_t img( view.width(), view.height()); boost::gil::copy_and_convert_pixels( view, boost::gil::view( img)); header.channels().insert( "R", Imf::HALF); header.channels().insert( "G", Imf::HALF); header.channels().insert( "B", Imf::HALF); if( write_alpha) header.channels().insert( "A", Imf::HALF); Imf::FrameBuffer frameBuffer; char *ptr = (char *) boost::gil::interleaved_view_get_raw_data( boost::gil::view( img)); std::size_t xstride = 4 * sizeof(half); std::size_t ystride = xstride * img.width(); frameBuffer.insert( "R", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "G", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "B", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); if( write_alpha) frameBuffer.insert( "A", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); Imf::OutputFile out_file( p.external_file_string().c_str(), header); out_file.setFrameBuffer( frameBuffer); out_file.writePixels( img.height()); }
fcExrTaskData(const char *p, int w, int h, fcExrCompression compression) : path(p), width(w), height(h), header(w, h) { switch (compression) { case fcExrCompression::None: header.compression() = Imf::NO_COMPRESSION; break; case fcExrCompression::RLE: header.compression() = Imf::RLE_COMPRESSION; break; case fcExrCompression::ZipS: header.compression() = Imf::ZIPS_COMPRESSION; break; case fcExrCompression::Zip: header.compression() = Imf::ZIP_COMPRESSION; break; case fcExrCompression::PIZ: header.compression() = Imf::PIZ_COMPRESSION; break; } }
static void headerToCompoundData( const Imf::Header &header, CompoundData *blindData ) { for ( Imf::Header::ConstIterator attrIt = header.begin(); attrIt != header.end(); attrIt++ ) { std::string name = attrIt.name(); DataPtr data = attributeToData( attrIt.attribute() ); if ( data ) { addHeaderAttribute( name, data, blindData ); } } }
void SaveExr(const Image<unsigned char>& image_in, const pangolin::PixelFormat& fmt, const std::string& filename, bool top_line_first) { PANGOLIN_UNUSED(image_in); PANGOLIN_UNUSED(fmt); PANGOLIN_UNUSED(filename); PANGOLIN_UNUSED(top_line_first); #ifdef HAVE_OPENEXR ManagedImage<unsigned char> flip_image; Image<unsigned char> image; if(top_line_first) { image = image_in; }else{ flip_image.Reinitialise(image_in.pitch,image_in.h); for(size_t y=0; y<image_in.h; ++y) { std::memcpy(flip_image.RowPtr(y), image_in.RowPtr(y), image_in.pitch); } image = flip_image; } Imf::Header header (image.w, image.h); SetOpenEXRChannels(header.channels(), fmt); Imf::OutputFile file (filename.c_str(), header); Imf::FrameBuffer frameBuffer; int ch=0; size_t ch_bits = 0; for(Imf::ChannelList::Iterator it = header.channels().begin(); it != header.channels().end(); ++it) { frameBuffer.insert( it.name(), Imf::Slice( it.channel().type, (char*)image.ptr + ch_bits/8, fmt.channel_bits[ch]/8, image.pitch ) ); ch_bits += fmt.channel_bits[ch++]; } file.setFrameBuffer(frameBuffer); file.writePixels(image.h); #else throw std::runtime_error("EXR Support not enabled. Please rebuild Pangolin."); #endif // HAVE_OPENEXR }
void SaveExr(const Image<unsigned char>& image_in, const pangolin::VideoPixelFormat& fmt, const std::string& filename, bool top_line_first) { #ifdef HAVE_OPENEXR Image<unsigned char> image; if(top_line_first) { image = image_in; }else{ image.Alloc(image_in.w,image_in.h,image_in.pitch); for(size_t y=0; y<image_in.h; ++y) { std::memcpy(image.ptr + y*image.pitch, image_in.ptr + (image_in.h-y-1)*image_in.pitch, image.pitch); } } Imf::Header header (image.w, image.h); SetOpenEXRChannels(header.channels(), fmt); Imf::OutputFile file (filename.c_str(), header); Imf::FrameBuffer frameBuffer; int ch=0; size_t ch_bits = 0; for(Imf::ChannelList::Iterator it = header.channels().begin(); it != header.channels().end(); ++it) { frameBuffer.insert( it.name(), Imf::Slice( it.channel().type, (char*)image.ptr + ch_bits/8, fmt.channel_bits[ch]/8, image.pitch ) ); ch_bits += fmt.channel_bits[ch++]; } file.setFrameBuffer(frameBuffer); file.writePixels(image.h); if(!top_line_first) { image.Dealloc(); } #else throw std::runtime_error("EXR Support not enabled. Please rebuild Pangolin."); #endif // HAVE_OPENEXR }
/** \brief Convert an OpenEXR header to our own header representation. * * \param exrHeader - input header * \param header - output header */ void convertHeader(const Imf::Header& exrHeader, CqTexFileHeader& header) { // Set width, height const Imath::Box2i& dataBox = exrHeader.dataWindow(); header.setWidth(dataBox.max.x - dataBox.min.x+1); header.setHeight(dataBox.max.y - dataBox.min.y+1); // display window const Imath::Box2i& displayBox = exrHeader.displayWindow(); header.set<Attr::DisplayWindow>( SqImageRegion( displayBox.max.x - displayBox.min.x, displayBox.max.y - displayBox.min.y, displayBox.min.x - dataBox.min.x, displayBox.min.y - dataBox.min.y) ); // Set tiling information ? // Aspect ratio header.set<Attr::PixelAspectRatio>(exrHeader.pixelAspectRatio()); TqChannelNameMap channelNameMap; // Convert channel representation const Imf::ChannelList& exrChannels = exrHeader.channels(); CqChannelList& channels = header.channelList(); for(Imf::ChannelList::ConstIterator i = exrChannels.begin(); i != exrChannels.end(); ++i) { // use lower case names for channels; OpenEXR uses upper case. std::string chanName = i.name(); std::transform(chanName.begin(), chanName.end(), chanName.begin(), ::tolower); channelNameMap[chanName] = i.name(); channels.addChannel( SqChannelInfo(chanName, channelTypeFromExr(i.channel().type)) ); } header.set<Attr::ExrChannelNameMap>(channelNameMap); channels.reorderChannels(); // Set compresssion type header.set<Attr::Compression>(exrCompressionToString(exrHeader.compression())); }
void write_half_rgb_exr( Imf::OStream& os, Imf::Header& header, const image::const_image_view_t& view) { boost::gil::rgba16f_image_t img( view.width(), view.height()); boost::gil::copy_and_convert_pixels( view, boost::gil::view( img)); header.channels().insert( "R", Imf::HALF); header.channels().insert( "G", Imf::HALF); header.channels().insert( "B", Imf::HALF); Imf::FrameBuffer frameBuffer; char *ptr = (char *) boost::gil::interleaved_view_get_raw_data( boost::gil::view( img)); std::size_t xstride = 4 * sizeof(half); std::size_t ystride = xstride * img.width(); frameBuffer.insert( "R", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "G", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "B", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); Imf::OutputFile out_file( os, header); out_file.setFrameBuffer( frameBuffer); out_file.writePixels( img.height()); }
/** Set the preview image using the dib embedded thumbnail */ static BOOL SetPreviewImage(FIBITMAP *dib, Imf::Header& header) { if(!FreeImage_GetThumbnail(dib)) { return FALSE; } FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib); if((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 32)) { // invalid thumbnail - ignore it FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL); } else { const unsigned thWidth = FreeImage_GetWidth(thumbnail); const unsigned thHeight = FreeImage_GetHeight(thumbnail); Imf::PreviewImage preview(thWidth, thHeight); // copy thumbnail to 32-bit RGBA preview image const BYTE* src_line = FreeImage_GetScanLine(thumbnail, thHeight - 1); Imf::PreviewRgba* dst_line = preview.pixels(); const unsigned srcPitch = FreeImage_GetPitch(thumbnail); for (unsigned y = 0; y < thHeight; y++) { const RGBQUAD* src_pixel = (RGBQUAD*)src_line; Imf::PreviewRgba* dst_pixel = dst_line; for(unsigned x = 0; x < thWidth; x++) { dst_pixel->r = src_pixel->rgbRed; dst_pixel->g = src_pixel->rgbGreen; dst_pixel->b = src_pixel->rgbBlue; dst_pixel->a = src_pixel->rgbReserved; src_pixel++; dst_pixel++; } src_line -= srcPitch; dst_line += thWidth; } header.setPreviewImage(preview); } return TRUE; }
void applyCtlExrToExr (Imf::Header inHeader, Imf::Header &outHeader, const Imf::Array2D<Imf::Rgba> &inPixels, Imf::Array2D<Imf::Rgba> &outPixels, int w, int h, const std::vector<std::string> &transformNames, const AttrMap &extraAttrs) { // // Make sure that the input and output headers contain // chromaticities and adoptedNeutral attributes. // if (!hasChromaticities (inHeader)) addChromaticities (inHeader, Chromaticities()); if (!hasAdoptedNeutral (inHeader)) addAdoptedNeutral (inHeader, chromaticities(inHeader).white); if (!hasChromaticities (outHeader)) addChromaticities (outHeader, chromaticities (inHeader)); if (!hasAdoptedNeutral (outHeader)) addAdoptedNeutral (outHeader, adoptedNeutral (inHeader)); // // Add extraAttrs to the input header, possibly overriding // the values of existing input header attributes. // for (AttrMap::const_iterator i = extraAttrs.begin(); i != extraAttrs.end(); ++i) { inHeader.insert (i->first.c_str(), *i->second); } // // Initialize an "environment" header with the same data that // would be available to rendering transforms in an image viewing // program. This allows transforms to bake color rendering into // the output file's pixels. // Header envHeader; initializeEnvHeader (envHeader); // // Set up input and output FrameBuffer objects for the transforms. // FrameBuffer inFb; initializeFrameBuffer (w, h, inPixels, inFb); FrameBuffer outFb; initializeFrameBuffer (w, h, outPixels, outFb); // // Run the CTL transforms // Box2i transformWindow (V2i (0, 0), V2i (w - 1, h - 1)); SimdInterpreter interpreter; #ifdef CTL_MODULE_BASE_PATH // // The configuration script has defined a default // location for CTL modules. Include this location // in the CTL module search path. // vector<string> paths = interpreter.modulePaths(); paths.push_back (CTL_MODULE_BASE_PATH); interpreter.setModulePaths (paths); #endif ImfCtl::applyTransforms (interpreter, transformNames, transformWindow, envHeader, inHeader, inFb, outHeader, outFb); }
bool OpenEXRInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { if (subimage < 0 || subimage >= m_nsubimages) // out of range return false; if (miplevel < 0 || miplevel >= m_nmiplevels) // out of range return false; m_subimage = subimage; m_miplevel = miplevel; if (miplevel == 0 && m_levelmode == Imf::ONE_LEVEL) { newspec = m_spec; return true; } // Compute the resolution of the requested mip level. int w = m_topwidth, h = m_topheight; if (m_levelmode == Imf::MIPMAP_LEVELS) { while (miplevel--) { if (m_roundingmode == Imf::ROUND_DOWN) { w = w / 2; h = h / 2; } else { w = (w + 1) / 2; h = (h + 1) / 2; } w = std::max (1, w); h = std::max (1, h); } } else if (m_levelmode == Imf::RIPMAP_LEVELS) { // FIXME } else { ASSERT(0); } m_spec.width = w; m_spec.height = h; // N.B. OpenEXR doesn't support data and display windows per MIPmap // level. So always take from the top level. Imath::Box2i datawindow = m_header->dataWindow(); Imath::Box2i displaywindow = m_header->displayWindow(); m_spec.x = datawindow.min.x; m_spec.y = datawindow.min.y; if (miplevel == 0) { m_spec.full_x = displaywindow.min.x; m_spec.full_y = displaywindow.min.y; m_spec.full_width = displaywindow.max.x - displaywindow.min.x + 1; m_spec.full_height = displaywindow.max.y - displaywindow.min.y + 1; } else { m_spec.full_x = m_spec.x; m_spec.full_y = m_spec.y; m_spec.full_width = m_spec.width; m_spec.full_height = m_spec.height; } if (m_cubeface) { m_spec.full_width = w; m_spec.full_height = w; } newspec = m_spec; return true; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { const char *channel_name[4] = { "R", "G", "B", "A" }; BOOL bIsFlipped = FALSE; half *halfData = NULL; if(!dib || !handle) return FALSE; try { // check for EXR_LC compression and verify that the format is RGB if((flags & EXR_LC) == EXR_LC) { FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(((image_type != FIT_RGBF) && (image_type != FIT_RGBAF)) || ((flags & EXR_FLOAT) == EXR_FLOAT)) { THROW (Iex::IoExc, "EXR_LC compression is only available with RGB[A]F images"); } if((FreeImage_GetWidth(dib) % 2) || (FreeImage_GetHeight(dib) % 2)) { THROW (Iex::IoExc, "EXR_LC compression only works when the width and height are a multiple of 2"); } } // wrap the FreeImage IO stream C_OStream ostream(io, handle); // compression Imf::Compression compress; if((flags & EXR_NONE) == EXR_NONE) { // no compression compress = Imf::NO_COMPRESSION; } else if((flags & EXR_ZIP) == EXR_ZIP) { // zlib compression, in blocks of 16 scan lines compress = Imf::ZIP_COMPRESSION; } else if((flags & EXR_PIZ) == EXR_PIZ) { // piz-based wavelet compression compress = Imf::PIZ_COMPRESSION; } else if((flags & EXR_PXR24) == EXR_PXR24) { // lossy 24-bit float compression compress = Imf::PXR24_COMPRESSION; } else if((flags & EXR_B44) == EXR_B44) { // lossy 44% float compression compress = Imf::B44_COMPRESSION; } else { // default value compress = Imf::PIZ_COMPRESSION; } // create the header int width = FreeImage_GetWidth(dib); int height = FreeImage_GetHeight(dib); int dx = 0, dy = 0; Imath::Box2i dataWindow (Imath::V2i (0, 0), Imath::V2i (width - 1, height - 1)); Imath::Box2i displayWindow (Imath::V2i (-dx, -dy), Imath::V2i (width - dx - 1, height - dy - 1)); Imf::Header header = Imf::Header(displayWindow, dataWindow, 1, Imath::V2f(0,0), 1, Imf::INCREASING_Y, compress); // handle thumbnail SetPreviewImage(dib, header); // check for EXR_LC compression if((flags & EXR_LC) == EXR_LC) { return SaveAsEXR_LC(ostream, dib, header, width, height); } // output pixel type Imf::PixelType pixelType; if((flags & EXR_FLOAT) == EXR_FLOAT) { pixelType = Imf::FLOAT; // save as float data type } else { // default value pixelType = Imf::HALF; // save as half data type } // check the data type and number of channels int components = 0; FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); switch(image_type) { case FIT_FLOAT: components = 1; // insert luminance channel header.channels().insert ("Y", Imf::Channel(pixelType)); break; case FIT_RGBF: components = 3; for(int c = 0; c < components; c++) { // insert R, G and B channels header.channels().insert (channel_name[c], Imf::Channel(pixelType)); } break; case FIT_RGBAF: components = 4; for(int c = 0; c < components; c++) { // insert R, G, B and A channels header.channels().insert (channel_name[c], Imf::Channel(pixelType)); } break; default: THROW (Iex::ArgExc, "Cannot save: invalid data type.\nConvert the image to float before saving as OpenEXR."); } // build a frame buffer (i.e. what we have on input) Imf::FrameBuffer frameBuffer; BYTE *bits = NULL; // pointer to our pixel buffer size_t bytespp = 0; // size of our pixel in bytes size_t bytespc = 0; // size of our pixel component in bytes unsigned pitch = 0; // size of our yStride in bytes if(pixelType == Imf::HALF) { // convert from float to half halfData = new(std::nothrow) half[width * height * components]; if(!halfData) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); for(int y = 0; y < height; y++) { float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); half *dst_bits = halfData + y * width * components; for(int x = 0; x < width; x++) { for(int c = 0; c < components; c++) { dst_bits[c] = src_bits[c]; } src_bits += components; dst_bits += components; } } bits = (BYTE*)halfData; bytespc = sizeof(half); bytespp = sizeof(half) * components; pitch = sizeof(half) * width * components; } else if(pixelType == Imf::FLOAT) { // invert dib scanlines bIsFlipped = FreeImage_FlipVertical(dib); bits = FreeImage_GetBits(dib); bytespc = sizeof(float); bytespp = sizeof(float) * components; pitch = FreeImage_GetPitch(dib); } if(image_type == FIT_FLOAT) { frameBuffer.insert ("Y", // name Imf::Slice (pixelType, // type (char*)(bits), // base bytespp, // xStride pitch)); // yStride } else if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) { for(int c = 0; c < components; c++) { char *channel_base = (char*)(bits) + c*bytespc; frameBuffer.insert (channel_name[c],// name Imf::Slice (pixelType, // type channel_base, // base bytespp, // xStride pitch)); // yStride } } // write the data Imf::OutputFile file (ostream, header); file.setFrameBuffer (frameBuffer); file.writePixels (height); if(halfData != NULL) delete[] halfData; if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } return TRUE; } catch(Iex::BaseExc & e) { if(halfData != NULL) delete[] halfData; if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } FreeImage_OutputMessageProc(s_format_id, e.what()); return FALSE; } }
// 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; }
fcExrFrameData(const char *p, int w, int h) : path(p), width(w), height(h), header(w, h) { header.compression() = Imf::ZIPS_COMPRESSION; }
bool render_flipbook( flipbook::flipbook_t *flip, node_t *n, int start, int end, bool mblur, int subsample) { render_cancelled = false; signal_connection_t connection = flip->closed.connect( &cancel_render); std::size_t mem_size = 0; render_context_t new_context = document_t::Instance().composition().current_context(); new_context.mode = flipbook_render; new_context.result_node = n; new_context.subsample = subsample; new_context.motion_blur_extra_samples = 0; new_context.motion_blur_shutter_factor = mblur ? 1 : 0; // recalc the domain, in case this is our first render n->recursive_calc_domain( new_context); image::image_t img( n->domain().size().x + 1, n->domain().size().y + 1); for( int i = start; i <= end; ++i) { try { new_context.time = i; renderer_t renderer( new_context, true); renderer.render(); // find the amount of memory required to store a frame. // not the most efficient way of doing it, but it's done only once. if( mem_size == 0) { // copy the renderer result to our buffer boost::gil::fill_pixels( boost::gil::view( img), image::pixel_t( 0, 0, 0, 0)); Imath::Box2i area( intersect( n->domain(), n->defined())); if( !area.isEmpty()) { boost::gil::copy_pixels( n->const_subimage_view( area), boost::gil::subimage_view( boost::gil::view( img), area.min.x - n->domain().min.x, area.min.y - n->domain().min.y, area.size().x + 1, area.size().y + 1)); } Imf::Header header = Imf::Header( renderer.domain().size().x + 1, renderer.domain().size().y + 1); header.compression() = Imf::B44_COMPRESSION; imageio::imf_null_ostream os; imageio::write_half_rgb_exr( os, header, boost::gil::const_view( img)); mem_size = os.size(); std::cout << "Flipbook: frame size = " << mem_size / 1024 << " kb \n"; } std::auto_ptr<imageio::imf_memory_ostream> os( new imageio::imf_memory_ostream( mem_size)); if( !os.get()) { // out of memory connection.disconnect(); return true; } // copy the renderer result to our buffer boost::gil::fill_pixels( boost::gil::view( img), image::pixel_t( 0, 0, 0, 0)); Imath::Box2i area( intersect( n->domain(), n->defined())); if( !area.isEmpty()) { boost::gil::copy_pixels( n->const_subimage_view( area), boost::gil::subimage_view( boost::gil::view( img), area.min.x - n->domain().min.x, area.min.y - n->domain().min.y, area.size().x + 1, area.size().y + 1)); } Imf::Header header = Imf::Header( renderer.domain().size().x + 1, renderer.domain().size().y + 1); header.compression() = Imf::B44_COMPRESSION; imageio::write_half_rgb_exr( *os, header, boost::gil::const_view( img)); if( render_cancelled) { connection.disconnect(); return false; } flip->add_frame( os); } catch( ...) { // TODO: report out of memory here // if we have any frame, then play connection.disconnect(); return !flip->empty(); } } connection.disconnect(); return true; }
void OpenEXRInput::query_channels (void) { m_spec.nchannels = 0; const Imf::ChannelList &channels (m_header->channels()); Imf::ChannelList::ConstIterator ci; int c; int red = -1, green = -1, blue = -1, alpha = -1, zee = -1; for (c = 0, ci = channels.begin(); ci != channels.end(); ++c, ++ci) { // std::cerr << "Channel " << ci.name() << '\n'; const char* name = ci.name(); m_channelnames.push_back (name); if (red < 0 && (iequals(name, "R") || iequals(name, "Red") || iends_with(name,".R") || iends_with(name,".Red"))) red = c; if (green < 0 && (iequals(name, "G") || iequals(name, "Green") || iends_with(name,".G") || iends_with(name,".Green"))) green = c; if (blue < 0 && (iequals(name, "B") || iequals(name, "Blue") || iends_with(name,".B") || iends_with(name,".Blue"))) blue = c; if (alpha < 0 && (iequals(name, "A") || iequals(name, "Alpha") || iends_with(name,".A") || iends_with(name,".Alpha"))) alpha = c; if (zee < 0 && (iequals(name, "Z") || iequals(name, "Depth") || iends_with(name,".Z") || iends_with(name,".Depth"))) zee = c; ++m_spec.nchannels; } m_userchannels.resize (m_spec.nchannels); int nc = 0; if (red >= 0) { m_spec.channelnames.push_back (m_channelnames[red]); m_userchannels[red] = nc++; } if (green >= 0) { m_spec.channelnames.push_back (m_channelnames[green]); m_userchannels[green] = nc++; } if (blue >= 0) { m_spec.channelnames.push_back (m_channelnames[blue]); m_userchannels[blue] = nc++; } if (alpha >= 0) { m_spec.channelnames.push_back (m_channelnames[alpha]); m_spec.alpha_channel = nc; m_userchannels[alpha] = nc++; } if (zee >= 0) { m_spec.channelnames.push_back (m_channelnames[zee]); m_spec.z_channel = nc; m_userchannels[zee] = nc++; } for (c = 0, ci = channels.begin(); ci != channels.end(); ++c, ++ci) { if (red == c || green == c || blue == c || alpha == c || zee == c) continue; // Already accounted for this channel m_userchannels[c] = nc; m_spec.channelnames.push_back (ci.name()); ++nc; } ASSERT ((int)m_spec.channelnames.size() == m_spec.nchannels); // FIXME: should we also figure out the layers? // Figure out data types -- choose the highest range m_spec.format = TypeDesc::UNKNOWN; std::vector<TypeDesc> chanformat; bool differing_chanformats = false; for (c = 0, ci = channels.begin(); ci != channels.end(); ++c, ++ci) { Imf::PixelType ptype = ci.channel().type; TypeDesc fmt = TypeDesc::HALF; switch (ptype) { case Imf::UINT : fmt = TypeDesc::UINT; if (m_spec.format == TypeDesc::UNKNOWN) m_spec.format = TypeDesc::UINT; break; case Imf::HALF : fmt = TypeDesc::HALF; if (m_spec.format != TypeDesc::FLOAT) m_spec.format = TypeDesc::HALF; break; case Imf::FLOAT : fmt = TypeDesc::FLOAT; m_spec.format = TypeDesc::FLOAT; break; default: ASSERT (0); } chanformat.push_back (fmt); m_pixeltype.push_back (ptype); if (fmt != chanformat[0]) differing_chanformats = true; } ASSERT (m_spec.format != TypeDesc::UNKNOWN); if (differing_chanformats) m_spec.channelformats = chanformat; }
bool OpenEXRInput::open (const std::string &name, ImageSpec &newspec) { // Quick check to reject non-exr files bool tiled; if (! Imf::isOpenExrFile (name.c_str(), tiled)) return false; m_spec = ImageSpec(); // Clear everything with default constructor // Unless otherwise specified, exr files are assumed to be linear. m_spec.attribute ("oiio:ColorSpace", "Linear"); try { if (tiled) { m_input_tiled = new Imf::TiledInputFile (name.c_str()); m_header = &(m_input_tiled->header()); } else { m_input_scanline = new Imf::InputFile (name.c_str()); m_header = &(m_input_scanline->header()); } } catch (const std::exception &e) { error ("OpenEXR exception: %s", e.what()); return false; } if (! m_input_scanline && ! m_input_tiled) { error ("Unknown error opening EXR file"); return false; } Imath::Box2i datawindow = m_header->dataWindow(); m_spec.x = datawindow.min.x; m_spec.y = datawindow.min.y; m_spec.z = 0; m_spec.width = datawindow.max.x - datawindow.min.x + 1; m_spec.height = datawindow.max.y - datawindow.min.y + 1; m_spec.depth = 1; m_topwidth = m_spec.width; // Save top-level mipmap dimensions m_topheight = m_spec.height; Imath::Box2i displaywindow = m_header->displayWindow(); m_spec.full_x = displaywindow.min.x; m_spec.full_y = displaywindow.min.y; m_spec.full_z = 0; m_spec.full_width = displaywindow.max.x - displaywindow.min.x + 1; m_spec.full_height = displaywindow.max.y - displaywindow.min.y + 1; m_spec.full_depth = 1; if (tiled) { m_spec.tile_width = m_input_tiled->tileXSize(); m_spec.tile_height = m_input_tiled->tileYSize(); } else { m_spec.tile_width = 0; m_spec.tile_height = 0; } m_spec.tile_depth = 1; query_channels (); // also sets format m_nsubimages = 1; if (tiled) { // FIXME: levelmode m_levelmode = m_input_tiled->levelMode(); m_roundingmode = m_input_tiled->levelRoundingMode(); if (m_levelmode == Imf::MIPMAP_LEVELS) { m_nmiplevels = m_input_tiled->numLevels(); m_spec.attribute ("openexr:roundingmode", m_roundingmode); } else if (m_levelmode == Imf::RIPMAP_LEVELS) { m_nmiplevels = std::max (m_input_tiled->numXLevels(), m_input_tiled->numYLevels()); m_spec.attribute ("openexr:roundingmode", m_roundingmode); } else { m_nmiplevels = 1; } } else { m_levelmode = Imf::ONE_LEVEL; m_nmiplevels = 1; } const Imf::EnvmapAttribute *envmap; envmap = m_header->findTypedAttribute<Imf::EnvmapAttribute>("envmap"); if (envmap) { m_cubeface = (envmap->value() == Imf::ENVMAP_CUBE); m_spec.attribute ("textureformat", m_cubeface ? "CubeFace Environment" : "LatLong Environment"); // OpenEXR conventions for env maps if (! m_cubeface) m_spec.attribute ("oiio:updirection", "y"); m_spec.attribute ("oiio:sampleborder", 1); // FIXME - detect CubeFace Shadow? } else { m_cubeface = false; if (tiled && m_levelmode == Imf::MIPMAP_LEVELS) m_spec.attribute ("textureformat", "Plain Texture"); // FIXME - detect Shadow } const Imf::CompressionAttribute *compressattr; compressattr = m_header->findTypedAttribute<Imf::CompressionAttribute>("compression"); if (compressattr) { const char *comp = NULL; switch (compressattr->value()) { case Imf::NO_COMPRESSION : comp = "none"; break; case Imf::RLE_COMPRESSION : comp = "rle"; break; case Imf::ZIPS_COMPRESSION : comp = "zip"; break; case Imf::ZIP_COMPRESSION : comp = "zip"; break; case Imf::PIZ_COMPRESSION : comp = "piz"; break; case Imf::PXR24_COMPRESSION : comp = "pxr24"; break; #ifdef IMF_B44_COMPRESSION // The enum Imf::B44_COMPRESSION is not defined in older versions // of OpenEXR, and there are no explicit version numbers in the // headers. BUT this other related #define is present only in // the newer version. case Imf::B44_COMPRESSION : comp = "b44"; break; case Imf::B44A_COMPRESSION : comp = "b44a"; break; #endif default: break; } if (comp) m_spec.attribute ("compression", comp); } for (Imf::Header::ConstIterator hit = m_header->begin(); hit != m_header->end(); ++hit) { const Imf::IntAttribute *iattr; const Imf::FloatAttribute *fattr; const Imf::StringAttribute *sattr; const Imf::M44fAttribute *mattr; const Imf::V3fAttribute *vattr; const char *name = hit.name(); std::string oname = exr_tag_to_ooio_std[name]; if (oname.empty()) // Empty string means skip this attrib continue; // if (oname == name) // oname = std::string(format_name()) + "_" + oname; const Imf::Attribute &attrib = hit.attribute(); std::string type = attrib.typeName(); if (type == "string" && (sattr = m_header->findTypedAttribute<Imf::StringAttribute> (name))) m_spec.attribute (oname, sattr->value().c_str()); else if (type == "int" && (iattr = m_header->findTypedAttribute<Imf::IntAttribute> (name))) m_spec.attribute (oname, iattr->value()); else if (type == "float" && (fattr = m_header->findTypedAttribute<Imf::FloatAttribute> (name))) m_spec.attribute (oname, fattr->value()); else if (type == "m44f" && (mattr = m_header->findTypedAttribute<Imf::M44fAttribute> (name))) m_spec.attribute (oname, TypeDesc::TypeMatrix, &(mattr->value())); else if (type == "v3f" && (vattr = m_header->findTypedAttribute<Imf::V3fAttribute> (name))) m_spec.attribute (oname, TypeDesc::TypeVector, &(vattr->value())); else { #if 0 std::cerr << " unknown attribute " << type << ' ' << name << "\n"; #endif } } m_subimage = 0; m_miplevel = 0; newspec = m_spec; return true; }
bool OpenEXROutput::open (const std::string &name, const ImageSpec &userspec, OpenMode mode) { if (mode == AppendSubimage) { error ("%s does not support subimages", format_name()); return false; } if (mode == AppendMIPLevel && (m_output_scanline || m_output_tiled)) { // Special case for appending to an open file -- we don't need // to close and reopen if (m_spec.tile_width && m_levelmode != Imf::ONE_LEVEL) { // OpenEXR does not support differing tile sizes on different // MIP-map levels. Reject the open() if not using the original // tile sizes. if (userspec.tile_width != m_spec.tile_width || userspec.tile_height != m_spec.tile_height) { error ("OpenEXR tiles must have the same size on all MIPmap levels"); return false; } // Copy the new mip level size. Keep everything else from the // original level. m_spec.width = userspec.width; m_spec.height = userspec.height; // N.B. do we need to copy anything else from userspec? ++m_miplevel; return true; } } m_spec = userspec; // Stash the spec if (m_spec.width < 1 || m_spec.height < 1) { error ("Image resolution must be at least 1x1, you asked for %d x %d", userspec.width, userspec.height); return false; } if (m_spec.depth < 1) m_spec.depth = 1; if (m_spec.depth > 1) { error ("%s does not support volume images (depth > 1)", format_name()); return false; } if (m_spec.full_width <= 0) m_spec.full_width = m_spec.width; if (m_spec.full_height <= 0) m_spec.full_height = m_spec.height; // Force use of one of the three data types that OpenEXR supports switch (m_spec.format.basetype) { case TypeDesc::UINT: m_spec.format = TypeDesc::UINT; break; case TypeDesc::FLOAT: case TypeDesc::DOUBLE: m_spec.format = TypeDesc::FLOAT; break; default: // Everything else defaults to half m_spec.format = TypeDesc::HALF; } Imath::Box2i dataWindow (Imath::V2i (m_spec.x, m_spec.y), Imath::V2i (m_spec.width + m_spec.x - 1, m_spec.height + m_spec.y - 1)); Imath::Box2i displayWindow (Imath::V2i (m_spec.full_x, m_spec.full_y), Imath::V2i (m_spec.full_width+m_spec.full_x-1, m_spec.full_height+m_spec.full_y-1)); m_header = new Imf::Header (displayWindow, dataWindow); // Insert channels into the header. Also give the channels names if // the user botched it. static const char *default_chan_names[] = { "R", "G", "B", "A" }; m_spec.channelnames.resize (m_spec.nchannels); for (int c = 0; c < m_spec.nchannels; ++c) { if (m_spec.channelnames[c].empty()) m_spec.channelnames[c] = (c<4) ? default_chan_names[c] : Strutil::format ("unknown %d", c); TypeDesc format = m_spec.channelformats.size() ? m_spec.channelformats[c] : m_spec.format; Imf::PixelType ptype; switch (format.basetype) { case TypeDesc::UINT: ptype = Imf::UINT; format = TypeDesc::UINT; break; case TypeDesc::FLOAT: case TypeDesc::DOUBLE: ptype = Imf::FLOAT; format = TypeDesc::FLOAT; break; default: // Everything else defaults to half ptype = Imf::HALF; format = TypeDesc::HALF; } #ifdef OPENEXR_VERSION_IS_1_6_OR_LATER // Hint to lossy compression methods that indicates whether // human perception of the quantity represented by this channel // is closer to linear or closer to logarithmic. Compression // methods may optimize image quality by adjusting pixel data // quantization acording to this hint. bool pLinear = iequals (m_spec.get_string_attribute ("oiio:ColorSpace", "Linear"), "Linear"); #endif m_pixeltype.push_back (ptype); if (m_spec.channelformats.size()) m_spec.channelformats[c] = format; m_header->channels().insert (m_spec.channelnames[c].c_str(), Imf::Channel(ptype, 1, 1 #ifdef OPENEXR_VERSION_IS_1_6_OR_LATER , pLinear #endif )); } ASSERT (m_pixeltype.size() == (size_t)m_spec.nchannels); // Default to ZIP compression if no request came with the user spec. if (! m_spec.find_attribute("compression")) m_spec.attribute ("compression", "zip"); // Default to increasingY line order, same as EXR. if (! m_spec.find_attribute("openexr:lineOrder")) m_spec.attribute ("openexr:lineOrder", "increasingY"); // Automatically set date field if the client didn't supply it. if (! m_spec.find_attribute("DateTime")) { time_t now; time (&now); struct tm mytm; Sysutil::get_local_time (&now, &mytm); std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d", mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); m_spec.attribute ("DateTime", date); } m_nsubimages = 1; m_subimage = 0; m_nmiplevels = 1; m_miplevel = 0; // Figure out if we are a mipmap or an environment map ImageIOParameter *param = m_spec.find_attribute ("textureformat"); const char *textureformat = param ? *(char **)param->data() : NULL; m_levelmode = Imf::ONE_LEVEL; // Default to no MIP-mapping m_roundingmode = m_spec.get_int_attribute ("openexr:roundingmode", Imf::ROUND_DOWN); if (textureformat) { if (iequals (textureformat, "Plain Texture")) { m_levelmode = m_spec.get_int_attribute ("openexr:levelmode", Imf::MIPMAP_LEVELS); } else if (iequals (textureformat, "CubeFace Environment")) { m_levelmode = m_spec.get_int_attribute ("openexr:levelmode", Imf::MIPMAP_LEVELS); m_header->insert ("envmap", Imf::EnvmapAttribute(Imf::ENVMAP_CUBE)); } else if (iequals (textureformat, "LatLong Environment")) { m_levelmode = m_spec.get_int_attribute ("openexr:levelmode", Imf::MIPMAP_LEVELS); m_header->insert ("envmap", Imf::EnvmapAttribute(Imf::ENVMAP_LATLONG)); } else if (iequals (textureformat, "Shadow")) { m_levelmode = Imf::ONE_LEVEL; // Force one level for shadow maps } if (m_levelmode == Imf::MIPMAP_LEVELS) { // Compute how many mip levels there will be int w = m_spec.width; int h = m_spec.height; while (w > 1 && h > 1) { if (m_roundingmode == Imf::ROUND_DOWN) { w = w / 2; h = h / 2; } else { w = (w + 1) / 2; h = (h + 1) / 2; } w = std::max (1, w); h = std::max (1, h); ++m_nmiplevels; } } } // Deal with all other params for (size_t p = 0; p < m_spec.extra_attribs.size(); ++p) put_parameter (m_spec.extra_attribs[p].name().string(), m_spec.extra_attribs[p].type(), m_spec.extra_attribs[p].data()); try { if (m_spec.tile_width) { m_header->setTileDescription ( Imf::TileDescription (m_spec.tile_width, m_spec.tile_height, Imf::LevelMode(m_levelmode), Imf::LevelRoundingMode(m_roundingmode))); m_output_tiled = new Imf::TiledOutputFile (name.c_str(), *m_header); } else { m_output_scanline = new Imf::OutputFile (name.c_str(), *m_header); } } catch (const std::exception &e) { error ("OpenEXR exception: %s", e.what()); m_output_scanline = NULL; return false; } if (! m_output_scanline && ! m_output_tiled) { error ("Unknown error opening EXR file"); return false; } return true; }
bool OpenEXROutput::put_parameter (const std::string &name, TypeDesc type, const void *data) { // Translate std::string xname = name; if (istarts_with (xname, "oiio:")) return false; else if (iequals(xname, "worldtocamera")) xname = "worldToCamera"; else if (iequals(xname, "worldtoscreen")) xname = "worldToNDC"; else if (iequals(xname, "DateTime")) xname = "capDate"; else if (iequals(xname, "description") || iequals(xname, "ImageDescription")) xname = "comments"; else if (iequals(xname, "Copyright")) xname = "owner"; else if (iequals(xname, "PixelAspectRatio")) xname = "pixelAspectRatio"; else if (iequals(xname, "ExposureTime")) xname = "expTime"; else if (iequals(xname, "FNumber")) xname = "aperture"; else if (istarts_with (xname, format_prefix)) xname = std::string (xname.begin()+format_prefix.size(), xname.end()); // std::cerr << "exr put '" << name << "' -> '" << xname << "'\n"; // Special cases if (iequals(xname, "Compression") && type == TypeDesc::STRING) { const char *str = *(char **)data; m_header->compression() = Imf::ZIP_COMPRESSION; // Default if (str) { if (iequals (str, "none")) m_header->compression() = Imf::NO_COMPRESSION; else if (iequals (str, "deflate") || iequals (str, "zip")) m_header->compression() = Imf::ZIP_COMPRESSION; else if (iequals (str, "rle")) m_header->compression() = Imf::RLE_COMPRESSION; else if (iequals (str, "zips")) m_header->compression() = Imf::ZIPS_COMPRESSION; else if (iequals (str, "piz")) m_header->compression() = Imf::PIZ_COMPRESSION; else if (iequals (str, "pxr24")) m_header->compression() = Imf::PXR24_COMPRESSION; #ifdef IMF_B44_COMPRESSION // The enum Imf::B44_COMPRESSION is not defined in older versions // of OpenEXR, and there are no explicit version numbers in the // headers. BUT this other related #define is present only in // the newer version. else if (iequals (str, "b44")) m_header->compression() = Imf::B44_COMPRESSION; else if (iequals (str, "b44a")) m_header->compression() = Imf::B44A_COMPRESSION; #endif } return true; } if (iequals (xname, "openexr:lineOrder") && type == TypeDesc::STRING) { const char *str = *(char **)data; m_header->lineOrder() = Imf::INCREASING_Y; // Default if (str) { if (iequals (str, "randomY")) m_header->lineOrder() = Imf::RANDOM_Y; else if (iequals (str, "decreasingY")) m_header->lineOrder() = Imf::DECREASING_Y; } return true; } // Supress planarconfig! if (iequals (xname, "planarconfig") || iequals (xname, "tiff:planarconfig")) return true; // General handling of attributes // FIXME -- police this if we ever allow arrays if (type == TypeDesc::INT || type == TypeDesc::UINT) { m_header->insert (xname.c_str(), Imf::IntAttribute (*(int*)data)); return true; } if (type == TypeDesc::INT16) { m_header->insert (xname.c_str(), Imf::IntAttribute (*(short*)data)); return true; } if (type == TypeDesc::UINT16) { m_header->insert (xname.c_str(), Imf::IntAttribute (*(unsigned short*)data)); return true; } if (type == TypeDesc::FLOAT) { m_header->insert (xname.c_str(), Imf::FloatAttribute (*(float*)data)); return true; } if (type == TypeDesc::HALF) { m_header->insert (xname.c_str(), Imf::FloatAttribute ((float)*(half*)data)); return true; } if (type == TypeDesc::TypeMatrix) { m_header->insert (xname.c_str(), Imf::M44fAttribute (*(Imath::M44f*)data)); return true; } if (type == TypeDesc::TypeString) { m_header->insert (xname.c_str(), Imf::StringAttribute (*(char**)data)); return true; } if (type == TypeDesc::TypeVector) { m_header->insert (xname.c_str(), Imf::V3fAttribute (*(Imath::V3f*)data)); return true; } #ifdef DEBUG std::cerr << "Don't know what to do with " << type.c_str() << ' ' << xname << "\n"; #endif return false; }