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 17package com.android.gallery3d.ui; 18 19import android.graphics.Bitmap; 20import android.graphics.Bitmap.Config; 21import android.graphics.BitmapFactory; 22import android.graphics.BitmapRegionDecoder; 23import android.graphics.Rect; 24 25import com.android.gallery3d.common.Utils; 26import com.android.gallery3d.data.BitmapPool; 27 28public class TileImageViewAdapter implements TileImageView.Model { 29 private static final String TAG = "TileImageViewAdapter"; 30 protected ScreenNail mScreenNail; 31 protected boolean mOwnScreenNail; 32 protected BitmapRegionDecoder mRegionDecoder; 33 protected int mImageWidth; 34 protected int mImageHeight; 35 protected int mLevelCount; 36 37 public TileImageViewAdapter() { 38 } 39 40 public TileImageViewAdapter( 41 Bitmap bitmap, BitmapRegionDecoder regionDecoder) { 42 Utils.checkNotNull(bitmap); 43 updateScreenNail(new BitmapScreenNail(bitmap), true); 44 mRegionDecoder = regionDecoder; 45 mImageWidth = regionDecoder.getWidth(); 46 mImageHeight = regionDecoder.getHeight(); 47 mLevelCount = calculateLevelCount(); 48 } 49 50 public synchronized void clear() { 51 updateScreenNail(null, false); 52 mImageWidth = 0; 53 mImageHeight = 0; 54 mLevelCount = 0; 55 mRegionDecoder = null; 56 } 57 58 public synchronized void setScreenNail(Bitmap bitmap, int width, int height) { 59 Utils.checkNotNull(bitmap); 60 updateScreenNail(new BitmapScreenNail(bitmap), true); 61 mImageWidth = width; 62 mImageHeight = height; 63 mRegionDecoder = null; 64 mLevelCount = 0; 65 } 66 67 public synchronized void setScreenNail( 68 ScreenNail screenNail, int width, int height) { 69 Utils.checkNotNull(screenNail); 70 updateScreenNail(screenNail, false); 71 mImageWidth = width; 72 mImageHeight = height; 73 mRegionDecoder = null; 74 mLevelCount = 0; 75 } 76 77 private void updateScreenNail(ScreenNail screenNail, boolean own) { 78 if (mScreenNail != null && mOwnScreenNail) { 79 mScreenNail.recycle(); 80 } 81 mScreenNail = screenNail; 82 mOwnScreenNail = own; 83 } 84 85 public synchronized void setRegionDecoder(BitmapRegionDecoder decoder) { 86 mRegionDecoder = Utils.checkNotNull(decoder); 87 mImageWidth = decoder.getWidth(); 88 mImageHeight = decoder.getHeight(); 89 mLevelCount = calculateLevelCount(); 90 } 91 92 private int calculateLevelCount() { 93 return Math.max(0, Utils.ceilLog2( 94 (float) mImageWidth / mScreenNail.getWidth())); 95 } 96 97 // Gets a sub image on a rectangle of the current photo. For example, 98 // getTile(1, 50, 50, 100, 3, pool) means to get the region located 99 // at (50, 50) with sample level 1 (ie, down sampled by 2^1) and the 100 // target tile size (after sampling) 100 with border 3. 101 // 102 // From this spec, we can infer the actual tile size to be 103 // 100 + 3x2 = 106, and the size of the region to be extracted from the 104 // photo to be 200 with border 6. 105 // 106 // As a result, we should decode region (50-6, 50-6, 250+6, 250+6) or 107 // (44, 44, 256, 256) from the original photo and down sample it to 106. 108 @Override 109 public Bitmap getTile(int level, int x, int y, int tileSize, 110 int borderSize, BitmapPool pool) { 111 int b = borderSize << level; 112 int t = tileSize << level; 113 114 Rect wantRegion = new Rect(x - b, y - b, x + t + b, y + t + b); 115 116 boolean needClear; 117 BitmapRegionDecoder regionDecoder = null; 118 119 synchronized (this) { 120 regionDecoder = mRegionDecoder; 121 if (regionDecoder == null) return null; 122 123 // We need to clear a reused bitmap, if wantRegion is not fully 124 // within the image. 125 needClear = !new Rect(0, 0, mImageWidth, mImageHeight) 126 .contains(wantRegion); 127 } 128 129 Bitmap bitmap = pool == null ? null : pool.getBitmap(); 130 if (bitmap != null) { 131 if (needClear) bitmap.eraseColor(0); 132 } else { 133 int s = tileSize + 2 * borderSize; 134 bitmap = Bitmap.createBitmap(s, s, Config.ARGB_8888); 135 } 136 137 BitmapFactory.Options options = new BitmapFactory.Options(); 138 options.inPreferredConfig = Config.ARGB_8888; 139 options.inPreferQualityOverSpeed = true; 140 options.inSampleSize = (1 << level); 141 options.inBitmap = bitmap; 142 143 try { 144 // In CropImage, we may call the decodeRegion() concurrently. 145 synchronized (regionDecoder) { 146 bitmap = regionDecoder.decodeRegion(wantRegion, options); 147 } 148 } finally { 149 if (options.inBitmap != bitmap && options.inBitmap != null) { 150 if (pool != null) pool.recycle(options.inBitmap); 151 options.inBitmap = null; 152 } 153 } 154 155 if (bitmap == null) { 156 Log.w(TAG, "fail in decoding region"); 157 } 158 return bitmap; 159 } 160 161 @Override 162 public ScreenNail getScreenNail() { 163 return mScreenNail; 164 } 165 166 @Override 167 public int getImageHeight() { 168 return mImageHeight; 169 } 170 171 @Override 172 public int getImageWidth() { 173 return mImageWidth; 174 } 175 176 @Override 177 public int getLevelCount() { 178 return mLevelCount; 179 } 180} 181