You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
exiv2/src/bigtiffimage.cpp

413 lines
16 KiB
C++

#include "bigtiffimage.hpp"
#include <cassert>
#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
{
return std::string();
}
void printStructure(std::ostream& os, PrintStructureOption option, int depth)
{
8 years ago
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 BIGTIFF 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;
8 years ago
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
8 years ago
: 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_);
8 years ago
// 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
8 years ago
io.seek(restore, BasicIo::beg); // restore
}
else // use 'data' as data :)
8 years ago
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;
8 years ago
printIFD(out,option,offset,depth);
}
io.seek(restore,BasicIo::beg); // restore
}
}
}
8 years ago
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--;
}
8 years ago
uint64_t readData(int size) const
{
const DataBuf data = Image::io().read(size);
assert(data.size_ != 0);
8 years ago
uint64_t result = 0;
8 years ago
if (size == 1)
{}
else if (size == 2)
result = byteSwap2(data, 0, doSwap_);
8 years ago
else if (size == 4)
result = byteSwap4(data, 0, doSwap_);
8 years ago
else if (size == 8)
result = byteSwap8(data, 0, doSwap_);
8 years ago
else
assert(!"unexpected size");
return result;
8 years ago
}
};
}
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;
}
}