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