How to load sampled image from Uri? - java

I need to load image with reduced resolution. How can I do this?
I tired next code:
public Bitmap decodeSampledBitmapFromUri(#NonNull Uri Uri, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
InputStream is = getContentResolver().openInputStream(Uri);
BitmapFactory.decodeStream(is, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // I can read width and height from options
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(is, null, options);
} catch (Exception ignored)
{
}
return null;
}
And I even can see the width and height of loaded image. But BitmapFactory.decodeStream returns null when I turned inJustDecodeBounds to false. Whats wrong?
The image selected by user from gallery. All permissions granted.

Your InputStream was closed by the first decodeStream(). Open a fresh InputStream for use with the second decodeStream().

Related

OutOfMemory issue loading several pictures

I have nine pictures that I want to load at the start of an activity, and I'm getting problems of OutOfMemory exceptions. At first I loaded them directly in the xml setting its src. So after getting java.lang.OutOfMemory I realized that maybe I needed to load the pictures more efficiently and I created this loop to be executed at the begining of the activity:
for(int i=0;i<9;i++){
String background = "background"+(i+1);
int idDrawable = getResources().getIdentifier(background, "drawable", getPackageName());
int idPicture = getResources().getIdentifier(background, "id", getPackageName());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), idDrawable, options);
options.inJustDecodeBounds = false;
ImageView image = (ImageView) findViewById(idPicture);
image.setImageBitmap(BitmapFactory.decodeResource(getResources(), idDrawable, options));
}
But I still have the same OutOfMemory issue, any ideas on what am I doing wrong?
Use following code.
Change this code
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), idDrawable, options);
to
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inSampleSize = 4;
BitmapFactory.decodeResource(getResources(), idDrawable, options);
and remove this line
options.inJustDecodeBounds = false;
Little bit of googeling and searching by yourself would lead you to that Android Developers HowTo
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Reset BufferedInputStream Holding a FileInputStream

I'm decoding a jpeg in two steps.
Check bounds, determine scale if necessary.
Decode within screen limits.
public static Bitmap decodeSampledBitmapFromInputStream(InputStream data, int reqWidth, int reqHeight)
{
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(data, null, options);
// Calculate inSampleSize
options.inSampleSize = Util.getExactSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
try {
// TODO: This works, but is there a better way?
if (data instanceof FileInputStream)
((FileInputStream)data).getChannel().position(0);
else
data.reset();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return BitmapFactory.decodeStream(data, null, options);
}
When the underlying stream is a FileInputStream it crashes on reset() with:
java.io.IOException: Mark has been invalidated.
So I added the instanceof section to manually reset the position of FileInputStreams, but this seems like a rather awkward solution. Is there no way to properly reset a BufferedInputStream encapsulating a FileInputStream?
Before using InputStream.reset you have to call InputStream.mark first to mark the position you want to return to later.

Decode a part of Bitmap from file in Android

I have a file with a very large image: for example 9000x9000.
I can't load the Bitmap in memory because the heap size. But I only need to display a small part of this bitmap for example the rect width=100-200 and height =200-400 (resulting size of the sub-bitmap =100x200)
How can I retrieve this bitmap from the file?
Note: I dont want to lose quality in the 100x200 image
Thanks
is it possible that there is a solution for this?
for example , BitmapRegionDecoder .
It should work for API10 and above...
Usage:
BitmapRegionDecoder.newInstance(...).decodeRegion(...)
It can easily be done using RapidDecoder.
I actually generated a 9000x9000 png which its file size is about 80MB and the 200x400 sized region was successfully loaded.
import rapid.decoder.BitmapDecoder;
Bitmap bitmap = BitmapDecoder.from("big-image.png")
.region(145, 192, 145 + 200, 192 + 400)
.decode();
imageView.setImageBitmap(bitmap);
It works for Android 2.2 and above.
I think that you can use the BitmapFactory method that allows you to specify the Rect that you want to decode.
public static Bitmap decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts)
Try this code:
public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
final int height = options.outHeight;
final int width = options.outWidth;
options.inPreferredConfig = Bitmap.Config.RGB_565;
int inSampleSize = 1;
if (height > reqHeight) {
inSampleSize = Math.round((float) height / (float) reqHeight);
}
int expectedWidth = width / inSampleSize;
if (expectedWidth > reqWidth) {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
I don't think you can. Not even on a PC, I fail to see how you could do that without loading the entire image: most image formats, for example PNGs, have the pixel data zipped, so you need to at least unzip the IDAT chunk before you can start doing anything else and that will basically decode the whole image.
In your shoes I would try to have a server do it for me. Where do you get the image anyway? Not from a server? Then try to make a WS request that will give you the proper part of the image. If the image does NOT come from the server you can nevertheless send it to your server to get back only the part of the image that you want.
try this code:
private Bitmap decodeFile(File f) {
Bitmap b = null;
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
b=Bitmap.createBitmap(BitmapFactory.decodeStream(fis, null, o), 100, 200, 200, 400, null, null);
fis.close();
} catch (IOException e) {
}
return b;
}
Am not sure, but this may give some idea to you

