diff --git a/include/mitsuba/core/bitmap.h b/include/mitsuba/core/bitmap.h index b05a6197..5ae2dadf 100644 --- a/include/mitsuba/core/bitmap.h +++ b/include/mitsuba/core/bitmap.h @@ -774,25 +774,23 @@ public: inline void setGamma(Float gamma) { m_gamma = gamma; } /// Set a string-valued metadata field - void setString(const std::string &key, const std::string &value); + inline void setMetadataString(const std::string &key, const std::string &value) { + m_metadata.setString(key, value); + } /// Return a string-valued metadata field - std::string getString(const std::string &key) const; - - /// Return a map of all present metadata - inline std::map &getMetadata() { - return m_metadata; + inline std::string getMetadataString(const std::string &key) const { + return m_metadata.getAsString(key); } - /// Return a map of all present metadata (const version) - inline const std::map &getMetadata() const { - return m_metadata; - } + /// Return a \ref Properties object containing the image metadata + inline Properties &getMetadata() { return m_metadata; } - /// Set the metadata associated with the bitmap - inline void setMetadata(const std::map &metadata) { - m_metadata = metadata; - } + /// Return a \ref Properties object containing the image metadata (const version) + inline const Properties &getMetadata() const { return m_metadata; } + + /// Set the a \ref Properties object containing the image metadata + inline void setMetadata(const Properties &metadata) { m_metadata = metadata; } //! @} // ====================================================================== @@ -910,9 +908,9 @@ protected: EComponentFormat m_componentFormat; Vector2i m_size; uint8_t *m_data; - std::map m_metadata; Float m_gamma; int m_channelCount; + Properties m_metadata; }; /** \brief Bitmap format conversion helper class diff --git a/include/mitsuba/core/properties.h b/include/mitsuba/core/properties.h index ab61a577..2b898836 100644 --- a/include/mitsuba/core/properties.h +++ b/include/mitsuba/core/properties.h @@ -71,6 +71,14 @@ public: struct Data { uint8_t *ptr; size_t size; + + inline bool operator==(const Data &d) const { + return ptr == d.ptr && size == d.size; + } + + inline bool operator!=(const Data &d) const { + return !operator==(d); + } }; /// Construct an empty property container @@ -172,6 +180,11 @@ public: /// Get a string (with default) std::string getString(const std::string &name, const std::string &defVal) const; + /// Return one of the parameters (converting it to a string if necessary) + std::string getAsString(const std::string &name) const; + /// Return one of the parameters (converting it to a string if necessary, with default value) + std::string getAsString(const std::string &name, const std::string &defVal) const; + /// Store an array containing the names of all stored properties void putPropertyNames(std::vector &results) const; @@ -206,6 +219,14 @@ public: /// Assignment operator void operator=(const Properties &props); + /// Equality comparison operator + bool operator==(const Properties &props) const; + + /// Inequality comparision operator + inline bool operator!=(const Properties &props) const { + return !operator==(props); + } + /// Return a string representation std::string toString() const; private: diff --git a/include/mitsuba/core/transform.h b/include/mitsuba/core/transform.h index a90523a8..634614f8 100644 --- a/include/mitsuba/core/transform.h +++ b/include/mitsuba/core/transform.h @@ -393,6 +393,16 @@ public: m_invTransform.serialize(stream); } + /// Equality comparison operator + inline bool operator==(const Transform &trafo) const { + return m_transform == trafo.m_transform; + } + + /// Inequality comparison operator + inline bool operator!=(const Transform &trafo) const { + return m_transform != trafo.m_transform; + } + /// Return a string representation std::string toString() const; private: diff --git a/src/films/hdrfilm.cpp b/src/films/hdrfilm.cpp index 341bdd15..57034d88 100644 --- a/src/films/hdrfilm.cpp +++ b/src/films/hdrfilm.cpp @@ -383,7 +383,7 @@ public: for (std::map::const_iterator it = m_tags.begin(); it != m_tags.end(); ++it) - bitmap->getMetadata()[it->first] = it->second; + bitmap->setMetadataString(it->first, it->second); fs::path filename = m_destFile; std::string extension = boost::to_lower_copy(filename.extension().string()); @@ -400,7 +400,7 @@ public: if (m_attachLog && logger->readLog(log)) { log += "\n\n"; log += Statistics::getInstance()->getStats(); - bitmap->setString("log", log); + bitmap->setMetadataString("log", log); } bitmap->write(m_fileFormat, stream); diff --git a/src/films/ldrfilm.cpp b/src/films/ldrfilm.cpp index e002d3d7..ea187fba 100644 --- a/src/films/ldrfilm.cpp +++ b/src/films/ldrfilm.cpp @@ -353,7 +353,7 @@ public: for (std::map::const_iterator it = m_tags.begin(); it != m_tags.end(); ++it) - bitmap->getMetadata()[it->first] = it->second; + bitmap->setMetadataString(it->first, it->second); fs::path filename = m_destFile; std::string extension = boost::to_lower_copy(filename.extension().string()); diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp index da604825..7c233339 100644 --- a/src/libcore/bitmap.cpp +++ b/src/libcore/bitmap.cpp @@ -38,6 +38,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include @@ -386,19 +391,6 @@ int Bitmap::getBytesPerComponent() const { } } - -void Bitmap::setString(const std::string &key, const std::string &value) { - m_metadata[key] = value; -} - -std::string Bitmap::getString(const std::string &key) const { - std::map::const_iterator it = m_metadata.find(key); - if (it != m_metadata.end()) - return it->second; - else - return ""; -} - Bitmap::~Bitmap() { if (m_data) freeAligned(m_data); @@ -1189,12 +1181,17 @@ std::string Bitmap::toString() const { << " type = " << m_pixelFormat << endl << " componentFormat = " << m_componentFormat << endl << " size = " << m_size.toString() << endl; - if (!m_metadata.empty()) { + + std::vector keys = m_metadata.getPropertyNames(); + if (!keys.empty()) { oss << " metadata = {" << endl; - for (std::map::const_iterator it = m_metadata.begin(); - it != m_metadata.end();) { - oss << " \"" << it->first << "\" => \"" << it->second << "\""; - if (++it != m_metadata.end()) + for (std::vector::const_iterator it = keys.begin(); it != keys.end(); ) { + std::string value = m_metadata.getAsString(*it); + if (value.size() > 50) + value = value.substr(0, 50) + ".. [truncated]"; + + oss << " \"" << *it << "\" => \"" << value << "\""; + if (++it != keys.end()) oss << ","; oss << endl; } @@ -1281,7 +1278,7 @@ void Bitmap::readPNG(Stream *stream) { png_get_text(png_ptr, info_ptr, &text_ptr, &textIdx); for (int i=0; ikey] = text_ptr->text; + setMetadataString(text_ptr->key, text_ptr->text); int intent; double gamma; if (png_get_sRGB(png_ptr, info_ptr, &intent)) { @@ -1357,20 +1354,23 @@ void Bitmap::writePNG(Stream *stream, int compression) const { png_text *text = NULL; - std::map metadata = m_metadata; - metadata["generated-by"] = "Mitsuba version " MTS_VERSION; + Properties metadata(m_metadata); + metadata.setString("generated-by", "Mitsuba version " MTS_VERSION); - text = new png_text[metadata.size()]; - memset(text, 0, sizeof(png_text) * metadata.size()); - int textIndex = 0; - for (std::map::iterator it = metadata.begin(); - it != metadata.end(); ++it) { - text[textIndex].key = const_cast(it->first.c_str()); - text[textIndex].text = const_cast(it->second.c_str()); - text[textIndex++].compression = PNG_TEXT_COMPRESSION_NONE; + std::vector keys = metadata.getPropertyNames(); + std::vector values(keys.size()); + + text = new png_text[keys.size()]; + memset(text, 0, sizeof(png_text) * keys.size()); + + for (size_t i = 0; i(keys[i].c_str()); + text[i].text = const_cast(values[i].c_str()); + text[i].compression = PNG_TEXT_COMPRESSION_NONE; } - png_set_text(png_ptr, info_ptr, text, textIndex); + png_set_text(png_ptr, info_ptr, text, keys.size()); if (m_gamma == -1) png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); @@ -1665,11 +1665,37 @@ void Bitmap::readOpenEXR(Stream *stream, const std::string &_prefix) { /* Load metadata if present */ for (Imf::Header::ConstIterator it = header.begin(); it != header.end(); ++it) { std::string name = it.name(), typeName = it.attribute().typeName(); - const Imf::StringAttribute *sattr = NULL; + const Imf::StringAttribute *sattr; + const Imf::IntAttribute *iattr; + const Imf::FloatAttribute *fattr; + const Imf::DoubleAttribute *dattr; + const Imf::V3fAttribute *vattr; + const Imf::M44fAttribute *mattr; if (typeName == "string" && (sattr = header.findTypedAttribute(name.c_str()))) - m_metadata[name] = sattr->value(); + m_metadata.setString(name, sattr->value()); + else if (typeName == "int" && + (iattr = header.findTypedAttribute(name.c_str()))) + m_metadata.setInteger(name, iattr->value()); + else if (typeName == "float" && + (fattr = header.findTypedAttribute(name.c_str()))) + m_metadata.setFloat(name, (Float) fattr->value()); + else if (typeName == "double" && + (dattr = header.findTypedAttribute(name.c_str()))) + m_metadata.setFloat(name, (Float) dattr->value()); + else if (typeName == "v3f" && + (vattr = header.findTypedAttribute(name.c_str()))) { + Imath::V3f vec = vattr->value(); + m_metadata.setVector(name, Vector(vec.x, vec.y, vec.z)); + } else if (typeName == "m44f" && + (mattr = header.findTypedAttribute(name.c_str()))) { + Matrix4x4 M; + for (int i=0; i<4; ++i) + for (int j=0; j<4; ++j) + M(i, j) = mattr->value().x[i][j]; + m_metadata.setTransform(name, Transform(M)); + } } updateChannelCount(); @@ -1896,13 +1922,45 @@ void Bitmap::writeOpenEXR(Stream *stream, pixelFormat = ERGBA; #endif - std::map metadata = m_metadata; - metadata["generated-by"] = "Mitsuba version " MTS_VERSION; + Properties metadata(m_metadata); + metadata.setString("generated-by", "Mitsuba version " MTS_VERSION); + + std::vector keys = metadata.getPropertyNames(); Imf::Header header(m_size.x, m_size.y); - for (std::map::const_iterator it = metadata.begin(); - it != metadata.end(); ++it) - header.insert(it->first.c_str(), Imf::StringAttribute(it->second.c_str())); + for (std::vector::const_iterator it = keys.begin(); it != keys.end(); ++it) { + Properties::EPropertyType type = metadata.getType(*it); + + switch (type) { + case Properties::EString: + header.insert(it->c_str(), Imf::StringAttribute(metadata.getString(*it))); + break; + case Properties::EInteger: + header.insert(it->c_str(), Imf::IntAttribute(metadata.getInteger(*it))); + break; + case Properties::EFloat: + header.insert(it->c_str(), Imf::FloatAttribute((float) metadata.getFloat(*it))); + break; + case Properties::EPoint: { + Point val = metadata.getPoint(*it); + header.insert(it->c_str(), Imf::V3fAttribute( + Imath::V3f((float) val.x, (float) val.y, (float) val.z))); + } + break; + case Properties::ETransform: { + Matrix4x4 val = metadata.getTransform(*it).getMatrix(); + header.insert(it->c_str(), Imf::M44fAttribute(Imath::M44f( + (float) val(0, 0), (float) val(0, 1), (float) val(0, 2), (float) val(0, 3), + (float) val(1, 0), (float) val(1, 1), (float) val(1, 2), (float) val(1, 3), + (float) val(2, 0), (float) val(2, 1), (float) val(2, 2), (float) val(2, 3), + (float) val(3, 0), (float) val(3, 1), (float) val(3, 2), (float) val(3, 3)))); + } + break; + default: + header.insert(it->c_str(), Imf::StringAttribute(metadata.getAsString(*it))); + break; + } + } if (pixelFormat == EXYZ || pixelFormat == EXYZA) { Imf::addChromaticities(header, Imf::Chromaticities( @@ -2363,14 +2421,16 @@ void Bitmap::writeRGBE(Stream *stream) const { Log(EError, "writeRGBE(): pixel format must be ERGB or ERGBA!"); stream->writeLine("#?RGBE"); - for (std::map::const_iterator it = m_metadata.begin(); - it != m_metadata.end(); ++it) { - stream->writeLine(formatString("# Metadata [%s]:", it->first.c_str())); - std::istringstream iss(it->second); + + std::vector keys = m_metadata.getPropertyNames(); + for (std::vector::const_iterator it = keys.begin(); it != keys.end(); ) { + stream->writeLine(formatString("# Metadata [%s]:", it->c_str())); + std::istringstream iss(m_metadata.getAsString(*it)); std::string buf; while (std::getline(iss, buf)) stream->writeLine(formatString("# %s", buf.c_str())); } + stream->writeLine("FORMAT=32-bit_rle_rgbe\n"); stream->writeLine(formatString("-Y %i +X %i", m_size.y, m_size.x)); diff --git a/src/libcore/properties.cpp b/src/libcore/properties.cpp index da93cf3c..b5922a88 100644 --- a/src/libcore/properties.cpp +++ b/src/libcore/properties.cpp @@ -45,7 +45,7 @@ struct PropertyElement { Type Properties::get##TypeName(const std::string &name) const { \ std::map::const_iterator it = m_elements->find(name); \ if (it == m_elements->end()) \ - SLog(EError, "Property \"%s\" missing", name.c_str()); \ + SLog(EError, "Property \"%s\" has not been specified!", name.c_str()); \ const BaseType *result = boost::get(&it->second.data); \ if (!result) \ SLog(EError, "The property \"%s\" has the wrong type (expected <" #ReadableName ">). The " \ @@ -78,18 +78,55 @@ DEFINE_PROPERTY_ACCESSOR(Spectrum, Spectrum, Spectrum, spectrum) DEFINE_PROPERTY_ACCESSOR(std::string, std::string, String, string) DEFINE_PROPERTY_ACCESSOR(Properties::Data, Properties::Data, Data, data) -class type_visitor : public boost::static_visitor { -public: - Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; } - Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; } - Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; } - Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; } - Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; } - Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; } - Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; } - Properties::EPropertyType operator()(const std::string &) const { return Properties::EString; } - Properties::EPropertyType operator()(const Properties::Data &) const { return Properties::EData; } -}; +namespace { + class TypeVisitor : public boost::static_visitor { + public: + Properties::EPropertyType operator()(const bool &) const { return Properties::EBoolean; } + Properties::EPropertyType operator()(const int64_t &) const { return Properties::EInteger; } + Properties::EPropertyType operator()(const Float &) const { return Properties::EFloat; } + Properties::EPropertyType operator()(const Point &) const { return Properties::EPoint; } + Properties::EPropertyType operator()(const Vector &) const { return Properties::EVector; } + Properties::EPropertyType operator()(const Transform &) const { return Properties::ETransform; } + Properties::EPropertyType operator()(const Spectrum &) const { return Properties::ESpectrum; } + Properties::EPropertyType operator()(const std::string &) const { return Properties::EString; } + Properties::EPropertyType operator()(const Properties::Data &) const { return Properties::EData; } + }; + + class EqualityVisitor : public boost::static_visitor { + public: + EqualityVisitor(const ElementData *ref) : ref(ref) { } + + bool operator()(const bool &v) const { const bool *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const int64_t &v) const { const int64_t *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const Float &v) const { const Float *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const Point &v) const { const Point *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const Vector &v) const { const Vector *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const Transform &v) const { const Transform *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const Spectrum &v) const { const Spectrum *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const std::string &v) const { const std::string *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + bool operator()(const Properties::Data &v) const { const Properties::Data *v2 = boost::get(ref); return v2 ? (v == *v2) : false; } + private: + const ElementData *ref; + }; + + class StringVisitor : public boost::static_visitor { + public: + StringVisitor(std::ostringstream &oss, bool quote) : oss(oss), quote(quote) { } + + void operator()(const bool &v) const { oss << (v ? "true" : "false"); } + void operator()(const int64_t &v) const { oss << v; } + void operator()(const Float &v) const { oss << v; } + void operator()(const Point &v) const { oss << v.toString(); } + void operator()(const Vector &v) const { oss << v.toString(); } + void operator()(const Transform &v) const { oss << v.toString(); } + void operator()(const Spectrum &v) const { oss << v.toString(); } + void operator()(const std::string &v) const { oss << (quote ? "\"" : "") << v << (quote ? "\"" : ""); } + void operator()(const Properties::Data &v) const { oss << v.ptr << " (size=" << v.size << ")"; } + private: + std::ostringstream &oss; + bool quote; + }; +} Properties::Properties() : m_id("unnamed") { @@ -145,13 +182,32 @@ Properties::EPropertyType Properties::getType(const std::string &name) const { if (it == m_elements->end()) SLog(EError, "Property \"%s\" has not been specified!", name.c_str()); - type_visitor myVisitor; - return boost::apply_visitor(myVisitor, it->second.data); + return boost::apply_visitor(TypeVisitor(), it->second.data); +} + +std::string Properties::getAsString(const std::string &name, const std::string &defVal) const { + if (m_elements->find(name) == m_elements->end()) + return defVal; + return getAsString(name); +} + +std::string Properties::getAsString(const std::string &name) const { + std::map::const_iterator it = m_elements->find(name); + if (it == m_elements->end()) + SLog(EError, "Property \"%s\" has not been specified!", name.c_str()); + + std::ostringstream oss; + StringVisitor strVisitor(oss, false); + boost::apply_visitor(strVisitor, it->second.data); + it->second.queried = true; + + return oss.str(); } std::string Properties::toString() const { std::map::const_iterator it = m_elements->begin(); std::ostringstream oss; + StringVisitor strVisitor(oss, true); oss << "Properties[" << endl << " pluginName = \"" << m_pluginName << "\"," << endl @@ -160,36 +216,7 @@ std::string Properties::toString() const { while (it != m_elements->end()) { oss << " \"" << (*it).first << "\" -> "; const ElementData &data = (*it).second.data; - EPropertyType type = boost::apply_visitor(type_visitor(), data); - switch (type) { - case EBoolean: - oss << (boost::get(data) ? "true" : "false"); - break; - case EInteger: - oss << boost::get(data); - break; - case EFloat: - oss << boost::get(data); - break; - case EPoint: - oss << boost::get(data).toString(); - break; - case ETransform: - oss << indent(boost::get(data).toString()); - break; - case ESpectrum: - oss << boost::get(data).toString(); - break; - case EString: - oss << "\"" << boost::get(data) << "\""; - break; - case EData: - oss << boost::get(data).ptr << " (size=" - << boost::get(data).size << ")"; - break; - default: - oss << ""; - } + boost::apply_visitor(strVisitor, data); if (++it != m_elements->end()) oss << ","; oss << endl; @@ -219,6 +246,21 @@ void Properties::putPropertyNames(std::vector &results) const { results.push_back((*it).first); } +bool Properties::operator==(const Properties &p) const { + if (m_pluginName != p.m_pluginName || m_id != p.m_id || m_elements->size() != p.m_elements->size()) + return false; + + std::map::const_iterator it = m_elements->begin(); + for (; it != m_elements->end(); ++it) { + const PropertyElement &first = it->second; + const PropertyElement &second = (*p.m_elements)[it->first]; + + if (!boost::apply_visitor(EqualityVisitor(&first.data), second.data)) + return false; + } + + return true; +} ConfigurableObject::ConfigurableObject(Stream *stream, InstanceManager *manager) : SerializableObject(stream, manager) { diff --git a/src/libpython/core.cpp b/src/libpython/core.cpp index 0e040329..f6c301c8 100644 --- a/src/libpython/core.cpp +++ b/src/libpython/core.cpp @@ -754,6 +754,7 @@ void export_core() { void (Bitmap::*accumulate_1)(const Bitmap *bitmap, Point2i sourceOffset, Point2i targetOffset, Vector2i size) = &Bitmap::accumulate; void (Bitmap::*accumulate_2)(const Bitmap *bitmap, Point2i targetOffset) = &Bitmap::accumulate; + const Properties &(Bitmap::*get_metadata)() const = &Bitmap::getMetadata; BP_CLASS(Bitmap, Object, (bp::init())) .def(bp::init()) @@ -769,8 +770,10 @@ void export_core() { .def("accumulate", accumulate_1) .def("accumulate", accumulate_2) .def("write", &bitmap_write) - .def("setString", &Bitmap::setString) - .def("getString", &Bitmap::getString, BP_RETURN_VALUE) + .def("setMetadataString", &Bitmap::setMetadataString) + .def("getMetadataString", &Bitmap::getMetadataString, BP_RETURN_VALUE) + .def("setMetadata", &Bitmap::setMetadata) + .def("getMetadata", get_metadata, BP_RETURN_VALUE) .def("setGamma", &Bitmap::setGamma) .def("getGamma", &Bitmap::getGamma) .def("getWidth", &Bitmap::getWidth)