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; }
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 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; }