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; 2000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Bitmap; 2100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.BitmapFactory; 2200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Canvas; 2300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Matrix; 2400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.graphics.Rect; 2500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport android.media.MediaMetadataRetriever; 26b01005ea21be2aedd0871fb4d41301dd09ef428aRay Chenimport android.media.MediaFile.MediaFileType; 27bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.net.Uri; 28bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.os.ParcelFileDescriptor; 29bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.provider.MediaStore.Images; 30bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenimport android.util.Log; 3100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 3244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chenimport java.io.FileInputStream; 3300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.FileDescriptor; 3400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chenimport java.io.IOException; 3500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 3600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen/** 37bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Thumbnail generation routines for media provider. 3800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 3900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 40bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chenpublic class ThumbnailUtils { 41bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static final String TAG = "ThumbnailUtils"; 42bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 43bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /* Maximum pixels size for created bitmap. */ 4449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384; 4545e4584a0f92b0bc69797714f651b12569d7fe85Posselwhite private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 160 * 120; 46bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static final int UNCONSTRAINED = -1; 47bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 4849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen /* Options used internally. */ 4949ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen private static final int OPTIONS_NONE = 0x0; 507a67f156fb5b84c072c0b2bce30c78e55c96db84Ray Chen private static final int OPTIONS_SCALE_UP = 0x1; 51bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 52bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 53bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Constant used to indicate we should recycle the input in 5449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * {@link #extractThumbnail(Bitmap, int, int, int)} unless the output is the input. 55bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 567a67f156fb5b84c072c0b2bce30c78e55c96db84Ray Chen public static final int OPTIONS_RECYCLE_INPUT = 0x2; 5700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 58bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 5944dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * Constant used to indicate the dimension of mini thumbnail. 6044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @hide Only used by media framework and media provider internally. 61bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 627a67f156fb5b84c072c0b2bce30c78e55c96db84Ray Chen public static final int TARGET_SIZE_MINI_THUMBNAIL = 320; 63bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 64bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 6544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * Constant used to indicate the dimension of micro thumbnail. 6644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @hide Only used by media framework and media provider internally. 67bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 6849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96; 6900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 70bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 71bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * This method first examines if the thumbnail embedded in EXIF is bigger than our target 72bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * size. If not, then it'll create a thumbnail from original image. Due to efficiency 73bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * consideration, we want to let MediaThumbRequest avoid calling this method twice for 74bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * both kinds, so it only requests for MICRO_KIND and set saveImage to true. 75bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 76bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * This method always returns a "square thumbnail" for MICRO_KIND thumbnail. 77bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 7844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param filePath the path of image file 7944dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param kind could be MINI_KIND or MICRO_KIND 80375fb9bfd29ea4480aa9582bf3956b435152c8d9Christer Fletcher * @return Bitmap, or null on failures 8149ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * 8249ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @hide This method is only used by media framework and media provider internally. 83bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 8444dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen public static Bitmap createImageThumbnail(String filePath, int kind) { 8544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen boolean wantMini = (kind == Images.Thumbnails.MINI_KIND); 8644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen int targetSize = wantMini 8744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen ? TARGET_SIZE_MINI_THUMBNAIL 8844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen : TARGET_SIZE_MICRO_THUMBNAIL; 8944dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen int maxPixels = wantMini 9044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen ? MAX_NUM_PIXELS_THUMBNAIL 9144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen : MAX_NUM_PIXELS_MICRO_THUMBNAIL; 92bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap(); 93bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Bitmap bitmap = null; 94bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen MediaFileType fileType = MediaFile.getFileType(filePath); 955a8b9627c7fdacc94791fb3024386739f8cf9a56Jaesung Chung if (fileType != null && (fileType.fileType == MediaFile.FILE_TYPE_JPEG 965a8b9627c7fdacc94791fb3024386739f8cf9a56Jaesung Chung || MediaFile.isRawImageFileType(fileType.fileType))) { 97bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap); 98bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen bitmap = sizedThumbnailBitmap.mBitmap; 99bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 100bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 101bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (bitmap == null) { 102399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang FileInputStream stream = null; 10344dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen try { 104399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang stream = new FileInputStream(filePath); 105399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang FileDescriptor fd = stream.getFD(); 10644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen BitmapFactory.Options options = new BitmapFactory.Options(); 10744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inSampleSize = 1; 10844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inJustDecodeBounds = true; 10944dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen BitmapFactory.decodeFileDescriptor(fd, null, options); 11044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen if (options.mCancel || options.outWidth == -1 11144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen || options.outHeight == -1) { 11244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen return null; 11344dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen } 11444dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inSampleSize = computeSampleSize( 11544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options, targetSize, maxPixels); 11644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inJustDecodeBounds = false; 11744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen 11844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inDither = false; 11944dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen options.inPreferredConfig = Bitmap.Config.ARGB_8888; 12044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options); 12144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen } catch (IOException ex) { 12244dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen Log.e(TAG, "", ex); 123375fb9bfd29ea4480aa9582bf3956b435152c8d9Christer Fletcher } catch (OutOfMemoryError oom) { 124375fb9bfd29ea4480aa9582bf3956b435152c8d9Christer Fletcher Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom); 125399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } finally { 126399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang try { 127399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang if (stream != null) { 128399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang stream.close(); 129399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } 130399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } catch (IOException ex) { 131399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang Log.e(TAG, "", ex); 132399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang } 133bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 134399381868992c68abefa5dde62bfdc0a7e33e5e5Dongwon Kang 135bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 136bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 137bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (kind == Images.Thumbnails.MICRO_KIND) { 138bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // now we make it a "square thumbnail" for MICRO_KIND thumbnail 13949ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen bitmap = extractThumbnail(bitmap, 14049ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen TARGET_SIZE_MICRO_THUMBNAIL, 14149ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT); 142bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 143bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return bitmap; 14400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 145bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 14600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen /** 147bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Create a video thumbnail for a video. May return null if the video is 14849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * corrupt or the format is not supported. 14900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 15044dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param filePath the path of video file 15144dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen * @param kind could be MINI_KIND or MICRO_KIND 15200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 15344dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen public static Bitmap createVideoThumbnail(String filePath, int kind) { 154bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Bitmap bitmap = null; 155bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 156bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 157bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen retriever.setDataSource(filePath); 158f6bd1ea0c79516a5ef3c0c463761deec1a80e419James Dong bitmap = retriever.getFrameAtTime(-1); 159bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (IllegalArgumentException ex) { 160bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // Assume this is a corrupt video file 161bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (RuntimeException ex) { 162bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // Assume this is a corrupt video file. 163bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } finally { 164bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 165bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen retriever.release(); 166bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (RuntimeException ex) { 167bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // Ignore failures while cleaning up. 168bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 169bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 170c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang 171c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang if (bitmap == null) return null; 172c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang 173c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang if (kind == Images.Thumbnails.MINI_KIND) { 174c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang // Scale down the bitmap if it's too large. 175c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int width = bitmap.getWidth(); 176c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int height = bitmap.getHeight(); 177c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int max = Math.max(width, height); 178c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang if (max > 512) { 179c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang float scale = 512f / max; 180c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int w = Math.round(scale * width); 181c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang int h = Math.round(scale * height); 182c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true); 183c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang } 184c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang } else if (kind == Images.Thumbnails.MICRO_KIND) { 18544dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen bitmap = extractThumbnail(bitmap, 18644dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen TARGET_SIZE_MICRO_THUMBNAIL, 18744dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen TARGET_SIZE_MICRO_THUMBNAIL, 18844dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen OPTIONS_RECYCLE_INPUT); 18944dcf658718fcc0b563dcad50fb59e8fe507cd0aRay Chen } 190bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return bitmap; 191bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 192bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 193bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 194bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Creates a centered bitmap of the desired size. 195bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 196bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * @param source original bitmap source 197bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * @param width targeted width 198bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * @param height targeted height 199bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 20049ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen public static Bitmap extractThumbnail( 20149ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen Bitmap source, int width, int height) { 20249ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen return extractThumbnail(source, width, height, OPTIONS_NONE); 20349ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen } 20449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen 20549ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen /** 20649ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * Creates a centered bitmap of the desired size. 20749ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * 20849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param source original bitmap source 20949ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param width targeted width 21049ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param height targeted height 21149ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen * @param options options used during thumbnail extraction 21249ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen */ 21349ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen public static Bitmap extractThumbnail( 21449ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen Bitmap source, int width, int height, int options) { 215bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (source == null) { 216bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return null; 217bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 218bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen 219bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen float scale; 220bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (source.getWidth() < source.getHeight()) { 221bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen scale = width / (float) source.getWidth(); 222bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } else { 223bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen scale = height / (float) source.getHeight(); 224bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 225bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Matrix matrix = new Matrix(); 226bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen matrix.setScale(scale, scale); 22749ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen Bitmap thumbnail = transform(matrix, source, width, height, 22849ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen OPTIONS_SCALE_UP | options); 22949ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen return thumbnail; 23000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 23100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 23200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen /* 23300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Compute the sample size as a function of minSideLength 23400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * and maxNumOfPixels. 23500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * minSideLength is used to specify that minimal width or height of a 23600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * bitmap. 23700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * maxNumOfPixels is used to specify the maximal size in pixels that is 23800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * tolerable in terms of memory usage. 23900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 24000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * The function returns a sample size based on the constraints. 24100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED, 24200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * which indicates no care of the corresponding constraint. 24300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * The functions prefers returning a sample size that 24400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED. 24500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * 24600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * Also, the function rounds up the sample size to a power of 2 or multiple 24700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * of 8 because BitmapFactory only honors sample size this way. 24800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * For example, BitmapFactory downsamples an image by 2 even though the 24900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * request is 3. So we round up the sample size to avoid OOM. 25000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 251bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static int computeSampleSize(BitmapFactory.Options options, 25200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int minSideLength, int maxNumOfPixels) { 25300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int initialSize = computeInitialSampleSize(options, minSideLength, 25400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen maxNumOfPixels); 25500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 25600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int roundedSize; 25700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (initialSize <= 8 ) { 25800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen roundedSize = 1; 25900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen while (roundedSize < initialSize) { 26000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen roundedSize <<= 1; 26100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 26200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 26300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen roundedSize = (initialSize + 7) / 8 * 8; 26400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 26500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 26600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return roundedSize; 26700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 26800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 26900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen private static int computeInitialSampleSize(BitmapFactory.Options options, 27000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int minSideLength, int maxNumOfPixels) { 27100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen double w = options.outWidth; 27200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen double h = options.outHeight; 27300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 27400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : 27500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); 27600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int upperBound = (minSideLength == UNCONSTRAINED) ? 128 : 27700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen (int) Math.min(Math.floor(w / minSideLength), 27800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Math.floor(h / minSideLength)); 27900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 28000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (upperBound < lowerBound) { 28100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen // return the larger one when there is no overlapping zone. 28200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return lowerBound; 28300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 28400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 28500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if ((maxNumOfPixels == UNCONSTRAINED) && 28600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen (minSideLength == UNCONSTRAINED)) { 28700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return 1; 28800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else if (minSideLength == UNCONSTRAINED) { 28900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return lowerBound; 29000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 29100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return upperBound; 29200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 29300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 29400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 295bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 296bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels. 297bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * The image data will be read from specified pfd if it's not null, otherwise 298bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * a new input stream will be created using specified ContentResolver. 299bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 300bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A 301bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * new BitmapFactory.Options will be created if options is null. 302bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 303bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, 304bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Uri uri, ContentResolver cr, ParcelFileDescriptor pfd, 305bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen BitmapFactory.Options options) { 306c32dd5f2d12107f3f1eff26dfd55a42d49c337deChih-Chung Chang Bitmap b = null; 30700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen try { 30800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (pfd == null) pfd = makeInputStream(uri, cr); 30900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (pfd == null) return null; 31000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (options == null) options = new BitmapFactory.Options(); 31100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 31200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen FileDescriptor fd = pfd.getFileDescriptor(); 31300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inSampleSize = 1; 31400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inJustDecodeBounds = true; 31500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen BitmapFactory.decodeFileDescriptor(fd, null, options); 31600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (options.mCancel || options.outWidth == -1 31700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen || options.outHeight == -1) { 31800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return null; 31900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 32000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inSampleSize = computeSampleSize( 32100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options, minSideLength, maxNumOfPixels); 32200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inJustDecodeBounds = false; 32300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 32400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inDither = false; 32500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen options.inPreferredConfig = Bitmap.Config.ARGB_8888; 32600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b = BitmapFactory.decodeFileDescriptor(fd, null, options); 32700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } catch (OutOfMemoryError ex) { 32800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Log.e(TAG, "Got oom exception ", ex); 32900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return null; 33000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } finally { 33100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen closeSilently(pfd); 33200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 33300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return b; 33400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 33500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 336bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static void closeSilently(ParcelFileDescriptor c) { 337bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen if (c == null) return; 338bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 339bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen c.close(); 340bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (Throwable t) { 341bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen // do nothing 342bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 343bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } 344ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 345bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static ParcelFileDescriptor makeInputStream( 346bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen Uri uri, ContentResolver cr) { 347bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen try { 348bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen return cr.openFileDescriptor(uri, "r"); 349bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen } catch (IOException ex) { 350ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen return null; 351ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 352ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 353ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 354bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 355bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Transform source Bitmap to targeted width and height. 356bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 357bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen private static Bitmap transform(Matrix scaler, 35800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap source, 35900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int targetWidth, 36000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int targetHeight, 36149ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen int options) { 36249ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0; 36349ffc0ff72a29f000b56deb34b0706cbfc5624bfRay Chen boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0; 36400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 36500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaX = source.getWidth() - targetWidth; 36600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaY = source.getHeight() - targetHeight; 36700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (!scaleUp && (deltaX < 0 || deltaY < 0)) { 36800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen /* 36900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * In this case the bitmap is smaller, at least in one dimension, 37000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * than the target. Transform it by placing as much of the image 37100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * as possible into the target and leaving the top/bottom or 37200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen * left/right (or both) black. 37300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen */ 37400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight, 37500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap.Config.ARGB_8888); 37600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Canvas c = new Canvas(b2); 37700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 37800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaXHalf = Math.max(0, deltaX / 2); 37900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int deltaYHalf = Math.max(0, deltaY / 2); 38000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Rect src = new Rect( 38100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaXHalf, 38200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaYHalf, 38300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaXHalf + Math.min(targetWidth, source.getWidth()), 38400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen deltaYHalf + Math.min(targetHeight, source.getHeight())); 38500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dstX = (targetWidth - src.width()) / 2; 38600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dstY = (targetHeight - src.height()) / 2; 38700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Rect dst = new Rect( 38800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dstX, 38900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dstY, 39000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetWidth - dstX, 39100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetHeight - dstY); 39200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen c.drawBitmap(source, src, dst, null); 39300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (recycle) { 39400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen source.recycle(); 39500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 3966311d0a079702b29984c0d31937345be105e1a5eDianne Hackborn c.setBitmap(null); 39700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return b2; 39800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 39900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float bitmapWidthF = source.getWidth(); 40000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float bitmapHeightF = source.getHeight(); 40100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 40200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float bitmapAspect = bitmapWidthF / bitmapHeightF; 40300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float viewAspect = (float) targetWidth / targetHeight; 40400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 40500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (bitmapAspect > viewAspect) { 40600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float scale = targetHeight / bitmapHeightF; 40700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (scale < .9F || scale > 1F) { 40800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler.setScale(scale, scale); 40900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 41000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler = null; 41100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 41200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 41300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen float scale = targetWidth / bitmapWidthF; 41400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (scale < .9F || scale > 1F) { 41500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler.setScale(scale, scale); 41600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 41700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen scaler = null; 41800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 41900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 42000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 42100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap b1; 42200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (scaler != null) { 42300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen // this is used for minithumb and crop, so we want to filter here. 42400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1 = Bitmap.createBitmap(source, 0, 0, 42500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen source.getWidth(), source.getHeight(), scaler, true); 42600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } else { 42700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1 = source; 42800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 42900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 43000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (recycle && b1 != source) { 43100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen source.recycle(); 43200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 43300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 43400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dx1 = Math.max(0, b1.getWidth() - targetWidth); 43500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen int dy1 = Math.max(0, b1.getHeight() - targetHeight); 43600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 43700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen Bitmap b2 = Bitmap.createBitmap( 43800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1, 43900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dx1 / 2, 44000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen dy1 / 2, 44100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetWidth, 44200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen targetHeight); 44300c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 44400c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (b2 != b1) { 44500c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen if (recycle || b1 != source) { 44600c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen b1.recycle(); 44700c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 44800c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 44900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 45000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen return b2; 45100c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen } 45200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen 453bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 454bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * SizedThumbnailBitmap contains the bitmap, which is downsampled either from 455bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * the thumbnail in exif or the full image. 456bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail 457bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * is not null. 458bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * 459bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight. 460bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 4616706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen private static class SizedThumbnailBitmap { 4626706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public byte[] mThumbnailData; 4636706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public Bitmap mBitmap; 4646706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public int mThumbnailWidth; 4656706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen public int mThumbnailHeight; 4666706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 467ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 468bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen /** 469bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image. 470bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * The functions returns a SizedThumbnailBitmap, 471bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen * which contains a downsampled bitmap and the thumbnail data in EXIF if exists. 472bf124e7e41f7850ac1b7be808221a462db6f3447Ray Chen */ 4736706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen private static void createThumbnailFromEXIF(String filePath, int targetSize, 4746706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) { 4756706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen if (filePath == null) return; 476ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen 4776706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen ExifInterface exif = null; 4786706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen byte [] thumbData = null; 4796706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen try { 4806706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exif = new ExifInterface(filePath); 48162b9aec7a0a4e1cf8cfec7e39ea3103ab510d72eGlenn Kasten thumbData = exif.getThumbnail(); 482ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } catch (IOException ex) { 483ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen Log.w(TAG, ex); 484ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 4856706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 4866706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.Options fullOptions = new BitmapFactory.Options(); 4876706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.Options exifOptions = new BitmapFactory.Options(); 4886706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int exifThumbWidth = 0; 4896706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int fullThumbWidth = 0; 4906706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 4916706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen // Compute exifThumbWidth. 4926706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen if (thumbData != null) { 4936706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifOptions.inJustDecodeBounds = true; 4946706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions); 4956706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels); 4966706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize; 4976706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 4986706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 4996706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen // Compute fullThumbWidth. 5006706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullOptions.inJustDecodeBounds = true; 5016706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen BitmapFactory.decodeFile(filePath, fullOptions); 5026706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels); 5036706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize; 5046706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen 5056706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen // Choose the larger thumbnail as the returning sizedThumbBitmap. 50674d4843641ca1f810e27989873697cba1f41338aRay Chen if (thumbData != null && exifThumbWidth >= fullThumbWidth) { 5076706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int width = exifOptions.outWidth; 5086706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen int height = exifOptions.outHeight; 5096706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen exifOptions.inJustDecodeBounds = false; 5106706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0, 5116706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen thumbData.length, exifOptions); 5126706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen if (sizedThumbBitmap.mBitmap != null) { 5136706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mThumbnailData = thumbData; 5146706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mThumbnailWidth = width; 5156706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mThumbnailHeight = height; 5166706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 5176706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } else { 5186706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen fullOptions.inJustDecodeBounds = false; 5196706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions); 5206706caf937bf39571b8e315d1a1e9ae4c6b27a41Wei-Ta Chen } 521ef093cd6c4ab4d3c8a1c8be5ed7147d5f06d7027Ray Chen } 52200c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen} 523