[tx-robot] updated from transifex
[pub/Android/ownCloud.git] / src / com / owncloud / android / utils / BitmapUtils.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author David A. Velasco
5 * Copyright (C) 2015 ownCloud Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2,
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20 package com.owncloud.android.utils;
21
22 import com.owncloud.android.lib.common.utils.Log_OC;
23
24 import android.graphics.Bitmap;
25 import android.graphics.BitmapFactory;
26 import android.graphics.Matrix;
27 import android.graphics.BitmapFactory.Options;
28 import android.media.ExifInterface;
29 import android.net.Uri;
30 import android.webkit.MimeTypeMap;
31
32 import java.io.File;
33
34 /**
35 * Utility class with methods for decoding Bitmaps.
36 */
37 public class BitmapUtils {
38
39
40 /**
41 * Decodes a bitmap from a file containing it minimizing the memory use, known that the bitmap
42 * will be drawn in a surface of reqWidth x reqHeight
43 *
44 * @param srcPath Absolute path to the file containing the image.
45 * @param reqWidth Width of the surface where the Bitmap will be drawn on, in pixels.
46 * @param reqHeight Height of the surface where the Bitmap will be drawn on, in pixels.
47 * @return
48 */
49 public static Bitmap decodeSampledBitmapFromFile(String srcPath, int reqWidth, int reqHeight) {
50
51 // set desired options that will affect the size of the bitmap
52 final Options options = new Options();
53 options.inScaled = true;
54 options.inPurgeable = true;
55 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
56 options.inPreferQualityOverSpeed = false;
57 }
58 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
59 options.inMutable = false;
60 }
61
62 // make a false load of the bitmap to get its dimensions
63 options.inJustDecodeBounds = true;
64
65 BitmapFactory.decodeFile(srcPath, options);
66
67 // calculate factor to subsample the bitmap
68 options.inSampleSize = calculateSampleFactor(options, reqWidth, reqHeight);
69
70 // decode bitmap with inSampleSize set
71 options.inJustDecodeBounds = false;
72 return BitmapFactory.decodeFile(srcPath, options);
73
74 }
75
76
77 /**
78 * Calculates a proper value for options.inSampleSize in order to decode a Bitmap minimizing
79 * the memory overload and covering a target surface of reqWidth x reqHeight if the original
80 * image is big enough.
81 *
82 * @param options Bitmap decoding options; options.outHeight and options.inHeight should
83 * be set.
84 * @param reqWidth Width of the surface where the Bitmap will be drawn on, in pixels.
85 * @param reqHeight Height of the surface where the Bitmap will be drawn on, in pixels.
86 * @return The largest inSampleSize value that is a power of 2 and keeps both
87 * height and width larger than reqWidth and reqHeight.
88 */
89 private static int calculateSampleFactor(Options options, int reqWidth, int reqHeight) {
90
91 final int height = options.outHeight;
92 final int width = options.outWidth;
93 int inSampleSize = 1;
94
95 if (height > reqHeight || width > reqWidth) {
96 final int halfHeight = height / 2;
97 final int halfWidth = width / 2;
98
99 // calculates the largest inSampleSize value (for smallest sample) that is a power of 2 and keeps both
100 // height and width **larger** than the requested height and width.
101 while ((halfHeight / inSampleSize) > reqHeight
102 && (halfWidth / inSampleSize) > reqWidth) {
103 inSampleSize *= 2;
104 }
105 }
106
107 return inSampleSize;
108 }
109
110 /**
111 * Rotate bitmap according to EXIF orientation.
112 * Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
113 * @param bitmap Bitmap to be rotated
114 * @param storagePath Path to source file of bitmap. Needed for EXIF information.
115 * @return correctly EXIF-rotated bitmap
116 */
117 public static Bitmap rotateImage(Bitmap bitmap, String storagePath){
118 Bitmap resultBitmap = bitmap;
119
120 try
121 {
122 ExifInterface exifInterface = new ExifInterface(storagePath);
123 int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
124
125 Matrix matrix = new Matrix();
126
127 // 1: nothing to do
128
129 // 2
130 if (orientation == ExifInterface.ORIENTATION_FLIP_HORIZONTAL)
131 {
132 matrix.postScale(-1.0f, 1.0f);
133 }
134 // 3
135 else if (orientation == ExifInterface.ORIENTATION_ROTATE_180)
136 {
137 matrix.postRotate(180);
138 }
139 // 4
140 else if (orientation == ExifInterface.ORIENTATION_FLIP_VERTICAL)
141 {
142 matrix.postScale(1.0f, -1.0f);
143 }
144 // 5
145 else if (orientation == ExifInterface.ORIENTATION_TRANSPOSE)
146 {
147 matrix.postRotate(-90);
148 matrix.postScale(1.0f, -1.0f);
149 }
150 // 6
151 else if (orientation == ExifInterface.ORIENTATION_ROTATE_90)
152 {
153 matrix.postRotate(90);
154 }
155 // 7
156 else if (orientation == ExifInterface.ORIENTATION_TRANSVERSE)
157 {
158 matrix.postRotate(90);
159 matrix.postScale(1.0f, -1.0f);
160 }
161 // 8
162 else if (orientation == ExifInterface.ORIENTATION_ROTATE_270)
163 {
164 matrix.postRotate(270);
165 }
166
167 // Rotate the bitmap
168 resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
169 if (resultBitmap != bitmap) {
170 bitmap.recycle();
171 }
172 }
173 catch (Exception exception)
174 {
175 Log_OC.e("BitmapUtil", "Could not rotate the image: " + storagePath);
176 }
177 return resultBitmap;
178 }
179
180 /**
181 * Convert HSL values to a RGB Color.
182 *
183 * @param h Hue is specified as degrees in the range 0 - 360.
184 * @param s Saturation is specified as a percentage in the range 1 - 100.
185 * @param l Lumanance is specified as a percentage in the range 1 - 100.
186 * @paran alpha the alpha value between 0 - 1
187 * adapted from https://svn.codehaus.org/griffon/builders/gfxbuilder/tags/GFXBUILDER_0.2/
188 * gfxbuilder-core/src/main/com/camick/awt/HSLColor.java
189 */
190 public static int[] HSLtoRGB(float h, float s, float l, float alpha)
191 {
192 if (s <0.0f || s > 100.0f)
193 {
194 String message = "Color parameter outside of expected range - Saturation";
195 throw new IllegalArgumentException( message );
196 }
197
198 if (l <0.0f || l > 100.0f)
199 {
200 String message = "Color parameter outside of expected range - Luminance";
201 throw new IllegalArgumentException( message );
202 }
203
204 if (alpha <0.0f || alpha > 1.0f)
205 {
206 String message = "Color parameter outside of expected range - Alpha";
207 throw new IllegalArgumentException( message );
208 }
209
210 // Formula needs all values between 0 - 1.
211
212 h = h % 360.0f;
213 h /= 360f;
214 s /= 100f;
215 l /= 100f;
216
217 float q = 0;
218
219 if (l < 0.5)
220 q = l * (1 + s);
221 else
222 q = (l + s) - (s * l);
223
224 float p = 2 * l - q;
225
226 int r = Math.round(Math.max(0, HueToRGB(p, q, h + (1.0f / 3.0f)) * 256));
227 int g = Math.round(Math.max(0, HueToRGB(p, q, h) * 256));
228 int b = Math.round(Math.max(0, HueToRGB(p, q, h - (1.0f / 3.0f)) * 256));
229
230 int[] array = {r, g, b};
231 return array;
232 }
233
234 private static float HueToRGB(float p, float q, float h){
235 if (h < 0) h += 1;
236
237 if (h > 1 ) h -= 1;
238
239 if (6 * h < 1)
240 {
241 return p + ((q - p) * 6 * h);
242 }
243
244 if (2 * h < 1 )
245 {
246 return q;
247 }
248
249 if (3 * h < 2)
250 {
251 return p + ( (q - p) * 6 * ((2.0f / 3.0f) - h) );
252 }
253
254 return p;
255 }
256
257 /**
258 * Checks if file passed is an image
259 * @param file
260 * @return true/false
261 */
262 public static boolean isImage(File file) {
263 Uri selectedUri = Uri.fromFile(file);
264 String fileExtension = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString().toLowerCase());
265 String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
266
267 return (mimeType != null && mimeType.startsWith("image/"));
268 }
269
270 }