1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2010 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.gallery3d.common;
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Bitmap;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Bitmap.CompressFormat;
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.BitmapFactory;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Canvas;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Matrix;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Paint;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.os.Build;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.FloatMath;
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log;
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.ByteArrayOutputStream;
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.lang.reflect.InvocationTargetException;
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.lang.reflect.Method;
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapublic class BitmapUtils {
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final String TAG = "BitmapUtils";
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static final int DEFAULT_JPEG_QUALITY = 90;
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static final int UNCONSTRAINED = -1;
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private BitmapUtils(){}
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /*
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Compute the sample size as a function of minSideLength
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * and maxNumOfPixels.
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * minSideLength is used to specify that minimal width or height of a
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * bitmap.
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * maxNumOfPixels is used to specify the maximal size in pixels that is
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * tolerable in terms of memory usage.
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * The function returns a sample size based on the constraints.
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Both size and minSideLength can be passed in as UNCONSTRAINED,
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * which indicates no care of the corresponding constraint.
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * The functions prefers returning a sample size that
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * generates a smaller bitmap, unless minSideLength = UNCONSTRAINED.
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     *
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * Also, the function rounds up the sample size to a power of 2 or multiple
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * of 8 because BitmapFactory only honors sample size this way.
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * For example, BitmapFactory downsamples an image by 2 even though the
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * request is 3. So we round up the sample size to avoid OOM.
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static int computeSampleSize(int width, int height,
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int minSideLength, int maxNumOfPixels) {
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int initialSize = computeInitialSampleSize(
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                width, height, minSideLength, maxNumOfPixels);
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return initialSize <= 8
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ? Utils.nextPowerOf2(initialSize)
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                : (initialSize + 7) / 8 * 8;
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static int computeInitialSampleSize(int w, int h,
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int minSideLength, int maxNumOfPixels) {
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (maxNumOfPixels == UNCONSTRAINED
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && minSideLength == UNCONSTRAINED) return 1;
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                (int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels));
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (minSideLength == UNCONSTRAINED) {
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return lowerBound;
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } else {
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int sampleSize = Math.min(w / minSideLength, h / minSideLength);
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return Math.max(sampleSize, lowerBound);
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // This computes a sample size which makes the longer side at least
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // minSideLength long. If that's not possible, return 1.
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static int computeSampleSizeLarger(int w, int h,
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int minSideLength) {
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int initialSize = Math.max(w / minSideLength, h / minSideLength);
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (initialSize <= 1) return 1;
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return initialSize <= 8
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ? Utils.prevPowerOf2(initialSize)
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                : initialSize / 8 * 8;
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Find the min x that 1 / x >= scale
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static int computeSampleSizeLarger(float scale) {
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int initialSize = (int) FloatMath.floor(1f / scale);
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (initialSize <= 1) return 1;
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return initialSize <= 8
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ? Utils.prevPowerOf2(initialSize)
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                : initialSize / 8 * 8;
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    // Find the max x that 1 / x <= scale.
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static int computeSampleSize(float scale) {
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Utils.assertTrue(scale > 0);
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int initialSize = Math.max(1, (int) FloatMath.ceil(1 / scale));
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return initialSize <= 8
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                ? Utils.nextPowerOf2(initialSize)
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                : (initialSize + 7) / 8 * 8;
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static Bitmap resizeBitmapByScale(
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Bitmap bitmap, float scale, boolean recycle) {
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int width = Math.round(bitmap.getWidth() * scale);
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int height = Math.round(bitmap.getHeight() * scale);
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (width == bitmap.getWidth()
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                && height == bitmap.getHeight()) return bitmap;
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Bitmap target = Bitmap.createBitmap(width, height, getConfig(bitmap));
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Canvas canvas = new Canvas(target);
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        canvas.scale(scale, scale);
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        canvas.drawBitmap(bitmap, 0, 0, paint);
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (recycle) bitmap.recycle();
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return target;
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static Bitmap.Config getConfig(Bitmap bitmap) {
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Bitmap.Config config = bitmap.getConfig();
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (config == null) {
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            config = Bitmap.Config.ARGB_8888;
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return config;
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static Bitmap resizeDownBySideLength(
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Bitmap bitmap, int maxLength, boolean recycle) {
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int srcWidth = bitmap.getWidth();
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int srcHeight = bitmap.getHeight();
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float scale = Math.min(
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                (float) maxLength / srcWidth, (float) maxLength / srcHeight);
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (scale >= 1.0f) return bitmap;
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return resizeBitmapByScale(bitmap, scale, recycle);
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int w = bitmap.getWidth();
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int h = bitmap.getHeight();
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (w == size && h == size) return bitmap;
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // scale the image so that the shorter side equals to the target;
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // the longer side will be center-cropped.
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        float scale = (float) size / Math.min(w,  h);
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int width = Math.round(scale * bitmap.getWidth());
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int height = Math.round(scale * bitmap.getHeight());
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Canvas canvas = new Canvas(target);
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        canvas.translate((size - width) / 2f, (size - height) / 2f);
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        canvas.scale(scale, scale);
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        canvas.drawBitmap(bitmap, 0, 0, paint);
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (recycle) bitmap.recycle();
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return target;
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static void recycleSilently(Bitmap bitmap) {
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (bitmap == null) return;
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            bitmap.recycle();
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (Throwable t) {
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.w(TAG, "unable recycle bitmap", t);
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static Bitmap rotateBitmap(Bitmap source, int rotation, boolean recycle) {
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (rotation == 0) return source;
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int w = source.getWidth();
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        int h = source.getHeight();
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Matrix m = new Matrix();
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        m.postRotate(rotation);
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, w, h, m, true);
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (recycle) source.recycle();
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return bitmap;
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static Bitmap createVideoThumbnail(String filePath) {
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // MediaMetadataRetriever is available on API Level 8
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        // but is hidden until API Level 10
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Class<?> clazz = null;
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        Object instance = null;
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            clazz = Class.forName("android.media.MediaMetadataRetriever");
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            instance = clazz.newInstance();
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Method method = clazz.getMethod("setDataSource", String.class);
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            method.invoke(instance, filePath);
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // The method name changes between API Level 9 and 10.
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (Build.VERSION.SDK_INT <= 9) {
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return (Bitmap) clazz.getMethod("captureFrame").invoke(instance);
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance);
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (data != null) {
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    if (bitmap != null) return bitmap;
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance);
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
213104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (IllegalArgumentException ex) {
214104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Assume this is a corrupt video file
215104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (RuntimeException ex) {
216104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Assume this is a corrupt video file.
217104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (InstantiationException e) {
218104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, "createVideoThumbnail", e);
219104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (InvocationTargetException e) {
220104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, "createVideoThumbnail", e);
221104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (ClassNotFoundException e) {
222104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, "createVideoThumbnail", e);
223104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (NoSuchMethodException e) {
224104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, "createVideoThumbnail", e);
225104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (IllegalAccessException e) {
226104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.e(TAG, "createVideoThumbnail", e);
227104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } finally {
228104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            try {
229104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (instance != null) {
230104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    clazz.getMethod("release").invoke(instance);
231104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
232104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } catch (Exception ignored) {
233104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
234104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
235104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return null;
236104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
237104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
238104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static byte[] compressToBytes(Bitmap bitmap) {
239104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return compressToBytes(bitmap, DEFAULT_JPEG_QUALITY);
240104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
241104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
242104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static byte[] compressToBytes(Bitmap bitmap, int quality) {
243104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        ByteArrayOutputStream baos = new ByteArrayOutputStream(65536);
244104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        bitmap.compress(CompressFormat.JPEG, quality, baos);
245104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return baos.toByteArray();
246104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
247104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
248104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static boolean isSupportedByRegionDecoder(String mimeType) {
249104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mimeType == null) return false;
250104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mimeType = mimeType.toLowerCase();
251104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mimeType.startsWith("image/") &&
252104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                (!mimeType.equals("image/gif") && !mimeType.endsWith("bmp"));
253104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
254104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
255104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public static boolean isRotationSupported(String mimeType) {
256104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mimeType == null) return false;
257104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mimeType = mimeType.toLowerCase();
258104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return mimeType.equals("image/jpeg");
259104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
260104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
261