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