Util.java revision fa021af9397011cf180003139e80a88902c4b70a
1/* 2 * Copyright (C) 2009 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 17package com.android.camera; 18 19import com.android.camera.gallery.IImage; 20 21import android.content.ContentResolver; 22import android.graphics.Bitmap; 23import android.graphics.BitmapFactory; 24import android.graphics.Canvas; 25import android.graphics.Matrix; 26import android.graphics.Rect; 27import android.media.MediaMetadataRetriever; 28import android.net.Uri; 29import android.os.ParcelFileDescriptor; 30import android.util.Log; 31 32import java.io.ByteArrayOutputStream; 33import java.io.Closeable; 34import java.io.FileDescriptor; 35import java.io.IOException; 36 37/** 38 * Collection of utility functions used in this package. 39 */ 40public class Util { 41 private static final String TAG = "db.Util"; 42 43 private Util() { 44 } 45 46 // Rotates the bitmap by the specified degree. 47 // If a new bitmap is created, the original bitmap is recycled. 48 public static Bitmap rotate(Bitmap b, int degrees) { 49 if (degrees != 0 && b != null) { 50 Matrix m = new Matrix(); 51 m.setRotate(degrees, 52 (float) b.getWidth() / 2, (float) b.getHeight() / 2); 53 try { 54 Bitmap b2 = Bitmap.createBitmap( 55 b, 0, 0, b.getWidth(), b.getHeight(), m, true); 56 if (b != b2) { 57 b.recycle(); 58 b = b2; 59 } 60 } catch (OutOfMemoryError ex) { 61 // We have no memory to rotate. Return the original bitmap. 62 } 63 } 64 return b; 65 } 66 67 /* 68 * Compute the sample size as a function of the image size and the target. 69 * Scale the image down so that both the width and height are just above the 70 * target. If this means that one of the dimension goes from above the 71 * target to below the target (e.g. given a width of 480 and an image width 72 * of 600 but sample size of 2 -- i.e. new width 300 -- bump the sample size 73 * down by 1. 74 */ 75 public static int computeSampleSize( 76 BitmapFactory.Options options, int target) { 77 int w = options.outWidth; 78 int h = options.outHeight; 79 80 int candidateW = w / target; 81 int candidateH = h / target; 82 int candidate = Math.max(candidateW, candidateH); 83 84 if (candidate == 0) return 1; 85 86 if (candidate > 1) { 87 if ((w > target) && (w / candidate) < target) candidate -= 1; 88 } 89 90 if (candidate > 1) { 91 if ((h > target) && (h / candidate) < target) candidate -= 1; 92 } 93 94 return candidate; 95 } 96 97 public static Bitmap transform(Matrix scaler, 98 Bitmap source, 99 int targetWidth, 100 int targetHeight, 101 boolean scaleUp) { 102 int deltaX = source.getWidth() - targetWidth; 103 int deltaY = source.getHeight() - targetHeight; 104 if (!scaleUp && (deltaX < 0 || deltaY < 0)) { 105 /* 106 * In this case the bitmap is smaller, at least in one dimension, 107 * than the target. Transform it by placing as much of the image 108 * as possible into the target and leaving the top/bottom or 109 * left/right (or both) black. 110 */ 111 Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight, 112 Bitmap.Config.ARGB_8888); 113 Canvas c = new Canvas(b2); 114 115 int deltaXHalf = Math.max(0, deltaX / 2); 116 int deltaYHalf = Math.max(0, deltaY / 2); 117 Rect src = new Rect( 118 deltaXHalf, 119 deltaYHalf, 120 deltaXHalf + Math.min(targetWidth, source.getWidth()), 121 deltaYHalf + Math.min(targetHeight, source.getHeight())); 122 int dstX = (targetWidth - src.width()) / 2; 123 int dstY = (targetHeight - src.height()) / 2; 124 Rect dst = new Rect( 125 dstX, 126 dstY, 127 targetWidth - dstX, 128 targetHeight - dstY); 129 c.drawBitmap(source, src, dst, null); 130 return b2; 131 } 132 float bitmapWidthF = source.getWidth(); 133 float bitmapHeightF = source.getHeight(); 134 135 float bitmapAspect = bitmapWidthF / bitmapHeightF; 136 float viewAspect = (float) targetWidth / targetHeight; 137 138 if (bitmapAspect > viewAspect) { 139 float scale = targetHeight / bitmapHeightF; 140 if (scale < .9F || scale > 1F) { 141 scaler.setScale(scale, scale); 142 } else { 143 scaler = null; 144 } 145 } else { 146 float scale = targetWidth / bitmapWidthF; 147 if (scale < .9F || scale > 1F) { 148 scaler.setScale(scale, scale); 149 } else { 150 scaler = null; 151 } 152 } 153 154 Bitmap b1; 155 if (scaler != null) { 156 // this is used for minithumb and crop, so we want to filter here. 157 b1 = Bitmap.createBitmap(source, 0, 0, 158 source.getWidth(), source.getHeight(), scaler, true); 159 } else { 160 b1 = source; 161 } 162 163 int dx1 = Math.max(0, b1.getWidth() - targetWidth); 164 int dy1 = Math.max(0, b1.getHeight() - targetHeight); 165 166 Bitmap b2 = Bitmap.createBitmap( 167 b1, 168 dx1 / 2, 169 dy1 / 2, 170 targetWidth, 171 targetHeight); 172 173 if (b1 != source) { 174 b1.recycle(); 175 } 176 177 return b2; 178 } 179 180 /** 181 * Creates a centered bitmap of the desired size. Recycles the input. 182 * @param source 183 */ 184 public static Bitmap extractMiniThumb( 185 Bitmap source, int width, int height) { 186 return Util.extractMiniThumb(source, width, height, true); 187 } 188 189 public static Bitmap extractMiniThumb( 190 Bitmap source, int width, int height, boolean recycle) { 191 if (source == null) { 192 return null; 193 } 194 195 float scale; 196 if (source.getWidth() < source.getHeight()) { 197 scale = width / (float) source.getWidth(); 198 } else { 199 scale = height / (float) source.getHeight(); 200 } 201 Matrix matrix = new Matrix(); 202 matrix.setScale(scale, scale); 203 Bitmap miniThumbnail = transform(matrix, source, width, height, false); 204 205 if (recycle && miniThumbnail != source) { 206 source.recycle(); 207 } 208 return miniThumbnail; 209 } 210 211 /** 212 * Creates a byte[] for a given bitmap of the desired size. Recycles the 213 * input bitmap. 214 */ 215 public static byte[] miniThumbData(Bitmap source) { 216 if (source == null) return null; 217 218 Bitmap miniThumbnail = extractMiniThumb( 219 source, IImage.MINI_THUMB_TARGET_SIZE, 220 IImage.MINI_THUMB_TARGET_SIZE); 221 222 ByteArrayOutputStream miniOutStream = new ByteArrayOutputStream(); 223 miniThumbnail.compress(Bitmap.CompressFormat.JPEG, 75, miniOutStream); 224 miniThumbnail.recycle(); 225 226 try { 227 miniOutStream.close(); 228 byte [] data = miniOutStream.toByteArray(); 229 return data; 230 } catch (java.io.IOException ex) { 231 Log.e(TAG, "got exception ex " + ex); 232 } 233 return null; 234 } 235 236 /** 237 * @return true if the mimetype is a video mimetype. 238 */ 239 public static boolean isVideoMimeType(String mimeType) { 240 return mimeType.startsWith("video/"); 241 } 242 243 /** 244 * Create a video thumbnail for a video. May return null if the video is 245 * corrupt. 246 * 247 * @param filePath 248 */ 249 public static Bitmap createVideoThumbnail(String filePath) { 250 Bitmap bitmap = null; 251 MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 252 try { 253 retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY); 254 retriever.setDataSource(filePath); 255 bitmap = retriever.captureFrame(); 256 } catch (IllegalArgumentException ex) { 257 // Assume this is a corrupt video file 258 } catch (RuntimeException ex) { 259 // Assume this is a corrupt video file. 260 } finally { 261 try { 262 retriever.release(); 263 } catch (RuntimeException ex) { 264 // Ignore failures while cleaning up. 265 } 266 } 267 return bitmap; 268 } 269 270 public static int indexOf(String [] array, String s) { 271 for (int i = 0; i < array.length; i++) { 272 if (array[i].equals(s)) { 273 return i; 274 } 275 } 276 return -1; 277 } 278 279 public static void closeSiliently(Closeable c) { 280 if (c == null) return; 281 try { 282 c.close(); 283 } catch (Throwable t) { 284 // do nothing 285 } 286 } 287 288 public static void closeSiliently(ParcelFileDescriptor c) { 289 if (c == null) return; 290 try { 291 c.close(); 292 } catch (Throwable t) { 293 // do nothing 294 } 295 } 296 297 /** 298 * Make a bitmap from a given Uri. 299 * 300 * @param uri 301 */ 302 public static Bitmap makeBitmap(int targetWidthOrHeight, Uri uri, 303 ContentResolver cr) { 304 ParcelFileDescriptor input = null; 305 try { 306 input = cr.openFileDescriptor(uri, "r"); 307 return makeBitmap(targetWidthOrHeight, uri, cr, input, null); 308 } catch (IOException ex) { 309 return null; 310 } finally { 311 closeSiliently(input); 312 } 313 } 314 315 public static Bitmap makeBitmap(int targetWidthHeight, Uri uri, 316 ContentResolver cr, ParcelFileDescriptor pfd, 317 BitmapFactory.Options options) { 318 Bitmap b = null; 319 try { 320 if (pfd == null) pfd = makeInputStream(uri, cr); 321 if (pfd == null) return null; 322 if (options == null) options = new BitmapFactory.Options(); 323 324 FileDescriptor fd = pfd.getFileDescriptor(); 325 options.inSampleSize = 1; 326 if (targetWidthHeight != -1) { 327 options.inJustDecodeBounds = true; 328 BitmapManager.instance().decodeFileDescriptor(fd, options); 329 if (options.mCancel || options.outWidth == -1 330 || options.outHeight == -1) { 331 return null; 332 } 333 options.inSampleSize = 334 computeSampleSize(options, targetWidthHeight); 335 options.inJustDecodeBounds = false; 336 } 337 338 options.inDither = false; 339 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 340 b = BitmapManager.instance().decodeFileDescriptor(fd, options); 341 } catch (OutOfMemoryError ex) { 342 Log.e(TAG, "Got oom exception ", ex); 343 return null; 344 } finally { 345 closeSiliently(pfd); 346 } 347 return b; 348 } 349 350 private static ParcelFileDescriptor makeInputStream( 351 Uri uri, ContentResolver cr) { 352 try { 353 return cr.openFileDescriptor(uri, "r"); 354 } catch (IOException ex) { 355 return null; 356 } 357 } 358 359 public static void debugWhere(String tag, String msg) { 360 Exception ex = new Exception(); 361 Log.d(tag, msg, ex); 362 } 363} 364