X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2Fdrew%2Fjpeg%2FExifReader.java;h=9e0fd5980fd0f33fa64a61a46b0db6fc345c53b4;hb=54b9d8bc8f0025ccf97a67d9dd217ef1f9cf082f;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..9e0fd59 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,18 @@ 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; + /** - * 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 +140,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 +175,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 +246,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 +263,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 +349,56 @@ 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); + } + } + + /** + * 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