2 * ownCloud Android client application
4 * @author David A. Velasco
5 * Copyright (C) 2015 ownCloud Inc.
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.
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.
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/>.
20 package com
.owncloud
.android
.utils
;
22 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
;
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
;
35 * Utility class with methods for decoding Bitmaps.
37 public class BitmapUtils
{
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
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.
49 public static Bitmap
decodeSampledBitmapFromFile(String srcPath
, int reqWidth
, int reqHeight
) {
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
;
58 if (android
.os
.Build
.VERSION
.SDK_INT
>= android
.os
.Build
.VERSION_CODES
.HONEYCOMB
) {
59 options
.inMutable
= false
;
62 // make a false load of the bitmap to get its dimensions
63 options
.inJustDecodeBounds
= true
;
65 BitmapFactory
.decodeFile(srcPath
, options
);
67 // calculate factor to subsample the bitmap
68 options
.inSampleSize
= calculateSampleFactor(options
, reqWidth
, reqHeight
);
70 // decode bitmap with inSampleSize set
71 options
.inJustDecodeBounds
= false
;
72 return BitmapFactory
.decodeFile(srcPath
, options
);
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.
82 * @param options Bitmap decoding options; options.outHeight and options.inHeight should
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.
89 private static int calculateSampleFactor(Options options
, int reqWidth
, int reqHeight
) {
91 final int height
= options
.outHeight
;
92 final int width
= options
.outWidth
;
95 if (height
> reqHeight
|| width
> reqWidth
) {
96 final int halfHeight
= height
/ 2;
97 final int halfWidth
= width
/ 2;
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
) {
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
117 public static Bitmap
rotateImage(Bitmap bitmap
, String storagePath
){
118 Bitmap resultBitmap
= bitmap
;
122 ExifInterface exifInterface
= new ExifInterface(storagePath
);
123 int orientation
= exifInterface
.getAttributeInt(ExifInterface
.TAG_ORIENTATION
, 1);
125 Matrix matrix
= new Matrix();
130 if (orientation
== ExifInterface
.ORIENTATION_FLIP_HORIZONTAL
)
132 matrix
.postScale(-1.0f
, 1.0f
);
135 else if (orientation
== ExifInterface
.ORIENTATION_ROTATE_180
)
137 matrix
.postRotate(180);
140 else if (orientation
== ExifInterface
.ORIENTATION_FLIP_VERTICAL
)
142 matrix
.postScale(1.0f
, -1.0f
);
145 else if (orientation
== ExifInterface
.ORIENTATION_TRANSPOSE
)
147 matrix
.postRotate(-90);
148 matrix
.postScale(1.0f
, -1.0f
);
151 else if (orientation
== ExifInterface
.ORIENTATION_ROTATE_90
)
153 matrix
.postRotate(90);
156 else if (orientation
== ExifInterface
.ORIENTATION_TRANSVERSE
)
158 matrix
.postRotate(90);
159 matrix
.postScale(1.0f
, -1.0f
);
162 else if (orientation
== ExifInterface
.ORIENTATION_ROTATE_270
)
164 matrix
.postRotate(270);
168 resultBitmap
= Bitmap
.createBitmap(bitmap
, 0, 0, bitmap
.getWidth(), bitmap
.getHeight(), matrix
, true
);
169 if (resultBitmap
!= bitmap
) {
173 catch (Exception exception
)
175 Log_OC
.e("BitmapUtil", "Could not rotate the image: " + storagePath
);
181 * Convert HSL values to a RGB Color.
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
190 public static int[] HSLtoRGB(float h
, float s
, float l
, float alpha
)
192 if (s
<0.0f
|| s
> 100.0f
)
194 String message
= "Color parameter outside of expected range - Saturation";
195 throw new IllegalArgumentException( message
);
198 if (l
<0.0f
|| l
> 100.0f
)
200 String message
= "Color parameter outside of expected range - Luminance";
201 throw new IllegalArgumentException( message
);
204 if (alpha
<0.0f
|| alpha
> 1.0f
)
206 String message
= "Color parameter outside of expected range - Alpha";
207 throw new IllegalArgumentException( message
);
210 // Formula needs all values between 0 - 1.
222 q
= (l
+ s
) - (s
* l
);
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));
230 int[] array
= {r
, g
, b
};
234 private static float HueToRGB(float p
, float q
, float h
){
241 return p
+ ((q
- p
) * 6 * h
);
251 return p
+ ( (q
- p
) * 6 * ((2.0f
/ 3.0f
) - h
) );
258 * Checks if file passed is an image
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
);
267 return (mimeType
!= null
&& mimeType
.startsWith("image/"));