Recycled bitmap exception - android

I'm getting this exception:
Exception: java.lang.IllegalStateException: Can't copy a recycled bitmap
My code is:
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int newWidth;
int newHeight;
if (width >= height) {
newWidth = Math.min(width,1024);
newHeight = (int) (((float)newWidth)*height/width);
}
else {
newHeight = Math.min(height, 1024);
newWidth = (int) (((float)newHeight)*width/height);
}
float scaleWidth = ((float)newWidth)/width;
float scaleHeight = ((float)newHeight)/height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
switch (orientation) {
case 3:
matrix.postRotate(180);
break;
case 6:
matrix.postRotate(90);
break;
case 8:
matrix.postRotate(270);
break;
}
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
bitmap.recycle();
try {
bitmap = resizedBitmap.copy(resizedBitmap.getConfig(), true);
}
catch (Exception e) {
Log.v(TAG,"Exception: "+e);
}
If the exception is telling me that I've recycled resizedBitmap, that is patently false! What am I doing wrong??

You are actually calling bitmap.recycle(); after this line:
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

Quote from Bitmap.createBitmap() method's Javadoc:
Returns an immutable bitmap from subset of the source bitmap, transformed by the optional matrix. The new bitmap may be the same object as source, or a copy may have been made. It is initialized with the same density as the original bitmap. If the source bitmap is immutable and the requested subset is the same as the source bitmap itself, then the source bitmap is returned and no new bitmap is created.
That mean that in some cases, i.e. when asking to resize a source bitmap to its actual size, there will be no difference between source and resized bitmap. To save memory the method will just return the same instance of bitmap.
To fix your code you should check whether a new bitmap has been created:
Bitmap resizedBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, width, height, matrix, true);
if (resizedBitmap != sourceBitmap) {
sourceBitmap.recycle();
}

Related

Rotate bitmap without outofMemoery exception on android

This is my code to rotate a image. Image is already down sampled and decoded. But this throws an exception because it creates an additional copy of the image. How can I generate the image safely?
public Bitmap rotateBitmap(Bitmap image, int angle) {
if (image != null) {
Matrix matrix = new Matrix();
matrix.postRotate(angle, (image.getWidth()) / 2,
(image.getHeight()) / 2);
return Bitmap.createBitmap(image, 0, 0, image.getWidth(),
image.getHeight(), matrix, true);
}
return null;
}

Rotate a bitmap using render script android

When I use following code, it ends up with outofmemory exception. After doing researh Render script looks like a good candidate. Where can I find sample code for similar operation and how can integrate it to my project.
public Bitmap rotateBitmap(Bitmap image, int angle) {
if (image != null) {
Matrix matrix = new Matrix();
matrix.postRotate(angle, (image.getWidth()) / 2,
(image.getHeight()) / 2);
return Bitmap.createBitmap(image, 0, 0, image.getWidth(),
image.getHeight(), matrix, true);
}
return null;
}
Basically rotating bitmap is a task of rotating 2D array without using additional memory. And this is the correct implementation with RenderScript: Android: rotate image without loading it to memory .
But this is not necessary if all you want is just to display rotated Bitmap. You can simply extend ImageView and rotate the Canvas while drawing on it:
canvas.save();
canvas.rotate(angle, X + (imageW / 2), Y + (imageH / 2));
canvas.drawBitmap(imageBmp, X, Y, null);
canvas.restore();
What about ScriptIntrinsic, since it's just a built-in RenderScript kernels for common operations you cannot do nothing above the already implemented functions: ScriptIntrinsic3DLUT, ScriptIntrinsicBLAS, ScriptIntrinsicBlend, ScriptIntrinsicBlur, ScriptIntrinsicColorMatrix, ScriptIntrinsicConvolve3x3, ScriptIntrinsicConvolve5x5, ScriptIntrinsicHistogram, ScriptIntrinsicLUT, ScriptIntrinsicResize, ScriptIntrinsicYuvToRGB. They do not include functionality to rotate bitmap at the moment so you should create your own ScriptC script.
Try this code..
private Bitmap RotateImage(Bitmap _bitmap, int angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
_bitmap = Bitmap.createBitmap(_bitmap, 0, 0, _bitmap.getWidth(), _bitmap.getHeight(), matrix, true);
return _bitmap;
}
Use this code when select image from gallery.
like this..
File _file = new File(file_name);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap bitmap = BitmapFactory.decodeFile(file_name, options);
try {
ExifInterface exif = new ExifInterface(file_name);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
bitmap = RotateImage(bitmap, 90);
} else if (orientation ==ExifInterface.ORIENTATION_ROTATE_270) {
bitmap = RotateImage(bitmap, 270);
}
} catch (Exception e) {
e.printStackTrace();
}
image_holder.setImageBitmap(bitmap);

