If I am using Picasso's fit() method in loading images from online, do I have to consider using BitmapFactory.Options.inSampleSize options to reduce the size of an image? I read from other stackoverflow's question (Here) that:
You can also make use of the BitmapFactory.Options.inSampleSize
options to reduce the size of an image while it is loaded, rather than
loading in the entire image and then creating a small copy, which
wastes time and memory.
Do I have to do it after using fit()? Or it is already done by Picasso?
Thanks a lot.
Both fit() and explicit calls to resize() (or resizeDimen) will use inSampleSize automatically when it is appropriate to do so.
Because inSampleSize should be a power of two for best performance and quality, your image needs to be at least twice as large as the target size for this to occur.
Related
I'm using picasso to load images in my recycler view adaper but it is taking to much time to load image. Here is my call to load image with picasso.
Picasso.with(hostActivity).load("ImageUrl").fit().centerCrop().into(holder.ImageView);
If I do same thing with asynctask task, image loaded instantly.
Am I doing any thing wrong?
Thanks.
fit() needs to wait for the size of the ImageView to be determined before it can size the image to match, and the size can't be calculated until the end of the layout pass. You might get quicker results by using resize() if you are able to predict reasonable width and height values.
You might also want to look at the Glide library as it takes a different approach to caching that can be quicker than Picasso in some cases, for example instead of caching full size images it caches the resized ones. However, there are many pros and cons to both libraries; although the syntaxes are very similar, some things that work in Picasso will not work in Glide, and vice versa.
I’m building an image-intensive social app where images are sent from the server to the device. When the device has smaller screen resolutions, I need to resize the bitmaps, on device, to match their intended display sizes.
The problem is that using createScaledBitmap causes me to run into a lot of out-of-memory errors after resizing a horde of thumbnail images.
What’s the most memory efficient way to resize bitmaps on Android?
This answer is summarised from Loading large bitmaps Efficiently
which explains how to use inSampleSize to load a down-scaled bitmap
version.
In particular Pre-scaling bitmaps explains the details of various
methods, how to combine them, and which are the most memory efficient.
There are three dominant ways to resize a bitmap on Android which have different memory properties:
createScaledBitmap API
This API will take in an existing bitmap, and create a NEW bitmap with the exact dimensions you’ve selected.
On the plus side, you can get exactly the image size you’re looking for (regardless of how it looks). But the downside, is that this API requires an existing bitmap in order to work. Meaning the image would have to be loaded, decoded, and a bitmap created, before being able to create a new, smaller version. This is ideal in terms of getting your exact dimensions, but horrible in terms of additional memory overhead. As such, this is kind-of a deal breaker for most app developers who tend to be memory conscious
inSampleSize flag
BitmapFactory.Options has a property noted as inSampleSize that will resize your image while decoding it, to avoid the need to decode to a temporary bitmap. This integer value used here will load an image at a 1/x reduced size. For example, setting inSampleSize to 2 returns an image that’s half the size, and Setting it to 4 returns an image that’s 1/ 4th the size. Basically image sizes will always be some power-of-two smaller than your source size.
From a memory perspective, using inSampleSize is a really fast operation. Effectively, it will only decode every Xth pixel of your image into your resulting bitmap. There’s two main issues with inSampleSize though:
It doesn’t give you exact resolutions. It only decreases the size of your bitmap by some power of 2.
It doesn’t produce the best quality resize. Most resizing filters produce good looking images by reading blocks of pixels, and then weighting them to produce the resized pixel in question. inSampleSize avoids all this by just reading every few pixels. The result is quite performant, and low memory, but quality suffers.
If you're only dealing with shrinking your image by some pow2 size, and filtering isn't an issue, then you can't find a more memory efficient (or performance efficient) method than inSampleSize.
inScaled, inDensity, inTargetDensity flags
If you need to scale an image to a dimension that’s not equal to a power of two, then you’ll need the inScaled, inDensity and inTargetDensity flags of BitmapOptions. When inScaled flag has been set, the system will derive the scaling value to apply to your bitmap by dividing the inTargetDensity by the inDensity values.
mBitmapOptions.inScaled = true;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity = dstWidth;
// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeResources(getResources(),
mImageIDs, mBitmapOptions);
Using this method will re-size your image, and also apply a ‘resizing filter’ to it, that is, the end result will look better because some additional math has been taken into account during the resizing step. But be warned: that extra filter step, takes extra processing time, and can quickly add up for big images, resulting in slow resizes, and extra memory allocations for the filter itself.
It’s generally not a good idea to apply this technique to an image that’s significantly larger than your desired size, due to the extra filtering overhead.
Magic Combination
From a memory and performance perspective, you can combine these options for the best results. (setting the inSampleSize, inScaled, inDensity and inTargetDensity flags)
inSampleSize will first be applied to the image, getting it to the next power-of-two LARGER than your target size. Then, inDensity & inTargetDensity are used to scale the result to exact dimensions that you want, applying a filter operation to clean up the image.
Combining these two is a much faster operation, since the inSampleSize step will reduce the number of pixels that the resulting Density-based step will need to apply it’s resizing filter on.
mBitmapOptions.inScaled = true;
mBitmapOptions.inSampleSize = 4;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity = dstWidth * mBitmapOptions.inSampleSize;
// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);
If you're needing to fit an image to specific dimensions, and some nicer filtering, then this technique is the best bridge to getting the right size, but done in a fast, low-memory footprint operation.
Getting image dimensions
Getting the image size without decoding the whole image
In order to resize your bitmap, you’ll need to know the incoming dimensions. You can use the inJustDecodeBounds flag to help you get the dimensions of the image, w/o needing to actually decode the pixel data.
// Decode just the boundaries
mBitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, mBitmapOptions);
srcWidth = mBitmapOptions.outWidth;
srcHeight = mBitmapOptions.outHeight;
//now go resize the image to the size you want
You can use this flag to decode the size first, and then calculate the proper values for scaling to your target resolution.
As nice (and accurate) as this answer is, it's also very complicated. Rather than re-invent the wheel, consider libraries like Glide, Picasso, UIL, Ion, or any number of others that implement this complex and error prone logic for you.
Colt himself even recommends taking a look at Glide and Picasso in the Pre-scaling Bitmaps Performance Patterns Video.
By using libraries, you can get every bit of efficiency mentioned in Colt's answer, but with vastly simpler APIs that work consistently across every version of Android.
java.lang.OutOfMemoryError: Failed to allocate a 313194252 byte allocation with 11901284 free bytes and 174MB until OOM
I got this at this line:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.handskizze);
My Picture has a size of 2480*3508 at 300dpi.
Is it maybe to big? How can I make it smaller?
Whenever there is a requirement to load any image always try to use any ImageLoader library.
There are plenty of options available.
Here are some of the best libraries that developers uses. Try anyone of them which you fill easy to implement.
Universal Image Loader: https://github.com/nostra13/Android-Universal-Image-Loader
Picasso: http://square.github.io/picasso/
Glide: https://github.com/bumptech/glide
You can find some more libraries but I feel these 3 are used most.
In manifest use below attribute with application tag
android:largeHeap="true"
Also use
BitmapFactory.Options o=new BitmapFactory.Options();
o.inSampleSize = 2; //change value from 0 to 8 as per need and image resolution
myBitMap=BitmapFactory.decodeResource(getResources(),R.drawable.handskizze, o);
First of all, i would say your image is way too big for mobile devices. You should scale it down using Photoshop (or any other image editing software) to reduce its size. By doing so you minimize the size of your .apk and the processing time it takes to downscale the image.
Additionally, i would recomment you to use an image loader library. Im using the Universal Image Loader in most of my projects. It gets the measurement of the ImageView you want to load your image into, and downscales the image to reduce its size. You should give it a try ;)
So, I have been using this amazing library Glide for showing native images in my gallery app. I am using ViewPager with FragmentStatePagerAdapter to show full size images. The pager's off screen limit is 1 (default to save memory). I am using this code to load images into ViewPager in my fragment:
Glide.with(getActivity())
.loadFromMediaStore(uri)
.asBitmap()
.signature(new MediaStoreSignature(mimeType, dateModified,
.into(mImageView);
Now, I am facing some issues here like:
Images take quite some amount of time to load (if not cached). So, while user is scrolling through the viewpager blank screen is shown while image is loading which is what I want to avoid. Is there any way I can do this? Maybe by precaching images?
Sometimes, while scrolling through large size images (mainly Camera photos) OOM Exception is thrown and user is left with blank screen as no image is loaded. This also happens when I am shifting from potrait to landscape mode. So, I tried to use methods like atMost() -- which degrade the quality of image further as images are already loaded in RGB_565 and approximate() which is also causing OOM. How can I achieve maximum image quality without getting OOM exceptions?
For the second issue, I was thinking to load lesser quality images for the off screen items and then enhance quality when they come on-screen. Is it possible?
I have also tried to use ARGB_8888 but the result was same: OOM exception.
TL;DR
Make sure the ImageView has match_parent or fixed dp as dimensions
wrap_content makes Glide load full resolution Bitmaps.
.placeholder() shows an image instead of empty space while loading large bitmap
.thumbnail(float) loads a downsampled version fast while the bigger image is loading in the background
Also look around the Glide issues, maybe you find something helpful.
Details
I would be curious what the xml is for the ImageView, because my guess is it's wrap_content which results in loading images at full resolution into Bitmaps (using a lot of memory). If that's the case I would suggest using match_parent or fixed dp to lower the resolution. Note: you won't use detail, because currently the image is downsampled at render time anyway, just pulling that forward to decoding phase.
You also have to make sure that your app doesn't have constraints for memory usage. Can you load 3 (off screen limit = 1 means 1+current+1 pages) camera photos into Bitmaps without Glide? Again, assuming this is full resolution, it should be possible to store 3 screen size amount of bytes in memory with or without Glide, but you have to guide Glide to not load at full resolution.
You can load smaller sized image via .thumbnail(), it accepts a full Glide.with... not including .into() OR there's a shorthand parameter which is just a percentage (in 0.0 ... 1.0), try the latter first. It should decode the image much faster, especially with a really small number like 0.1 and then when higher quality one finishes it's replaced.
So the simpler option is to add .thumbnail() to your current load. A more convoluted one involves to start loading a lower resolution image with .sizeMultiplier() at the same time the Fragment's view is created and then start loading the high resolution one when the ViewPager has changed page. This helps with peeking the pages.
Alternatively you can just use a .placeholder() while the image is loading so it's not empty space, but "something" there.
Regarding using ARGB_8888 (32 bit per pixel): if you increase the memory consumed by Bitmaps (compared to RGB_565 (16 bit per pixel) don't expect to get run out of memory later. Once you get it working with 565 you can try increasing, but until then it's futile trying.
Also look around the Glide issues, maybe you find something helpful.
I need to upload a large image. The right way would be to use BitmapFactory.decodeFileDescriptor with inSampleSize options. But the problem, that the image is still to big to fit in memory on some devices. Is there a way, to somehow recieve an InputStream of a scaled image instead of bitmap?
Or can you suggest any other way?
Always the best place to start is the provided documentation.. they provide sample code of how to Displaying Bitmaps Efficiently
start from here, it'll help you Load Large Bitmaps Efficiently