Showing posts with label OutOfMemoryError. Show all posts
Showing posts with label OutOfMemoryError. Show all posts

Thursday, April 19, 2012

Loading Large Bitmaps Efficiently in Android

Bitmaps can quickly consume application's available memory budget leading to an application crash due to the dreaded exception:
java.lang.OutofMemoryError: bitmap size exceeds VM budget
  So working with limited memory, ideally we only want to load a lower resolution version of image in memory.The lower resolution version should match the size of the UI component that displays it.For this we have to consider some factors.

  • Estimated memory usage of loading the full image in memory.
  • Amount of memory you are willing to commit to loading this image given any other memory requirements of your application.
  • Dimensions of the target ImageView or UI component that the image is to be loaded into.
  • Screen size and density of the current device.
Here is another simple example for sampling the images. 

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        if (width > height) {
            inSampleSize = Math.round((float)height / (float)reqHeight);
        } else {
            inSampleSize = Math.round((float)width / (float)reqWidth);
        }
    }
    return inSampleSize;
}

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

// for loading an image with size 100 *100 

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));


Reference : http://developer.android.com/training/displaying-bitmaps/index.html

Wednesday, April 11, 2012

Image resize in Android

Some times we need to  reduce the size of images retrieved from camera down to  a few smaller sizes or situations like  OutOfMemoryError due to  bitmap size  , images that showing in a list view or grid view  etc.  Then we resize the picture for reducing the memory usage .  Here is an simple example for doing this. 
/**
  * This function is used to read the original image file from a given path
  * and then scale it and save it to the application's private scope.
  * 
  * @param sourceImageFilePath Complete path name for the file to be opened.
  * @param desiredWidth The desired image width for this image.
  * @param context To identify the context's application package for writing a private image file.
  * @param destinationImageName The file name of the image to be saved within the applications private scope.
  * @return The scaled bitmap image after saved to the destination file.
  */
 
 public static Bitmap saveGalleryImageBitmap(String sourceImageFilePath, int desiredWidth, Context context, String destinationImageName, int rotate) {
  Bitmap scaledBitmap = null;
  Bitmap rotatedBitmap = null;
  try {
   // Get the source image's dimensions
   BitmapFactory.Options options = new BitmapFactory.Options();
   options.inJustDecodeBounds = true;
   BitmapFactory.decodeFile(sourceImageFilePath, options);
 
   int srcWidth = options.outWidth;
 
   // Only scale if the source is big enough. This code is just trying to fit a image into a certain width.
   if (desiredWidth > srcWidth)
    desiredWidth = srcWidth;
 
   // Calculate the correct inSampleSize/scale value. This helps reduce
   // memory use. It should be a power of 2
   // from:
   // http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue/823966#823966
   int inSampleSize = 1;
   while (srcWidth / 2 > desiredWidth) {
    srcWidth /= 2;
    inSampleSize *= 2;
   }
 
   float desiredScale = (float) desiredWidth / srcWidth;
 
   // Decode with inSampleSize
   options.inJustDecodeBounds = false;
   options.inDither = false;
   options.inSampleSize = inSampleSize;
   options.inScaled = false;
   options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
   Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(sourceImageFilePath, options);
 
   // Resize
   Matrix matrix = new Matrix();
   matrix.postScale(desiredScale, desiredScale);
   scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0, sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
   sampledSrcBitmap = null;
 
   // Save
   FileOutputStream out = null;
   try {
    out = context.openFileOutput(destinationImageName, Context.MODE_PRIVATE);
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   }
   
   Matrix rotateMatrix = new Matrix();
   rotateMatrix.postRotate(rotate); // For rotating the image.
   rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), rotateMatrix, true);
   scaledBitmap = null;
   
   rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);   
  } catch (OutOfMemoryError e) {
   scaledBitmap = null;
   e.printStackTrace();
  } catch (Exception e) {
   scaledBitmap = null;
   e.printStackTrace();
  }
  return rotatedBitmap;
 }