So I got this background image for my activity. Its a 480x800 png.
It has a gradient in it, so there is danger of banding, that's why I made it 99% opaque to force the best color mode.
On my device, and even on a HTC magic this is no problem.
However, on the default 1.6 emulator, I get out of memory errors. What to do?
The background is set in code with:
bgView.setImageResource(R.drawable.baby_pink_solid);
Setting the max VM heap to 192 and device ram size to 256 does not seem to be a solution.
Try accessing the Bitmap in code, and then set it via setImageBitmap(). If you get an OOM when you are decoding the Bitmap in code, then this is why you are getting it from setImageResource().
I have found that Bitmaps are a tricky thing to handle on Android, and you have to be careful when using them!
Also check out #Sadeshkumar Periyasamy's answer, this is useful for decoding Bitmaps or larger sizes for devices which do not have as much power as todays devices.
Try this code to scale any bitmap:
public class ImageScale
{
/**
* Decodes the path of the image to Bitmap Image.
* #param imagePath : path of the image.
* #return Bitmap image.
*/
public Bitmap decodeImage(String imagePath)
{
Bitmap bitmap=null;
try
{
File file=new File(imagePath);
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(file),null,o);
final int REQUIRED_SIZE=200;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true)
{
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize=scale;
bitmap=BitmapFactory.decodeStream(new FileInputStream(file), null, options);
}
catch(Exception e)
{
bitmap = null;
}
return bitmap;
}
/**
* Resizes the given Bitmap to Given size.
* #param bm : Bitmap to resize.
* #param newHeight : Height to resize.
* #param newWidth : Width to resize.
* #return Resized Bitmap.
*/
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth)
{
Bitmap resizedBitmap = null;
try
{
if(bm!=null)
{
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
// resizedBitmap = Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
}
}
catch(Exception e)
{
resizedBitmap = null;
}
return resizedBitmap;
}
}
Related
I am getting memory error in my android game and i think its caused by my image loading function. everything is working fine on my mobile but on tablet i get memory exceed error.
I am using a matrix because i need to resize the image to a float value. But in this case i have to load the image in full size first and then resize it with the matrix and i think this causes the memory error.
is this the correct way to handle image resize?
public class Sprite {
private Bitmap bitmap = null;
private float scaleX, scaleY;
public Sprite(String path, float targetWidth, float targetHeight, Context context) {
InputStream istr;
AssetManager assetManager = context.getAssets();
Matrix scalematrix = new Matrix();
try {
istr = assetManager.open(path);
bitmap = BitmapFactory.decodeStream(istr);
scaleX = targetWidth / bitmap.getWidth();
scaleY = targetHeight / bitmap.getHeight();
scalematrix.postScale(scaleX, scaleY);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), scalematrix, true);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
BitmapFactory.Options has public int inSampleSize -- If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
You may try to use BitmapFactory.decodeXXX(..., BitmapFactory.Options opts) (decodeFile() etc.)
Is there any method that gets the phones resolution (or dp) and scales bitmaps accordingly? I have all my images in xhdpi folder and at the moment they do not scale the way they should.
I want an efficiant and memory-friendly method that can do the scaling automatically. If not, what is the next best thing? completely new area for me. So any tutorial-link is also appriciated.
this is what I use to load bitmaps atm:
public Bitmap loadBitmap(int resourceID) {
Options options = new BitmapFactory.Options();
options.inScaled = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap tempBmp = null;
try {
tempBmp = BitmapFactory.decodeResource(getResources(), resourceID,
options);
} catch (OutOfMemoryError e) {
} catch (Error e) {
}
return tempBmp;
}
If you wanna scale bitmap for each phone resolution, you should know phone screen size, scale ratio.
This code will return width (w) & height (h) of screen.
DisplayMetrics dMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
float density = dMetrics.density;
int w = Math.round(dMetrics.widthPixels / density);
int h = Math.round(dMetrics.heightPixels / density);
activity is instance of Activity which would you like to get screen size.
You have to remember that: When your device is in landscape orientation, w > h. When it in portrait orientation w < h.
So from width & height you can detect your device is in what orientation.
Example:
From w & h of device and ratio (which you want to scale) you can calculate new bitmap size to scale it.
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;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
I am trying to make an application, which fetches the image from the storage and place it into that application image size so that it looks good for it.
Scaling bitmaps from memory can be very memory intensive. To avoid crashing your app on old devices, I recommend doing this.
I use these two methods to load a bitmap and scale it down. I split them into two functions. createLightweightScaledBitmapFromStream() uses options.inSampleSize to perform a rough scaling to the required dimensions. Then, createScaledBitmapFromStream() uses a more memory intensive Bitmap.createScaledBitmap() to finish scaling the image to the desired resolution.
Call createScaledBitmapFromStream() and you should be all set.
Lightweight scaling
public static Bitmap createLightweightScaledBitmapFromStream(InputStream is, int minShrunkWidth, int minShrunkHeight, Bitmap.Config config) {
BufferedInputStream bis = new BufferedInputStream(is, 32 * 1024);
try {
BitmapFactory.Options options = new BitmapFactory.Options();
if (config != null) {
options.inPreferredConfig = config;
}
final BitmapFactory.Options decodeBoundsOptions = new BitmapFactory.Options();
decodeBoundsOptions.inJustDecodeBounds = true;
bis.mark(Integer.MAX_VALUE);
BitmapFactory.decodeStream(bis, null, decodeBoundsOptions);
bis.reset();
final int width = decodeBoundsOptions.outWidth;
final int height = decodeBoundsOptions.outHeight;
Log.v("Original bitmap dimensions: %d x %d", width, height);
int sampleRatio = Math.max(width / minShrunkWidth, height / minShrunkHeight);
if (sampleRatio >= 2) {
options.inSampleSize = sampleRatio;
}
Log.v("Bitmap sample size = %d", options.inSampleSize);
Bitmap ret = BitmapFactory.decodeStream(bis, null, options);
Log.d("Sampled bitmap size = %d X %d", options.outWidth, options.outHeight);
return ret;
} catch (IOException e) {
Log.e("Error resizing bitmap from InputStream.", e);
} finally {
Util.ensureClosed(bis);
}
return null;
}
Final Scaling (Calls lightweight scaling first)
public static Bitmap createScaledBitmapFromStream(InputStream is, int maxWidth, int maxHeight, Bitmap.Config config) {
// Start by grabbing the bitmap from file, sampling down a little first if the image is huge.
Bitmap tempBitmap = createLightweightScaledBitmapFromStream(is, maxWidth, maxHeight, config);
Bitmap outBitmap = tempBitmap;
int width = tempBitmap.getWidth();
int height = tempBitmap.getHeight();
// Find the greatest ration difference, as this is what we will shrink both sides to.
float ratio = calculateBitmapScaleFactor(width, height, maxWidth, maxHeight);
if (ratio < 1.0f) { // Don't blow up small images, only shrink bigger ones.
int newWidth = (int) (ratio * width);
int newHeight = (int) (ratio * height);
Log.v("Scaling image further down to %d x %d", newWidth, newHeight);
outBitmap = Bitmap.createScaledBitmap(tempBitmap, newWidth, newHeight, true);
Log.d("Final bitmap dimensions: %d x %d", outBitmap.getWidth(), outBitmap.getHeight());
tempBitmap.recycle();
}
return outBitmap;
}
I am using bitmap to get a image from a url using this
public void loadImage(String url){
try {
bitmap = BitmapFactory.decodeStream((InputStream)new URL(url).getContent());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Is there anyway i can resize the image from here? setting the width and the height of it still keeping the resolution?
Use: Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
You can try this too.
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inSampleSize = 1; // 1 = 100% if you write 4 means 1/4 = 25%
bitmap = BitmapFactory.decodeStream((InputStream)new URL(url).getContent(),
null, bmOptions);
bmImage.setImageBitmap(bitmap);
Maybe this will help you:
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;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
You can just use Picasso(very simple!):
Picasso.with(context) .load(url) .resize(10, 10) .into(imageView)
http://square.github.io/picasso/
why don't you set the width and height property of imageView in XML?
I find out that the image from url is resized automatically so that it can fit in that imageView.
I needed similar capabilities in my app also. I found the best solution for me here.
https://github.com/coomar2841/image-chooser-library/blob/dev/src/com/kbeanie/imagechooser/threads/MediaProcessorThread.java
You might not need all that functionality, but at some point the guy is compressing /scaling bitmaps.
I've got a bitmap... and if the bitmap's height is greater than maxHeight, or the width is greater than maxWidth, I'd like to proportionally resize the image so that it fits in maxWidth X maxHeight. Here's what I'm trying:
BitmapDrawable bmp = new BitmapDrawable(getResources(), PHOTO_PATH);
int width = bmp.getIntrinsicWidth();
int height = bmp.getIntrinsicHeight();
float ratio = (float)width/(float)height;
float scaleWidth = width;
float scaleHeight = height;
if((float)mMaxWidth/(float)mMaxHeight > ratio) {
scaleWidth = (float)mMaxHeight * ratio;
}
else {
scaleHeight = (float)mMaxWidth / ratio;
}
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap out = Bitmap.createBitmap(bmp.getBitmap(),
0, 0, width, height, matrix, true);
try {
out.compress(Bitmap.CompressFormat.JPEG, 100,
new FileOutputStream(PHOTO_PATH));
}
catch(FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
I get the following exception:
java.lang.IllegalArgumentException: bitmap size exceeds 32bits
What am I doing wrong here?
Your scaleWidth and scaleHeight should be scale factors (so not very big numbers) but your code seems to pass in the actual width and height you're looking for. So you're ending up massively increasing the size of your bitmap.
I think there are other problems with the code to derive scaleWidth and scaleHeight as well though. For one thing your code always has either scaleWidth = width or scaleHeight = height, and changes only one of them, so you're going to be distorting the aspect ratio of your image as well. If you just want to resize the image then you should just have a single scaleFactor.
Also, why does your if statement check for, effectively, maxRatio > ratio ? Shouldn't you be checking for width > maxWidth or height > maxHeight?
This is because the value of scaleWidth or scaleHeight is too large,scaleWidth or scaleHeight is mean enlarge or shrink 's rate,but not width or height,too large rate lead to bitmap size exceeds 32 bits
matrix.postScale(scaleWidth, scaleHeight);
this is how i did it:
public Bitmap decodeAbtoBm(byte[] b){
Bitmap bm; // prepare object to return
// clear system and runtime of rubbish
System.gc();
Runtime.getRuntime().gc();
//Decode image size only
BitmapFactory.Options oo = new BitmapFactory.Options();
// only decodes size, not the whole image
// See Android documentation for more info.
oo.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(b, 0, b.length ,oo);
//The new size we want to scale to
final int REQUIRED_SIZE=200;
// Important function to resize proportionally.
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(oo.outWidth/scale/2>=REQUIRED_SIZE
&& oo.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2; // Actual scaler
//Decode Options: byte array image with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale; // set scaler
o2.inPurgeable = true; // for effeciency
o2.inInputShareable = true;
// Do actual decoding, this takes up resources and could crash
// your app if you do not do it properly
bm = BitmapFactory.decodeByteArray(b, 0, b.length,o2);
// Just to be safe, clear system and runtime of rubbish again!
System.gc();
Runtime.getRuntime().gc();
return bm; // return Bitmap to the method that called it
}