summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d
diff options
context:
space:
mode:
authorOwen Lin <owenlin@google.com>2012-03-29 14:27:58 +0800
committerOwen Lin <owenlin@google.com>2012-04-05 15:15:39 +0800
commitd8fb81f601830385a2343d08ad5dd171e4c7bfe0 (patch)
treee075c193323154ef35abc0a452ae9b82c31ce01d /src/com/android/gallery3d
parent5e96a9f26b2a8745235a706df5352ae7fb872a93 (diff)
downloadandroid_packages_apps_Gallery2-d8fb81f601830385a2343d08ad5dd171e4c7bfe0.tar.gz
android_packages_apps_Gallery2-d8fb81f601830385a2343d08ad5dd171e4c7bfe0.tar.bz2
android_packages_apps_Gallery2-d8fb81f601830385a2343d08ad5dd171e4c7bfe0.zip
Upload textures in background.
Change-Id: I365f7be9aaab793366884249cbb10e8b6f0ab0e6
Diffstat (limited to 'src/com/android/gallery3d')
-rw-r--r--src/com/android/gallery3d/app/AbstractGalleryActivity.java4
-rw-r--r--src/com/android/gallery3d/app/AlbumDataAdapter.java3
-rw-r--r--src/com/android/gallery3d/app/AlbumSetDataAdapter.java3
-rw-r--r--src/com/android/gallery3d/data/ImageCacheRequest.java3
-rw-r--r--src/com/android/gallery3d/data/MediaItem.java10
-rw-r--r--src/com/android/gallery3d/ui/AlbumLabelMaker.java54
-rw-r--r--src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java103
-rw-r--r--src/com/android/gallery3d/ui/AlbumSetView.java13
-rw-r--r--src/com/android/gallery3d/ui/AlbumSlidingWindow.java46
-rw-r--r--src/com/android/gallery3d/ui/AlbumView.java9
-rw-r--r--src/com/android/gallery3d/ui/BitmapPool.java65
-rw-r--r--src/com/android/gallery3d/ui/GLRoot.java5
-rw-r--r--src/com/android/gallery3d/ui/GLRootView.java17
-rw-r--r--src/com/android/gallery3d/ui/SlotView.java16
-rw-r--r--src/com/android/gallery3d/ui/TextureUploader.java97
-rw-r--r--src/com/android/gallery3d/ui/TileImageView.java3
16 files changed, 325 insertions, 126 deletions
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
index 899e9bfa4..64c430d7f 100644
--- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java
+++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
@@ -33,7 +33,7 @@ import android.view.WindowManager;
import com.android.gallery3d.R;
import com.android.gallery3d.data.DataManager;
-import com.android.gallery3d.ui.BitmapPool;
+import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.ui.GLRoot;
import com.android.gallery3d.ui.GLRootView;
import com.android.gallery3d.util.ThreadPool;
@@ -177,7 +177,7 @@ public class AbstractGalleryActivity extends Activity implements GalleryActivity
} finally {
mGLRootView.unlockRenderThread();
}
- BitmapPool.clear();
+ MediaItem.getMicroThumbPool().clear();
}
@Override
diff --git a/src/com/android/gallery3d/app/AlbumDataAdapter.java b/src/com/android/gallery3d/app/AlbumDataAdapter.java
index 1d10b89f4..ec46e50e5 100644
--- a/src/com/android/gallery3d/app/AlbumDataAdapter.java
+++ b/src/com/android/gallery3d/app/AlbumDataAdapter.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.app;
import android.os.Handler;
import android.os.Message;
+import android.os.Process;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.ContentListener;
@@ -314,6 +315,8 @@ public class AlbumDataAdapter implements AlbumView.Model {
@Override
public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
boolean updateComplete = false;
while (mActive) {
synchronized (this) {
diff --git a/src/com/android/gallery3d/app/AlbumSetDataAdapter.java b/src/com/android/gallery3d/app/AlbumSetDataAdapter.java
index e739a96fb..eea039d19 100644
--- a/src/com/android/gallery3d/app/AlbumSetDataAdapter.java
+++ b/src/com/android/gallery3d/app/AlbumSetDataAdapter.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.app;
import android.os.Handler;
import android.os.Message;
+import android.os.Process;
import android.os.SystemClock;
import com.android.gallery3d.common.Utils;
@@ -307,6 +308,8 @@ public class AlbumSetDataAdapter implements AlbumSetView.Model {
@Override
public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
boolean updateComplete = false;
while (mActive) {
synchronized (this) {
diff --git a/src/com/android/gallery3d/data/ImageCacheRequest.java b/src/com/android/gallery3d/data/ImageCacheRequest.java
index 64dfc9f45..d3d4f51e7 100644
--- a/src/com/android/gallery3d/data/ImageCacheRequest.java
+++ b/src/com/android/gallery3d/data/ImageCacheRequest.java
@@ -22,7 +22,6 @@ import android.graphics.BitmapFactory;
import com.android.gallery3d.app.GalleryApp;
import com.android.gallery3d.common.BitmapUtils;
import com.android.gallery3d.data.ImageCacheService.ImageData;
-import com.android.gallery3d.ui.BitmapPool;
import com.android.gallery3d.util.ThreadPool.Job;
import com.android.gallery3d.util.ThreadPool.JobContext;
@@ -56,7 +55,7 @@ abstract class ImageCacheRequest implements Job<Bitmap> {
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap;
if (mType == MediaItem.TYPE_MICROTHUMBNAIL) {
- bitmap = BitmapPool.decode(jc, BitmapPool.TYPE_MICRO_THUMB,
+ bitmap = MediaItem.getMicroThumbPool().decode(jc,
data.mData, data.mOffset, data.mData.length - data.mOffset, options);
} else {
bitmap = DecodeUtils.decode(jc,
diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java
index b2632f1cc..2b8f8a353 100644
--- a/src/com/android/gallery3d/data/MediaItem.java
+++ b/src/com/android/gallery3d/data/MediaItem.java
@@ -19,8 +19,9 @@ package com.android.gallery3d.data;
import android.graphics.Bitmap;
import android.graphics.BitmapRegionDecoder;
-import com.android.gallery3d.util.ThreadPool.Job;
+import com.android.gallery3d.ui.BitmapPool;
import com.android.gallery3d.ui.ScreenNail;
+import com.android.gallery3d.util.ThreadPool.Job;
// MediaItem represents an image or a video item.
public abstract class MediaItem extends MediaObject {
@@ -39,6 +40,9 @@ public abstract class MediaItem extends MediaObject {
public static final String MIME_TYPE_JPEG = "image/jpeg";
+ private static final BitmapPool sMicroThumbPool =
+ new BitmapPool(MICROTHUMBNAIL_TARGET_SIZE, MICROTHUMBNAIL_TARGET_SIZE);
+
// TODO: fix default value for latlng and change this.
public static final double INVALID_LATLNG = 0f;
@@ -108,4 +112,8 @@ public abstract class MediaItem extends MediaObject {
"should only request thumb/microthumb from cache");
}
}
+
+ public static BitmapPool getMicroThumbPool() {
+ return sMicroThumbPool;
+ }
}
diff --git a/src/com/android/gallery3d/ui/AlbumLabelMaker.java b/src/com/android/gallery3d/ui/AlbumLabelMaker.java
index 22fe16d5f..68fa6958a 100644
--- a/src/com/android/gallery3d/ui/AlbumLabelMaker.java
+++ b/src/com/android/gallery3d/ui/AlbumLabelMaker.java
@@ -8,6 +8,7 @@ import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.TextUtils;
@@ -27,6 +28,9 @@ public class AlbumLabelMaker {
private final TextPaint mCountPaint;
private final Context mContext;
+ private int mLabelWidth;
+ private BitmapPool mBitmapPool;
+
private final LazyLoadedBitmap mLocalSetIcon;
private final LazyLoadedBitmap mPicasaIcon;
private final LazyLoadedBitmap mCameraIcon;
@@ -89,14 +93,20 @@ public class AlbumLabelMaker {
}
}
+ public synchronized void setLabelWidth(int width) {
+ if (mLabelWidth == width) return;
+ mLabelWidth = width;
+ mBitmapPool = new BitmapPool(mLabelWidth, mSpec.labelBackgroundHeight);
+ }
+
public ThreadPool.Job<Bitmap> requestLabel(
- String title, String count, int sourceType, int slotWidth) {
- return new AlbumLabelJob(null, title, count, sourceType, slotWidth);
+ String title, String count, int sourceType) {
+ return new AlbumLabelJob(null, title, count, sourceType);
}
public ThreadPool.Job<Bitmap> requestLabel(
- MediaSet album, int sourceType, int slotWidth) {
- return new AlbumLabelJob(album, null, null, sourceType, slotWidth);
+ MediaSet album, int sourceType) {
+ return new AlbumLabelJob(album, null, null, sourceType);
}
private static void drawText(Canvas canvas,
@@ -114,15 +124,13 @@ public class AlbumLabelMaker {
private final String mTitle;
private final String mCount;
private final int mSourceType;
- private final int mSlotWidth;
public AlbumLabelJob(MediaSet album,
- String title, String count, int sourceType, int slotWidth) {
+ String title, String count, int sourceType) {
mAlbum = album;
mTitle = title;
mCount = count;
mSourceType = sourceType;
- mSlotWidth = slotWidth;
}
@Override
@@ -136,22 +144,36 @@ public class AlbumLabelMaker {
? Utils.ensureNotNull(mCount)
: String.valueOf(album.getTotalMediaItemCount());
Bitmap icon = getOverlayAlbumIcon(mSourceType);
- Bitmap bitmap = Bitmap.createBitmap(mSlotWidth,
- s.labelBackgroundHeight, Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
+
+ Bitmap bitmap = null;
+ Canvas canvas;
+ int labelWidth;
+
+ synchronized (this) {
+ labelWidth = mLabelWidth;
+ bitmap = mBitmapPool.getBitmap();
+ }
+ if (bitmap == null) {
+ bitmap = Bitmap.createBitmap(labelWidth,
+ s.labelBackgroundHeight, Config.ARGB_8888);
+ canvas = new Canvas(bitmap);
+ } else {
+ canvas = new Canvas(bitmap);
+ canvas.drawColor(0, PorterDuff.Mode.SRC);
+ }
// draw title
if (jc.isCancelled()) return null;
int x = s.leftMargin;
int y = s.titleOffset;
- drawText(canvas, x, y, title, mSlotWidth - s.leftMargin, mTitlePaint);
+ drawText(canvas, x, y, title, labelWidth - s.leftMargin, mTitlePaint);
// draw the count
if (jc.isCancelled()) return null;
if (icon != null) x = s.iconSize;
y += s.titleFontSize + s.countOffset;
drawText(canvas, x, y, count,
- mSlotWidth - s.leftMargin - s.iconSize, mCountPaint);
+ labelWidth - s.leftMargin - s.iconSize, mCountPaint);
// draw the icon
if (icon != null) {
@@ -166,4 +188,12 @@ public class AlbumLabelMaker {
return bitmap;
}
}
+
+ public void reycleLabel(Bitmap label) {
+ mBitmapPool.recycle(label);
+ }
+
+ public void clearRecycledLabels() {
+ mBitmapPool.clear();
+ }
}
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
index 6eb391198..467b52233 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
@@ -57,6 +57,7 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
private final ThreadPool mThreadPool;
private final AlbumLabelMaker mLabelMaker;
private final String mLoadingText;
+ private final TextureUploader mTextureUploader;
private int mActiveRequestCount = 0;
private boolean mIsActive = false;
@@ -94,6 +95,7 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
mLabelMaker = new AlbumLabelMaker(activity.getAndroidContext(), labelSpec);
mLoadingText = activity.getAndroidContext().getString(R.string.loading);
+ mTextureUploader = new TextureUploader(activity.getGLRoot());
mHandler = new SynchronizedHandler(activity.getGLRoot()) {
@Override
@@ -168,7 +170,11 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
0, Math.max(0, mSize - data.length));
int contentEnd = Math.min(contentStart + data.length, mSize);
setContentWindow(contentStart, contentEnd);
- if (mIsActive) updateAllImageRequests();
+
+ if (mIsActive) {
+ updateTextureUploadQueue();
+ updateAllImageRequests();
+ }
}
// We would like to request non active slots in the following order:
@@ -236,8 +242,8 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
entry.label = null;
}
if (album != null) {
- entry.labelLoader = new AlbumLabelLoader(
- slotIndex, album, entry.sourceType, mSlotWidth);
+ entry.labelLoader =
+ new AlbumLabelLoader(slotIndex, album, entry.sourceType);
}
entry.coverItem = cover;
@@ -271,22 +277,23 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
return loader.isRequestInProgress();
}
- private void notifySlotChanged(int slotIndex) {
- // If the updated content is not cached, ignore it
- if (slotIndex < mContentStart || slotIndex >= mContentEnd) {
- Log.w(TAG, String.format(
- "invalid update: %s is outside (%s, %s)",
- slotIndex, mContentStart, mContentEnd) );
- return;
+ private void queueTextureForUpload(boolean isActive, Texture texture) {
+ if ((texture == null) || !(texture instanceof BitmapTexture)) return;
+ if (isActive) {
+ mTextureUploader.addFgTexture((BitmapTexture) texture);
+ } else {
+ mTextureUploader.addBgTexture((BitmapTexture) texture);
}
+ }
- AlbumSetEntry entry = mData[slotIndex % mData.length];
- MediaSet set = mSource.getMediaSet(slotIndex);
- MediaItem coverItem = mSource.getCoverItem(slotIndex);
- updateAlbumSetEntry(entry, slotIndex, set, coverItem);
- updateAllImageRequests();
- if (mListener != null && isActiveSlot(slotIndex)) {
- mListener.onContentChanged();
+ private void updateTextureUploadQueue() {
+ if (!mIsActive) return;
+ mTextureUploader.clear();
+ for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
+ AlbumSetEntry entry = mData[i % mData.length];
+ boolean isActive = isActiveSlot(i);
+ queueTextureForUpload(isActive, entry.label);
+ queueTextureForUpload(isActive, entry.content);
}
}
@@ -318,13 +325,30 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
// paused, ignore slot changed event
return;
}
- notifySlotChanged(index);
+
+ // If the updated content is not cached, ignore it
+ if (index < mContentStart || index >= mContentEnd) {
+ Log.w(TAG, String.format(
+ "invalid update: %s is outside (%s, %s)",
+ index, mContentStart, mContentEnd) );
+ return;
+ }
+
+ AlbumSetEntry entry = mData[index % mData.length];
+ MediaSet set = mSource.getMediaSet(index);
+ MediaItem coverItem = mSource.getCoverItem(index);
+ updateAlbumSetEntry(entry, index, set, coverItem);
+ updateAllImageRequests();
+ updateTextureUploadQueue();
+ if (mListener != null && isActiveSlot(index)) {
+ mListener.onContentChanged();
+ }
}
public BitmapTexture getLoadingTexture() {
if (mLoadingLabel == null) {
Bitmap bitmap = mLabelMaker.requestLabel(mLoadingText, null,
- SelectionDrawer.DATASOURCE_TYPE_NOT_CATEGORIZED, mSlotWidth)
+ SelectionDrawer.DATASOURCE_TYPE_NOT_CATEGORIZED)
.run(ThreadPool.JOB_CONTEXT_STUB);
mLoadingLabel = new BitmapTexture(bitmap);
mLoadingLabel.setOpaque(false);
@@ -337,6 +361,7 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
freeSlotContent(i);
}
+ mLabelMaker.clearRecycledLabels();
}
public void resume() {
@@ -362,7 +387,7 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
@Override
protected void recycleBitmap(Bitmap bitmap) {
- BitmapPool.recycle(BitmapPool.TYPE_MICRO_THUMB, bitmap);
+ MediaItem.getMicroThumbPool().recycle(bitmap);
}
@Override
@@ -379,16 +404,19 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
@Override
public void updateEntry() {
Bitmap bitmap = getBitmap();
- if (bitmap == null) return;
+ if (bitmap == null) return; // error or recycled
AlbumSetEntry entry = mData[mSlotIndex % mData.length];
- BitmapTexture content = new BitmapTexture(bitmap);
- entry.content = content;
+ BitmapTexture texture = new BitmapTexture(bitmap);
+ entry.content = texture;
if (isActiveSlot(mSlotIndex)) {
+ mTextureUploader.addFgTexture(texture);
--mActiveRequestCount;
if (mActiveRequestCount == 0) requestNonactiveImages();
if (mListener != null) mListener.onContentChanged();
+ } else {
+ mTextureUploader.addBgTexture(texture);
}
}
}
@@ -439,24 +467,23 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
private final MediaSet mMediaSet;
private final int mSlotIndex;
private final int mSourceType;
- private final int mLabelWidth;
- public AlbumLabelLoader(int slotIndex,
- MediaSet mediaSet, int sourceType, int labelWidth) {
+ public AlbumLabelLoader(
+ int slotIndex, MediaSet mediaSet, int sourceType) {
mSlotIndex = slotIndex;
mMediaSet = mediaSet;
mSourceType = sourceType;
- mLabelWidth = labelWidth;
}
@Override
protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
return mThreadPool.submit(mLabelMaker.requestLabel(
- mMediaSet, mSourceType, mLabelWidth), l);
+ mMediaSet, mSourceType), l);
}
@Override
protected void recycleBitmap(Bitmap bitmap) {
+ mLabelMaker.reycleLabel(bitmap);
}
@Override
@@ -466,28 +493,33 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
@Override
public void updateEntry() {
- if (isRecycled()) return;
-
Bitmap bitmap = getBitmap();
- if (bitmap == null) return;
+ if (bitmap == null) return; // Error or recycled
AlbumSetEntry entry = mData[mSlotIndex % mData.length];
- BitmapTexture content = new BitmapTexture(bitmap);
- content.setOpaque(false);
- entry.label = content;
+ BitmapTexture texture = new BitmapTexture(bitmap);
+ texture.setOpaque(false);
+ entry.label = texture;
if (isActiveSlot(mSlotIndex)) {
+ mTextureUploader.addFgTexture(texture);
--mActiveRequestCount;
if (mActiveRequestCount == 0) requestNonactiveImages();
if (mListener != null) mListener.onContentChanged();
+ } else {
+ mTextureUploader.addBgTexture(texture);
}
}
}
public void onSlotSizeChanged(int width, int height) {
if (mSlotWidth == width) return;
+
mSlotWidth = width;
mLoadingLabel = null;
+ mLabelMaker.setLabelWidth(mSlotWidth);
+
+ if (!mIsActive) return;
for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
AlbumSetEntry entry = mData[i % mData.length];
@@ -498,8 +530,9 @@ public class AlbumSetSlidingWindow implements AlbumSetView.ModelListener {
}
entry.labelLoader = (entry.album == null)
? null
- : new AlbumLabelLoader(i, entry.album, entry.sourceType, width);
+ : new AlbumLabelLoader(i, entry.album, entry.sourceType);
}
updateAllImageRequests();
+ updateTextureUploadQueue();
}
}
diff --git a/src/com/android/gallery3d/ui/AlbumSetView.java b/src/com/android/gallery3d/ui/AlbumSetView.java
index 70ecbb49b..8a169b11a 100644
--- a/src/com/android/gallery3d/ui/AlbumSetView.java
+++ b/src/com/android/gallery3d/ui/AlbumSetView.java
@@ -98,6 +98,13 @@ public class AlbumSetView implements SlotView.SlotRenderer {
}
}
+ private static Texture checkTexture(GLCanvas canvas, Texture texture) {
+ return ((texture == null) || ((texture instanceof UploadedTexture)
+ && !((UploadedTexture) texture).isContentValid(canvas)))
+ ? null
+ : texture;
+ }
+
@Override
public int renderSlot(GLCanvas canvas, int index, int pass, int width, int height) {
AlbumSetEntry entry = mDataWindow.get(index);
@@ -107,8 +114,7 @@ public class AlbumSetView implements SlotView.SlotRenderer {
private int renderContent(GLCanvas canvas,
int pass, AlbumSetEntry entry, int width, int height) {
- // Fit the content into the box
- Texture content = entry.content;
+ Texture content = checkTexture(canvas, entry.content);
if (content == null) {
content = mWaitLoadingTexture;
@@ -120,6 +126,7 @@ public class AlbumSetView implements SlotView.SlotRenderer {
content = entry.content;
}
+ // Fit the content into the box
int w = content.getWidth();
int h = content.getHeight();
@@ -150,7 +157,7 @@ public class AlbumSetView implements SlotView.SlotRenderer {
// We show the loading message only when the album is still loading
// (Not when we are still preparing the label)
- Texture content = entry.label;
+ Texture content = checkTexture(canvas, entry.label);
if (entry.album == null) {
content = mDataWindow.getLoadingTexture();
}
diff --git a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
index c88ca7d97..af6dc460d 100644
--- a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
@@ -53,6 +53,11 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener {
}
private final AlbumView.Model mSource;
+ private final AlbumEntry mData[];
+ private final SynchronizedHandler mHandler;
+ private final JobLimiter mThreadPool;
+ private final TextureUploader mTextureUploader;
+
private int mSize;
private int mContentStart = 0;
@@ -63,11 +68,6 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener {
private Listener mListener;
- private final AlbumEntry mData[];
-
- private SynchronizedHandler mHandler;
- private JobLimiter mThreadPool;
-
private int mActiveRequestCount = 0;
private boolean mIsActive = false;
@@ -87,6 +87,7 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener {
};
mThreadPool = new JobLimiter(activity.getThreadPool(), JOB_LIMIT);
+ mTextureUploader = new TextureUploader(activity.getGLRoot());
}
public void setListener(Listener listener) {
@@ -156,9 +157,29 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener {
0, Math.max(0, mSize - data.length));
int contentEnd = Math.min(contentStart + data.length, mSize);
setContentWindow(contentStart, contentEnd);
+ updateUploadedTextures();
if (mIsActive) updateAllImageRequests();
}
+ private void uploadTexture(boolean isActive, Texture texture) {
+ if ((texture == null) || !(texture instanceof BitmapTexture)) return;
+ if (isActive) {
+ mTextureUploader.addFgTexture((BitmapTexture) texture);
+ } else {
+ mTextureUploader.addBgTexture((BitmapTexture) texture);
+ }
+ }
+
+ private void updateUploadedTextures() {
+ if (!mIsActive) return;
+ mTextureUploader.clear();
+ for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
+ AlbumEntry entry = mData[i % mData.length];
+ boolean isActive = isActiveSlot(i);
+ uploadTexture(isActive, entry.content);
+ }
+ }
+
// We would like to request non active slots in the following order:
// Order: 8 6 4 2 1 3 5 7
// |---------|---------------|---------|
@@ -244,8 +265,8 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener {
}
private class ThumbnailLoader extends BitmapLoader {
- final int mSlotIndex;
- final MediaItem mItem;
+ private final int mSlotIndex;
+ private final MediaItem mItem;
public ThumbnailLoader(int slotIndex, MediaItem item) {
mSlotIndex = slotIndex;
@@ -254,7 +275,7 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener {
@Override
protected void recycleBitmap(Bitmap bitmap) {
- BitmapPool.recycle(BitmapPool.TYPE_MICRO_THUMB, bitmap);
+ MediaItem.getMicroThumbPool().recycle(bitmap);
}
@Override
@@ -269,16 +290,19 @@ public class AlbumSlidingWindow implements AlbumView.ModelListener {
}
public void updateEntry() {
- if (isRecycled()) return;
+ Bitmap bitmap = getBitmap();
+ if (bitmap == null) return; // error or recycled
AlbumEntry entry = mData[mSlotIndex % mData.length];
- Bitmap bitmap = entry.contentLoader.getBitmap();
- if (bitmap != null) entry.content = new BitmapTexture(bitmap);
+ entry.content = new BitmapTexture(bitmap);
if (isActiveSlot(mSlotIndex)) {
+ mTextureUploader.addFgTexture((BitmapTexture) entry.content);
--mActiveRequestCount;
if (mActiveRequestCount == 0) requestNonactiveImages();
if (mListener != null) mListener.onContentChanged();
+ } else {
+ mTextureUploader.addBgTexture((BitmapTexture) entry.content);
}
}
}
diff --git a/src/com/android/gallery3d/ui/AlbumView.java b/src/com/android/gallery3d/ui/AlbumView.java
index 827d57dec..1ed3ded29 100644
--- a/src/com/android/gallery3d/ui/AlbumView.java
+++ b/src/com/android/gallery3d/ui/AlbumView.java
@@ -79,10 +79,17 @@ public class AlbumView implements SlotView.SlotRenderer {
mSlotView.invalidate();
}
+ private static Texture checkTexture(GLCanvas canvas, Texture texture) {
+ return ((texture == null) || ((texture instanceof UploadedTexture)
+ && !((UploadedTexture) texture).isContentValid(canvas)))
+ ? null
+ : texture;
+ }
+
@Override
public int renderSlot(GLCanvas canvas, int index, int pass, int width, int height) {
AlbumSlidingWindow.AlbumEntry entry = mDataWindow.get(index);
- Texture content = entry.content;
+ Texture content = checkTexture(canvas, entry.content);
if (content == null) {
content = mWaitLoadingTexture;
diff --git a/src/com/android/gallery3d/ui/BitmapPool.java b/src/com/android/gallery3d/ui/BitmapPool.java
index e910aece1..2cd3a4e51 100644
--- a/src/com/android/gallery3d/ui/BitmapPool.java
+++ b/src/com/android/gallery3d/ui/BitmapPool.java
@@ -7,7 +7,6 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import com.android.gallery3d.data.DecodeUtils;
-import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.util.ThreadPool.JobContext;
import java.io.FileDescriptor;
@@ -16,63 +15,47 @@ import java.util.ArrayList;
public class BitmapPool {
private static final String TAG = "BitmapPool";
- public static final int TYPE_MICRO_THUMB = 0;
- private static final int TYPE_COUNT = 1;
private static final int POOL_SIZE = 16;
- private static final int EXPECTED_WIDTH[] = {MediaItem.MICROTHUMBNAIL_TARGET_SIZE};
- private static final int EXPECTED_HEIGHT[] = {MediaItem.MICROTHUMBNAIL_TARGET_SIZE};
+ private final ArrayList<Bitmap> mPool = new ArrayList<Bitmap>(POOL_SIZE);
- @SuppressWarnings("unchecked")
- private static final ArrayList<Bitmap> sPools[] = new ArrayList[TYPE_COUNT];
- static {
- for (int i = 0; i < TYPE_COUNT; ++i) {
- sPools[i] = new ArrayList<Bitmap>();
- }
- }
+ private final int mWidth;
+ private final int mHeight;
- private BitmapPool() {
+ public BitmapPool(int width, int height) {
+ mWidth = width;
+ mHeight = height;
}
- public static Bitmap getBitmap(int type) {
- ArrayList<Bitmap> list = sPools[type];
- synchronized (list) {
- int size = list.size();
- return size > 0 ? list.remove(size - 1) : null;
- }
+ public synchronized Bitmap getBitmap() {
+ int size = mPool.size();
+ return size > 0 ? mPool.remove(size - 1) : null;
}
- public static void recycle(int type, Bitmap bitmap) {
+ public void recycle(Bitmap bitmap) {
if (bitmap == null) return;
- if ((bitmap.getWidth() != EXPECTED_WIDTH[type])
- || (bitmap.getHeight() != EXPECTED_HEIGHT[type])) {
+ if ((bitmap.getWidth() != mWidth) || (bitmap.getHeight() != mHeight)) {
bitmap.recycle();
return;
}
- ArrayList<Bitmap> list = sPools[type];
- synchronized (list) {
- if (list.size() < POOL_SIZE) list.add(bitmap);
+ synchronized (this) {
+ if (mPool.size() < POOL_SIZE) mPool.add(bitmap);
}
}
- public static void clear() {
- for (int i = 0; i < TYPE_COUNT; ++i) {
- ArrayList<Bitmap> list = sPools[i];
- synchronized (list) {
- list.clear();
- }
- }
+ public synchronized void clear() {
+ mPool.clear();
}
- public static Bitmap decode(JobContext jc, int type,
+ public Bitmap decode(JobContext jc,
byte[] data, int offset, int length, BitmapFactory.Options options) {
if (options == null) options = new BitmapFactory.Options();
if (options.inSampleSize < 1) options.inSampleSize = 1;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- options.inBitmap = (options.inSampleSize == 1) ? getBitmap(type) : null;
+ options.inBitmap = (options.inSampleSize == 1) ? getBitmap() : null;
try {
Bitmap bitmap = DecodeUtils.decode(jc, data, offset, length, options);
if (options.inBitmap != null && options.inBitmap != bitmap) {
- recycle(type, bitmap);
+ recycle(options.inBitmap);
options.inBitmap = null;
}
return bitmap;
@@ -80,7 +63,7 @@ public class BitmapPool {
if (options.inBitmap == null) throw e;
Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap");
- recycle(type, options.inBitmap);
+ recycle(options.inBitmap);
options.inBitmap = null;
return DecodeUtils.decode(jc, data, offset, length, options);
}
@@ -88,16 +71,16 @@ public class BitmapPool {
// This is the same as the method above except the source data comes
// from a file descriptor instead of a byte array.
- public static Bitmap decode(int type,
- JobContext jc, FileDescriptor fileDescriptor, Options options) {
+ public Bitmap decode(JobContext jc,
+ FileDescriptor fileDescriptor, Options options) {
if (options == null) options = new BitmapFactory.Options();
if (options.inSampleSize < 1) options.inSampleSize = 1;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- options.inBitmap = (options.inSampleSize == 1) ? getBitmap(type) : null;
+ options.inBitmap = (options.inSampleSize == 1) ? getBitmap() : null;
try {
Bitmap bitmap = DecodeUtils.decode(jc, fileDescriptor, options);
if (options.inBitmap != null&& options.inBitmap != bitmap) {
- recycle(type, bitmap);
+ recycle(options.inBitmap);
options.inBitmap = null;
}
return bitmap;
@@ -105,7 +88,7 @@ public class BitmapPool {
if (options.inBitmap == null) throw e;
Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap");
- recycle(type, options.inBitmap);
+ recycle(options.inBitmap);
options.inBitmap = null;
return DecodeUtils.decode(jc, fileDescriptor, options);
}
diff --git a/src/com/android/gallery3d/ui/GLRoot.java b/src/com/android/gallery3d/ui/GLRoot.java
index 834e85c91..fe040ba34 100644
--- a/src/com/android/gallery3d/ui/GLRoot.java
+++ b/src/com/android/gallery3d/ui/GLRoot.java
@@ -20,8 +20,11 @@ import com.android.gallery3d.anim.CanvasAnimation;
public interface GLRoot {
+ // Listener will be called when GL is idle AND before each frame.
+ // Mainly used for uploading textures.
public static interface OnGLIdleListener {
- public boolean onGLIdle(GLRoot root, GLCanvas canvas);
+ public boolean onGLIdle(
+ GLCanvas canvas, boolean renderRequested);
}
public void addOnGLIdleListener(OnGLIdleListener listener);
diff --git a/src/com/android/gallery3d/ui/GLRootView.java b/src/com/android/gallery3d/ui/GLRootView.java
index 51853fffa..bed2908c4 100644
--- a/src/com/android/gallery3d/ui/GLRootView.java
+++ b/src/com/android/gallery3d/ui/GLRootView.java
@@ -30,8 +30,8 @@ import com.android.gallery3d.common.Utils;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.Profile;
+import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
import javax.microedition.khronos.egl.EGLConfig;
@@ -82,8 +82,8 @@ public class GLRootView extends GLSurfaceView
private final ArrayList<CanvasAnimation> mAnimations =
new ArrayList<CanvasAnimation>();
- private final LinkedList<OnGLIdleListener> mIdleListeners =
- new LinkedList<OnGLIdleListener>();
+ private final ArrayDeque<OnGLIdleListener> mIdleListeners =
+ new ArrayDeque<OnGLIdleListener>();
private final IdleRunner mIdleRunner = new IdleRunner();
@@ -267,6 +267,7 @@ public class GLRootView extends GLSurfaceView
} finally {
mRenderLock.unlock();
}
+
if (DEBUG_PROFILE_SLOW_ONLY) {
long t = System.nanoTime();
long durationInMs = (t - mLastDrawFinishTime) / 1000000;
@@ -305,7 +306,6 @@ public class GLRootView extends GLSurfaceView
gl.glScissor(clip.left, clip.top, clip.width(), clip.height());
}
-
if (mContentView != null) {
mContentView.render(mCanvas);
}
@@ -323,9 +323,7 @@ public class GLRootView extends GLSurfaceView
}
synchronized (mIdleListeners) {
- if (!mRenderRequested && !mIdleListeners.isEmpty()) {
- mIdleRunner.enable();
- }
+ if (!mIdleListeners.isEmpty()) mIdleRunner.enable();
}
if (DEBUG_INVALIDATE) {
@@ -370,19 +368,18 @@ public class GLRootView extends GLSurfaceView
OnGLIdleListener listener;
synchronized (mIdleListeners) {
mActive = false;
- if (mRenderRequested) return;
if (mIdleListeners.isEmpty()) return;
listener = mIdleListeners.removeFirst();
}
mRenderLock.lock();
try {
- if (!listener.onGLIdle(GLRootView.this, mCanvas)) return;
+ if (!listener.onGLIdle(mCanvas, mRenderRequested)) return;
} finally {
mRenderLock.unlock();
}
synchronized (mIdleListeners) {
mIdleListeners.addLast(listener);
- enable();
+ if (!mRenderRequested) enable();
}
}
diff --git a/src/com/android/gallery3d/ui/SlotView.java b/src/com/android/gallery3d/ui/SlotView.java
index aef97a436..5e12c74b4 100644
--- a/src/com/android/gallery3d/ui/SlotView.java
+++ b/src/com/android/gallery3d/ui/SlotView.java
@@ -84,6 +84,9 @@ public class SlotView extends GLView {
public static final int OVERSCROLL_SYSTEM = 1;
public static final int OVERSCROLL_NONE = 2;
+ // to prevent allocating memory
+ private final Rect mTempRect = new Rect();
+
public SlotView(Context context, Spec spec) {
mGestureDetector =
new GestureDetector(context, new MyGestureListener());
@@ -105,7 +108,7 @@ public class SlotView extends GLView {
if (index < 0 || index >= slotCount) {
return;
}
- Rect rect = mLayout.getSlotRect(index);
+ Rect rect = mLayout.getSlotRect(index, mTempRect);
int position = WIDE
? (rect.left + rect.right - getWidth()) / 2
: (rect.top + rect.bottom - getHeight()) / 2;
@@ -113,7 +116,7 @@ public class SlotView extends GLView {
}
public void makeSlotVisible(int index) {
- Rect rect = mLayout.getSlotRect(index);
+ Rect rect = mLayout.getSlotRect(index, mTempRect);
int visibleBegin = WIDE ? mScrollX : mScrollY;
int visibleLength = WIDE ? getWidth() : getHeight();
int visibleEnd = visibleBegin + visibleLength;
@@ -198,7 +201,7 @@ public class SlotView extends GLView {
}
public Rect getSlotRect(int slotIndex) {
- return mLayout.getSlotRect(slotIndex);
+ return mLayout.getSlotRect(slotIndex, new Rect());
}
@Override
@@ -315,7 +318,7 @@ public class SlotView extends GLView {
private int renderItem(
GLCanvas canvas, int index, int pass, boolean paperActive) {
canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX);
- Rect rect = getSlotRect(index);
+ Rect rect = mLayout.getSlotRect(index, mTempRect);
if (paperActive) {
canvas.multiplyMatrix(mPaper.getTransform(rect, mScrollX), 0);
} else {
@@ -443,7 +446,7 @@ public class SlotView extends GLView {
return vPadding != mVerticalPadding || hPadding != mHorizontalPadding;
}
- public Rect getSlotRect(int index) {
+ public Rect getSlotRect(int index, Rect rect) {
int col, row;
if (WIDE) {
col = index / mUnitCount;
@@ -455,7 +458,8 @@ public class SlotView extends GLView {
int x = mHorizontalPadding + col * (mSlotWidth + mSlotGap);
int y = mVerticalPadding + row * (mSlotHeight + mSlotGap);
- return new Rect(x, y, x + mSlotWidth, y + mSlotHeight);
+ rect.set(x, y, x + mSlotWidth, y + mSlotHeight);
+ return rect;
}
public int getSlotWidth() {
diff --git a/src/com/android/gallery3d/ui/TextureUploader.java b/src/com/android/gallery3d/ui/TextureUploader.java
new file mode 100644
index 000000000..a372eab70
--- /dev/null
+++ b/src/com/android/gallery3d/ui/TextureUploader.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 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.ui;
+
+import com.android.gallery3d.ui.GLRoot.OnGLIdleListener;
+
+import java.util.ArrayDeque;
+
+public class TextureUploader implements OnGLIdleListener {
+ private static final int INIT_CAPACITY = 64;
+ private static final int QUOTA_PER_FRAME = 1;
+
+ private final ArrayDeque<UploadedTexture> mFgTextures =
+ new ArrayDeque<UploadedTexture>(INIT_CAPACITY);
+ private final ArrayDeque<UploadedTexture> mBgTextures =
+ new ArrayDeque<UploadedTexture>(INIT_CAPACITY);
+ private final GLRoot mGLRoot;
+ private transient boolean mIsQueued = false;
+
+ public TextureUploader(GLRoot root) {
+ mGLRoot = root;
+ }
+
+ public synchronized void clear() {
+ mFgTextures.clear();
+ mBgTextures.clear();
+ }
+
+ // caller should hold synchronized on "this"
+ private void queueSelfIfNeed() {
+ if (mIsQueued) return;
+ mIsQueued = true;
+ mGLRoot.addOnGLIdleListener(this);
+ }
+
+ public synchronized void addBgTexture(UploadedTexture t) {
+ mBgTextures.addLast(t);
+ queueSelfIfNeed();
+ }
+
+ public synchronized void addFgTexture(UploadedTexture t) {
+ mFgTextures.addLast(t);
+ queueSelfIfNeed();
+ }
+
+ private int upload(GLCanvas canvas, ArrayDeque<UploadedTexture> deque,
+ int uploadQuota, boolean isBackground) {
+ while (uploadQuota > 0) {
+ UploadedTexture t;
+ synchronized (this) {
+ if (deque.isEmpty()) break;
+ t = deque.removeFirst();
+ }
+ if (!t.isContentValid(canvas)) {
+ t.updateContent(canvas);
+
+ // It will took some more time for a texture to be drawn for
+ // the first time.
+ // Thus, when scrolling, if a new column appears on screen,
+ // it may cause a UI jank even these textures are uploaded.
+ if (isBackground) t.draw(canvas, 0, 0);
+ --uploadQuota;
+ }
+ }
+ return uploadQuota;
+ }
+
+ @Override
+ public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
+ int uploadQuota = QUOTA_PER_FRAME;
+ uploadQuota = upload(canvas, mFgTextures, uploadQuota, false);
+ if (uploadQuota < QUOTA_PER_FRAME) mGLRoot.requestRender();
+
+ // don't upload background texture if there is pending render request
+ if (!renderRequested) {
+ upload(canvas, mBgTextures, uploadQuota, true);
+ }
+ synchronized (this) {
+ mIsQueued = !mFgTextures.isEmpty() || !mBgTextures.isEmpty();
+ return mIsQueued;
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index d2ce1677a..610a34688 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -548,7 +548,8 @@ public class TileImageView extends GLView {
AtomicBoolean mActive = new AtomicBoolean(false);
@Override
- public boolean onGLIdle(GLRoot root, GLCanvas canvas) {
+ public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
+ if (renderRequested) return false;
int quota = UPLOAD_LIMIT;
Tile tile;
while (true) {