Merge pull request #866 from owncloud/download_folder
[pub/Android/ownCloud.git] / src / com / owncloud / android / utils / BitmapUtils.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012-2014 ownCloud Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17 package com.owncloud.android.utils;
18
19 import com.owncloud.android.lib.common.utils.Log_OC;
20
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.graphics.Matrix;
24 import android.graphics.BitmapFactory.Options;
25 import android.media.ExifInterface;
26 import android.net.Uri;
27 import android.webkit.MimeTypeMap;
28
29 import java.io.File;
30
31 /**
32 * Utility class with methods for decoding Bitmaps.
33 *
34 * @author David A. Velasco
35 */
36 public class BitmapUtils {
37
38
39 /**
40 * Decodes a bitmap from a file containing it minimizing the memory use, known that the bitmap
41 * will be drawn in a surface of reqWidth x reqHeight
42 *
43 * @param srcPath Absolute path to the file containing the image.
44 * @param reqWidth Width of the surface where the Bitmap will be drawn on, in pixels.
45 * @param reqHeight Height of the surface where the Bitmap will be drawn on, in pixels.
46 * @return
47 */
48 public static Bitmap decodeSampledBitmapFromFile(String srcPath, int reqWidth, int reqHeight) {
49
50 // set desired options that will affect the size of the bitmap
51 final Options options = new Options();
52 options.inScaled = true;
53 options.inPurgeable = true;
54 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
55 options.inPreferQualityOverSpeed = false;
56 }
57 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
58 options.inMutable = false;
59 }
60
61 // make a false load of the bitmap to get its dimensions
62 options.inJustDecodeBounds = true;
63
64 BitmapFactory.decodeFile(srcPath, options);
65
66 // calculate factor to subsample the bitmap
67 options.inSampleSize = calculateSampleFactor(options, reqWidth, reqHeight);
68
69 // decode bitmap with inSampleSize set
70 options.inJustDecodeBounds = false;
71 return BitmapFactory.decodeFile(srcPath, options);
72
73 }
74
75
76 /**
77 * Calculates a proper value for options.inSampleSize in order to decode a Bitmap minimizing
78 * the memory overload and covering a target surface of reqWidth x reqHeight if the original
79 * image is big enough.
80 *
81 * @param options Bitmap decoding options; options.outHeight and options.inHeight should
82 * be set.
83 * @param reqWidth Width of the surface where the Bitmap will be drawn on, in pixels.
84 * @param reqHeight Height of the surface where the Bitmap will be drawn on, in pixels.
85 * @return The largest inSampleSize value that is a power of 2 and keeps both
86 * height and width larger than reqWidth and reqHeight.
87 */
88 private static int calculateSampleFactor(Options options, int reqWidth, int reqHeight) {
89
90 final int height = options.outHeight;
91 final int width = options.outWidth;
92 int inSampleSize = 1;
93
94 if (height > reqHeight || width > reqWidth) {
95 final int halfHeight = height / 2;
96 final int halfWidth = width / 2;
97
98 while ((halfHeight / inSampleSize) > reqHeight
99 && (halfWidth / inSampleSize) > reqWidth) {
100 inSampleSize *= 2;
101 }
102 }
103
104 return inSampleSize;
105 }
106
107 /**
108 * Rotate bitmap according to EXIF orientation.
109 * Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
110 * @param bitmap Bitmap to be rotated
111 * @param storagePath Path to source file of bitmap. Needed for EXIF information.
112 * @return correctly EXIF-rotated bitmap
113 */
114 public static Bitmap rotateImage(Bitmap bitmap, String storagePath){
115 Bitmap resultBitmap = bitmap;
116
117 try
118 {
119 ExifInterface exifInterface = new ExifInterface(storagePath);
120 int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
121
122 Matrix matrix = new Matrix();
123
124 // 1: nothing to do
125
126 // 2
127 if (orientation == ExifInterface.ORIENTATION_FLIP_HORIZONTAL)
128 {
129 matrix.postScale(-1.0f, 1.0f);
130 }
131 // 3
132 else if (orientation == ExifInterface.ORIENTATION_ROTATE_180)
133 {
134 matrix.postRotate(180);
135 }
136 // 4
137 else if (orientation == ExifInterface.ORIENTATION_FLIP_VERTICAL)
138 {
139 matrix.postScale(1.0f, -1.0f);
140 }
141 // 5
142 else if (orientation == ExifInterface.ORIENTATION_TRANSPOSE)
143 {
144 matrix.postRotate(-90);
145 matrix.postScale(1.0f, -1.0f);
146 }
147 // 6
148 else if (orientation == ExifInterface.ORIENTATION_ROTATE_90)
149 {
150 matrix.postRotate(90);
151 }
152 // 7
153 else if (orientation == ExifInterface.ORIENTATION_TRANSVERSE)
154 {
155 matrix.postRotate(90);
156 matrix.postScale(1.0f, -1.0f);
157 }
158 // 8
159 else if (orientation == ExifInterface.ORIENTATION_ROTATE_270)
160 {
161 matrix.postRotate(270);
162 }
163
164 // Rotate the bitmap
165 resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
166 if (resultBitmap != bitmap) {
167 bitmap.recycle();
168 }
169 }
170 catch (Exception exception)
171 {
172 Log_OC.e("BitmapUtil", "Could not rotate the image: " + storagePath);
173 }
174 return resultBitmap;
175 }
176
177 /**
178 * Checks if file passed is an image
179 * @param file
180 * @return true/false
181 */
182 public static boolean isImage(File file) {
183 Uri selectedUri = Uri.fromFile(file);
184 String fileExtension = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString().toLowerCase());
185 String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
186
187 return (mimeType != null && mimeType.startsWith("image/"));
188 }
189
190 }