ImageUtils.java revision 9418aa1ad5f48066e41b3b5405c6c03caf3653d9
1/*
2 * Copyright (C) 2011 Google Inc.
3 * Licensed to The Android Open Source Project.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.ex.photo.util;
19
20import android.content.ContentResolver;
21import android.graphics.Bitmap;
22import android.graphics.BitmapFactory;
23import android.graphics.Point;
24import android.graphics.Rect;
25import android.net.Uri;
26import android.os.Build;
27import android.util.Log;
28
29import com.android.ex.photo.PhotoViewActivity;
30
31import java.io.FileNotFoundException;
32import java.io.IOException;
33import java.io.InputStream;
34
35/**
36 * Image utilities
37 */
38public class ImageUtils {
39    // Logging
40    private static final String TAG = "ImageUtils";
41
42    /** Minimum class memory class to use full-res photos */
43    private final static long MIN_NORMAL_CLASS = 32;
44    /** Minimum class memory class to use small photos */
45    private final static long MIN_SMALL_CLASS = 24;
46
47    public static enum ImageSize {
48        EXTRA_SMALL,
49        SMALL,
50        NORMAL,
51    }
52
53    public static final ImageSize sUseImageSize;
54    static {
55        // On HC and beyond, assume devices are more capable
56        if (Build.VERSION.SDK_INT >= 11) {
57            sUseImageSize = ImageSize.NORMAL;
58        } else {
59            if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) {
60                // We have plenty of memory; use full sized photos
61                sUseImageSize = ImageSize.NORMAL;
62            } else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) {
63                // We have slight less memory; use smaller sized photos
64                sUseImageSize = ImageSize.SMALL;
65            } else {
66                // We have little memory; use very small sized photos
67                sUseImageSize = ImageSize.EXTRA_SMALL;
68            }
69        }
70    }
71
72    /**
73     * @return true if the MimeType type is image
74     */
75    public static boolean isImageMimeType(String mimeType) {
76        return mimeType != null && mimeType.startsWith("image/");
77    }
78
79    /**
80     * Create a bitmap from a local URI
81     *
82     * @param resolver The ContentResolver
83     * @param uri The local URI
84     * @param maxSize The maximum size (either width or height)
85     *
86     * @return The new bitmap
87     */
88    public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) {
89        InputStream inputStream = null;
90        try {
91            final BitmapFactory.Options opts = new BitmapFactory.Options();
92            final Point bounds = getImageBounds(resolver, uri);
93
94            inputStream = resolver.openInputStream(uri);
95            opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize);
96
97            final Bitmap decodedBitmap = decodeStream(inputStream, null, opts);
98
99            // Correct thumbnail orientation as necessary
100            // TODO: Fix rotation if it's actually a problem
101            //return rotateBitmap(resolver, uri, decodedBitmap);
102            return decodedBitmap;
103
104        } catch (FileNotFoundException exception) {
105            // Do nothing - the photo will appear to be missing
106        } catch (IOException exception) {
107            // Do nothing - the photo will appear to be missing
108        } finally {
109            try {
110                if (inputStream != null) {
111                    inputStream.close();
112                }
113            } catch (IOException ignore) {
114            }
115        }
116        return null;
117    }
118
119    /**
120     * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,
121     * BitmapFactory.Options)} that returns {@code null} on {@link
122     * OutOfMemoryError}.
123     *
124     * @param is The input stream that holds the raw data to be decoded into a
125     *           bitmap.
126     * @param outPadding If not null, return the padding rect for the bitmap if
127     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
128     *                   no bitmap is returned (null) then padding is
129     *                   unchanged.
130     * @param opts null-ok; Options that control downsampling and whether the
131     *             image should be completely decoded, or just is size returned.
132     * @return The decoded bitmap, or null if the image data could not be
133     *         decoded, or, if opts is non-null, if opts requested only the
134     *         size be returned (in opts.outWidth and opts.outHeight)
135     */
136    public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {
137        try {
138            return BitmapFactory.decodeStream(is, outPadding, opts);
139        } catch (OutOfMemoryError oome) {
140            Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);
141            return null;
142        }
143    }
144
145    /**
146     * Gets the image bounds
147     *
148     * @param resolver The ContentResolver
149     * @param uri The uri
150     *
151     * @return The image bounds
152     */
153    private static Point getImageBounds(ContentResolver resolver, Uri uri)
154            throws IOException {
155        final BitmapFactory.Options opts = new BitmapFactory.Options();
156        InputStream inputStream = null;
157
158        try {
159            opts.inJustDecodeBounds = true;
160            inputStream = resolver.openInputStream(uri);
161            decodeStream(inputStream, null, opts);
162
163            return new Point(opts.outWidth, opts.outHeight);
164        } finally {
165            try {
166                if (inputStream != null) {
167                    inputStream.close();
168                }
169            } catch (IOException ignore) {
170            }
171        }
172    }
173}
174