From d0e63a841a0d068537bb1de0f36970eba3739233 Mon Sep 17 00:00:00 2001 From: Jin Park Date: Tue, 9 Aug 2016 20:44:52 +0900 Subject: [PATCH] ExifInterface: Refactor IFD type variables ExifInterface uses an ambiguous term called "hint" to indicate the type of IFD that needs to be parsed. This CL substitutes the use of such term with IfdType class. Bug: 30749097 Change-Id: Id97d09882f37818978b773d55846707915efeb49 --- media/java/android/media/ExifInterface.java | 321 +++++++++----------- 1 file changed, 151 insertions(+), 170 deletions(-) diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 7ff5ca330e3a4..3ff17ff62b311 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; @@ -1148,16 +1151,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, @@ -1173,11 +1184,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); @@ -1191,6 +1198,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 @@ -1242,14 +1251,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; @@ -1485,7 +1502,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); @@ -1644,7 +1661,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: { @@ -1876,9 +1893,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); @@ -2147,9 +2164,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) @@ -2250,7 +2267,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; @@ -2310,12 +2327,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); @@ -2324,7 +2341,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 = @@ -2335,13 +2352,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); } } } @@ -2374,7 +2391,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 @@ -2411,8 +2428,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); } @@ -2441,7 +2458,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 = @@ -2463,18 +2480,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); } @@ -2482,7 +2499,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); @@ -2501,8 +2518,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); } } } @@ -2517,47 +2534,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); } } @@ -2675,25 +2664,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)); } } @@ -2745,8 +2734,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; @@ -2771,12 +2760,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 || @@ -2803,7 +2792,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; @@ -2815,10 +2805,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) { @@ -2837,12 +2827,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) { @@ -2873,7 +2863,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); } @@ -2885,7 +2875,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); // PEF files have a Make or Model tag that begins with "PENTAX" or a compression tag // that is 65535. @@ -2912,11 +2902,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); } } } @@ -2951,7 +2941,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); @@ -3098,21 +3088,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."); } } @@ -3196,9 +3186,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); @@ -3211,16 +3201,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 { @@ -3237,33 +3217,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)); } @@ -3283,15 +3263,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; @@ -3308,17 +3288,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. @@ -3331,17 +3311,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(); @@ -3366,14 +3347,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) { @@ -3700,9 +3681,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"); } @@ -3710,13 +3691,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) { @@ -3734,9 +3715,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; } } }