diff options
Diffstat (limited to 'src/com/android/gallery3d/ui/TileImageViewAdapter.java')
-rw-r--r-- | src/com/android/gallery3d/ui/TileImageViewAdapter.java | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java new file mode 100644 index 000000000..0c1f66d0c --- /dev/null +++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2010 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 android.annotation.TargetApi; +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.ApiHelper; +import com.android.gallery3d.common.Utils; +import com.android.photos.data.GalleryBitmapPool; + +public class TileImageViewAdapter implements TileImageView.TileSource { + private static final String TAG = "TileImageViewAdapter"; + protected ScreenNail mScreenNail; + protected boolean mOwnScreenNail; + protected BitmapRegionDecoder mRegionDecoder; + protected int mImageWidth; + protected int mImageHeight; + protected int mLevelCount; + + public TileImageViewAdapter() { + } + + public synchronized void clear() { + mScreenNail = null; + mImageWidth = 0; + mImageHeight = 0; + mLevelCount = 0; + mRegionDecoder = null; + } + + // Caller is responsible to recycle the ScreenNail + public synchronized void setScreenNail( + ScreenNail screenNail, int width, int height) { + Utils.checkNotNull(screenNail); + mScreenNail = screenNail; + mImageWidth = width; + mImageHeight = height; + mRegionDecoder = null; + mLevelCount = 0; + } + + public synchronized void setRegionDecoder(BitmapRegionDecoder decoder) { + mRegionDecoder = Utils.checkNotNull(decoder); + mImageWidth = decoder.getWidth(); + mImageHeight = decoder.getHeight(); + mLevelCount = calculateLevelCount(); + } + + private int calculateLevelCount() { + return Math.max(0, Utils.ceilLog2( + (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. + @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) + @Override + 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); + } + + int t = tileSize << level; + + Rect wantRegion = new Rect(x, y, x + t, y + t); + + boolean needClear; + BitmapRegionDecoder regionDecoder = null; + + synchronized (this) { + regionDecoder = mRegionDecoder; + if (regionDecoder == null) return null; + + // We need to clear a reused bitmap, if wantRegion is not fully + // within the image. + needClear = !new Rect(0, 0, mImageWidth, mImageHeight) + .contains(wantRegion); + } + + Bitmap bitmap = GalleryBitmapPool.getInstance().get(tileSize, tileSize); + if (bitmap != null) { + if (needClear) bitmap.eraseColor(0); + } else { + bitmap = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888); + } + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Config.ARGB_8888; + options.inPreferQualityOverSpeed = true; + options.inSampleSize = (1 << level); + 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) { + GalleryBitmapPool.getInstance().put(options.inBitmap); + options.inBitmap = null; + } + } + + if (bitmap == null) { + Log.w(TAG, "fail in decoding region"); + } + return bitmap; + } + + private Bitmap getTileWithoutReusingBitmap( + int level, int x, int y, int tileSize) { + int t = tileSize << level; + Rect wantRegion = new Rect(x, y, x + t, y + t); + + BitmapRegionDecoder regionDecoder; + Rect overlapRegion; + + synchronized (this) { + regionDecoder = mRegionDecoder; + if (regionDecoder == null) return null; + overlapRegion = new Rect(0, 0, mImageWidth, mImageHeight); + Utils.assertTrue(overlapRegion.intersect(wantRegion)); + } + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Config.ARGB_8888; + options.inPreferQualityOverSpeed = true; + options.inSampleSize = (1 << level); + Bitmap bitmap = null; + + // In CropImage, we may call the decodeRegion() concurrently. + synchronized (regionDecoder) { + bitmap = regionDecoder.decodeRegion(overlapRegion, options); + } + + if (bitmap == null) { + Log.w(TAG, "fail in decoding region"); + } + + if (wantRegion.equals(overlapRegion)) return bitmap; + + Bitmap result = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888); + Canvas canvas = new Canvas(result); + canvas.drawBitmap(bitmap, + (overlapRegion.left - wantRegion.left) >> level, + (overlapRegion.top - wantRegion.top) >> level, null); + return result; + } + + + @Override + public ScreenNail getScreenNail() { + return mScreenNail; + } + + @Override + public int getImageHeight() { + return mImageHeight; + } + + @Override + public int getImageWidth() { + return mImageWidth; + } + + @Override + public int getLevelCount() { + return mLevelCount; + } +} |