TMI-TIFF: Initial commit. Major work in progress. :-)

This commit is contained in:
Harald Kuhr
2012-05-22 00:00:11 +02:00
parent 9492ed67f1
commit 98361194ea
36 changed files with 2070 additions and 678 deletions

View File

@@ -90,6 +90,8 @@ final class EXIFEntry extends AbstractEntry {
return "Orientation";
case TIFF.TAG_SAMPLES_PER_PIXELS:
return "SamplesPerPixels";
case TIFF.TAG_ROWS_PER_STRIP:
return "RowsPerStrip";
case TIFF.TAG_X_RESOLUTION:
return "XResolution";
case TIFF.TAG_Y_RESOLUTION:
@@ -120,6 +122,10 @@ final class EXIFEntry extends AbstractEntry {
return "YCbCrSubSampling";
case TIFF.TAG_YCBCR_POSITIONING:
return "YCbCrPositioning";
case TIFF.TAG_COLOR_MAP:
return "ColorMap";
case TIFF.TAG_EXTRA_SAMPLES:
return "ExtraSamples";
case EXIF.TAG_EXPOSURE_TIME:
return "ExposureTime";

View File

@@ -95,6 +95,7 @@ public final class EXIFReader extends MetadataReader {
EXIFEntry entry = readEntry(pInput);
if (entry == null) {
// System.err.println("Expected: " + entryCount + " values, found only " + i);
// TODO: Log warning?
nextOffset = 0;
break;
@@ -199,13 +200,13 @@ public final class EXIFReader extends MetadataReader {
Object value = entry.getValue();
if (value instanceof Byte) {
offset = ((Byte) value & 0xff);
offset = (Byte) value & 0xff;
}
else if (value instanceof Short) {
offset = ((Short) value & 0xffff);
offset = (Short) value & 0xffff;
}
else if (value instanceof Integer) {
offset = ((Integer) value & 0xffffffffL);
offset = (Integer) value & 0xffffffffL;
}
else if (value instanceof Long) {
offset = (Long) value;
@@ -222,7 +223,7 @@ public final class EXIFReader extends MetadataReader {
int tagId = pInput.readUnsignedShort();
short type = pInput.readShort();
// This isn't really an entry, and the directory entry count was wront
// This isn't really an entry, and the directory entry count was wrong OR bad data...
if (tagId == 0 && type == 0) {
return null;
}
@@ -236,24 +237,28 @@ public final class EXIFReader extends MetadataReader {
if (type <= 0 || type > 13) {
// Invalid tag, this is just for debugging
System.err.printf("Bad EXIF data at offset: %08x\n", pInput.getStreamPosition() - 8l);
System.err.println("tagId: " + tagId);
long offset = pInput.getStreamPosition() - 8l;
System.err.printf("Bad EXIF");
System.err.println("tagId: " + tagId + (tagId <= 0 ? "(INVALID)" : ""));
System.err.println("type: " + type + " (INVALID)");
System.err.println("count: " + count);
pInput.mark();
pInput.seek(pInput.getStreamPosition() - 8);
pInput.seek(offset);
try {
byte[] bytes = new byte[8 + Math.max(20, count)];
int len = pInput.read(bytes);
System.err.print("data: " + HexDump.dump(bytes, 0, len));
System.err.print(HexDump.dump(offset, bytes, 0, len));
System.err.println(len < count ? "[...]" : "");
}
finally {
pInput.reset();
}
return null;
}
int valueLength = getValueLength(type, count);
@@ -484,7 +489,7 @@ public final class EXIFReader extends MetadataReader {
Object value = entry.getValue();
if (value instanceof byte[]) {
byte[] bytes = (byte[]) value;
System.err.println(HexDump.dump(bytes, 0, Math.min(bytes.length, 128)));
System.err.println(HexDump.dump(0, bytes, 0, Math.min(bytes.length, 128)));
}
}
}
@@ -501,10 +506,10 @@ public final class EXIFReader extends MetadataReader {
private static final int WIDTH = 32;
public static String dump(byte[] bytes) {
return dump(bytes, 0, bytes.length);
return dump(0, bytes, 0, bytes.length);
}
public static String dump(byte[] bytes, int off, int len) {
public static String dump(long offset, byte[] bytes, int off, int len) {
StringBuilder builder = new StringBuilder();
int i;
@@ -513,7 +518,7 @@ public final class EXIFReader extends MetadataReader {
if (i > 0 ) {
builder.append("\n");
}
builder.append(String.format("%08x: ", i + off));
builder.append(String.format("%08x: ", i + off + offset));
}
else if (i > 0 && i % 2 == 0) {
builder.append(" ");

View File

@@ -118,8 +118,11 @@ public interface TIFF {
/// C. Tags relating to image data characteristics
int TAG_TRANSFER_FUNCTION = 301;
int TAG_PREDICTOR = 317;
int TAG_WHITE_POINT = 318;
int TAG_PRIMARY_CHROMATICITIES = 319;
int TAG_COLOR_MAP = 320;
int TAG_EXTRA_SAMPLES = 338;
int TAG_YCBCR_COEFFICIENTS = 529;
int TAG_REFERENCE_BLACK_WHITE = 532;
@@ -151,4 +154,11 @@ public interface TIFF {
int TAG_MODI_PLAIN_TEXT = 37679;
int TAG_MODI_OLE_PROPERTY_SET = 37680;
int TAG_MODI_TEXT_POS_INFO = 37681;
int TAG_TILE_WIDTH = 322;
int TAG_TILE_HEIGTH = 323;
int TAG_TILE_OFFSETS = 324;
int TAG_TILE_BYTE_COUNTS = 325;
int TAG_JPEG_TABLES = 347;
}

View File

@@ -45,6 +45,8 @@ public interface JPEG {
/** Define Quantization Tables segment marker (DQT). */
int DQT = 0xFFDB;
/** Define Huffman Tables segment marker (DHT). */
int DHT = 0xFFC4;
// App segment markers (APPn).
int APP0 = 0xFFE0;

View File

@@ -30,10 +30,12 @@ package com.twelvemonkeys.imageio.metadata.jpeg;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.plugins.jpeg.JPEGQTable;
import javax.imageio.stream.ImageInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
@@ -83,7 +85,7 @@ public final class JPEGQuality {
}
// Adapted from ImageMagick coders/jpeg.c & http://blog.apokalyptik.com/2009/09/16/quality-time-with-your-jpegs/
private static int getJPEGQuality(final short[][] quantizationTables) throws IOException {
private static int getJPEGQuality(final int[][] quantizationTables) throws IOException {
// System.err.println("tables: " + Arrays.deepToString(tables));
// TODO: Determine lossless JPEG
@@ -188,8 +190,24 @@ public final class JPEGQuality {
return -1;
}
private static short[][] getQuantizationTables(List<JPEGSegment> dqtSegments) throws IOException {
short[][] tables = new short[4][];
public static JPEGQTable[] getQTables(final List<JPEGSegment> dqtSegments) throws IOException {
int[][] tables = getQuantizationTables(dqtSegments);
List<JPEGQTable> qTables = new ArrayList<JPEGQTable>();
for (int[] table : tables) {
if (table != null) {
qTables.add(new JPEGQTable(table));
}
else {
break;
}
}
return qTables.toArray(new JPEGQTable[qTables.size()]);
}
private static int[][] getQuantizationTables(final List<JPEGSegment> dqtSegments) throws IOException {
int[][] tables = new int[4][];
// JPEG may contain multiple DQT marker segments
for (JPEGSegment segment : dqtSegments) {
@@ -223,7 +241,7 @@ public final class JPEGQuality {
byte[] qtData = new byte[DCT_SIZE_2 * (bits + 1)];
data.readFully(qtData);
read += qtData.length;
tables[num] = new short[DCT_SIZE_2];
tables[num] = new int[DCT_SIZE_2];
// Expand (this is slightly inefficient)
switch (bits) {

View File

@@ -175,4 +175,19 @@ public class EXIFReaderTest extends MetadataReaderAbstractTest {
assertNotNull(exif);
assertEquals(3, exif.size());
}
@Test
public void testTIFFWithBadExifIFD() throws IOException {
// This image seems to contain bad TIFF data. But as other tools are able to read, so should we..
// It seems that the EXIF data (at offset 494196 or 0x78a74) overlaps with a custom
// Microsoft 'OLE Property Set' entry at 0x78a70 (UNDEFINED, count 5632)...
ImageInputStream stream = ImageIO.createImageInputStream(getResource("/tiff/chifley_logo.tif"));
Directory directory = createReader().read(stream);
assertEquals(22, directory.size());
// Some (all?) of the EXIF data is duplicated in the XMP, meaning PhotoShop can probably re-create it
Directory exif = (Directory) directory.getEntryById(TIFF.TAG_EXIF_IFD).getValue();
assertNotNull(exif);
assertEquals(0, exif.size()); // EXIFTool reports "Warning: Bad ExifIFD directory"
}
}

View File

@@ -145,6 +145,10 @@ public class JPEGQualityTest {
}
}
@Test
public void testGetQTables() {
fail("Not implemented");
}
private BufferedImage createTestImage() {
BufferedImage image = new BufferedImage(90, 60, BufferedImage.TYPE_3BYTE_BGR);