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