Loading media/java/android/media/ExifInterface.java +73 −64 Original line number Diff line number Diff line Loading @@ -1443,7 +1443,9 @@ public class ExifInterface { private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mHasThumbnail; // The following values used for indicating a thumbnail position. private boolean mHasThumbnailStrips; private boolean mAreThumbnailStripsConsecutive; // Used to indicate the position of the thumbnail (includes offset to EXIF data segment). private int mThumbnailOffset; private int mThumbnailLength; private byte[] mThumbnailBytes; Loading Loading @@ -1574,7 +1576,8 @@ public class ExifInterface { mFilename = null; if (shouldBeExifDataOnly) { if (!isExifDataOnly(inputStream)) { inputStream = new BufferedInputStream(inputStream, SIGNATURE_CHECK_SIZE); if (!isExifDataOnly((BufferedInputStream) inputStream)) { Log.w(TAG, "Given data does not follow the structure of an Exif-only data."); return; } Loading Loading @@ -2262,10 +2265,12 @@ public class ExifInterface { /** * Returns the offset and length of thumbnail inside the image file, or * {@code null} if there is no thumbnail. * {@code null} if either there is no thumbnail or the thumbnail bytes are stored * non-consecutively. * * @return two-element array, the offset in the first value, and length in * the second, or {@code null} if no thumbnail was found. * the second, or {@code null} if no thumbnail was found or the thumbnail strips are * not placed consecutively. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. Loading @@ -2277,13 +2282,12 @@ public class ExifInterface { } if (mHasThumbnail) { if (mIsExifDataOnly) { return new long[] { mThumbnailOffset + mExifOffset, mThumbnailLength }; if (mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) { return null; } return new long[] { mThumbnailOffset, mThumbnailLength }; } else { return null; } return null; } /** Loading Loading @@ -2741,20 +2745,17 @@ public class ExifInterface { return true; } private static boolean isExifDataOnly(InputStream inputStream) { try { private static boolean isExifDataOnly(BufferedInputStream in) throws IOException { in.mark(IDENTIFIER_EXIF_APP1.length); byte[] signatureCheckBytes = new byte[IDENTIFIER_EXIF_APP1.length]; inputStream.read(signatureCheckBytes); if (Arrays.equals(signatureCheckBytes, IDENTIFIER_EXIF_APP1)) { return true; } } catch (IOException e) { if (DEBUG) { Log.w(TAG, "Encountered error while checking whether input stream is Exif data only"); in.read(signatureCheckBytes); in.reset(); for (int i = 0; i < IDENTIFIER_EXIF_APP1.length; i++) { if (signatureCheckBytes[i] != IDENTIFIER_EXIF_APP1[i]) { return false; } } return false; return true; } /** Loading Loading @@ -2828,10 +2829,9 @@ public class ExifInterface { final long offset = start + IDENTIFIER_EXIF_APP1.length; final byte[] value = Arrays.copyOfRange(bytes, IDENTIFIER_EXIF_APP1.length, bytes.length); readExifSegment(value, imageType); // Save offset values for handleThumbnailFromJfif() function mExifOffset = (int) offset; readExifSegment(value, imageType); } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) { // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6 final long offset = start + IDENTIFIER_XMP_APP1.length; Loading Loading @@ -3136,6 +3136,8 @@ public class ExifInterface { if (in.read(bytes) != length) { throw new IOException("Can't read exif"); } // Save offset values for handling thumbnail and attribute offsets. mExifOffset = offset; readExifSegment(bytes, IFD_TYPE_PRIMARY); } Loading @@ -3148,13 +3150,13 @@ public class ExifInterface { } private void getStandaloneAttributes(ByteOrderedDataInputStream in) throws IOException { in.skipBytes(IDENTIFIER_EXIF_APP1.length); // TODO: Need to handle potential OutOfMemoryError byte[] data = new byte[in.available()]; in.readFully(data); readExifSegment(data, IFD_TYPE_PRIMARY); // Save offset values for handleThumbnailFromJfif() function // Save offset values for handling thumbnail and attribute offsets. mExifOffset = IDENTIFIER_EXIF_APP1.length; readExifSegment(data, IFD_TYPE_PRIMARY); } /** Loading Loading @@ -3335,13 +3337,11 @@ public class ExifInterface { + "\n recorded CRC value: " + dataCrcValue + ", calculated CRC " + "value: " + crc.getValue()); } // Save offset values for handleThumbnailFromJfif() function mExifOffset = bytesRead; readExifSegment(data, IFD_TYPE_PRIMARY); validateImages(); // Save offset values for handleThumbnailFromJfif() function mExifOffset = bytesRead; break; } else { // Skip to next chunk Loading Loading @@ -3396,6 +3396,8 @@ public class ExifInterface { throw new IOException("Failed to read given length for given PNG chunk " + "type: " + byteArrayToHexString(code)); } // Save offset values for handling thumbnail and attribute offsets. mExifOffset = bytesRead; readExifSegment(payload, IFD_TYPE_PRIMARY); break; } else { Loading @@ -3416,8 +3418,6 @@ public class ExifInterface { bytesRead += skipped; } } // Save offset values for handleThumbnailFromJfif() function mExifOffset = bytesRead; } catch (EOFException e) { // Should not reach here. Will only reach here if the file is corrupted or // does not follow the WebP specifications Loading Loading @@ -3448,7 +3448,7 @@ public class ExifInterface { // Write EXIF APP1 segment dataOutputStream.writeByte(MARKER); dataOutputStream.writeByte(MARKER_APP1); writeExifSegment(dataOutputStream, 6); writeExifSegment(dataOutputStream); byte[] bytes = new byte[4096]; Loading Loading @@ -3565,7 +3565,7 @@ public class ExifInterface { new ByteOrderedDataOutputStream(exifByteArrayOutputStream, ByteOrder.BIG_ENDIAN); // Store Exif data in separate byte array writeExifSegment(exifDataOutputStream, 0); writeExifSegment(exifDataOutputStream); byte[] exifBytes = ((ByteArrayOutputStream) exifDataOutputStream.mOutputStream).toByteArray(); // Write EXIF chunk data Loading Loading @@ -3835,7 +3835,7 @@ public class ExifInterface { continue; } final int bytesOffset = dataInputStream.peek(); final int bytesOffset = dataInputStream.peek() + mExifOffset; final byte[] bytes = new byte[(int) byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, Loading Loading @@ -3965,29 +3965,17 @@ public class ExifInterface { int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder); int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder); switch (mMimeType) { case IMAGE_TYPE_JPEG: case IMAGE_TYPE_RAF: case IMAGE_TYPE_RW2: case IMAGE_TYPE_PNG: case IMAGE_TYPE_WEBP: thumbnailOffset += mExifOffset; break; case IMAGE_TYPE_ORF: if (mMimeType == IMAGE_TYPE_ORF) { // Update offset value since RAF files have IFD data preceding MakerNote data. thumbnailOffset += mOrfMakerNoteOffset; break; } // The following code limits the size of thumbnail size not to overflow EXIF data area. thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset); if (DEBUG) { Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset + ", length: " + thumbnailLength); } if (thumbnailOffset > 0 && thumbnailLength > 0) { mHasThumbnail = true; mThumbnailOffset = thumbnailOffset; // Need to add mExifOffset, which is the offset to the EXIF data segment mThumbnailOffset = thumbnailOffset + mExifOffset; mThumbnailLength = thumbnailLength; mThumbnailCompression = DATA_JPEG; Loading @@ -3995,12 +3983,16 @@ public class ExifInterface { && mSeekableFileDescriptor == null) { // TODO: Need to handle potential OutOfMemoryError // Save the thumbnail in memory if the input doesn't support reading again. byte[] thumbnailBytes = new byte[thumbnailLength]; in.seek(thumbnailOffset); byte[] thumbnailBytes = new byte[mThumbnailLength]; in.seek(mThumbnailOffset); in.readFully(thumbnailBytes); mThumbnailBytes = thumbnailBytes; } } if (DEBUG) { Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset + ", length: " + thumbnailLength); } } } Loading @@ -4018,12 +4010,16 @@ public class ExifInterface { long[] stripByteCounts = convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder)); if (stripOffsets == null) { Log.w(TAG, "stripOffsets should not be null."); if (stripOffsets == null || stripOffsets.length == 0) { Log.w(TAG, "stripOffsets should not be null or have zero length."); return; } if (stripByteCounts == null || stripByteCounts.length == 0) { Log.w(TAG, "stripByteCounts should not be null or have zero length."); return; } if (stripByteCounts == null) { Log.w(TAG, "stripByteCounts should not be null."); if (stripOffsets.length != stripByteCounts.length) { Log.w(TAG, "stripOffsets and stripByteCounts should have same length."); return; } Loading @@ -4034,10 +4030,18 @@ public class ExifInterface { int bytesRead = 0; int bytesAdded = 0; mHasThumbnail = mHasThumbnailStrips = mAreThumbnailStripsConsecutive = true; for (int i = 0; i < stripOffsets.length; i++) { int stripOffset = (int) stripOffsets[i]; int stripByteCount = (int) stripByteCounts[i]; // Check if strips are consecutive // TODO: Add test for non-consecutive thumbnail image if (i < stripOffsets.length - 1 && stripOffset + stripByteCount != stripOffsets[i + 1]) { mAreThumbnailStripsConsecutive = false; } // Skip to offset int skipBytes = stripOffset - bytesRead; if (skipBytes < 0) { Loading @@ -4057,12 +4061,15 @@ public class ExifInterface { stripBytes.length); bytesAdded += stripBytes.length; } mHasThumbnail = true; mThumbnailBytes = totalStripBytes; if (mAreThumbnailStripsConsecutive) { // Need to add mExifOffset, which is the offset to the EXIF data segment mThumbnailOffset = (int) stripOffsets[0] + mExifOffset; mThumbnailLength = totalStripBytes.length; } } } // Check if thumbnail data type is currently supported or not private boolean isSupportedDataType(HashMap thumbnailData) throws IOException { Loading Loading @@ -4218,8 +4225,7 @@ public class ExifInterface { } // Writes an Exif segment into the given output stream. private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, int exifOffsetFromBeginning) throws IOException { private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throws IOException { // The following variables are for calculating each IFD tag group size in bytes. int[] ifdOffsets = new int[EXIF_TAGS.length]; int[] ifdDataSizes = new int[EXIF_TAGS.length]; Loading Loading @@ -4278,7 +4284,9 @@ public class ExifInterface { } // Calculate IFD offsets. int position = 8; // 8 bytes are for TIFF headers // 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes // (offset of IFDs) int position = 8; for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { if (!mAttributes[ifdType].isEmpty()) { ifdOffsets[ifdType] = position; Loading @@ -4289,7 +4297,8 @@ public class ExifInterface { int thumbnailOffset = position; mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; // Need to add mExifOffset, which is the offset to the EXIF data segment mThumbnailOffset = thumbnailOffset + mExifOffset; position += mThumbnailLength; } Loading Loading
media/java/android/media/ExifInterface.java +73 −64 Original line number Diff line number Diff line Loading @@ -1443,7 +1443,9 @@ public class ExifInterface { private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mHasThumbnail; // The following values used for indicating a thumbnail position. private boolean mHasThumbnailStrips; private boolean mAreThumbnailStripsConsecutive; // Used to indicate the position of the thumbnail (includes offset to EXIF data segment). private int mThumbnailOffset; private int mThumbnailLength; private byte[] mThumbnailBytes; Loading Loading @@ -1574,7 +1576,8 @@ public class ExifInterface { mFilename = null; if (shouldBeExifDataOnly) { if (!isExifDataOnly(inputStream)) { inputStream = new BufferedInputStream(inputStream, SIGNATURE_CHECK_SIZE); if (!isExifDataOnly((BufferedInputStream) inputStream)) { Log.w(TAG, "Given data does not follow the structure of an Exif-only data."); return; } Loading Loading @@ -2262,10 +2265,12 @@ public class ExifInterface { /** * Returns the offset and length of thumbnail inside the image file, or * {@code null} if there is no thumbnail. * {@code null} if either there is no thumbnail or the thumbnail bytes are stored * non-consecutively. * * @return two-element array, the offset in the first value, and length in * the second, or {@code null} if no thumbnail was found. * the second, or {@code null} if no thumbnail was found or the thumbnail strips are * not placed consecutively. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. Loading @@ -2277,13 +2282,12 @@ public class ExifInterface { } if (mHasThumbnail) { if (mIsExifDataOnly) { return new long[] { mThumbnailOffset + mExifOffset, mThumbnailLength }; if (mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) { return null; } return new long[] { mThumbnailOffset, mThumbnailLength }; } else { return null; } return null; } /** Loading Loading @@ -2741,20 +2745,17 @@ public class ExifInterface { return true; } private static boolean isExifDataOnly(InputStream inputStream) { try { private static boolean isExifDataOnly(BufferedInputStream in) throws IOException { in.mark(IDENTIFIER_EXIF_APP1.length); byte[] signatureCheckBytes = new byte[IDENTIFIER_EXIF_APP1.length]; inputStream.read(signatureCheckBytes); if (Arrays.equals(signatureCheckBytes, IDENTIFIER_EXIF_APP1)) { return true; } } catch (IOException e) { if (DEBUG) { Log.w(TAG, "Encountered error while checking whether input stream is Exif data only"); in.read(signatureCheckBytes); in.reset(); for (int i = 0; i < IDENTIFIER_EXIF_APP1.length; i++) { if (signatureCheckBytes[i] != IDENTIFIER_EXIF_APP1[i]) { return false; } } return false; return true; } /** Loading Loading @@ -2828,10 +2829,9 @@ public class ExifInterface { final long offset = start + IDENTIFIER_EXIF_APP1.length; final byte[] value = Arrays.copyOfRange(bytes, IDENTIFIER_EXIF_APP1.length, bytes.length); readExifSegment(value, imageType); // Save offset values for handleThumbnailFromJfif() function mExifOffset = (int) offset; readExifSegment(value, imageType); } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) { // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6 final long offset = start + IDENTIFIER_XMP_APP1.length; Loading Loading @@ -3136,6 +3136,8 @@ public class ExifInterface { if (in.read(bytes) != length) { throw new IOException("Can't read exif"); } // Save offset values for handling thumbnail and attribute offsets. mExifOffset = offset; readExifSegment(bytes, IFD_TYPE_PRIMARY); } Loading @@ -3148,13 +3150,13 @@ public class ExifInterface { } private void getStandaloneAttributes(ByteOrderedDataInputStream in) throws IOException { in.skipBytes(IDENTIFIER_EXIF_APP1.length); // TODO: Need to handle potential OutOfMemoryError byte[] data = new byte[in.available()]; in.readFully(data); readExifSegment(data, IFD_TYPE_PRIMARY); // Save offset values for handleThumbnailFromJfif() function // Save offset values for handling thumbnail and attribute offsets. mExifOffset = IDENTIFIER_EXIF_APP1.length; readExifSegment(data, IFD_TYPE_PRIMARY); } /** Loading Loading @@ -3335,13 +3337,11 @@ public class ExifInterface { + "\n recorded CRC value: " + dataCrcValue + ", calculated CRC " + "value: " + crc.getValue()); } // Save offset values for handleThumbnailFromJfif() function mExifOffset = bytesRead; readExifSegment(data, IFD_TYPE_PRIMARY); validateImages(); // Save offset values for handleThumbnailFromJfif() function mExifOffset = bytesRead; break; } else { // Skip to next chunk Loading Loading @@ -3396,6 +3396,8 @@ public class ExifInterface { throw new IOException("Failed to read given length for given PNG chunk " + "type: " + byteArrayToHexString(code)); } // Save offset values for handling thumbnail and attribute offsets. mExifOffset = bytesRead; readExifSegment(payload, IFD_TYPE_PRIMARY); break; } else { Loading @@ -3416,8 +3418,6 @@ public class ExifInterface { bytesRead += skipped; } } // Save offset values for handleThumbnailFromJfif() function mExifOffset = bytesRead; } catch (EOFException e) { // Should not reach here. Will only reach here if the file is corrupted or // does not follow the WebP specifications Loading Loading @@ -3448,7 +3448,7 @@ public class ExifInterface { // Write EXIF APP1 segment dataOutputStream.writeByte(MARKER); dataOutputStream.writeByte(MARKER_APP1); writeExifSegment(dataOutputStream, 6); writeExifSegment(dataOutputStream); byte[] bytes = new byte[4096]; Loading Loading @@ -3565,7 +3565,7 @@ public class ExifInterface { new ByteOrderedDataOutputStream(exifByteArrayOutputStream, ByteOrder.BIG_ENDIAN); // Store Exif data in separate byte array writeExifSegment(exifDataOutputStream, 0); writeExifSegment(exifDataOutputStream); byte[] exifBytes = ((ByteArrayOutputStream) exifDataOutputStream.mOutputStream).toByteArray(); // Write EXIF chunk data Loading Loading @@ -3835,7 +3835,7 @@ public class ExifInterface { continue; } final int bytesOffset = dataInputStream.peek(); final int bytesOffset = dataInputStream.peek() + mExifOffset; final byte[] bytes = new byte[(int) byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, Loading Loading @@ -3965,29 +3965,17 @@ public class ExifInterface { int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder); int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder); switch (mMimeType) { case IMAGE_TYPE_JPEG: case IMAGE_TYPE_RAF: case IMAGE_TYPE_RW2: case IMAGE_TYPE_PNG: case IMAGE_TYPE_WEBP: thumbnailOffset += mExifOffset; break; case IMAGE_TYPE_ORF: if (mMimeType == IMAGE_TYPE_ORF) { // Update offset value since RAF files have IFD data preceding MakerNote data. thumbnailOffset += mOrfMakerNoteOffset; break; } // The following code limits the size of thumbnail size not to overflow EXIF data area. thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset); if (DEBUG) { Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset + ", length: " + thumbnailLength); } if (thumbnailOffset > 0 && thumbnailLength > 0) { mHasThumbnail = true; mThumbnailOffset = thumbnailOffset; // Need to add mExifOffset, which is the offset to the EXIF data segment mThumbnailOffset = thumbnailOffset + mExifOffset; mThumbnailLength = thumbnailLength; mThumbnailCompression = DATA_JPEG; Loading @@ -3995,12 +3983,16 @@ public class ExifInterface { && mSeekableFileDescriptor == null) { // TODO: Need to handle potential OutOfMemoryError // Save the thumbnail in memory if the input doesn't support reading again. byte[] thumbnailBytes = new byte[thumbnailLength]; in.seek(thumbnailOffset); byte[] thumbnailBytes = new byte[mThumbnailLength]; in.seek(mThumbnailOffset); in.readFully(thumbnailBytes); mThumbnailBytes = thumbnailBytes; } } if (DEBUG) { Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset + ", length: " + thumbnailLength); } } } Loading @@ -4018,12 +4010,16 @@ public class ExifInterface { long[] stripByteCounts = convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder)); if (stripOffsets == null) { Log.w(TAG, "stripOffsets should not be null."); if (stripOffsets == null || stripOffsets.length == 0) { Log.w(TAG, "stripOffsets should not be null or have zero length."); return; } if (stripByteCounts == null || stripByteCounts.length == 0) { Log.w(TAG, "stripByteCounts should not be null or have zero length."); return; } if (stripByteCounts == null) { Log.w(TAG, "stripByteCounts should not be null."); if (stripOffsets.length != stripByteCounts.length) { Log.w(TAG, "stripOffsets and stripByteCounts should have same length."); return; } Loading @@ -4034,10 +4030,18 @@ public class ExifInterface { int bytesRead = 0; int bytesAdded = 0; mHasThumbnail = mHasThumbnailStrips = mAreThumbnailStripsConsecutive = true; for (int i = 0; i < stripOffsets.length; i++) { int stripOffset = (int) stripOffsets[i]; int stripByteCount = (int) stripByteCounts[i]; // Check if strips are consecutive // TODO: Add test for non-consecutive thumbnail image if (i < stripOffsets.length - 1 && stripOffset + stripByteCount != stripOffsets[i + 1]) { mAreThumbnailStripsConsecutive = false; } // Skip to offset int skipBytes = stripOffset - bytesRead; if (skipBytes < 0) { Loading @@ -4057,12 +4061,15 @@ public class ExifInterface { stripBytes.length); bytesAdded += stripBytes.length; } mHasThumbnail = true; mThumbnailBytes = totalStripBytes; if (mAreThumbnailStripsConsecutive) { // Need to add mExifOffset, which is the offset to the EXIF data segment mThumbnailOffset = (int) stripOffsets[0] + mExifOffset; mThumbnailLength = totalStripBytes.length; } } } // Check if thumbnail data type is currently supported or not private boolean isSupportedDataType(HashMap thumbnailData) throws IOException { Loading Loading @@ -4218,8 +4225,7 @@ public class ExifInterface { } // Writes an Exif segment into the given output stream. private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, int exifOffsetFromBeginning) throws IOException { private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throws IOException { // The following variables are for calculating each IFD tag group size in bytes. int[] ifdOffsets = new int[EXIF_TAGS.length]; int[] ifdDataSizes = new int[EXIF_TAGS.length]; Loading Loading @@ -4278,7 +4284,9 @@ public class ExifInterface { } // Calculate IFD offsets. int position = 8; // 8 bytes are for TIFF headers // 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes // (offset of IFDs) int position = 8; for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { if (!mAttributes[ifdType].isEmpty()) { ifdOffsets[ifdType] = position; Loading @@ -4289,7 +4297,8 @@ public class ExifInterface { int thumbnailOffset = position; mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; // Need to add mExifOffset, which is the offset to the EXIF data segment mThumbnailOffset = thumbnailOffset + mExifOffset; position += mThumbnailLength; } Loading