summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorOwen Lin <owenlin@google.com>2012-05-17 15:59:25 -0700
committerOwen Lin <owenlin@google.com>2012-05-22 11:55:06 -0700
commit08476ae7099dcb4c65e348474d6abe7e9bb2a150 (patch)
treed91dae941b2b1462d51bf3c84cb0c30646a56c75 /src
parent3bd73388114f851dcb67d1ce38966e4d22fd1d22 (diff)
downloadandroid_packages_apps_Snap-08476ae7099dcb4c65e348474d6abe7e9bb2a150.tar.gz
android_packages_apps_Snap-08476ae7099dcb4c65e348474d6abe7e9bb2a150.tar.bz2
android_packages_apps_Snap-08476ae7099dcb4c65e348474d6abe7e9bb2a150.zip
Reuse bitmap to prevent GC in TiledImageView.
bug:5948093 Change-Id: Iad89c5809bde0de5409752330f607aab153dceba
Diffstat (limited to 'src')
-rw-r--r--src/com/android/gallery3d/app/PhotoDataAdapter.java5
-rw-r--r--src/com/android/gallery3d/ui/BitmapTileProvider.java29
-rw-r--r--src/com/android/gallery3d/ui/TileImageView.java49
-rw-r--r--src/com/android/gallery3d/ui/TileImageViewAdapter.java88
-rw-r--r--src/com/android/gallery3d/ui/UploadedTexture.java7
5 files changed, 99 insertions, 79 deletions
diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java
index 29154a082..6a4961eb2 100644
--- a/src/com/android/gallery3d/app/PhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java
@@ -23,6 +23,7 @@ import android.os.Message;
import com.android.gallery3d.common.BitmapUtils;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.ContentListener;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.LocalMediaItem;
@@ -490,8 +491,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
}
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize) {
- return mTileProvider.getTile(level, x, y, tileSize, borderSize);
+ int borderSize, BitmapPool pool) {
+ return mTileProvider.getTile(level, x, y, tileSize, borderSize, pool);
}
public boolean isEmpty() {
diff --git a/src/com/android/gallery3d/ui/BitmapTileProvider.java b/src/com/android/gallery3d/ui/BitmapTileProvider.java
index 320118e89..d4c9b1d30 100644
--- a/src/com/android/gallery3d/ui/BitmapTileProvider.java
+++ b/src/com/android/gallery3d/ui/BitmapTileProvider.java
@@ -21,6 +21,7 @@ import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.data.BitmapPool;
import java.util.ArrayList;
@@ -49,44 +50,46 @@ public class BitmapTileProvider implements TileImageView.Model {
mConfig = Config.ARGB_8888;
}
+ @Override
public ScreenNail getScreenNail() {
return mScreenNail;
}
+ @Override
public int getImageHeight() {
return mImageHeight;
}
+ @Override
public int getImageWidth() {
return mImageWidth;
}
+ @Override
public int getLevelCount() {
return mMipmaps.length;
}
+ @Override
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize) {
+ int borderSize, BitmapPool pool) {
x >>= level;
y >>= level;
int size = tileSize + 2 * borderSize;
- Bitmap result = Bitmap.createBitmap(size, size, mConfig);
+
+ Bitmap result = pool == null ? null : pool.getBitmap();
+ if (result == null) {
+ result = Bitmap.createBitmap(size, size, mConfig);
+ } else {
+ result.eraseColor(0);
+ }
+
Bitmap mipmap = mMipmaps[level];
Canvas canvas = new Canvas(result);
int offsetX = -x + borderSize;
int offsetY = -y + borderSize;
canvas.drawBitmap(mipmap, offsetX, offsetY, null);
-
- // If the valid region (covered by mipmap or border) is smaller than the
- // result bitmap, subset it.
- int endX = offsetX + mipmap.getWidth() + borderSize;
- int endY = offsetY + mipmap.getHeight() + borderSize;
- if (endX < size || endY < size) {
- return Bitmap.createBitmap(result, 0, 0, Math.min(size, endX),
- Math.min(size, endY));
- } else {
- return result;
- }
+ return result;
}
public void recycle() {
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index fb0e333a1..2f8f22333 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -25,6 +25,7 @@ import android.util.LongSparseArray;
import com.android.gallery3d.app.GalleryContext;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.DecodeUtils;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.ThreadPool;
@@ -43,8 +44,12 @@ public class TileImageView extends GLView {
// texture to avoid seams between tiles.
private static final int TILE_SIZE = 254;
private static final int TILE_BORDER = 1;
+ private static final int BITMAP_SIZE = TILE_SIZE + TILE_BORDER * 2;
private static final int UPLOAD_LIMIT = 1;
+ private static final BitmapPool sTilePool =
+ new BitmapPool(BITMAP_SIZE, BITMAP_SIZE, 128);
+
/*
* This is the tile state in the CPU side.
* Life of a Tile:
@@ -96,9 +101,9 @@ public class TileImageView extends GLView {
private final LongSparseArray<Tile> mActiveTiles = new LongSparseArray<Tile>();
// The following three queue is guarded by TileImageView.this
- private TileQueue mRecycledQueue = new TileQueue();
- private TileQueue mUploadQueue = new TileQueue();
- private TileQueue mDecodeQueue = new TileQueue();
+ private final TileQueue mRecycledQueue = new TileQueue();
+ private final TileQueue mUploadQueue = new TileQueue();
+ private final TileQueue mDecodeQueue = new TileQueue();
// The width and height of the full-sized bitmap
protected int mImageWidth = SIZE_UNKNOWN;
@@ -116,7 +121,7 @@ public class TileImageView extends GLView {
private final TileUploader mTileUploader = new TileUploader();
private boolean mIsTextureFreed;
private Future<Void> mTileDecoder;
- private ThreadPool mThreadPool;
+ private final ThreadPool mThreadPool;
private boolean mBackgroundTileUploaded;
public static interface Model {
@@ -138,7 +143,7 @@ public class TileImageView extends GLView {
//
// The method would be called in another thread.
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize);
+ int borderSize, BitmapPool pool);
}
public TileImageView(GalleryContext context) {
@@ -373,6 +378,7 @@ public class TileImageView extends GLView {
}
}
setScreenNail(null);
+ sTilePool.clear();
}
public void prepareTextures() {
@@ -480,7 +486,10 @@ public class TileImageView extends GLView {
synchronized (this) {
if (tile.mTileState == STATE_RECYCLING) {
tile.mTileState = STATE_RECYCLED;
- tile.mDecodedTile = null;
+ if (tile.mDecodedTile != null) {
+ sTilePool.recycle(tile.mDecodedTile);
+ tile.mDecodedTile = null;
+ }
mRecycledQueue.push(tile);
return false;
}
@@ -505,7 +514,10 @@ public class TileImageView extends GLView {
return;
}
tile.mTileState = STATE_RECYCLED;
- tile.mDecodedTile = null;
+ if (tile.mDecodedTile != null) {
+ sTilePool.recycle(tile.mDecodedTile);
+ tile.mDecodedTile = null;
+ }
mRecycledQueue.push(tile);
}
@@ -626,12 +638,12 @@ public class TileImageView extends GLView {
}
private class Tile extends UploadedTexture {
- int mX;
- int mY;
- int mTileLevel;
- Tile mNext;
- Bitmap mDecodedTile;
- volatile int mTileState = STATE_ACTIVATED;
+ public int mX;
+ public int mY;
+ public int mTileLevel;
+ public Tile mNext;
+ public Bitmap mDecodedTile;
+ public volatile int mTileState = STATE_ACTIVATED;
public Tile(int x, int y, int level) {
mX = x;
@@ -641,7 +653,7 @@ public class TileImageView extends GLView {
@Override
protected void onFreeBitmap(Bitmap bitmap) {
- bitmap.recycle();
+ sTilePool.recycle(bitmap);
}
boolean decode() {
@@ -649,7 +661,7 @@ public class TileImageView extends GLView {
// by (1 << mTilelevel) from a region in the original image.
try {
mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(
- mTileLevel, mX, mY, TILE_SIZE, TILE_BORDER));
+ mTileLevel, mX, mY, TILE_SIZE, TILE_BORDER, sTilePool));
} catch (Throwable t) {
Log.w(TAG, "fail to decode tile", t);
}
@@ -659,6 +671,13 @@ public class TileImageView extends GLView {
@Override
protected Bitmap onGetBitmap() {
Utils.assertTrue(mTileState == STATE_DECODED);
+
+ // We need to override the width and height, so that we won't
+ // draw beyond the boundaries.
+ int rightEdge = ((mImageWidth - mX) >> mTileLevel) + TILE_BORDER;
+ int bottomEdge = ((mImageHeight - mY) >> mTileLevel) + TILE_BORDER;
+ setSize(Math.min(BITMAP_SIZE, rightEdge), Math.min(BITMAP_SIZE, bottomEdge));
+
Bitmap bitmap = mDecodedTile;
mDecodedTile = null;
mTileState = STATE_ACTIVATED;
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
index 5c9281202..0b4ac0375 100644
--- a/src/com/android/gallery3d/ui/TileImageViewAdapter.java
+++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
@@ -20,10 +20,10 @@ import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
import android.graphics.Rect;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
public class TileImageViewAdapter implements TileImageView.Model {
private static final String TAG = "TileImageViewAdapter";
@@ -94,70 +94,68 @@ public class TileImageViewAdapter implements TileImageView.Model {
(float) mImageWidth / mScreenNail.getWidth()));
}
+ // Gets a sub image on a rectangle of the current photo. For example,
+ // getTile(1, 50, 50, 100, 3, pool) means to get the region located
+ // at (50, 50) with sample level 1 (ie, down sampled by 2^1) and the
+ // target tile size (after sampling) 100 with border 3.
+ //
+ // From this spec, we can infer the actual tile size to be
+ // 100 + 3x2 = 106, and the size of the region to be extracted from the
+ // photo to be 200 with border 6.
+ //
+ // As a result, we should decode region (50-6, 50-6, 250+6, 250+6) or
+ // (44, 44, 256, 256) from the original photo and down sample it to 106.
@Override
public Bitmap getTile(int level, int x, int y, int tileSize,
- int borderSize) {
- // wantRegion is the rectangle on the original image we want. askRegion
- // is the rectangle on the original image that we will ask from
- // mRegionDecoder. Both are in the coordinates of the original image,
- // not the coordinates of the scaled-down images.
- Rect wantRegion = new Rect();
- Rect askRegion = new Rect();
-
+ int borderSize, BitmapPool pool) {
int b = borderSize << level;
- wantRegion.set(x - b, y - b, x + (tileSize << level) + b,
- y + (tileSize << level) + b);
+ int t = tileSize << level;
+
+ Rect wantRegion = new Rect(x - b, y - b, x + t + b, y + t + b);
+ boolean needClear;
BitmapRegionDecoder regionDecoder = null;
+
synchronized (this) {
regionDecoder = mRegionDecoder;
if (regionDecoder == null) return null;
- // askRegion is the intersection of wantRegion and the original image.
- askRegion.set(0, 0, mImageWidth, mImageHeight);
+
+ // We need to clear a reused bitmap, if wantRegion is not fully
+ // within the image.
+ needClear = !new Rect(0, 0, mImageWidth, mImageHeight)
+ .contains(wantRegion);
}
- Utils.assertTrue(askRegion.intersect(wantRegion));
+ Bitmap bitmap = pool == null ? null : pool.getBitmap();
+ if (bitmap != null) {
+ if (needClear) bitmap.eraseColor(0);
+ } else {
+ int s = tileSize + 2 * borderSize;
+ bitmap = Bitmap.createBitmap(s, s, Config.ARGB_8888);
+ }
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.ARGB_8888;
options.inPreferQualityOverSpeed = true;
options.inSampleSize = (1 << level);
-
- Bitmap bitmap;
-
- // In CropImage, we may call the decodeRegion() concurrently.
- synchronized (regionDecoder) {
- bitmap = regionDecoder.decodeRegion(askRegion, options);
+ options.inBitmap = bitmap;
+
+ try {
+ // In CropImage, we may call the decodeRegion() concurrently.
+ synchronized (regionDecoder) {
+ bitmap = regionDecoder.decodeRegion(wantRegion, options);
+ }
+ } finally {
+ if (options.inBitmap != bitmap && options.inBitmap != null) {
+ if (pool != null) pool.recycle(options.inBitmap);
+ options.inBitmap = null;
+ }
}
if (bitmap == null) {
Log.w(TAG, "fail in decoding region");
- return null;
- }
-
- if (wantRegion.equals(askRegion)) return bitmap;
-
- // Now the wantRegion does not match the askRegion. This means we are at
- // a boundary tile, and we need to add paddings. Create a new Bitmap
- // and copy over.
- int size = tileSize + 2 * borderSize;
- Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- int offsetX = (askRegion.left - wantRegion.left) >> level;
- int offsetY = (askRegion.top - wantRegion.top) >> level;
- canvas.drawBitmap(bitmap, offsetX, offsetY, null);
-
- // If the valid region (covered by bitmap or border) is smaller than the
- // result bitmap, subset it.
- int endX = offsetX + bitmap.getWidth() + borderSize;
- int endY = offsetY + bitmap.getHeight() + borderSize;
- bitmap.recycle();
- if (endX < size || endY < size) {
- return Bitmap.createBitmap(result, 0, 0, Math.min(size, endX),
- Math.min(size, endY));
- } else {
- return result;
}
+ return bitmap;
}
@Override
diff --git a/src/com/android/gallery3d/ui/UploadedTexture.java b/src/com/android/gallery3d/ui/UploadedTexture.java
index 85aa1c4b2..24c8d1db6 100644
--- a/src/com/android/gallery3d/ui/UploadedTexture.java
+++ b/src/com/android/gallery3d/ui/UploadedTexture.java
@@ -128,10 +128,6 @@ abstract class UploadedTexture extends BasicTexture {
int h = mBitmap.getHeight() + mBorder * 2;
if (mWidth == UNSPECIFIED) {
setSize(w, h);
- } else if (mWidth != w || mHeight != h) {
- throw new IllegalStateException(String.format(
- "cannot change size: this = %s, orig = %sx%s, new = %sx%s",
- toString(), mWidth, mHeight, w, h));
}
}
return mBitmap;
@@ -218,6 +214,9 @@ abstract class UploadedTexture extends BasicTexture {
int height = bHeight + mBorder * 2;
int texWidth = getTextureWidth();
int texHeight = getTextureHeight();
+
+ Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
+
// Define a vertically flipped crop rectangle for
// OES_draw_texture.
// The four values in sCropRect are: left, bottom, width, and