summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/filtershow/cache/ImageLoader.java')
-rw-r--r--src/com/android/gallery3d/filtershow/cache/ImageLoader.java363
1 files changed, 209 insertions, 154 deletions
diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
index ca8c7a392..7c594c67f 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -29,18 +29,15 @@ import android.graphics.Rect;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
+import android.webkit.MimeTypeMap;
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMeta;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
-import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.filtershow.imageshow.MasterImage;
-import com.android.gallery3d.filtershow.pipeline.ImagePreset;
-import com.android.gallery3d.filtershow.tools.SaveCopyTask;
import com.android.gallery3d.util.XmpUtilHelper;
-import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -50,8 +47,6 @@ public final class ImageLoader {
private static final String LOGTAG = "ImageLoader";
public static final String JPEG_MIME_TYPE = "image/jpeg";
-
- public static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
public static final int DEFAULT_COMPRESS_QUALITY = 95;
public static final int ORI_NORMAL = ExifInterface.Orientation.TOP_LEFT;
@@ -65,42 +60,39 @@ public final class ImageLoader {
private static final int BITMAP_LOAD_BACKOUT_ATTEMPTS = 5;
- public static final int MAX_BITMAP_DIM = 900;
- public static final int SMALL_BITMAP_DIM = 160;
-
private ImageLoader() {}
- public static int getOrientation(Context context, Uri uri) {
- if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
- String mimeType = context.getContentResolver().getType(uri);
- if (mimeType != ImageLoader.JPEG_MIME_TYPE) {
- return -1;
- }
- String path = uri.getPath();
- int orientation = -1;
- ExifInterface exif = new ExifInterface();
- try {
- exif.readExif(path);
- orientation = ExifInterface.getRotationForOrientationValue(
- exif.getTagIntValue(ExifInterface.TAG_ORIENTATION).shortValue());
- } catch (IOException e) {
- Log.w(LOGTAG, "Failed to read EXIF orientation", e);
- }
- return orientation;
+ /**
+ * Returns the Mime type for a Url. Safe to use with Urls that do not
+ * come from Gallery's content provider.
+ */
+ public static String getMimeType(Uri src) {
+ String postfix = MimeTypeMap.getFileExtensionFromUrl(src.toString());
+ String ret = null;
+ if (postfix != null) {
+ ret = MimeTypeMap.getSingleton().getMimeTypeFromExtension(postfix);
}
+ return ret;
+ }
+
+ /**
+ * Returns the image's orientation flag. Defaults to ORI_NORMAL if no valid
+ * orientation was found.
+ */
+ public static int getMetadataOrientation(Context context, Uri uri) {
+ if (uri == null || context == null) {
+ throw new IllegalArgumentException("bad argument to getOrientation");
+ }
+
+ // First try to find orientation data in Gallery's ContentProvider.
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri,
- new String[] {
- MediaStore.Images.ImageColumns.ORIENTATION
- },
+ new String[] { MediaStore.Images.ImageColumns.ORIENTATION },
null, null, null);
- if (cursor.moveToNext()) {
+ if (cursor != null && cursor.moveToNext()) {
int ori = cursor.getInt(0);
-
switch (ori) {
- case 0:
- return ORI_NORMAL;
case 90:
return ORI_ROTATE_90;
case 270:
@@ -108,24 +100,73 @@ public final class ImageLoader {
case 180:
return ORI_ROTATE_180;
default:
- return -1;
+ return ORI_NORMAL;
}
- } else {
- return -1;
}
} catch (SQLiteException e) {
- return -1;
+ // Do nothing
} catch (IllegalArgumentException e) {
- return -1;
+ // Do nothing
} finally {
Utils.closeSilently(cursor);
}
+
+ // Fall back to checking EXIF tags in file.
+ if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
+ String mimeType = getMimeType(uri);
+ if (!JPEG_MIME_TYPE.equals(mimeType)) {
+ return ORI_NORMAL;
+ }
+ String path = uri.getPath();
+ ExifInterface exif = new ExifInterface();
+ try {
+ exif.readExif(path);
+ Integer tagval = exif.getTagIntValue(ExifInterface.TAG_ORIENTATION);
+ if (tagval != null) {
+ int orientation = tagval;
+ switch(orientation) {
+ case ORI_NORMAL:
+ case ORI_ROTATE_90:
+ case ORI_ROTATE_180:
+ case ORI_ROTATE_270:
+ case ORI_FLIP_HOR:
+ case ORI_FLIP_VERT:
+ case ORI_TRANSPOSE:
+ case ORI_TRANSVERSE:
+ return orientation;
+ default:
+ return ORI_NORMAL;
+ }
+ }
+ } catch (IOException e) {
+ Log.w(LOGTAG, "Failed to read EXIF orientation", e);
+ }
+ }
+ return ORI_NORMAL;
}
- public static Bitmap decodeImage(Context context, int id, BitmapFactory.Options options) {
- return BitmapFactory.decodeResource(context.getResources(), id, options);
+ /**
+ * Returns the rotation of image at the given URI as one of 0, 90, 180,
+ * 270. Defaults to 0.
+ */
+ public static int getMetadataRotation(Context context, Uri uri) {
+ int orientation = getMetadataOrientation(context, uri);
+ switch(orientation) {
+ case ORI_ROTATE_90:
+ return 90;
+ case ORI_ROTATE_180:
+ return 180;
+ case ORI_ROTATE_270:
+ return 270;
+ default:
+ return 0;
+ }
}
+ /**
+ * Takes an orientation and a bitmap, and returns the bitmap transformed
+ * to that orientation.
+ */
public static Bitmap orientBitmap(Bitmap bitmap, int ori) {
Matrix matrix = new Matrix();
int w = bitmap.getWidth();
@@ -166,12 +207,16 @@ public final class ImageLoader {
default:
return bitmap;
}
-
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
}
- private static Bitmap loadRegionBitmap(Context context, Uri uri, BitmapFactory.Options options,
+ /**
+ * Returns the bitmap for the rectangular region given by "bounds"
+ * if it is a subset of the bitmap stored at uri. Otherwise returns
+ * null.
+ */
+ public static Bitmap loadRegionBitmap(Context context, Uri uri, BitmapFactory.Options options,
Rect bounds) {
InputStream is = null;
try {
@@ -184,75 +229,120 @@ public final class ImageLoader {
}
return decoder.decodeRegion(bounds, options);
} catch (FileNotFoundException e) {
- Log.e(LOGTAG, "FileNotFoundException: " + uri);
- } catch (Exception e) {
- e.printStackTrace();
+ Log.e(LOGTAG, "FileNotFoundException for " + uri, e);
+ } catch (IOException e) {
+ Log.e(LOGTAG, "FileNotFoundException for " + uri, e);
} finally {
Utils.closeSilently(is);
}
return null;
}
- public static Bitmap loadOrientedScaledBitmap(MasterImage master, Context context, Uri uri,
- int size, boolean enforceSize, int orientation) {
- Bitmap bmap = loadScaledBitmap(master, context, uri, size, enforceSize);
- if (bmap != null) {
- bmap = orientBitmap(bmap, orientation);
- }
- return bmap;
+ /**
+ * Returns the bounds of the bitmap stored at a given Url.
+ */
+ public static Rect loadBitmapBounds(Context context, Uri uri) {
+ BitmapFactory.Options o = new BitmapFactory.Options();
+ loadBitmap(context, uri, o);
+ return new Rect(0, 0, o.outWidth, o.outHeight);
}
- public static Bitmap loadScaledBitmap(MasterImage master, Context context, Uri uri, int size,
- boolean enforceSize) {
- InputStream is = null;
- try {
- is = context.getContentResolver().openInputStream(uri);
- Log.v(LOGTAG, "loading uri " + uri.getPath() + " input stream: "
- + is);
- BitmapFactory.Options o = new BitmapFactory.Options();
- o.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(is, null, o);
-
- int width_tmp = o.outWidth;
- int height_tmp = o.outHeight;
-
- master.setOriginalBounds(new Rect(0, 0, width_tmp, height_tmp));
-
- int scale = 1;
- while (true) {
- if (width_tmp <= 2 || height_tmp <= 2) {
- break;
- }
- if (!enforceSize
- || (width_tmp <= MAX_BITMAP_DIM
- && height_tmp <= MAX_BITMAP_DIM)) {
- if (width_tmp / 2 < size || height_tmp / 2 < size) {
- break;
- }
- }
- width_tmp /= 2;
- height_tmp /= 2;
- scale *= 2;
- }
+ /**
+ * Loads a bitmap that has been downsampled using sampleSize from a given url.
+ */
+ public static Bitmap loadDownsampledBitmap(Context context, Uri uri, int sampleSize) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inMutable = true;
+ options.inSampleSize = sampleSize;
+ return loadBitmap(context, uri, options);
+ }
- // decode with inSampleSize
- BitmapFactory.Options o2 = new BitmapFactory.Options();
- o2.inSampleSize = scale;
- o2.inMutable = true;
- Utils.closeSilently(is);
+ /**
+ * Returns the bitmap from the given uri loaded using the given options.
+ * Returns null on failure.
+ */
+ public static Bitmap loadBitmap(Context context, Uri uri, BitmapFactory.Options o) {
+ if (uri == null || context == null) {
+ throw new IllegalArgumentException("bad argument to loadBitmap");
+ }
+ InputStream is = null;
+ try {
is = context.getContentResolver().openInputStream(uri);
- return BitmapFactory.decodeStream(is, null, o2);
+ return BitmapFactory.decodeStream(is, null, o);
} catch (FileNotFoundException e) {
- Log.e(LOGTAG, "FileNotFoundException: " + uri);
- } catch (Exception e) {
- e.printStackTrace();
+ Log.e(LOGTAG, "FileNotFoundException for " + uri, e);
} finally {
Utils.closeSilently(is);
}
return null;
}
+ /**
+ * Loads a bitmap at a given URI that is downsampled so that both sides are
+ * smaller than maxSideLength. The Bitmap's original dimensions are stored
+ * in the rect originalBounds.
+ *
+ * @param uri URI of image to open.
+ * @param context context whose ContentResolver to use.
+ * @param maxSideLength max side length of returned bitmap.
+ * @param originalBounds set to the actual bounds of the stored bitmap.
+ * @return downsampled bitmap or null if this operation failed.
+ */
+ public static Bitmap loadConstrainedBitmap(Uri uri, Context context, int maxSideLength,
+ Rect originalBounds) {
+ if (maxSideLength <= 0 || originalBounds == null || uri == null || context == null) {
+ throw new IllegalArgumentException("bad argument to getScaledBitmap");
+ }
+ // Get width and height of stored bitmap
+ Rect storedBounds = loadBitmapBounds(context, uri);
+ originalBounds.set(storedBounds);
+ int w = storedBounds.width();
+ int h = storedBounds.height();
+
+ // If bitmap cannot be decoded, return null
+ if (w <= 0 || h <= 0) {
+ return null;
+ }
+
+ // Find best downsampling size
+ int imageSide = Math.max(w, h);
+ int sampleSize = 1;
+ while (imageSide > maxSideLength) {
+ imageSide >>>= 1;
+ sampleSize <<= 1;
+ }
+
+ // Make sure sample size is reasonable
+ if (sampleSize <= 0 ||
+ 0 >= (int) (Math.min(w, h) / sampleSize)) {
+ return null;
+ }
+ return loadDownsampledBitmap(context, uri, sampleSize);
+ }
+
+ /**
+ * Loads a bitmap at a given URI that is downsampled so that both sides are
+ * smaller than maxSideLength. The Bitmap's original dimensions are stored
+ * in the rect originalBounds. The output is also transformed to the given
+ * orientation.
+ *
+ * @param uri URI of image to open.
+ * @param context context whose ContentResolver to use.
+ * @param maxSideLength max side length of returned bitmap.
+ * @param orientation the orientation to transform the bitmap to.
+ * @param originalBounds set to the actual bounds of the stored bitmap.
+ * @return downsampled bitmap or null if this operation failed.
+ */
+ public static Bitmap loadOrientedConstrainedBitmap(Uri uri, Context context, int maxSideLength,
+ int orientation, Rect originalBounds) {
+ Bitmap bmap = loadConstrainedBitmap(uri, context, maxSideLength, originalBounds);
+ if (bmap != null) {
+ bmap = orientBitmap(bmap, orientation);
+ }
+ return bmap;
+ }
+
public static Bitmap getScaleOneImageForPreset(Context context, Uri uri, Rect bounds,
Rect destination) {
BitmapFactory.Options options = new BitmapFactory.Options();
@@ -272,88 +362,53 @@ public final class ImageLoader {
return bmp;
}
- public static void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity,
- File destination) {
- Uri selectedImageUri = filterShowActivity.getSelectedImageUri();
- new SaveCopyTask(filterShowActivity, MasterImage.getImage().getUri(), selectedImageUri,
- destination,
- new SaveCopyTask.Callback() {
-
- @Override
- public void onComplete(Uri result) {
- filterShowActivity.completeSaveImage(result);
- }
-
- }).execute(preset);
- }
-
- public static Bitmap loadMutableBitmap(Context context, Uri sourceUri) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- return loadMutableBitmap(context, sourceUri, options);
- }
-
- public static Bitmap loadMutableBitmap(Context context, Uri sourceUri,
- BitmapFactory.Options options) {
- // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
- // exist)
- options.inMutable = true;
-
- Bitmap bitmap = decodeUriWithBackouts(context, sourceUri, options);
- if (bitmap == null) {
- return null;
- }
- int orientation = ImageLoader.getOrientation(context, sourceUri);
- bitmap = ImageLoader.orientBitmap(bitmap, orientation);
- return bitmap;
- }
-
- public static Bitmap decodeUriWithBackouts(Context context, Uri sourceUri,
- BitmapFactory.Options options) {
+ /**
+ * Loads a bitmap that is downsampled by at least the input sample size. In
+ * low-memory situations, the bitmap may be downsampled further.
+ */
+ public static Bitmap loadBitmapWithBackouts(Context context, Uri sourceUri, int sampleSize) {
boolean noBitmap = true;
int num_tries = 0;
- InputStream is = getInputStream(context, sourceUri);
-
- if (options.inSampleSize < 1) {
- options.inSampleSize = 1;
+ if (sampleSize <= 0) {
+ sampleSize = 1;
}
- // Stopgap fix for low-memory devices.
Bitmap bmap = null;
while (noBitmap) {
- if (is == null) {
- return null;
- }
try {
// Try to decode, downsample if low-memory.
- bmap = BitmapFactory.decodeStream(is, null, options);
+ bmap = loadDownsampledBitmap(context, sourceUri, sampleSize);
noBitmap = false;
} catch (java.lang.OutOfMemoryError e) {
- // Try 5 times before failing for good.
+ // Try with more downsampling before failing for good.
if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) {
throw e;
}
- is = null;
bmap = null;
System.gc();
- is = getInputStream(context, sourceUri);
- options.inSampleSize *= 2;
+ sampleSize *= 2;
}
}
- Utils.closeSilently(is);
return bmap;
}
- private static InputStream getInputStream(Context context, Uri sourceUri) {
- InputStream is = null;
- try {
- is = context.getContentResolver().openInputStream(sourceUri);
- } catch (FileNotFoundException e) {
- Log.w(LOGTAG, "could not load bitmap ", e);
- Utils.closeSilently(is);
- is = null;
+ /**
+ * Loads an oriented bitmap that is downsampled by at least the input sample
+ * size. In low-memory situations, the bitmap may be downsampled further.
+ */
+ public static Bitmap loadOrientedBitmapWithBackouts(Context context, Uri sourceUri,
+ int sampleSize) {
+ Bitmap bitmap = loadBitmapWithBackouts(context, sourceUri, sampleSize);
+ if (bitmap == null) {
+ return null;
}
- return is;
+ int orientation = getMetadataOrientation(context, sourceUri);
+ bitmap = orientBitmap(bitmap, orientation);
+ return bitmap;
}
+ /**
+ * Loads bitmap from a resource that may be downsampled in low-memory situations.
+ */
public static Bitmap decodeResourceWithBackouts(Resources res, BitmapFactory.Options options,
int id) {
boolean noBitmap = true;
@@ -370,7 +425,7 @@ public final class ImageLoader {
res, id, options);
noBitmap = false;
} catch (java.lang.OutOfMemoryError e) {
- // Try 5 times before failing for good.
+ // Retry before failing for good.
if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) {
throw e;
}