summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorChih-Chung Chang <chihchung@google.com>2009-07-24 10:58:40 +0800
committerChih-Chung Chang <chihchung@google.com>2009-07-24 15:42:52 +0800
commit4250e214f539a3caee1434889c5660d594b45dfd (patch)
tree58a11701ddcec57d12091c94b8ccc549a89a5a32 /src/com/android
parentd06aa697fb037b92b40b8e1ae6ab7db5c5b64d13 (diff)
downloadpackages_apps_LegacyCamera-4250e214f539a3caee1434889c5660d594b45dfd.zip
packages_apps_LegacyCamera-4250e214f539a3caee1434889c5660d594b45dfd.tar.gz
packages_apps_LegacyCamera-4250e214f539a3caee1434889c5660d594b45dfd.tar.bz2
Simplify ImageGetter.
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/camera/ImageGetter.java299
-rw-r--r--src/com/android/camera/ReviewImage.java261
-rw-r--r--src/com/android/camera/Util.java20
-rw-r--r--src/com/android/camera/ViewImage.java291
-rw-r--r--src/com/android/camera/gallery/BaseImage.java10
-rw-r--r--src/com/android/camera/gallery/IImage.java6
-rw-r--r--src/com/android/camera/gallery/Image.java3
-rw-r--r--src/com/android/camera/gallery/UriImage.java27
-rw-r--r--src/com/android/camera/gallery/VideoObject.java3
9 files changed, 356 insertions, 564 deletions
diff --git a/src/com/android/camera/ImageGetter.java b/src/com/android/camera/ImageGetter.java
new file mode 100644
index 0000000..60095da
--- /dev/null
+++ b/src/com/android/camera/ImageGetter.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2009 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.camera;
+
+import com.android.camera.gallery.IImage;
+import com.android.camera.gallery.IImageList;
+import com.android.camera.gallery.VideoObject;
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/*
+ * Here's the loading strategy. For any given image, load the thumbnail
+ * into memory and post a callback to display the resulting bitmap.
+ *
+ * Then proceed to load the full image bitmap. Three things can
+ * happen at this point:
+ *
+ * 1. the image fails to load because the UI thread decided
+ * to move on to a different image. This "cancellation" happens
+ * by virtue of the UI thread closing the stream containing the
+ * image being decoded. BitmapFactory.decodeStream returns null
+ * in this case.
+ *
+ * 2. the image loaded successfully. At that point we post
+ * a callback to the UI thread to actually show the bitmap.
+ *
+ * 3. when the post runs it checks to see if the image that was
+ * loaded is still the one we want. The UI may have moved on
+ * to some other image and if so we just drop the newly loaded
+ * bitmap on the floor.
+ */
+
+interface ImageGetterCallback {
+ public void imageLoaded(int pos, int offset, Bitmap bitmap,
+ boolean isThumb);
+ public boolean wantsThumbnail(int pos, int offset);
+ public boolean wantsFullImage(int pos, int offset);
+ public int fullImageSizeToUse(int pos, int offset);
+ public void completed();
+ public int [] loadOrder();
+}
+
+class ImageGetter {
+
+ @SuppressWarnings("unused")
+ private static final String TAG = "ImageGetter";
+
+ // The thread which does the work.
+ private Thread mGetterThread;
+
+ // The current request serial number.
+ // This is increased by one each time a new job is assigned.
+ // It is only written in the main thread.
+ private int mCurrentSerial;
+
+ // The base position that's being retrieved. The actual images retrieved
+ // are this base plus each of the offets. -1 means there is no current
+ // request needs to be finished.
+ private int mCurrentPosition = -1;
+
+ // The callback to invoke for each image.
+ private ImageGetterCallback mCB;
+
+ // The image list for the images.
+ private IImageList mImageList;
+
+ // The handler to do callback.
+ private GetterHandler mHandler;
+
+ // True if we want to cancel the current loading.
+ private volatile boolean mCancel = true;
+
+ // True if the getter thread is idle waiting.
+ private boolean mIdle = false;
+
+ // True when the getter thread should exit.
+ private boolean mDone = false;
+
+ private class ImageGetterRunnable implements Runnable {
+
+ private Runnable callback(final int position, final int offset,
+ final boolean isThumb, final Bitmap bitmap,
+ final int requestSerial) {
+ return new Runnable() {
+ public void run() {
+ // check for inflight callbacks that aren't applicable
+ // any longer before delivering them
+ if (requestSerial == mCurrentSerial) {
+ mCB.imageLoaded(position, offset, bitmap, isThumb);
+ } else if (bitmap != null) {
+ bitmap.recycle();
+ }
+ }
+ };
+ }
+
+ private Runnable completedCallback(final int requestSerial) {
+ return new Runnable() {
+ public void run() {
+ if (requestSerial == mCurrentSerial) {
+ mCB.completed();
+ }
+ }
+ };
+ }
+
+ public void run() {
+ while (true) {
+ synchronized (ImageGetter.this) {
+ while (mCancel || mDone || mCurrentPosition == -1) {
+ if (mDone) return;
+ mIdle = true;
+ ImageGetter.this.notify();
+ try {
+ ImageGetter.this.wait();
+ } catch (InterruptedException ex) {
+ // ignore
+ }
+ mIdle = false;
+ }
+ }
+
+ executeRequest();
+
+ synchronized (ImageGetter.this) {
+ mCurrentPosition = -1;
+ }
+ }
+ }
+ private void executeRequest() {
+ int imageCount = mImageList.getCount();
+
+ int [] order = mCB.loadOrder();
+ for (int i = 0; i < order.length; i++) {
+ if (mCancel) return;
+ int offset = order[i];
+ int imageNumber = mCurrentPosition + offset;
+ if (imageNumber >= 0 && imageNumber < imageCount) {
+ if (!mCB.wantsThumbnail(mCurrentPosition, offset)) {
+ continue;
+ }
+
+ IImage image = mImageList.getImageAt(imageNumber);
+ if (image == null) continue;
+ if (mCancel) return;
+
+ Bitmap b = image.thumbBitmap();
+ if (b == null) continue;
+ if (mCancel) {
+ b.recycle();
+ return;
+ }
+
+ Runnable cb = callback(mCurrentPosition, offset,
+ true, b, mCurrentSerial);
+ mHandler.postGetterCallback(cb);
+ }
+ }
+
+ for (int i = 0; i < order.length; i++) {
+ if (mCancel) return;
+ int offset = order[i];
+ int imageNumber = mCurrentPosition + offset;
+ if (imageNumber >= 0 && imageNumber < imageCount) {
+ if (!mCB.wantsFullImage(mCurrentPosition, offset)) {
+ continue;
+ }
+
+ IImage image = mImageList.getImageAt(imageNumber);
+ if (image == null) continue;
+ if (image instanceof VideoObject) continue;
+ if (mCancel) return;
+
+ int sizeToUse = mCB.fullImageSizeToUse(
+ mCurrentPosition, offset);
+ Bitmap b = image.fullSizeBitmap(sizeToUse,
+ IImage.ROTATE_AS_NEEDED, IImage.USE_NATIVE);
+ if (b == null) continue;
+ if (mCancel) {
+ b.recycle();
+ return;
+ }
+
+ Runnable cb = callback(mCurrentPosition, offset,
+ false, b, mCurrentSerial);
+ mHandler.postGetterCallback(cb);
+ }
+ }
+
+ mHandler.postGetterCallback(completedCallback(mCurrentSerial));
+ }
+ }
+
+ public ImageGetter() {
+ mGetterThread = new Thread(new ImageGetterRunnable());
+ mGetterThread.setName("ImageGettter");
+ mGetterThread.start();
+ }
+
+ // Cancels current loading (without waiting).
+ public synchronized void cancelCurrent() {
+ Util.Assert(mGetterThread != null);
+ mCancel = true;
+ BitmapManager.instance().cancelThreadDecoding(mGetterThread);
+ }
+
+ // Cancels current loading (with waiting).
+ private synchronized void cancelCurrentAndWait() {
+ cancelCurrent();
+ while (mIdle != true) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ // ignore.
+ }
+ }
+ }
+
+ // Stops this image getter.
+ public void stop() {
+ synchronized (this) {
+ cancelCurrentAndWait();
+ mDone = true;
+ notify();
+ }
+ try {
+ mGetterThread.join();
+ } catch (InterruptedException ex) {
+ // Ignore the exception
+ }
+ mGetterThread = null;
+ }
+
+ public synchronized void setPosition(int position, ImageGetterCallback cb,
+ IImageList imageList, GetterHandler handler) {
+ // Cancel the previous request.
+ cancelCurrentAndWait();
+
+ // Set new data.
+ mCurrentPosition = position;
+ mCB = cb;
+ mImageList = imageList;
+ mHandler = handler;
+ mCurrentSerial += 1;
+
+ // Kick-start the current request.
+ mCancel = false;
+ BitmapManager.instance().allowThreadDecoding(mGetterThread);
+ notify();
+ }
+}
+
+class GetterHandler extends Handler {
+ private static final int IMAGE_GETTER_CALLBACK = 1;
+
+ @Override
+ public void handleMessage(Message message) {
+ switch(message.what) {
+ case IMAGE_GETTER_CALLBACK:
+ ((Runnable) message.obj).run();
+ break;
+ }
+ }
+
+ public void postGetterCallback(Runnable callback) {
+ postDelayedGetterCallback(callback, 0);
+ }
+
+ public void postDelayedGetterCallback(Runnable callback, long delay) {
+ if (callback == null) {
+ throw new NullPointerException();
+ }
+ Message message = Message.obtain();
+ message.what = IMAGE_GETTER_CALLBACK;
+ message.obj = callback;
+ sendMessageDelayed(message, delay);
+ }
+
+ public void removeAllGetterCallbacks() {
+ removeMessages(IMAGE_GETTER_CALLBACK);
+ }
+}
diff --git a/src/com/android/camera/ReviewImage.java b/src/com/android/camera/ReviewImage.java
index 9d6d146..73f6bae 100644
--- a/src/com/android/camera/ReviewImage.java
+++ b/src/com/android/camera/ReviewImage.java
@@ -71,7 +71,7 @@ public class ReviewImage extends Activity implements View.OnClickListener {
private static final boolean AUTO_DISMISS = true;
private static final boolean NO_AUTO_DISMISS = false;
- private ReviewImageGetter mGetter;
+ private ImageGetter mGetter;
private Uri mSavedUri;
private boolean mPaused = true;
@@ -79,7 +79,7 @@ public class ReviewImage extends Activity implements View.OnClickListener {
private static final int[] sOrderAdjacents = new int[] {0, 1, -1};
private static final int[] sOrderSlideshow = new int[] {0};
- final LocalHandler mHandler = new LocalHandler();
+ final GetterHandler mHandler = new GetterHandler();
private final Random mRandom = new Random(System.currentTimeMillis());
private int [] mShuffleOrder = null;
@@ -423,7 +423,7 @@ public class ReviewImage extends Activity implements View.OnClickListener {
}
ImageGetterCallback cb = new ImageGetterCallback() {
- public void completed(boolean wasCanceled) {
+ public void completed() {
mImageView.setFocusableInTouchMode(true);
mImageView.requestFocus();
}
@@ -475,7 +475,7 @@ public class ReviewImage extends Activity implements View.OnClickListener {
// Could be null if we're stopping a slide show in the course of pausing
if (mGetter != null) {
- mGetter.setPosition(pos, cb);
+ mGetter.setPosition(pos, cb, mAllImages, mHandler);
}
updateActionIcons();
showOnScreenControls(AUTO_DISMISS);
@@ -708,7 +708,7 @@ public class ReviewImage extends Activity implements View.OnClickListener {
final long targetDisplayTime = System.currentTimeMillis() + delay;
ImageGetterCallback cb = new ImageGetterCallback() {
- public void completed(boolean wasCanceled) {
+ public void completed() {
}
public boolean wantsThumbnail(int pos, int offset) {
@@ -796,12 +796,12 @@ public class ReviewImage extends Activity implements View.OnClickListener {
if (mShuffleOrder != null) {
pos = mShuffleOrder[pos];
}
- mGetter.setPosition(pos, cb);
+ mGetter.setPosition(pos, cb, mAllImages, mHandler);
}
}
private void makeGetter() {
- mGetter = new ReviewImageGetter(this);
+ mGetter = new ImageGetter();
}
private IImageList buildImageListFromUri(Uri uri) {
@@ -984,253 +984,6 @@ public class ReviewImage extends Activity implements View.OnClickListener {
break;
}
}
-
- static class LocalHandler extends Handler {
- private static final int IMAGE_GETTER_CALLBACK = 1;
-
- @Override
- public void handleMessage(Message message) {
- switch(message.what) {
- case IMAGE_GETTER_CALLBACK:
- ((Runnable) message.obj).run();
- break;
- }
- }
-
- public void postGetterCallback(Runnable callback) {
- postDelayedGetterCallback(callback, 0);
- }
-
- public void postDelayedGetterCallback(Runnable callback, long delay) {
- if (callback == null) {
- throw new NullPointerException();
- }
- Message message = Message.obtain();
- message.what = IMAGE_GETTER_CALLBACK;
- message.obj = callback;
- sendMessageDelayed(message, delay);
- }
-
- public void removeAllGetterCallbacks() {
- removeMessages(IMAGE_GETTER_CALLBACK);
- }
- }
-}
-
-class ReviewImageGetter {
-
- @SuppressWarnings("unused")
- private static final String TAG = "ImageGetter";
-
- // The thread which does the work.
- private final Thread mGetterThread;
-
- // The base position that's being retrieved. The actual images retrieved
- // are this base plus each of the offets.
- private int mCurrentPosition = -1;
-
- // The callback to invoke for each image.
- private ImageGetterCallback mCB;
-
- // This is the loader cancelable that gets set while we're loading an image.
- // If we change position we can cancel the current load using this.
- private Cancelable<Bitmap> mLoad;
-
- // True if we're canceling the current load.
- private boolean mCancelCurrent = false;
-
- // True when the therad should exit.
- private boolean mDone = false;
-
- // True when the loader thread is waiting for work.
- private boolean mReady = false;
-
- // The ViewImage this ImageGetter belongs to
- ReviewImage mViewImage;
-
- void cancelCurrent() {
- synchronized (this) {
- if (!mReady) {
- mCancelCurrent = true;
- Cancelable<Bitmap> load = mLoad;
- if (load != null) {
- load.requestCancel();
- }
- mCancelCurrent = false;
- }
- }
- }
-
- private class ImageGetterRunnable implements Runnable {
- private Runnable callback(final int position, final int offset,
- final boolean isThumb, final Bitmap bitmap) {
- return new Runnable() {
- public void run() {
- // check for inflight callbacks that aren't applicable
- // any longer before delivering them
- if (!isCanceled() && position == mCurrentPosition) {
- mCB.imageLoaded(position, offset, bitmap, isThumb);
- } else if (bitmap != null) {
- bitmap.recycle();
- }
- }
- };
- }
-
- private Runnable completedCallback(final boolean wasCanceled) {
- return new Runnable() {
- public void run() {
- mCB.completed(wasCanceled);
- }
- };
- }
-
- public void run() {
- int lastPosition = -1;
- while (!mDone) {
- synchronized (ReviewImageGetter.this) {
- mReady = true;
- ReviewImageGetter.this.notify();
-
- if (mCurrentPosition == -1
- || lastPosition == mCurrentPosition) {
- try {
- ReviewImageGetter.this.wait();
- } catch (InterruptedException ex) {
- continue;
- }
- }
-
- lastPosition = mCurrentPosition;
- mReady = false;
- }
-
- if (lastPosition != -1) {
- int imageCount = mViewImage.mAllImages.getCount();
-
- int [] order = mCB.loadOrder();
- for (int i = 0; i < order.length; i++) {
- int offset = order[i];
- int imageNumber = lastPosition + offset;
- if (imageNumber >= 0 && imageNumber < imageCount) {
- IImage image = mViewImage.mAllImages
- .getImageAt(lastPosition + offset);
- if (image == null || isCanceled()) {
- break;
- }
- if (mCB.wantsThumbnail(lastPosition, offset)) {
- Bitmap b = image.thumbBitmap();
- mViewImage.mHandler.postGetterCallback(
- callback(lastPosition, offset,
- true, b));
- }
- }
- }
-
- for (int i = 0; i < order.length; i++) {
- int offset = order[i];
- int imageNumber = lastPosition + offset;
- if (imageNumber >= 0 && imageNumber < imageCount) {
- IImage image = mViewImage.mAllImages
- .getImageAt(lastPosition + offset);
- if (mCB.wantsFullImage(lastPosition, offset)
- && !(image instanceof VideoObject)) {
- int sizeToUse = mCB.fullImageSizeToUse(
- lastPosition, offset);
- if (image != null && !isCanceled()) {
- mLoad = image.fullSizeBitmapCancelable(
- sizeToUse,
- Util.createNativeAllocOptions());
- }
- if (mLoad != null) {
- // The return value could be null if the
- // bitmap is too big, or we cancelled it.
- Bitmap b;
- try {
- b = mLoad.get();
- } catch (InterruptedException e) {
- b = null;
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- } catch (CancellationException e) {
- b = null;
- }
- mLoad = null;
- if (b != null) {
- if (isCanceled()) {
- b.recycle();
- } else {
- Runnable cb = callback(
- lastPosition, offset,
- false, b);
- mViewImage.mHandler
- .postGetterCallback(cb);
- }
- }
- }
- }
- }
- }
- mViewImage.mHandler.postGetterCallback(
- completedCallback(isCanceled()));
- }
- }
- }
- }
-
- public ReviewImageGetter(ReviewImage viewImage) {
- mViewImage = viewImage;
- mGetterThread = new Thread(new ImageGetterRunnable());
- mGetterThread.setName("ImageGettter");
- mGetterThread.start();
- }
-
- private boolean isCanceled() {
- synchronized (this) {
- return mCancelCurrent;
- }
- }
-
- public void setPosition(int position, ImageGetterCallback cb) {
- synchronized (this) {
- if (!mReady) {
- try {
- mCancelCurrent = true;
- // if the thread is waiting before loading the full size
- // image then this will free it up
- BitmapManager.instance()
- .cancelThreadDecoding(mGetterThread);
- ReviewImageGetter.this.notify();
- ReviewImageGetter.this.wait();
- BitmapManager.instance()
- .allowThreadDecoding(mGetterThread);
- mCancelCurrent = false;
- } catch (InterruptedException ex) {
- // not sure what to do here
- }
- }
- }
-
- mCurrentPosition = position;
- mCB = cb;
-
- synchronized (this) {
- ReviewImageGetter.this.notify();
- }
- }
-
- public void stop() {
- synchronized (this) {
- mDone = true;
- ReviewImageGetter.this.notify();
- }
- try {
- BitmapManager.instance().cancelThreadDecoding(mGetterThread);
- mGetterThread.join();
- } catch (InterruptedException ex) {
- // Ignore the exception
- }
- }
}
class ImageViewTouch2 extends ImageViewTouchBase {
diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java
index 356426f..cefc6d8 100644
--- a/src/com/android/camera/Util.java
+++ b/src/com/android/camera/Util.java
@@ -302,10 +302,19 @@ public class Util {
*/
public static Bitmap makeBitmap(int targetWidthOrHeight, Uri uri,
ContentResolver cr) {
+ return makeBitmap(targetWidthOrHeight, uri, cr, false);
+ }
+
+ public static Bitmap makeBitmap(int targetWidthOrHeight, Uri uri,
+ ContentResolver cr, boolean useNative) {
ParcelFileDescriptor input = null;
try {
input = cr.openFileDescriptor(uri, "r");
- return makeBitmap(targetWidthOrHeight, uri, cr, input, null);
+ BitmapFactory.Options options = null;
+ if (useNative) {
+ options = createNativeAllocOptions();
+ }
+ return makeBitmap(targetWidthOrHeight, uri, cr, input, options);
} catch (IOException ex) {
return null;
} finally {
@@ -313,6 +322,15 @@ public class Util {
}
}
+ public static Bitmap makeBitmap(int targetWidthHeight,
+ ParcelFileDescriptor pfd, boolean useNative) {
+ BitmapFactory.Options options = null;
+ if (useNative) {
+ options = createNativeAllocOptions();
+ }
+ return makeBitmap(targetWidthHeight, null, null, pfd, options);
+ }
+
public static Bitmap makeBitmap(int targetWidthHeight, Uri uri,
ContentResolver cr, ParcelFileDescriptor pfd,
BitmapFactory.Options options) {
diff --git a/src/com/android/camera/ViewImage.java b/src/com/android/camera/ViewImage.java
index acc6c87..6ee8a50 100644
--- a/src/com/android/camera/ViewImage.java
+++ b/src/com/android/camera/ViewImage.java
@@ -79,7 +79,7 @@ public class ViewImage extends Activity implements View.OnClickListener {
private static final int[] sOrderAdjacents = new int[] {0, 1, -1};
private static final int[] sOrderSlideshow = new int[] {0};
- final LocalHandler mHandler = new LocalHandler();
+ final GetterHandler mHandler = new GetterHandler();
private final Random mRandom = new Random(System.currentTimeMillis());
private int [] mShuffleOrder = null;
@@ -460,7 +460,7 @@ public class ViewImage extends Activity implements View.OnClickListener {
}
ImageGetterCallback cb = new ImageGetterCallback() {
- public void completed(boolean wasCanceled) {
+ public void completed() {
if (!mShowActionIcons) {
mImageView.setFocusableInTouchMode(true);
mImageView.requestFocus();
@@ -514,7 +514,7 @@ public class ViewImage extends Activity implements View.OnClickListener {
// Could be null if we're stopping a slide show in the course of pausing
if (mGetter != null) {
- mGetter.setPosition(pos, cb);
+ mGetter.setPosition(pos, cb, mAllImages, mHandler);
}
updateActionIcons();
showOnScreenControls(AUTO_DISMISS);
@@ -766,7 +766,7 @@ public class ViewImage extends Activity implements View.OnClickListener {
final long targetDisplayTime = System.currentTimeMillis() + delay;
ImageGetterCallback cb = new ImageGetterCallback() {
- public void completed(boolean wasCanceled) {
+ public void completed() {
}
public boolean wantsThumbnail(int pos, int offset) {
@@ -854,12 +854,12 @@ public class ViewImage extends Activity implements View.OnClickListener {
if (mShuffleOrder != null) {
pos = mShuffleOrder[pos];
}
- mGetter.setPosition(pos, cb);
+ mGetter.setPosition(pos, cb, mAllImages, mHandler);
}
}
private void makeGetter() {
- mGetter = new ImageGetter(this);
+ mGetter = new ImageGetter();
}
private IImageList buildImageListFromUri(Uri uri) {
@@ -1052,37 +1052,6 @@ public class ViewImage extends Activity implements View.OnClickListener {
break;
}
}
-
- static class LocalHandler extends Handler {
- private static final int IMAGE_GETTER_CALLBACK = 1;
-
- @Override
- public void handleMessage(Message message) {
- switch(message.what) {
- case IMAGE_GETTER_CALLBACK:
- ((Runnable) message.obj).run();
- break;
- }
- }
-
- public void postGetterCallback(Runnable callback) {
- postDelayedGetterCallback(callback, 0);
- }
-
- public void postDelayedGetterCallback(Runnable callback, long delay) {
- if (callback == null) {
- throw new NullPointerException();
- }
- Message message = Message.obtain();
- message.what = IMAGE_GETTER_CALLBACK;
- message.obj = callback;
- sendMessageDelayed(message, delay);
- }
-
- public void removeAllGetterCallbacks() {
- removeMessages(IMAGE_GETTER_CALLBACK);
- }
- }
}
class ImageViewTouch extends ImageViewTouchBase {
@@ -1208,254 +1177,6 @@ class ImageViewTouch extends ImageViewTouchBase {
}
}
-/*
- * Here's the loading strategy. For any given image, load the thumbnail
- * into memory and post a callback to display the resulting bitmap.
- *
- * Then proceed to load the full image bitmap. Three things can
- * happen at this point:
- *
- * 1. the image fails to load because the UI thread decided
- * to move on to a different image. This "cancellation" happens
- * by virtue of the UI thread closing the stream containing the
- * image being decoded. BitmapFactory.decodeStream returns null
- * in this case.
- *
- * 2. the image loaded successfully. At that point we post
- * a callback to the UI thread to actually show the bitmap.
- *
- * 3. when the post runs it checks to see if the image that was
- * loaded is still the one we want. The UI may have moved on
- * to some other image and if so we just drop the newly loaded
- * bitmap on the floor.
- */
-
-interface ImageGetterCallback {
- public void imageLoaded(int pos, int offset, Bitmap bitmap,
- boolean isThumb);
- public boolean wantsThumbnail(int pos, int offset);
- public boolean wantsFullImage(int pos, int offset);
- public int fullImageSizeToUse(int pos, int offset);
- public void completed(boolean wasCanceled);
- public int [] loadOrder();
-}
-
-class ImageGetter {
-
- @SuppressWarnings("unused")
- private static final String TAG = "ImageGetter";
-
- // The thread which does the work.
- private final Thread mGetterThread;
-
- // The base position that's being retrieved. The actual images retrieved
- // are this base plus each of the offets.
- private int mCurrentPosition = -1;
-
- // The callback to invoke for each image.
- private ImageGetterCallback mCB;
-
- // This is the loader cancelable that gets set while we're loading an image.
- // If we change position we can cancel the current load using this.
- private Cancelable<Bitmap> mLoad;
-
- // True if we're canceling the current load.
- private boolean mCancelCurrent = false;
-
- // True when the therad should exit.
- private boolean mDone = false;
-
- // True when the loader thread is waiting for work.
- private boolean mReady = false;
-
- // The ViewImage this ImageGetter belongs to
- ViewImage mViewImage;
-
- void cancelCurrent() {
- synchronized (this) {
- if (!mReady) {
- mCancelCurrent = true;
- Cancelable<Bitmap> load = mLoad;
- if (load != null) {
- load.requestCancel();
- }
- mCancelCurrent = false;
- }
- }
- }
-
- private class ImageGetterRunnable implements Runnable {
- private Runnable callback(final int position, final int offset,
- final boolean isThumb, final Bitmap bitmap) {
- return new Runnable() {
- public void run() {
- // check for inflight callbacks that aren't applicable
- // any longer before delivering them
- if (!isCanceled() && position == mCurrentPosition) {
- mCB.imageLoaded(position, offset, bitmap, isThumb);
- } else if (bitmap != null) {
- bitmap.recycle();
- }
- }
- };
- }
-
- private Runnable completedCallback(final boolean wasCanceled) {
- return new Runnable() {
- public void run() {
- mCB.completed(wasCanceled);
- }
- };
- }
-
- public void run() {
- int lastPosition = -1;
- while (!mDone) {
- synchronized (ImageGetter.this) {
- mReady = true;
- ImageGetter.this.notify();
-
- if (mCurrentPosition == -1
- || lastPosition == mCurrentPosition) {
- try {
- ImageGetter.this.wait();
- } catch (InterruptedException ex) {
- continue;
- }
- }
-
- lastPosition = mCurrentPosition;
- mReady = false;
- }
-
- if (lastPosition != -1) {
- int imageCount = mViewImage.mAllImages.getCount();
-
- int [] order = mCB.loadOrder();
- for (int i = 0; i < order.length; i++) {
- int offset = order[i];
- int imageNumber = lastPosition + offset;
- if (imageNumber >= 0 && imageNumber < imageCount) {
- IImage image = mViewImage.mAllImages
- .getImageAt(lastPosition + offset);
- if (image == null || isCanceled()) {
- break;
- }
- if (mCB.wantsThumbnail(lastPosition, offset)) {
- Bitmap b = image.thumbBitmap();
- mViewImage.mHandler.postGetterCallback(
- callback(lastPosition, offset,
- true, b));
- }
- }
- }
-
- for (int i = 0; i < order.length; i++) {
- int offset = order[i];
- int imageNumber = lastPosition + offset;
- if (imageNumber >= 0 && imageNumber < imageCount) {
- IImage image = mViewImage.mAllImages
- .getImageAt(lastPosition + offset);
- if (mCB.wantsFullImage(lastPosition, offset)
- && !(image instanceof VideoObject)) {
- int sizeToUse = mCB.fullImageSizeToUse(
- lastPosition, offset);
- if (image != null && !isCanceled()) {
- mLoad = image.fullSizeBitmapCancelable(
- sizeToUse,
- Util.createNativeAllocOptions());
- }
- if (mLoad != null) {
- // The return value could be null if the
- // bitmap is too big, or we cancelled it.
- Bitmap b;
- try {
- b = mLoad.get();
- } catch (InterruptedException e) {
- b = null;
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- } catch (CancellationException e) {
- b = null;
- }
- mLoad = null;
- if (b != null) {
- if (isCanceled()) {
- b.recycle();
- } else {
- Runnable cb = callback(
- lastPosition, offset,
- false, b);
- mViewImage.mHandler.
- postGetterCallback(cb);
- }
- }
- }
- }
- }
- }
- mViewImage.mHandler.postGetterCallback(
- completedCallback(isCanceled()));
- }
- }
- }
- }
-
- public ImageGetter(ViewImage viewImage) {
- mViewImage = viewImage;
- mGetterThread = new Thread(new ImageGetterRunnable());
- mGetterThread.setName("ImageGettter");
- mGetterThread.start();
- }
-
- private boolean isCanceled() {
- synchronized (this) {
- return mCancelCurrent;
- }
- }
-
- public void setPosition(int position, ImageGetterCallback cb) {
- synchronized (this) {
- if (!mReady) {
- try {
- mCancelCurrent = true;
- // if the thread is waiting before loading the full size
- // image then this will free it up
- BitmapManager.instance()
- .cancelThreadDecoding(mGetterThread);
- ImageGetter.this.notify();
- ImageGetter.this.wait();
- BitmapManager.instance()
- .allowThreadDecoding(mGetterThread);
- mCancelCurrent = false;
- } catch (InterruptedException ex) {
- // not sure what to do here
- }
- }
- }
-
- mCurrentPosition = position;
- mCB = cb;
-
- synchronized (this) {
- ImageGetter.this.notify();
- }
- }
-
- public void stop() {
- synchronized (this) {
- mDone = true;
- ImageGetter.this.notify();
- }
- try {
- BitmapManager.instance().cancelThreadDecoding(mGetterThread);
- mGetterThread.join();
- } catch (InterruptedException ex) {
- // Ignore the exception
- }
- }
-}
-
// This is a cache for Bitmap displayed in ViewImage (normal mode, thumb only).
class BitmapCache implements ImageViewTouchBase.Recycler {
public static class Entry {
diff --git a/src/com/android/camera/gallery/BaseImage.java b/src/com/android/camera/gallery/BaseImage.java
index f6b0211..10a0c9f 100644
--- a/src/com/android/camera/gallery/BaseImage.java
+++ b/src/com/android/camera/gallery/BaseImage.java
@@ -159,15 +159,17 @@ public abstract class BaseImage implements IImage {
}
public Bitmap fullSizeBitmap(int targetWidthHeight) {
- return fullSizeBitmap(targetWidthHeight, true);
+ return fullSizeBitmap(targetWidthHeight, IImage.ROTATE_AS_NEEDED,
+ IImage.NO_NATIVE);
}
- protected Bitmap fullSizeBitmap(
- int targetWidthHeight, boolean rotateAsNeeded) {
+ public Bitmap fullSizeBitmap(
+ int targetWidthHeight, boolean rotateAsNeeded, boolean useNative) {
Uri url = mContainer.contentUri(mId);
if (url == null) return null;
- Bitmap b = Util.makeBitmap(targetWidthHeight, url, mContentResolver);
+ Bitmap b = Util.makeBitmap(targetWidthHeight, url, mContentResolver,
+ useNative);
if (b != null && rotateAsNeeded) {
b = Util.rotate(b, getDegreesRotated());
}
diff --git a/src/com/android/camera/gallery/IImage.java b/src/com/android/camera/gallery/IImage.java
index 7217926..63db330 100644
--- a/src/com/android/camera/gallery/IImage.java
+++ b/src/com/android/camera/gallery/IImage.java
@@ -34,6 +34,12 @@ public interface IImage {
/** Get the bitmap for the full size image. */
public abstract Bitmap fullSizeBitmap(int targetWidthOrHeight);
+ public abstract Bitmap fullSizeBitmap(int targetWidthOrHeight,
+ boolean rotateAsNeeded, boolean useNative);
+ public static final boolean ROTATE_AS_NEEDED = true;
+ public static final boolean NO_ROTATE = false;
+ public static final boolean USE_NATIVE = true;
+ public static final boolean NO_NATIVE = false;
/** Get the cancelable object for the bitmap of the full size image. */
public abstract Cancelable<Bitmap> fullSizeBitmapCancelable(
diff --git a/src/com/android/camera/gallery/Image.java b/src/com/android/camera/gallery/Image.java
index 4eba5b3..095b0dc 100644
--- a/src/com/android/camera/gallery/Image.java
+++ b/src/com/android/camera/gallery/Image.java
@@ -273,7 +273,8 @@ public class Image extends BaseImage implements IImage {
}
if (bitmap == null) {
- bitmap = fullSizeBitmap(THUMBNAIL_TARGET_SIZE, false);
+ bitmap = fullSizeBitmap(THUMBNAIL_TARGET_SIZE, IImage.NO_ROTATE,
+ IImage.NO_NATIVE);
// No thumbnail found... storing the new one.
bitmap = mContainer.storeThumbnail(bitmap, mId);
}
diff --git a/src/com/android/camera/gallery/UriImage.java b/src/com/android/camera/gallery/UriImage.java
index f793999..88f67f6 100644
--- a/src/com/android/camera/gallery/UriImage.java
+++ b/src/com/android/camera/gallery/UriImage.java
@@ -74,28 +74,19 @@ class UriImage implements IImage {
}
public Bitmap fullSizeBitmap(int targetWidthHeight) {
+ return fullSizeBitmap(targetWidthHeight, IImage.ROTATE_AS_NEEDED,
+ IImage.NO_NATIVE);
+ }
+
+ public Bitmap fullSizeBitmap(int targetWidthHeight, boolean rotateAsNeeded,
+ boolean useNative) {
try {
ParcelFileDescriptor pfdInput = getPFD();
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapManager.instance().decodeFileDescriptor(
- pfdInput.getFileDescriptor(), options);
-
- if (targetWidthHeight != -1) {
- options.inSampleSize =
- Util.computeSampleSize(options, targetWidthHeight);
- }
-
- options.inJustDecodeBounds = false;
- options.inDither = false;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-
- Bitmap b = BitmapManager.instance().decodeFileDescriptor(
- pfdInput.getFileDescriptor(), options);
- pfdInput.close();
+ Bitmap b = Util.makeBitmap(targetWidthHeight, pfdInput,
+ useNative);
return b;
} catch (Exception ex) {
- Log.e(TAG, "got exception decoding bitmap " + ex.toString());
+ Log.e(TAG, "got exception decoding bitmap ", ex);
return null;
}
}
diff --git a/src/com/android/camera/gallery/VideoObject.java b/src/com/android/camera/gallery/VideoObject.java
index 80c9dfe..a533ae9 100644
--- a/src/com/android/camera/gallery/VideoObject.java
+++ b/src/com/android/camera/gallery/VideoObject.java
@@ -64,7 +64,8 @@ public class VideoObject extends BaseImage implements IImage {
}
@Override
- public Bitmap fullSizeBitmap(int targetWidthHeight) {
+ public Bitmap fullSizeBitmap(int targetWidthHeight, boolean rotateAsNeeded,
+ boolean useNative) {
return Util.createVideoThumbnail(mDataPath);
}