100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen/* 200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Copyright (C) 2009 The Android Open Source Project 300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Licensed under the Apache License, Version 2.0 (the "License"); 500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * you may not use this file except in compliance with the License. 600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * You may obtain a copy of the License at 700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * http://www.apache.org/licenses/LICENSE-2.0 900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 1000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Unless required by applicable law or agreed to in writing, software 1100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * distributed under the License is distributed on an "AS IS" BASIS, 1200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * See the License for the specific language governing permissions and 1400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * limitations under the License. 1500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 1600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 1700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenpackage android.media; 1800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 1900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.content.ContentResolver; 20ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chenimport android.content.ContentUris; 21ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chenimport android.content.ContentValues; 22ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chenimport android.database.Cursor; 2300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Bitmap; 2400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.BitmapFactory; 2500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Canvas; 2600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Matrix; 2700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Rect; 2800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.media.MediaMetadataRetriever; 29b01005ea21be2aedd0871fb4d41301dd09ef428aRay Chenimport android.media.MediaFile.MediaFileType; 30bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.net.Uri; 31bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.os.ParcelFileDescriptor; 32bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.provider.BaseColumns; 33bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.provider.MediaStore.Images; 34bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.provider.MediaStore.Images.Thumbnails; 35bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.util.Log; 3600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 3744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chenimport java.io.FileInputStream; 3800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.FileDescriptor; 3900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.IOException; 4000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.OutputStream; 4100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 4200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen/** 43bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Thumbnail generation routines for media provider. 4400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 4500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 46bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenpublic class ThumbnailUtils { 47bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static final String TAG = "ThumbnailUtils"; 48bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 49bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /* Maximum pixels size for created bitmap. */ 5049ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384; 5149ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 128 * 128; 52bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static final int UNCONSTRAINED = -1; 53bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 5449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen /* Options used internally. */ 5549ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen private static final int OPTIONS_NONE = 0x0; 567a67f156fb5b84c072c0b2bce30c78e55c96db84Ray Chen private static final int OPTIONS_SCALE_UP = 0x1; 57bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 58bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 59bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Constant used to indicate we should recycle the input in 6049ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * {@link #extractThumbnail(Bitmap, int, int, int)} unless the output is the input. 61bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 627a67f156fb5b84c072c0b2bce30c78e55c96db84Ray Chen public static final int OPTIONS_RECYCLE_INPUT = 0x2; 6300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 64bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 6544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * Constant used to indicate the dimension of mini thumbnail. 6644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @hide Only used by media framework and media provider internally. 67bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 687a67f156fb5b84c072c0b2bce30c78e55c96db84Ray Chen public static final int TARGET_SIZE_MINI_THUMBNAIL = 320; 69bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 70bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 7144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * Constant used to indicate the dimension of micro thumbnail. 7244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @hide Only used by media framework and media provider internally. 73bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 7449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96; 7500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 76bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 77bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * This method first examines if the thumbnail embedded in EXIF is bigger than our target 78bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * size. If not, then it'll create a thumbnail from original image. Due to efficiency 79bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * consideration, we want to let MediaThumbRequest avoid calling this method twice for 80bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * both kinds, so it only requests for MICRO_KIND and set saveImage to true. 81bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 82bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * This method always returns a "square thumbnail" for MICRO_KIND thumbnail. 83bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 8444dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param filePath the path of image file 8544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param kind could be MINI_KIND or MICRO_KIND 86375fb9bfd29ea4480aa9582bf3956b435152c8d9Christer Fletcher * @return Bitmap, or null on failures 8749ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * 8849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @hide This method is only used by media framework and media provider internally. 89bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 9044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen public static Bitmap createImageThumbnail(String filePath, int kind) { 9144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen boolean wantMini = (kind == Images.Thumbnails.MINI_KIND); 9244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen int targetSize = wantMini 9344dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen ? TARGET_SIZE_MINI_THUMBNAIL 9444dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen : TARGET_SIZE_MICRO_THUMBNAIL; 9544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen int maxPixels = wantMini 9644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen ? MAX_NUM_PIXELS_THUMBNAIL 9744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen : MAX_NUM_PIXELS_MICRO_THUMBNAIL; 98bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap(); 99bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Bitmap bitmap = null; 100bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen MediaFileType fileType = MediaFile.getFileType(filePath); 101bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) { 102bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap); 103bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen bitmap = sizedThumbnailBitmap.mBitmap; 104bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 105bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 106bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (bitmap == null) { 107399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang FileInputStream stream = null; 10844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen try { 109399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang stream = new FileInputStream(filePath); 110399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang FileDescriptor fd = stream.getFD(); 11144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen BitmapFactory.Options options = new BitmapFactory.Options(); 11244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inSampleSize = 1; 11344dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inJustDecodeBounds = true; 11444dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen BitmapFactory.decodeFileDescriptor(fd, null, options); 11544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen if (options.mCancel || options.outWidth == -1 11644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen || options.outHeight == -1) { 11744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen return null; 11844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen } 11944dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inSampleSize = computeSampleSize( 12044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options, targetSize, maxPixels); 12144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inJustDecodeBounds = false; 12244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen 12344dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inDither = false; 12444dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inPreferredConfig = Bitmap.Config.ARGB_8888; 12544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options); 12644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen } catch (IOException ex) { 12744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen Log.e(TAG, "", ex); 128375fb9bfd29ea4480aa9582bf3956b435152c8d9Christer Fletcher } catch (OutOfMemoryError oom) { 129375fb9bfd29ea4480aa9582bf3956b435152c8d9Christer Fletcher Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom); 130399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } finally { 131399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang try { 132399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang if (stream != null) { 133399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang stream.close(); 134399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } 135399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } catch (IOException ex) { 136399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang Log.e(TAG, "", ex); 137399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } 138bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 139399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang 140bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 141bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 142bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (kind == Images.Thumbnails.MICRO_KIND) { 143bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // now we make it a "square thumbnail" for MICRO_KIND thumbnail 14449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen bitmap = extractThumbnail(bitmap, 14549ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen TARGET_SIZE_MICRO_THUMBNAIL, 14649ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT); 147bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 148bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return bitmap; 14900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 150bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 15100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen /** 152bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Create a video thumbnail for a video. May return null if the video is 15349ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * corrupt or the format is not supported. 15400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 15544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param filePath the path of video file 15644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param kind could be MINI_KIND or MICRO_KIND 15700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 15844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen public static Bitmap createVideoThumbnail(String filePath, int kind) { 159bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Bitmap bitmap = null; 160bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 161bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 162bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen retriever.setDataSource(filePath); 163f6bd1ea0c79516a5ef3c0c463761deec1a80e419James Dong bitmap = retriever.getFrameAtTime(-1); 164bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (IllegalArgumentException ex) { 165bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // Assume this is a corrupt video file 166bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (RuntimeException ex) { 167bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // Assume this is a corrupt video file. 168bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } finally { 169bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 170bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen retriever.release(); 171bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (RuntimeException ex) { 172bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // Ignore failures while cleaning up. 173bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 174bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 175c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang 176c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang if (bitmap == null) return null; 177c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang 178c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang if (kind == Images.Thumbnails.MINI_KIND) { 179c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang // Scale down the bitmap if it's too large. 180c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int width = bitmap.getWidth(); 181c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int height = bitmap.getHeight(); 182c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int max = Math.max(width, height); 183c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang if (max > 512) { 184c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang float scale = 512f / max; 185c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int w = Math.round(scale * width); 186c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int h = Math.round(scale * height); 187c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); 188c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang } 189c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang } else if (kind == Images.Thumbnails.MICRO_KIND) { 19044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen bitmap = extractThumbnail(bitmap, 19144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen TARGET_SIZE_MICRO_THUMBNAIL, 19244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen TARGET_SIZE_MICRO_THUMBNAIL, 19344dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen OPTIONS_RECYCLE_INPUT); 19444dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen } 195bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return bitmap; 196bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 197bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 198bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 199bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Creates a centered bitmap of the desired size. 200bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 201bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * @param source original bitmap source 202bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * @param width targeted width 203bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * @param height targeted height 204bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 20549ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen public static Bitmap extractThumbnail( 20649ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen Bitmap source, int width, int height) { 20749ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen return extractThumbnail(source, width, height, OPTIONS_NONE); 20849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen } 20949ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen 21049ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen /** 21149ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * Creates a centered bitmap of the desired size. 21249ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * 21349ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param source original bitmap source 21449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param width targeted width 21549ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param height targeted height 21649ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param options options used during thumbnail extraction 21749ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen */ 21849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen public static Bitmap extractThumbnail( 21949ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen Bitmap source, int width, int height, int options) { 220bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (source == null) { 221bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return null; 222bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 223bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 224bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen float scale; 225bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (source.getWidth() < source.getHeight()) { 226bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen scale = width / (float) source.getWidth(); 227bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } else { 228bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen scale = height / (float) source.getHeight(); 229bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 230bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Matrix matrix = new Matrix(); 231bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen matrix.setScale(scale, scale); 23249ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen Bitmap thumbnail = transform(matrix, source, width, height, 23349ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen OPTIONS_SCALE_UP | options); 23449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen return thumbnail; 23500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 23600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 23700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen /* 23800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Compute the sample size as a function of minSideLength 23900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * and maxNumOfPixels. 24000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * minSideLength is used to specify that minimal width or height of a 24100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * bitmap. 24200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * maxNumOfPixels is used to specify the maximal size in pixels that is 24300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * tolerable in terms of memory usage. 24400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 24500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * The function returns a sample size based on the constraints. 24600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED, 24700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * which indicates no care of the corresponding constraint. 24800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * The functions prefers returning a sample size that 24900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED. 25000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 25100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Also, the function rounds up the sample size to a power of 2 or multiple 25200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * of 8 because BitmapFactory only honors sample size this way. 25300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * For example, BitmapFactory downsamples an image by 2 even though the 25400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * request is 3. So we round up the sample size to avoid OOM. 25500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 256bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static int computeSampleSize(BitmapFactory.Options options, 25700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int minSideLength, int maxNumOfPixels) { 25800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int initialSize = computeInitialSampleSize(options, minSideLength, 25900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen maxNumOfPixels); 26000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 26100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int roundedSize; 26200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (initialSize <= 8 ) { 26300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen roundedSize = 1; 26400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen while (roundedSize < initialSize) { 26500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen roundedSize <<= 1; 26600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 26700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 26800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen roundedSize = (initialSize + 7) / 8 * 8; 26900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 27000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 27100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return roundedSize; 27200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 27300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 27400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen private static int computeInitialSampleSize(BitmapFactory.Options options, 27500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int minSideLength, int maxNumOfPixels) { 27600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen double w = options.outWidth; 27700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen double h = options.outHeight; 27800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 27900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : 28000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); 28100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int upperBound = (minSideLength == UNCONSTRAINED) ? 128 : 28200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen (int) Math.min(Math.floor(w / minSideLength), 28300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Math.floor(h / minSideLength)); 28400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 28500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (upperBound < lowerBound) { 28600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen // return the larger one when there is no overlapping zone. 28700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return lowerBound; 28800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 28900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 29000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if ((maxNumOfPixels == UNCONSTRAINED) && 29100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen (minSideLength == UNCONSTRAINED)) { 29200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return 1; 29300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else if (minSideLength == UNCONSTRAINED) { 29400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return lowerBound; 29500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 29600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return upperBound; 29700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 29800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 29900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 300bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 301bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels. 302bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * The image data will be read from specified pfd if it's not null, otherwise 303bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * a new input stream will be created using specified ContentResolver. 304bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 305bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A 306bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * new BitmapFactory.Options will be created if options is null. 307bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 308bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, 309bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Uri uri, ContentResolver cr, ParcelFileDescriptor pfd, 310bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen BitmapFactory.Options options) { 311c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang Bitmap b = null; 31200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen try { 31300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (pfd == null) pfd = makeInputStream(uri, cr); 31400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (pfd == null) return null; 31500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (options == null) options = new BitmapFactory.Options(); 31600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 31700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen FileDescriptor fd = pfd.getFileDescriptor(); 31800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inSampleSize = 1; 31900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inJustDecodeBounds = true; 32000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen BitmapFactory.decodeFileDescriptor(fd, null, options); 32100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (options.mCancel || options.outWidth == -1 32200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen || options.outHeight == -1) { 32300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return null; 32400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 32500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inSampleSize = computeSampleSize( 32600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options, minSideLength, maxNumOfPixels); 32700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inJustDecodeBounds = false; 32800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 32900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inDither = false; 33000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inPreferredConfig = Bitmap.Config.ARGB_8888; 33100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b = BitmapFactory.decodeFileDescriptor(fd, null, options); 33200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } catch (OutOfMemoryError ex) { 33300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Log.e(TAG, "Got oom exception ", ex); 33400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return null; 33500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } finally { 33600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen closeSilently(pfd); 33700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 33800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return b; 33900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 34000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 341bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static void closeSilently(ParcelFileDescriptor c) { 342bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (c == null) return; 343bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 344bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen c.close(); 345bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (Throwable t) { 346bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // do nothing 347bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 348bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 349ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 350bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static ParcelFileDescriptor makeInputStream( 351bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Uri uri, ContentResolver cr) { 352bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 353bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return cr.openFileDescriptor(uri, "r"); 354bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (IOException ex) { 355ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen return null; 356ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 357ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 358ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 359bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 360bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Transform source Bitmap to targeted width and height. 361bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 362bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static Bitmap transform(Matrix scaler, 36300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap source, 36400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int targetWidth, 36500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int targetHeight, 36649ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen int options) { 36749ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0; 36849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0; 36900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 37000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaX = source.getWidth() - targetWidth; 37100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaY = source.getHeight() - targetHeight; 37200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (!scaleUp && (deltaX < 0 || deltaY < 0)) { 37300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen /* 37400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * In this case the bitmap is smaller, at least in one dimension, 37500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * than the target. Transform it by placing as much of the image 37600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * as possible into the target and leaving the top/bottom or 37700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * left/right (or both) black. 37800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 37900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight, 38000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap.Config.ARGB_8888); 38100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Canvas c = new Canvas(b2); 38200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 38300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaXHalf = Math.max(0, deltaX / 2); 38400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaYHalf = Math.max(0, deltaY / 2); 38500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Rect src = new Rect( 38600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaXHalf, 38700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaYHalf, 38800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaXHalf + Math.min(targetWidth, source.getWidth()), 38900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaYHalf + Math.min(targetHeight, source.getHeight())); 39000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dstX = (targetWidth - src.width()) / 2; 39100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dstY = (targetHeight - src.height()) / 2; 39200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Rect dst = new Rect( 39300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dstX, 39400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dstY, 39500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetWidth - dstX, 39600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetHeight - dstY); 39700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen c.drawBitmap(source, src, dst, null); 39800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (recycle) { 39900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen source.recycle(); 40000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 4016311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn c.setBitmap(null); 40200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return b2; 40300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 40400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float bitmapWidthF = source.getWidth(); 40500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float bitmapHeightF = source.getHeight(); 40600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 40700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float bitmapAspect = bitmapWidthF / bitmapHeightF; 40800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float viewAspect = (float) targetWidth / targetHeight; 40900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 41000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (bitmapAspect > viewAspect) { 41100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float scale = targetHeight / bitmapHeightF; 41200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (scale < .9F || scale > 1F) { 41300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler.setScale(scale, scale); 41400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 41500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler = null; 41600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 41700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 41800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float scale = targetWidth / bitmapWidthF; 41900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (scale < .9F || scale > 1F) { 42000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler.setScale(scale, scale); 42100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 42200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler = null; 42300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 42400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 42500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 42600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap b1; 42700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (scaler != null) { 42800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen // this is used for minithumb and crop, so we want to filter here. 42900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1 = Bitmap.createBitmap(source, 0, 0, 43000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen source.getWidth(), source.getHeight(), scaler, true); 43100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 43200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1 = source; 43300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 43400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 43500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (recycle && b1 != source) { 43600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen source.recycle(); 43700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 43800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 43900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dx1 = Math.max(0, b1.getWidth() - targetWidth); 44000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dy1 = Math.max(0, b1.getHeight() - targetHeight); 44100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 44200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap b2 = Bitmap.createBitmap( 44300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1, 44400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dx1 / 2, 44500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dy1 / 2, 44600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetWidth, 44700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetHeight); 44800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 44900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (b2 != b1) { 45000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (recycle || b1 != source) { 45100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1.recycle(); 45200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 45300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 45400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 45500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return b2; 45600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 45700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 458bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 459bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * SizedThumbnailBitmap contains the bitmap, which is downsampled either from 460bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * the thumbnail in exif or the full image. 461bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail 462bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * is not null. 463bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 464bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight. 465bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 4666706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen private static class SizedThumbnailBitmap { 4676706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public byte[] mThumbnailData; 4686706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public Bitmap mBitmap; 4696706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public int mThumbnailWidth; 4706706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public int mThumbnailHeight; 4716706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 472ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 473bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 474bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image. 475bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * The functions returns a SizedThumbnailBitmap, 476bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * which contains a downsampled bitmap and the thumbnail data in EXIF if exists. 477bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 4786706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen private static void createThumbnailFromEXIF(String filePath, int targetSize, 4796706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) { 4806706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen if (filePath == null) return; 481ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 4826706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen ExifInterface exif = null; 4836706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen byte [] thumbData = null; 4846706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen try { 4856706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exif = new ExifInterface(filePath); 48662b9aec7a0a4e1cf8cfec7e39ea3103ab510d72eGlenn Kasten thumbData = exif.getThumbnail(); 487ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } catch (IOException ex) { 488ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen Log.w(TAG, ex); 489ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 4906706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 4916706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.Options fullOptions = new BitmapFactory.Options(); 4926706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.Options exifOptions = new BitmapFactory.Options(); 4936706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int exifThumbWidth = 0; 4946706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int fullThumbWidth = 0; 4956706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 4966706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen // Compute exifThumbWidth. 4976706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen if (thumbData != null) { 4986706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifOptions.inJustDecodeBounds = true; 4996706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions); 5006706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels); 5016706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize; 5026706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 5036706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 5046706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen // Compute fullThumbWidth. 5056706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullOptions.inJustDecodeBounds = true; 5066706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.decodeFile(filePath, fullOptions); 5076706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels); 5086706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize; 5096706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 5106706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen // Choose the larger thumbnail as the returning sizedThumbBitmap. 51174d4843641ca1f810e27989873697cba1f41338aRay Chen if (thumbData != null && exifThumbWidth >= fullThumbWidth) { 5126706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int width = exifOptions.outWidth; 5136706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int height = exifOptions.outHeight; 5146706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifOptions.inJustDecodeBounds = false; 5156706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0, 5166706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen thumbData.length, exifOptions); 5176706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen if (sizedThumbBitmap.mBitmap != null) { 5186706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mThumbnailData = thumbData; 5196706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mThumbnailWidth = width; 5206706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mThumbnailHeight = height; 5216706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 5226706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } else { 5236706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullOptions.inJustDecodeBounds = false; 5246706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions); 5256706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 526ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 52700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen} 528