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