Displaying Bitmaps Efficiently Training - updates for KitKat inBitmap changes
Bug: 10411797

Change-Id: I720d3ade42da31400a6cc7c7ce9e15793f836363
diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip
index 48bea78..8668897 100644
--- a/docs/downloads/training/BitmapFun.zip
+++ b/docs/downloads/training/BitmapFun.zip
Binary files differ
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 @@
 image gallery application.</p>
 
 <p>The sample code of this class uses a {@code DiskLruCache} implementation that is pulled from the 
-<a href="https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/libcore/io/DiskLruCache.java">Android source</a>. Here’s updated example code that adds a disk cache in addition
-to the existing memory cache:</p>
+<a href="https://android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java">Android source</a>.
+Here’s updated example code that adds a disk cache in addition to the existing memory cache:</p>
 
 <pre>
 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 @@
 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:</p>
+a method to calculate a sample size value that is a power of two based on a target width and
+height:</p>
 
 <pre>
 public static int calculateInSampleSize(
@@ -107,26 +108,26 @@
     final int width = options.outWidth;
     int inSampleSize = 1;
 
-    if (height > reqHeight || width > reqWidth) {
+    if (height &gt; reqHeight || width &gt; 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) &gt; reqHeight
+                && (halfWidth / inSampleSize) &gt; reqWidth) {
+            inSampleSize *= 2;
+        }
     }
 
     return inSampleSize;
 }
 </pre>
 
-<p class="note"><strong>Note:</strong> 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.</p>
+<p class="note"><strong>Note:</strong> 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.</p>
 
 <p>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 @@
 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.
-<strong>As of Android 3.0 (API Level 11), the pixel data is stored on the
+<strong>As of Android 3.0 (API level 11), the pixel data is stored on the
 Dalvik heap along with the associated bitmap.</strong></li>
 
 </ul>
@@ -140,27 +140,16 @@
 
 <h2 id="inBitmap">Manage Memory on Android 3.0 and Higher</h2>
 
-<p>Android 3.0 (API Level 11) introduces the
+<p>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}:</p>
-<ul>
-  <li>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).</li>
-
-
-<li>The {@link android.graphics.Bitmap.Config configuration} of the reused bitmap
-overrides the setting of
-{@link android.graphics.BitmapFactory.Options#inPreferredConfig}, if set. </li>
-
-  <li>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).</li>
+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.
 
 <h3>Save a bitmap for later use</h3>
 
@@ -283,14 +272,39 @@
 satisfies the size criteria to be used for
 {@link android.graphics.BitmapFactory.Options#inBitmap}:</p>
 
-<pre>private static boolean canUseForInBitmap(
+<pre>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 &gt;= 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 &lt;= 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;
 }</pre>
 
 </body>