PageRenderTime 39ms CodeModel.GetById 12ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/packages/WallpaperCropper/src/com/android/gallery3d/common/BitmapUtils.java

https://github.com/aizuzi/platform_frameworks_base
Java | 260 lines | 185 code | 30 blank | 45 comment | 41 complexity | 40a83813f3cccbcc465a4487884c5720 MD5 | raw file
  1/*
  2 * Copyright (C) 2010 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.gallery3d.common;
 18
 19import android.graphics.Bitmap;
 20import android.graphics.Bitmap.CompressFormat;
 21import android.graphics.BitmapFactory;
 22import android.graphics.Canvas;
 23import android.graphics.Matrix;
 24import android.graphics.Paint;
 25import android.os.Build;
 26import android.util.FloatMath;
 27import android.util.Log;
 28
 29import java.io.ByteArrayOutputStream;
 30import java.lang.reflect.InvocationTargetException;
 31import java.lang.reflect.Method;
 32
 33public class BitmapUtils {
 34    private static final String TAG = "BitmapUtils";
 35    private static final int DEFAULT_JPEG_QUALITY = 90;
 36    public static final int UNCONSTRAINED = -1;
 37
 38    private BitmapUtils(){}
 39
 40    /*
 41     * Compute the sample size as a function of minSideLength
 42     * and maxNumOfPixels.
 43     * minSideLength is used to specify that minimal width or height of a
 44     * bitmap.
 45     * maxNumOfPixels is used to specify the maximal size in pixels that is
 46     * tolerable in terms of memory usage.
 47     *
 48     * The function returns a sample size based on the constraints.
 49     * Both size and minSideLength can be passed in as UNCONSTRAINED,
 50     * which indicates no care of the corresponding constraint.
 51     * The functions prefers returning a sample size that
 52     * generates a smaller bitmap, unless minSideLength = UNCONSTRAINED.
 53     *
 54     * Also, the function rounds up the sample size to a power of 2 or multiple
 55     * of 8 because BitmapFactory only honors sample size this way.
 56     * For example, BitmapFactory downsamples an image by 2 even though the
 57     * request is 3. So we round up the sample size to avoid OOM.
 58     */
 59    public static int computeSampleSize(int width, int height,
 60            int minSideLength, int maxNumOfPixels) {
 61        int initialSize = computeInitialSampleSize(
 62                width, height, minSideLength, maxNumOfPixels);
 63
 64        return initialSize <= 8
 65                ? Utils.nextPowerOf2(initialSize)
 66                : (initialSize + 7) / 8 * 8;
 67    }
 68
 69    private static int computeInitialSampleSize(int w, int h,
 70            int minSideLength, int maxNumOfPixels) {
 71        if (maxNumOfPixels == UNCONSTRAINED
 72                && minSideLength == UNCONSTRAINED) return 1;
 73
 74        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
 75                (int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels));
 76
 77        if (minSideLength == UNCONSTRAINED) {
 78            return lowerBound;
 79        } else {
 80            int sampleSize = Math.min(w / minSideLength, h / minSideLength);
 81            return Math.max(sampleSize, lowerBound);
 82        }
 83    }
 84
 85    // This computes a sample size which makes the longer side at least
 86    // minSideLength long. If that's not possible, return 1.
 87    public static int computeSampleSizeLarger(int w, int h,
 88            int minSideLength) {
 89        int initialSize = Math.max(w / minSideLength, h / minSideLength);
 90        if (initialSize <= 1) return 1;
 91
 92        return initialSize <= 8
 93                ? Utils.prevPowerOf2(initialSize)
 94                : initialSize / 8 * 8;
 95    }
 96
 97    // Find the min x that 1 / x >= scale
 98    public static int computeSampleSizeLarger(float scale) {
 99        int initialSize = (int) FloatMath.floor(1f / scale);
100        if (initialSize <= 1) return 1;
101
102        return initialSize <= 8
103                ? Utils.prevPowerOf2(initialSize)
104                : initialSize / 8 * 8;
105    }
106
107    // Find the max x that 1 / x <= scale.
108    public static int computeSampleSize(float scale) {
109        Utils.assertTrue(scale > 0);
110        int initialSize = Math.max(1, (int) FloatMath.ceil(1 / scale));
111        return initialSize <= 8
112                ? Utils.nextPowerOf2(initialSize)
113                : (initialSize + 7) / 8 * 8;
114    }
115
116    public static Bitmap resizeBitmapByScale(
117            Bitmap bitmap, float scale, boolean recycle) {
118        int width = Math.round(bitmap.getWidth() * scale);
119        int height = Math.round(bitmap.getHeight() * scale);
120        if (width == bitmap.getWidth()
121                && height == bitmap.getHeight()) return bitmap;
122        Bitmap target = Bitmap.createBitmap(width, height, getConfig(bitmap));
123        Canvas canvas = new Canvas(target);
124        canvas.scale(scale, scale);
125        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
126        canvas.drawBitmap(bitmap, 0, 0, paint);
127        if (recycle) bitmap.recycle();
128        return target;
129    }
130
131    private static Bitmap.Config getConfig(Bitmap bitmap) {
132        Bitmap.Config config = bitmap.getConfig();
133        if (config == null) {
134            config = Bitmap.Config.ARGB_8888;
135        }
136        return config;
137    }
138
139    public static Bitmap resizeDownBySideLength(
140            Bitmap bitmap, int maxLength, boolean recycle) {
141        int srcWidth = bitmap.getWidth();
142        int srcHeight = bitmap.getHeight();
143        float scale = Math.min(
144                (float) maxLength / srcWidth, (float) maxLength / srcHeight);
145        if (scale >= 1.0f) return bitmap;
146        return resizeBitmapByScale(bitmap, scale, recycle);
147    }
148
149    public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
150        int w = bitmap.getWidth();
151        int h = bitmap.getHeight();
152        if (w == size && h == size) return bitmap;
153
154        // scale the image so that the shorter side equals to the target;
155        // the longer side will be center-cropped.
156        float scale = (float) size / Math.min(w,  h);
157
158        Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
159        int width = Math.round(scale * bitmap.getWidth());
160        int height = Math.round(scale * bitmap.getHeight());
161        Canvas canvas = new Canvas(target);
162        canvas.translate((size - width) / 2f, (size - height) / 2f);
163        canvas.scale(scale, scale);
164        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
165        canvas.drawBitmap(bitmap, 0, 0, paint);
166        if (recycle) bitmap.recycle();
167        return target;
168    }
169
170    public static void recycleSilently(Bitmap bitmap) {
171        if (bitmap == null) return;
172        try {
173            bitmap.recycle();
174        } catch (Throwable t) {
175            Log.w(TAG, "unable recycle bitmap", t);
176        }
177    }
178
179    public static Bitmap rotateBitmap(Bitmap source, int rotation, boolean recycle) {
180        if (rotation == 0) return source;
181        int w = source.getWidth();
182        int h = source.getHeight();
183        Matrix m = new Matrix();
184        m.postRotate(rotation);
185        Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, w, h, m, true);
186        if (recycle) source.recycle();
187        return bitmap;
188    }
189
190    public static Bitmap createVideoThumbnail(String filePath) {
191        // MediaMetadataRetriever is available on API Level 8
192        // but is hidden until API Level 10
193        Class<?> clazz = null;
194        Object instance = null;
195        try {
196            clazz = Class.forName("android.media.MediaMetadataRetriever");
197            instance = clazz.newInstance();
198
199            Method method = clazz.getMethod("setDataSource", String.class);
200            method.invoke(instance, filePath);
201
202            // The method name changes between API Level 9 and 10.
203            if (Build.VERSION.SDK_INT <= 9) {
204                return (Bitmap) clazz.getMethod("captureFrame").invoke(instance);
205            } else {
206                byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
207                if (data != null) {
208                    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
209                    if (bitmap != null) return bitmap;
210                }
211                return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
212            }
213        } catch (IllegalArgumentException ex) {
214            // Assume this is a corrupt video file
215        } catch (RuntimeException ex) {
216            // Assume this is a corrupt video file.
217        } catch (InstantiationException e) {
218            Log.e(TAG, "createVideoThumbnail", e);
219        } catch (InvocationTargetException e) {
220            Log.e(TAG, "createVideoThumbnail", e);
221        } catch (ClassNotFoundException e) {
222            Log.e(TAG, "createVideoThumbnail", e);
223        } catch (NoSuchMethodException e) {
224            Log.e(TAG, "createVideoThumbnail", e);
225        } catch (IllegalAccessException e) {
226            Log.e(TAG, "createVideoThumbnail", e);
227        } finally {
228            try {
229                if (instance != null) {
230                    clazz.getMethod("release").invoke(instance);
231                }
232            } catch (Exception ignored) {
233            }
234        }
235        return null;
236    }
237
238    public static byte[] compressToBytes(Bitmap bitmap) {
239        return compressToBytes(bitmap, DEFAULT_JPEG_QUALITY);
240    }
241
242    public static byte[] compressToBytes(Bitmap bitmap, int quality) {
243        ByteArrayOutputStream baos = new ByteArrayOutputStream(65536);
244        bitmap.compress(CompressFormat.JPEG, quality, baos);
245        return baos.toByteArray();
246    }
247
248    public static boolean isSupportedByRegionDecoder(String mimeType) {
249        if (mimeType == null) return false;
250        mimeType = mimeType.toLowerCase();
251        return mimeType.startsWith("image/") &&
252                (!mimeType.equals("image/gif") && !mimeType.endsWith("bmp"));
253    }
254
255    public static boolean isRotationSupported(String mimeType) {
256        if (mimeType == null) return false;
257        mimeType = mimeType.toLowerCase();
258        return mimeType.equals("image/jpeg");
259    }
260}