Immutable Bitmap Crash Error

Immutable bitmap crash error

You have to convert your workingBitmap to Mutable Bitmap for drawing on Canvas. (Note: this method does not help save memory, it will use extra memory)

Bitmap workingBitmap = Bitmap.createBitmap(chosenFrame);
Bitmap mutableBitmap = workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(mutableBitmap);

This answer helps don't waste memory
Convert immutable bitmap to a mutable bitmap

BitmapFactory.decodeResource returns a mutable Bitmap in Android 2.2 and an immutable Bitmap in Android 1.6

You can convert your immutable bitmap to a mutable bitmap.

I found an acceptable solution that uses only the memory of one bitmap.

A source bitmap is raw saved (RandomAccessFile) on disk (no ram memory), then source bitmap is released, (now, there's no bitmap at memory), and after that, the file info is loaded to another bitmap. This way is possible to make a bitmap copy having just one bitmap stored in ram memory per time.

See the full solution and implementation here: Android: convert Immutable Bitmap into Mutable

I add a improvement to this solution, that now works with any type of Bitmaps (ARGB_8888, RGB_565, etc), and deletes the temp file. See my method:

/**
* Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates
* more memory that there is already allocated.
*
* @param imgIn - Source image. It will be released, and should not be used more
* @return a copy of imgIn, but muttable.
*/
public static Bitmap convertToMutable(Bitmap imgIn) {
try {
//this is the file going to use temporally to save the bytes.
// This file will not be a image, it will store the raw image data.
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");

//Open an RandomAccessFile
//Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
//into AndroidManifest.xml file
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

// get the width and height of the source bitmap.
int width = imgIn.getWidth();
int height = imgIn.getHeight();
Config type = imgIn.getConfig();

//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height);
imgIn.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
imgIn.recycle();
System.gc();// try to force the bytes from the imgIn to be released

//Create a new bitmap to load the bitmap again. Probably the memory will be available.
imgIn = Bitmap.createBitmap(width, height, type);
map.position(0);
//load it back from temporary
imgIn.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();

// delete the temp file
file.delete();

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return imgIn;
}

Crash in Android App

If you are working with API Level 11 and up then you need to specify inMutable as part of your BitMapFactory.Options, so;

BitmapFactory.Option bitmapFactoryOptions = new BitmapFactory.Options();
bitmapFactoryOptions.inPreferredConfig = Bitmap.Config.RGB_565;
bitmapFactoryOption.inMutable = true;

If you want to go earlier than API Level 11 then you will want to make a mutable bitmap using the following snippet;

Bitmap immutableBitmap = BitmapFactory.decodeFile(picturePath, bitmapFactoryOptions);
int left = 0;
int top = 0;
int width = immutableBitmap.getWidth();
int height = immutableBitmap.getHeight();
boolean hasAlpha = immutableBitmap.hasAlpha();
Bitmap mutableBitmap = immutableBitmap.createBitmap(width, height, hasAlpha);
Canvas c = new Canvas();
c.setDevice(mutableBitmap);
c.drawBitmap(immutableBitmap, left, top, paint);

The paint variable comes from your pre-existing code.

This answer writes the original bitmap to disk and then creates a mutable version of the bitmap, avoiding the need to have two bitmaps in memory.

Crash using createBitmap and drawBitmap

Sorry for not replying in one month but I was on my vacations :)

Anyway, I finally found the mistake into the code... a really stupid mistake:

When I wrote:

c.drawBitmap(bmOut, 0, 0, paint);

I should put the original bitmap and not the out one:

c.drawBitmap(src, 0, 0, paint);

So final code, a little bit reorganized...

public static Bitmap doFilter (Bitmap src) {
float[] transfMatrix = {
1.5f, 0, 0, 0, 0,
0, 1.5f, 0, 0, 0,
0, 0, 1.5f, 0, 0,
0, 0, 0, 1, 0};
ColorMatrix cm = new ColorMatrix();
cm.postConcat(new ColorMatrix(transfMatrix));
Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig());
Canvas c = new Canvas(bmOut);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
c.drawBitmap(src, 0, 0, paint);
return bmOut;

Thanks all!

error with setPixels

I think your Bitmap is not mutable (see setPixel()'s documentation).

If so, create a mutable copy of this Bitmap (using Bitmap.copy(Bitmap.Config config, boolean isMutable) as an example) and work on this one.

bitmap.copy() throws out of memory error

First of all try to find a little time to read good official documentation about bitmaps: Displaying Bitmaps Efficiently

It will give you understanding why and when java.lang.OutofMemoryError happens. And how to avoid it.

What about your question: see this article: Android: convert Immutable Bitmap into Mutable

But from API Level 11 only options.inMutable available to load the
file into a mutable bitmap.

So, if we are building application with API level less than 11, then
we have to find some other alternatives.

One alternative is creating another bitmap by copying the source

bitmap. mBitmap = mBitmap.copy(ARGB_8888 ,true);

But the will throw OutOfMemoryException if the source file is big.
Actually incase if we want to edit an original file, then we will face
this issue. We should be able to load at-least image into memory, but
most we can not allocate another copy into memory.

So, we have to save the decoded bytes into some where and clear
existing bitmap, then create a new mutable bitmap and load back the
saved bytes into bitmap again. Even to copy bytes we cannot create
another ByteBuffer inside the memory. In that case need to use
MappedByteBuffer that will allocate bytes inside a disk file.

Following code would explain clearly:

//this is the file going to use temporally to save the bytes. 

File file = new File("/mnt/sdcard/sample/temp.txt");
file.getParentFile().mkdirs();

//Open an RandomAccessFile
/*Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
into AndroidManifest.xml file*/
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

// get the width and height of the source bitmap.
int width = bitmap.getWidth();
int height = bitmap.getHeight();

//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width*height*4);
bitmap.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
bitmap.recycle();
//Create a new bitmap to load the bitmap again.
bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
map.position(0);
//load it back from temporary
bitmap.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();

And here is sample code.



Related Topics



Leave a reply



Submit