• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 
17 package com.android.photoeditor;
18 
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.graphics.Bitmap;
22 import android.graphics.Bitmap.CompressFormat;
23 import android.graphics.BitmapFactory;
24 import android.graphics.Matrix;
25 import android.graphics.Rect;
26 import android.net.Uri;
27 import android.provider.MediaStore.Images.ImageColumns;
28 import android.util.Log;
29 
30 import java.io.Closeable;
31 import java.io.File;
32 import java.io.FileNotFoundException;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.OutputStream;
37 
38 /**
39  * Utils for bitmap operations.
40  */
41 public class BitmapUtils {
42 
43     private static final String TAG = "BitmapUtils";
44     private static final int DEFAULT_COMPRESS_QUALITY = 90;
45     private static final int INDEX_ORIENTATION = 0;
46 
47     private static final String[] IMAGE_PROJECTION = new String[] {
48         ImageColumns.ORIENTATION
49     };
50 
51     private final Context context;
52 
BitmapUtils(Context context)53     public BitmapUtils(Context context) {
54         this.context = context;
55     }
56 
57     /**
58      * Returns an immutable bitmap from subset of source bitmap transformed by the given matrix.
59      */
createBitmap(Bitmap bitmap, Matrix m)60     public static Bitmap createBitmap(Bitmap bitmap, Matrix m) {
61         // TODO: Re-implement createBitmap to avoid ARGB -> RBG565 conversion on platforms
62         // prior to honeycomb.
63         return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true);
64     }
65 
closeStream(Closeable stream)66     private void closeStream(Closeable stream) {
67         if (stream != null) {
68             try {
69                 stream.close();
70             } catch (IOException e) {
71                 e.printStackTrace();
72             }
73         }
74     }
75 
getBitmapBounds(Uri uri)76     private Rect getBitmapBounds(Uri uri) {
77         Rect bounds = new Rect();
78         InputStream is = null;
79 
80         try {
81             is = context.getContentResolver().openInputStream(uri);
82             BitmapFactory.Options options = new BitmapFactory.Options();
83             options.inJustDecodeBounds = true;
84             BitmapFactory.decodeStream(is, null, options);
85 
86             bounds.right = options.outWidth;
87             bounds.bottom = options.outHeight;
88         } catch (FileNotFoundException e) {
89             e.printStackTrace();
90         } finally {
91             closeStream(is);
92         }
93 
94         return bounds;
95     }
96 
getOrientation(Uri uri)97     private int getOrientation(Uri uri) {
98         int orientation = 0;
99         Cursor cursor = context.getContentResolver().query(uri, IMAGE_PROJECTION, null, null, null);
100         if ((cursor != null) && cursor.moveToNext()) {
101             orientation = cursor.getInt(INDEX_ORIENTATION);
102         }
103         return orientation;
104     }
105 
106     /**
107      * Decodes immutable bitmap that keeps aspect-ratio and spans most within the given rectangle.
108      */
decodeBitmap(Uri uri, int width, int height)109     private Bitmap decodeBitmap(Uri uri, int width, int height) {
110         InputStream is = null;
111         Bitmap bitmap = null;
112 
113         try {
114             // TODO: Take max pixels allowed into account for calculation to avoid possible OOM.
115             Rect bounds = getBitmapBounds(uri);
116             int sampleSize = Math.max(bounds.width() / width, bounds.height() / height);
117             sampleSize = Math.min(sampleSize,
118                     Math.max(bounds.width() / height, bounds.height() / width));
119 
120             BitmapFactory.Options options = new BitmapFactory.Options();
121             options.inSampleSize = Math.max(sampleSize, 1);
122             options.inPreferredConfig = Bitmap.Config.ARGB_8888;
123 
124             is = context.getContentResolver().openInputStream(uri);
125             bitmap = BitmapFactory.decodeStream(is, null, options);
126         } catch (FileNotFoundException e) {
127             Log.e(TAG, "FileNotFoundException: " + uri);
128         } finally {
129             closeStream(is);
130         }
131 
132         // Scale down the sampled bitmap if it's still larger than the desired dimension.
133         if (bitmap != null) {
134             float scale = Math.min((float) width / bitmap.getWidth(),
135                     (float) height / bitmap.getHeight());
136             scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(),
137                     (float) width / bitmap.getHeight()));
138             if (scale < 1) {
139                 Matrix m = new Matrix();
140                 m.setScale(scale, scale);
141                 Bitmap transformed = createBitmap(bitmap, m);
142                 bitmap.recycle();
143                 return transformed;
144             }
145         }
146         return bitmap;
147     }
148 
149     /**
150      * Gets decoded bitmap that keeps orientation as well.
151      */
getBitmap(Uri uri, int width, int height)152     public Bitmap getBitmap(Uri uri, int width, int height) {
153         Bitmap bitmap = decodeBitmap(uri, width, height);
154 
155         // Rotate the decoded bitmap according to its orientation if it's necessary.
156         if (bitmap != null) {
157             int orientation = getOrientation(uri);
158             if (orientation != 0) {
159                 Matrix m = new Matrix();
160                 m.setRotate(orientation);
161                 Bitmap transformed = createBitmap(bitmap, m);
162                 bitmap.recycle();
163                 return transformed;
164             }
165         }
166         return bitmap;
167     }
168 
169     /**
170      * Saves the bitmap by given directory, filename, and format; if the directory is given null,
171      * then saves it under the cache directory.
172      */
saveBitmap( Bitmap bitmap, String directory, String filename, CompressFormat format)173     public File saveBitmap(
174             Bitmap bitmap, String directory, String filename, CompressFormat format) {
175 
176         if (directory == null) {
177             directory = context.getCacheDir().getAbsolutePath();
178         } else {
179             // Check if the given directory exists or try to create it.
180             File file = new File(directory);
181             if (!file.isDirectory() && !file.mkdirs()) {
182                 return null;
183             }
184         }
185 
186         File file = null;
187         OutputStream os = null;
188 
189         try {
190             filename = (format == CompressFormat.PNG) ? filename + ".png" : filename + ".jpg";
191             file = new File(directory, filename);
192             os = new FileOutputStream(file);
193             bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, os);
194         } catch (FileNotFoundException e) {
195             e.printStackTrace();
196         } finally {
197             closeStream(os);
198         }
199         return file;
200     }
201 }
202