/** \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())); }
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; }
// 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; }
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; }