summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChih-Chung Chang <chihchung@google.com>2011-10-24 19:46:07 +0800
committerOwen Lin <owenlin@google.com>2011-11-05 17:30:43 +0800
commit89e853d8b714fcef0a864265bb4ddfdd50f07e43 (patch)
tree861652c3fd077b071ca905c1265dc456f9f76f7d
parenta93cf07b2cc645122b5699374e5a0bc85080059e (diff)
downloadandroid_packages_apps_Snap-89e853d8b714fcef0a864265bb4ddfdd50f07e43.tar.gz
android_packages_apps_Snap-89e853d8b714fcef0a864265bb4ddfdd50f07e43.tar.bz2
android_packages_apps_Snap-89e853d8b714fcef0a864265bb4ddfdd50f07e43.zip
Fix 5457600: In UriImage, read exif to determine orientation.
Change-Id: Iacc5c52747a4ed4dd8f6cab48ce784aff907525b
-rw-r--r--src/com/android/gallery3d/data/Exif.java161
-rw-r--r--src/com/android/gallery3d/data/UriImage.java19
-rw-r--r--src/com/android/gallery3d/ui/AbstractDisplayItem.java5
3 files changed, 182 insertions, 3 deletions
diff --git a/src/com/android/gallery3d/data/Exif.java b/src/com/android/gallery3d/data/Exif.java
new file mode 100644
index 000000000..6f314df13
--- /dev/null
+++ b/src/com/android/gallery3d/data/Exif.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.data;
+
+import android.util.Log;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+public class Exif {
+ private static final String TAG = "CameraExif";
+
+ public static int getOrientation(InputStream is) {
+ if (is == null) {
+ return 0;
+ }
+
+ byte[] buf = new byte[8];
+ int length = 0;
+
+ // ISO/IEC 10918-1:1993(E)
+ while (read(is, buf, 2) && (buf[0] & 0xFF) == 0xFF) {
+ int marker = buf[1] & 0xFF;
+
+ // Check if the marker is a padding.
+ if (marker == 0xFF) {
+ continue;
+ }
+
+ // Check if the marker is SOI or TEM.
+ if (marker == 0xD8 || marker == 0x01) {
+ continue;
+ }
+ // Check if the marker is EOI or SOS.
+ if (marker == 0xD9 || marker == 0xDA) {
+ return 0;
+ }
+
+ // Get the length and check if it is reasonable.
+ if (!read(is, buf, 2)) {
+ return 0;
+ }
+ length = pack(buf, 0, 2, false);
+ if (length < 2) {
+ Log.e(TAG, "Invalid length");
+ return 0;
+ }
+ length -= 2;
+
+ // Break if the marker is EXIF in APP1.
+ if (marker == 0xE1 && length >= 6) {
+ if (!read(is, buf, 6)) return 0;
+ length -= 6;
+ if (pack(buf, 0, 4, false) == 0x45786966 &&
+ pack(buf, 4, 2, false) == 0) {
+ break;
+ }
+ }
+
+ // Skip other markers.
+ try {
+ is.skip(length);
+ } catch (IOException ex) {
+ return 0;
+ }
+ length = 0;
+ }
+
+ // JEITA CP-3451 Exif Version 2.2
+ if (length > 8) {
+ int offset = 0;
+ byte[] jpeg = new byte[length];
+ if (!read(is, jpeg, length)) {
+ return 0;
+ }
+
+ // Identify the byte order.
+ int tag = pack(jpeg, offset, 4, false);
+ if (tag != 0x49492A00 && tag != 0x4D4D002A) {
+ Log.e(TAG, "Invalid byte order");
+ return 0;
+ }
+ boolean littleEndian = (tag == 0x49492A00);
+
+ // Get the offset and check if it is reasonable.
+ int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
+ if (count < 10 || count > length) {
+ Log.e(TAG, "Invalid offset");
+ return 0;
+ }
+ offset += count;
+ length -= count;
+
+ // Get the count and go through all the elements.
+ count = pack(jpeg, offset - 2, 2, littleEndian);
+ while (count-- > 0 && length >= 12) {
+ // Get the tag and check if it is orientation.
+ tag = pack(jpeg, offset, 2, littleEndian);
+ if (tag == 0x0112) {
+ // We do not really care about type and count, do we?
+ int orientation = pack(jpeg, offset + 8, 2, littleEndian);
+ switch (orientation) {
+ case 1:
+ return 0;
+ case 3:
+ return 180;
+ case 6:
+ return 90;
+ case 8:
+ return 270;
+ }
+ Log.i(TAG, "Unsupported orientation");
+ return 0;
+ }
+ offset += 12;
+ length -= 12;
+ }
+ }
+
+ Log.i(TAG, "Orientation not found");
+ return 0;
+ }
+
+ private static int pack(byte[] bytes, int offset, int length,
+ boolean littleEndian) {
+ int step = 1;
+ if (littleEndian) {
+ offset += length - 1;
+ step = -1;
+ }
+
+ int value = 0;
+ while (length-- > 0) {
+ value = (value << 8) | (bytes[offset] & 0xFF);
+ offset += step;
+ }
+ return value;
+ }
+
+ private static boolean read(InputStream is, byte[] buf, int length) {
+ try {
+ return is.read(buf, 0, length) == length;
+ } catch (IOException ex) {
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java
index e97b03544..b8691dfdf 100644
--- a/src/com/android/gallery3d/data/UriImage.java
+++ b/src/com/android/gallery3d/data/UriImage.java
@@ -32,7 +32,9 @@ import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.webkit.MimeTypeMap;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.InputStream;
import java.net.URI;
import java.net.URL;
@@ -52,6 +54,7 @@ public class UriImage extends MediaItem {
private int mState = STATE_INIT;
private int mWidth;
private int mHeight;
+ private int mRotation;
private GalleryApp mApplication;
@@ -103,6 +106,12 @@ public class UriImage extends MediaItem {
|| ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
|| ContentResolver.SCHEME_FILE.equals(scheme)) {
try {
+ if (mContentType.equalsIgnoreCase("image/jpeg")) {
+ InputStream is = mApplication.getContentResolver()
+ .openInputStream(mUri);
+ mRotation = Exif.getOrientation(is);
+ Utils.closeSilently(is);
+ }
mFileDescriptor = mApplication.getContentResolver()
.openFileDescriptor(mUri, "r");
if (jc.isCancelled()) return STATE_INIT;
@@ -120,6 +129,11 @@ public class UriImage extends MediaItem {
Log.w(TAG, "download failed " + url);
return STATE_ERROR;
}
+ if (mContentType.equalsIgnoreCase("image/jpeg")) {
+ InputStream is = new FileInputStream(mCacheEntry.cacheFile);
+ mRotation = Exif.getOrientation(is);
+ Utils.closeSilently(is);
+ }
mFileDescriptor = ParcelFileDescriptor.open(
mCacheEntry.cacheFile, ParcelFileDescriptor.MODE_READ_ONLY);
return STATE_DOWNLOADED;
@@ -273,4 +287,9 @@ public class UriImage extends MediaItem {
public int getHeight() {
return 0;
}
+
+ @Override
+ public int getRotation() {
+ return mRotation;
+ }
}
diff --git a/src/com/android/gallery3d/ui/AbstractDisplayItem.java b/src/com/android/gallery3d/ui/AbstractDisplayItem.java
index aad3919b5..28acc3b75 100644
--- a/src/com/android/gallery3d/ui/AbstractDisplayItem.java
+++ b/src/com/android/gallery3d/ui/AbstractDisplayItem.java
@@ -36,12 +36,10 @@ public abstract class AbstractDisplayItem extends DisplayItem {
private Bitmap mBitmap;
protected final MediaItem mMediaItem;
- private int mRotation;
public AbstractDisplayItem(MediaItem item) {
mMediaItem = item;
if (item == null) mState = STATE_ERROR;
- if (item != null) mRotation = mMediaItem.getRotation();
}
protected void updateImage(Bitmap bitmap, boolean isCancelled) {
@@ -65,7 +63,8 @@ public abstract class AbstractDisplayItem extends DisplayItem {
@Override
public int getRotation() {
- return mRotation;
+ if (mMediaItem != null) return mMediaItem.getRotation();
+ return 0;
}
@Override