Why my Bitmap after scaling seems empty

I am trying to scale a Bitmap, doubling its size.
But after the scaling the bitmap shows empty, all plain gray...
here is the code:
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(2, 2);
// recreate the new Bitmap and set it back
Bitmap bm2=Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
//bm.recycle();
EDIT EDIT EDIT EDIT EDIT
I figured it out it's memory issue, if I do it with small images it works fine.
Still the problem remains with large pictures!!!
Thanks for any suggestion!!!
As from the Bitmap Doc it is clearly saying that Bitmap.createBitmap() returns the same bitmap or part of the source bitmap.So may be here it returns the same bitmap object.But here you are recycling it
bm.recycle();
thats why you are getting null
Use this method
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
and pass width as 1920 and height as 2560

Bitmap size exceed VM budget

I have read those posts on this issue but my case is abit different as I am NOT DISPLAYING the bitmap but just post-processing the image from raw data.
First, I call ImageProcessing.rgbToBitmap(data,width, height); which will return a Bitmap object. Then I perform these 3 functions SEPARATELY.
Rotate Bitmap
Add a watermark overlay to Bitmap
Add date at lower right hand corner of Bitmap
All 3 methods called will create an return a Bitmap object which probably causes the crash as I am trying to save an image every 1000ms! Sometimes the images saved are distorted probably due to the memory error.
I am posting my codes below and any advices are greatly appreciated. I do not want to compromise on the quality on the image taken though. (Need to preserve the resolution)
public static Bitmap addWatermark(Resources res, Bitmap source) {
int w, h;
Canvas c;
Paint paint;
Bitmap bmp, watermark;
Matrix matrix;
float scale;
RectF r;
w = source.getWidth();
h = source.getHeight();
// Create the new bitmap
bmp = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
| Paint.FILTER_BITMAP_FLAG);
// Copy the original bitmap into the new one
c = new Canvas(bmp);
c.drawBitmap(source, 0, 0, paint);
// Load the watermark
watermark = BitmapFactory.decodeResource(res, R.drawable.watermark);
// Scale the watermark to be approximately 10% of the source image
// height
scale = (float) (((float) h * 0.80) / (float) watermark.getHeight());
// Create the matrix
matrix = new Matrix();
matrix.postScale(scale, scale);
// Determine the post-scaled size of the watermark
r = new RectF(0, 0, watermark.getWidth(), watermark.getHeight());
matrix.mapRect(r);
// Center watermark
matrix.postTranslate((w - r.width()) / 2, (h - r.height()) / 2);
// Draw the watermark
c.drawBitmap(watermark, matrix, paint);
// Free up the bitmap memory
watermark.recycle();
return bmp;
}
public static Bitmap addDate(Bitmap src, String date) {
int w = src.getWidth();
int h = src.getHeight();
//Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Bitmap result = src;
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
paint.setColor(Color.rgb(255, 185, 15));
paint.setTextSize(20);
paint.setAntiAlias(true);
canvas.drawText(date, w - 200, h - 20, paint);
return result;
}
public static Bitmap rotate(Bitmap src, int rotation) {
int width = src.getWidth();
int height = src.getHeight();
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// rotate the Bitmap
matrix.postRotate(rotation);
// recreate the new Bitmap, swap width and height and apply
// transform
Bitmap rotatedBitmap = Bitmap.createBitmap(src, 0, 0, width, height,
matrix, true);
return rotatedBitmap;
}
Try this first:
Change
// Copy the original bitmap into the new one
c = new Canvas(bmp);
c.drawBitmap(source, 0, 0, paint);
with
// Copy the original bitmap into the new one
c = new Canvas(bmp);
bmp.recycle(); //here or below
c.drawBitmap(source, 0, 0, paint);
//below bmp.recycle();
and here:
canvas.drawText(date, w - 200, h - 20, paint);
return result;
with
canvas.drawText(date, w - 200, h - 20, paint);
result.recycle();
return result;
and here
Bitmap rotatedBitmap = Bitmap.createBitmap(src, 0, 0, width, height,
matrix, true);
return rotatedBitmap;
with
Bitmap rotatedBitmap = Bitmap.createBitmap(src, 0, 0, width, height,
matrix, true);
return rotatedBitmap;
rotatedBitmap.recycle();
Add all this (recycle();) maybe this is already enough and also try the code with smaller bitmaps, en see if it still crashes (like 50px by 50px).
And no, their is no way to increase the VM of your phone.

