From 0e0845d7beac40b9cdf2a3017108e4880c6c9ee7 Mon Sep 17 00:00:00 2001 From: Andreas Huggel Date: Wed, 21 Mar 2012 00:53:32 +0000 Subject: [PATCH] #635: Added experimental write-support for CR2. --- src/cr2image.cpp | 121 ++++++++++++++++++++++++++------------ src/cr2image.hpp | 17 +----- src/cr2image_int.hpp | 5 +- src/exif.cpp | 6 +- src/orfimage.cpp | 3 +- src/tiffcomposite.cpp | 20 ++++++- src/tiffcomposite_int.hpp | 7 ++- src/tifffwd_int.hpp | 1 + src/tiffimage.cpp | 30 +++++++++- src/tiffimage_int.hpp | 59 ++++++++++++++++++- 10 files changed, 203 insertions(+), 66 deletions(-) diff --git a/src/cr2image.cpp b/src/cr2image.cpp index 8ca56b6a..e394d56d 100644 --- a/src/cr2image.cpp +++ b/src/cr2image.cpp @@ -86,18 +86,6 @@ namespace Exiv2 { return 0; } - void Cr2Image::setExifData(const ExifData& /*exifData*/) - { - // Todo: implement me! - throw(Error(32, "Exif metadata", "CR2")); - } - - void Cr2Image::setIptcData(const IptcData& /*iptcData*/) - { - // Todo: implement me! - throw(Error(32, "IPTC metadata", "CR2")); - } - void Cr2Image::setComment(const std::string& /*comment*/) { // not supported @@ -129,8 +117,29 @@ namespace Exiv2 { void Cr2Image::writeMetadata() { - // Todo: implement me! - throw(Error(31, "CR2")); +#ifdef DEBUG + std::cerr << "Writing CR2 file " << io_->path() << "\n"; +#endif + ByteOrder bo = byteOrder(); + byte* pData = 0; + long size = 0; + IoCloser closer(*io_); + if (io_->open() == 0) { + // Ensure that this is the correct image type + if (isCr2Type(*io_, false)) { + pData = io_->mmap(true); + size = io_->size(); + Cr2Header cr2Header; + if (0 == cr2Header.read(pData, 16)) { + bo = cr2Header.byteOrder(); + } + } + } + if (bo == invalidByteOrder) { + bo = littleEndian; + } + setByteOrder(bo); + Cr2Parser::encode(*io_, pData, size, bo, exifData_, iptcData_, xmpData_); // may throw } // Cr2Image::writeMetadata ByteOrder Cr2Parser::decode( @@ -153,27 +162,45 @@ namespace Exiv2 { } WriteMethod Cr2Parser::encode( - Blob& blob, - const byte* /*pData*/, - uint32_t /*size*/, - const ExifData& /*exifData*/, - const IptcData& /*iptcData*/, - const XmpData& /*xmpData*/ + BasicIo& io, + const byte* pData, + uint32_t size, + ByteOrder byteOrder, + const ExifData& exifData, + const IptcData& iptcData, + const XmpData& xmpData ) { - /* Todo: Implement me! - - TiffParserWorker::encode(blob, - pData, - size, - exifData, - iptcData, - xmpData, - TiffCreator::create, - TiffMapping::findEncoder); - */ - blob.clear(); - return wmIntrusive; + // Copy to be able to modify the Exif data + ExifData ed = exifData; + + // Delete IFDs which do not occur in TIFF images + static const IfdId filteredIfds[] = { + panaRawId + }; + for (unsigned int i = 0; i < EXV_COUNTOF(filteredIfds); ++i) { +#ifdef DEBUG + std::cerr << "Warning: Exif IFD " << filteredIfds[i] << " not encoded\n"; +#endif + ed.erase(std::remove_if(ed.begin(), + ed.end(), + FindExifdatum(filteredIfds[i])), + ed.end()); + } + + std::auto_ptr header(new Cr2Header(byteOrder)); + OffsetWriter offsetWriter; + offsetWriter.setOrigin(OffsetWriter::cr2RawIfdOffset, Cr2Header::offset2addr(), byteOrder); + return TiffParserWorker::encode(io, + pData, + size, + ed, + iptcData, + xmpData, + Tag::root, + TiffMapping::findEncoder, + header.get(), + &offsetWriter); } // ************************************************************************* @@ -210,8 +237,8 @@ namespace Exiv2 { const char* Cr2Header::cr2sig_ = "CR\2\0"; - Cr2Header::Cr2Header() - : TiffHeaderBase(42, 16, littleEndian, 0x00000010), + Cr2Header::Cr2Header(ByteOrder byteOrder) + : TiffHeaderBase(42, 16, byteOrder, 0x00000010), offset2_(0x00000000) { } @@ -243,8 +270,26 @@ namespace Exiv2 { DataBuf Cr2Header::write() const { - // Todo: Implement me! - return DataBuf(); - } + DataBuf buf(16); + switch (byteOrder()) { + case littleEndian: + buf.pData_[0] = 0x49; + buf.pData_[1] = 0x49; + break; + case bigEndian: + buf.pData_[0] = 0x4d; + buf.pData_[1] = 0x4d; + break; + case invalidByteOrder: + assert(false); + break; + } + us2Data(buf.pData_ + 2, tag(), byteOrder()); + ul2Data(buf.pData_ + 4, 0x00000010, byteOrder()); + memcpy(buf.pData_ + 8, cr2sig_, 4); + // Write a dummy value for the RAW IFD offset. The offset-writer is used to set this offset in a second pass. + ul2Data(buf.pData_ + 12, 0x00000000, byteOrder()); + return buf; + } // Cr2Header::write }} // namespace Internal, Exiv2 diff --git a/src/cr2image.hpp b/src/cr2image.hpp index 00e0c391..ebc309db 100644 --- a/src/cr2image.hpp +++ b/src/cr2image.hpp @@ -79,21 +79,7 @@ namespace Exiv2 { //! @name Manipulators //@{ void readMetadata(); - /*! - @brief Todo: Write metadata back to the image. This method is not - yet implemented. Calling it will throw an Error(31). - */ void writeMetadata(); - /*! - @brief Todo: Not supported yet, requires writeMetadata(). Calling - this function will throw an Error(32). - */ - void setExifData(const ExifData& exifData); - /*! - @brief Todo: Not supported yet, requires writeMetadata(). Calling - this function will throw an Error(32). - */ - void setIptcData(const IptcData& iptcData); /*! @brief Not supported. CR2 format does not contain a comment. Calling this function will throw an Error(32). @@ -143,9 +129,10 @@ namespace Exiv2 { See TiffParser::encode(). */ static WriteMethod encode( - Blob& blob, + BasicIo& io, const byte* pData, uint32_t size, + ByteOrder byteOrder, const ExifData& exifData, const IptcData& iptcData, const XmpData& xmpData diff --git a/src/cr2image_int.hpp b/src/cr2image_int.hpp index 7615d928..f9c3a1f7 100644 --- a/src/cr2image_int.hpp +++ b/src/cr2image_int.hpp @@ -53,7 +53,7 @@ namespace Exiv2 { //! @name Creators //@{ //! Default constructor - Cr2Header(); + Cr2Header(ByteOrder byteOrder =littleEndian); //! Destructor. ~Cr2Header(); //@} @@ -68,6 +68,9 @@ namespace Exiv2 { DataBuf write() const; //@} + // Return the address of offset2 from the start of the header + static uint32_t offset2addr() { return 12; } + private: // DATA uint32_t offset2_; //!< Bytes 12-15 from the header diff --git a/src/exif.cpp b/src/exif.cpp index 6a67559d..8c6bde4e 100644 --- a/src/exif.cpp +++ b/src/exif.cpp @@ -720,7 +720,8 @@ namespace Exiv2 { emptyXmp, Tag::root, TiffMapping::findEncoder, - header.get()); + header.get(), + 0); if (mio1.size() <= 65527) { append(blob, mio1.mmap(), mio1.size()); return wm; @@ -817,7 +818,8 @@ namespace Exiv2 { emptyXmp, Tag::root, TiffMapping::findEncoder, - header.get()); + header.get(), + 0); append(blob, mio2.mmap(), mio2.size()); #ifdef DEBUG if (wm == wmIntrusive) { diff --git a/src/orfimage.cpp b/src/orfimage.cpp index ac055567..b7f7b5eb 100644 --- a/src/orfimage.cpp +++ b/src/orfimage.cpp @@ -197,7 +197,8 @@ namespace Exiv2 { xmpData, Tag::root, TiffMapping::findEncoder, - header.get()); + header.get(), + 0); } // ************************************************************************* diff --git a/src/tiffcomposite.cpp b/src/tiffcomposite.cpp index 250994f7..7c2e7f2d 100644 --- a/src/tiffcomposite.cpp +++ b/src/tiffcomposite.cpp @@ -69,8 +69,8 @@ namespace Exiv2 { && key.g_ == group_; } - IoWrapper::IoWrapper(BasicIo& io, const byte* pHeader, long size) - : io_(io), pHeader_(pHeader), size_(size), wroteHeader_(false) + IoWrapper::IoWrapper(BasicIo& io, const byte* pHeader, long size, OffsetWriter* pow) + : io_(io), pHeader_(pHeader), size_(size), wroteHeader_(false), pow_(pow) { if (pHeader_ == 0 || size_ == 0) wroteHeader_ = true; } @@ -93,6 +93,11 @@ namespace Exiv2 { return io_.putb(data); } + void IoWrapper::setTarget(int id, uint32_t target) + { + if (pow_) pow_->setTarget(OffsetWriter::OffsetId(id), target); + } + TiffComponent::TiffComponent(uint16_t tag, IfdId group) : tag_(tag), group_(group), pStart_(0) { @@ -1095,6 +1100,15 @@ namespace Exiv2 { // Nothing to do if there are no entries and the size of the next IFD is 0 if (compCount == 0 && sizeNext == 0) return 0; + // Remember the offset of the CR2 RAW IFD + if (group() == ifd3Id) { +#ifdef DEBUG + std::cerr << "Directory " << groupName(group()) << " offset is 0x" + << std::setw(8) << std::setfill('0') << std::hex << offset << std::dec + << "\n"; +#endif + ioWrapper.setTarget(OffsetWriter::cr2RawIfdOffset, offset); + } // Size of all directory entries, without values and additional data const uint32_t sizeDir = 2 + 12 * compCount + (hasNext_ ? 4 : 0); @@ -1392,7 +1406,7 @@ namespace Exiv2 { std::sort(elements_.begin(), elements_.end(), cmpTagLt); uint32_t idx = 0; MemIo mio; - IoWrapper mioWrapper(mio, 0, 0); + IoWrapper mioWrapper(mio, 0, 0, 0); // Some array entries need to have the size in the first element if (cfg()->hasSize_) { byte buf[4]; diff --git a/src/tiffcomposite_int.hpp b/src/tiffcomposite_int.hpp index 052c3ee7..7389199e 100644 --- a/src/tiffcomposite_int.hpp +++ b/src/tiffcomposite_int.hpp @@ -130,10 +130,10 @@ namespace Exiv2 { /*! brief Constructor. - The IO wrapper owns neither of the objects passed in so the caller is + The IO wrapper owns none of the objects passed in so the caller is responsible to keep them alive. */ - IoWrapper(BasicIo& io, const byte* pHeader, long size); + IoWrapper(BasicIo& io, const byte* pHeader, long size, OffsetWriter* pow); //@} //! @name Manipulators @@ -152,6 +152,8 @@ namespace Exiv2 { by the data passed in the argument. */ int putb(byte data); + //! Wrapper for OffsetWriter::setTarget(), using an int instead of the enum to reduce include deps + void setTarget(int id, uint32_t target); //@} private: @@ -160,6 +162,7 @@ namespace Exiv2 { const byte* pHeader_; //! Pointer to the header data. long size_; //! Size of the header data. bool wroteHeader_; //! Indicates if the header has been written. + OffsetWriter* pow_; //! Pointer to an offset-writer, if any, or 0 }; // class IoWrapper /*! diff --git a/src/tifffwd_int.hpp b/src/tifffwd_int.hpp index d3eacc1f..9370977c 100644 --- a/src/tifffwd_int.hpp +++ b/src/tifffwd_int.hpp @@ -73,6 +73,7 @@ namespace Exiv2 { struct TiffMappingInfo; class IoWrapper; + class OffsetWriter; // ***************************************************************************** // type definitions diff --git a/src/tiffimage.cpp b/src/tiffimage.cpp index 826fa398..4e9ab361 100644 --- a/src/tiffimage.cpp +++ b/src/tiffimage.cpp @@ -273,7 +273,8 @@ namespace Exiv2 { xmpData, Tag::root, TiffMapping::findEncoder, - header.get()); + header.get(), + 0); } // TiffParser::encode // ************************************************************************* @@ -1840,7 +1841,8 @@ namespace Exiv2 { const XmpData& xmpData, uint32_t root, FindEncoderFct findEncoderFct, - TiffHeaderBase* pHeader + TiffHeaderBase* pHeader, + OffsetWriter* pOffsetWriter ) { /* @@ -1890,7 +1892,7 @@ namespace Exiv2 { DataBuf header = pHeader->write(); BasicIo::AutoPtr tempIo(io.temporary()); // may throw assert(tempIo.get() != 0); - IoWrapper ioWrapper(*tempIo, header.pData_, header.size_); + IoWrapper ioWrapper(*tempIo, header.pData_, header.size_, pOffsetWriter); uint32_t imageIdx(uint32_t(-1)); createdTree->write(ioWrapper, pHeader->byteOrder(), @@ -1898,6 +1900,7 @@ namespace Exiv2 { uint32_t(-1), uint32_t(-1), imageIdx); + if (pOffsetWriter) pOffsetWriter->writeOffsets(*tempIo); io.transfer(*tempIo); // may throw #ifndef SUPPRESS_WARNINGS EXV_INFO << "Write strategy: Intrusive\n"; @@ -2202,6 +2205,27 @@ namespace Exiv2 { std::cerr << "Not an image tag: " << key << " (4)\n"; #endif return false; + } // TiffHeader::isImageTag + + void OffsetWriter::setOrigin(OffsetId id, uint32_t origin, ByteOrder byteOrder) + { + offsetList_[id] = OffsetData(origin, byteOrder); + } + + void OffsetWriter::setTarget(OffsetId id, uint32_t target) + { + OffsetList::iterator it = offsetList_.find(id); + if (it != offsetList_.end()) it->second.target_ = target; + } + + void OffsetWriter::writeOffsets(BasicIo& io) const + { + for (OffsetList::const_iterator it = offsetList_.begin(); it != offsetList_.end(); ++it) { + io.seek(it->second.origin_, BasicIo::beg); + byte buf[4] = { 0, 0, 0, 0 }; + l2Data(buf, it->second.target_, it->second.byteOrder_); + io.write(buf, 4); + } } }} // namespace Internal, Exiv2 diff --git a/src/tiffimage_int.hpp b/src/tiffimage_int.hpp index 83633c4d..45752884 100644 --- a/src/tiffimage_int.hpp +++ b/src/tiffimage_int.hpp @@ -38,6 +38,8 @@ #include "types.hpp" // + standard includes +#include +#include // ***************************************************************************** // namespace extensions @@ -328,7 +330,8 @@ namespace Exiv2 { const XmpData& xmpData, uint32_t root, FindEncoderFct findEncoderFct, - TiffHeaderBase* pHeader + TiffHeaderBase* pHeader, + OffsetWriter* pOffsetWriter ); private: @@ -413,6 +416,60 @@ namespace Exiv2 { }; // class TiffMapping + /*! + @brief Class to insert pointers or offsets to computed addresses at + specific locations in an image. Used for offsets which are + best computed during the regular write process. They are + written in a second pass, using the writeOffsets() method. + */ + class OffsetWriter { + public: + //! Identifiers for supported offsets + enum OffsetId { + cr2RawIfdOffset //!< CR2 RAW IFD offset, a pointer in the CR2 header to the 4th IFD in a CR2 image + }; + //! @name Manipulators + //@{ + /*! + @brief Set the \em origin of the offset for \em id, i.e., the location in the image where the offset is, + and the byte order to encode the offset. + + If the list doesn't contain an entry for \em id yet, this function will create one. + */ + void setOrigin(OffsetId id, uint32_t origin, ByteOrder byteOrder); + /*! + @brief Set the \em target for offset \em id, i.e., the address to which the offset points. + + If the list doesn't contain an entry with \em id yet, this function won't do anything. + */ + void setTarget(OffsetId id, uint32_t target); + //@} + + //! @name Accessors + //@{ + //! Write the offsets to the IO instance \em io. + void writeOffsets(BasicIo& io) const; + //@} + private: + //! Data structure for the offset list. + struct OffsetData { + //! Default constructor + OffsetData() : origin_(0), target_(0), byteOrder_(littleEndian) {} + //! Constructor + OffsetData(uint32_t origin, ByteOrder byteOrder) : origin_(origin), target_(0), byteOrder_(byteOrder) {} + // DATA + uint32_t origin_; //!< Origin address + uint32_t target_; //!< Target address + ByteOrder byteOrder_; //!< Byte order to use to encode target address + }; + //! Type of the list containing an identifier and an address pair. + typedef std::map OffsetList; + + // DATA + OffsetList offsetList_; //!< List of the offsets to replace + + }; // class OffsetWriter + // Todo: Move this class to metadatum_int.hpp or tags_int.hpp //! Unary predicate that matches an Exifdatum with a given IfdId. class FindExifdatum {