#342 Initial BigTIFF support

This commit is contained in:
Harald Kuhr 2017-08-21 21:59:09 +02:00
parent 3f5f48d8cb
commit 792b531b0e
20 changed files with 499 additions and 44 deletions

View File

@ -41,6 +41,7 @@ public interface TIFF {
short BYTE_ORDER_MARK_LITTLE_ENDIAN = ('I' << 8) | 'I'; short BYTE_ORDER_MARK_LITTLE_ENDIAN = ('I' << 8) | 'I';
int TIFF_MAGIC = 42; int TIFF_MAGIC = 42;
int BIGTIFF_MAGIC = 43;
short TYPE_BYTE = 1; short TYPE_BYTE = 1;
short TYPE_ASCII = 2; short TYPE_ASCII = 2;
@ -56,6 +57,10 @@ public interface TIFF {
short TYPE_FLOAT = 11; short TYPE_FLOAT = 11;
short TYPE_DOUBLE = 12; short TYPE_DOUBLE = 12;
short TYPE_IFD = 13; short TYPE_IFD = 13;
// BigTIFF
short TYPE_LONG8 = 16;
short TYPE_SLONG8 = 17;
short TYPE_IFD8 = 18;
/* /*
1 = BYTE 8-bit unsigned integer. 1 = BYTE 8-bit unsigned integer.
2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte 2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte

View File

@ -387,7 +387,7 @@ public final class TIFFEntry extends AbstractEntry {
throw new UnsupportedOperationException(String.format("Method guessType not implemented for type %s", value.getClass())); throw new UnsupportedOperationException(String.format("Method guessType not implemented for type %s", value.getClass()));
} }
static int getValueLength(final int pType, final int pCount) { static long getValueLength(final int pType, final long pCount) {
if (pType > 0 && pType < TIFF.TYPE_LENGTHS.length) { if (pType > 0 && pType < TIFF.TYPE_LENGTHS.length) {
return TIFF.TYPE_LENGTHS[pType] * pCount; return TIFF.TYPE_LENGTHS[pType] * pCount;
} }

View File

@ -59,6 +59,8 @@ public final class TIFFReader extends MetadataReader {
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.metadata.exif.debug")); final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.metadata.exif.debug"));
static final Collection<Integer> KNOWN_IFDS = Collections.unmodifiableCollection(Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD, TIFF.TAG_SUB_IFD)); static final Collection<Integer> KNOWN_IFDS = Collections.unmodifiableCollection(Arrays.asList(TIFF.TAG_EXIF_IFD, TIFF.TAG_GPS_IFD, TIFF.TAG_INTEROP_IFD, TIFF.TAG_SUB_IFD));
private boolean longOffsets;
private int offsetSize;
@Override @Override
public Directory read(final ImageInputStream input) throws IOException { public Directory read(final ImageInputStream input) throws IOException {
@ -77,18 +79,41 @@ public final class TIFFReader extends MetadataReader {
throw new IIOException(String.format("Invalid TIFF byte order mark '%s', expected: 'II' or 'MM'", StringUtil.decode(bom, 0, bom.length, "ASCII"))); throw new IIOException(String.format("Invalid TIFF byte order mark '%s', expected: 'II' or 'MM'", StringUtil.decode(bom, 0, bom.length, "ASCII")));
} }
// TODO: BigTiff uses version 43 instead of TIFF's 42, and header is slightly different, see // BigTiff uses version 43 instead of TIFF's 42, and header is slightly different, see
// http://www.awaresystems.be/imaging/tiff/bigtiff.html // http://www.awaresystems.be/imaging/tiff/bigtiff.html
int magic = input.readUnsignedShort(); int magic = input.readUnsignedShort();
if (magic != TIFF.TIFF_MAGIC) { if (magic == TIFF.TIFF_MAGIC) {
throw new IIOException(String.format("Wrong TIFF magic in EXIF data: %04x, expected: %04x", magic, TIFF.TIFF_MAGIC)); longOffsets = false;
offsetSize = 4;
}
else if (magic == TIFF.BIGTIFF_MAGIC) {
longOffsets = true;
offsetSize = 8;
// Just validate we're ok
int offsetSize = input.readUnsignedShort();
if (offsetSize != 8) {
throw new IIOException(String.format("Unexpected BigTIFF offset size: %04x, expected: %04x", offsetSize, 8));
} }
long directoryOffset = input.readUnsignedInt(); int padding = input.readUnsignedShort();
if (padding != 0) {
throw new IIOException(String.format("Unexpected BigTIFF padding: %04x, expected: %04x", padding, 0));
}
}
else {
throw new IIOException(String.format("Wrong TIFF magic in input data: %04x, expected: %04x", magic, TIFF.TIFF_MAGIC));
}
long directoryOffset = readOffset(input);
return readDirectory(input, directoryOffset, true); return readDirectory(input, directoryOffset, true);
} }
private long readOffset(final ImageInputStream input) throws IOException {
return longOffsets ? input.readLong() : input.readUnsignedInt();
}
// TODO: Consider re-writing so that the linked IFD parsing is done externally to the method // TODO: Consider re-writing so that the linked IFD parsing is done externally to the method
protected Directory readDirectory(final ImageInputStream pInput, final long pOffset, final boolean readLinked) throws IOException { protected Directory readDirectory(final ImageInputStream pInput, final long pOffset, final boolean readLinked) throws IOException {
List<IFD> ifds = new ArrayList<>(); List<IFD> ifds = new ArrayList<>();
@ -97,14 +122,7 @@ public final class TIFFReader extends MetadataReader {
pInput.seek(pOffset); pInput.seek(pOffset);
long nextOffset = -1; long nextOffset = -1;
int entryCount; long entryCount = readEntryCount(pInput);
try {
entryCount = pInput.readUnsignedShort();
}
catch (EOFException e) {
// Treat EOF here as empty Sub-IFD
entryCount = 0;
}
for (int i = 0; i < entryCount; i++) { for (int i = 0; i < entryCount; i++) {
try { try {
@ -122,7 +140,7 @@ public final class TIFFReader extends MetadataReader {
if (readLinked) { if (readLinked) {
if (nextOffset == -1) { if (nextOffset == -1) {
try { try {
nextOffset = pInput.readUnsignedInt(); nextOffset = readOffset(pInput);
} }
catch (EOFException e) { catch (EOFException e) {
// catch EOF here as missing EOF marker // catch EOF here as missing EOF marker
@ -150,6 +168,16 @@ public final class TIFFReader extends MetadataReader {
return new TIFFDirectory(ifds); return new TIFFDirectory(ifds);
} }
private long readEntryCount(final ImageInputStream pInput) throws IOException {
try {
return longOffsets ? pInput.readLong() : pInput.readUnsignedShort();
}
catch (EOFException e) {
// Treat EOF here as empty Sub-IFD
return 0;
}
}
// TODO: Might be better to leave this for client code, as it's tempting go really overboard and support any possible embedded format.. // TODO: Might be better to leave this for client code, as it's tempting go really overboard and support any possible embedded format..
private void readSubdirectories(ImageInputStream input, List<Entry> entries, List<Integer> subIFDIds) throws IOException { private void readSubdirectories(ImageInputStream input, List<Entry> entries, List<Integer> subIFDIds) throws IOException {
if (subIFDIds == null || subIFDIds.isEmpty()) { if (subIFDIds == null || subIFDIds.isEmpty()) {
@ -215,31 +243,27 @@ public final class TIFFReader extends MetadataReader {
offsets = (long[]) value; offsets = (long[]) value;
} }
else { else {
throw new IIOException(String.format("Unknown pointer type: %s", (value != null throw new IIOException(String.format("Unknown pointer type: %s", value != null ? value.getClass() : null));
? value.getClass()
: null)));
} }
return offsets; return offsets;
} }
private TIFFEntry readEntry(final ImageInputStream pInput) throws IOException { private TIFFEntry readEntry(final ImageInputStream pInput) throws IOException {
// TODO: BigTiff entries are different
int tagId = pInput.readUnsignedShort(); int tagId = pInput.readUnsignedShort();
short type = pInput.readShort(); short type = pInput.readShort();
int count = readValueCount(pInput); // Number of values
int count = pInput.readInt(); // Number of values
// It's probably a spec violation to have count 0, but we'll be lenient about it // It's probably a spec violation to have count 0, but we'll be lenient about it
if (count < 0) { if (count < 0) {
throw new IIOException(String.format("Illegal count %d for tag %s type %s @%08x", count, tagId, type, pInput.getStreamPosition())); throw new IIOException(String.format("Illegal count %d for tag %s type %s @%08x", count, tagId, type, pInput.getStreamPosition()));
} }
if (type <= 0 || type > 13) { if (!isValidType(type)) {
pInput.skipBytes(4); // read Value pInput.skipBytes(4); // read Value
// Invalid tag, this is just for debugging // Invalid tag, this is just for debugging
long offset = pInput.getStreamPosition() - 12l; long offset = pInput.getStreamPosition() - 12L;
if (DEBUG) { if (DEBUG) {
System.err.printf("Bad EXIF data @%08x\n", pInput.getStreamPosition()); System.err.printf("Bad EXIF data @%08x\n", pInput.getStreamPosition());
@ -266,22 +290,37 @@ public final class TIFFReader extends MetadataReader {
return null; return null;
} }
int valueLength = getValueLength(type, count); long valueLength = getValueLength(type, count);
Object value; Object value;
// TODO: For BigTiff allow size > 4 && <= 8 in addition if (valueLength > 0 && valueLength <= offsetSize) {
if (valueLength > 0 && valueLength <= 4) {
value = readValueInLine(pInput, type, count); value = readValueInLine(pInput, type, count);
pInput.skipBytes(4 - valueLength); pInput.skipBytes(offsetSize - valueLength);
} }
else { else {
long valueOffset = pInput.readUnsignedInt(); // This is the *value* iff the value size is <= 4 bytes long valueOffset = readOffset(pInput); // This is the *value* iff the value size is <= 4 bytes
value = readValueAt(pInput, valueOffset, type, count); value = readValueAt(pInput, valueOffset, type, count);
} }
return new TIFFEntry(tagId, type, value); return new TIFFEntry(tagId, type, value);
} }
private boolean isValidType(final short type) {
return type > 0 && type < TIFF.TYPE_LENGTHS.length && TIFF.TYPE_LENGTHS[type] > 0;
}
private int readValueCount(final ImageInputStream pInput) throws IOException {
return assertIntCount(longOffsets ? pInput.readLong() : pInput.readUnsignedInt());
}
private int assertIntCount(final long count) throws IOException {
if (count > Integer.MAX_VALUE) {
throw new IIOException(String.format("Unsupported TIFF value count value: %s > Integer.MAX_VALUE", count));
}
return (int) count;
}
private Object readValueAt(final ImageInputStream pInput, final long pOffset, final short pType, final int pCount) throws IOException { private Object readValueAt(final ImageInputStream pInput, final long pOffset, final short pType, final int pCount) throws IOException {
long pos = pInput.getStreamPosition(); long pos = pInput.getStreamPosition();
try { try {
@ -423,16 +462,17 @@ public final class TIFFReader extends MetadataReader {
return srationals; return srationals;
// BigTiff: // BigTiff:
case 16: // LONG8 case TIFF.TYPE_LONG8:
case 17: // SLONG8 case TIFF.TYPE_SLONG8:
case 18: // IFD8 case TIFF.TYPE_IFD8:
// TODO: Assert BigTiff (version == 43) // TODO: Assert BigTiff (version == 43)
if (pCount == 1) { if (pCount == 1) {
long val = pInput.readLong(); long val = pInput.readLong();
if (pType != 17 && val < 0) { if (pType != TIFF.TYPE_SLONG8 && val < 0) {
throw new IIOException(String.format("Value > %s", Long.MAX_VALUE)); throw new IIOException(String.format("Value > %s", Long.MAX_VALUE));
} }
return val; return val;
} }
@ -459,6 +499,17 @@ public final class TIFFReader extends MetadataReader {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
// if (true) {
// ImageInputStream stream = ImageIO.createImageInputStream(new File(args[0]));
//
// byte[] b = new byte[Math.min((int) stream.length(), 1024)];
// stream.readFully(b);
//
// System.err.println(HexDump.dump(b));
//
// return;
// }
//
TIFFReader reader = new TIFFReader(); TIFFReader reader = new TIFFReader();
ImageInputStream stream = ImageIO.createImageInputStream(new File(args[0])); ImageInputStream stream = ImageIO.createImageInputStream(new File(args[0]));

View File

@ -163,15 +163,11 @@ public final class TIFFWriter extends MetadataWriter {
return WORD_LENGTH + computeDataSize(new IFD(directory)) + directory.size() * ENTRY_LENGTH; return WORD_LENGTH + computeDataSize(new IFD(directory)) + directory.size() * ENTRY_LENGTH;
} }
public long computeIFDOffsetSize(final Collection<Entry> directory) {
return computeDataSize(new IFD(directory)) + LONGWORD_LENGTH;
}
private long computeDataSize(final Directory directory) { private long computeDataSize(final Directory directory) {
long dataSize = 0; long dataSize = 0;
for (Entry entry : directory) { for (Entry entry : directory) {
int length = getValueLength(getType(entry), getCount(entry)); long length = getValueLength(getType(entry), getCount(entry));
if (length < 0) { if (length < 0) {
throw new IllegalArgumentException(String.format("Unknown size for entry %s", entry)); throw new IllegalArgumentException(String.format("Unknown size for entry %s", entry));
@ -229,13 +225,13 @@ public final class TIFFWriter extends MetadataWriter {
private long writeValue(final Entry entry, final long dataOffset, final ImageOutputStream stream) throws IOException { private long writeValue(final Entry entry, final long dataOffset, final ImageOutputStream stream) throws IOException {
short type = getType(entry); short type = getType(entry);
int valueLength = getValueLength(type, getCount(entry)); long valueLength = getValueLength(type, getCount(entry));
if (valueLength <= LONGWORD_LENGTH) { if (valueLength <= LONGWORD_LENGTH) {
writeValueInline(entry.getValue(), type, stream); writeValueInline(entry.getValue(), type, stream);
// Pad // Pad
for (int i = valueLength; i < LONGWORD_LENGTH; i++) { for (long i = valueLength; i < LONGWORD_LENGTH; i++) {
stream.write(0); stream.write(0);
} }

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2012, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.tiff;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import java.io.IOException;
import java.util.Locale;
/**
* BigTIFFImageReaderSpi.
* <p/>
* This is a separate service provider for the BigTIFF format, to support
* special cases where one does not want BigTIFF support.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BigTIFFImageReaderSpi.java,v 1.0 08.05.12 15:14 haraldk Exp$
*/
public final class BigTIFFImageReaderSpi extends ImageReaderSpiBase {
/**
* Creates a {@code BigTIFFImageReaderSpi}.
*/
public BigTIFFImageReaderSpi() {
super(new BigTIFFProviderInfo());
}
public boolean canDecodeInput(final Object pSource) throws IOException {
return TIFFImageReaderSpi.canDecodeAs(pSource, TIFF.BIGTIFF_MAGIC);
}
public TIFFImageReader createReaderInstance(final Object pExtension) {
return new TIFFImageReader(this);
}
public String getDescription(final Locale pLocale) {
return "BigTIFF image reader";
}
}

View File

@ -0,0 +1,29 @@
package com.twelvemonkeys.imageio.plugins.tiff;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
/**
* BigTIFFProviderInfo.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
* @version $Id: BigTIFFProviderInfo.java,v 1.0 26/04/2017 harald.kuhr Exp$
*/
final class BigTIFFProviderInfo extends ReaderWriterProviderInfo {
protected BigTIFFProviderInfo() {
super(
BigTIFFProviderInfo.class,
new String[] {"bigtiff", "BigTIFF", "BIGTIFF"},
new String[] {"tif", "tiff", "btf", "tf8", "btiff"},
new String[] {
"image/tiff", "image/x-tiff"
},
"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader",
new String[] {"com.twelvemonkeys.imageio.plugins.tiff.BigTIFFImageReaderSpi"},
null,
null,
false, TIFFStreamMetadata.SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadataFormat", null, null,
true, TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat", null, null
);
}
}

View File

@ -157,7 +157,7 @@ public final class TIFFImageReader extends ImageReaderBase {
private CompoundDirectory IFDs; private CompoundDirectory IFDs;
private Directory currentIFD; private Directory currentIFD;
TIFFImageReader(final TIFFImageReaderSpi provider) { TIFFImageReader(final ImageReaderSpi provider) {
super(provider); super(provider);
} }

View File

@ -71,6 +71,10 @@ public final class TIFFImageReaderSpi extends ImageReaderSpiBase {
} }
public boolean canDecodeInput(final Object pSource) throws IOException { public boolean canDecodeInput(final Object pSource) throws IOException {
return canDecodeAs(pSource, TIFF.TIFF_MAGIC);
}
static boolean canDecodeAs(final Object pSource, final int magic) throws IOException {
if (!(pSource instanceof ImageInputStream)) { if (!(pSource instanceof ImageInputStream)) {
return false; return false;
} }
@ -95,11 +99,7 @@ public final class TIFFImageReaderSpi extends ImageReaderSpiBase {
return false; return false;
} }
// TODO: BigTiff uses version 43 instead of TIFF's 42, and header is slightly different, see return stream.readUnsignedShort() == magic;
// http://www.awaresystems.be/imaging/tiff/bigtiff.html
int magic = stream.readUnsignedShort();
return magic == TIFF.TIFF_MAGIC;
} }
finally { finally {
stream.setByteOrder(originalOrder); stream.setByteOrder(originalOrder);

View File

@ -1 +1,2 @@
com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi
com.twelvemonkeys.imageio.plugins.tiff.BigTIFFImageReaderSpi

View File

@ -0,0 +1,68 @@
package com.twelvemonkeys.imageio.plugins.tiff;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* BigTIFFImageReaderTest.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
* @version $Id: BigTIFFImageReaderTest.java,v 1.0 26/04/2017 harald.kuhr Exp$
*/
public class BigTIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader> {
private static final BigTIFFImageReaderSpi SPI = new BigTIFFImageReaderSpi();
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/bigtiff/BigTIFF.tif"), new Dimension(64, 64)),
new TestData(getClassLoaderResource("/bigtiff/BigTIFFMotorola.tif"), new Dimension(64, 64)),
new TestData(getClassLoaderResource("/bigtiff/BigTIFFLong.tif"), new Dimension(64, 64)),
new TestData(getClassLoaderResource("/bigtiff/BigTIFFLong8.tif"), new Dimension(64, 64)),
new TestData(getClassLoaderResource("/bigtiff/BigTIFFMotorolaLongStrips.tif"), new Dimension(64, 64)),
new TestData(getClassLoaderResource("/bigtiff/BigTIFFLong8Tiles.tif"), new Dimension(64, 64)),
new TestData(getClassLoaderResource("/bigtiff/BigTIFFSubIFD4.tif"), new Dimension(64, 64)),
new TestData(getClassLoaderResource("/bigtiff/BigTIFFSubIFD8.tif"), new Dimension(64, 64))
);
}
@Override
protected ImageReaderSpi createProvider() {
return SPI;
}
@Override
protected Class<TIFFImageReader> getReaderClass() {
return TIFFImageReader.class;
}
@Override
protected TIFFImageReader createReader() {
return SPI.createReaderInstance(null);
}
@Override
protected List<String> getFormatNames() {
return Arrays.asList("bigtiff", "BigTIFF", "BIGTIFF");
}
@Override
protected List<String> getSuffixes() {
return Arrays.asList("tif", "tiff", "btf", "tf8");
}
@Override
protected List<String> getMIMETypes() {
return Collections.singletonList("image/tiff");
}
// TODO: Test that all BigTIFFs are decoded equal to the classic TIFF
// TODO: Test metadata
}

Binary file not shown.

View File

@ -0,0 +1,239 @@
<html>
<head>
</head>
<body>
<p>
These images were created by <a href="http://www.awaresystems.be/">AWare Systems</a>.
</p>
<h1>Index</h1>
<p>
<a href="#classic">Classic.tif</a><br>
<a href="#bigtiff">BigTIFF.tif</a><br>
<a href="#bigtiffmotorola">BigTIFFMotorola.tif</a><br>
<a href="#bigtifflong">BigTIFFLong.tif</a><br>
<a href="#bigtifflong8">BigTIFFLong8.tif</a><br>
<a href="#bigtiffmotorolalongstrips">BigTIFFMotorolaLongStrips.tif</a><br>
<a href="#bigtifflong8tiles">BigTIFFLong8Tiles.tif</a><br>
<a href="#bigtiffsubifd4">BigTIFFSubIFD4.tif</a><br>
<a href="#bigtiffsubifd8">BigTIFFSubIFD8.tif</a><br>
</p>
<h1 id="classic">Classic.tif</h1>
<p>
Classic.tif is a basic Classic TIFF file. All files in this package have the same actual image content,
so this TIFF file serves as a reference.
</p>
<p>
Format: Classic TIFF<br>
Byte Order: Intel<br>
Ifd Offset: 12302<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 12288<br>
</p>
<h1 id="bigtiff">BigTIFF.tif</h1>
<p>
BigTIFF.tif ressembles Classic.tif as close as possible. Except that it's a BigTIFF, that is...
</p>
<p>
Format: BigTIFF<br>
Byte Order: Intel<br>
Ifd Offset: 12304<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 12288<br>
</p>
<h1 id="bigtiffmotorola">BigTIFFMotorola.tif</h1>
<p>
BigTIFFMotorola.tif reverses the byte order.
</p>
<p>
Format: BigTIFF<br>
Byte Order: Motorola<br>
Ifd Offset: 12304<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 12288<br>
</p>
<h1 id="bigtifflong">BigTIFFLong.tif</h1>
<p>
All previous TIFFs specify DataType Short for StripOffsets and StripByteCounts tags. This BigTIFF instead specifies DataType Long, for these tags.
</p>
<p>
Format: BigTIFF<br>
Byte Order: Intel<br>
Ifd Offset: 12304<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Long): 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Long): 12288<br>
</p>
<h1 id="bigtifflong8">BigTIFFLong8.tif</h1>
<p>
This next one specifies DataType Long8, for StripOffsets and StripByteCounts tags.
</p>
<p>
Format: BigTIFF<br>
Byte Order: Intel<br>
Ifd Offset: 12304<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Long8): 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Long8): 12288<br>
</p>
<h1 id="bigtiffmotorolalongstrips">BigTIFFMotorolaLongStrips.tif</h1>
<p>
This BigTIFF has Motorola byte order, plus, it's divided over two strips. StripOffsets and StripByteCounts tags have DataType Long, so their actual value fits inside the IFD.
</p>
<p>
Format: BigTIFF<br>
Byte Order: Motorola<br>
Ifd Offset: 12304<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (2 Long): 16, 6160<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (2 Long): 6144, 6144<br>
</p>
<h1 id="bigtifflong8tiles">BigTIFFLong8Tiles.tif</h1>
<p>
BigTIFFLong8Tiles.tif is a tiled BigTIFF. TileOffsets and TileByteCounts tags specify DataType Long8.
</p>
<p>
Format: BigTIFF<br>
Byte Order: Intel<br>
Ifd Offset: 12368<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;TileWidth (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;TileLength (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;TileOffsets (4 Long8): 16, 3088, 6160, 9232<br>
&nbsp;&nbsp;&nbsp;&nbsp;TileByteCounts (4 Long8): 3072, 3072, 3072, 3072<br>
</p>
<h1 id="bigtiffsubifd4">BigTIFFSubIFD4.tif</h1>
<p>
This BigTIFF contains two pages, the second page showing almost the same image content as the first, except that the black square is white, and text color is black.
Both pages point to a downsample SubIFD, using SubIFDs DataType TIFF_IFD.
</p>
<p>
Format: BigTIFF<br>
Byte Order: Intel<br>
Ifd Offset: 15572<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 3284<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 12288<br>
&nbsp;&nbsp;&nbsp;&nbsp;SubIFDs (1 IFD): 3088<br>
SubIfd Offset: 3088<br>
&nbsp;&nbsp;&nbsp;&nbsp;NewSubFileType (1 Long): 1<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 3072<br>
Ifd Offset: 31324<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 19036<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 12288<br>
&nbsp;&nbsp;&nbsp;&nbsp;SubIFDs (1 IFD): 18840<br>
SubIfd Offset: 18840<br>
&nbsp;&nbsp;&nbsp;&nbsp;NewSubFileType (1 Long): 1<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 15768<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 3072<br>
</p>
<h1 id="bigtiffsubifd8">BigTIFFSubIFD8.tif</h1>
<p>
BigTIFFSubIFD4.tif is very much the same as BigTIFFSubIFD4.tif, except that the new DataType TIFF_IFD8 is used for the SubIFDs tag.
</p>
<p>
Format: BigTIFF<br>
Byte Order: Intel<br>
Ifd Offset: 15572<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 3284<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 12288<br>
&nbsp;&nbsp;&nbsp;&nbsp;SubIFDs (1 IFD8): 3088<br>
SubIfd Offset: 3088<br>
&nbsp;&nbsp;&nbsp;&nbsp;NewSubFileType (1 Long): 1<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 16<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 3072<br>
Ifd Offset: 31324<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 19036<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 64<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 12288<br>
&nbsp;&nbsp;&nbsp;&nbsp;SubIFDs (1 IFD8): 18840<br>
SubIfd Offset: 18840<br>
&nbsp;&nbsp;&nbsp;&nbsp;NewSubFileType (1 Long): 1<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageWidth (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;ImageLength (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;BitsPerSample (3 Short): 8, 8, 8<br>
&nbsp;&nbsp;&nbsp;&nbsp;PhotometricInterpretation (1 Short): RGB<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripOffsets (1 Short): 15768<br>
&nbsp;&nbsp;&nbsp;&nbsp;SamplesPerPixel (1 Short): 3<br>
&nbsp;&nbsp;&nbsp;&nbsp;RowsPerStrip (1 Short): 32<br>
&nbsp;&nbsp;&nbsp;&nbsp;StripByteCounts (1 Short): 3072<br>
</p>
</body>
</html>

Binary file not shown.