diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 2c4c6e55d542f..d413083c2ebb9 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -25,6 +25,7 @@ import android.system.Os; import android.system.OsConstants; import android.util.Log; import android.util.Pair; +import android.annotation.IntDef; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -55,6 +56,8 @@ import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import libcore.io.IoUtils; import libcore.io.Streams; @@ -1166,16 +1169,24 @@ public class ExifInterface { // The following values are used for indicating pointers to the other Image File Directories. // Indices of Exif Ifd tag groups - private static final int IFD_TIFF_HINT = 0; - private static final int IFD_EXIF_HINT = 1; - private static final int IFD_GPS_HINT = 2; - private static final int IFD_INTEROPERABILITY_HINT = 3; - private static final int IFD_THUMBNAIL_HINT = 4; - private static final int IFD_PREVIEW_HINT = 5; - private static final int ORF_MAKER_NOTE_HINT = 6; - private static final int ORF_CAMERA_SETTINGS_HINT = 7; - private static final int ORF_IMAGE_PROCESSING_HINT = 8; - private static final int PEF_HINT = 9; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({IFD_TYPE_PRIMARY, IFD_TYPE_EXIF, IFD_TYPE_GPS, IFD_TYPE_INTEROPERABILITY, + IFD_TYPE_THUMBNAIL, IFD_TYPE_PREVIEW, IFD_TYPE_ORF_MAKER_NOTE, + IFD_TYPE_ORF_CAMERA_SETTINGS, IFD_TYPE_ORF_IMAGE_PROCESSING, IFD_TYPE_PEF}) + public @interface IfdType {} + + private static final int IFD_TYPE_PRIMARY = 0; + private static final int IFD_TYPE_EXIF = 1; + private static final int IFD_TYPE_GPS = 2; + private static final int IFD_TYPE_INTEROPERABILITY = 3; + private static final int IFD_TYPE_THUMBNAIL = 4; + private static final int IFD_TYPE_PREVIEW = 5; + private static final int IFD_TYPE_ORF_MAKER_NOTE = 6; + private static final int IFD_TYPE_ORF_CAMERA_SETTINGS = 7; + private static final int IFD_TYPE_ORF_IMAGE_PROCESSING = 8; + private static final int IFD_TYPE_PEF = 9; + // List of Exif tag groups private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] { IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS, @@ -1191,11 +1202,7 @@ public class ExifInterface { new ExifTag(TAG_ORF_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE), new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE) }; - // List of indices of the indicated tag groups according to the EXIF_POINTER_TAGS - private static final int[] EXIF_POINTER_TAG_HINTS = new int[] { - IFD_PREVIEW_HINT, IFD_EXIF_HINT, IFD_GPS_HINT, IFD_INTEROPERABILITY_HINT, - ORF_CAMERA_SETTINGS_HINT, ORF_IMAGE_PROCESSING_HINT - }; + // Tags for indicating the thumbnail offset and length private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG = new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG); @@ -1209,6 +1216,8 @@ public class ExifInterface { private static final HashSet sTagSetForCompatibility = new HashSet<>(Arrays.asList( TAG_F_NUMBER, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE, TAG_GPS_TIMESTAMP)); + // Mappings from tag number to IFD type for pointer tags. + private static final HashMap sExifPointerTagMap = new HashMap(); // See JPEG File Interchange Format Version 1.02. // The following values are defined for handling JPEG streams. In this implementation, we are @@ -1260,14 +1269,22 @@ public class ExifInterface { sFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); // Build up the hash tables to look up Exif tags for reading Exif tags. - for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { - sExifTagMapsForReading[hint] = new HashMap(); - sExifTagMapsForWriting[hint] = new HashMap(); - for (ExifTag tag : EXIF_TAGS[hint]) { - sExifTagMapsForReading[hint].put(tag.number, tag); - sExifTagMapsForWriting[hint].put(tag.name, tag); + for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { + sExifTagMapsForReading[ifdType] = new HashMap(); + sExifTagMapsForWriting[ifdType] = new HashMap(); + for (ExifTag tag : EXIF_TAGS[ifdType]) { + sExifTagMapsForReading[ifdType].put(tag.number, tag); + sExifTagMapsForWriting[ifdType].put(tag.name, tag); } } + + // Build up the hash table to look up Exif pointer tags. + sExifPointerTagMap.put(EXIF_POINTER_TAGS[0].number, IFD_TYPE_PREVIEW); // 330 + sExifPointerTagMap.put(EXIF_POINTER_TAGS[1].number, IFD_TYPE_EXIF); // 34665 + sExifPointerTagMap.put(EXIF_POINTER_TAGS[2].number, IFD_TYPE_GPS); // 34853 + sExifPointerTagMap.put(EXIF_POINTER_TAGS[3].number, IFD_TYPE_INTEROPERABILITY); // 40965 + sExifPointerTagMap.put(EXIF_POINTER_TAGS[4].number, IFD_TYPE_ORF_CAMERA_SETTINGS); // 8224 + sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256 } private final String mFilename; @@ -1503,7 +1520,7 @@ public class ExifInterface { } for (int i = 0 ; i < EXIF_TAGS.length; ++i) { - if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) { + if (i == IFD_TYPE_THUMBNAIL && !mHasThumbnail) { continue; } final Object obj = sExifTagMapsForWriting[i].get(tag); @@ -1662,7 +1679,7 @@ public class ExifInterface { switch (mMimeType) { case IMAGE_TYPE_JPEG: { - getJpegAttributes(in, 0, IFD_TIFF_HINT); // 0 is offset + getJpegAttributes(in, 0, IFD_TYPE_PRIMARY); // 0 is offset break; } case IMAGE_TYPE_RAF: { @@ -1894,9 +1911,9 @@ public class ExifInterface { } ExifAttribute imageLengthAttribute = - (ExifAttribute) mAttributes[IFD_THUMBNAIL_HINT].get(TAG_IMAGE_LENGTH); + (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH); ExifAttribute imageWidthAttribute = - (ExifAttribute) mAttributes[IFD_THUMBNAIL_HINT].get(TAG_IMAGE_WIDTH); + (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH); if (imageLengthAttribute != null && imageWidthAttribute != null) { int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder); int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder); @@ -2165,9 +2182,9 @@ public class ExifInterface { * * @param inputStream The input stream that starts with the JPEG data. * @param jpegOffset The offset value in input stream for JPEG data. - * @param imageTypes The image type from which to retrieve metadata. Use IFD_TIFF_HINT for - * primary image, IFD_PREVIEW_HINT for preview image, and - * IFD_THUMBNAIL_HINT for thumbnail image. + * @param imageTypes The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for + * primary image, IFD_TYPE_PREVIEW for preview image, and + * IFD_TYPE_THUMBNAIL for thumbnail image. * @throws IOException If the data contains invalid JPEG markers, offsets, or length values. */ private void getJpegAttributes(InputStream inputStream, int jpegOffset, int imageType) @@ -2268,7 +2285,7 @@ public class ExifInterface { } length = 0; if (getAttribute(TAG_USER_COMMENT) == null) { - mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, ExifAttribute.createString( + mAttributes[IFD_TYPE_EXIF].put(TAG_USER_COMMENT, ExifAttribute.createString( new String(bytes, ASCII))); } break; @@ -2328,12 +2345,12 @@ public class ExifInterface { parseTiffHeaders(dataInputStream, exifBytes.length); // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6. - readImageFileDirectory(dataInputStream, IFD_TIFF_HINT); + readImageFileDirectory(dataInputStream, IFD_TYPE_PRIMARY); // Update ImageLength/Width tags for all image data. - updateImageSizeValues(in, IFD_TIFF_HINT); - updateImageSizeValues(in, IFD_PREVIEW_HINT); - updateImageSizeValues(in, IFD_THUMBNAIL_HINT); + updateImageSizeValues(in, IFD_TYPE_PRIMARY); + updateImageSizeValues(in, IFD_TYPE_PREVIEW); + updateImageSizeValues(in, IFD_TYPE_THUMBNAIL); // Check if each image data is in valid position. validateImages(in); @@ -2342,7 +2359,7 @@ public class ExifInterface { // PEF files contain a MakerNote data, which contains the data for ColorSpace tag. // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData() ExifAttribute makerNoteAttribute = - (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE); + (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE); if (makerNoteAttribute != null) { // Create an ordered DataInputStream for MakerNote ByteOrderAwarenessDataInputStream makerNoteDataInputStream = @@ -2353,13 +2370,13 @@ public class ExifInterface { makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE); // Read IFD data from MakerNote - readImageFileDirectory(makerNoteDataInputStream, PEF_HINT); + readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_PEF); // Update ColorSpace tag ExifAttribute colorSpaceAttribute = - (ExifAttribute) mAttributes[PEF_HINT].get(TAG_COLOR_SPACE); + (ExifAttribute) mAttributes[IFD_TYPE_PEF].get(TAG_COLOR_SPACE); if (colorSpaceAttribute != null) { - mAttributes[IFD_EXIF_HINT].put(TAG_COLOR_SPACE, colorSpaceAttribute); + mAttributes[IFD_TYPE_EXIF].put(TAG_COLOR_SPACE, colorSpaceAttribute); } } } @@ -2392,7 +2409,7 @@ public class ExifInterface { in.reset(); // Retrieve JPEG image metadata - getJpegAttributes(in, rafJpegOffset, IFD_PREVIEW_HINT); + getJpegAttributes(in, rafJpegOffset, IFD_TYPE_PREVIEW); // Skip to CFA header offset. // A while loop is used because the skip method may not be able to skip the requested amount @@ -2429,8 +2446,8 @@ public class ExifInterface { ExifAttribute.createUShort(imageLength, mExifByteOrder); ExifAttribute imageWidthAttribute = ExifAttribute.createUShort(imageWidth, mExifByteOrder); - mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, imageLengthAttribute); - mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, imageWidthAttribute); + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, imageLengthAttribute); + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, imageWidthAttribute); if (DEBUG) { Log.d(TAG, "Updated to length: " + imageLength + ", width: " + imageWidth); } @@ -2459,7 +2476,7 @@ public class ExifInterface { // proprietary tags and therefore does not have offical documentation // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html ExifAttribute makerNoteAttribute = - (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE); + (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_MAKER_NOTE); if (makerNoteAttribute != null) { // Create an ordered DataInputStream for MakerNote ByteOrderAwarenessDataInputStream makerNoteDataInputStream = @@ -2481,18 +2498,18 @@ public class ExifInterface { } // Read IFD data from MakerNote - readImageFileDirectory(makerNoteDataInputStream, ORF_MAKER_NOTE_HINT); + readImageFileDirectory(makerNoteDataInputStream, IFD_TYPE_ORF_MAKER_NOTE); // Retrieve & update preview image offset & length values ExifAttribute imageLengthAttribute = (ExifAttribute) - mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_ORF_PREVIEW_IMAGE_START); + mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_START); ExifAttribute bitsPerSampleAttribute = (ExifAttribute) - mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_ORF_PREVIEW_IMAGE_LENGTH); + mAttributes[IFD_TYPE_ORF_CAMERA_SETTINGS].get(TAG_ORF_PREVIEW_IMAGE_LENGTH); if (imageLengthAttribute != null && bitsPerSampleAttribute != null) { - mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT, + mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT, imageLengthAttribute); - mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, + mAttributes[IFD_TYPE_PREVIEW].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, bitsPerSampleAttribute); } @@ -2500,7 +2517,7 @@ public class ExifInterface { // Retrieve primary image length & width values // See piex.cc GetOlympusPreviewImage() ExifAttribute aspectFrameAttribute = (ExifAttribute) - mAttributes[ORF_IMAGE_PROCESSING_HINT].get(TAG_ORF_ASPECT_FRAME); + mAttributes[IFD_TYPE_ORF_IMAGE_PROCESSING].get(TAG_ORF_ASPECT_FRAME); if (aspectFrameAttribute != null) { int[] aspectFrameValues = new int[4]; aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder); @@ -2519,8 +2536,8 @@ public class ExifInterface { ExifAttribute primaryImageLengthAttribute = ExifAttribute.createUShort(primaryImageLength, mExifByteOrder); - mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute); - mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute); + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute); + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute); } } } @@ -2535,47 +2552,19 @@ public class ExifInterface { // Retrieve preview and/or thumbnail image data ExifAttribute jpgFromRawAttribute = - (ExifAttribute) mAttributes[IFD_TIFF_HINT].get(TAG_RW2_JPG_FROM_RAW); + (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_JPG_FROM_RAW); if (jpgFromRawAttribute != null) { - getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_PREVIEW_HINT); + getJpegAttributes(in, mRw2JpgFromRawOffset, IFD_TYPE_PREVIEW); } // Set ISO tag value if necessary ExifAttribute rw2IsoAttribute = - (ExifAttribute) mAttributes[IFD_TIFF_HINT].get(TAG_RW2_ISO); + (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].get(TAG_RW2_ISO); ExifAttribute exifIsoAttribute = - (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_ISO_SPEED_RATINGS); + (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_ISO_SPEED_RATINGS); if (rw2IsoAttribute != null && exifIsoAttribute == null) { // Place this attribute only if it doesn't exist - mAttributes[IFD_EXIF_HINT].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute); - } - } - - // PEF is TIFF-based and contains 3 IFDs. It also contains a MakerNote data, which contains the - // ColorSpace tag data. - // See http://lclevy.free.fr/raw/ and piex.cc PefGetPreviewData() - private void getPefAttributes(InputStream in) throws IOException { - // Retrieve ColorSpace tag - ExifAttribute makerNoteAttribute = - (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE); - if (makerNoteAttribute != null) { - // Create an ordered DataInputStream for MakerNote - ByteOrderAwarenessDataInputStream makerNoteDataInputStream = - new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes); - makerNoteDataInputStream.setByteOrder(mExifByteOrder); - - // Seek to MakerNote data - makerNoteDataInputStream.seek(PEF_MAKER_NOTE_SKIP_SIZE); - - // Read IFD data from MakerNote - readImageFileDirectory(makerNoteDataInputStream, PEF_HINT); - - // Update ColorSpace tag - ExifAttribute colorSpaceAttribute = - (ExifAttribute) mAttributes[PEF_HINT].get(TAG_COLOR_SPACE); - if (colorSpaceAttribute != null) { - mAttributes[IFD_EXIF_HINT].put(TAG_COLOR_SPACE, colorSpaceAttribute); - } + mAttributes[IFD_TYPE_EXIF].put(TAG_ISO_SPEED_RATINGS, rw2IsoAttribute); } } @@ -2693,25 +2682,25 @@ public class ExifInterface { // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag. String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL); if (valueOfDateTimeOriginal != null) { - mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME, + mAttributes[IFD_TYPE_PRIMARY].put(TAG_DATETIME, ExifAttribute.createString(valueOfDateTimeOriginal)); } // Add the default value. if (getAttribute(TAG_IMAGE_WIDTH) == null) { - mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(0, mExifByteOrder)); } if (getAttribute(TAG_IMAGE_LENGTH) == null) { - mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, + mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(0, mExifByteOrder)); } if (getAttribute(TAG_ORIENTATION) == null) { - mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION, + mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION, ExifAttribute.createULong(0, mExifByteOrder)); } if (getAttribute(TAG_LIGHT_SOURCE) == null) { - mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE, + mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE, ExifAttribute.createULong(0, mExifByteOrder)); } } @@ -2763,8 +2752,8 @@ public class ExifInterface { } // Reads image file directory, which is a tag group in EXIF. - private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint) - throws IOException { + private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, + @IfdType int ifdType) throws IOException { if (dataInputStream.peek() + 2 > dataInputStream.mLength) { // Return if there is no data from the offset. return; @@ -2789,12 +2778,12 @@ public class ExifInterface { long nextEntryOffset = dataInputStream.peek() + 4; // Look up a corresponding tag from tag number - ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber); + ExifTag tag = (ExifTag) sExifTagMapsForReading[ifdType].get(tagNumber); if (DEBUG) { - Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " + - "numberOfComponents: %d", hint, tagNumber, tag != null ? tag.name : null, - dataFormat, numberOfComponents)); + Log.d(TAG, String.format("ifdType: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " + + "numberOfComponents: %d", ifdType, tagNumber, + tag != null ? tag.name : null, dataFormat, numberOfComponents)); } if (tag == null || dataFormat <= 0 || @@ -2821,7 +2810,8 @@ public class ExifInterface { if (tag.name == TAG_MAKER_NOTE) { // Save offset value for reading thumbnail mOrfMakerNoteOffset = offset; - } else if (hint == ORF_MAKER_NOTE_HINT && tag.name == TAG_ORF_THUMBNAIL_IMAGE) { + } else if (ifdType == IFD_TYPE_ORF_MAKER_NOTE + && tag.name == TAG_ORF_THUMBNAIL_IMAGE) { // Retrieve & update values for thumbnail offset and length values for ORF mOrfThumbnailOffset = offset; mOrfThumbnailLength = numberOfComponents; @@ -2833,10 +2823,10 @@ public class ExifInterface { ExifAttribute jpegInterchangeFormatLengthAttribute = ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder); - mAttributes[IFD_THUMBNAIL_HINT].put(TAG_COMPRESSION, compressionAttribute); - mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT, + mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_COMPRESSION, compressionAttribute); + mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT, jpegInterchangeFormatAttribute); - mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, + mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, jpegInterchangeFormatLengthAttribute); } } else if (mMimeType == IMAGE_TYPE_RW2) { @@ -2855,12 +2845,12 @@ public class ExifInterface { } // Recursively parse IFD when a IFD pointer tag appears. - int innerIfdHint = getIfdHintFromTagNumber(tagNumber); + Object nextIfdType = sExifPointerTagMap.get(tagNumber); if (DEBUG) { - Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount); + Log.d(TAG, "nextIfdType: " + nextIfdType + " byteCount: " + byteCount); } - if (innerIfdHint >= 0) { + if (nextIfdType != null) { long offset = -1L; // Get offset from data field switch (dataFormat) { @@ -2891,7 +2881,7 @@ public class ExifInterface { } if (offset > 0L && offset < dataInputStream.mLength) { dataInputStream.seek(offset); - readImageFileDirectory(dataInputStream, innerIfdHint); + readImageFileDirectory(dataInputStream, (int) nextIfdType); } else { Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset); } @@ -2903,7 +2893,7 @@ public class ExifInterface { byte[] bytes = new byte[byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes); - mAttributes[hint].put(tag.name, attribute); + mAttributes[ifdType].put(tag.name, attribute); // DNG files have a DNG Version tag specifying the version of specifications that the // image file is following. @@ -2937,11 +2927,11 @@ public class ExifInterface { // since the first IFD offset is at least 8. if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) { dataInputStream.seek(nextIfdOffset); - if (mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { + if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) { // Do not overwrite thumbnail IFD data if it alreay exists. - readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT); - } else if (mAttributes[IFD_PREVIEW_HINT].isEmpty()) { - readImageFileDirectory(dataInputStream, IFD_PREVIEW_HINT); + readImageFileDirectory(dataInputStream, IFD_TYPE_THUMBNAIL); + } else if (mAttributes[IFD_TYPE_PREVIEW].isEmpty()) { + readImageFileDirectory(dataInputStream, IFD_TYPE_PREVIEW); } } } @@ -2976,7 +2966,7 @@ public class ExifInterface { // Sets thumbnail offset & length attributes based on JpegInterchangeFormat or StripOffsets tags private void setThumbnailData(InputStream in) throws IOException { - HashMap thumbnailData = mAttributes[IFD_THUMBNAIL_HINT]; + HashMap thumbnailData = mAttributes[IFD_TYPE_THUMBNAIL]; ExifAttribute compressionAttribute = (ExifAttribute) thumbnailData.get(TAG_COMPRESSION); @@ -3146,21 +3136,21 @@ public class ExifInterface { // Validate primary, preview, thumbnail image data by comparing image size private void validateImages(InputStream in) throws IOException { // Swap images based on size (primary > preview > thumbnail) - swapBasedOnImageSize(IFD_TIFF_HINT, IFD_PREVIEW_HINT); - swapBasedOnImageSize(IFD_TIFF_HINT, IFD_THUMBNAIL_HINT); - swapBasedOnImageSize(IFD_PREVIEW_HINT, IFD_THUMBNAIL_HINT); + swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW); + swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL); + swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL); // Check whether thumbnail image exists and whether preview image satisfies the thumbnail // image requirements - if (mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { - if (isThumbnail(mAttributes[IFD_PREVIEW_HINT])) { - mAttributes[IFD_THUMBNAIL_HINT] = mAttributes[IFD_PREVIEW_HINT]; - mAttributes[IFD_PREVIEW_HINT] = new HashMap(); + if (mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) { + if (isThumbnail(mAttributes[IFD_TYPE_PREVIEW])) { + mAttributes[IFD_TYPE_THUMBNAIL] = mAttributes[IFD_TYPE_PREVIEW]; + mAttributes[IFD_TYPE_PREVIEW] = new HashMap(); } } // Check if the thumbnail image satisfies the thumbnail size requirements - if (!isThumbnail(mAttributes[IFD_THUMBNAIL_HINT])) { + if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) { Log.d(TAG, "No image meets the size requirements of a thumbnail image."); } } @@ -3244,9 +3234,9 @@ public class ExifInterface { if (newSubfileTypeValue == ORIGINAL_RESOLUTION_IMAGE) { // Update only for the primary image (OriginalResolutionImage) ExifAttribute pixelXDimAttribute = - (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_PIXEL_X_DIMENSION); + (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_X_DIMENSION); ExifAttribute pixelYDimAttribute = - (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_PIXEL_Y_DIMENSION); + (ExifAttribute) mAttributes[IFD_TYPE_EXIF].get(TAG_PIXEL_Y_DIMENSION); if (pixelXDimAttribute != null && pixelYDimAttribute != null) { mAttributes[imageType].put(TAG_IMAGE_WIDTH, pixelXDimAttribute); @@ -3259,16 +3249,6 @@ public class ExifInterface { } } - // Gets the corresponding IFD group index of the given tag number for writing Exif Tags. - private static int getIfdHintFromTagNumber(int tagNumber) { - for (int i = 0; i < EXIF_POINTER_TAG_HINTS.length; ++i) { - if (EXIF_POINTER_TAGS[i].number == tagNumber) { - return EXIF_POINTER_TAG_HINTS[i]; - } - } - return -1; - } - // Writes an Exif segment into the given output stream. private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream, int exifOffsetFromBeginning) throws IOException { @@ -3285,33 +3265,33 @@ public class ExifInterface { removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); // Remove null value tags. - for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { - for (Object obj : mAttributes[hint].entrySet().toArray()) { + for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { + for (Object obj : mAttributes[ifdType].entrySet().toArray()) { final Map.Entry entry = (Map.Entry) obj; if (entry.getValue() == null) { - mAttributes[hint].remove(entry.getKey()); + mAttributes[ifdType].remove(entry.getKey()); } } } // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD // offset when there is one or more tags in the thumbnail IFD. - if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { - mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name, + if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) { + mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name, ExifAttribute.createULong(0, mExifByteOrder)); } - if (!mAttributes[IFD_GPS_HINT].isEmpty()) { - mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name, + if (!mAttributes[IFD_TYPE_GPS].isEmpty()) { + mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name, ExifAttribute.createULong(0, mExifByteOrder)); } - if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { - mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name, + if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) { + mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(0, mExifByteOrder)); } if (mHasThumbnail) { - mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, + mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, ExifAttribute.createULong(0, mExifByteOrder)); - mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, + mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, ExifAttribute.createULong(mThumbnailLength, mExifByteOrder)); } @@ -3331,15 +3311,15 @@ public class ExifInterface { // Calculate IFD offsets. int position = 8; - for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { - if (!mAttributes[hint].isEmpty()) { - ifdOffsets[hint] = position; - position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint]; + for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { + if (!mAttributes[ifdType].isEmpty()) { + ifdOffsets[ifdType] = position; + position += 2 + mAttributes[ifdType].size() * 12 + 4 + ifdDataSizes[ifdType]; } } if (mHasThumbnail) { int thumbnailOffset = position; - mAttributes[IFD_THUMBNAIL_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, + mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; position += mThumbnailLength; @@ -3356,17 +3336,17 @@ public class ExifInterface { } // Update IFD pointer tags with the calculated offsets. - if (!mAttributes[IFD_EXIF_HINT].isEmpty()) { - mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name, - ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder)); + if (!mAttributes[IFD_TYPE_EXIF].isEmpty()) { + mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[1].name, + ExifAttribute.createULong(ifdOffsets[IFD_TYPE_EXIF], mExifByteOrder)); } - if (!mAttributes[IFD_GPS_HINT].isEmpty()) { - mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name, - ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder)); + if (!mAttributes[IFD_TYPE_GPS].isEmpty()) { + mAttributes[IFD_TYPE_PRIMARY].put(EXIF_POINTER_TAGS[2].name, + ExifAttribute.createULong(ifdOffsets[IFD_TYPE_GPS], mExifByteOrder)); } - if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) { - mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong( - ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder)); + if (!mAttributes[IFD_TYPE_INTEROPERABILITY].isEmpty()) { + mAttributes[IFD_TYPE_EXIF].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong( + ifdOffsets[IFD_TYPE_INTEROPERABILITY], mExifByteOrder)); } // Write TIFF Headers. See JEITA CP-3451C Section 4.5.2. Table 1. @@ -3379,17 +3359,18 @@ public class ExifInterface { dataOutputStream.writeUnsignedInt(IFD_OFFSET); // Write IFD groups. See JEITA CP-3451C Section 4.5.8. Figure 9. - for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { - if (!mAttributes[hint].isEmpty()) { + for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { + if (!mAttributes[ifdType].isEmpty()) { // See JEITA CP-3451C Section 4.6.2: IFD structure. // Write entry count - dataOutputStream.writeUnsignedShort(mAttributes[hint].size()); + dataOutputStream.writeUnsignedShort(mAttributes[ifdType].size()); // Write entry info - int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4; - for (Map.Entry entry : (Set) mAttributes[hint].entrySet()) { + int dataOffset = ifdOffsets[ifdType] + 2 + mAttributes[ifdType].size() * 12 + 4; + for (Map.Entry entry : (Set) mAttributes[ifdType].entrySet()) { // Convert tag name to tag number. - final ExifTag tag = (ExifTag) sExifTagMapsForWriting[hint].get(entry.getKey()); + final ExifTag tag = + (ExifTag) sExifTagMapsForWriting[ifdType].get(entry.getKey()); final int tagNumber = tag.number; final ExifAttribute attribute = (ExifAttribute) entry.getValue(); final int size = attribute.size(); @@ -3414,14 +3395,14 @@ public class ExifInterface { // Write the next offset. It writes the offset of thumbnail IFD if there is one or // more tags in the thumbnail IFD when the current IFD is the primary image TIFF // IFD; Otherwise 0. - if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) { - dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]); + if (ifdType == 0 && !mAttributes[IFD_TYPE_THUMBNAIL].isEmpty()) { + dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_TYPE_THUMBNAIL]); } else { dataOutputStream.writeUnsignedInt(0); } // Write values of data field exceeding 4 bytes after the next offset. - for (Map.Entry entry : (Set) mAttributes[hint].entrySet()) { + for (Map.Entry entry : (Set) mAttributes[ifdType].entrySet()) { ExifAttribute attribute = (ExifAttribute) entry.getValue(); if (attribute.bytes.length > 4) { @@ -3748,9 +3729,9 @@ public class ExifInterface { } // Swaps image data based on image size - private void swapBasedOnImageSize(int firstImageHint, int secondImageHint) + private void swapBasedOnImageSize(@IfdType int firstIfdType, @IfdType int secondIfdType) throws IOException { - if (mAttributes[firstImageHint].isEmpty() || mAttributes[secondImageHint].isEmpty()) { + if (mAttributes[firstIfdType].isEmpty() || mAttributes[secondIfdType].isEmpty()) { if (DEBUG) { Log.d(TAG, "Cannot perform swap since only one image data exists"); } @@ -3758,13 +3739,13 @@ public class ExifInterface { } ExifAttribute firstImageLengthAttribute = - (ExifAttribute) mAttributes[firstImageHint].get(TAG_IMAGE_LENGTH); + (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_LENGTH); ExifAttribute firstImageWidthAttribute = - (ExifAttribute) mAttributes[firstImageHint].get(TAG_IMAGE_WIDTH); + (ExifAttribute) mAttributes[firstIfdType].get(TAG_IMAGE_WIDTH); ExifAttribute secondImageLengthAttribute = - (ExifAttribute) mAttributes[secondImageHint].get(TAG_IMAGE_LENGTH); + (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_LENGTH); ExifAttribute secondImageWidthAttribute = - (ExifAttribute) mAttributes[secondImageHint].get(TAG_IMAGE_WIDTH); + (ExifAttribute) mAttributes[secondIfdType].get(TAG_IMAGE_WIDTH); if (firstImageLengthAttribute == null || firstImageWidthAttribute == null) { if (DEBUG) { @@ -3782,9 +3763,9 @@ public class ExifInterface { if (firstImageLengthValue < secondImageLengthValue && firstImageWidthValue < secondImageWidthValue) { - HashMap tempMap = mAttributes[firstImageHint]; - mAttributes[firstImageHint] = mAttributes[secondImageHint]; - mAttributes[secondImageHint] = tempMap; + HashMap tempMap = mAttributes[firstIfdType]; + mAttributes[firstIfdType] = mAttributes[secondIfdType]; + mAttributes[secondIfdType] = tempMap; } } }