Util.java revision 9a4c2cac83f50e29063d27605c9f8d9e676a6f22
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 android.app.Activity; 20import android.app.AlertDialog; 21import android.content.DialogInterface; 22import android.graphics.Bitmap; 23import android.graphics.BitmapFactory; 24import android.graphics.Matrix; 25import android.util.Log; 26import android.view.View; 27import android.view.animation.Animation; 28import android.view.animation.TranslateAnimation; 29 30import com.android.camera.gallery.IImage; 31 32import java.io.Closeable; 33 34/** 35 * Collection of utility functions used in this package. 36 */ 37public class Util { 38 private static final String TAG = "Util"; 39 public static final int DIRECTION_LEFT = 0; 40 public static final int DIRECTION_RIGHT = 1; 41 public static final int DIRECTION_UP = 2; 42 public static final int DIRECTION_DOWN = 3; 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 minSideLength 70 * and maxNumOfPixels. 71 * minSideLength is used to specify that minimal width or height of a 72 * bitmap. 73 * maxNumOfPixels is used to specify the maximal size in pixels that is 74 * tolerable in terms of memory usage. 75 * 76 * The function returns a sample size based on the constraints. 77 * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED, 78 * which indicates no care of the corresponding constraint. 79 * The functions prefers returning a sample size that 80 * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED. 81 * 82 * Also, the function rounds up the sample size to a power of 2 or multiple 83 * of 8 because BitmapFactory only honors sample size this way. 84 * For example, BitmapFactory downsamples an image by 2 even though the 85 * request is 3. So we round up the sample size to avoid OOM. 86 */ 87 public static int computeSampleSize(BitmapFactory.Options options, 88 int minSideLength, int maxNumOfPixels) { 89 int initialSize = computeInitialSampleSize(options, minSideLength, 90 maxNumOfPixels); 91 92 int roundedSize; 93 if (initialSize <= 8) { 94 roundedSize = 1; 95 while (roundedSize < initialSize) { 96 roundedSize <<= 1; 97 } 98 } else { 99 roundedSize = (initialSize + 7) / 8 * 8; 100 } 101 102 return roundedSize; 103 } 104 105 private static int computeInitialSampleSize(BitmapFactory.Options options, 106 int minSideLength, int maxNumOfPixels) { 107 double w = options.outWidth; 108 double h = options.outHeight; 109 110 int lowerBound = (maxNumOfPixels == IImage.UNCONSTRAINED) ? 1 : 111 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); 112 int upperBound = (minSideLength == IImage.UNCONSTRAINED) ? 128 : 113 (int) Math.min(Math.floor(w / minSideLength), 114 Math.floor(h / minSideLength)); 115 116 if (upperBound < lowerBound) { 117 // return the larger one when there is no overlapping zone. 118 return lowerBound; 119 } 120 121 if ((maxNumOfPixels == IImage.UNCONSTRAINED) && 122 (minSideLength == IImage.UNCONSTRAINED)) { 123 return 1; 124 } else if (minSideLength == IImage.UNCONSTRAINED) { 125 return lowerBound; 126 } else { 127 return upperBound; 128 } 129 } 130 131 public static <T> int indexOf(T [] array, T s) { 132 for (int i = 0; i < array.length; i++) { 133 if (array[i].equals(s)) { 134 return i; 135 } 136 } 137 return -1; 138 } 139 140 public static void closeSilently(Closeable c) { 141 if (c == null) return; 142 try { 143 c.close(); 144 } catch (Throwable t) { 145 // do nothing 146 } 147 } 148 149 public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) { 150 try { 151 BitmapFactory.Options options = new BitmapFactory.Options(); 152 options.inJustDecodeBounds = true; 153 BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 154 options); 155 if (options.mCancel || options.outWidth == -1 156 || options.outHeight == -1) { 157 return null; 158 } 159 options.inSampleSize = computeSampleSize( 160 options, IImage.UNCONSTRAINED, maxNumOfPixels); 161 options.inJustDecodeBounds = false; 162 163 options.inDither = false; 164 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 165 return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, 166 options); 167 } catch (OutOfMemoryError ex) { 168 Log.e(TAG, "Got oom exception ", ex); 169 return null; 170 } 171 } 172 173 public static void Assert(boolean cond) { 174 if (!cond) { 175 throw new AssertionError(); 176 } 177 } 178 179 public static void showFatalErrorAndFinish( 180 final Activity activity, String title, String message) { 181 DialogInterface.OnClickListener buttonListener = 182 new DialogInterface.OnClickListener() { 183 public void onClick(DialogInterface dialog, int which) { 184 activity.finish(); 185 } 186 }; 187 new AlertDialog.Builder(activity) 188 .setCancelable(false) 189 .setIcon(android.R.drawable.ic_dialog_alert) 190 .setTitle(title) 191 .setMessage(message) 192 .setNeutralButton(R.string.details_ok, buttonListener) 193 .show(); 194 } 195 196 public static Animation slideOut(View view, int to) { 197 view.setVisibility(View.INVISIBLE); 198 Animation anim; 199 switch (to) { 200 case DIRECTION_LEFT: 201 anim = new TranslateAnimation(0, -view.getWidth(), 0, 0); 202 break; 203 case DIRECTION_RIGHT: 204 anim = new TranslateAnimation(0, view.getWidth(), 0, 0); 205 break; 206 case DIRECTION_UP: 207 anim = new TranslateAnimation(0, 0, 0, -view.getHeight()); 208 break; 209 case DIRECTION_DOWN: 210 anim = new TranslateAnimation(0, 0, 0, view.getHeight()); 211 break; 212 default: 213 throw new IllegalArgumentException(Integer.toString(to)); 214 } 215 anim.setDuration(500); 216 view.startAnimation(anim); 217 return anim; 218 } 219 220 public static Animation slideIn(View view, int from) { 221 view.setVisibility(View.VISIBLE); 222 Animation anim; 223 switch (from) { 224 case DIRECTION_LEFT: 225 anim = new TranslateAnimation(-view.getWidth(), 0, 0, 0); 226 break; 227 case DIRECTION_RIGHT: 228 anim = new TranslateAnimation(view.getWidth(), 0, 0, 0); 229 break; 230 case DIRECTION_UP: 231 anim = new TranslateAnimation(0, 0, -view.getHeight(), 0); 232 break; 233 case DIRECTION_DOWN: 234 anim = new TranslateAnimation(0, 0, view.getHeight(), 0); 235 break; 236 default: 237 throw new IllegalArgumentException(Integer.toString(from)); 238 } 239 anim.setDuration(500); 240 view.startAnimation(anim); 241 return anim; 242 } 243 244 public static <T> T checkNotNull(T object) { 245 if (object == null) throw new NullPointerException(); 246 return object; 247 } 248 249 public static boolean equals(Object a, Object b) { 250 return (a == b) || (a == null ? false : a.equals(b)); 251 } 252 253 public static boolean isPowerOf2(int n) { 254 return (n & -n) == n; 255 } 256 257 public static int nextPowerOf2(int n) { 258 n -= 1; 259 n |= n >>> 16; 260 n |= n >>> 8; 261 n |= n >>> 4; 262 n |= n >>> 2; 263 n |= n >>> 1; 264 return n + 1; 265 } 266} 267