Merge branch 'develop' into rename_with_special_character
[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
27 /**
28 * Utility class with methods for decoding Bitmaps.
29 *
30 * @author David A. Velasco
31 */
32 public class BitmapUtils {
33
34
35 /**
36 * Decodes a bitmap from a file containing it minimizing the memory use, known that the bitmap
37 * will be drawn in a surface of reqWidth x reqHeight
38 *
39 * @param srcPath Absolute path to the file containing the image.
40 * @param reqWidth Width of the surface where the Bitmap will be drawn on, in pixels.
41 * @param reqHeight Height of the surface where the Bitmap will be drawn on, in pixels.
42 * @return
43 */
44 public static Bitmap decodeSampledBitmapFromFile(String srcPath, int reqWidth, int reqHeight) {
45
46 // set desired options that will affect the size of the bitmap
47 final Options options = new Options();
48 options.inScaled = true;
49 options.inPurgeable = true;
50 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
51 options.inPreferQualityOverSpeed = false;
52 }
53 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
54 options.inMutable = false;
55 }
56
57 // make a false load of the bitmap to get its dimensions
58 options.inJustDecodeBounds = true;
59
60 BitmapFactory.decodeFile(srcPath, options);
61
62 // calculate factor to subsample the bitmap
63 options.inSampleSize = calculateSampleFactor(options, reqWidth, reqHeight);
64
65 // decode bitmap with inSampleSize set
66 options.inJustDecodeBounds = false;
67 return BitmapFactory.decodeFile(srcPath, options);
68
69 }
70
71
72 /**
73 * Calculates a proper value for options.inSampleSize in order to decode a Bitmap minimizing
74 * the memory overload and covering a target surface of reqWidth x reqHeight if the original
75 * image is big enough.
76 *
77 * @param options Bitmap decoding options; options.outHeight and options.inHeight should
78 * be set.
79 * @param reqWidth Width of the surface where the Bitmap will be drawn on, in pixels.
80 * @param reqHeight Height of the surface where the Bitmap will be drawn on, in pixels.
81 * @return The largest inSampleSize value that is a power of 2 and keeps both
82 * height and width larger than reqWidth and reqHeight.
83 */
84 private static int calculateSampleFactor(Options options, int reqWidth, int reqHeight) {
85
86 final int height = options.outHeight;
87 final int width = options.outWidth;
88 int inSampleSize = 1;
89
90 if (height > reqHeight || width > reqWidth) {
91 final int halfHeight = height / 2;
92 final int halfWidth = width / 2;
93
94 while ((halfHeight / inSampleSize) > reqHeight
95 && (halfWidth / inSampleSize) > reqWidth) {
96 inSampleSize *= 2;
97 }
98 }
99
100 return inSampleSize;
101 }
102
103 /**
104 * Rotate bitmap according to EXIF orientation.
105 * Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
106 * @param bitmap Bitmap to be rotated
107 * @param storagePath Path to source file of bitmap. Needed for EXIF information.
108 * @return correctly EXIF-rotated bitmap
109 */
110 public static Bitmap rotateImage(Bitmap bitmap, String storagePath){
111 Bitmap resultBitmap = bitmap;
112
113 try
114 {
115 ExifInterface exifInterface = new ExifInterface(storagePath);
116 int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
117
118 Matrix matrix = new Matrix();
119
120 // 1: nothing to do
121
122 // 2
123 if (orientation == ExifInterface.ORIENTATION_FLIP_HORIZONTAL)
124 {
125 matrix.postScale(-1.0f, 1.0f);
126 }
127 // 3
128 else if (orientation == ExifInterface.ORIENTATION_ROTATE_180)
129 {
130 matrix.postRotate(180);
131 }
132 // 4
133 else if (orientation == ExifInterface.ORIENTATION_FLIP_VERTICAL)
134 {
135 matrix.postScale(1.0f, -1.0f);
136 }
137 // 5
138 else if (orientation == ExifInterface.ORIENTATION_TRANSPOSE)
139 {
140 matrix.postRotate(-90);
141 matrix.postScale(1.0f, -1.0f);
142 }
143 // 6
144 else if (orientation == ExifInterface.ORIENTATION_ROTATE_90)
145 {
146 matrix.postRotate(90);
147 }
148 // 7
149 else if (orientation == ExifInterface.ORIENTATION_TRANSVERSE)
150 {
151 matrix.postRotate(90);
152 matrix.postScale(1.0f, -1.0f);
153 }
154 // 8
155 else if (orientation == ExifInterface.ORIENTATION_ROTATE_270)
156 {
157 matrix.postRotate(270);
158 }
159
160 // Rotate the bitmap
161 resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
162 if (resultBitmap != bitmap) {
163 bitmap.recycle();
164 }
165 }
166 catch (Exception exception)
167 {
168 Log_OC.e("BitmapUtil", "Could not rotate the image: " + storagePath);
169 }
170 return resultBitmap;
171 }
172
173
174 }