1

First off, I have read many posts and articles about out of memory exceptions but none of them have helped with my situation. What I'm trying to do is load an image from the sd card but scale it to an exact pixel size.

I first get the width and height of the image and calculate the sample size:

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(backgroundPath, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, getWidth(), getHeight());

Here's how I get the sample size (although its not really relevant):

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;

    // NOTE: we could use Math.floor here for potential better image quality
    // however, this also results in more out of memory issues
    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;
}

Now that I have a sample size I load the image from disk to an approximate size (sample size):

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    options.inPurgeable = true;
    Bitmap bmp = BitmapFactory.decodeFile(backgroundPath, options);

Now, I scale this bitmap that I have created to the exact size I need and clean up:

    // scale the bitmap to the exact size we need
    Bitmap editedBmp = Bitmap.createScaledBitmap(bmp, (int) (width * scaleFactor), (int) (height * scaleFactor), true); 

    // clean up first bitmap
    bmp.recycle();
    bmp = null;
    System.gc();    // I know you shouldnt do this, but I'm desperate 

The above step is usually get my out of memory exception. Does anyone know a way to load an exact size bitmap from disk to avoid having to create two separate bitmaps like above?

Also, it seems like more exceptions occur when the user runs this code for a second time (sets a new image). However, I make sure to unload the drawable that was created from the bitmap which allows it to be garbage collected before this code is run again.

Any suggestions?

Thanks, Nick

Nick
  • 6,375
  • 5
  • 36
  • 53
  • Before creating the `editedBmp`, log the width and height of it, as well as the scaleFactor to make sure that your parameters are correct. – Jave Feb 22 '13 at 18:16
  • instead of createScaledBitmap can you do what is done here? http://developer.android.com/training/displaying-bitmaps/load-bitmap.html , just changing the decodeResources to decodeFile and passing in the size options? – John Boker Feb 22 '13 at 18:17
  • @Jave yes, the width, height, and scaleFactor are correct. A out of memory exception doesnt always happen and when it does not the image is the proper size. – Nick Feb 22 '13 at 18:20
  • @JohnBoker That example uses a sample size (similar to what I'm doing in the first step). This will only give you an approximate size and not the actual size it needs to be. – Nick Feb 22 '13 at 18:24
  • You're doubling the memory usage for bitmap allocation by decoding and then scaling to exact size. Why not just use bmp? If it's going into an ImageView, you can play with ScaleType to compare interpolation vs. crop. – Krylez Feb 22 '13 at 18:37
  • @Krylez Unfortunately, its not going into a ImageView. The bmp is being drawn onto a canvas, so it needs to be the proper size beforehand – Nick Feb 22 '13 at 18:43
  • @Nick instead of resizing the full bitmap can you use that example by google and get a sample size a little larger than you need, then resize that bitmap? it might be more memory efficient that way. – John Boker Feb 22 '13 at 18:59
  • @JohnBoker That's exactly what I'm doing in the code above – Nick Feb 22 '13 at 19:06
  • Oh, okay. In that case you still don't need to create the intermediate bitmap. You can use the drawbitmap method that specifies source & destination Rect or you can do the scaling in a Matrix, whichever you find simpler. – Krylez Feb 22 '13 at 19:20
  • Hmm...I'll have to give that a shot and see if it works. Thanks. – Nick Feb 22 '13 at 22:28
  • @Krylez It looked like that did the trick. Thanks! If you put your comment as an answer, I will accept it. – Nick Feb 27 '13 at 18:39

2 Answers2

2

In your case there's no need to create the intermediate bitmap after you've performed the first decode. Since you're drawing to to a Canvas, you can use either the following methods (whichever you find most convenient) to scale the image to the perfect size.

drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)

drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
Krylez
  • 17,414
  • 4
  • 32
  • 41
0

Maybe this method would be helpful, I think I pulled it off of stackoverflow myself. It solved my out of memory exception issue.

private Bitmap decodeFile(File f){
    try {
        //Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f),null,o);

        //The new size we want to scale to
        final int REQUIRED_SIZE=250;

        //Find the correct scale value. It should be the power of 2.
        int scale=1;
        while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
            scale*=2;

        //Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize=scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}

    return null;
} 
Jacob
  • 920
  • 6
  • 19
  • This uses a sample size like what I'm doing in the first step in my question. This does not provide you with the exact size of the required size. – Nick Feb 22 '13 at 18:44
  • I think the Bitmap.createScaledBitmap() call that you are using brings a full bitmap into memory. I use the above method to create a scaled bitmap and then i use Bitmap.createScaledBitmap() on the returned bitmap to size it to the finishe size that I need. Sorry I wasn't of help to you. – Jacob Feb 22 '13 at 19:22