X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fdrew%2Fjpeg%2FExifReader.java;h=0084b87b169a6b0913f386e677c34e15aaeb757f;hb=1ee49ae3c8ef3aa2e63eadd458531e5f8bd4f92c;hp=4e58199f0aab1bd4c5d1fccc6a164e1c114c9196;hpb=23959e65a6a0d581e657b07186d18b7a1ac5afeb;p=GpsPrune.git diff --git a/tim/prune/drew/jpeg/ExifReader.java b/tim/prune/drew/jpeg/ExifReader.java index 4e58199..0084b87 100644 --- a/tim/prune/drew/jpeg/ExifReader.java +++ b/tim/prune/drew/jpeg/ExifReader.java @@ -20,31 +20,32 @@ public class ExifReader */ private boolean _isMotorolaByteOrder; - /** - * The number of bytes used per format descriptor. - */ + /** Thumbnail offset */ + private int _thumbnailOffset = -1; + /** Thumbnail length */ + private int _thumbnailLength = -1; + + /** The number of bytes used per format descriptor */ private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; - /** - * The number of formats known. - */ + /** The number of formats known */ private static final int MAX_FORMAT_CODE = 12; // Format types // Note: Cannot use the DataFormat enumeration in the case statement that uses these tags. // Is there a better way? - private static final int FMT_BYTE = 1; + //private static final int FMT_BYTE = 1; private static final int FMT_STRING = 2; - private static final int FMT_USHORT = 3; - private static final int FMT_ULONG = 4; + //private static final int FMT_USHORT = 3; + //private static final int FMT_ULONG = 4; private static final int FMT_URATIONAL = 5; - private static final int FMT_SBYTE = 6; - private static final int FMT_UNDEFINED = 7; - private static final int FMT_SSHORT = 8; - private static final int FMT_SLONG = 9; + //private static final int FMT_SBYTE = 6; + //private static final int FMT_UNDEFINED = 7; + //private static final int FMT_SSHORT = 8; + //private static final int FMT_SLONG = 9; private static final int FMT_SRATIONAL = 10; - private static final int FMT_SINGLE = 11; - private static final int FMT_DOUBLE = 12; + //private static final int FMT_SINGLE = 11; + //private static final int FMT_DOUBLE = 12; public static final int TAG_EXIF_OFFSET = 0x8769; public static final int TAG_INTEROP_OFFSET = 0xA005; @@ -71,11 +72,20 @@ public class ExifReader public static final int TAG_GPS_TIMESTAMP = 0x0007; /** GPS date (atomic clock) GPSDateStamp 23 1d RATIONAL 3 */ public static final int TAG_GPS_DATESTAMP = 0x001d; + /** Exif timestamp */ + public static final int TAG_DATETIME_ORIGINAL = 0x9003; + /** Thumbnail offset */ + private static final int TAG_THUMBNAIL_OFFSET = 0x0201; + /** Thumbnail length */ + private static final int TAG_THUMBNAIL_LENGTH = 0x0202; + /** Orientation of image */ + private static final int TAG_ORIENTATION = 0x0112; + /** - * Creates an ExifReader for a Jpeg file. + * Creates an ExifReader for a Jpeg file * @param inFile File object to attempt to read from - * @throws JpegProcessingException on failure + * @throws JpegException on failure */ public ExifReader(File inFile) throws JpegException { @@ -132,7 +142,7 @@ public class ExifReader firstDirectoryOffset = 14; } - HashMap processedDirectoryOffsets = new HashMap(); + HashMap processedDirectoryOffsets = new HashMap(); // 0th IFD (we merge with Exif IFD) processDirectory(metadata, false, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET); @@ -167,15 +177,15 @@ public class ExifReader * 2 bytes: format code * 4 bytes: component count */ - private void processDirectory(JpegData inMetadata, boolean inIsGPS, HashMap inDirectoryOffsets, + private void processDirectory(JpegData inMetadata, boolean inIsGPS, HashMap inDirectoryOffsets, int inDirOffset, int inTiffHeaderOffset) { // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist - if (inDirectoryOffsets.containsKey(new Integer(inDirOffset))) + if (inDirectoryOffsets.containsKey(Integer.valueOf(inDirOffset))) return; // remember that we've visited this directory so that we don't visit it again later - inDirectoryOffsets.put(new Integer(inDirOffset), "processed"); + inDirectoryOffsets.put(Integer.valueOf(inDirOffset), "processed"); if (inDirOffset >= _data.length || inDirOffset < 0) { @@ -238,11 +248,11 @@ public class ExifReader // Calculate the value as an offset for cases where the tag represents a directory final int subdirOffset = inTiffHeaderOffset + get32Bits(tagValueOffset); - // TODO: Also look for timestamp(s) in Exif for correlation - which directory? + // Look in both basic Exif tags (for timestamp, thumbnail) and Gps tags (for lat, long, altitude, timestamp) switch (tagType) { case TAG_EXIF_OFFSET: - // ignore + processDirectory(inMetadata, false, inDirectoryOffsets, subdirOffset, inTiffHeaderOffset); continue; case TAG_INTEROP_OFFSET: // ignore @@ -255,9 +265,14 @@ public class ExifReader continue; default: // not a known directory, so must just be a normal tag - // ignore if we're not in gps directory if (inIsGPS) + { processGpsTag(inMetadata, tagType, tagValueOffset, componentCount, formatCode); + } + else + { + processExifTag(inMetadata, tagType, tagValueOffset, componentCount, formatCode); + } break; } } @@ -336,16 +351,61 @@ public class ExifReader inMetadata.setAltitude(readRational(inTagValueOffset, inFormatCode, inComponentCount)); break; case TAG_GPS_TIMESTAMP: - inMetadata.setTimestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount)); + inMetadata.setGpsTimestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount)); break; case TAG_GPS_DATESTAMP: - inMetadata.setDatestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount)); + inMetadata.setGpsDatestamp(readRationalArray(inTagValueOffset, inFormatCode, inComponentCount)); break; default: // ignore all other tags } } + /** + * Process a general Exif tag + * @param inMetadata metadata holding extracted values + * @param inTagType tag type (eg latitude) + * @param inTagValueOffset start offset in data array + * @param inComponentCount component count for tag + * @param inFormatCode format code, eg byte + */ + private void processExifTag(JpegData inMetadata, int inTagType, int inTagValueOffset, + int inComponentCount, int inFormatCode) + { + // Only interested in original timestamp, thumbnail offset and thumbnail length + if (inTagType == TAG_DATETIME_ORIGINAL) + { + inMetadata.setOriginalTimestamp(readString(inTagValueOffset, inFormatCode, inComponentCount)); + } + else if (inTagType == TAG_THUMBNAIL_OFFSET) { + _thumbnailOffset = TIFF_HEADER_START_OFFSET + get16Bits(inTagValueOffset); + extractThumbnail(inMetadata); + } + else if (inTagType == TAG_THUMBNAIL_LENGTH) { + _thumbnailLength = get16Bits(inTagValueOffset); + extractThumbnail(inMetadata); + } + else if (inTagType == TAG_ORIENTATION) { + if (inMetadata.getOrientationCode() < 1) { + inMetadata.setOrientationCode(get16Bits(inTagValueOffset)); + } + } + } + + /** + * Attempt to extract the thumbnail image + */ + private void extractThumbnail(JpegData inMetadata) + { + if (_thumbnailOffset > 0 && _thumbnailLength > 0 && inMetadata.getThumbnailImage() == null) + { + byte[] thumbnailBytes = new byte[_thumbnailLength]; + System.arraycopy(_data, _thumbnailOffset, thumbnailBytes, 0, _thumbnailLength); + inMetadata.setThumbnailImage(thumbnailBytes); + } + } + + /** * Calculate the tag value offset * @param inByteCount @@ -358,7 +418,7 @@ public class ExifReader if (inByteCount > 4) { // If it's bigger than 4 bytes, the dir entry contains an offset. - // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an + // dirEntryOffset must be passed, as some makers (e.g. FujiFilm) incorrectly use an // offset relative to the start of the makernote itself, not the TIFF segment. final int offsetVal = get32Bits(inDirEntryOffset + 8); if (offsetVal + inByteCount > _data.length) @@ -453,7 +513,8 @@ public class ExifReader private int get16Bits(int offset) { if (offset<0 || offset+2>_data.length) - throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")"); + throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + + offset + " where max index is " + (_data.length - 1) + ")"); if (_isMotorolaByteOrder) { // Motorola - MSB first