blob: 9b41143941ca4b13965c784e774783fce751ccc7 [file] [log] [blame]
Yuli Huang6a12ad72011-09-12 22:25:30 +08001/*
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
17package com.android.gallery3d.photoeditor;
18
19import android.content.Context;
20import android.database.Cursor;
21import android.graphics.Bitmap;
22import android.graphics.Bitmap.CompressFormat;
23import android.graphics.BitmapFactory;
Yuli Huang6a12ad72011-09-12 22:25:30 +080024import android.graphics.Matrix;
Yuli Huang6a12ad72011-09-12 22:25:30 +080025import android.graphics.Rect;
Yuli Huang6a12ad72011-09-12 22:25:30 +080026import android.net.Uri;
27import android.provider.MediaStore.Images.ImageColumns;
28import android.util.Log;
29
30import java.io.Closeable;
31import java.io.File;
32import java.io.FileNotFoundException;
33import java.io.FileOutputStream;
34import java.io.IOException;
35import java.io.InputStream;
36import java.io.OutputStream;
37
38/**
39 * Utils for bitmap operations.
40 */
41public 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
53 public BitmapUtils(Context context) {
54 this.context = context;
55 }
56
Yuli Huang1d9a19d2011-12-02 17:35:34 +080057 private static Bitmap createBitmap(Bitmap source, Matrix m) {
58 return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), m, true);
Yuli Huang6a12ad72011-09-12 22:25:30 +080059 }
60
61 private void closeStream(Closeable stream) {
62 if (stream != null) {
63 try {
64 stream.close();
65 } catch (IOException e) {
66 e.printStackTrace();
67 }
68 }
69 }
70
71 private Rect getBitmapBounds(Uri uri) {
72 Rect bounds = new Rect();
73 InputStream is = null;
74
75 try {
76 is = context.getContentResolver().openInputStream(uri);
77 BitmapFactory.Options options = new BitmapFactory.Options();
78 options.inJustDecodeBounds = true;
79 BitmapFactory.decodeStream(is, null, options);
80
81 bounds.right = options.outWidth;
82 bounds.bottom = options.outHeight;
83 } catch (FileNotFoundException e) {
84 e.printStackTrace();
85 } finally {
86 closeStream(is);
87 }
88
89 return bounds;
90 }
91
92 private int getOrientation(Uri uri) {
93 int orientation = 0;
Yuli Huangfef5b982011-09-28 01:21:15 +080094 Cursor cursor = null;
Yuli Huang6a12ad72011-09-12 22:25:30 +080095 try {
Yuli Huangfef5b982011-09-28 01:21:15 +080096 cursor = context.getContentResolver().query(uri, IMAGE_PROJECTION, null, null, null);
Yuli Huang6a12ad72011-09-12 22:25:30 +080097 if ((cursor != null) && cursor.moveToNext()) {
98 orientation = cursor.getInt(INDEX_ORIENTATION);
99 }
Yuli Huangfef5b982011-09-28 01:21:15 +0800100 } catch (Exception e) {
101 // Ignore error for no orientation column; just use the default orientation value 0.
Yuli Huang6a12ad72011-09-12 22:25:30 +0800102 } finally {
103 if (cursor != null) {
104 cursor.close();
105 }
106 }
107 return orientation;
108 }
109
110 /**
Yuli Huang1d9a19d2011-12-02 17:35:34 +0800111 * Decodes bitmap that keeps aspect-ratio and spans most within the bounds.
Yuli Huang6a12ad72011-09-12 22:25:30 +0800112 */
113 private Bitmap decodeBitmap(Uri uri, int width, int height) {
114 InputStream is = null;
115 Bitmap bitmap = null;
116
117 try {
118 // TODO: Take max pixels allowed into account for calculation to avoid possible OOM.
119 Rect bounds = getBitmapBounds(uri);
120 int sampleSize = Math.max(bounds.width() / width, bounds.height() / height);
121 sampleSize = Math.min(sampleSize,
122 Math.max(bounds.width() / height, bounds.height() / width));
123
124 BitmapFactory.Options options = new BitmapFactory.Options();
125 options.inSampleSize = Math.max(sampleSize, 1);
126 options.inPreferredConfig = Bitmap.Config.ARGB_8888;
127
128 is = context.getContentResolver().openInputStream(uri);
129 bitmap = BitmapFactory.decodeStream(is, null, options);
130 } catch (FileNotFoundException e) {
131 Log.e(TAG, "FileNotFoundException: " + uri);
132 } finally {
133 closeStream(is);
134 }
135
Yuli Huangf66bd122011-10-26 20:10:06 +0800136 // Ensure bitmap in 8888 format, good for editing as well as GL compatible.
137 if ((bitmap != null) && (bitmap.getConfig() != Bitmap.Config.ARGB_8888)) {
138 Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, true);
139 bitmap.recycle();
140 bitmap = copy;
141 }
142
Yuli Huang6a12ad72011-09-12 22:25:30 +0800143 if (bitmap != null) {
Yuli Huangf66bd122011-10-26 20:10:06 +0800144 // Scale down the sampled bitmap if it's still larger than the desired dimension.
Yuli Huang6a12ad72011-09-12 22:25:30 +0800145 float scale = Math.min((float) width / bitmap.getWidth(),
146 (float) height / bitmap.getHeight());
147 scale = Math.max(scale, Math.min((float) height / bitmap.getWidth(),
148 (float) width / bitmap.getHeight()));
149 if (scale < 1) {
150 Matrix m = new Matrix();
151 m.setScale(scale, scale);
Yuli Huang1d9a19d2011-12-02 17:35:34 +0800152 Bitmap transformed = createBitmap(bitmap, m);
Yuli Huang6a12ad72011-09-12 22:25:30 +0800153 bitmap.recycle();
154 return transformed;
155 }
156 }
157 return bitmap;
158 }
159
160 /**
Yuli Huang1d9a19d2011-12-02 17:35:34 +0800161 * Gets decoded bitmap (maybe immutable) that keeps orientation as well.
Yuli Huang6a12ad72011-09-12 22:25:30 +0800162 */
163 public Bitmap getBitmap(Uri uri, int width, int height) {
164 Bitmap bitmap = decodeBitmap(uri, width, height);
165
166 // Rotate the decoded bitmap according to its orientation if it's necessary.
167 if (bitmap != null) {
168 int orientation = getOrientation(uri);
169 if (orientation != 0) {
170 Matrix m = new Matrix();
171 m.setRotate(orientation);
Yuli Huang1d9a19d2011-12-02 17:35:34 +0800172 Bitmap transformed = createBitmap(bitmap, m);
Yuli Huang6a12ad72011-09-12 22:25:30 +0800173 bitmap.recycle();
174 return transformed;
175 }
176 }
177 return bitmap;
178 }
179
180 /**
181 * Saves the bitmap by given directory, filename, and format; if the directory is given null,
182 * then saves it under the cache directory.
183 */
Yuli Huang03669482012-04-10 18:51:36 +0800184 public File saveBitmap(Bitmap bitmap, File directory, String filename, CompressFormat format) {
Yuli Huang6a12ad72011-09-12 22:25:30 +0800185
186 if (directory == null) {
Yuli Huang03669482012-04-10 18:51:36 +0800187 directory = context.getCacheDir();
Yuli Huang6a12ad72011-09-12 22:25:30 +0800188 } else {
189 // Check if the given directory exists or try to create it.
Yuli Huang03669482012-04-10 18:51:36 +0800190 if (!directory.isDirectory() && !directory.mkdirs()) {
Yuli Huang6a12ad72011-09-12 22:25:30 +0800191 return null;
192 }
193 }
194
195 File file = null;
196 OutputStream os = null;
197
198 try {
199 filename = (format == CompressFormat.PNG) ? filename + ".png" : filename + ".jpg";
200 file = new File(directory, filename);
201 os = new FileOutputStream(file);
202 bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, os);
203 } catch (FileNotFoundException e) {
204 e.printStackTrace();
205 } finally {
206 closeStream(os);
207 }
208 return file;
209 }
210}