From 26b488a2553c778c320e3cadc0f3100be77f1ee1 Mon Sep 17 00:00:00 2001 From: Andreas Huggel Date: Thu, 18 Sep 2008 10:26:58 +0000 Subject: [PATCH] Added preview support (Vladimir Nadvornik) --- samples/Makefile | 1 + samples/prevtest.cpp | 42 +++++ src/Makefile | 3 +- src/preview.cpp | 362 +++++++++++++++++++++++++++++++++++++++++++ src/preview.hpp | 156 +++++++++++++++++++ 5 files changed, 563 insertions(+), 1 deletion(-) create mode 100644 samples/prevtest.cpp create mode 100644 src/preview.cpp create mode 100644 src/preview.hpp diff --git a/samples/Makefile b/samples/Makefile index 83ba239d..f9202c67 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -65,6 +65,7 @@ BINSRC = addmoddel.cpp \ iptctest.cpp \ key-test.cpp \ largeiptc-test.cpp \ + prevtest.cpp \ stringto-test.cpp \ tiff-test.cpp \ write-test.cpp \ diff --git a/samples/prevtest.cpp b/samples/prevtest.cpp new file mode 100644 index 00000000..c9afb96c --- /dev/null +++ b/samples/prevtest.cpp @@ -0,0 +1,42 @@ +// ***************************************************************** -*- C++ -*- +// prevtest.cpp, $Rev$ +// Test access to preview images + +#include +#include + +#include +#include +#include + +int main(int argc, char* const argv[]) +try { + + if (argc != 2) { + std::cout << "Usage: " << argv[0] << " file\n"; + return 1; + } + + Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]); + assert(image.get() != 0); + image->readMetadata(); + + Exiv2::PreviewImageLoader loader(*image); + + Exiv2::PreviewPropertiesList list = loader.getPreviewPropertiesList(); + + for (Exiv2::PreviewPropertiesList::iterator pos = list.begin(); pos != list.end(); pos++) + { + char buf[50]; + Exiv2::PreviewImage image = loader.getPreviewImage(*pos); + sprintf(buf, "%ld", image.length()); + image.writeFile(std::string(argv[1]) + "_" + buf); + std::cout << "found preview " << pos->id_ << ", length: " << pos->length_ << "\n"; + } + + return 0; +} +catch (Exiv2::AnyError& e) { + std::cout << "Caught Exiv2 exception '" << e << "'\n"; + return -1; +} diff --git a/src/Makefile b/src/Makefile index 51cd380a..fe928a8d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -94,7 +94,8 @@ ifdef HAVE_LIBZ CCSRC += pngimage.cpp \ pngchunk.cpp endif -CCSRC += properties.cpp \ +CCSRC += preview.cpp \ + properties.cpp \ psdimage.cpp \ rafimage.cpp \ sigmamn.cpp \ diff --git a/src/preview.cpp b/src/preview.cpp new file mode 100644 index 00000000..419d9518 --- /dev/null +++ b/src/preview.cpp @@ -0,0 +1,362 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2008 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/* + File: preview.cpp + Version: $Rev$ + Author(s): Vladimir Nadvornik (vn) + History: 18-Sep-08, vn: created + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id$") + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include + +#include "preview.hpp" +#include "futils.hpp" + +#include "image.hpp" +#include "cr2image.hpp" + + +namespace Exiv2 { + + /*! + Base class for image loaders. Provides virtual methods for reading properties + and DataBuf. + */ + class Loader { + protected: + Loader(PreviewProperties::PreviewId id, const Image &image); + + public: + typedef std::auto_ptr AutoPtr; + + //! Create a Loader subclass for requested id + static Loader::AutoPtr create(PreviewProperties::PreviewId id, const Image &image); + + //! Check if a preview image with given params exists in the image + virtual bool valid() const = 0; + + //! Get properties of a preview image with given params + virtual PreviewProperties getProperties() const; + + //! Get properties of a preview image with given params + virtual DataBuf getData() const = 0; + + //! A number of image loaders configured in the loaderList_ table + static PreviewProperties::PreviewId getNumLoaders(); + + protected: + typedef AutoPtr (*CreateFunc)(PreviewProperties::PreviewId id, const Image &image, int parIdx); + + struct LoaderList { + const char *imageMimeType_; //!< Image type for which is the loader valid, NULL matches all images + CreateFunc create_; //!< Function that creates particular loader instance + int parIdx_; //!< Parameter that is passed into CreateFunc + + }; + + static const LoaderList loaderList_[]; // PreviewId is an index to this table + + PreviewProperties::PreviewId id_; + const Image &image_; + }; + + + //! Loader for Jpeg previews that are not read into ExifData directly + class LoaderExifJpeg : public Loader { + public: + LoaderExifJpeg(PreviewProperties::PreviewId id, const Image &image, int parIdx); + + virtual bool valid() const; + virtual PreviewProperties getProperties() const; + virtual DataBuf getData() const; + + long getOffset() const; + long getLength() const; + + protected: + + // this table lists possible offset/length key pairs + // parIdx is an index to this table + + struct Param { + const char* offsetKey_; + const char* lengthKey_; + }; + static const Param param_[]; + + ExifKey offsetKey_; + ExifKey lengthKey_; + }; + + Loader::AutoPtr createLoaderExifJpeg(PreviewProperties::PreviewId id, const Image &image, int parIdx); + + + //! Loader for standard Exif thumbnail - just a wrapper around ExifThumbC + class LoaderExifThumbC : public Loader { + public: + LoaderExifThumbC(PreviewProperties::PreviewId id, const Image &image); + + virtual bool valid() const; + virtual PreviewProperties getProperties() const; + virtual DataBuf getData() const; + + protected: + ExifThumbC thumb_; + }; + + Loader::AutoPtr createLoaderExifThumbC(PreviewProperties::PreviewId id, const Image &image, int parIdx); + +// ***************************************************************************** +// class member definitions + + const Loader::LoaderList Loader::loaderList_[] = { + { NULL, createLoaderExifThumbC, 0}, + { NULL, createLoaderExifJpeg, 0}, + { NULL, createLoaderExifJpeg, 1}, + { NULL, createLoaderExifJpeg, 2}, + { NULL, createLoaderExifJpeg, 3}, + { "image/x-canon-cr2", createLoaderExifJpeg, 4} // FIXME: this needs to be fixed (enabled) in cr2image.cpp + }; + + const LoaderExifJpeg::Param LoaderExifJpeg::param_[] = { + { "Exif.Image.JPEGInterchangeFormat", "Exif.Image.JPEGInterchangeFormatLength" }, // 0 + { "Exif.SubImage1.JPEGInterchangeFormat", "Exif.SubImage1.JPEGInterchangeFormatLength", }, // 1 + { "Exif.SubImage2.JPEGInterchangeFormat", "Exif.SubImage2.JPEGInterchangeFormatLength", }, // 2 + { "Exif.Image2.JPEGInterchangeFormat", "Exif.Image2.JPEGInterchangeFormatLength", }, // 3 + { "Exif.Image.StripOffsets", "Exif.Image.StripByteCounts", }, // 4 + }; + + + PreviewImage::PreviewImage(const PreviewProperties &properties, DataBuf &data) + : properties_(properties), data_(data) + { + } + + PreviewImage::PreviewImage(const PreviewImage &src) + : properties_(src.properties_), data_(const_cast(src.data_)) + { + } + + + long PreviewImage::writeFile(const std::string& path) const + { + std::string name = path + extension(); + return Exiv2::writeFile(data_, name); + } + + DataBuf &PreviewImage::data() + { + return data_; + } + + const char* PreviewImage::mimeType() const + { + return properties_.mimeType_; + } + + const char* PreviewImage::extension() const + { + return properties_.extension_; + } + + long PreviewImage::length() const + { + return properties_.length_; + } + + Loader::AutoPtr Loader::create(PreviewProperties::PreviewId id, const Image &image) + { + if (id < 0 || id >= Loader::getNumLoaders()) + return AutoPtr(); + + if (loaderList_[id].imageMimeType_ && + std::string(loaderList_[id].imageMimeType_) != std::string(image.mimeType())) + return AutoPtr(); + + AutoPtr loader = loaderList_[id].create_(id, image, loaderList_[id].parIdx_); + + if (loader.get() && !loader->valid()) loader.reset(); + return loader; + } + + Loader::Loader(PreviewProperties::PreviewId id, const Image &image) + : id_(id), image_(image) + { + } + + PreviewProperties Loader::getProperties() const + { + PreviewProperties prop; + prop.id_ = id_; + return prop; + } + + PreviewProperties::PreviewId Loader::getNumLoaders() + { + return (PreviewProperties::PreviewId)EXV_COUNTOF(loaderList_); + } + + LoaderExifJpeg::LoaderExifJpeg(PreviewProperties::PreviewId id, const Image &image, int parIdx) + : Loader(id, image), + offsetKey_(param_[parIdx].offsetKey_), + lengthKey_(param_[parIdx].lengthKey_) + { + } + + Loader::AutoPtr createLoaderExifJpeg(PreviewProperties::PreviewId id, const Image &image, int parIdx) + { + return Loader::AutoPtr(new LoaderExifJpeg(id, image, parIdx)); + } + + long LoaderExifJpeg::getLength() const + { + long length = 0; + ExifData::const_iterator pos = image_.exifData().findKey(lengthKey_); + if (pos != image_.exifData().end()) { + length = pos->toLong(); + } + return length; + } + + long LoaderExifJpeg::getOffset() const + { + long offset = 0; + ExifData::const_iterator pos = image_.exifData().findKey(offsetKey_); + if (pos != image_.exifData().end()) { + offset = pos->toLong(); + } + return offset; + } + + bool LoaderExifJpeg::valid() const + { + long offset = getOffset(); + long length = getLength(); + + if (offset == 0 || length == 0) return false; + if (offset + length > image_.io().size()) return false; + return true; + } + + PreviewProperties LoaderExifJpeg::getProperties() const + { + PreviewProperties prop = Loader::getProperties(); + prop.mimeType_ = "image/jpeg"; + prop.extension_ = ".jpg"; + prop.length_ = getLength(); + return prop; + } + + DataBuf LoaderExifJpeg::getData() const + { + if (!valid()) return DataBuf(); + BasicIo &io = image_.io(); + + if (io.open() != 0) { + throw Error(9, io.path(), strError()); + } + IoCloser closer(io); + + long offset = getOffset(); + long length = getLength(); + + const byte *base = io.mmap(); + + return DataBuf(base + offset, length); + } + + + LoaderExifThumbC::LoaderExifThumbC(PreviewProperties::PreviewId id, const Image &image) + : Loader(id, image), + thumb_(image_.exifData()) + { + } + + Loader::AutoPtr createLoaderExifThumbC(PreviewProperties::PreviewId id, const Image &image, int /* parIdx */) + { + return Loader::AutoPtr(new LoaderExifThumbC(id, image)); + } + + bool LoaderExifThumbC::valid() const + { + return thumb_.copy().size_ > 0; // FIXME: this is inefficient + } + + PreviewProperties LoaderExifThumbC::getProperties() const + { + PreviewProperties prop = Loader::getProperties(); + prop.length_ = thumb_.copy().size_; // FIXME: this is inefficient + prop.mimeType_ = thumb_.mimeType(); + prop.extension_ = thumb_.extension(); + return prop; + } + + DataBuf LoaderExifThumbC::getData() const + { + return thumb_.copy(); + } + + bool cmpPreviewProperties(const PreviewProperties& lhs, const PreviewProperties& rhs) + { + return lhs.length_ < rhs.length_; + } + + PreviewImageLoader::PreviewImageLoader(const Image& image) + : image_(image) + { + } + + PreviewPropertiesList PreviewImageLoader::getPreviewPropertiesList() const + { + PreviewPropertiesList list; + // go through the loader table and store all successfuly created loaders in the list + for (PreviewProperties::PreviewId id = 0; id < Loader::getNumLoaders(); id++) { + Loader::AutoPtr loader = Loader::create(id, image_); + if (loader.get()) { + list.push_back(loader->getProperties()); + } + } + std::sort(list.begin(), list.end(), cmpPreviewProperties); + return list; + } + + PreviewImage PreviewImageLoader::getPreviewImage(const PreviewProperties &properties) const + { + Loader::AutoPtr loader = Loader::create(properties.id_, image_); + DataBuf buf; + if (loader.get()) { + buf = loader->getData(); + } + return PreviewImage(properties, buf); + } + +} // namespace Exiv2 diff --git a/src/preview.hpp b/src/preview.hpp new file mode 100644 index 00000000..13fedf7e --- /dev/null +++ b/src/preview.hpp @@ -0,0 +1,156 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2008 Andreas Huggel + * + * This program is part of the Exiv2 distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. + */ +/*! + @file preview.hpp + @brief Classes to access preview images embedded in an image. + @version $Rev$ + @author Vladimir Nadvornik (vn) + nadvornik@suse.cz + @date 18-Sep-08, vn: created + */ +#ifndef PREVIEW_HPP_ +#define PREVIEW_HPP_ + +// ***************************************************************************** +// included header files +#include "types.hpp" +#include "image.hpp" +#include "basicio.hpp" + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 { + +// ***************************************************************************** +// class definitions + + /*! + @brief Preview image properties. + */ + struct PreviewProperties + { + public: + //! type of preview image. + typedef int PreviewId; + + //! Preview image mime type. + const char* mimeType_; + + //! Preview image extension. + const char* extension_; + + //! Preview image length in bytes. + long length_; + + //! identifies type of preview image. + PreviewId id_; + }; + + //! Container type to hold all preview images metadata. + typedef std::vector PreviewPropertiesList; + + /*! + @brief Compare two preview images by length. Return true if the + lhs is smaller than rhs. + */ + bool cmpPreviewProperties(const PreviewProperties& lhs, const PreviewProperties& rhs); + + /*! + @brief Class that holds preview image properties and data buffer. + */ + class PreviewImage { + public: + //@{ + //! Constructor. + PreviewImage(const PreviewProperties &properties, DataBuf &data); + + //! Copy constructor + PreviewImage(const PreviewImage &src); + //@} + + //! @name Accessors + //@{ + + /*! + @brief Return a reference to image data. + */ + DataBuf &data(); + + /*! + @brief Write the thumbnail image to a file. + + A filename extension is appended to \em path according to the image + type of the thumbnail, so \em path should not include an extension. + The function will overwrite an existing file of the same name. + + @param path File name of the thumbnail without extension. + @return The number of bytes written. + */ + long writeFile(const std::string& path) const; + /*! + @brief Return the MIME type of the thumbnail, either \c "image/tiff" + or \c "image/jpeg". + */ + const char* mimeType() const; + /*! + @brief Return the file extension for the format of the thumbnail + (".tif" or ".jpg"). + */ + const char* extension() const; + + /*! + @brief Return the thumbnail length in bytes. + */ + long length() const; + //@} + + private: + PreviewProperties properties_; + DataBuf data_; + }; // class PreviewImage + + /*! + @brief Class for extracting preview images from image metadata. + */ + class PreviewImageLoader { + public: + //@{ + //! Constructor. + PreviewImageLoader(const Image& image); + + /*! + @brief Return list of preview properties. + */ + PreviewPropertiesList getPreviewPropertiesList() const; + /*! + @brief Return image data for given properties. + */ + PreviewImage getPreviewImage(const PreviewProperties &properties) const; + + //@} + + private: + const Image &image_; + + }; // class PreviewImageLoader +} // namespace Exiv2 + +#endif // #ifndef PREVIEW_HPP_