From 876eb87515c351cd97b22745475df77a5436cd58 Mon Sep 17 00:00:00 2001 From: HumanDynamo Date: Thu, 18 Jun 2009 06:43:50 +0000 Subject: [PATCH] Add PGF image format codec. Read and Write metadata is supported. PGF is an image format based on Wavelet compression. Metadata are hosted in header of file into an unique byte array. Metadata are saved into this area using a blank PNG file as container. Comments, EXIF, IPTC, and XMP are supported. --- src/Makefile | 3 +- src/image.cpp | 2 + src/pgfimage.cpp | 306 +++++++++++++++++++++++++++++++++++++++++++++++ src/pgfimage.hpp | 138 +++++++++++++++++++++ 4 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 src/pgfimage.cpp create mode 100644 src/pgfimage.hpp diff --git a/src/Makefile b/src/Makefile index 173ec269..bb323d87 100644 --- a/src/Makefile +++ b/src/Makefile @@ -90,7 +90,8 @@ CCSRC = basicio.cpp \ nikonmn.cpp \ olympusmn.cpp \ orfimage.cpp \ - panasonicmn.cpp + panasonicmn.cpp \ + pgfimage.cpp ifdef HAVE_LIBZ CCSRC += pngimage.cpp \ pngchunk.cpp diff --git a/src/image.cpp b/src/image.cpp index 24056a78..61f5844b 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -60,6 +60,7 @@ EXIV2_RCSID("@(#) $Id$") #include "bmpimage.hpp" #include "jp2image.hpp" #include "rw2image.hpp" +#include "pgfimage.hpp" #include "xmpsidecar.hpp" // + standard includes @@ -117,6 +118,7 @@ namespace { { ImageType::bmp, newBmpInstance, isBmpType, amNone, amNone, amNone, amNone }, { ImageType::jp2, newJp2Instance, isJp2Type, amReadWrite, amReadWrite, amReadWrite, amNone }, { ImageType::rw2, newRw2Instance, isRw2Type, amRead, amRead, amRead, amNone }, + { ImageType::pgf, newPgfInstance, isPgfType, amReadWrite, amReadWrite, amReadWrite, amNone }, // End of list marker { ImageType::none, 0, 0, amNone, amNone, amNone, amNone } }; diff --git a/src/pgfimage.cpp b/src/pgfimage.cpp new file mode 100644 index 00000000..cefbbf32 --- /dev/null +++ b/src/pgfimage.cpp @@ -0,0 +1,306 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2009 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: pgfimage.cpp + Version: $Rev: 1750 $ + Author(s): Gilles Caulier (cgilles) + History: 16-Jun-09, gc: submitted + Credits: See header file + */ +// ***************************************************************************** +#include "rcsid.hpp" +EXIV2_RCSID("@(#) $Id: pgfimage.cpp 1750 2009-02-16 14:30:51Z ahuggel $") + +// ***************************************************************************** + +#define DEBUG 1 + +// ***************************************************************************** +// included header files +#ifdef _MSC_VER +# include "exv_msvc.h" +#else +# include "exv_conf.h" +#endif + +#include "pgfimage.hpp" +#include "image.hpp" +#include "pngimage.hpp" +#include "basicio.hpp" +#include "error.hpp" +#include "futils.hpp" + +// + standard includes +#include +#include +#include +#include + +// Signature from front of PGF file +const unsigned char pgfSignature[3] = { 0x50, 0x47, 0x46 }; + +// ***************************************************************************** +// class member definitions +namespace Exiv2 { + + PgfImage::PgfImage(BasicIo::AutoPtr io, bool /*create*/) + : Image(ImageType::pgf, mdExif | mdIptc, io) + { + } // PgfImage::PgfImage + + void PgfImage::readMetadata() + { +#ifdef DEBUG + std::cerr << "Exiv2::PgfImage::readMetadata: Reading PGF file " << io_->path() << "\n"; +#endif + if (io_->open() != 0) + { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + // Ensure that this is the correct image type + if (!isPgfType(*io_, true)) + { + if (io_->error() || io_->eof()) throw Error(14); + throw Error(3, "PGF"); + } + clearMetadata(); + + readPgfMagicNumber(*io_); + + uint32_t headerSize = readPgfHeaderSize(*io_); + + readPgfHeaderStructure(*io_, &pixelWidth_, &pixelHeight_); + + // And now, the most interresting, the user data byte array where metadata are stored as small image. + + long size = 8 + headerSize - io_->tell(); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::readMetadata: Found Image data (" << size << " bytes)\n"; +#endif + + if (size < 0) throw Error(20); + if (size == 0) return; + + DataBuf imgData(size); + std::memset(imgData.pData_, 0x0, imgData.size_); + long bufRead = io_->read(imgData.pData_, imgData.size_); + if (io_->error()) throw Error(14); + if (bufRead != imgData.size_) throw Error(20); + + Image::AutoPtr image = Exiv2::ImageFactory::open(imgData.pData_, imgData.size_); + image->readMetadata(); + exifData() = image->exifData(); + iptcData() = image->iptcData(); + xmpData() = image->xmpData(); + + } // PgfImage::readMetadata + + void PgfImage::writeMetadata() + { + if (io_->open() != 0) + { + throw Error(9, io_->path(), strError()); + } + IoCloser closer(*io_); + BasicIo::AutoPtr tempIo(io_->temporary()); // may throw + assert (tempIo.get() != 0); + + doWriteMetadata(*tempIo); // may throw + io_->close(); + io_->transfer(*tempIo); // may throw + + } // PgfImage::writeMetadata + + void PgfImage::doWriteMetadata(BasicIo& outIo) + { + if (!io_->isopen()) throw Error(20); + if (!outIo.isopen()) throw Error(21); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::doWriteMetadata: Writing PGF file " << io_->path() << "\n"; + std::cout << "Exiv2::PgfImage::doWriteMetadata: tmp file created " << outIo.path() << "\n"; +#endif + + // Ensure that this is the correct image type + if (!isPgfType(*io_, true)) + { + if (io_->error() || io_->eof()) throw Error(20); + throw Error(22); + } + + // Ensure PGF version. + byte mnb = readPgfMagicNumber(*io_); + + readPgfHeaderSize(*io_); + + int w, h; + DataBuf header = readPgfHeaderStructure(*io_, &w, &h); + + Image::AutoPtr img = ImageFactory::create(ImageType::png); + + img->setExifData(exifData_); + img->setIptcData(iptcData_); + img->setXmpData(xmpData_); + img->writeMetadata(); + int imgSize = img->io().size(); + DataBuf imgBuf = img->io().read(imgSize); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::doWriteMetadata: Creating image to host metadata (" << imgSize << " bytes)\n"; +#endif + + //--------------------------------------------------------------- + + // Write PGF Signature. + if (outIo.write(pgfSignature, 3) != 3) throw Error(21); + + // Write Magic number. + if (outIo.putb(mnb) == EOF) throw Error(21); + + // Write new Header size. + uint32_t newHeaderSize = header.size_ + imgSize; + DataBuf buffer(4); + memcpy (buffer.pData_, &newHeaderSize, 4); + if (outIo.write(buffer.pData_, 4) != 4) throw Error(21); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage: new PGF header size : " << newHeaderSize << " bytes\n"; + + printf("%x\n", buffer.pData_[0]); + printf("%x\n", buffer.pData_[1]); + printf("%x\n", buffer.pData_[2]); + printf("%x\n", buffer.pData_[3]); +#endif + + // Write Header data. + if (outIo.write(header.pData_, header.size_) != header.size_) throw Error(21); + + // Write new metadata byte array. + if (outIo.write(imgBuf.pData_, imgBuf.size_) != imgBuf.size_) throw Error(21); + + // Copy the rest of PGF image data. + + DataBuf buf(4096); + long readSize = 0; + while ((readSize=io_->read(buf.pData_, buf.size_))) + { + if (outIo.write(buf.pData_, readSize) != readSize) throw Error(21); + } + if (outIo.error()) throw Error(21); + + } // PgfImage::doWriteMetadata + + byte PgfImage::readPgfMagicNumber(BasicIo& iIo) + { + byte b = iIo.getb(); + if (iIo.error()) throw Error(14); + + if (b < 0x36) // 0x36 = '6'. + { + // Not right Magick version. +#ifdef DEBUG + std::cout << "Exiv2::PgfImage::readMetadata: wrong Magick number\n"; +#endif + } + + return b; + } // PgfImage::readPgfMagicNumber + + uint32_t PgfImage::readPgfHeaderSize(BasicIo& iIo) + { + DataBuf buffer(4); + long bufRead = iIo.read(buffer.pData_, buffer.size_); + if (iIo.error()) throw Error(14); + if (bufRead != buffer.size_) throw Error(20); + + uint32_t headerSize = 0; + memcpy (&headerSize, buffer.pData_, 4); // TODO : check endianness. + if (headerSize <= 0 ) throw Error(22); + +#ifdef DEBUG + std::cout << "Exiv2::PgfImage: PGF header size : " << headerSize << " bytes\n"; +#endif + + return headerSize; + } // PgfImage::readPgfHeaderSize + + DataBuf PgfImage::readPgfHeaderStructure(BasicIo& iIo, int* width, int* height) + { + DataBuf header(16); + long bufRead = iIo.read(header.pData_, header.size_); + if (iIo.error()) throw Error(14); + if (bufRead != header.size_) throw Error(20); + + memcpy(width, &header.pData_[0], 4); // TODO : check endianness. + memcpy(height, &header.pData_[4], 4); // TODO : check endianness. + + /* NOTE: properties not yet used + byte nLevels = buffer.pData_[8]; + byte quality = buffer.pData_[9]; + byte bpp = buffer.pData_[10]; + byte channels = buffer.pData_[11]; + */ + byte mode = header.pData_[12]; + + if (mode == 2) // Indexed color image. We pass color table (256 * 3 bytes). + { + header.alloc(16 + 256*3); + + bufRead = iIo.read(&header.pData_[16], 256*3); + if (iIo.error()) throw Error(14); + if (bufRead != 256*3) throw Error(20); + } + + return header; + } // PgfImage::readPgfHeaderStructure + + // ************************************************************************* + // free functions + Image::AutoPtr newPgfInstance(BasicIo::AutoPtr io, bool create) + { + Image::AutoPtr image(new PgfImage(io, create)); + if (!image->good()) + { + image.reset(); + } + return image; + } + + bool isPgfType(BasicIo& iIo, bool advance) + { + const int32_t len = 3; + byte buf[len]; + iIo.read(buf, len); + if (iIo.error() || iIo.eof()) + { + return false; + } + int rc = memcmp(buf, pgfSignature, 3); + if (!advance || rc != 0) + { + iIo.seek(-len, BasicIo::cur); + } + + return rc == 0; + } +} // namespace Exiv2 diff --git a/src/pgfimage.hpp b/src/pgfimage.hpp new file mode 100644 index 00000000..35a71c5c --- /dev/null +++ b/src/pgfimage.hpp @@ -0,0 +1,138 @@ +// ***************************************************************** -*- C++ -*- +/* + * Copyright (C) 2004-2009 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 pgfimage.hpp + @brief PGF image, implemented using the following references: + PGF specification from libpgf web site
+ @version $Rev: 1750 $ + @author Andreas Huggel (ahu) + ahuggel@gmx.net + @author Gilles Caulier (cgilles) + caulier dot gilles at gmail dot com + @date 16-Jun-09, gc: submitted + */ +#ifndef PGFIMAGE_HPP_ +#define PGFIMAGE_HPP_ + +// ***************************************************************************** +// included header files +#include "image.hpp" +#include "basicio.hpp" +#include "types.hpp" + +// + standard includes +#include + +// ***************************************************************************** +// namespace extensions +namespace Exiv2 +{ + +// ***************************************************************************** +// class definitions + + // Add PGF to the supported image formats + namespace ImageType + { + const int pgf = 17; //!< PGF image type (see class PgfImage) + } + + /*! + @brief Class to access PGF images. Exif and IPTC metadata are supported + directly. + */ + class EXIV2API PgfImage : public Image { + public: + //! @name Creators + //@{ + /*! + @brief Constructor that can either open an existing PGF image or create + a new image from scratch. If a new image is to be created, any + existing data is overwritten. Since the constructor can not return + a result, callers should check the good() method after object + construction to determine success or failure. + @param io An auto-pointer that owns a BasicIo instance used for + reading and writing image metadata. \b Important: The constructor + takes ownership of the passed in BasicIo instance through the + auto-pointer. Callers should not continue to use the BasicIo + instance after it is passed to this method. Use the Image::io() + method to get a temporary reference. + @param create Specifies if an existing image should be read (false) + or if a new file should be created (true). + This option is not yet implemented. + */ + PgfImage(BasicIo::AutoPtr io, bool create); + //@} + + //! @name Manipulators + //@{ + void readMetadata(); + void writeMetadata(); + //@} + + //! @name Accessors + //@{ + std::string mimeType() const { return "image/pgf"; } + //@} + + private: + //! @name NOT implemented + //@{ + //! Copy constructor + PgfImage(const PgfImage& rhs); + //! Assignment operator + PgfImage& operator=(const PgfImage& rhs); + /*! + @brief Provides the main implementation of writeMetadata() by + writing all buffered metadata to the provided BasicIo. + @param oIo BasicIo instance to write to (a temporary location). + + @return 4 if opening or writing to the associated BasicIo fails + */ + EXV_DLLLOCAL void doWriteMetadata(BasicIo& oIo); + //! Read Magick number. Only version >= 6 is supported. + byte readPgfMagicNumber(BasicIo& iIo); + //! Read PGF Header size encoded in 32 bits integer. + uint32_t readPgfHeaderSize(BasicIo& iIo); + //! Read header structure. + DataBuf readPgfHeaderStructure(BasicIo& iIo, int* width, int* height); + //@} + + }; // class PgfImage + +// ***************************************************************************** +// template, inline and free functions + + // These could be static private functions on Image subclasses but then + // ImageFactory needs to be made a friend. + /*! + @brief Create a new PgfImage instance and return an auto-pointer to it. + Caller owns the returned object and the auto-pointer ensures that + it will be deleted. + */ + EXIV2API Image::AutoPtr newPgfInstance(BasicIo::AutoPtr io, bool create); + + //! Check if the file iIo is a PGF image. + EXIV2API bool isPgfType(BasicIo& iIo, bool advance); + +} // namespace Exiv2 + +#endif // #ifndef PGFIMAGE_HPP_