From 3a4a1d7174ae515b505f6573f8c5ab01902610e5 Mon Sep 17 00:00:00 2001 From: Adam Koch Date: Fri, 25 Oct 2013 15:28:41 -0400 Subject: Displaying Bitmaps Efficiently Training - updates for KitKat inBitmap changes Bug: 10411797 Change-Id: I720d3ade42da31400a6cc7c7ce9e15793f836363 --- .../training/displaying-bitmaps/cache-bitmap.jd | 4 +- .../training/displaying-bitmaps/load-bitmap.jd | 27 +++++----- .../training/displaying-bitmaps/manage-memory.jd | 60 +++++++++++++--------- 3 files changed, 53 insertions(+), 38 deletions(-) (limited to 'docs/html/training') diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd index ad084c2..ff9c3a0 100644 --- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd +++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd @@ -188,8 +188,8 @@ appropriate place to store cached images if they are accessed more frequently, f image gallery application.

The sample code of this class uses a {@code DiskLruCache} implementation that is pulled from the -Android source. Here’s updated example code that adds a disk cache in addition -to the existing memory cache:

+Android source. +Here’s updated example code that adds a disk cache in addition to the existing memory cache:

 private DiskLruCache mDiskLruCache;
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
index 633ffd2..938901f 100644
--- a/docs/html/training/displaying-bitmaps/load-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -97,7 +97,8 @@ android.graphics.BitmapFactory.Options} object. For example, an image with resol
 is decoded with an {@link android.graphics.BitmapFactory.Options#inSampleSize} of 4 produces a
 bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full
 image (assuming a bitmap configuration of {@link android.graphics.Bitmap.Config ARGB_8888}). Here’s
-a method to calculate a the sample size value based on a target width and height:

+a method to calculate a sample size value that is a power of two based on a target width and +height:

 public static int calculateInSampleSize(
@@ -107,26 +108,26 @@ public static int calculateInSampleSize(
     final int width = options.outWidth;
     int inSampleSize = 1;
 
-    if (height > reqHeight || width > reqWidth) {
+    if (height > reqHeight || width > reqWidth) {
 
-        // Calculate ratios of height and width to requested height and width
-        final int heightRatio = Math.round((float) height / (float) reqHeight);
-        final int widthRatio = Math.round((float) width / (float) reqWidth);
+        final int halfHeight = height / 2;
+        final int halfWidth = width / 2;
 
-        // Choose the smallest ratio as inSampleSize value, this will guarantee
-        // a final image with both dimensions larger than or equal to the
-        // requested height and width.
-        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
+        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+        // height and width larger than the requested height and width.
+        while ((halfHeight / inSampleSize) > reqHeight
+                && (halfWidth / inSampleSize) > reqWidth) {
+            inSampleSize *= 2;
+        }
     }
 
     return inSampleSize;
 }
 
-

Note: Using powers of 2 for {@link -android.graphics.BitmapFactory.Options#inSampleSize} values is faster and more efficient for the -decoder. However, if you plan to cache the resized versions in memory or on disk, it’s usually still -worth decoding to the most appropriate image dimensions to save space.

+

Note: A power of two value is calculated because the decoder uses +a final value by rounding down to the nearest power of two, as per the {@link +android.graphics.BitmapFactory.Options#inSampleSize} documentation.

To use this method, first decode with {@link android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code true}, pass the options diff --git a/docs/html/training/displaying-bitmaps/manage-memory.jd b/docs/html/training/displaying-bitmaps/manage-memory.jd index 60ac2e6..0e1279e 100644 --- a/docs/html/training/displaying-bitmaps/manage-memory.jd +++ b/docs/html/training/displaying-bitmaps/manage-memory.jd @@ -56,7 +56,7 @@ bitmap is stored in native memory. It is separate from the bitmap itself, which is stored in the Dalvik heap. The pixel data in native memory is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash. -As of Android 3.0 (API Level 11), the pixel data is stored on the +As of Android 3.0 (API level 11), the pixel data is stored on the Dalvik heap along with the associated bitmap. @@ -140,27 +140,16 @@ private synchronized boolean hasValidBitmap() {

Manage Memory on Android 3.0 and Higher

-

Android 3.0 (API Level 11) introduces the +

Android 3.0 (API level 11) introduces the {@link android.graphics.BitmapFactory.Options#inBitmap BitmapFactory.Options.inBitmap} field. If this option is set, decode methods that take the {@link android.graphics.BitmapFactory.Options Options} object will attempt to reuse an existing bitmap when loading content. This means that the bitmap's memory is reused, resulting in improved performance, and -removing both memory allocation and de-allocation. There are some caveats in using -{@link android.graphics.BitmapFactory.Options#inBitmap}:

-
    -
  • The reused bitmap must be of the same size as the source content (to make -sure that the same amount of memory is used), and in JPEG or PNG format -(whether as a resource or as a stream).
  • - - -
  • The {@link android.graphics.Bitmap.Config configuration} of the reused bitmap -overrides the setting of -{@link android.graphics.BitmapFactory.Options#inPreferredConfig}, if set.
  • - -
  • You should always use the returned bitmap of the decode method, -because you can't assume that reusing the bitmap worked (for example, if there is -a size mismatch).
  • +removing both memory allocation and de-allocation. However, there are certain restrictions with how +{@link android.graphics.BitmapFactory.Options#inBitmap} can be used. In particular, before Android +4.4 (API level 19), only equal sized bitmaps are supported. For details, please see the +{@link android.graphics.BitmapFactory.Options#inBitmap} documentation.

    Save a bitmap for later use

    @@ -283,14 +272,39 @@ protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) { satisfies the size criteria to be used for {@link android.graphics.BitmapFactory.Options#inBitmap}:

    -
    private static boolean canUseForInBitmap(
    +
    static boolean canUseForInBitmap(
             Bitmap candidate, BitmapFactory.Options targetOptions) {
    -    int width = targetOptions.outWidth / targetOptions.inSampleSize;
    -    int height = targetOptions.outHeight / targetOptions.inSampleSize;
     
    -    // Returns true if "candidate" can be used for inBitmap re-use with
    -    // "targetOptions".
    -    return candidate.getWidth() == width && candidate.getHeight() == height;
    +    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    +        // From Android 4.4 (KitKat) onward we can re-use if the byte size of
    +        // the new bitmap is smaller than the reusable bitmap candidate
    +        // allocation byte count.
    +        int width = targetOptions.outWidth / targetOptions.inSampleSize;
    +        int height = targetOptions.outHeight / targetOptions.inSampleSize;
    +        int byteCount = width * height * getBytesPerPixel(candidate.getConfig());
    +        return byteCount <= candidate.getAllocationByteCount();
    +    }
    +
    +    // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1
    +    return candidate.getWidth() == targetOptions.outWidth
    +            && candidate.getHeight() == targetOptions.outHeight
    +            && targetOptions.inSampleSize == 1;
    +}
    +
    +/**
    + * A helper function to return the byte usage per pixel of a bitmap based on its configuration.
    + */
    +static int getBytesPerPixel(Config config) {
    +    if (config == Config.ARGB_8888) {
    +        return 4;
    +    } else if (config == Config.RGB_565) {
    +        return 2;
    +    } else if (config == Config.ARGB_4444) {
    +        return 2;
    +    } else if (config == Config.ALPHA_8) {
    +        return 1;
    +    }
    +    return 1;
     }
    -- cgit v1.1