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