Home | History | Annotate | Download | only in displaying-bitmaps
      1 page.title=Loading Large Bitmaps Efficiently
      2 parent.title=Displaying Bitmaps Efficiently
      3 parent.link=index.html
      4 
      5 trainingnavtop=true
      6 
      7 @jd:body
      8 
      9 <div id="tb-wrapper">
     10 <div id="tb">
     11 
     12 <h2>This lesson teaches you to</h2>
     13 <ol>
     14   <li><a href="#read-bitmap">Read Bitmap Dimensions and Type</a></li>
     15   <li><a href="#load-bitmap">Load a Scaled Down Version into Memory</a></li>
     16 </ol>
     17 
     18 <h2>Try it out</h2>
     19 
     20 <div class="download-box">
     21   <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
     22   <p class="filename">BitmapFun.zip</p>
     23 </div>
     24 
     25 </div>
     26 </div>
     27 
     28 <p>Images come in all shapes and sizes. In many cases they are larger than required for a typical
     29 application user interface (UI). For example, the system Gallery application displays photos taken
     30 using your Android devices's camera which are typically much higher resolution than the screen
     31 density of your device.</p>
     32 
     33 <p>Given that you are working with limited memory, ideally you only want to load a lower resolution
     34 version in memory. The lower resolution version should match the size of the UI component that
     35 displays it. An image with a higher resolution does not provide any visible benefit, but still takes
     36 up precious memory and incurs additional performance overhead due to additional on the fly
     37 scaling.</p>
     38 
     39 <p>This lesson walks you through decoding large bitmaps without exceeding the per application
     40 memory limit by loading a smaller subsampled version in memory.</p>
     41 
     42 <h2 id="read-bitmap">Read Bitmap Dimensions and Type</h2>
     43 
     44 <p>The {@link android.graphics.BitmapFactory} class provides several decoding methods ({@link
     45 android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
     46 decodeByteArray()}, {@link
     47 android.graphics.BitmapFactory#decodeFile(java.lang.String,android.graphics.BitmapFactory.Options)
     48 decodeFile()}, {@link
     49 android.graphics.BitmapFactory#decodeResource(android.content.res.Resources,int,android.graphics.BitmapFactory.Options)
     50 decodeResource()}, etc.) for creating a {@link android.graphics.Bitmap} from various sources. Choose
     51 the most appropriate decode method based on your image data source. These methods attempt to
     52 allocate memory for the constructed bitmap and therefore can easily result in an {@code OutOfMemory}
     53 exception. Each type of decode method has additional signatures that let you specify decoding
     54 options via the {@link android.graphics.BitmapFactory.Options} class. Setting the {@link
     55 android.graphics.BitmapFactory.Options#inJustDecodeBounds} property to {@code true} while decoding
     56 avoids memory allocation, returning {@code null} for the bitmap object but setting {@link
     57 android.graphics.BitmapFactory.Options#outWidth}, {@link
     58 android.graphics.BitmapFactory.Options#outHeight} and {@link
     59 android.graphics.BitmapFactory.Options#outMimeType}. This technique allows you to read the
     60 dimensions and type of the image data prior to construction (and memory allocation) of the
     61 bitmap.</p>
     62 
     63 <pre>
     64 BitmapFactory.Options options = new BitmapFactory.Options();
     65 options.inJustDecodeBounds = true;
     66 BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
     67 int imageHeight = options.outHeight;
     68 int imageWidth = options.outWidth;
     69 String imageType = options.outMimeType;
     70 </pre>
     71 
     72 <p>To avoid {@code java.lang.OutOfMemory} exceptions, check the dimensions of a bitmap before
     73 decoding it, unless you absolutely trust the source to provide you with predictably sized image data
     74 that comfortably fits within the available memory.</p>
     75 
     76 <h2 id="load-bitmap">Load a Scaled Down Version into Memory</h2>
     77 
     78 <p>Now that the image dimensions are known, they can be used to decide if the full image should be
     79 loaded into memory or if a subsampled version should be loaded instead. Here are some factors to
     80 consider:</p>
     81 
     82 <ul>
     83   <li>Estimated memory usage of loading the full image in memory.</li>
     84   <li>Amount of memory you are willing to commit to loading this image given any other memory
     85   requirements of your application.</li>
     86   <li>Dimensions of the target {@link android.widget.ImageView} or UI component that the image
     87   is to be loaded into.</li>
     88   <li>Screen size and density of the current device.</li>
     89 </ul>
     90 
     91 <p>For example, its not worth loading a 1024x768 pixel image into memory if it will eventually be
     92 displayed in a 128x96 pixel thumbnail in an {@link android.widget.ImageView}.</p>
     93 
     94 <p>To tell the decoder to subsample the image, loading a smaller version into memory, set {@link
     95 android.graphics.BitmapFactory.Options#inSampleSize} to {@code true} in your {@link
     96 android.graphics.BitmapFactory.Options} object. For example, an image with resolution 2048x1536 that
     97 is decoded with an {@link android.graphics.BitmapFactory.Options#inSampleSize} of 4 produces a
     98 bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full
     99 image (assuming a bitmap configuration of {@link android.graphics.Bitmap.Config ARGB_8888}). Heres
    100 a method to calculate a sample size value that is a power of two based on a target width and
    101 height:</p>
    102 
    103 <pre>
    104 public static int calculateInSampleSize(
    105             BitmapFactory.Options options, int reqWidth, int reqHeight) {
    106     // Raw height and width of image
    107     final int height = options.outHeight;
    108     final int width = options.outWidth;
    109     int inSampleSize = 1;
    110 
    111     if (height &gt; reqHeight || width &gt; reqWidth) {
    112 
    113         final int halfHeight = height / 2;
    114         final int halfWidth = width / 2;
    115 
    116         // Calculate the largest inSampleSize value that is a power of 2 and keeps both
    117         // height and width larger than the requested height and width.
    118         while ((halfHeight / inSampleSize) &gt; reqHeight
    119                 && (halfWidth / inSampleSize) &gt; reqWidth) {
    120             inSampleSize *= 2;
    121         }
    122     }
    123 
    124     return inSampleSize;
    125 }
    126 </pre>
    127 
    128 <p class="note"><strong>Note:</strong> A power of two value is calculated because the decoder uses
    129 a final value by rounding down to the nearest power of two, as per the {@link
    130 android.graphics.BitmapFactory.Options#inSampleSize} documentation.</p>
    131 
    132 <p>To use this method, first decode with {@link
    133 android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code true}, pass the options
    134 through and then decode again using the new {@link
    135 android.graphics.BitmapFactory.Options#inSampleSize} value and {@link
    136 android.graphics.BitmapFactory.Options#inJustDecodeBounds} set to {@code false}:</p>
    137 
    138 <a name="decodeSampledBitmapFromResource"></a>
    139 <pre>
    140 public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    141         int reqWidth, int reqHeight) {
    142 
    143     // First decode with inJustDecodeBounds=true to check dimensions
    144     final BitmapFactory.Options options = new BitmapFactory.Options();
    145     options.inJustDecodeBounds = true;
    146     BitmapFactory.decodeResource(res, resId, options);
    147 
    148     // Calculate inSampleSize
    149     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    150 
    151     // Decode bitmap with inSampleSize set
    152     options.inJustDecodeBounds = false;
    153     return BitmapFactory.decodeResource(res, resId, options);
    154 }
    155 </pre>
    156 
    157 <p>This method makes it easy to load a bitmap of arbitrarily large size into an {@link
    158 android.widget.ImageView} that displays a 100x100 pixel thumbnail, as shown in the following example
    159 code:</p>
    160 
    161 <pre>
    162 mImageView.setImageBitmap(
    163     decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
    164 </pre>
    165 
    166 <p>You can follow a similar process to decode bitmaps from other sources, by substituting the
    167 appropriate {@link
    168 android.graphics.BitmapFactory#decodeByteArray(byte[],int,int,android.graphics.BitmapFactory.Options)
    169 BitmapFactory.decode*} method as needed.</p>
    170