Merge "[DO NOT MERGE] Return correct offsets for getAttributeRange()" into qt-dev

This commit is contained in:
Jin Seok Park
2019-12-04 02:16:32 +00:00
committed by Android (Google) Code Review

View File

@@ -1345,7 +1345,9 @@ public class ExifInterface {
private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN; private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mHasThumbnail; 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 mThumbnailOffset;
private int mThumbnailLength; private int mThumbnailLength;
private byte[] mThumbnailBytes; private byte[] mThumbnailBytes;
@@ -2043,10 +2045,12 @@ public class ExifInterface {
/** /**
* Returns the offset and length of thumbnail inside the image file, or * 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 * @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 * @throws IllegalStateException if {@link #saveAttributes()} has been
* called since the underlying file was initially parsed, since * called since the underlying file was initially parsed, since
* that means offsets may have changed. * that means offsets may have changed.
@@ -2058,10 +2062,12 @@ public class ExifInterface {
} }
if (mHasThumbnail) { if (mHasThumbnail) {
if (mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) {
return null;
}
return new long[] { mThumbnailOffset, mThumbnailLength }; return new long[] { mThumbnailOffset, mThumbnailLength };
} else {
return null;
} }
return null;
} }
/** /**
@@ -2536,10 +2542,9 @@ public class ExifInterface {
final byte[] value = Arrays.copyOfRange(bytes, final byte[] value = Arrays.copyOfRange(bytes,
IDENTIFIER_EXIF_APP1.length, bytes.length); IDENTIFIER_EXIF_APP1.length, bytes.length);
readExifSegment(value, imageType);
// Save offset values for createJpegThumbnailBitmap() function // Save offset values for createJpegThumbnailBitmap() function
mExifOffset = (int) offset; mExifOffset = (int) offset;
readExifSegment(value, imageType);
} else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) { } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) {
// See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6 // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
final long offset = start + IDENTIFIER_XMP_APP1.length; final long offset = start + IDENTIFIER_XMP_APP1.length;
@@ -2843,6 +2848,8 @@ public class ExifInterface {
if (in.read(bytes) != length) { if (in.read(bytes) != length) {
throw new IOException("Can't read exif"); throw new IOException("Can't read exif");
} }
// Save offset values for handling thumbnail and attribute offsets.
mExifOffset = offset;
readExifSegment(bytes, IFD_TYPE_PRIMARY); readExifSegment(bytes, IFD_TYPE_PRIMARY);
} }
@@ -2988,7 +2995,7 @@ public class ExifInterface {
// Write EXIF APP1 segment // Write EXIF APP1 segment
dataOutputStream.writeByte(MARKER); dataOutputStream.writeByte(MARKER);
dataOutputStream.writeByte(MARKER_APP1); dataOutputStream.writeByte(MARKER_APP1);
writeExifSegment(dataOutputStream, 6); writeExifSegment(dataOutputStream);
byte[] bytes = new byte[4096]; byte[] bytes = new byte[4096];
@@ -3319,7 +3326,7 @@ public class ExifInterface {
continue; continue;
} }
final int bytesOffset = dataInputStream.peek(); final int bytesOffset = dataInputStream.peek() + mExifOffset;
final byte[] bytes = new byte[(int) byteCount]; final byte[] bytes = new byte[(int) byteCount];
dataInputStream.readFully(bytes); dataInputStream.readFully(bytes);
ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
@@ -3451,31 +3458,28 @@ public class ExifInterface {
// The following code limits the size of thumbnail size not to overflow EXIF data area. // The following code limits the size of thumbnail size not to overflow EXIF data area.
thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset); thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset);
if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF if (mMimeType == IMAGE_TYPE_ORF) {
|| mMimeType == IMAGE_TYPE_RW2) {
thumbnailOffset += mExifOffset;
} else if (mMimeType == IMAGE_TYPE_ORF) {
// Update offset value since RAF files have IFD data preceding MakerNote data. // Update offset value since RAF files have IFD data preceding MakerNote data.
thumbnailOffset += mOrfMakerNoteOffset; thumbnailOffset += mOrfMakerNoteOffset;
} }
if (DEBUG) {
Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
+ ", length: " + thumbnailLength);
}
if (thumbnailOffset > 0 && thumbnailLength > 0) { if (thumbnailOffset > 0 && thumbnailLength > 0) {
mHasThumbnail = true; mHasThumbnail = true;
mThumbnailOffset = thumbnailOffset; mThumbnailOffset = thumbnailOffset + mExifOffset;
mThumbnailLength = thumbnailLength; mThumbnailLength = thumbnailLength;
mThumbnailCompression = DATA_JPEG; mThumbnailCompression = DATA_JPEG;
if (mFilename == null && mAssetInputStream == null if (mFilename == null && mAssetInputStream == null
&& mSeekableFileDescriptor == null) { && mSeekableFileDescriptor == null) {
// Save the thumbnail in memory if the input doesn't support reading again. // Save the thumbnail in memory if the input doesn't support reading again.
byte[] thumbnailBytes = new byte[thumbnailLength]; byte[] thumbnailBytes = new byte[mThumbnailLength];
in.seek(thumbnailOffset); in.seek(mThumbnailOffset);
in.readFully(thumbnailBytes); in.readFully(thumbnailBytes);
mThumbnailBytes = thumbnailBytes; mThumbnailBytes = thumbnailBytes;
} }
if (DEBUG) {
Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
+ ", length: " + thumbnailLength);
}
} }
} }
} }
@@ -3494,12 +3498,16 @@ public class ExifInterface {
long[] stripByteCounts = long[] stripByteCounts =
convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder)); convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
if (stripOffsets == null) { if (stripOffsets == null || stripOffsets.length == 0) {
Log.w(TAG, "stripOffsets should not be null."); Log.w(TAG, "stripOffsets should not be null or have zero length.");
return; return;
} }
if (stripByteCounts == null) { if (stripByteCounts == null || stripByteCounts.length == 0) {
Log.w(TAG, "stripByteCounts should not be null."); Log.w(TAG, "stripByteCounts should not be null or have zero length.");
return;
}
if (stripOffsets.length != stripByteCounts.length) {
Log.w(TAG, "stripOffsets and stripByteCounts should have same length.");
return; return;
} }
@@ -3509,10 +3517,18 @@ public class ExifInterface {
int bytesRead = 0; int bytesRead = 0;
int bytesAdded = 0; int bytesAdded = 0;
mHasThumbnail = mHasThumbnailStrips = mAreThumbnailStripsConsecutive = true;
for (int i = 0; i < stripOffsets.length; i++) { for (int i = 0; i < stripOffsets.length; i++) {
int stripOffset = (int) stripOffsets[i]; int stripOffset = (int) stripOffsets[i];
int stripByteCount = (int) stripByteCounts[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 // Skip to offset
int skipBytes = stripOffset - bytesRead; int skipBytes = stripOffset - bytesRead;
if (skipBytes < 0) { if (skipBytes < 0) {
@@ -3531,10 +3547,13 @@ public class ExifInterface {
stripBytes.length); stripBytes.length);
bytesAdded += stripBytes.length; bytesAdded += stripBytes.length;
} }
mHasThumbnail = true;
mThumbnailBytes = totalStripBytes; mThumbnailBytes = totalStripBytes;
mThumbnailLength = totalStripBytes.length;
if (mAreThumbnailStripsConsecutive) {
// Need to add mExifOffset, which is the offset to the EXIF data segment
mThumbnailOffset = (int) stripOffsets[0] + mExifOffset;
mThumbnailLength = totalStripBytes.length;
}
} }
} }
@@ -3691,8 +3710,7 @@ public class ExifInterface {
} }
// Writes an Exif segment into the given output stream. // Writes an Exif segment into the given output stream.
private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throws IOException {
int exifOffsetFromBeginning) throws IOException {
// The following variables are for calculating each IFD tag group size in bytes. // The following variables are for calculating each IFD tag group size in bytes.
int[] ifdOffsets = new int[EXIF_TAGS.length]; int[] ifdOffsets = new int[EXIF_TAGS.length];
int[] ifdDataSizes = new int[EXIF_TAGS.length]; int[] ifdDataSizes = new int[EXIF_TAGS.length];
@@ -3751,6 +3769,8 @@ public class ExifInterface {
} }
// Calculate IFD offsets. // Calculate IFD offsets.
// 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes
// (offset of IFDs)
int position = 8; int position = 8;
for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
if (!mAttributes[ifdType].isEmpty()) { if (!mAttributes[ifdType].isEmpty()) {
@@ -3762,7 +3782,8 @@ public class ExifInterface {
int thumbnailOffset = position; int thumbnailOffset = position;
mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); 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; position += mThumbnailLength;
} }