6

In onPictureTaken, I want to do the following:

Bitmap decodedPicture = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = new Matrix();
matrix.preScale(-1.0f, 1.0f);
Bitmap picture = Bitmap.createBitmap(decodedPicture, 0, 0, decodedPicture.getWidth(), decodedPicture.getHeight(), matrix, false);

View v1 = mainLayout.getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap screenshot = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);

Bitmap scaledPicture = Bitmap.createScaledBitmap(picture, screenshot.getWidth(), screenshot.getHeight(), true);

Bitmap compos = Bitmap.createBitmap(scaledPicture.getWidth(), scaledPicture.getHeight(), Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(compos);
canvas.drawBitmap(scaledPicture, new Matrix(), null);
canvas.drawBitmap(screenshot, new Matrix(), null);

MediaStore.Images.Media.insertImage(getContentResolver(), compos, "name" , "description");
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));

My only requirement is that I'd like to save a high-quality photo... Seems I might have to sacrifice that.

On my Nexus 4 and newer devices, this code runs fine and as expected. But on older devices that have less memory, I'm running out of RAM! :(

How do I do the same image manipulation without running up against the memory limit?? I'm not trying to display these images on screen, so the solutions that have to do with a scaled down image don't really apply here...

1
  • Can you null some references, and then hint a GC? That should remove some unnecessary data... Commented Apr 29, 2013 at 17:39

4 Answers 4

3

you need to read the bitmap in with an increased sample size. the trick is finding the correct sample size that won't result in reduced resolution when you ultimately scale the image. i wrote a blog entry about it here that includes a nice utility class for scaling,

http://zerocredibility.wordpress.com/2011/01/27/android-bitmap-scaling/

you could probably simplify that class quite a bit depending on your specific needs.

the jist is to read just the size of the bitmap. calculate the optimal sample size based on your desired scaled size, read the bitmap in using that sample size, then fine-scale it to exactly the size you want.

Sign up to request clarification or add additional context in comments.

Comments

0

You have so many Bitmap object lying around. try recycling/reusing some of this.

Not exactly sure what is your requirement is but i can see you can save some memory by simply doing this.

        Bitmap decodedPicture = BitmapFactory.decodeByteArray(data, 0, data.length);
        Matrix matrix = new Matrix();
        matrix.preScale(-1.0f, 1.0f);
        Bitmap picture = Bitmap.createBitmap(decodedPicture, 0, 0, decodedPicture.getWidth(), decodedPicture.getHeight(), matrix, false);

        decodedPicture.recycle();
        decodedPicture=null;

        View v1 = mainLayout.getRootView();
        v1.setDrawingCacheEnabled(true);
        Bitmap screenshot = Bitmap.createBitmap(v1.getDrawingCache());
        v1.setDrawingCacheEnabled(false);

        Bitmap scaledPicture = Bitmap.createScaledBitmap(picture, screenshot.getWidth(), screenshot.getHeight(), true);

        picture.recycle();
        picture=null;

        Bitmap compos = Bitmap.createBitmap(scaledPicture.getWidth(), scaledPicture.getHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(compos);
        canvas.drawBitmap(scaledPicture, new Matrix(), null);
        canvas.drawBitmap(screenshot, new Matrix(), null);

        MediaStore.Images.Media.insertImage(getContentResolver(), compos, "name" , "description");
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));

Also look into your memory footprint, make sure device wise memory you are using are is not too big.

FYI, on post honycomb devices bitmap pixel image allocated on native layer. You need recycle() or finalizer() to restore memory

1 Comment

using finalize() is a bad idea. stackoverflow.com/questions/2506488/java-finalize-method-call. also, from the Android documentation on recycle(): 'This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.'
0

Considering you don't want to resize your bitmap and don't want to display it, I'd do something like this:

  1. Load the Bitmap with inJustDecodeBounds to see its original height and width (code from here)

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);
    
    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
  2. Depending on the size and memory you have, you can directly process it from there (i.e. load the Bitmap) or proceed to load a number of chunks of said Bitmap with the Bitmap.createBitmap method that allows you to only load a chunk of data. Optionally: consider converting it into a byte array (see code below) and null+ recycle() before you process the chunk.

code

ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
bitmapPicture.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY, byteArrayBitmapStream);
byte[] b = byteArrayBitmapStream.toByteArray();

Comments

0

I use for work with Bitmap class WeakReference and after I always call recycle on the instance object WeakReference, the snippet code for rotate image:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
WeakReference<Bitmap> imageBitmapReference = new WeakReference<Bitmap>(BitmapFactory.decodeByteArray(params[0], 0, params[0].length, options));

Matrix mat = new Matrix();
mat.postRotate(90.0f); 
imageBitmapReference = new WeakReference<Bitmap (Bitmap.createBitmap(imageBitmapReference.get(), 0, 0, resolution[0], resolution[1], mat, true));

FileOutputStream fos = new FileOutputStream(filename);
imageBitmapReference.get().compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
imageBitmapReference.get().recycle();

And second solution how work with Bitmap and don't get OutOfMemory Exception is use library Universal Image Loader

(Of course is so the third solution set in your AndroidManifest property android:largeHeap="true" and really DON'T USE THIS property).

The perfect material is on the http://developer.android.com/training/displaying-bitmaps/index.html and video https://www.youtube.com/watch?v=_CruQY55HOk

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.