--- /dev/null
+/*
+ * 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 android.util;
+
+/**
+ * Helper class for crating pools of objects. An example use looks like this:
+ * <pre>
+ * public class MyPooledClass {
+ *
+ * private static final SynchronizedPool<MyPooledClass> sPool =
+ * new SynchronizedPool<MyPooledClass>(10);
+ *
+ * public static MyPooledClass obtain() {
+ * MyPooledClass instance = sPool.acquire();
+ * return (instance != null) ? instance : new MyPooledClass();
+ * }
+ *
+ * public void recycle() {
+ * // Clear state if needed.
+ * sPool.release(this);
+ * }
+ *
+ * . . .
+ * }
+ * </pre>
+ *
+ * @hide
+ */
+public final class Pools {
+
+ /**
+ * Interface for managing a pool of objects.
+ *
+ * @param <T> The pooled type.
+ */
+ public static interface Pool<T> {
+
+ /**
+ * @return An instance from the pool if such, null otherwise.
+ */
+ public T acquire();
+
+ /**
+ * Release an instance to the pool.
+ *
+ * @param instance The instance to release.
+ * @return Whether the instance was put in the pool.
+ *
+ * @throws IllegalStateException If the instance is already in the pool.
+ */
+ public boolean release(T instance);
+ }
+
+ private Pools() {
+ /* do nothing - hiding constructor */
+ }
+
+ /**
+ * Simple (non-synchronized) pool of objects.
+ *
+ * @param <T> The pooled type.
+ */
+ public static class SimplePool<T> implements Pool<T> {
+ private final Object[] mPool;
+
+ private int mPoolSize;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param maxPoolSize The max pool size.
+ *
+ * @throws IllegalArgumentException If the max pool size is less than zero.
+ */
+ public SimplePool(int maxPoolSize) {
+ if (maxPoolSize <= 0) {
+ throw new IllegalArgumentException("The max pool size must be > 0");
+ }
+ mPool = new Object[maxPoolSize];
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T acquire() {
+ if (mPoolSize > 0) {
+ final int lastPooledIndex = mPoolSize - 1;
+ T instance = (T) mPool[lastPooledIndex];
+ mPool[lastPooledIndex] = null;
+ mPoolSize--;
+ return instance;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean release(T instance) {
+ if (isInPool(instance)) {
+ throw new IllegalStateException("Already in the pool!");
+ }
+ if (mPoolSize < mPool.length) {
+ mPool[mPoolSize] = instance;
+ mPoolSize++;
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isInPool(T instance) {
+ for (int i = 0; i < mPoolSize; i++) {
+ if (mPool[i] == instance) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Synchronized) pool of objects.
+ *
+ * @param <T> The pooled type.
+ */
+ public static class SynchronizedPool<T> extends SimplePool<T> {
+ private final Object mLock = new Object();
+
+ /**
+ * Creates a new instance.
+ *
+ * @param maxPoolSize The max pool size.
+ *
+ * @throws IllegalArgumentException If the max pool size is less than zero.
+ */
+ public SynchronizedPool(int maxPoolSize) {
+ super(maxPoolSize);
+ }
+
+ @Override
+ public T acquire() {
+ synchronized (mLock) {
+ return super.acquire();
+ }
+ }
+
+ @Override
+ public boolean release(T element) {
+ synchronized (mLock) {
+ return super.release(element);
+ }
+ }
+ }
+}
\ No newline at end of file
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
-import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
+import com.android.photos.data.GalleryBitmapPool;
import com.android.gallery3d.ui.GLRoot;
import com.android.gallery3d.ui.GLRootView;
import com.android.gallery3d.util.LightCycleHelper.PanoramaViewHelper;
} finally {
mGLRootView.unlockRenderThread();
}
- clearBitmapPool(MediaItem.getMicroThumbPool());
- clearBitmapPool(MediaItem.getThumbPool());
-
+ GalleryBitmapPool.getInstance().clear();
MediaItem.getBytesBufferPool().clear();
}
- private static void clearBitmapPool(BitmapPool pool) {
- if (pool != null) pool.clear();
- }
-
@Override
protected void onDestroy() {
super.onDestroy();
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.LocalMediaItem;
import com.android.gallery3d.data.MediaItem;
}
@Override
- public Bitmap getTile(int level, int x, int y, int tileSize, BitmapPool pool) {
- return mTileProvider.getTile(level, x, y, tileSize, pool);
+ public Bitmap getTile(int level, int x, int y, int tileSize) {
+ return mTileProvider.getTile(level, x, y, tileSize);
}
@Override
+++ /dev/null
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.data;
-
-import android.graphics.Bitmap;
-
-import com.android.gallery3d.common.Utils;
-
-import java.util.ArrayList;
-
-public class BitmapPool {
- @SuppressWarnings("unused")
- private static final String TAG = "BitmapPool";
-
- private final ArrayList<Bitmap> mPool;
- private final int mPoolLimit;
-
- // mOneSize is true if the pool can only cache Bitmap with one size.
- private final boolean mOneSize;
- private final int mWidth, mHeight; // only used if mOneSize is true
-
- // Construct a BitmapPool which caches bitmap with the specified size.
- public BitmapPool(int width, int height, int poolLimit) {
- mWidth = width;
- mHeight = height;
- mPoolLimit = poolLimit;
- mPool = new ArrayList<Bitmap>(poolLimit);
- mOneSize = true;
- }
-
- // Construct a BitmapPool which caches bitmap with any size;
- public BitmapPool(int poolLimit) {
- mWidth = -1;
- mHeight = -1;
- mPoolLimit = poolLimit;
- mPool = new ArrayList<Bitmap>(poolLimit);
- mOneSize = false;
- }
-
- // Get a Bitmap from the pool.
- public synchronized Bitmap getBitmap() {
- Utils.assertTrue(mOneSize);
- int size = mPool.size();
- return size > 0 ? mPool.remove(size - 1) : null;
- }
-
- // Get a Bitmap from the pool with the specified size.
- public synchronized Bitmap getBitmap(int width, int height) {
- Utils.assertTrue(!mOneSize);
- for (int i = mPool.size() - 1; i >= 0; i--) {
- Bitmap b = mPool.get(i);
- if (b.getWidth() == width && b.getHeight() == height) {
- return mPool.remove(i);
- }
- }
- return null;
- }
-
- // Put a Bitmap into the pool, if the Bitmap has a proper size. Otherwise
- // the Bitmap will be recycled. If the pool is full, an old Bitmap will be
- // recycled.
- public void recycle(Bitmap bitmap) {
- if (bitmap == null) return;
- if (mOneSize && ((bitmap.getWidth() != mWidth) ||
- (bitmap.getHeight() != mHeight))) {
- bitmap.recycle();
- return;
- }
- synchronized (this) {
- if (mPool.size() >= mPoolLimit) mPool.remove(0);
- mPool.add(bitmap);
- }
- }
-
- public synchronized void clear() {
- mPool.clear();
- }
-
- public boolean isOneSize() {
- return mOneSize;
- }
-}
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.BitmapUtils;
import com.android.gallery3d.common.Utils;
+import com.android.photos.data.GalleryBitmapPool;
import com.android.gallery3d.ui.Log;
import com.android.gallery3d.util.ThreadPool.CancelListener;
import com.android.gallery3d.util.ThreadPool.JobContext;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public static Bitmap decode(JobContext jc, byte[] data, int offset,
- int length, BitmapFactory.Options options, BitmapPool pool) {
- if (pool == null) {
- return decode(jc, data, offset, length, options);
- }
-
+ public static Bitmap decodeUsingPool(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)
- ? findCachedBitmap(pool, jc, data, offset, length, options) : null;
+ ? findCachedBitmap(jc, data, offset, length, options) : null;
try {
Bitmap bitmap = decode(jc, data, offset, length, options);
if (options.inBitmap != null && options.inBitmap != bitmap) {
- pool.recycle(options.inBitmap);
+ GalleryBitmapPool.getInstance().put(options.inBitmap);
options.inBitmap = null;
}
return bitmap;
if (options.inBitmap == null) throw e;
Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap");
- pool.recycle(options.inBitmap);
+ GalleryBitmapPool.getInstance().put(options.inBitmap);
options.inBitmap = null;
return decode(jc, data, offset, length, options);
}
// This is the same as the method above except the source data comes
// from a file descriptor instead of a byte array.
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public static Bitmap decode(JobContext jc,
- FileDescriptor fileDescriptor, Options options, BitmapPool pool) {
- if (pool == null) {
- return decode(jc, fileDescriptor, options);
- }
-
+ public static Bitmap decodeUsingPool(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)
- ? findCachedBitmap(pool, jc, fileDescriptor, options) : null;
+ ? findCachedBitmap(jc, fileDescriptor, options) : null;
try {
Bitmap bitmap = DecodeUtils.decode(jc, fileDescriptor, options);
if (options.inBitmap != null && options.inBitmap != bitmap) {
- pool.recycle(options.inBitmap);
+ GalleryBitmapPool.getInstance().put(options.inBitmap);
options.inBitmap = null;
}
return bitmap;
if (options.inBitmap == null) throw e;
Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap");
- pool.recycle(options.inBitmap);
+ GalleryBitmapPool.getInstance().put(options.inBitmap);
options.inBitmap = null;
return decode(jc, fileDescriptor, options);
}
}
- private static Bitmap findCachedBitmap(BitmapPool pool, JobContext jc,
- byte[] data, int offset, int length, Options options) {
- if (pool.isOneSize()) return pool.getBitmap();
+ private static Bitmap findCachedBitmap(JobContext jc, byte[] data,
+ int offset, int length, Options options) {
decodeBounds(jc, data, offset, length, options);
- return pool.getBitmap(options.outWidth, options.outHeight);
+ return GalleryBitmapPool.getInstance().get(options.outWidth, options.outHeight);
}
- private static Bitmap findCachedBitmap(BitmapPool pool, JobContext jc,
- FileDescriptor fileDescriptor, Options options) {
- if (pool.isOneSize()) return pool.getBitmap();
+ private static Bitmap findCachedBitmap(JobContext jc, FileDescriptor fileDescriptor,
+ Options options) {
decodeBounds(jc, fileDescriptor, options);
- return pool.getBitmap(options.outWidth, options.outHeight);
+ return GalleryBitmapPool.getInstance().get(options.outWidth, options.outHeight);
}
}
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap;
if (mType == MediaItem.TYPE_MICROTHUMBNAIL) {
- bitmap = DecodeUtils.decode(jc,
- buffer.data, buffer.offset, buffer.length, options,
- MediaItem.getMicroThumbPool());
+ bitmap = DecodeUtils.decodeUsingPool(jc,
+ buffer.data, buffer.offset, buffer.length, options);
} else {
- bitmap = DecodeUtils.decode(jc,
- buffer.data, buffer.offset, buffer.length, options,
- MediaItem.getThumbPool());
+ bitmap = DecodeUtils.decodeUsingPool(jc,
+ buffer.data, buffer.offset, buffer.length, options);
}
if (bitmap == null && !jc.isCancelled()) {
Log.w(TAG, "decode cached failed " + debugTag());
private static final int BYTESBUFFER_SIZE = 200 * 1024;
private static int sMicrothumbnailTargetSize = 200;
- private static BitmapPool sMicroThumbPool;
private static final BytesBufferPool sMicroThumbBufferPool =
new BytesBufferPool(BYTESBUFFE_POOL_SIZE, BYTESBUFFER_SIZE);
private static int sThumbnailTargetSize = 640;
- private static final BitmapPool sThumbPool =
- ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_FACTORY
- ? new BitmapPool(4)
- : null;
// TODO: fix default value for latlng and change this.
public static final double INVALID_LATLNG = 0f;
}
}
- public static BitmapPool getMicroThumbPool() {
- if (ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_FACTORY && sMicroThumbPool == null) {
- initializeMicroThumbPool();
- }
- return sMicroThumbPool;
- }
-
- public static BitmapPool getThumbPool() {
- return sThumbPool;
- }
-
public static BytesBufferPool getBytesBufferPool() {
return sMicroThumbBufferPool;
}
- private static void initializeMicroThumbPool() {
- sMicroThumbPool =
- ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_FACTORY
- ? new BitmapPool(sMicrothumbnailTargetSize, sMicrothumbnailTargetSize, 16)
- : null;
- }
-
public static void setThumbnailSizes(int size, int microSize) {
sThumbnailTargetSize = size;
if (sMicrothumbnailTargetSize != microSize) {
sMicrothumbnailTargetSize = microSize;
- initializeMicroThumbPool();
}
}
}
public void deviceRemoved(MtpDevice device) {
if (device == mDevice) {
setDevice(null);
- MtpBitmapFetch.onDeviceDisconnected(device);
}
}
import android.view.WindowManager;
import com.android.camera.Exif;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
-
-import java.util.ArrayList;
+import com.android.photos.data.GalleryBitmapPool;
public class MtpBitmapFetch {
- private static final int BITMAP_POOL_SIZE = 32;
- private static BitmapPool sThumbnailPool = new BitmapPool(BITMAP_POOL_SIZE);
private static int sMaxSize = 0;
public static void recycleThumbnail(Bitmap b) {
if (b != null) {
- sThumbnailPool.recycle(b);
+ GalleryBitmapPool.getInstance().put(b);
}
}
o.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o);
if (o.outWidth == 0 || o.outHeight == 0) return null;
- o.inBitmap = sThumbnailPool.getBitmap(o.outWidth, o.outHeight);
+ o.inBitmap = GalleryBitmapPool.getInstance().get(o.outWidth, o.outHeight);
o.inMutable = true;
o.inJustDecodeBounds = false;
o.inSampleSize = 1;
return new BitmapWithMetadata(created, Exif.getOrientation(imageBytes));
}
- public static void onDeviceDisconnected(MtpDevice device) {
- sThumbnailPool.clear();
- }
-
public static void configureForContext(Context context) {
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
import android.text.TextUtils;
import com.android.gallery3d.R;
-import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.DataSourceType;
+import com.android.photos.data.GalleryBitmapPool;
import com.android.gallery3d.util.ThreadPool;
import com.android.gallery3d.util.ThreadPool.JobContext;
private final Context mContext;
private int mLabelWidth;
- private BitmapPool mBitmapPool;
+ private int mBitmapWidth;
+ private int mBitmapHeight;
private final LazyLoadedBitmap mLocalSetIcon;
private final LazyLoadedBitmap mPicasaIcon;
if (mLabelWidth == width) return;
mLabelWidth = width;
int borders = 2 * BORDER_SIZE;
- mBitmapPool = new BitmapPool(
- width + borders, mSpec.labelBackgroundHeight + borders, 16);
+ mBitmapWidth = width + borders;
+ mBitmapHeight = mSpec.labelBackgroundHeight + borders;
}
public ThreadPool.Job<Bitmap> requestLabel(
synchronized (this) {
labelWidth = mLabelWidth;
- bitmap = mBitmapPool.getBitmap();
+ bitmap = GalleryBitmapPool.getInstance().get(mBitmapWidth, mBitmapHeight);
}
if (bitmap == null) {
}
public void recycleLabel(Bitmap label) {
- mBitmapPool.recycle(label);
- }
-
- public void clearRecycledLabels() {
- if (mBitmapPool != null) mBitmapPool.clear();
+ GalleryBitmapPool.getInstance().put(label);
}
}
import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.app.AlbumSetDataLoader;
import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.DataSourceType;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.data.MediaObject;
for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
freeSlotContent(i);
}
- mLabelMaker.clearRecycledLabels();
}
public void resume() {
}
@Override
- protected void recycleBitmap(Bitmap bitmap) {
- BitmapPool pool = MediaItem.getMicroThumbPool();
- if (pool != null) pool.recycle(bitmap);
- }
-
- @Override
protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
return mThreadPool.submit(mMediaItem.requestImage(
MediaItem.TYPE_MICROTHUMBNAIL), l);
}
@Override
- protected void recycleBitmap(Bitmap bitmap) {
- mLabelMaker.recycleLabel(bitmap);
- }
-
- @Override
protected void onLoadComplete(Bitmap bitmap) {
mHandler.obtainMessage(MSG_UPDATE_ALBUM_ENTRY, this).sendToTarget();
}
import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.app.AlbumDataLoader;
import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.data.MediaObject;
-import com.android.gallery3d.data.Path;
import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
+import com.android.gallery3d.data.Path;
import com.android.gallery3d.glrenderer.Texture;
import com.android.gallery3d.glrenderer.TiledTexture;
import com.android.gallery3d.util.Future;
}
@Override
- protected void recycleBitmap(Bitmap bitmap) {
- BitmapPool pool = MediaItem.getMicroThumbPool();
- if (pool != null) pool.recycle(bitmap);
- }
-
- @Override
protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
return mThreadPool.submit(
mItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL), this);
import android.graphics.Bitmap;
+import com.android.photos.data.GalleryBitmapPool;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.FutureListener;
mBitmap = future.get();
if (mState == STATE_RECYCLED) {
if (mBitmap != null) {
- recycleBitmap(mBitmap);
+ GalleryBitmapPool.getInstance().put(mBitmap);
mBitmap = null;
}
return; // don't call callback
public synchronized void recycle() {
mState = STATE_RECYCLED;
if (mBitmap != null) {
- recycleBitmap(mBitmap);
+ GalleryBitmapPool.getInstance().put(mBitmap);
mBitmap = null;
}
if (mTask != null) mTask.cancel();
}
abstract protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l);
- abstract protected void recycleBitmap(Bitmap bitmap);
abstract protected void onLoadComplete(Bitmap bitmap);
}
import android.graphics.Canvas;
import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.data.BitmapPool;
+import com.android.photos.data.GalleryBitmapPool;
import java.util.ArrayList;
}
@Override
- public Bitmap getTile(int level, int x, int y, int tileSize,
- BitmapPool pool) {
+ public Bitmap getTile(int level, int x, int y, int tileSize) {
x >>= level;
y >>= level;
- Bitmap result = pool == null ? null : pool.getBitmap();
+ Bitmap result = GalleryBitmapPool.getInstance().get(tileSize, tileSize);
if (result == null) {
result = Bitmap.createBitmap(tileSize, tileSize, mConfig);
} else {
import android.view.WindowManager;
import com.android.gallery3d.app.GalleryContext;
-import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
import com.android.gallery3d.data.DecodeUtils;
+import com.android.photos.data.GalleryBitmapPool;
import com.android.gallery3d.glrenderer.GLCanvas;
import com.android.gallery3d.glrenderer.UploadedTexture;
import com.android.gallery3d.util.Future;
// TILE_SIZE must be 2^N
private static int sTileSize;
- private static BitmapPool sTilePool;
-
/*
* This is the tile state in the CPU side.
* Life of a Tile:
// still refers to the coordinate on the original image.
//
// The method would be called in another thread.
- public Bitmap getTile(int level, int x, int y, int tileSize,
- BitmapPool pool);
+ public Bitmap getTile(int level, int x, int y, int tileSize);
}
public static boolean isHighResolution(Context context) {
} else {
sTileSize = 256;
}
- sTilePool =
- ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER
- ? new BitmapPool(sTileSize, sTileSize, 128)
- : null;
}
}
}
}
setScreenNail(null);
- if (sTilePool != null) sTilePool.clear();
}
public void prepareTextures() {
if (tile.mTileState == STATE_RECYCLING) {
tile.mTileState = STATE_RECYCLED;
if (tile.mDecodedTile != null) {
- if (sTilePool != null) sTilePool.recycle(tile.mDecodedTile);
+ GalleryBitmapPool.getInstance().put(tile.mDecodedTile);
tile.mDecodedTile = null;
}
mRecycledQueue.push(tile);
}
tile.mTileState = STATE_RECYCLED;
if (tile.mDecodedTile != null) {
- if (sTilePool != null) sTilePool.recycle(tile.mDecodedTile);
+ GalleryBitmapPool.getInstance().put(tile.mDecodedTile);
tile.mDecodedTile = null;
}
mRecycledQueue.push(tile);
@Override
protected void onFreeBitmap(Bitmap bitmap) {
- if (sTilePool != null) sTilePool.recycle(bitmap);
+ GalleryBitmapPool.getInstance().put(bitmap);
}
boolean decode() {
// by (1 << mTilelevel) from a region in the original image.
try {
mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(
- mTileLevel, mX, mY, sTileSize, sTilePool));
+ mTileLevel, mX, mY, sTileSize));
} catch (Throwable t) {
Log.w(TAG, "fail to decode tile", t);
}
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
+import com.android.photos.data.GalleryBitmapPool;
public class TileImageViewAdapter implements TileImageView.TileSource {
private static final String TAG = "TileImageViewAdapter";
// (44, 44, 256, 256) from the original photo and down sample it to 106.
@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
@Override
- public Bitmap getTile(int level, int x, int y, int tileSize, BitmapPool pool) {
+ public Bitmap getTile(int level, int x, int y, int tileSize) {
if (!ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER) {
return getTileWithoutReusingBitmap(level, x, y, tileSize);
}
.contains(wantRegion);
}
- Bitmap bitmap = pool == null ? null : pool.getBitmap();
+ Bitmap bitmap = GalleryBitmapPool.getInstance().get(tileSize, tileSize);
if (bitmap != null) {
if (needClear) bitmap.eraseColor(0);
} else {
}
} finally {
if (options.inBitmap != bitmap && options.inBitmap != null) {
- if (pool != null) pool.recycle(options.inBitmap);
+ GalleryBitmapPool.getInstance().put(options.inBitmap);
options.inBitmap = null;
}
}
import android.graphics.RectF;
import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
-import com.android.gallery3d.data.MediaItem;
+import com.android.photos.data.GalleryBitmapPool;
import com.android.gallery3d.glrenderer.GLCanvas;
import com.android.gallery3d.glrenderer.TiledTexture;
mHeight = Math.round(scale * height);
}
- private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
- if (pool == null || bitmap == null) return;
- pool.recycle(bitmap);
- }
-
// Combines the two ScreenNails.
// Returns the used one and recycle the unused one.
public ScreenNail combine(ScreenNail other) {
mWidth = newer.mWidth;
mHeight = newer.mHeight;
if (newer.mTexture != null) {
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+ if (mBitmap != null) GalleryBitmapPool.getInstance().put(mBitmap);
if (mTexture != null) mTexture.recycle();
mBitmap = newer.mBitmap;
mTexture = newer.mTexture;
mTexture.recycle();
mTexture = null;
}
- recycleBitmap(MediaItem.getThumbPool(), mBitmap);
- mBitmap = null;
+ if (mBitmap != null) {
+ GalleryBitmapPool.getInstance().put(mBitmap);
+ mBitmap = null;
+ }
}
public static void disableDrawPlaceholder() {
--- /dev/null
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.util.Pools.Pool;
+import android.util.Pools.SimplePool;
+
+import com.android.photos.data.SparseArrayBitmapPool.Node;
+
+public class GalleryBitmapPool {
+
+ private static final int CAPACITY_BYTES = 20971520;
+ private static final int POOL_INDEX_NONE = -1;
+ private static final int POOL_INDEX_SQUARE = 0;
+ private static final int POOL_INDEX_PHOTO = 1;
+ private static final int POOL_INDEX_MISC = 2;
+
+ private static final Point[] COMMON_PHOTO_ASPECT_RATIOS =
+ { new Point(4, 3), new Point(3, 2), new Point(16, 9) };
+
+ private int mCapacityBytes;
+ private SparseArrayBitmapPool [] mPools;
+ private Pool<Node> mSharedNodePool = new SimplePool<Node>(128);
+
+ private GalleryBitmapPool(int capacityBytes) {
+ mPools = new SparseArrayBitmapPool[3];
+ mPools[POOL_INDEX_SQUARE] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
+ mPools[POOL_INDEX_PHOTO] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
+ mPools[POOL_INDEX_MISC] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
+ mCapacityBytes = capacityBytes;
+ }
+
+ private static GalleryBitmapPool sInstance;
+ public static GalleryBitmapPool getInstance() {
+ if (sInstance == null) {
+ sInstance = new GalleryBitmapPool(CAPACITY_BYTES);
+ }
+ return sInstance;
+ }
+
+ private SparseArrayBitmapPool getPoolForDimensions(int width, int height) {
+ int index = getPoolIndexForDimensions(width, height);
+ if (index == POOL_INDEX_NONE) {
+ return null;
+ } else {
+ return mPools[index];
+ }
+ }
+
+ private int getPoolIndexForDimensions(int width, int height) {
+ if (width <= 0 || height <= 0) {
+ return POOL_INDEX_NONE;
+ }
+ if (width == height) {
+ return POOL_INDEX_SQUARE;
+ }
+ int min, max;
+ if (width > height) {
+ min = height;
+ max = width;
+ } else {
+ min = width;
+ max = height;
+ }
+ for (Point ar : COMMON_PHOTO_ASPECT_RATIOS) {
+ if (min * ar.x == max * ar.y) {
+ return POOL_INDEX_PHOTO;
+ }
+ }
+ return POOL_INDEX_MISC;
+ }
+
+ public synchronized int getCapacity() {
+ return mCapacityBytes;
+ }
+
+ public synchronized int getSize() {
+ int total = 0;
+ for (SparseArrayBitmapPool p : mPools) {
+ total += p.getSize();
+ }
+ return total;
+ }
+
+ public Bitmap get(int width, int height) {
+ SparseArrayBitmapPool pool = getPoolForDimensions(width, height);
+ if (pool == null) {
+ return null;
+ } else {
+ return pool.get(width, height);
+ }
+ }
+
+ public boolean put(Bitmap b) {
+ if (b == null) {
+ return false;
+ }
+ SparseArrayBitmapPool pool = getPoolForDimensions(b.getWidth(), b.getHeight());
+ if (pool == null) {
+ b.recycle();
+ return false;
+ } else {
+ return pool.put(b);
+ }
+ }
+
+ public void clear() {
+ for (SparseArrayBitmapPool p : mPools) {
+ p.clear();
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 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.photos.data;
+
+import android.graphics.Bitmap;
+import android.util.SparseArray;
+
+import android.util.Pools.Pool;
+
+public class SparseArrayBitmapPool {
+
+ private static final int BITMAPS_TO_KEEP_AFTER_UNNEEDED_HINT = 4;
+ private int mCapacityBytes;
+ private SparseArray<Node> mStore = new SparseArray<Node>();
+ private int mSizeBytes = 0;
+
+ private Pool<Node> mNodePool;
+ private Node mPoolNodesHead = null;
+ private Node mPoolNodesTail = null;
+
+ protected static class Node {
+ Bitmap bitmap;
+ Node prevInBucket;
+ Node nextInBucket;
+ Node nextInPool;
+ Node prevInPool;
+ }
+
+ public SparseArrayBitmapPool(int capacityBytes, Pool<Node> nodePool) {
+ mCapacityBytes = capacityBytes;
+ mNodePool = nodePool;
+ }
+
+ public synchronized void setCapacity(int capacityBytes) {
+ mCapacityBytes = capacityBytes;
+ freeUpCapacity(0);
+ }
+
+ private void freeUpCapacity(int bytesNeeded) {
+ int targetSize = mCapacityBytes - bytesNeeded;
+ while (mPoolNodesTail != null && mSizeBytes > targetSize) {
+ unlinkAndRecycleNode(mPoolNodesTail, true);
+ }
+ }
+
+ private void unlinkAndRecycleNode(Node n, boolean recycleBitmap) {
+ // Remove the node from its spot in its bucket
+ if (n.prevInBucket != null) {
+ n.prevInBucket.nextInBucket = n.nextInBucket;
+ } else {
+ mStore.put(n.bitmap.getWidth(), n.nextInBucket);
+ }
+ if (n.nextInBucket != null) {
+ n.nextInBucket.prevInBucket = n.prevInBucket;
+ }
+
+ // Remove the node from its spot in the list of pool nodes
+ if (n.prevInPool != null) {
+ n.prevInPool.nextInPool = n.nextInPool;
+ } else {
+ mPoolNodesHead = n.nextInPool;
+ }
+ if (n.nextInPool != null) {
+ n.nextInPool.prevInPool = n.prevInPool;
+ } else {
+ mPoolNodesTail = n.prevInPool;
+ }
+
+ // Recycle the node
+ n.nextInBucket = null;
+ n.nextInPool = null;
+ n.prevInBucket = null;
+ n.prevInPool = null;
+ mSizeBytes -= n.bitmap.getByteCount();
+ if (recycleBitmap) n.bitmap.recycle();
+ n.bitmap = null;
+ mNodePool.release(n);
+ }
+
+ public synchronized int getCapacity() {
+ return mCapacityBytes;
+ }
+
+ public synchronized int getSize() {
+ return mSizeBytes;
+ }
+
+ public synchronized Bitmap get(int width, int height) {
+ Node cur = mStore.get(width);
+ while (cur != null) {
+ if (cur.bitmap.getHeight() == height) {
+ Bitmap b = cur.bitmap;
+ unlinkAndRecycleNode(cur, false);
+ return b;
+ }
+ cur = cur.nextInBucket;
+ }
+ return null;
+ }
+
+ public synchronized boolean put(Bitmap b) {
+ if (b == null) {
+ return false;
+ }
+ int bytes = b.getByteCount();
+ freeUpCapacity(bytes);
+ Node newNode = mNodePool.acquire();
+ if (newNode == null) {
+ newNode = new Node();
+ }
+ newNode.bitmap = b;
+ newNode.prevInBucket = null;
+ newNode.prevInPool = null;
+ newNode.nextInPool = mPoolNodesHead;
+ mPoolNodesHead = newNode;
+ int key = b.getWidth();
+ newNode.nextInBucket = mStore.get(key);
+ if (newNode.nextInBucket != null) {
+ newNode.nextInBucket.prevInBucket = newNode;
+ }
+ mStore.put(key, newNode);
+ if (newNode.nextInPool == null) {
+ mPoolNodesTail = newNode;
+ }
+ mSizeBytes += bytes;
+ return true;
+ }
+
+ public synchronized void clear() {
+ freeUpCapacity(mCapacityBytes);
+ }
+}