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