1d8732610ff48dc57063559d55ea3c84775e30e41John Reck/* 2d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Copyright (C) 2013 The Android Open Source Project 3d8732610ff48dc57063559d55ea3c84775e30e41John Reck * 4d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Licensed under the Apache License, Version 2.0 (the "License"); 5d8732610ff48dc57063559d55ea3c84775e30e41John Reck * you may not use this file except in compliance with the License. 6d8732610ff48dc57063559d55ea3c84775e30e41John Reck * You may obtain a copy of the License at 7d8732610ff48dc57063559d55ea3c84775e30e41John Reck * 8d8732610ff48dc57063559d55ea3c84775e30e41John Reck * http://www.apache.org/licenses/LICENSE-2.0 9d8732610ff48dc57063559d55ea3c84775e30e41John Reck * 10d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Unless required by applicable law or agreed to in writing, software 11d8732610ff48dc57063559d55ea3c84775e30e41John Reck * distributed under the License is distributed on an "AS IS" BASIS, 12d8732610ff48dc57063559d55ea3c84775e30e41John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d8732610ff48dc57063559d55ea3c84775e30e41John Reck * See the License for the specific language governing permissions and 14d8732610ff48dc57063559d55ea3c84775e30e41John Reck * limitations under the License. 15d8732610ff48dc57063559d55ea3c84775e30e41John Reck */ 16d8732610ff48dc57063559d55ea3c84775e30e41John Reck 17d8732610ff48dc57063559d55ea3c84775e30e41John Reckpackage com.android.photos; 18d8732610ff48dc57063559d55ea3c84775e30e41John Reck 19c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reckimport android.annotation.TargetApi; 20f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport android.content.Context; 21d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.Bitmap; 22f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport android.graphics.Bitmap.Config; 23d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.BitmapFactory; 24d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.BitmapRegionDecoder; 25f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport android.graphics.Canvas; 26d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.Rect; 27f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport android.os.Build; 28f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport android.os.Build.VERSION_CODES; 29d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.util.Log; 30f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 31c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reckimport com.android.gallery3d.common.BitmapUtils; 32f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport com.android.gallery3d.glrenderer.BasicTexture; 33f5ef4801465a48ce3e33e1a75568060b2cf61185John Reckimport com.android.gallery3d.glrenderer.BitmapTexture; 34d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport com.android.photos.views.TiledImageRenderer; 35d8732610ff48dc57063559d55ea3c84775e30e41John Reck 36d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport java.io.IOException; 37d8732610ff48dc57063559d55ea3c84775e30e41John Reck 38f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck/** 39f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck * A {@link com.android.photos.views.TiledImageRenderer.TileSource} using 40f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck * {@link BitmapRegionDecoder} to wrap a local file 41f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck */ 42c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) 43d8732610ff48dc57063559d55ea3c84775e30e41John Reckpublic class BitmapRegionTileSource implements TiledImageRenderer.TileSource { 44d8732610ff48dc57063559d55ea3c84775e30e41John Reck 45f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private static final String TAG = "BitmapRegionTileSource"; 46d8732610ff48dc57063559d55ea3c84775e30e41John Reck 47f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private static final boolean REUSE_BITMAP = 48f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; 49c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck private static final int GL_SIZE_LIMIT = 2048; 50c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck // This must be no larger than half the size of the GL_SIZE_LIMIT 51c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck // due to decodePreview being allowed to be up to 2x the size of the target 52f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private static final int MAX_PREVIEW_SIZE = 1024; 53d8732610ff48dc57063559d55ea3c84775e30e41John Reck 54f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck BitmapRegionDecoder mDecoder; 55f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck int mWidth; 56f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck int mHeight; 57f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck int mTileSize; 58f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private BasicTexture mPreview; 59f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private final int mRotation; 60f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 61f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck // For use only by getTile 62f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private Rect mWantRegion = new Rect(); 63f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private Rect mOverlapRegion = new Rect(); 64f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private BitmapFactory.Options mOptions; 65f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private Canvas mCanvas; 66f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 67f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck public BitmapRegionTileSource(Context context, String path, int previewSize, int rotation) { 68f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mTileSize = TiledImageRenderer.suggestedTileSize(context); 69f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mRotation = rotation; 70d8732610ff48dc57063559d55ea3c84775e30e41John Reck try { 71d8732610ff48dc57063559d55ea3c84775e30e41John Reck mDecoder = BitmapRegionDecoder.newInstance(path, true); 72f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mWidth = mDecoder.getWidth(); 73f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mHeight = mDecoder.getHeight(); 74d8732610ff48dc57063559d55ea3c84775e30e41John Reck } catch (IOException e) { 75d8732610ff48dc57063559d55ea3c84775e30e41John Reck Log.w("BitmapRegionTileSource", "ctor failed", e); 76d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 77f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions = new BitmapFactory.Options(); 78f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; 79f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions.inPreferQualityOverSpeed = true; 80f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions.inTempStorage = new byte[16 * 1024]; 81f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck if (previewSize != 0) { 82f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE); 83f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck // Although this is the same size as the Bitmap that is likely already 84f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck // loaded, the lifecycle is different and interactions are on a different 85f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck // thread. Thus to simplify, this source will decode its own bitmap. 86c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck Bitmap preview = decodePreview(path, previewSize); 87c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) { 88f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mPreview = new BitmapTexture(preview); 89f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } else { 90f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck Log.w(TAG, String.format( 91f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck "Failed to create preview of apropriate size! " 92c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck + " in: %dx%d, out: %dx%d", 93c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck mWidth, mHeight, 94f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck preview.getWidth(), preview.getHeight())); 95f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 96f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 97d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 98d8732610ff48dc57063559d55ea3c84775e30e41John Reck 99d8732610ff48dc57063559d55ea3c84775e30e41John Reck @Override 100d8732610ff48dc57063559d55ea3c84775e30e41John Reck public int getTileSize() { 101f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return mTileSize; 102d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 103d8732610ff48dc57063559d55ea3c84775e30e41John Reck 104d8732610ff48dc57063559d55ea3c84775e30e41John Reck @Override 105d8732610ff48dc57063559d55ea3c84775e30e41John Reck public int getImageWidth() { 106f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return mWidth; 107d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 108d8732610ff48dc57063559d55ea3c84775e30e41John Reck 109d8732610ff48dc57063559d55ea3c84775e30e41John Reck @Override 110d8732610ff48dc57063559d55ea3c84775e30e41John Reck public int getImageHeight() { 111f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return mHeight; 112f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 113f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 114f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck @Override 115f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck public BasicTexture getPreview() { 116f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return mPreview; 117f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 118f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 119f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck @Override 120f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck public int getRotation() { 121f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return mRotation; 122d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 123d8732610ff48dc57063559d55ea3c84775e30e41John Reck 124d8732610ff48dc57063559d55ea3c84775e30e41John Reck @Override 125d8732610ff48dc57063559d55ea3c84775e30e41John Reck public Bitmap getTile(int level, int x, int y, Bitmap bitmap) { 126d8732610ff48dc57063559d55ea3c84775e30e41John Reck int tileSize = getTileSize(); 127f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck if (!REUSE_BITMAP) { 128f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return getTileWithoutReusingBitmap(level, x, y, tileSize); 129f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 130d8732610ff48dc57063559d55ea3c84775e30e41John Reck 131f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck int t = tileSize << level; 132f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mWantRegion.set(x, y, x + t, y + t); 133d8732610ff48dc57063559d55ea3c84775e30e41John Reck 134d8732610ff48dc57063559d55ea3c84775e30e41John Reck if (bitmap == null) { 135d8732610ff48dc57063559d55ea3c84775e30e41John Reck bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888); 136d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 137d8732610ff48dc57063559d55ea3c84775e30e41John Reck 138f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions.inSampleSize = (1 << level); 139f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions.inBitmap = bitmap; 140d8732610ff48dc57063559d55ea3c84775e30e41John Reck 141d8732610ff48dc57063559d55ea3c84775e30e41John Reck try { 142f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck bitmap = mDecoder.decodeRegion(mWantRegion, mOptions); 143d8732610ff48dc57063559d55ea3c84775e30e41John Reck } finally { 144f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck if (mOptions.inBitmap != bitmap && mOptions.inBitmap != null) { 145f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions.inBitmap = null; 146d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 147d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 148d8732610ff48dc57063559d55ea3c84775e30e41John Reck 149d8732610ff48dc57063559d55ea3c84775e30e41John Reck if (bitmap == null) { 150d8732610ff48dc57063559d55ea3c84775e30e41John Reck Log.w("BitmapRegionTileSource", "fail in decoding region"); 151d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 152d8732610ff48dc57063559d55ea3c84775e30e41John Reck return bitmap; 153d8732610ff48dc57063559d55ea3c84775e30e41John Reck } 154f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 155f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck private Bitmap getTileWithoutReusingBitmap( 156f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck int level, int x, int y, int tileSize) { 157f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 158f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck int t = tileSize << level; 159f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mWantRegion.set(x, y, x + t, y + t); 160f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 161f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOverlapRegion.set(0, 0, mWidth, mHeight); 162f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 163f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mOptions.inSampleSize = (1 << level); 164f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck Bitmap bitmap = mDecoder.decodeRegion(mOverlapRegion, mOptions); 165f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 166f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck if (bitmap == null) { 167f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck Log.w(TAG, "fail in decoding region"); 168f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 169f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 170f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck if (mWantRegion.equals(mOverlapRegion)) { 171f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return bitmap; 172f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 173f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck 174f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck Bitmap result = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888); 175f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck if (mCanvas == null) { 176f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mCanvas = new Canvas(); 177f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 178f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mCanvas.setBitmap(result); 179f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mCanvas.drawBitmap(bitmap, 180f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck (mOverlapRegion.left - mWantRegion.left) >> level, 181f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck (mOverlapRegion.top - mWantRegion.top) >> level, null); 182f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck mCanvas.setBitmap(null); 183f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck return result; 184f5ef4801465a48ce3e33e1a75568060b2cf61185John Reck } 185c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck 186c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck /** 187c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck * Note that the returned bitmap may have a long edge that's longer 188c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck * than the targetSize, but it will always be less than 2x the targetSize 189c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck */ 190c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck private Bitmap decodePreview(String file, int targetSize) { 191c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck float scale = (float) targetSize / Math.max(mWidth, mHeight); 192c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck mOptions.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); 193c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck mOptions.inJustDecodeBounds = false; 194c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck 195c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck Bitmap result = BitmapFactory.decodeFile(file, mOptions); 196c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck if (result == null) { 197c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck return null; 198c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck } 199c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck 200c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck // We need to resize down if the decoder does not support inSampleSize 201c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck // or didn't support the specified inSampleSize (some decoders only do powers of 2) 202c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight())); 203c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck 204c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck if (scale <= 0.5) { 205c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck result = BitmapUtils.resizeBitmapByScale(result, scale, true); 206c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck } 207c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck return ensureGLCompatibleBitmap(result); 208c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck } 209c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck 210c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck private static Bitmap ensureGLCompatibleBitmap(Bitmap bitmap) { 211c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck if (bitmap == null || bitmap.getConfig() != null) { 212c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck return bitmap; 213c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck } 214c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck Bitmap newBitmap = bitmap.copy(Config.ARGB_8888, false); 215c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck bitmap.recycle(); 216c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck return newBitmap; 217c31aee6a67b08716b916906b2db2096e1b929b4aJohn Reck } 218d8732610ff48dc57063559d55ea3c84775e30e41John Reck} 219