Android Crop Center of Bitmap

I have bitmaps which are squares or rectangles. I take the shortest side and do something like this:
int value = 0;
if (bitmap.getHeight() <= bitmap.getWidth()) {
value = bitmap.getHeight();
} else {
value = bitmap.getWidth();
}
Bitmap finalBitmap = null;
finalBitmap = Bitmap.createBitmap(bitmap, 0, 0, value, value);
Then I scale it to a 144 x 144 Bitmap using this:
Bitmap lastBitmap = null;
lastBitmap = Bitmap.createScaledBitmap(finalBitmap, 144, 144, true);
Problem is that it crops the top left corner of the original bitmap, Anyone has the code to crop the center of the bitmap?
This can be achieved with: Bitmap.createBitmap(source, x, y, width, height)
if (srcBmp.getWidth() >= srcBmp.getHeight()){
dstBmp = Bitmap.createBitmap(
srcBmp,
srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
0,
srcBmp.getHeight(),
srcBmp.getHeight()
);
}else{
dstBmp = Bitmap.createBitmap(
srcBmp,
0,
srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
srcBmp.getWidth(),
srcBmp.getWidth()
);
}
While most of the above answers provide a way to do this, there is already a built-in way to accomplish this and it's 1 line of code (ThumbnailUtils.extractThumbnail())
int dimension = getSquareCropDimensionForBitmap(bitmap);
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension);
...
//I added this method because people keep asking how
//to calculate the dimensions of the bitmap...see comments below
public int getSquareCropDimensionForBitmap(Bitmap bitmap)
{
//use the smallest dimension of the image to crop to
return Math.min(bitmap.getWidth(), bitmap.getHeight());
}
If you want the bitmap object to be recycled, you can pass options that make it so:
bitmap = ThumbnailUtils.extractThumbnail(bitmap, dimension, dimension, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
From: ThumbnailUtils Documentation
public static Bitmap extractThumbnail (Bitmap source, int width, int
height)
Added in API level 8 Creates a centered bitmap of the desired size.
Parameters source original bitmap source width targeted width
height targeted height
I was getting out of memory errors sometimes when using the accepted answer, and using ThumbnailUtils resolved those issues for me. Plus, this is much cleaner and more reusable.
Have you considered doing this from the layout.xml ? You could set for your ImageView the ScaleType to android:scaleType="centerCrop" and set the dimensions of the image in the ImageView inside the layout.xml.
You can used following code that can solve your problem.
Matrix matrix = new Matrix();
matrix.postScale(0.5f, 0.5f);
Bitmap croppedBitmap = Bitmap.createBitmap(bitmapOriginal, 100, 100,100, 100, matrix, true);
Above method do postScalling of image before cropping, so you can get best result with cropped image without getting OOM error.
For more detail you can refer this blog
Here a more complete snippet that crops out the center of an [bitmap] of arbitrary dimensions and scales the result to your desired [IMAGE_SIZE]. So you will always get a [croppedBitmap] scaled square of the image center with a fixed size. ideal for thumbnailing and such.
Its a more complete combination of the other solutions.
final int IMAGE_SIZE = 255;
boolean landscape = bitmap.getWidth() > bitmap.getHeight();
float scale_factor;
if (landscape) scale_factor = (float)IMAGE_SIZE / bitmap.getHeight();
else scale_factor = (float)IMAGE_SIZE / bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale_factor, scale_factor);
Bitmap croppedBitmap;
if (landscape){
int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
Probably the easiest solution so far:
public static Bitmap cropCenter(Bitmap bmp) {
int dimension = Math.min(bmp.getWidth(), bmp.getHeight());
return ThumbnailUtils.extractThumbnail(bmp, dimension, dimension);
}
imports:
import android.media.ThumbnailUtils;
import java.lang.Math;
import android.graphics.Bitmap;
To correct #willsteel solution:
if (landscape){
int start = (tempBitmap.getWidth() - tempBitmap.getHeight()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, start, 0, tempBitmap.getHeight(), tempBitmap.getHeight(), matrix, true);
} else {
int start = (tempBitmap.getHeight() - tempBitmap.getWidth()) / 2;
croppedBitmap = Bitmap.createBitmap(tempBitmap, 0, start, tempBitmap.getWidth(), tempBitmap.getWidth(), matrix, true);
}
public Bitmap getResizedBitmap(Bitmap bm) {
int width = bm.getWidth();
int height = bm.getHeight();
int narrowSize = Math.min(width, height);
int differ = (int)Math.abs((bm.getHeight() - bm.getWidth())/2.0f);
width = (width == narrowSize) ? 0 : differ;
height = (width == 0) ? differ : 0;
Bitmap resizedBitmap = Bitmap.createBitmap(bm, width, height, narrowSize, narrowSize);
bm.recycle();
return resizedBitmap;
}
public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
if (w == size && h == size) return bitmap;
// scale the image so that the shorter side equals to the target;
// the longer side will be center-cropped.
float scale = (float) size / Math.min(w, h);
Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap));
int width = Math.round(scale * bitmap.getWidth());
int height = Math.round(scale * bitmap.getHeight());
Canvas canvas = new Canvas(target);
canvas.translate((size - width) / 2f, (size - height) / 2f);
canvas.scale(scale, scale);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG);
canvas.drawBitmap(bitmap, 0, 0, paint);
if (recycle) bitmap.recycle();
return target;
}
private static Bitmap.Config getConfig(Bitmap bitmap) {
Bitmap.Config config = bitmap.getConfig();
if (config == null) {
config = Bitmap.Config.ARGB_8888;
}
return config;
}
val sourceWidth = source.width
val sourceHeight = source.height
val xScale = newWidth.toFloat() / sourceWidth
val yScale = newHeight.toFloat() / sourceHeight
val scale = xScale.coerceAtLeast(yScale)
val scaledWidth = scale * sourceWidth
val scaledHeight = scale * sourceHeight
val left = (newWidth - scaledWidth) / 2
val top = (newHeight - scaledHeight) / 2
val targetRect = RectF(
left, top, left + scaledWidth, top
+ scaledHeight
)
val dest = Bitmap.createBitmap(
newWidth, newHeight,
source.config
)
val mutableDest = dest.copy(source.config, true)
val canvas = Canvas(mutableDest)
canvas.drawBitmap(source, null, targetRect, null)
binding.imgView.setImageBitmap(mutableDest)

Categories

Resources