Util.java revision 35f8af0ff228adfeec6bb40844d362f6dd62dab6
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 * Create a video thumbnail for a video. May return null if the video is 247 * corrupt. 248 * 249 * @param filePath 250 */ 251 public static Bitmap createVideoThumbnail(String filePath) { 252 Bitmap bitmap = null; 253 MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 254 try { 255 retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY); 256 retriever.setDataSource(filePath); 257 bitmap = retriever.captureFrame(); 258 } catch (IllegalArgumentException ex) { 259 // Assume this is a corrupt video file 260 } catch (RuntimeException ex) { 261 // Assume this is a corrupt video file. 262 } finally { 263 try { 264 retriever.release(); 265 } catch (RuntimeException ex) { 266 // Ignore failures while cleaning up. 267 } 268 } 269 return bitmap; 270 } 271 272 public static <T> int indexOf(T [] array, T s) { 273 for (int i = 0; i < array.length; i++) { 274 if (array[i].equals(s)) { 275 return i; 276 } 277 } 278 return -1; 279 } 280 281 public static void closeSilently(Closeable c) { 282 if (c == null) return; 283 try { 284 c.close(); 285 } catch (Throwable t) { 286 // do nothing 287 } 288 } 289 290 public static void closeSilently(ParcelFileDescriptor c) { 291 if (c == null) return; 292 try { 293 c.close(); 294 } catch (Throwable t) { 295 // do nothing 296 } 297 } 298 299 /** 300 * Make a bitmap from a given Uri. 301 * 302 * @param uri 303 */ 304 public static Bitmap makeBitmap(int targetWidthOrHeight, Uri uri, 305 ContentResolver cr) { 306 ParcelFileDescriptor input = null; 307 try { 308 input = cr.openFileDescriptor(uri, "r"); 309 return makeBitmap(targetWidthOrHeight, uri, cr, input, null); 310 } catch (IOException ex) { 311 return null; 312 } finally { 313 closeSilently(input); 314 } 315 } 316 317 public static Bitmap makeBitmap(int targetWidthHeight, Uri uri, 318 ContentResolver cr, ParcelFileDescriptor pfd, 319 BitmapFactory.Options options) { 320 Bitmap b = null; 321 try { 322 if (pfd == null) pfd = makeInputStream(uri, cr); 323 if (pfd == null) return null; 324 if (options == null) options = new BitmapFactory.Options(); 325 326 FileDescriptor fd = pfd.getFileDescriptor(); 327 options.inSampleSize = 1; 328 if (targetWidthHeight != -1) { 329 options.inJustDecodeBounds = true; 330 BitmapManager.instance().decodeFileDescriptor(fd, options); 331 if (options.mCancel || options.outWidth == -1 332 || options.outHeight == -1) { 333 return null; 334 } 335 options.inSampleSize = 336 computeSampleSize(options, targetWidthHeight); 337 options.inJustDecodeBounds = false; 338 } 339 340 options.inDither = false; 341 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 342 b = BitmapManager.instance().decodeFileDescriptor(fd, options); 343 } catch (OutOfMemoryError ex) { 344 Log.e(TAG, "Got oom exception ", ex); 345 return null; 346 } finally { 347 closeSilently(pfd); 348 } 349 return b; 350 } 351 352 private static ParcelFileDescriptor makeInputStream( 353 Uri uri, ContentResolver cr) { 354 try { 355 return cr.openFileDescriptor(uri, "r"); 356 } catch (IOException ex) { 357 return null; 358 } 359 } 360 361 public static void debugWhere(String tag, String msg) { 362 Log.d(tag, msg + " --- stack trace begins: "); 363 StackTraceElement elements[] = Thread.currentThread().getStackTrace(); 364 // skip first 3 element, they are not related to the caller 365 for (int i = 3, n = elements.length; i < n; ++i) { 366 StackTraceElement st = elements[i]; 367 String message = String.format(" at %s.%s(%s:%s)", 368 st.getClassName(), st.getMethodName(), st.getFileName(), 369 st.getLineNumber()); 370 Log.d(tag, message); 371 } 372 Log.d(tag, msg + " --- stack trace ends."); 373 } 374 375 public static <T> void showProgressDialog(final Context context, 376 String title, String message, PriorityTask<T> task) { 377 final ProgressDialog dialog = 378 ProgressDialog.show(context, title, message); 379 380 task.addCallback(new PriorityTask.Callback<T>() { 381 382 public void onCanceled(PriorityTask<T> t) { 383 dialog.dismiss(); 384 } 385 386 public void onFail(PriorityTask<T> t, Throwable error) { 387 dialog.dismiss(); 388 } 389 390 public void onResultAvailable(PriorityTask<T> t, T result) { 391 dialog.dismiss(); 392 } 393 }); 394 } 395 396 public static synchronized OnClickListener getNullOnClickListener() { 397 if (sNullOnClickListener == null) { 398 sNullOnClickListener = new OnClickListener() { 399 public void onClick(View v) { 400 } 401 }; 402 } 403 return sNullOnClickListener; 404 } 405 406 public static void Assert(boolean cond) { 407 if (!cond) { 408 throw new AssertionError(); 409 } 410 } 411 412 public static Bitmap makeRoundedCorner(Bitmap thumb, int rx, int ry) { 413 if (thumb == null) return null; 414 int width = thumb.getWidth(); 415 int height = thumb.getHeight(); 416 417 Bitmap result = Bitmap.createBitmap( 418 width, height, Bitmap.Config.ARGB_8888); 419 Canvas canvas = new Canvas(result); 420 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 421 Path path = new Path(); 422 path.addRoundRect(new RectF( 423 0, 0, width, height), rx, ry, Path.Direction.CCW); 424 canvas.clipPath(path); 425 canvas.drawBitmap(thumb, 0, 0, paint); 426 return result; 427 } 428 429 public static boolean equals(String a, String b) { 430 // return true if both string are null or the content equals 431 return a == b || a.equals(b); 432 } 433} 434