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