Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.gallery3d.ui;
     18 
     19 import android.annotation.TargetApi;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Bitmap.Config;
     22 import android.graphics.BitmapFactory;
     23 import android.graphics.BitmapRegionDecoder;
     24 import android.graphics.Canvas;
     25 import android.graphics.Rect;
     26 
     27 import com.android.gallery3d.common.ApiHelper;
     28 import com.android.gallery3d.common.Utils;
     29 import com.android.photos.data.GalleryBitmapPool;
     30 
     31 public class TileImageViewAdapter implements TileImageView.TileSource {
     32     private static final String TAG = "TileImageViewAdapter";
     33     protected ScreenNail mScreenNail;
     34     protected boolean mOwnScreenNail;
     35     protected BitmapRegionDecoder mRegionDecoder;
     36     protected int mImageWidth;
     37     protected int mImageHeight;
     38     protected int mLevelCount;
     39 
     40     public TileImageViewAdapter() {
     41     }
     42 
     43     public synchronized void clear() {
     44         mScreenNail = null;
     45         mImageWidth = 0;
     46         mImageHeight = 0;
     47         mLevelCount = 0;
     48         mRegionDecoder = null;
     49     }
     50 
     51     // Caller is responsible to recycle the ScreenNail
     52     public synchronized void setScreenNail(
     53             ScreenNail screenNail, int width, int height) {
     54         Utils.checkNotNull(screenNail);
     55         mScreenNail = screenNail;
     56         mImageWidth = width;
     57         mImageHeight = height;
     58         mRegionDecoder = null;
     59         mLevelCount = 0;
     60     }
     61 
     62     public synchronized void setRegionDecoder(BitmapRegionDecoder decoder) {
     63         mRegionDecoder = Utils.checkNotNull(decoder);
     64         mImageWidth = decoder.getWidth();
     65         mImageHeight = decoder.getHeight();
     66         mLevelCount = calculateLevelCount();
     67     }
     68 
     69     private int calculateLevelCount() {
     70         return Math.max(0, Utils.ceilLog2(
     71                 (float) mImageWidth / mScreenNail.getWidth()));
     72     }
     73 
     74     // Gets a sub image on a rectangle of the current photo. For example,
     75     // getTile(1, 50, 50, 100, 3, pool) means to get the region located
     76     // at (50, 50) with sample level 1 (ie, down sampled by 2^1) and the
     77     // target tile size (after sampling) 100 with border 3.
     78     //
     79     // From this spec, we can infer the actual tile size to be
     80     // 100 + 3x2 = 106, and the size of the region to be extracted from the
     81     // photo to be 200 with border 6.
     82     //
     83     // As a result, we should decode region (50-6, 50-6, 250+6, 250+6) or
     84     // (44, 44, 256, 256) from the original photo and down sample it to 106.
     85     @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
     86     @Override
     87     public Bitmap getTile(int level, int x, int y, int tileSize) {
     88         if (!ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER) {
     89             return getTileWithoutReusingBitmap(level, x, y, tileSize);
     90         }
     91 
     92         int t = tileSize << level;
     93 
     94         Rect wantRegion = new Rect(x, y, x + t, y + t);
     95 
     96         boolean needClear;
     97         BitmapRegionDecoder regionDecoder = null;
     98 
     99         synchronized (this) {
    100             regionDecoder = mRegionDecoder;
    101             if (regionDecoder == null) return null;
    102 
    103             // We need to clear a reused bitmap, if wantRegion is not fully
    104             // within the image.
    105             needClear = !new Rect(0, 0, mImageWidth, mImageHeight)
    106                     .contains(wantRegion);
    107         }
    108 
    109         Bitmap bitmap = GalleryBitmapPool.getInstance().get(tileSize, tileSize);
    110         if (bitmap != null) {
    111             if (needClear) bitmap.eraseColor(0);
    112         } else {
    113             bitmap = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888);
    114         }
    115 
    116         BitmapFactory.Options options = new BitmapFactory.Options();
    117         options.inPreferredConfig = Config.ARGB_8888;
    118         options.inPreferQualityOverSpeed = true;
    119         options.inSampleSize =  (1 << level);
    120         options.inBitmap = bitmap;
    121 
    122         try {
    123             // In CropImage, we may call the decodeRegion() concurrently.
    124             synchronized (regionDecoder) {
    125                 bitmap = regionDecoder.decodeRegion(wantRegion, options);
    126             }
    127         } finally {
    128             if (options.inBitmap != bitmap && options.inBitmap != null) {
    129                 GalleryBitmapPool.getInstance().put(options.inBitmap);
    130                 options.inBitmap = null;
    131             }
    132         }
    133 
    134         if (bitmap == null) {
    135             Log.w(TAG, "fail in decoding region");
    136         }
    137         return bitmap;
    138     }
    139 
    140     private Bitmap getTileWithoutReusingBitmap(
    141             int level, int x, int y, int tileSize) {
    142         int t = tileSize << level;
    143         Rect wantRegion = new Rect(x, y, x + t, y + t);
    144 
    145         BitmapRegionDecoder regionDecoder;
    146         Rect overlapRegion;
    147 
    148         synchronized (this) {
    149             regionDecoder = mRegionDecoder;
    150             if (regionDecoder == null) return null;
    151             overlapRegion = new Rect(0, 0, mImageWidth, mImageHeight);
    152             Utils.assertTrue(overlapRegion.intersect(wantRegion));
    153         }
    154 
    155         BitmapFactory.Options options = new BitmapFactory.Options();
    156         options.inPreferredConfig = Config.ARGB_8888;
    157         options.inPreferQualityOverSpeed = true;
    158         options.inSampleSize =  (1 << level);
    159         Bitmap bitmap = null;
    160 
    161         // In CropImage, we may call the decodeRegion() concurrently.
    162         synchronized (regionDecoder) {
    163             bitmap = regionDecoder.decodeRegion(overlapRegion, options);
    164         }
    165 
    166         if (bitmap == null) {
    167             Log.w(TAG, "fail in decoding region");
    168         }
    169 
    170         if (wantRegion.equals(overlapRegion)) return bitmap;
    171 
    172         Bitmap result = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888);
    173         Canvas canvas = new Canvas(result);
    174         canvas.drawBitmap(bitmap,
    175                 (overlapRegion.left - wantRegion.left) >> level,
    176                 (overlapRegion.top - wantRegion.top) >> level, null);
    177         return result;
    178     }
    179 
    180 
    181     @Override
    182     public ScreenNail getScreenNail() {
    183         return mScreenNail;
    184     }
    185 
    186     @Override
    187     public int getImageHeight() {
    188         return mImageHeight;
    189     }
    190 
    191     @Override
    192     public int getImageWidth() {
    193         return mImageWidth;
    194     }
    195 
    196     @Override
    197     public int getLevelCount() {
    198         return mLevelCount;
    199     }
    200 }
    201