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