diff options
author | Chih-Chung Chang <chihchung@google.com> | 2009-07-24 10:58:40 +0800 |
---|---|---|
committer | Chih-Chung Chang <chihchung@google.com> | 2009-07-24 15:42:52 +0800 |
commit | 4250e214f539a3caee1434889c5660d594b45dfd (patch) | |
tree | 58a11701ddcec57d12091c94b8ccc549a89a5a32 /src/com/android | |
parent | d06aa697fb037b92b40b8e1ae6ab7db5c5b64d13 (diff) | |
download | packages_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.java | 299 | ||||
-rw-r--r-- | src/com/android/camera/ReviewImage.java | 261 | ||||
-rw-r--r-- | src/com/android/camera/Util.java | 20 | ||||
-rw-r--r-- | src/com/android/camera/ViewImage.java | 291 | ||||
-rw-r--r-- | src/com/android/camera/gallery/BaseImage.java | 10 | ||||
-rw-r--r-- | src/com/android/camera/gallery/IImage.java | 6 | ||||
-rw-r--r-- | src/com/android/camera/gallery/Image.java | 3 | ||||
-rw-r--r-- | src/com/android/camera/gallery/UriImage.java | 27 | ||||
-rw-r--r-- | src/com/android/camera/gallery/VideoObject.java | 3 |
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); } |