Android: Slow performance using a big image in ImageView - android

I have an Activity where I have a background image in an ImageView, and nine transparent buttons on top of the image.
http://i.stack.imgur.com/GGQqd.png
When I tap any of the buttons I make a RelativeLayout appears in the top of the background.
http://i.stack.imgur.com/JvKQK.jpg
The problem is: When I use a big resolution image as a background in the layout the performance is very poor.
I'm using a PNG image of 2048 x 1536, and it's weight is about 500k.
When I use a smaller image like 1024 x 768 the application works fine, but it looks terrible.
Is there a restriction or a method to use big images without losing performance?

That bitmap is huge. The 500k you mention is the compressed size. Images are uncompressed in memory when you use them. You're actually looking at 2048×1536×4 = 12582912 = over 12MB of memory use.
If you're on a device like Nexus 9 that has that screen resolution, then you can reasonably assume it also has the memory, GPU and bus bandwidth to deal with an image of that size. However, if you're on a lower resolution device (most devices are, keep in mind that even Full HD is just 65% as large), then this image is phenomenally wasteful of memory. You can buy low end devices today with a 240x320 screen, where a full screen bitmap is a mere 2.5% the size of your image.
You're going to have to scale that image when you load it. Use BitmapFactory.Options to set the desired size before loading the image.
Furthermore, you're putting text directly on top of it. Text rendering requires alpha transparency, which requires sampling the underlying image. If you can put the text on an opaque background and put that on top, you'll save some GPU load as well, but I'm actually not sure how much performance that's going to get you. It may not be so big a deal.

You can use Dave Morissey's Subsampling Scale Image View as a drop in replacement of the standard image view.
XML layout:
<com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:id="#+id/photo"/>
</RelativeLayout>
Set image in code and disable touch:
photo = (SubsamplingScaleImageView) view.findViewById(R.id.photo);
photo.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
photo.setImage(ImageSource.uri(Uri.fromFile(new File("/somepath/file.png"))));
build.gradle:
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.6.0'

Related

Huge spike in memory consumption when using png with lot of transparent area

I am using a button with a png background for CopyToClipboard function in my app (used about 6-7 times in various fragment). Since the image should be small for my purpose, I increased the area of my image by putting in extra transparent area around the image so that I could increase the button size for clickable area but keep the image small (I know its not efficient and since then I have devised better way to achieve this).
I noticed the huge spikes later on afterwards when uploading some other images (big images about 150kb size), and after a lot of debugging (and I do mean a lot!) I found that the problem was not due to the bigger images but due to the CopyToClipboard image which was of just 8kb in size!!! Changing back to older CopyToClipboard image (having lesser Transparent area) brought the memory consumption back to normal.
My question is why did that happen? For such a small image to create such huge spikes (more than doubled the memory consumption from previous) and made the app slow, is quite baffling.
Image shown below : The White area is the transparent area. My button dimension : 15dp x 15dp .
I repeat, My question is why did that happen? Not a solution for it since I already solved the problem.
It doesn't really matter that your image is only 7-8KB on disk, because it will take much more memory when it's decoded.
Apparently, large transparent area can be efficiently encoded in PNG file, so the image has that small size.
But in fact, it's dimension is (600 x 745), so in memory it will take roughly (600 * 745 * 4) bytes, plus some meta information, so nearly 2 megabytes. 4 multiplier stands for an amount of bytes needed to encode color with alpha channel.
Android Bitmaps are represented internally by linear one-dimensional array of integers, so you can imagine that system needs to allocate an array with size 600 * 745 = 447000 to create your Bitmap.
That's why memory consumption is so high for such a simple image.

Load Very Large Bitmap

