I have a question that I seem to find the answer nowhere.
Does this lines of code:
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
actually mean that, that file is being downloaded? Android docs say something like this:
decode with inJustDecodeBounds=true to check dimensions
and
Does:
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
BitmapFactory.decodeStream(is, null, options);
means that it will actually download the file smaller (not downloaded as original size and copied after that to a smaller size bitmap).
Clear example: I have some url's that point to many 2000 x 1500 images. By decoding those files and loading them to bitmaps, do I need to have enough memory for downloading the file at its full resolution (2000 x 1500), if I only need thumbnails of (200 x 150)?
I know another answer has already been accepted as the right one but for clarity...
This line options.inJustDecodeBounds = true; means that the call to BitmapFactory.decodeStream(is, null, options); doesn't get the bitmap information but does get bounding information and the MimeType.
You can then use these returned values, outWidth, outHeight and outMimeType to get a 'resampled' version of the bitmap data by setting options.inJustDecodeBounds = false; and setting the sample size to a given ratio determined by your desired output dimensions options. inSampleSize = [int].
See this very informative page for more information: Loading Large Bitmaps Efficiently
Indeed looks like the file is being downloaded, and the effiency part comes where the bitmap being loaded in memory is the smaller one ( the one being decoded with inSampleSize options).
Related
I have a project on Android and iOS, there is a section where it shows a 360 view of a car, in iOS I just get the 60 images from internal storage that were previously downloaded from the internet, and these images are showed when the user starts to swipe the car to the right or to the left. And this process takes just 200ms to be ready to interact with the user.
But in Android I have to take first the 60 images and convert them in bitmaps, and then the process is similar to the iOS process. Nevertheless, this first process, convert all of them to bitmap takes about 3 seconds o more. I have read the process to show an image in Android in many sources of information and seems like is necessary use a bitmap for this task, so is possible to reduce the time to get all bitmaps?, or could I save a bitmap to avoid converting the PNG file again to a new bitmap when the users open the same view again?
The code that I'm using to convert png file to a bitmap object is the same that Android developers documentation recommend:
https://developer.android.com/topic/performance/graphics/load-bitmap
My source code for get bitmap is:
public static Bitmap decodeSampledBitmapFromFile(String filePath, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
options.inSampleSize = 1;
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap =BitmapFactory.decodeFile(filePath, options);
Log.d("img360","bitmap res "+bitmap.getWidth()+ " h "+bitmap.getHeight());
return bitmap;
}
I'm really thankful for your help and support.
If you want to download images from Internet and display them on android
Just put an imageview and use picaso or glide library
It is faster and easier
I'm new to android and am working on my first real app so i apologize if this question has been answered, or isn't detailed enough. I'm having the common OutOfMemoryError when loading bitmaps to my android app. I've searched around on stackoverflow and went through the google tutorial that describes how to use InSampleSize to reduce the memory for each bitmap.
When testing out the app to see how much memory I was saving by down scaling my bitmaps I noticed that the heap size was still growing at the same rate. I.E. When I use an InSampleSize = 8 vs not scaling the bitmap at all (or InSampleSize = 1) the heap grows the same for each.
I have tried printing the byteCount with:
myImage.getByteCount()
for both of the scaling SampleSizes listed above and they both have identical ByteCounts. Is this the expected behavior? Shouldn't the byte count be reduced since I'm downscaling images?
I was expecting to see the memory used to the bitmap reduced by a factor of 8 or so. Am I missing something, or is my understanding of image scaling incorrect? Thanks.
edit: After doing some testing I discovered that if I used createScaledBitmap the memory was reduced but it required me first inflating the original Bitmap then scaling. I'm assuming this is non-ideal since it requires that. I thought the first method would do this for me but according to my heap dumps it isn't.
Initial Code and heap dump:
private static Bitmap decodeSampledBitmap(Resources res, int resId, int width, int height
{
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Config.ARGB_8888;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Updated code (same as above with the only difference being) and heap dump:
private static Bitmap decodeSampledBitmap(Resource res, int ResId, int width, int height)
{
....
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, options);
return Bitmap.createScaledBitmap(bitmap, options.outWidth/options.inSampleSize, options.outHeight/options.inSampleSize, false);
}
It won't let me post images due to my reputation but according to heap dump it is using 79.155MB. Using the second technique the heap dump is using 26.912 MB.
Are you scaling the Bitmap after decoding it using InSampleSize?
How to solve OOM issue in android . I have tried almost every things like scaling bitmap,inPurgeable in BitmapOption,releasing all resources etc. but still getting OOM issue.
This is basically in images taken by camera or any image i.e. larger then 1.5 mb. I have also images 15-20 mb size in my app.
this is what i´m doing to avoid OOM errors, Using some of the code of the android training. This is in my class "ScaledFactor"
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inPurgeable = true; // I aded this to the android training code, without this I still have OOM errors. so try using inPurgeable = true
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
In my Activity I use
background = ScaledFactor.decodeSampledBitmapFromResource(getResources(), R.drawable.menu, screenheight, screenwidth); // screeenhieght is the output height , screenwidth is the output width
Then in the on destroy method, or after calling an other intent I use background.recycle();
I´m not using the hdpi, ldpi and so folders... I just use the drawable with large bitmaps, and do the scalling. this way you save some mb on the final apk file
The android training code is here for more info http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap
C ya ! Hope this helps, I spent a hole day trying to figure this out and reading all the questions and answers in this forum. This is just the example of the background image but i have more than 20 images in my game all loaded this way but with smaller output size, and it works very smooth.
Have you tried Bitmap.recycle(); ?
It once solved my Out of Memory issue.
By following this link, I have written the following code to show a large image bitmap from sdcard.
try {
InputStream lStreamToImage = context.getContentResolver().openInputStream(Uri.parse(imagePath));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
options.inSampleSize = 8; //Decrease the size of decoded image
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(lStreamToImage, null, options);
} catch(Exception e){}
image.setImageBitmap(bitmap);
But it is not returning the bitmap(I mean it returns null). In logcat it is showing the below message repeatedly
08-02 17:21:04.389: D/skia(19359): --- SkImageDecoder::Factory returned null
If I will comment the options.inJustDecodeBounds line and rerun it, it works fine but slowly. The developer guide link I provided above says to use inJustDecodeBounds to load bitmaps efficiently.
Please tell me where I am doing wrong.
inJustDecodeBounds does not load bitmaps. That's the point of it. It loads the dimensions of the bitmap without loading the actual bitmap so you can do any pre-processing or checking on the bitmap before you actually load it. This is helpful is you, say, were having memory issues and you needed to check if loading a bitmap would crash you program.
The reason your bitmap might be loading slowly is because it's probably very large and SD cards are very slow.
EDIT:
From the documentation:
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.
Edit 2:
Looking at your code with the example provided by Google, it looks like you are doing relatively the same thing. The reason it's returning null is possibly your InputStream has been modified in the first decoding and thus not starting at the beginning of the bitmap's memory address (they use a resource ID rather than InputStream.
From the code you supplied here, here's what I've figured. You are ALWAYS setting a sample size to 8 regardless of what the first decoding gives you. The reason Google decodes the first time is to figure out what the actual size of the bitmap is versus what they want. They determine that the bitmap is ZxZ dimensions and they want YxY dimensions, so they calculate the samplesize that they should use from the second decoding. You are not doing this. You are simply retrieving the dimensions of the bitmap and not using them. THEN, you set the sample size to a hard-coded 8, swapping it to a hard-coded ARGB_4444 bitmap, then decoding the full bitmap in to memory. In other words, these three lines are not being used:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
Setting inJustDecodeBounds merely gives you the bitmap's dimensions without putting the bitmap in to memory. It doesn't make it more efficient. It's meant to allow you to load bitmaps in a smaller memory space if they are too big because you can pre-decide what size it should be without decoding the whole thing).
The reason decoding the bitmap is slow might merely be a CPU thing. Depending on the size of your bitmap, you're loading the bitmap from an InputStream from the SDcard which is a slow operation in itself.
I'm getting an image (.png) from SQLiteDatabase and using this code to decode the bytearray into a bitmap:
Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = true;
options.inScaled = true;
options.inDensity = 240;
options.inTargetDensity = metrics.densityDpi;
Bitmap bmp = BitmapFactory.decodeStream(new ByteArrayInputStream(imageAsBytes), null, options);
As you can see, image (3) should be like (2), but it doesn't.
1) = Image with no scale (metrics.densityDpi = 240);
2) = same .png above, but compiled in res/drawable;
3) = Image with down scale (with metrics.densityDpi = 120);
I also tried options.inDither = false;, but I see no difference.
So what's wrong with my code?
There a few other things I would try:
Load the png with no scale, when you come to draw the Image (either from within an ImageView or directly onto the canvas) set a Matrix to scale the image
Alternatively, load the image in the required density and try drawing the Bitmap directly to the canvas with a Paint object. After instantiating your Paint, enable Bitmap filtering (this will increase the image quality)
setFilterBitmap(true)
Finally, you could always load the Bitmap (density independent) and resize the Bitmap manually using Bitmap.createScaledBitmap, make sure you set the third paramenter to true (this enabled bitmap filtering for increased quality). Below is an example of scaling a bitmap where 100 is the desired size:
Bitmap.createScaledBitmap ( original_bitmap, 100, 100, true);
Briefly, the best quality downscaling algorithm consists of 2 steps:
downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()
Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/