• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TileImageViewAdapter()40     public TileImageViewAdapter() {
41     }
42 
clear()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
setScreenNail( ScreenNail screenNail, int width, int height)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 
setRegionDecoder(BitmapRegionDecoder decoder)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 
calculateLevelCount()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
getTile(int level, int x, int y, int tileSize)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 
getTileWithoutReusingBitmap( int level, int x, int y, int tileSize)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
getScreenNail()182     public ScreenNail getScreenNail() {
183         return mScreenNail;
184     }
185 
186     @Override
getImageHeight()187     public int getImageHeight() {
188         return mImageHeight;
189     }
190 
191     @Override
getImageWidth()192     public int getImageWidth() {
193         return mImageWidth;
194     }
195 
196     @Override
getLevelCount()197     public int getLevelCount() {
198         return mLevelCount;
199     }
200 }
201