DecodeUtils.java revision 113bfc77c4468411da9ae1290553c3be89f8df9a
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.data;
18
19import com.android.gallery3d.common.BitmapUtils;
20import com.android.gallery3d.common.Utils;
21import com.android.gallery3d.util.ThreadPool.CancelListener;
22import com.android.gallery3d.util.ThreadPool.JobContext;
23
24import android.content.ContentResolver;
25import android.graphics.Bitmap;
26import android.graphics.Bitmap.Config;
27import android.graphics.BitmapFactory;
28import android.graphics.BitmapFactory.Options;
29import android.graphics.BitmapRegionDecoder;
30import android.graphics.Rect;
31import android.net.Uri;
32import android.os.ParcelFileDescriptor;
33
34import java.io.FileDescriptor;
35import java.io.FileInputStream;
36
37public class DecodeUtils {
38    private static final String TAG = "DecodeService";
39
40    private static class DecodeCanceller implements CancelListener {
41        Options mOptions;
42        public DecodeCanceller(Options options) {
43            mOptions = options;
44        }
45        public void onCancel() {
46            mOptions.requestCancelDecode();
47        }
48    }
49
50    public static Bitmap requestDecode(JobContext jc, final String filePath,
51            Options options) {
52        if (options == null) options = new Options();
53        jc.setCancelListener(new DecodeCanceller(options));
54        return ensureGLCompatibleBitmap(
55                BitmapFactory.decodeFile(filePath, options));
56    }
57
58    public static Bitmap requestDecode(JobContext jc, FileDescriptor fd, Options options) {
59        if (options == null) options = new Options();
60        jc.setCancelListener(new DecodeCanceller(options));
61        return ensureGLCompatibleBitmap(
62                BitmapFactory.decodeFileDescriptor(fd, null, options));
63    }
64
65    public static Bitmap requestDecode(JobContext jc, byte[] bytes,
66            Options options) {
67        return requestDecode(jc, bytes, 0, bytes.length, options);
68    }
69
70    public static Bitmap requestDecode(JobContext jc, byte[] bytes, int offset,
71            int length, Options options) {
72        if (options == null) options = new Options();
73        jc.setCancelListener(new DecodeCanceller(options));
74        return ensureGLCompatibleBitmap(
75                BitmapFactory.decodeByteArray(bytes, offset, length, options));
76    }
77
78    public static Bitmap requestDecode(JobContext jc, final String filePath,
79            Options options, int targetSize) {
80        FileInputStream fis = null;
81        try {
82            fis = new FileInputStream(filePath);
83            FileDescriptor fd = fis.getFD();
84            return requestDecode(jc, fd, options, targetSize);
85        } catch (Exception ex) {
86            Log.w(TAG, ex);
87            return null;
88        } finally {
89            Utils.closeSilently(fis);
90        }
91    }
92
93    public static Bitmap requestDecode(JobContext jc, FileDescriptor fd,
94            Options options, int targetSize) {
95        if (options == null) options = new Options();
96        jc.setCancelListener(new DecodeCanceller(options));
97
98        options.inJustDecodeBounds = true;
99        BitmapFactory.decodeFileDescriptor(fd, null, options);
100        if (jc.isCancelled()) return null;
101
102        options.inSampleSize = BitmapUtils.computeSampleSizeLarger(
103                options.outWidth, options.outHeight, targetSize);
104        options.inJustDecodeBounds = false;
105        return ensureGLCompatibleBitmap(
106                BitmapFactory.decodeFileDescriptor(fd, null, options));
107    }
108
109    /**
110     * Decodes the bitmap from the given byte array if the image size is larger than the given
111     * requirement.
112     *
113     * Note: The returned image may be resized down. However, both width and heigh must be
114     * larger than the <code>targetSize</code>.
115     */
116    public static Bitmap requestDecodeIfBigEnough(JobContext jc, byte[] data,
117            Options options, int targetSize) {
118        if (options == null) options = new Options();
119        jc.setCancelListener(new DecodeCanceller(options));
120
121        options.inJustDecodeBounds = true;
122        BitmapFactory.decodeByteArray(data, 0, data.length, options);
123        if (jc.isCancelled()) return null;
124        if (options.outWidth < targetSize || options.outHeight < targetSize) {
125            return null;
126        }
127        options.inSampleSize = BitmapUtils.computeSampleSizeLarger(
128                options.outWidth, options.outHeight, targetSize);
129        options.inJustDecodeBounds = false;
130        return ensureGLCompatibleBitmap(
131                BitmapFactory.decodeByteArray(data, 0, data.length, options));
132    }
133
134    public static Bitmap requestDecode(JobContext jc,
135            FileDescriptor fileDescriptor, Rect paddings, Options options) {
136        if (options == null) options = new Options();
137        jc.setCancelListener(new DecodeCanceller(options));
138        return ensureGLCompatibleBitmap(BitmapFactory.decodeFileDescriptor
139                (fileDescriptor, paddings, options));
140    }
141
142    // TODO: This function should not be called directly from
143    // DecodeUtils.requestDecode(...), since we don't have the knowledge
144    // if the bitmap will be uploaded to GL.
145    public static Bitmap ensureGLCompatibleBitmap(Bitmap bitmap) {
146        if (bitmap == null || bitmap.getConfig() != null) return bitmap;
147        Bitmap newBitmap = bitmap.copy(Config.ARGB_8888, false);
148        bitmap.recycle();
149        return newBitmap;
150    }
151
152    public static BitmapRegionDecoder requestCreateBitmapRegionDecoder(
153            JobContext jc, byte[] bytes, int offset, int length,
154            boolean shareable) {
155        if (offset < 0 || length <= 0 || offset + length > bytes.length) {
156            throw new IllegalArgumentException(String.format(
157                    "offset = %s, length = %s, bytes = %s",
158                    offset, length, bytes.length));
159        }
160
161        try {
162            return BitmapRegionDecoder.newInstance(
163                    bytes, offset, length, shareable);
164        } catch (Throwable t)  {
165            Log.w(TAG, t);
166            return null;
167        }
168    }
169
170    public static BitmapRegionDecoder requestCreateBitmapRegionDecoder(
171            JobContext jc, String filePath, boolean shareable) {
172        try {
173            return BitmapRegionDecoder.newInstance(filePath, shareable);
174        } catch (Throwable t)  {
175            Log.w(TAG, t);
176            return null;
177        }
178    }
179
180    public static BitmapRegionDecoder requestCreateBitmapRegionDecoder(
181            JobContext jc, FileDescriptor fd, boolean shareable) {
182        try {
183            return BitmapRegionDecoder.newInstance(fd, shareable);
184        } catch (Throwable t)  {
185            Log.w(TAG, t);
186            return null;
187        }
188    }
189
190    public static BitmapRegionDecoder requestCreateBitmapRegionDecoder(
191            JobContext jc, Uri uri, ContentResolver resolver,
192            boolean shareable) {
193        ParcelFileDescriptor pfd = null;
194        try {
195            pfd = resolver.openFileDescriptor(uri, "r");
196            return BitmapRegionDecoder.newInstance(
197                    pfd.getFileDescriptor(), shareable);
198        } catch (Throwable t) {
199            Log.w(TAG, t);
200            return null;
201        } finally {
202            Utils.closeSilently(pfd);
203        }
204    }
205}
206