diff --git a/include/exiv2/bigtiffimage.hpp b/include/exiv2/bigtiffimage.hpp new file mode 100644 index 00000000..d0dc57ab --- /dev/null +++ b/include/exiv2/bigtiffimage.hpp @@ -0,0 +1,16 @@ + +#include "basicio.hpp" +#include "image.hpp" + +namespace Exiv2 +{ + +namespace ImageType +{ + const int bigtiff = 22; +} + +Image::AutoPtr newBigTiffInstance(BasicIo::AutoPtr, bool); +bool isBigTiffType(BasicIo &, bool); + +} diff --git a/include/exiv2/image.hpp b/include/exiv2/image.hpp index 73edb813..1d7c4c25 100644 --- a/include/exiv2/image.hpp +++ b/include/exiv2/image.hpp @@ -345,6 +345,7 @@ namespace Exiv2 { bool isStringType(uint16_t type); bool isShortType(uint16_t type); bool isLongType(uint16_t type); + bool isLongLongType(uint16_t type); bool isRationalType(uint16_t type); bool is2ByteType(uint16_t type); bool is4ByteType(uint16_t type); @@ -352,10 +353,12 @@ namespace Exiv2 { bool isPrintXMP(uint16_t type, Exiv2::PrintStructureOption option); bool isPrintICC(uint16_t type, Exiv2::PrintStructureOption option); - uint32_t byteSwap(uint32_t value,bool bSwap); - uint16_t byteSwap(uint16_t value,bool bSwap); - uint16_t byteSwap2(DataBuf& buf,size_t offset,bool bSwap); - uint32_t byteSwap4(DataBuf& buf,size_t offset,bool bSwap); + uint64_t byteSwap(uint64_t value,bool bSwap) const; + uint32_t byteSwap(uint32_t value,bool bSwap) const; + uint16_t byteSwap(uint16_t value,bool bSwap) const; + uint16_t byteSwap2(const DataBuf& buf,size_t offset,bool bSwap) const; + uint32_t byteSwap4(const DataBuf& buf,size_t offset,bool bSwap) const; + uint64_t byteSwap8(const DataBuf& buf,size_t offset,bool bSwap) const; //@} @@ -493,6 +496,12 @@ namespace Exiv2 { int pixelHeight_; //!< image pixel height NativePreviewList nativePreviews_; //!< list of native previews + //! Return tag name for given tag id. + const std::string& tagName(uint16_t tag); + + //! Return tag type for given tag id. + const char* typeName(uint16_t tag) const; + private: //! @name NOT implemented //@{ @@ -502,9 +511,6 @@ namespace Exiv2 { Image& operator=(const Image& rhs); //@} - //! Return tag name for given tag id. - const std::string& tagName(uint16_t tag); - // DATA int imageType_; //!< Image type uint16_t supportedMetadata_; //!< Bitmap with all supported metadata types diff --git a/include/exiv2/types.hpp b/include/exiv2/types.hpp index b19a79ab..9cd2fef5 100644 --- a/include/exiv2/types.hpp +++ b/include/exiv2/types.hpp @@ -137,6 +137,9 @@ namespace Exiv2 { tiffFloat =11, //!< TIFF FLOAT type, single precision (4-byte) IEEE format. tiffDouble =12, //!< TIFF DOUBLE type, double precision (8-byte) IEEE format. tiffIfd =13, //!< TIFF IFD type, 32-bit (4-byte) unsigned integer. + unsignedLongLong =16, //!< Exif LONG LONG type, 64-bit (8-byte) unsigned integer. + signedLongLong =17, //!< Exif LONG LONG type, 64-bit (8-byte) signed integer. + tiffIfd8 =18, //!< TIFF IFD type, 64-bit (8-byte) unsigned integer. string =0x10000, //!< IPTC string type. date =0x10001, //!< IPTC date type. time =0x10002, //!< IPTC time type. @@ -266,6 +269,8 @@ namespace Exiv2 { EXIV2API uint16_t getUShort(const byte* buf, ByteOrder byteOrder); //! Read a 4 byte unsigned long value from the data buffer EXIV2API uint32_t getULong(const byte* buf, ByteOrder byteOrder); + //! Read a 8 byte unsigned long value from the data buffer + EXIV2API uint64_t getULongLong(const byte* buf, ByteOrder byteOrder); //! Read an 8 byte unsigned rational value from the data buffer EXIV2API URational getURational(const byte* buf, ByteOrder byteOrder); //! Read a 2 byte signed short value from the data buffer diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d0b8a66b..99d9a90d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,6 +84,7 @@ set( LIBEXIV2_HDR ../include/exiv2/asfvideo.hpp # Add library C++ source files to this list set( LIBEXIV2_SRC asfvideo.cpp basicio.cpp + bigtiffimage.cpp bmpimage.cpp canonmn.cpp casiomn.cpp diff --git a/src/bigtiffimage.cpp b/src/bigtiffimage.cpp new file mode 100644 index 00000000..ecbe1f92 --- /dev/null +++ b/src/bigtiffimage.cpp @@ -0,0 +1,412 @@ + +#include "bigtiffimage.hpp" + +#include + +#include "exif.hpp" +#include "image_int.hpp" + + +namespace Exiv2 +{ + + namespace + { + struct Header + { + enum Format + { + StandardTiff, + BigTiff, + }; + + Header(): byteOrder_(invalidByteOrder), version_(-1), data_size_(0), dir_offset_(0) {} + Header(const ByteOrder& order, int v, int size, uint64_t offset): + byteOrder_(order), + version_(v), + data_size_(size), + dir_offset_(offset) + { + + } + + bool isValid() const + { + return version_ != -1; + } + + ByteOrder byteOrder() const + { + assert(isValid()); + return byteOrder_; + } + + int version() const + { + assert(isValid()); + return version_; + } + + Format format() const + { + assert(isValid()); + return version_ == 0x2A? StandardTiff: BigTiff; + } + + int dataSize() const + { + assert(isValid()); + return data_size_; + } + + uint64_t dirOffset() const + { + assert(isValid()); + return dir_offset_; + } + + private: + ByteOrder byteOrder_; + int version_; // 42 or 43 - regular tiff or big tiff + int data_size_; // 4 or 8 + uint64_t dir_offset_; + }; + + Header readHeader(BasicIo& io) + { + byte header[2]; + io.read(header, 2); + + ByteOrder byteOrder = invalidByteOrder; + if (header[0] == 'I' && header[1] == 'I') + byteOrder = littleEndian; + else if (header[0] == 'M' && header[1] == 'M') + byteOrder = bigEndian; + + if (byteOrder == invalidByteOrder) + return Header(); + + byte version[2]; + io.read(version, 2); + + const uint16_t magic = getUShort(version, byteOrder); + + if (magic != 0x2A && magic != 0x2B) + return Header(); + + Header result; + + if (magic == 0x2A) + { + byte buffer[4]; + io.read(buffer, 4); + + const uint32_t offset = getULong(buffer, byteOrder); + result = Header(byteOrder, magic, 4, offset); + } + else + { + byte buffer[8]; + io.read(buffer, 2); + const int size = getUShort(buffer, byteOrder); + assert(size == 8); + + io.read(buffer, 2); // null + + io.read(buffer, 8); + const uint64_t offset = getULongLong(buffer, byteOrder); + + result = Header(byteOrder, magic, size, offset); + } + + return result; + } + + class BigTiffImage: public Image + { + public: + BigTiffImage(BasicIo::AutoPtr io): + Image(ImageType::bigtiff, mdExif, io), + header_(), + dataSize_(0), + doSwap_(false) + { + header_ = readHeader(Image::io()); + assert(header_.isValid()); + + doSwap_ = (isLittleEndianPlatform() && header_.byteOrder() == bigEndian) + || (isBigEndianPlatform() && header_.byteOrder() == littleEndian); + + dataSize_ = header_.format() == Header::StandardTiff? 4 : 8; + } + + virtual ~BigTiffImage() {} + + // overrides + void readMetadata() + { + + } + + void writeMetadata() + { + + } + + std::string mimeType() const + { + + } + + void printStructure(std::ostream& os, PrintStructureOption option, int depth) + { + printIFD(os, option, header_.dirOffset(), depth - 1); + } + + private: + Header header_; + int dataSize_; + bool doSwap_; + + void printIFD(std::ostream& out, PrintStructureOption option, uint64_t dir_offset, int depth) + { + BasicIo& io = Image::io(); + + depth++; + bool bFirst = true; + + // buffer + bool bPrint = true; + + do + { + // Read top of directory + io.seek(dir_offset, BasicIo::beg); + + const uint64_t entries = readData(header_.format() == Header::StandardTiff? 2: 8); + const bool tooBig = entries > 500; + + if ( bFirst && bPrint ) + { + out << Internal::indent(depth) << Internal::stringFormat("STRUCTURE OF TIFF FILE ") << io.path() << std::endl; + if (tooBig) + out << Internal::indent(depth) << "entries = " << entries << std::endl; + } + + if (tooBig) + break; + + // Read the dictionary + for ( uint64_t i = 0; i < entries; i ++ ) + { + if ( bFirst && bPrint ) + out << Internal::indent(depth) + << " address | tag | " + << " type | count | offset | value\n"; + + bFirst = false; + + const uint16_t tag = readData(2); + const uint16_t type = readData(2); + const uint64_t count = readData(dataSize_); + const DataBuf data = io.read(dataSize_); // Read data as raw value. what should be done about it will be decided depending on type + + std::string sp = "" ; // output spacer + + //prepare to print the value + // TODO: figure out what's going on with kount + const uint64_t kount = isStringType(type)? (count > 32 ? 32 : count) // restrict long arrays + : count > 5 ? 5 + : count + ; + const uint32_t pad = isStringType(type) ? 1 : 0; + const uint32_t size = isStringType(type) ? 1 + : is2ByteType(type) ? 2 + : is4ByteType(type) ? 4 + : is8ByteType(type) ? 8 + : 1; + + DataBuf buf(size * count + pad); + + const uint64_t offset = header_.format() == Header::StandardTiff? + byteSwap4(data, 0, doSwap_): + byteSwap8(data, 0, doSwap_); + + // big data? Use 'data' as pointer to real data + const bool usePointer = count*size > dataSize_; + + if ( usePointer ) // read into buffer + { + size_t restore = io.tell(); // save + io.seek(offset, BasicIo::beg); // position + io.read(buf.pData_, count * size); // read + io.seek(restore, BasicIo::beg); // restore + } + else // use 'data' as data :) + std::memcpy(buf.pData_, data.pData_, count * size); // copy data + + if ( bPrint ) + { + const int entrySize = header_.format() == Header::StandardTiff? 12: 20; + const uint64_t address = dir_offset + 2 + i * entrySize; + const std::string offsetString = usePointer? + Internal::stringFormat("%10u", offset): + ""; + + out << Internal::indent(depth) + << Internal::stringFormat("%8u | %#06x %-25s |%10s |%9u |%10s | ", + address, tag, tagName(tag).c_str(), typeName(type), count, offsetString.c_str()); + + if ( isShortType(type) ) + { + for ( size_t k = 0 ; k < kount ; k++ ) + { + out << sp << byteSwap2(buf, k*size, doSwap_); + sp = " "; + } + } + else if ( isLongType(type) ) + { + for ( size_t k = 0 ; k < kount ; k++ ) + { + out << sp << byteSwap4(buf, k*size, doSwap_); + sp = " "; + } + } + else if ( isLongLongType(type) ) + { + for ( size_t k = 0 ; k < kount ; k++ ) + { + out << sp << byteSwap8(buf, k*size, doSwap_); + sp = " "; + } + } + else if ( isRationalType(type) ) + { + for ( size_t k = 0 ; k < kount ; k++ ) + { + uint32_t a = byteSwap4(buf, k*size+0, doSwap_); + uint32_t b = byteSwap4(buf, k*size+4, doSwap_); + out << sp << a << "/" << b; + sp = " "; + } + } + else if ( isStringType(type) ) + out << sp << Internal::binaryToString(buf, kount); + + sp = kount == count ? "" : " ..."; + out << sp << std::endl; + + if ( option == kpsRecursive && + (tag == 0x8769 /* ExifTag */ || tag == 0x014a/*SubIFDs*/ || type == tiffIfd || type == tiffIfd8) ) + { + for ( size_t k = 0 ; k < count ; k++ ) + { + const size_t restore = io.tell(); + const uint64_t ifdOffset = type == tiffIfd8? + byteSwap8(buf, k*size, doSwap_): + byteSwap4(buf, k*size, doSwap_); + + std::cerr << "tag = " << Internal::stringFormat("%#x",tag) << std::endl; + printIFD(out, option, ifdOffset, depth); + io.seek(restore, BasicIo::beg); + } + } + else if ( option == kpsRecursive && tag == 0x83bb /* IPTCNAA */ ) + { + const size_t restore = io.tell(); // save + io.seek(offset, BasicIo::beg); // position + byte* bytes=new byte[count] ; // allocate memory + io.read(bytes,count) ; // read + io.seek(restore, BasicIo::beg); // restore + IptcData::printStructure(out,bytes,count,depth); + delete[] bytes; // free + } + else if ( option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) + { + size_t restore = io.tell(); // save + + uint32_t jump= 10 ; + byte bytes[20] ; + const char* chars = (const char*) &bytes[0] ; + io.seek(dir_offset, BasicIo::beg); // position + io.read(bytes,jump ) ; // read + bytes[jump]=0 ; + if ( ::strcmp("Nikon",chars) == 0 ) + { + // tag is an embedded tiff + byte* bytes=new byte[count-jump] ; // allocate memory + io.read(bytes,count-jump) ; // read + MemIo memIo(bytes,count-jump) ; // create a file + std::cerr << "Nikon makernote" << std::endl; + // printTiffStructure(memIo,out,option,depth); TODO: fix it + delete[] bytes ; // free + } + else + { + // tag is an IFD + io.seek(0, BasicIo::beg); // position + std::cerr << "makernote" << std::endl; + printIFD(out,option,offset,depth); + } + + io.seek(restore,BasicIo::beg); // restore + } + } + } + + const uint64_t nextDirOffset = readData(dataSize_); + + dir_offset = tooBig ? 0 : nextDirOffset; + out.flush(); + } while (dir_offset != 0); + + if ( bPrint ) + out << Internal::indent(depth) << "END " << io.path() << std::endl; + + depth--; + } + + uint64_t readData(int size) const + { + const DataBuf data = Image::io().read(size); + assert(data.size_ != 0); + + uint64_t result = 0; + + if (size == 1) + {} + else if (size == 2) + result = byteSwap2(data, 0, doSwap_); + else if (size == 4) + result = byteSwap4(data, 0, doSwap_); + else if (size == 8) + result = byteSwap8(data, 0, doSwap_); + else + assert(!"unexpected size"); + + return result; + } + }; + } + + + Image::AutoPtr newBigTiffInstance(BasicIo::AutoPtr io, bool) + { + return Image::AutoPtr(new BigTiffImage(io)); + } + + + bool isBigTiffType(BasicIo& io, bool advance) + { + const long pos = io.tell(); + const Header header = readHeader(io); + const bool valid = header.isValid(); + + if (valid == false || advance == false) + io.seek(pos, BasicIo::beg); + + return valid; + } + +} diff --git a/src/image.cpp b/src/image.cpp index a7198328..ea78911b 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -47,6 +47,7 @@ EXIV2_RCSID("@(#) $Id$") #include "tiffimage_int.hpp" #include "tiffcomposite_int.hpp" #include "tiffvisitor_int.hpp" +#include "bigtiffimage.hpp" #include "webpimage.hpp" #include "orfimage.hpp" #include "gifimage.hpp" @@ -112,6 +113,7 @@ namespace { { ImageType::crw, newCrwInstance, isCrwType, amReadWrite, amNone, amNone, amReadWrite }, { ImageType::mrw, newMrwInstance, isMrwType, amRead, amRead, amRead, amNone }, { ImageType::tiff, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone }, + { ImageType::bigtiff, newBigTiffInstance, isBigTiffType, amRead, amRead, amRead, amNone }, { ImageType::webp, newWebPInstance, isWebPType, amReadWrite, amNone, amReadWrite, amNone }, { ImageType::dng, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone }, { ImageType::nef, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone }, @@ -195,6 +197,11 @@ namespace Exiv2 { || type == Exiv2::signedLong ; } + bool Image::isLongLongType(uint16_t type) { + return type == Exiv2::unsignedLongLong + || type == Exiv2::signedLongLong + ; + } bool Image::isRationalType(uint16_t type) { return type == Exiv2::unsignedRational || type == Exiv2::signedRational @@ -237,7 +244,19 @@ namespace Exiv2 { } bool Image::isLittleEndianPlatform() { return !isBigEndianPlatform(); } - uint32_t Image::byteSwap(uint32_t value,bool bSwap) + uint64_t Image::byteSwap(uint64_t value,bool bSwap) const + { + uint64_t result = 0; + byte* source_value = reinterpret_cast(&value); + byte* destination_value = reinterpret_cast(&result); + + for (int i = 0; i < 8; i++) + destination_value[i] = source_value[8 - i - 1]; + + return bSwap ? result : value; + } + + uint32_t Image::byteSwap(uint32_t value,bool bSwap) const { uint32_t result = 0; result |= (value & 0x000000FF) << 24; @@ -247,7 +266,7 @@ namespace Exiv2 { return bSwap ? result : value; } - uint16_t Image::byteSwap(uint16_t value,bool bSwap) + uint16_t Image::byteSwap(uint16_t value,bool bSwap) const { uint16_t result = 0; result |= (value & 0x00FF) << 8; @@ -255,7 +274,7 @@ namespace Exiv2 { return bSwap ? result : value; } - uint16_t Image::byteSwap2(DataBuf& buf,size_t offset,bool bSwap) + uint16_t Image::byteSwap2(const DataBuf& buf,size_t offset,bool bSwap) const { uint16_t v; char* p = (char*) &v; @@ -264,7 +283,7 @@ namespace Exiv2 { return Image::byteSwap(v,bSwap); } - uint32_t Image::byteSwap4(DataBuf& buf,size_t offset,bool bSwap) + uint32_t Image::byteSwap4(const DataBuf& buf,size_t offset,bool bSwap) const { uint32_t v; char* p = (char*) &v; @@ -275,7 +294,18 @@ namespace Exiv2 { return Image::byteSwap(v,bSwap); } - static const char* typeName(uint16_t tag) + uint64_t Image::byteSwap8(const DataBuf& buf,size_t offset,bool bSwap) const + { + uint64_t v; + byte* p = reinterpret_cast(&v); + + for(int i = 0; i < 8; i++) + p[i] = buf.pData_[offset + i]; + + return Image::byteSwap(v,bSwap); + } + + const char* Image::typeName(uint16_t tag) const { //! List of TIFF image tags const char* result = NULL; diff --git a/src/types.cpp b/src/types.cpp index df509703..3d539908 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -252,6 +252,22 @@ namespace Exiv2 { } } + uint64_t getULongLong(const byte* buf, ByteOrder byteOrder) + { + if (byteOrder == littleEndian) { + return (uint64_t)buf[7] << 56 | (uint64_t)buf[6] << 48 + | (uint64_t)buf[5] << 40 | (uint64_t)buf[4] << 32 + | (uint64_t)buf[3] << 24 | (uint64_t)buf[2] << 16 + | (uint64_t)buf[1] << 8 | (uint64_t)buf[0]; + } + else { + return (uint64_t)buf[0] << 56 | (uint64_t)buf[1] << 48 + | (uint64_t)buf[2] << 40 | (uint64_t)buf[3] << 32 + | (uint64_t)buf[4] << 24 | (uint64_t)buf[5] << 16 + | (uint64_t)buf[6] << 8 | (uint64_t)buf[7]; + } + } + URational getURational(const byte* buf, ByteOrder byteOrder) { uint32_t nominator = getULong(buf, byteOrder);