2 package com.android.gallery3d.filtershow.cache;
4 import android.content.ContentResolver;
5 import android.content.Context;
6 import android.content.res.Resources;
7 import android.database.Cursor;
8 import android.database.sqlite.SQLiteException;
9 import android.graphics.Bitmap;
10 import android.graphics.BitmapFactory;
11 import android.graphics.BitmapRegionDecoder;
12 import android.graphics.Matrix;
13 import android.graphics.Rect;
14 import android.media.ExifInterface;
15 import android.net.Uri;
16 import android.provider.MediaStore;
17 import android.util.Log;
19 import com.android.gallery3d.R;
20 import com.android.gallery3d.common.Utils;
21 import com.android.gallery3d.filtershow.FilterShowActivity;
22 import com.android.gallery3d.filtershow.HistoryAdapter;
23 import com.android.gallery3d.filtershow.imageshow.ImageShow;
24 import com.android.gallery3d.filtershow.presets.ImagePreset;
25 import com.android.gallery3d.filtershow.tools.ProcessedBitmap;
26 import com.android.gallery3d.filtershow.tools.SaveCopyTask;
28 import java.io.Closeable;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.Vector;
35 public class ImageLoader {
37 private static final String LOGTAG = "ImageLoader";
38 private final Vector<ImageShow> mListeners = new Vector<ImageShow>();
39 private Bitmap mOriginalBitmapSmall = null;
40 private Bitmap mOriginalBitmapLarge = null;
41 private Bitmap mBackgroundBitmap = null;
42 private Bitmap mFullOriginalBitmap = null;
43 private Bitmap mSaveCopy = null;
45 private Cache mCache = null;
46 private Cache mHiresCache = null;
47 private final ZoomCache mZoomCache = new ZoomCache();
49 private int mOrientation = 0;
50 private HistoryAdapter mAdapter = null;
52 private static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
53 private static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
54 private static final int ORI_ROTATE_180 = ExifInterface.ORIENTATION_ROTATE_180;
55 private static final int ORI_ROTATE_270 = ExifInterface.ORIENTATION_ROTATE_270;
56 private static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
57 private static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
58 private static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
59 private static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE;
61 private Context mContext = null;
62 private Uri mUri = null;
64 private Rect mOriginalBounds = null;
66 public ImageLoader(Context context) {
68 mCache = new DelayedPresetCache(this, 30);
69 mHiresCache = new DelayedPresetCache(this, 2);
72 public void loadBitmap(Uri uri,int size) {
74 mOrientation = getOrientation(uri);
76 mOriginalBitmapSmall = loadScaledBitmap(uri, 160);
77 mOriginalBitmapLarge = loadScaledBitmap(uri, size);
85 public Rect getOriginalBounds() {
86 return mOriginalBounds;
89 private int getOrientation(Uri uri) {
90 if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
91 return getOrientationFromPath(uri.getPath());
96 cursor = mContext.getContentResolver().query(uri,
98 MediaStore.Images.ImageColumns.ORIENTATION
101 if (cursor.moveToNext()){
102 int ori = cursor.getInt(0);
105 case 0: return ORI_NORMAL;
106 case 90: return ORI_ROTATE_90;
107 case 270: return ORI_ROTATE_270;
108 case 180: return ORI_ROTATE_180;
115 } catch (SQLiteException e){
116 return ExifInterface.ORIENTATION_UNDEFINED;
118 Utils.closeSilently(cursor);
122 private int getOrientationFromPath(String path) {
123 int orientation = -1;
125 ExifInterface EXIF = new ExifInterface(path);
126 orientation = EXIF.getAttributeInt(ExifInterface.TAG_ORIENTATION,
128 } catch (IOException e) {
134 private void updateBitmaps() {
135 if (mOrientation > 1) {
136 mOriginalBitmapSmall = rotateToPortrait(mOriginalBitmapSmall,mOrientation);
137 mOriginalBitmapLarge = rotateToPortrait(mOriginalBitmapLarge,mOrientation);
139 mCache.setOriginalBitmap(mOriginalBitmapSmall);
140 mHiresCache.setOriginalBitmap(mOriginalBitmapLarge);
144 private Bitmap rotateToPortrait(Bitmap bitmap,int ori) {
145 Matrix matrix = new Matrix();
146 int w = bitmap.getWidth();
147 int h = bitmap.getHeight();
148 if (ori == ORI_ROTATE_90 ||
149 ori == ORI_ROTATE_270 ||
150 ori == ORI_TRANSPOSE||
151 ori == ORI_TRANSVERSE) {
159 matrix.setRotate(90,w/2f,h/2f);
162 matrix.setRotate(180,w/2f,h/2f);
165 matrix.setRotate(270,w/2f,h/2f);
168 matrix.preScale(-1, 1);
171 matrix.preScale(1, -1);
174 matrix.setRotate(90,w/2f,h/2f);
175 matrix.preScale(1, -1);
178 matrix.setRotate(270,w/2f,h/2f);
179 matrix.preScale(1, -1);
184 return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
185 bitmap.getHeight(), matrix, true);
188 private void closeStream(Closeable stream) {
189 if (stream != null) {
192 } catch (IOException e) {
198 private Bitmap loadRegionBitmap(Uri uri, Rect bounds) {
199 InputStream is = null;
201 is = mContext.getContentResolver().openInputStream(uri);
202 BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
203 return decoder.decodeRegion(bounds, null);
204 } catch (FileNotFoundException e) {
205 Log.e(LOGTAG, "FileNotFoundException: " + uri);
206 } catch (Exception e) {
214 private Bitmap loadScaledBitmap(Uri uri, int size) {
215 InputStream is = null;
217 is = mContext.getContentResolver().openInputStream(uri);
218 Log.v(LOGTAG, "loading uri " + uri.getPath() + " input stream: "
220 BitmapFactory.Options o = new BitmapFactory.Options();
221 o.inJustDecodeBounds = true;
222 BitmapFactory.decodeStream(is, null, o);
224 int width_tmp = o.outWidth;
225 int height_tmp = o.outHeight;
227 mOriginalBounds = new Rect(0, 0, width_tmp, height_tmp);
231 if (width_tmp / 2 < size || height_tmp / 2 < size)
238 // decode with inSampleSize
239 BitmapFactory.Options o2 = new BitmapFactory.Options();
240 o2.inSampleSize = scale;
243 is = mContext.getContentResolver().openInputStream(uri);
244 return BitmapFactory.decodeStream(is, null, o2);
245 } catch (FileNotFoundException e) {
246 Log.e(LOGTAG, "FileNotFoundException: " + uri);
247 } catch (Exception e) {
255 public Bitmap getBackgroundBitmap(Resources resources) {
256 if (mBackgroundBitmap == null) {
257 mBackgroundBitmap = BitmapFactory.decodeResource(resources,
258 R.drawable.filtershow_background);
260 return mBackgroundBitmap;
264 public Bitmap getOriginalBitmapSmall() {
265 return mOriginalBitmapSmall;
268 public Bitmap getOriginalBitmapLarge() {
269 return mOriginalBitmapLarge;
272 public void addListener(ImageShow imageShow) {
273 if (!mListeners.contains(imageShow)) {
274 mListeners.add(imageShow);
278 public void warnListeners() {
279 for (int i = 0; i < mListeners.size(); i++) {
280 ImageShow imageShow = mListeners.elementAt(i);
281 imageShow.updateImage();
285 // TODO: this currently does the loading + filtering on the UI thread -- need to
286 // move this to a background thread.
287 public Bitmap getScaleOneImageForPreset(ImageShow caller, ImagePreset imagePreset, Rect bounds,
289 Bitmap bmp = mZoomCache.getImage(imagePreset, bounds);
290 if (force || bmp == null) {
291 bmp = loadRegionBitmap(mUri, bounds);
293 // TODO: this workaround for RS might not be needed ultimately
294 Bitmap bmp2 = bmp.copy(Bitmap.Config.ARGB_8888, true);
295 float scaleFactor = imagePreset.getScaleFactor();
296 imagePreset.setScaleFactor(1.0f);
297 bmp2 = imagePreset.apply(bmp2);
298 imagePreset.setScaleFactor(scaleFactor);
299 mZoomCache.setImage(imagePreset, bounds, bmp2);
307 public Bitmap getImageForPreset(ImageShow caller, ImagePreset imagePreset,
309 if (mOriginalBitmapSmall == null) {
312 if (mOriginalBitmapLarge == null) {
316 Bitmap filteredImage = null;
319 filteredImage = mHiresCache.get(imagePreset);
321 filteredImage = mCache.get(imagePreset);
324 if (filteredImage == null) {
326 cachePreset(imagePreset, mHiresCache, caller);
328 cachePreset(imagePreset, mCache, caller);
331 return filteredImage;
334 public void resetImageForPreset(ImagePreset imagePreset, ImageShow caller) {
335 mHiresCache.reset(imagePreset);
336 mCache.reset(imagePreset);
337 mZoomCache.reset(imagePreset);
340 public Uri saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
342 BitmapFactory.Options options = new BitmapFactory.Options();
343 options.inMutable = true;
345 if (mFullOriginalBitmap != null) {
346 mFullOriginalBitmap.recycle();
349 InputStream is = null;
352 is = mContext.getContentResolver().openInputStream(mUri);
353 mFullOriginalBitmap = BitmapFactory.decodeStream(is, null, options);
354 // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
356 mSaveCopy = mFullOriginalBitmap;
357 preset.setIsHighQuality(true);
358 preset.setScaleFactor(1.0f);
359 ProcessedBitmap processedBitmap = new ProcessedBitmap(mSaveCopy, preset);
360 new SaveCopyTask(mContext, mUri, destination, new SaveCopyTask.Callback() {
363 public void onComplete(Uri result) {
364 filterShowActivity.completeSaveImage(result);
367 }).execute(processedBitmap);
368 } catch (FileNotFoundException e) {
377 public void setAdapter(HistoryAdapter adapter) {
381 public HistoryAdapter getHistory() {
385 private void cachePreset(ImagePreset preset, Cache cache, ImageShow caller) {
386 cache.prepare(preset);
387 cache.addObserver(caller);