mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-10-04 11:26:44 -04:00
TMI-TIFF: Initial commit. Major work in progress. :-)
This commit is contained in:
@@ -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";
|
||||
|
@@ -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(" ");
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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"
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user