diff --git a/src/preview.cpp b/src/preview.cpp index b4dede9a..6278d5bf 100644 --- a/src/preview.cpp +++ b/src/preview.cpp @@ -43,6 +43,7 @@ EXIV2_RCSID("@(#) $Id$") #include "image.hpp" #include "cr2image.hpp" +#include "tiffimage.hpp" // ***************************************************************************** namespace { @@ -151,11 +152,43 @@ namespace Exiv2 { Loader::AutoPtr createLoaderExifThumbC(PreviewId id, const Image &image, int parIdx); + //! Loader for Tiff previews + class LoaderTiff : public Loader { + public: + LoaderTiff(PreviewId id, const Image &image, int parIdx); + + virtual bool valid() const; + virtual PreviewProperties getProperties() const; + virtual DataBuf getData() const; + + protected: + const char *group_; + long length_; + bool valid_; + std::string offsetTag_; + std::string sizeTag_; + + // this table lists possible groups + // parIdx is an index to this table + + struct Param { + const char* group_; + }; + static const Param param_[]; + + + }; + + Loader::AutoPtr createLoaderTiff(PreviewId id, const Image &image, int parIdx); + // ***************************************************************************** // class member definitions const Loader::LoaderList Loader::loaderList_[] = { { NULL, createLoaderExifThumbC, 0}, + { NULL, createLoaderTiff, 0}, + { NULL, createLoaderTiff, 1}, + { NULL, createLoaderTiff, 2}, { NULL, createLoaderExifJpeg, 0}, { NULL, createLoaderExifJpeg, 1}, { NULL, createLoaderExifJpeg, 2}, @@ -171,6 +204,11 @@ namespace Exiv2 { { "Exif.Image.StripOffsets", "Exif.Image.StripByteCounts", }, // 4 }; + const LoaderTiff::Param LoaderTiff::param_[] = { + { "Image" }, // 0 + { "SubImage1" }, // 1 + { "SubImage2" }, // 2 + }; PreviewImage::PreviewImage(const PreviewProperties &properties, DataBuf &data) : properties_(properties), data_(data) @@ -341,6 +379,131 @@ namespace Exiv2 { return thumb_.copy(); } + LoaderTiff::LoaderTiff(PreviewId id, const Image &image, int parIdx) + : Loader(id, image), + group_(param_[parIdx].group_), + length_(0), valid_(false) + { + const ExifData &exifData = image_.exifData(); + + int offsetCount = 0; + + // check if the group_ contains a preview image + + ExifData::const_iterator pos = exifData.findKey(ExifKey(std::string("Exif.") + group_ + ".NewSubfileType")); + if (pos == image_.exifData().end() || pos->value().toLong() != 1) { + return; + } + + pos = exifData.findKey(ExifKey(std::string("Exif.") + group_ + ".StripOffsets")); + if (pos != image_.exifData().end()) { + offsetTag_ = "StripOffsets"; + sizeTag_ = "StripByteCounts"; + offsetCount = pos->value().count(); + } + else { + pos = exifData.findKey(ExifKey(std::string("Exif.") + group_ + ".TileOffsets")); + if (pos != image_.exifData().end()) { + offsetTag_ = "TileOffsets"; + sizeTag_ = "TileByteCounts"; + offsetCount = pos->value().count(); + } + else { + return; + } + } + + pos = exifData.findKey(ExifKey(std::string("Exif.") + group_ + '.' + sizeTag_)); + if (pos != image_.exifData().end()) { + if (offsetCount != pos->value().count()) return; + for (int i = 0; i < offsetCount; i++) + length_ += pos->value().toLong(i); + } + else return; + + if (length_ == 0) return; + + valid_ = true; + } + + Loader::AutoPtr createLoaderTiff(PreviewId id, const Image &image, int parIdx) + { + return Loader::AutoPtr(new LoaderTiff(id, image, parIdx)); + } + + bool LoaderTiff::valid() const + { + return valid_; + } + + PreviewProperties LoaderTiff::getProperties() const + { + PreviewProperties prop = Loader::getProperties(); + prop.length_ = length_; + prop.mimeType_ = "image/tiff"; + prop.extension_ = ".tif"; + return prop; + } + + DataBuf LoaderTiff::getData() const + { + const ExifData &exifData = image_.exifData(); + + ExifData preview; + + // copy tags + for (ExifData::const_iterator pos = exifData.begin(); pos != exifData.end(); ++pos) { + if (pos->groupName() == group_) { + if (pos->tagName() == "NewSubfileType") + continue; + + preview.add(ExifKey("Exif.Image." + pos->tagName()), &pos->value()); + } + } + + // read image data + BasicIo &io = image_.io(); + + if (io.open() != 0) { + throw Error(9, io.path(), strError()); + } + IoCloser closer(io); + + const byte *base = io.mmap(); + + Value &dataValue = const_cast(preview["Exif.Image." + offsetTag_].value()); + const Value &sizes = preview["Exif.Image." + sizeTag_].value(); + + if (sizes.count() == 1) { + // this saves one copying of the buffer + uint32_t offset = dataValue.toLong(0); + uint32_t length = sizes.toLong(0); + if (offset + length <= io.size()) + dataValue.setDataArea(base + offset, length); + } + else { + // FIXME: the buffer is probably copied twice, it should be optimized + DataBuf buf(length_); + byte *pos = buf.pData_; + for (int i = 0; i < sizes.count(); i++) { + uint32_t offset = dataValue.toLong(i); + uint32_t length = sizes.toLong(i); + if (offset + length <= io.size()) + memcpy(pos, base + offset, length); + pos += length; + } + dataValue.setDataArea(buf.pData_, buf.size_); + } + + // write new image + Blob blob; + const IptcData emptyIptc; + const XmpData emptyXmp; + TiffParser::encode(blob, 0, 0, Exiv2::littleEndian, preview, emptyIptc, emptyXmp); + return DataBuf(&blob[0], static_cast(blob.size())); + } + + PreviewImageLoader::PreviewImageLoader(const Image& image) : image_(image) {