OSDN Git Service

am b9c316af: Merge "filters now support orientation" into gb-ub-photos-arches
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / cache / ImageLoader.java
1
2 package com.android.gallery3d.filtershow.cache;
3
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;
18
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;
27
28 import java.io.Closeable;
29 import java.io.File;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.Vector;
34
35 public class ImageLoader {
36
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;
44
45     private Cache mCache = null;
46     private Cache mHiresCache = null;
47     private final ZoomCache mZoomCache = new ZoomCache();
48
49     private int mOrientation = 0;
50     private HistoryAdapter mAdapter = null;
51
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;
60
61     private Context mContext = null;
62     private Uri mUri = null;
63
64     private Rect mOriginalBounds = null;
65
66     public ImageLoader(Context context) {
67         mContext = context;
68         mCache = new DelayedPresetCache(this, 30);
69         mHiresCache = new DelayedPresetCache(this, 2);
70     }
71
72     public void loadBitmap(Uri uri,int size) {
73         mUri = uri;
74         mOrientation = getOrientation(uri);
75
76         mOriginalBitmapSmall = loadScaledBitmap(uri, 160);
77         mOriginalBitmapLarge = loadScaledBitmap(uri, size);
78         updateBitmaps();
79     }
80
81     public Uri getUri() {
82         return mUri;
83     }
84
85     public Rect getOriginalBounds() {
86         return mOriginalBounds;
87     }
88
89     private int getOrientation(Uri uri) {
90         if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
91             return getOrientationFromPath(uri.getPath());
92         }
93
94         Cursor cursor = null;
95         try {
96             cursor = mContext.getContentResolver().query(uri,
97                     new String[] {
98                         MediaStore.Images.ImageColumns.ORIENTATION
99                     },
100                     null, null, null);
101             if (cursor.moveToNext()){
102               int ori =   cursor.getInt(0);
103
104               switch (ori){
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;
109                   default:
110                       return -1;
111               }
112             } else{
113                 return -1;
114             }
115         } catch (SQLiteException e){
116             return ExifInterface.ORIENTATION_UNDEFINED;
117         } finally {
118             Utils.closeSilently(cursor);
119         }
120     }
121
122     private int getOrientationFromPath(String path) {
123         int orientation = -1;
124         try {
125             ExifInterface EXIF = new ExifInterface(path);
126             orientation = EXIF.getAttributeInt(ExifInterface.TAG_ORIENTATION,
127                     1);
128         } catch (IOException e) {
129             e.printStackTrace();
130         }
131         return orientation;
132     }
133
134     private void updateBitmaps() {
135         if (mOrientation > 1) {
136             mOriginalBitmapSmall = rotateToPortrait(mOriginalBitmapSmall,mOrientation);
137             mOriginalBitmapLarge = rotateToPortrait(mOriginalBitmapLarge,mOrientation);
138         }
139         mCache.setOriginalBitmap(mOriginalBitmapSmall);
140         mHiresCache.setOriginalBitmap(mOriginalBitmapLarge);
141         warnListeners();
142     }
143
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) {
152                int tmp = w;
153                w = h;
154                h = tmp;
155            }
156            switch(ori){
157                case ORI_NORMAL:
158                case ORI_ROTATE_90:
159                    matrix.setRotate(90,w/2f,h/2f);
160                    break;
161                case ORI_ROTATE_180:
162                    matrix.setRotate(180,w/2f,h/2f);
163                    break;
164                case ORI_ROTATE_270:
165                    matrix.setRotate(270,w/2f,h/2f);
166                    break;
167                case ORI_FLIP_HOR:
168                    matrix.preScale(-1, 1);
169                    break;
170               case ORI_FLIP_VERT:
171                    matrix.preScale(1, -1);
172                    break;
173                case ORI_TRANSPOSE:
174                    matrix.setRotate(90,w/2f,h/2f);
175                    matrix.preScale(1, -1);
176                    break;
177                case ORI_TRANSVERSE:
178                    matrix.setRotate(270,w/2f,h/2f);
179                    matrix.preScale(1, -1);
180                    break;
181                default:
182             }
183
184         return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
185                 bitmap.getHeight(), matrix, true);
186     }
187
188     private void closeStream(Closeable stream) {
189         if (stream != null) {
190             try {
191                 stream.close();
192             } catch (IOException e) {
193                 e.printStackTrace();
194             }
195         }
196     }
197
198     private Bitmap loadRegionBitmap(Uri uri, Rect bounds) {
199         InputStream is = null;
200         try {
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) {
207             e.printStackTrace();
208         } finally {
209             closeStream(is);
210         }
211         return null;
212     }
213
214     private Bitmap loadScaledBitmap(Uri uri, int size) {
215         InputStream is = null;
216         try {
217             is = mContext.getContentResolver().openInputStream(uri);
218             Log.v(LOGTAG, "loading uri " + uri.getPath() + " input stream: "
219                     + is);
220             BitmapFactory.Options o = new BitmapFactory.Options();
221             o.inJustDecodeBounds = true;
222             BitmapFactory.decodeStream(is, null, o);
223
224             int width_tmp = o.outWidth;
225             int height_tmp = o.outHeight;
226
227             mOriginalBounds = new Rect(0, 0, width_tmp, height_tmp);
228
229             int scale = 1;
230             while (true) {
231                 if (width_tmp / 2 < size || height_tmp / 2 < size)
232                     break;
233                 width_tmp /= 2;
234                 height_tmp /= 2;
235                 scale *= 2;
236             }
237
238             // decode with inSampleSize
239             BitmapFactory.Options o2 = new BitmapFactory.Options();
240             o2.inSampleSize = scale;
241
242             closeStream(is);
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) {
248             e.printStackTrace();
249         } finally {
250             closeStream(is);
251         }
252         return null;
253     }
254
255     public Bitmap getBackgroundBitmap(Resources resources) {
256         if (mBackgroundBitmap == null) {
257             mBackgroundBitmap = BitmapFactory.decodeResource(resources,
258                     R.drawable.filtershow_background);
259         }
260         return mBackgroundBitmap;
261
262     }
263
264     public Bitmap getOriginalBitmapSmall() {
265         return mOriginalBitmapSmall;
266     }
267
268     public Bitmap getOriginalBitmapLarge() {
269         return mOriginalBitmapLarge;
270     }
271
272     public void addListener(ImageShow imageShow) {
273         if (!mListeners.contains(imageShow)) {
274             mListeners.add(imageShow);
275         }
276     }
277
278     public void warnListeners() {
279         for (int i = 0; i < mListeners.size(); i++) {
280             ImageShow imageShow = mListeners.elementAt(i);
281             imageShow.updateImage();
282         }
283     }
284
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,
288             boolean force) {
289         Bitmap bmp = mZoomCache.getImage(imagePreset, bounds);
290         if (force || bmp == null) {
291             bmp = loadRegionBitmap(mUri, bounds);
292             if (bmp != null) {
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);
300                 return bmp2;
301             }
302         }
303         return bmp;
304     }
305
306     // Caching method
307     public Bitmap getImageForPreset(ImageShow caller, ImagePreset imagePreset,
308             boolean hiRes) {
309         if (mOriginalBitmapSmall == null) {
310             return null;
311         }
312         if (mOriginalBitmapLarge == null) {
313             return null;
314         }
315
316         Bitmap filteredImage = null;
317
318         if (hiRes) {
319             filteredImage = mHiresCache.get(imagePreset);
320         } else {
321             filteredImage = mCache.get(imagePreset);
322         }
323
324         if (filteredImage == null) {
325             if (hiRes) {
326                 cachePreset(imagePreset, mHiresCache, caller);
327             } else {
328                 cachePreset(imagePreset, mCache, caller);
329             }
330         }
331         return filteredImage;
332     }
333
334     public void resetImageForPreset(ImagePreset imagePreset, ImageShow caller) {
335         mHiresCache.reset(imagePreset);
336         mCache.reset(imagePreset);
337         mZoomCache.reset(imagePreset);
338     }
339
340     public Uri saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
341             File destination) {
342         BitmapFactory.Options options = new BitmapFactory.Options();
343         options.inMutable = true;
344
345         if (mFullOriginalBitmap != null) {
346             mFullOriginalBitmap.recycle();
347         }
348
349         InputStream is = null;
350         Uri saveUri = null;
351         try {
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
355             // exist)
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() {
361
362                 @Override
363                 public void onComplete(Uri result) {
364                     filterShowActivity.completeSaveImage(result);
365                 }
366
367             }).execute(processedBitmap);
368         } catch (FileNotFoundException e) {
369             e.printStackTrace();
370         } finally {
371             closeStream(is);
372         }
373
374         return saveUri;
375     }
376
377     public void setAdapter(HistoryAdapter adapter) {
378         mAdapter = adapter;
379     }
380
381     public HistoryAdapter getHistory() {
382         return mAdapter;
383     }
384
385     private void cachePreset(ImagePreset preset, Cache cache, ImageShow caller) {
386         cache.prepare(preset);
387         cache.addObserver(caller);
388     }
389 }