Im a newbie in android, So i would like to know is there any way to get the dimensions of a Bitmap without loading the bitmap into memory.??
You can set the BitmapFactory.Options with inJustDecodeBounds to get the image width and height without loading the bitmap pixel in memory
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, bitmapOptions);
int imageWidth = bitmapOptions.outWidth;
int imageHeight = bitmapOptions.outHeight;
inputStream.close();
For more details:
public boolean inJustDecodeBounds
Since: API Level 1 If set to true,
the decoder will return null (no bitmap), but the out... fields will
still be set, allowing the caller to query the bitmap without having
to allocate the memory for its pixels.
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inJustDecodeBounds
Related
Hello I have this piece of code:
EDIT:
imageName = data.getData();
try{
InputStream stream = getContentResolver().openInputStream(imageName);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeStream(stream,null,options);
final int REQUIRED_WIDTH=(int)screenWidth;
final int REQUIRED_HIGHT=(int)screenHeight;
int scale=1;
while(options.outWidth/scale/2>=REQUIRED_WIDTH && options.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
bitmap = BitmapFactory.decodeStream(stream, null, o2);
if ( bitmap != null ){
ok=true;
}
}catch(Exception e){
Toast.makeText(getBaseContext(),"error", Toast.LENGTH_SHORT).show();
}
But the bitmap is still null
Can Anyone tell me why ? or better... How to fix it ?
Ok, so If I'll do it like it's now in edited post, how can I "resize" the image to get rid of outOfMemory Exception ?
Aha! Now we are getting somewhere.
What you had, in a prior edit of your question, was:
read the entire bitmap into a byte array
write the entire bitmap into another byte array as a low-quality JPEG
read the entire bitmap into a Bitmap, backed by yet a third byte array
This will result in your consuming ~2.1x the heap space of your current implementation, which is already giving you OutOfMemoryError messages. The byte arrays from #1 and #3 above will be the same size, equal to:
width x height x 4
where the width and height are expressed in pixels.
To reduce your memory consumption, you need to do to things:
Read in the bitmap once, as your current code does.
Use BitmapFactory.Options to control the decoding of the bitmap. In particular, use inSampleSize to reduce the number of pixels in the resulting Bitmap. Quoting the JavaDocs for inSampleSize:
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. Any value <= 1 is treated the same as 1. Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2.
This sample project demonstrates the use of inSampleSize for various hardcoded values. The actual Bitmap loading comes from:
private Bitmap load(String path, int inSampleSize) throws IOException {
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inSampleSize=inSampleSize;
return(BitmapFactory.decodeStream(assets().open(path), null, opts));
}
Your getPathFromUri method is failing. It should be returning something like /storage/emulated/0/DCIM/camera/bla bla.jpg. The file:// is the authority and isn't part of the path. BitmapFactory is looking for an actual file location path.
You can strip that manually or use Uri class with something like this:
Uri imgUri = Uri.parse(imgPath);
String filePath = imgUri.getPath();
I'm creating my own gallery using ViewPager, which is 'connected' to ArrayList holding paths of images to display.
So far, I use:
Bitmap bitmap = BitmapFactory.decodeFile(path);
imageView.setImageBitmap(bitmap);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
It's working, but the pictures are pretty big and I unnecessarily decode whole picture, because it's scaled down later to fit layout. It is slow and a crash with OutOfMemoryError happens sometimes, when I scroll fast.
So the question is:
How can I efficiently decode an image so it fits 100% with ImageView.ScaleType.CENTER_INSIDE knowing only its path?
Do not decode it directy. use inJustDecodeBounds = true from BitmapFactory.Options, to load only width and height from the Bitmap. Then you can calcuate the inSampleSize to scale your Bitmap
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
decodeFile will return null and options will contains width and height. Then you calculate the inSampleSize and:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = myInsampleSize;
Bitmap output = BitmapFactory.decodeFile(path, options);
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
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)
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;
}
I want to scale down a 500x500px resource to fit always a specific size which is determined by the width of the screen.
Currently I use the code from the Android Developers Site (Loading Large Bitmaps Efficiently), but the quality is not as good as I would use the 500x500px resource in a ImageView (as source in xml) and just scale the ImageView and not the Bitmap.
But it's slow and I want to scale the Bitmap, too, to be memory efficient and fast.
Edit: The drawable which I wanna scale is in the drawable folder of my app.
Edit2: My current approaches.
The left image is the method from Loading Large Bitmaps Efficiently without any modifications. The center image is done with the method provided by #Salman Zaidi with this little modification: o.inPreferredConfig = Config.ARGB_8888; and o2.inPreferredConfig = Config.ARGB_8888;
The right image is an imageview where the image source is defined in xml and the quality I wanna reach with a scaled bitmap.
private Bitmap decodeImage(File f) {
Bitmap b = null;
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
float sc = 0.0f;
int scale = 1;
//if image height is greater than width
if (o.outHeight > o.outWidth) {
sc = o.outHeight / 400;
scale = Math.round(sc);
}
//if image width is greater than height
else {
sc = o.outWidth / 400;
scale = Math.round(sc);
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = new FileInputStream(f);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
} catch (IOException e) {
}
return b;
}
Here '400' is the new width (in case image is in portrait mode) or new height (in case image is in landscape mode). You can set the value of your own choice.. Scaled bitmap will not take much memory space..
Dudes, inSampleSize param is made for memory optimization, while loading a bitmap from resources or memory. So for your issue you should use this:
Bitmap bmp = BitmapFactory.decode...;
bmp = bmp.createScaledBitmap(bmp, 400, 400, false);
inSampleSizelets lets you to scale bitmap with descret steps. Scale ratios are 2,4 and so on. So when your use decoding with options, where inSampleSize=2 you loads a 250x250 bitmap from memory and then stretch it to 400x400
Check this training:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
It shows how to resize bitmaps efficiently
How to scale bitmaps at runtime to a very small size and then storing them in internal storage? how to call the scaled bitmaps into the program from the storage at runtime and if its not there, call it from drawable folder, scale it, write it to storage and then bind it to the view.
If you want to scale the bitmap you could use Bitmap.createScaledBitmap
To scale an arbitrary bitmap to 32x32 you could do it as follows:
Bitmap smallBitmap = Bitmap.createScaledBitmap( fullSizeBitmap, 32, 32, true );
You can use BitmapFactory.Options class to crop image to any size.
You can use following:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = 8; // 1/8th of actual image.
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Here, when you use a Bitmap, always call its bmp.recycle() method, since GC can't clear the memory held by Bitmap, if your bitmap is not getting garbage collected, then also you get the OME.