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