#include "DDImage/Box.h" /////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2011 The Foundry Visionmongers Ltd. All Rights Reserved. // Portions contributed and copyright held by others as indicated. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above // copyright notice, this list of conditions and the following // disclaimer. // // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided with // the distribution. // // * Neither the name of The Foundry Visionmongers nor any other contributors // to this software may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// namespace { enum ExrMetaDataMode { eNoMetaData, eDefaultMetaData, eDefaultMetaDataAndEXR, eAllMetadataExceptInput, eAllMetaData }; static const Imf::Compression ctypes[6] = { Imf::NO_COMPRESSION, Imf::ZIPS_COMPRESSION, Imf::ZIP_COMPRESSION, Imf::PIZ_COMPRESSION, Imf::RLE_COMPRESSION, Imf::B44_COMPRESSION }; static const char* const cnames[] = { "none", "Zip (1 scanline)", "Zip (16 scanlines)", "PIZ Wavelet (32 scanlines)", "RLE", "B44", NULL }; static const char* const dnames[] = { "16 bit half", "32 bit float", NULL }; static const char* const metadata_modes[] = { "no metadata", "default metadata", "default metadata and exr/*", "all metadata except input/*", "all metadata", NULL }; /// convert a line between the EXR line numbering (0 = top) and the Nuke one (0 = bottom) int lineToLine(int line, const Imath::Box2i& displayWindow) { return (displayWindow.max.y - line); } /// convert a bounding box from the EXR bbox format (0, 0 = top left, bottom/right numbers inclusive) /// to the Nuke format (0, 0 = bottom left, top/right numbers exclusive DD::Image::Box boxToBox(const Imath::Box2i& exrBox, const Imath::Box2i& displayWindow) { return DD::Image::Box(exrBox.min.x, lineToLine(exrBox.max.y, displayWindow), exrBox.max.x + 1, lineToLine(exrBox.min.y, displayWindow) + 1); } /// convert an EXR channel name to a nuke channel ID, creating the channel if needed DD::Image::Channel getExrChannel(const char* name) { if (!strcmp(name, "R")) return DD::Image::Chan_Red; if (!strcmp(name, "G")) return DD::Image::Chan_Green; if (!strcmp(name, "B")) return DD::Image::Chan_Blue; if (!strcmp(name, "A")) return DD::Image::Chan_Alpha; if (!strcmp(name, "Z")) return DD::Image::Chan_DeepFront; if (!strcmp(name, "ZBack")) return DD::Image::Chan_DeepBack; return DD::Image::getChannel(name); } /// convert a nuke channel into an EXR channel name const char* getExrChannelName(DD::Image::Channel z) { switch (z) { case DD::Image::Chan_Red: return "R"; case DD::Image::Chan_Green: return "G"; case DD::Image::Chan_Blue: return "B"; case DD::Image::Chan_Alpha: return "A"; case DD::Image::Chan_DeepFront: return "Z"; case DD::Image::Chan_DeepBack: return "ZBack"; default: return DD::Image::getName(z); } } bool timeCodeFromString(const std::string& str, Imf::TimeCode& attr, DD::Image::Op* iop) { if (str.length() != 11) return false; int hours = 0, mins = 0, secs = 0, frames = 0; sscanf(str.c_str(), "%02d:%02d:%02d:%02d", &hours, &mins, &secs, &frames); try { // if some thing is out of range an exception is throw // in this case just report a warning on console Imf::TimeCode a; a.setHours(hours); a.setMinutes(mins); a.setSeconds(secs); a.setFrame(frames); attr = a; } catch (const std::exception& exc) { iop->warning("EXR: Time Code Metadata warning [%s]\n", exc.what()); return false; } return true; } bool edgeCodeFromString(const std::string& str, Imf::KeyCode& attr, DD::Image::Op* iop) { int mfcCode, filmType, prefix, count, perfOffset; sscanf(str.c_str(), "%d %d %d %d %d", &mfcCode, &filmType, &prefix, &count, &perfOffset); try { // if some thing is out of range an exception is throw // in this case just report a warning on console Imf::KeyCode a; a.setFilmMfcCode(mfcCode); a.setFilmType(filmType); a.setPrefix(prefix); a.setCount(count); a.setPerfOffset(perfOffset); attr = a; } catch (const std::exception& exc) { iop->warning("EXR: Edge Code Metadata warning [%s]\n", exc.what()); return false; } return true; } void metadataToExrHeader(ExrMetaDataMode metadataMode, const DD::Image::MetaData::Bundle& metadata, Imf::Header& exrheader, DD::Image::Op* op, DD::Image::Hash* nodeHash, bool doNotWriteNukePrefix ) { if (metadataMode != eNoMetaData ) { // NB: if specific things are added to this list the tooltip for the "metadata" knob needs // updating std::string timeCodeStr = metadata.getString(DD::Image::MetaData::TIMECODE); if (!timeCodeStr.empty()) { Imf::TimeCode attr; if (timeCodeFromString(timeCodeStr, attr, op)) { Imf::addTimeCode(exrheader, attr); } } std::string edgeCodeStr = metadata.getString(DD::Image::MetaData::EDGECODE); if (!edgeCodeStr.empty()) { Imf::KeyCode attr; if (edgeCodeFromString(edgeCodeStr, attr, op)) { Imf::addKeyCode(exrheader, attr); } } double frameRate = metadata.getDouble(DD::Image::MetaData::FRAME_RATE); if (frameRate != 0) { Imf::Rational fps = Imf::guessExactFps(frameRate); Imf::addFramesPerSecond(exrheader, fps); } double exposure = metadata.getDouble(DD::Image::MetaData::EXPOSURE); if (exposure != 0) { Imf::addExpTime(exrheader, (float)exposure); } if ( nodeHash ) { std::ostringstream hashString; hashString << std::hex << nodeHash->value(); Imf::StringAttribute hashAttr; hashAttr.value() = hashString.str(); exrheader.insert(DD::Image::MetaData::Nuke::NODE_HASH, hashAttr); } } if (metadataMode) { for (DD::Image::MetaData::Bundle::const_iterator it = metadata.begin(); it != metadata.end(); it++) { std::string exrPropName = ""; if (it->first == DD::Image::MetaData::EXR::EXR_TILED ) { // strip exr/tiled as we always write scanline exrs exrPropName = ""; } else if (it->first.substr(0, strlen(DD::Image::MetaData::EXR::EXR_PREFIX)) == DD::Image::MetaData::EXR::EXR_PREFIX && metadataMode >= eDefaultMetaDataAndEXR) { exrPropName = it->first.substr(strlen(DD::Image::MetaData::EXR::EXR_PREFIX)); } else if (it->first.substr(0, strlen(DD::Image::MetaData::INPUT_PREFIX)) != DD::Image::MetaData::INPUT_PREFIX && metadataMode >= eAllMetadataExceptInput) { if ( doNotWriteNukePrefix ) exrPropName = it->first; else exrPropName = DD::Image::MetaData::Nuke::NUKE_PREFIX + it->first; } else if ( metadataMode >= 4) { if ( doNotWriteNukePrefix ) exrPropName = it->first; else exrPropName = DD::Image::MetaData::Nuke::NUKE_PREFIX + it->first; } Imf::Attribute* attr = 0; const DD::Image::MetaData::Bundle::PropertyPtr prop = it->second; size_t psize = DD::Image::MetaData::getPropertySize(prop); if (!exrPropName.empty()) { if ( DD::Image::MetaData::isPropertyDouble(prop) ) { if (psize == 1) { attr = new Imf::FloatAttribute( (float)DD::Image::MetaData::getPropertyDouble(prop, 0) ); } else if (psize == 2) { attr = new Imf::V2fAttribute(Imath::V2f( (float)DD::Image::MetaData::getPropertyDouble(prop, 0), (float)DD::Image::MetaData::getPropertyDouble(prop, 1) )); } else if (psize == 3) { attr = new Imf::V3fAttribute(Imath::V3f( (float)DD::Image::MetaData::getPropertyDouble(prop, 0), (float)DD::Image::MetaData::getPropertyDouble(prop, 1), (float)DD::Image::MetaData::getPropertyDouble(prop, 2) )); } else if (psize == 4) { attr = new Imf::Box2fAttribute(Imath::Box2f( Imath::V2f((float)DD::Image::MetaData::getPropertyDouble(prop, 0), (float)DD::Image::MetaData::getPropertyDouble(prop, 1)), Imath::V2f((float)DD::Image::MetaData::getPropertyDouble(prop, 2), (float)DD::Image::MetaData::getPropertyDouble(prop, 3) ))); } else if (psize == 9) { float val[3][3]; for (size_t i = 0; i < psize; i++) { val[i / 3][i % 3] = (float)DD::Image::MetaData::getPropertyDouble(prop, i); } attr = new Imf::M33fAttribute(Imath::M33f(val)); } else if (psize == 16) { float val[4][4]; for (size_t i = 0; i < psize; i++) { val[i / 4][i % 4] = (float)DD::Image::MetaData::getPropertyDouble(prop, i); } attr = new Imf::M44fAttribute(Imath::M44f(val)); } } else if (DD::Image::MetaData::isPropertyInt( prop )) { if (psize == 1) { attr = new Imf::IntAttribute(DD::Image::MetaData::getPropertyInt(prop, 0)); } else if (psize == 2) { attr = new Imf::V2iAttribute( Imath::V2i(DD::Image::MetaData::getPropertyInt(prop, 0), DD::Image::MetaData::getPropertyInt(prop, 1)) ); } else if (psize == 3) { attr = new Imf::V3iAttribute( Imath::V3i(DD::Image::MetaData::getPropertyInt(prop, 0), DD::Image::MetaData::getPropertyInt(prop, 1), DD::Image::MetaData::getPropertyInt(prop, 2))); } else if (psize == 4) { attr = new Imf::Box2iAttribute( Imath::Box2i(Imath::V2i(DD::Image::MetaData::getPropertyInt(prop, 0), DD::Image::MetaData::getPropertyInt(prop, 1)), Imath::V2i(DD::Image::MetaData::getPropertyInt(prop, 2), DD::Image::MetaData::getPropertyInt(prop, 3)))) ; } } else if ( DD::Image::MetaData::isPropertyString(prop) ) { if (psize == 1) { attr = new Imf::StringAttribute( DD::Image::MetaData::getPropertyString(prop, 0) ); } } } if (attr && exrheader.find(exrPropName.c_str()) == exrheader.end()) { exrheader.insert(exrPropName.c_str(), *attr); } delete attr; } } } void exrHeaderToMetadata( const Imf::Header& header, DD::Image::MetaData::Bundle& meta, bool doNotAttachPrefix ) { Imf::Compression compression = header.compression(); meta.setData( ( doNotAttachPrefix ? "" : std::string(DD::Image::MetaData::EXR::EXR_PREFIX) ) + "compression", int(compression) ); if ( header.hasTileDescription() ) { meta.setData( ( doNotAttachPrefix ? "" : std::string(DD::Image::MetaData::EXR::EXR_PREFIX) ) + "tiled", true); } for (Imf::Header::ConstIterator i = header.begin(); i != header.end(); i++) { const char* type = i.attribute().typeName(); std::string key = std::string(DD::Image::MetaData::EXR::EXR_PREFIX) + i.name(); if ( doNotAttachPrefix ) key = i.name(); if (!strcmp(i.name(), "timeCode")) { key = DD::Image::MetaData::TIMECODE; } if (!strcmp(i.name(), "expTime")) { key = DD::Image::MetaData::EXPOSURE; } if (!strcmp(i.name(), "framesPerSecond")) { key = DD::Image::MetaData::FRAME_RATE; } if (!strcmp(i.name(), "keyCode")) { key = DD::Image::MetaData::EDGECODE; } if (!strcmp(i.name(), DD::Image::MetaData::Nuke::NODE_HASH )) { key = DD::Image::MetaData::Nuke::NODE_HASH; } if (!strcmp(type, "string")) { const Imf::StringAttribute* attr = static_cast(&i.attribute()); meta.setData(key, attr->value()); } else if (!strcmp(type, "int")) { const Imf::IntAttribute* attr = static_cast(&i.attribute()); meta.setData(key, attr->value()); } else if (!strcmp(type, "v2i")) { const Imf::V2iAttribute* attr = static_cast(&i.attribute()); int values[2] = { attr->value().x, attr->value().y }; meta.setData(key, values, 2); } else if (!strcmp(type, "v3i")) { const Imf::V3iAttribute* attr = static_cast(&i.attribute()); int values[3] = { attr->value().x, attr->value().y, attr->value().z }; meta.setData(key, values, 3); } else if (!strcmp(type, "box2i")) { const Imf::Box2iAttribute* attr = static_cast(&i.attribute()); int values[4] = { attr->value().min.x, attr->value().min.y, attr->value().max.x, attr->value().max.y }; meta.setData(key, values, 4); } else if (!strcmp(type, "float")) { const Imf::FloatAttribute* attr = static_cast(&i.attribute()); meta.setData(key, attr->value()); } else if (!strcmp(type, "v2f")) { const Imf::V2fAttribute* attr = static_cast(&i.attribute()); float values[2] = { attr->value().x, attr->value().y }; meta.setData(key, values, 2); } else if (!strcmp(type, "v3f")) { const Imf::V3fAttribute* attr = static_cast(&i.attribute()); float values[3] = { attr->value().x, attr->value().y, attr->value().z }; meta.setData(key, values, 3); } else if (!strcmp(type, "box2f")) { const Imf::Box2fAttribute* attr = static_cast(&i.attribute()); float values[4] = { attr->value().min.x, attr->value().min.y, attr->value().max.x, attr->value().max.y }; meta.setData(key, values, 4); } else if (!strcmp(type, "m33f")) { const Imf::M33fAttribute* attr = static_cast(&i.attribute()); std::vector values; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { values.push_back((attr->value())[i][j]); } } meta.setData(key, values); } else if (!strcmp(type, "m44f")) { const Imf::M44fAttribute* attr = static_cast(&i.attribute()); std::vector values; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { values.push_back((attr->value())[i][j]); } } meta.setData(key, values); } else if (!strcmp(type, "timecode")) { const Imf::TimeCodeAttribute* attr = static_cast(&i.attribute()); char timecode[20]; sprintf(timecode, "%02i:%02i:%02i:%02i", attr->value().hours(), attr->value().minutes(), attr->value().seconds(), attr->value().frame()); meta.setData(key, timecode); } else if (!strcmp(type, "keycode")) { const Imf::KeyCodeAttribute* attr = static_cast(&i.attribute()); char keycode[30]; sprintf(keycode, "%02i %02i %06i %04i %02i", attr->value().filmMfcCode(), attr->value().filmType(), attr->value().prefix(), attr->value().count(), attr->value().perfOffset()); meta.setData(key, keycode); } else if (!strcmp(type, "rational")) { const Imf::RationalAttribute* attr = static_cast(&i.attribute()); meta.setData(key, (double)attr->value()); }else if(!strcmp(type,"stringvector")){ const Imf::StringVectorAttribute * attr = static_cast(&i.attribute()); std::string data; for(size_t i =0 ; ivalue().size();i++) { data+=attr->value()[i]+"\n"; } meta.setData(key,data); } } } }