I'm taking whole screenshot of a WebView and display the bitmap on an ImageView. The bitmap can be 7 screen height. (E.g. 1440x14000 px)
I'm frequently face with
OutOfMemoryError.
I've seen this
This says that load a scaled down version into memory but I don't want to lose image quality. There are the same approaches on the web.
Is there any way to handle OutOfMemoryError without loading scaled down version?
The bitmap can be 7 screen height. (E.g. 1440x14000 px)
Note that this means that the user cannot see the whole image at once at full resolution.
I'm frequently face with OutOfMemoryError
On most devices, you will have a very difficult time loading an image that large, as you cannot get a single contiguous memory block that big.
This says that load a scaled down version into memory but I don't want to lose image quality
To some extent, you do not have much of a choice. If you want the user to see the full extent of the picture at once, the image has to be scaled to fit the screen.
Is there any way to handle OutOfMemoryError without loading scaled down version?
There are ImageView replacements that offer pan and zoom. Some of those, such as this one, handle loading in pieces of the image at a time, with whatever scaling is necessary for the current zoom level, to make it more likely that you will be able to show the user the entire image.
It is not a solution, of course, but I'm also not familiar with your exact needs, so maybe this may help you a little - you can try to play with bitmap options during decoding. Try to use Bitmap.Config.inPreferredConfig as RGB_565 - this will reduce size of your bitmap twice comparing to default ARGB_8888. But, of course, if you use complex images in your web page this may reduce their quality.

Imageview looks like mapview

I have a image (resolution 8328x3987). And I want to load that image in my app with zoom controls for better viewing.
As we all know that Android will not load large images directly on device (like in my example). So Android system suggested us to scale down the image to load large images. I have tried this and scale my image upto four times as:
options.inSampleSize = 4;
Using this way my image will displayed on screen, but when I zoom-in the image then this image becomes unreadable (text becomes very blurry), this is because, maybe I scale down the image before showing?
But when I see that image in to device's default gallery app (Android Lollipop, Photos app), then this image looks like a mapview (only visible portion is readable and outside is blurry, and when I move the image then visible area becomes readable) when I zoomed in at max level. So my questions is:
Is Android lollipop added any new way to load large files that looks like mapview?
If not, then do you have an idea how that apps do this or any example?
Reducing the sample size of an image that big will not work at all.(very very bad resolution and memory consumption)
I suggest you to divide image to tiles and recycle the views just like map views do.
This library may help you. Or you can do it your way too.

how to scale images appropriately to fit the screen

In my app I have a Gallery-similar functionality and faced the issue that I need to scale the images. My test device in Nexus 5 which has a screen width of 1080p (360dp). Below are screenshots of an image having a width of 604 pixels opened with my app and the system-default Gallery:
My app:
=============================
Gallery:
The code I use to show the image is quite simple:
pathToImage = getIntent().getStringExtra("path");
img = (ImageView) findViewById(R.id.imgPictureView);
try {
bmpImage = BitmapFactory.decodeFile(pathToImage);
img.setImageBitmap(bmpImage);
} catch (Exception e) {
e.printStackTrace();
}
Opening a hi-res camera photo causes my ImageView to adjust its width to fill the screen, however I'm getting a heap size of about 80 (!!) MB.
It's obvious that the system gallery decides whether to scale the Bitmap up/down to make it fit the screen. For example, it scales a 60px image up, but only by a small factor so it doesn't become really blurry. I guess it also scales large camera pictures down to prevent OutOfMemoryError's. So in my example above, the gallery assumed the width of 604px is enough to scale it up to 1080 without noticeable quality loss, my app of course not due to lack of any additional code.
So the question is: how can I achieve the same result and determine the factor to scale images up/down without quality loss depending on the current screen?
Note: for those who will now post tons of (beyond controversy amazing) code examples showing how to actually scale a Bitmap or determine the screen width - this is NOT my question.
Bitmap can use a lot of memory and you got to scale them down. Instead of giving you a copied solution, here's a detailed documentation:
http://developer.android.com/training/displaying-bitmaps/index.html
You really should go through the whole guide. There's a lot to learn.
Second, set your ImageView's width to MATCH_PARENT and try changing its scaleType attribute to centerCrop. Play with the other available values for scaleType to get your desired result.

difference between setting imageView smaller vs scaling image itself

I guess this question could be applied to both iphone and and android.
When I want to show smaller image,
I can set imageView to smaller size and let iphone handle the resizing to fit in the view.
Or
I can actually resize the image itself.(using opengl call, or such as wrapper call createScaledBitmap in android)
I find option 1 is faster.
I always wondered why?
I also wondered the memory consumed by the image is governed by the size of imageView or image?
This is good question, I think when we resize the imageView to smaller that And image(UIImage) is greater that time memory consumption is more because of that image (UIImage) size is more respect to display in imageView. I think second option would be good to save memory .
Image resizing need extra CPU cycles.So Its better to manually resize the image.

Categories

Resources