Decode a bitmap rotated

I'm looking for a way of decoding a bitmap from file rotated (0,90,180,270 degrees). I need that because the image is big and if I decode the bitmap and then use something like
Bitmap rotatedBitmap=Bitmap.createBitmap(source, x, y, width, height, m, filter)
There is a moment when both bitmap are in memory and there is risk of running out of memory.
Any ideas? Thanks.
Perhaps this is what you are looking for?
//decodes image and scales it to reduce memory consumption
//NOTE: if the image has dimensions which exceed int width and int height
//its dimensions will be altered.
private Bitmap decodeToLowResImage(byte [] b, int width, int height) {
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new ByteArrayInputStream(b), null, o);
//The new size we want to scale to
final int REQUIRED_SIZE_WIDTH=(int)(width*0.7);
final int REQUIRED_SIZE_HEIGHT=(int)(height*0.7);
//Find the correct scale value. It should be the power of 2.
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE_WIDTH || height_tmp/2<REQUIRED_SIZE_HEIGHT)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new ByteArrayInputStream(b), null, o2);
} catch (OutOfMemoryError e) {
//handle this
}
return null;
}
If you wish to use an NDK based solution, I've created one here, and i've made a github project here .
this will avoid OOM by putting the data into the native C "world", recycle the old data, and return the result back, after rotation.
it doesn't require any downsampling.

Android, Java, Creating a thumbnail keeping aspect ratio

I'm trying to create a thumbnail of a certain height but maintain the aspect ratio. I'm using the code below but the problem arises when say if an image is somewhat small the image generated will not fill the thumbnail area. imageURI is just the path to the image.
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imageURI, o);
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=4;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale++;
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
Bitmap bitmap = BitmapFactory.decodeFile(imageURI, o2);
Your code is always scaling the bitmap to have at least 1/4 of width or heigth. If the original image is already large enough it will be even smaller.
I assume you display the image in an ImageView (your thumbnail area?. If the image does not fill the ImageView you have to configure the ImageView to resize the image correctly. If your ImageView and the image to display have different aspect ratios, the only way to make the image fill the ImageView will distort the image.
What I do: I use the BitmapFactory to decode the image into a size thats larger, but nearly the size I want the thumbnail to have. Its better to use powers of two as scaling parameter, so I do that. And then I set the android:scaleType parameter of ImageView to make the image display as I like:
public static Bitmap decodeBitmap(Uri bitmapUri, ContentResolver resolver, int width, int height) throws IOException{
InputStream is = resolver.openInputStream(bitmapUri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is,null,options);
is.close();
int ratio = Math.min(options.outWidth/width, options.outHeight/height);
int sampleSize = Integer.highestOneBit((int)Math.floor(ratio));
if(sampleSize == 0){
sampleSize = 1;
}
Log.d(RSBLBitmapFactory.class, "Sample Size: " + sampleSize);
options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
is = resolver.openInputStream(bitmapUri);
Bitmap b = BitmapFactory.decodeStream(is,null,options);
is.close();
return b;
}
<ImageView android:scaleType="fitXY"></ImageView>

Categories