Util.java revision fa021af9397011cf180003139e80a88902c4b70a
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import com.android.camera.gallery.IImage;
20
21import android.content.ContentResolver;
22import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
24import android.graphics.Canvas;
25import android.graphics.Matrix;
26import android.graphics.Rect;
27import android.media.MediaMetadataRetriever;
28import android.net.Uri;
29import android.os.ParcelFileDescriptor;
30import android.util.Log;
31
32import java.io.ByteArrayOutputStream;
33import java.io.Closeable;
34import java.io.FileDescriptor;
35import java.io.IOException;
36
37/**
38 * Collection of utility functions used in this package.
39 */
40public class Util {
41    private static final String TAG = "db.Util";
42
43    private Util() {
44    }
45
46    // Rotates the bitmap by the specified degree.
47    // If a new bitmap is created, the original bitmap is recycled.
48    public static Bitmap rotate(Bitmap b, int degrees) {
49        if (degrees != 0 && b != null) {
50            Matrix m = new Matrix();
51            m.setRotate(degrees,
52                    (float) b.getWidth() / 2, (float) b.getHeight() / 2);
53            try {
54                Bitmap b2 = Bitmap.createBitmap(
55                        b, 0, 0, b.getWidth(), b.getHeight(), m, true);
56                if (b != b2) {
57                    b.recycle();
58                    b = b2;
59                }
60            } catch (OutOfMemoryError ex) {
61                // We have no memory to rotate. Return the original bitmap.
62            }
63        }
64        return b;
65    }
66
67    /*
68     * Compute the sample size as a function of the image size and the target.
69     * Scale the image down so that both the width and height are just above the
70     * target. If this means that one of the dimension goes from above the
71     * target to below the target (e.g. given a width of 480 and an image width
72     * of 600 but sample size of 2 -- i.e. new width 300 -- bump the sample size
73     * down by 1.
74     */
75    public static int computeSampleSize(
76            BitmapFactory.Options options, int target) {
77        int w = options.outWidth;
78        int h = options.outHeight;
79
80        int candidateW = w / target;
81        int candidateH = h / target;
82        int candidate = Math.max(candidateW, candidateH);
83
84        if (candidate == 0) return 1;
85
86        if (candidate > 1) {
87            if ((w > target) && (w / candidate) < target) candidate -= 1;
88        }
89
90        if (candidate > 1) {
91            if ((h > target) && (h / candidate) < target) candidate -= 1;
92        }
93
94        return candidate;
95    }
96
97    public static Bitmap transform(Matrix scaler,
98                                   Bitmap source,
99                                   int targetWidth,
100                                   int targetHeight,
101                                   boolean scaleUp) {
102        int deltaX = source.getWidth() - targetWidth;
103        int deltaY = source.getHeight() - targetHeight;
104        if (!scaleUp && (deltaX < 0 || deltaY < 0)) {
105            /*
106             * In this case the bitmap is smaller, at least in one dimension,
107             * than the target.  Transform it by placing as much of the image
108             * as possible into the target and leaving the top/bottom or
109             * left/right (or both) black.
110             */
111            Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,
112                    Bitmap.Config.ARGB_8888);
113            Canvas c = new Canvas(b2);
114
115            int deltaXHalf = Math.max(0, deltaX / 2);
116            int deltaYHalf = Math.max(0, deltaY / 2);
117            Rect src = new Rect(
118                    deltaXHalf,
119                    deltaYHalf,
120                    deltaXHalf + Math.min(targetWidth, source.getWidth()),
121                    deltaYHalf + Math.min(targetHeight, source.getHeight()));
122            int dstX = (targetWidth  - src.width())  / 2;
123            int dstY = (targetHeight - src.height()) / 2;
124            Rect dst = new Rect(
125                    dstX,
126                    dstY,
127                    targetWidth - dstX,
128                    targetHeight - dstY);
129            c.drawBitmap(source, src, dst, null);
130            return b2;
131        }
132        float bitmapWidthF = source.getWidth();
133        float bitmapHeightF = source.getHeight();
134
135        float bitmapAspect = bitmapWidthF / bitmapHeightF;
136        float viewAspect   = (float) targetWidth / targetHeight;
137
138        if (bitmapAspect > viewAspect) {
139            float scale = targetHeight / bitmapHeightF;
140            if (scale < .9F || scale > 1F) {
141                scaler.setScale(scale, scale);
142            } else {
143                scaler = null;
144            }
145        } else {
146            float scale = targetWidth / bitmapWidthF;
147            if (scale < .9F || scale > 1F) {
148                scaler.setScale(scale, scale);
149            } else {
150                scaler = null;
151            }
152        }
153
154        Bitmap b1;
155        if (scaler != null) {
156            // this is used for minithumb and crop, so we want to filter here.
157            b1 = Bitmap.createBitmap(source, 0, 0,
158                    source.getWidth(), source.getHeight(), scaler, true);
159        } else {
160            b1 = source;
161        }
162
163        int dx1 = Math.max(0, b1.getWidth() - targetWidth);
164        int dy1 = Math.max(0, b1.getHeight() - targetHeight);
165
166        Bitmap b2 = Bitmap.createBitmap(
167                b1,
168                dx1 / 2,
169                dy1 / 2,
170                targetWidth,
171                targetHeight);
172
173        if (b1 != source) {
174            b1.recycle();
175        }
176
177        return b2;
178    }
179
180    /**
181     * Creates a centered bitmap of the desired size. Recycles the input.
182     * @param source
183     */
184    public static Bitmap extractMiniThumb(
185            Bitmap source, int width, int height) {
186        return Util.extractMiniThumb(source, width, height, true);
187    }
188
189    public static Bitmap extractMiniThumb(
190            Bitmap source, int width, int height, boolean recycle) {
191        if (source == null) {
192            return null;
193        }
194
195        float scale;
196        if (source.getWidth() < source.getHeight()) {
197            scale = width / (float) source.getWidth();
198        } else {
199            scale = height / (float) source.getHeight();
200        }
201        Matrix matrix = new Matrix();
202        matrix.setScale(scale, scale);
203        Bitmap miniThumbnail = transform(matrix, source, width, height, false);
204
205        if (recycle && miniThumbnail != source) {
206            source.recycle();
207        }
208        return miniThumbnail;
209    }
210
211    /**
212     * Creates a byte[] for a given bitmap of the desired size. Recycles the
213     * input bitmap.
214     */
215    public static byte[] miniThumbData(Bitmap source) {
216        if (source == null) return null;
217
218        Bitmap miniThumbnail = extractMiniThumb(
219                source, IImage.MINI_THUMB_TARGET_SIZE,
220                IImage.MINI_THUMB_TARGET_SIZE);
221
222        ByteArrayOutputStream miniOutStream = new ByteArrayOutputStream();
223        miniThumbnail.compress(Bitmap.CompressFormat.JPEG, 75, miniOutStream);
224        miniThumbnail.recycle();
225
226        try {
227            miniOutStream.close();
228            byte [] data = miniOutStream.toByteArray();
229            return data;
230        } catch (java.io.IOException ex) {
231            Log.e(TAG, "got exception ex " + ex);
232        }
233        return null;
234    }
235
236    /**
237     * @return true if the mimetype is a video mimetype.
238     */
239    public static boolean isVideoMimeType(String mimeType) {
240        return mimeType.startsWith("video/");
241    }
242
243    /**
244     * Create a video thumbnail for a video. May return null if the video is
245     * corrupt.
246     *
247     * @param filePath
248     */
249    public static Bitmap createVideoThumbnail(String filePath) {
250        Bitmap bitmap = null;
251        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
252        try {
253            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
254            retriever.setDataSource(filePath);
255            bitmap = retriever.captureFrame();
256        } catch (IllegalArgumentException ex) {
257            // Assume this is a corrupt video file
258        } catch (RuntimeException ex) {
259            // Assume this is a corrupt video file.
260        } finally {
261            try {
262                retriever.release();
263            } catch (RuntimeException ex) {
264                // Ignore failures while cleaning up.
265            }
266        }
267        return bitmap;
268    }
269
270    public static int indexOf(String [] array, String s) {
271        for (int i = 0; i < array.length; i++) {
272            if (array[i].equals(s)) {
273                return i;
274            }
275        }
276        return -1;
277    }
278
279    public static void closeSiliently(Closeable c) {
280        if (c == null) return;
281        try {
282            c.close();
283        } catch (Throwable t) {
284            // do nothing
285        }
286    }
287
288    public static void closeSiliently(ParcelFileDescriptor c) {
289        if (c == null) return;
290        try {
291            c.close();
292        } catch (Throwable t) {
293            // do nothing
294        }
295    }
296
297    /**
298     * Make a bitmap from a given Uri.
299     *
300     * @param uri
301     */
302    public static Bitmap makeBitmap(int targetWidthOrHeight, Uri uri,
303            ContentResolver cr) {
304        ParcelFileDescriptor input = null;
305        try {
306            input = cr.openFileDescriptor(uri, "r");
307            return makeBitmap(targetWidthOrHeight, uri, cr, input, null);
308        } catch (IOException ex) {
309            return null;
310        } finally {
311            closeSiliently(input);
312        }
313    }
314
315    public static Bitmap makeBitmap(int targetWidthHeight, Uri uri,
316            ContentResolver cr, ParcelFileDescriptor pfd,
317            BitmapFactory.Options options) {
318        Bitmap b = null;
319        try {
320            if (pfd == null) pfd = makeInputStream(uri, cr);
321            if (pfd == null) return null;
322            if (options == null) options = new BitmapFactory.Options();
323
324            FileDescriptor fd = pfd.getFileDescriptor();
325            options.inSampleSize = 1;
326            if (targetWidthHeight != -1) {
327                options.inJustDecodeBounds = true;
328                BitmapManager.instance().decodeFileDescriptor(fd, options);
329                if (options.mCancel || options.outWidth == -1
330                        || options.outHeight == -1) {
331                    return null;
332                }
333                options.inSampleSize =
334                        computeSampleSize(options, targetWidthHeight);
335                options.inJustDecodeBounds = false;
336            }
337
338            options.inDither = false;
339            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
340            b = BitmapManager.instance().decodeFileDescriptor(fd, options);
341        } catch (OutOfMemoryError ex) {
342            Log.e(TAG, "Got oom exception ", ex);
343            return null;
344        } finally {
345            closeSiliently(pfd);
346        }
347        return b;
348    }
349
350    private static ParcelFileDescriptor makeInputStream(
351            Uri uri, ContentResolver cr) {
352        try {
353            return cr.openFileDescriptor(uri, "r");
354        } catch (IOException ex) {
355            return null;
356        }
357    }
358
359    public static void debugWhere(String tag, String msg) {
360        Exception ex = new Exception();
361        Log.d(tag, msg, ex);
362    }
363}
364