/packages/WallpaperCropper/src/com/android/gallery3d/common/BitmapUtils.java

https://github.com/aizuzi/platform_frameworks_base · Java · 260 lines · 185 code · 30 blank · 45 comment · 41 complexity · 40a83813f3cccbcc465a4487884c5720 MD5 · raw file

  1. /*
  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. package com.android.gallery3d.common;
  17. import android.graphics.Bitmap;
  18. import android.graphics.Bitmap.CompressFormat;
  19. import android.graphics.BitmapFactory;
  20. import android.graphics.Canvas;
  21. import android.graphics.Matrix;
  22. import android.graphics.Paint;
  23. import android.os.Build;
  24. import android.util.FloatMath;
  25. import android.util.Log;
  26. import java.io.ByteArrayOutputStream;
  27. import java.lang.reflect.InvocationTargetException;
  28. import java.lang.reflect.Method;
  29. public class BitmapUtils {
  30. private static final String TAG = "BitmapUtils";
  31. private static final int DEFAULT_JPEG_QUALITY = 90;
  32. public static final int UNCONSTRAINED = -1;
  33. private BitmapUtils(){}
  34. /*
  35. * Compute the sample size as a function of minSideLength
  36. * and maxNumOfPixels.
  37. * minSideLength is used to specify that minimal width or height of a
  38. * bitmap.
  39. * maxNumOfPixels is used to specify the maximal size in pixels that is
  40. * tolerable in terms of memory usage.
  41. *
  42. * The function returns a sample size based on the constraints.
  43. * Both size and minSideLength can be passed in as UNCONSTRAINED,
  44. * which indicates no care of the corresponding constraint.
  45. * The functions prefers returning a sample size that
  46. * generates a smaller bitmap, unless minSideLength = UNCONSTRAINED.
  47. *
  48. * Also, the function rounds up the sample size to a power of 2 or multiple
  49. * of 8 because BitmapFactory only honors sample size this way.
  50. * For example, BitmapFactory downsamples an image by 2 even though the
  51. * request is 3. So we round up the sample size to avoid OOM.
  52. */
  53. public static int computeSampleSize(int width, int height,
  54. int minSideLength, int maxNumOfPixels) {
  55. int initialSize = computeInitialSampleSize(
  56. width, height, minSideLength, maxNumOfPixels);
  57. return initialSize <= 8
  58. ? Utils.nextPowerOf2(initialSize)
  59. : (initialSize + 7) / 8 * 8;
  60. }
  61. private static int computeInitialSampleSize(int w, int h,
  62. int minSideLength, int maxNumOfPixels) {
  63. if (maxNumOfPixels == UNCONSTRAINED
  64. && minSideLength == UNCONSTRAINED) return 1;
  65. int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
  66. (int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels));
  67. if (minSideLength == UNCONSTRAINED) {
  68. return lowerBound;
  69. } else {
  70. int sampleSize = Math.min(w / minSideLength, h / minSideLength);
  71. return Math.max(sampleSize, lowerBound);
  72. }
  73. }
  74. // This computes a sample size which makes the longer side at least
  75. // minSideLength long. If that's not possible, return 1.
  76. public static int computeSampleSizeLarger(int w, int h,
  77. int minSideLength) {
  78. int initialSize = Math.max(w / minSideLength, h / minSideLength);
  79. if (initialSize <= 1) return 1;
  80. return initialSize <= 8
  81. ? Utils.prevPowerOf2(initialSize)
  82. : initialSize / 8 * 8;
  83. }
  84. // Find the min x that 1 / x >= scale
  85. public static int computeSampleSizeLarger(float scale) {
  86. int initialSize = (int) FloatMath.floor(1f / scale);
  87. if (initialSize <= 1) return 1;
  88. return initialSize <= 8
  89. ? Utils.prevPowerOf2(initialSize)
  90. : initialSize / 8 * 8;
  91. }
  92. // Find the max x that 1 / x <= scale.
  93. public static int computeSampleSize(float scale) {
  94. Utils.assertTrue(scale > 0);
  95. int initialSize = Math.max(1, (int) FloatMath.ceil(1 / scale));
  96. return initialSize <= 8
  97. ? Utils.nextPowerOf2(initialSize)
  98. : (initialSize + 7) / 8 * 8;
  99. }
  100. public static Bitmap resizeBitmapByScale(
  101. Bitmap bitmap, float scale, boolean recycle) {
  102. int width = Math.round(bitmap.getWidth() * scale);
  103. int height = Math.round(bitmap.getHeight() * scale);
  104. if (width == bitmap.getWidth()
  105. && height == bitmap.getHeight()) return bitmap;
  106. Bitmap target = Bitmap.createBitmap(width, height, getConfig(bitmap));
  107. Canvas canvas = new Canvas(target);
  108. canvas.scale(scale, scale);
  109. Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
  110. canvas.drawBitmap(bitmap, 0, 0, paint);
  111. if (recycle) bitmap.recycle();
  112. return target;
  113. }
  114. private static Bitmap.Config getConfig(Bitmap bitmap) {
  115. Bitmap.Config config = bitmap.getConfig();
  116. if (config == null) {
  117. config = Bitmap.Config.ARGB_8888;
  118. }
  119. return config;
  120. }
  121. public static Bitmap resizeDownBySideLength(
  122. Bitmap bitmap, int maxLength, boolean recycle) {
  123. int srcWidth = bitmap.getWidth();
  124. int srcHeight = bitmap.getHeight();
  125. float scale = Math.min(
  126. (float) maxLength / srcWidth, (float) maxLength / srcHeight);
  127. if (scale >= 1.0f) return bitmap;
  128. return resizeBitmapByScale(bitmap, scale, recycle);
  129. }
  130. public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
  131. int w = bitmap.getWidth();
  132. int h = bitmap.getHeight();
  133. if (w == size && h == size) return bitmap;
  134. // scale the image so that the shorter side equals to the target;
  135. // the longer side will be center-cropped.
  136. float scale = (float) size / Math.min(w, h);
  137. Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
  138. int width = Math.round(scale * bitmap.getWidth());
  139. int height = Math.round(scale * bitmap.getHeight());
  140. Canvas canvas = new Canvas(target);
  141. canvas.translate((size - width) / 2f, (size - height) / 2f);
  142. canvas.scale(scale, scale);
  143. Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
  144. canvas.drawBitmap(bitmap, 0, 0, paint);
  145. if (recycle) bitmap.recycle();
  146. return target;
  147. }
  148. public static void recycleSilently(Bitmap bitmap) {
  149. if (bitmap == null) return;
  150. try {
  151. bitmap.recycle();
  152. } catch (Throwable t) {
  153. Log.w(TAG, "unable recycle bitmap", t);
  154. }
  155. }
  156. public static Bitmap rotateBitmap(Bitmap source, int rotation, boolean recycle) {
  157. if (rotation == 0) return source;
  158. int w = source.getWidth();
  159. int h = source.getHeight();
  160. Matrix m = new Matrix();
  161. m.postRotate(rotation);
  162. Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, w, h, m, true);
  163. if (recycle) source.recycle();
  164. return bitmap;
  165. }
  166. public static Bitmap createVideoThumbnail(String filePath) {
  167. // MediaMetadataRetriever is available on API Level 8
  168. // but is hidden until API Level 10
  169. Class<?> clazz = null;
  170. Object instance = null;
  171. try {
  172. clazz = Class.forName("android.media.MediaMetadataRetriever");
  173. instance = clazz.newInstance();
  174. Method method = clazz.getMethod("setDataSource", String.class);
  175. method.invoke(instance, filePath);
  176. // The method name changes between API Level 9 and 10.
  177. if (Build.VERSION.SDK_INT <= 9) {
  178. return (Bitmap) clazz.getMethod("captureFrame").invoke(instance);
  179. } else {
  180. byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
  181. if (data != null) {
  182. Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
  183. if (bitmap != null) return bitmap;
  184. }
  185. return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
  186. }
  187. } catch (IllegalArgumentException ex) {
  188. // Assume this is a corrupt video file
  189. } catch (RuntimeException ex) {
  190. // Assume this is a corrupt video file.
  191. } catch (InstantiationException e) {
  192. Log.e(TAG, "createVideoThumbnail", e);
  193. } catch (InvocationTargetException e) {
  194. Log.e(TAG, "createVideoThumbnail", e);
  195. } catch (ClassNotFoundException e) {
  196. Log.e(TAG, "createVideoThumbnail", e);
  197. } catch (NoSuchMethodException e) {
  198. Log.e(TAG, "createVideoThumbnail", e);
  199. } catch (IllegalAccessException e) {
  200. Log.e(TAG, "createVideoThumbnail", e);
  201. } finally {
  202. try {
  203. if (instance != null) {
  204. clazz.getMethod("release").invoke(instance);
  205. }
  206. } catch (Exception ignored) {
  207. }
  208. }
  209. return null;
  210. }
  211. public static byte[] compressToBytes(Bitmap bitmap) {
  212. return compressToBytes(bitmap, DEFAULT_JPEG_QUALITY);
  213. }
  214. public static byte[] compressToBytes(Bitmap bitmap, int quality) {
  215. ByteArrayOutputStream baos = new ByteArrayOutputStream(65536);
  216. bitmap.compress(CompressFormat.JPEG, quality, baos);
  217. return baos.toByteArray();
  218. }
  219. public static boolean isSupportedByRegionDecoder(String mimeType) {
  220. if (mimeType == null) return false;
  221. mimeType = mimeType.toLowerCase();
  222. return mimeType.startsWith("image/") &&
  223. (!mimeType.equals("image/gif") && !mimeType.endsWith("bmp"));
  224. }
  225. public static boolean isRotationSupported(String mimeType) {
  226. if (mimeType == null) return false;
  227. mimeType = mimeType.toLowerCase();
  228. return mimeType.equals("image/jpeg");
  229. }
  230. }