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.
Related
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.
I am saving three imageviews to one bitmap after the user edits the image(s). The problem is that once the final result is saved, the resulting saved image doesn't always look the same on different screen resolutions.
On my phone, which is a Asus Padfone X, the saved image comes out perfectly. On my partners phone, Note 6, the positioning of the saved images on the resulting bitmap are off. (See example below)
I am using the following code to save the imageviews to a single image:
Bitmap mainBmp = backImage.getDrawingCache();
Bitmap centerBmp = centerImage.getDrawingCache();
Bitmap handleBmp = handleImage.getDrawingCache();
Canvas canvas = new Canvas(mainBmp);
canvas.drawBitmap(centerBmp, 17, 95, null);
canvas.drawBitmap(handleBmp, 17, 85, null);
Which saves properly on my phone but not one with a bigger screen/higher resolution. How do I ensure the resulting saved image looks the same regardless of the device it is saved on?
EXAMPLE OF DIFFERENCE
You need to scale. For example, lets say that device 1 is 500x1000 pixels and device 2 is 600x1200. Lets say the bitmap is 300x400 and starts at an offset of 30 pixels in x and 100 in y.
The bitmap needs to grow in size by the ratio of the two screens sizes. So it would need to be 300*(600/500) pixels wide and 400*(1200/1000) pixels big. The offset would need to scale as well- to 30*(600/500) and 100*(1200/1000).
Please note that when scaling you may get stretching, pixelation, and other image defects. The more you stretch the worse it will be. Shrinking can also give defects, but these tend to be more minor.
This is the simple case- the two screens I gave have the same aspect ratio (ratio of width:height). In the case of different aspect ratios, you need to choose:
1)Keep the aspect ratio of the image the same. This will mean you'll have blank space in 1 direction, but the image won't be effected.
2)Allow the aspect ratio of the image to change. This will leave no blank space, but will skew the image.
For #2, use the same code as above. For #1, figure out which dimension needs to scale the least, and use that to scale x and y.
It is not possible to scale from any size phone to any size phone without any defects on a generic image. You'll have to choose between the two options above.
I also found that if I instead use imageview.getWidth() and imageview.getHeight(), I can position each "layer" where I need it to be no matter the screen size. I tested this method on a small android device all the way up to a tablet and the saved image came out perfect across all devices.
What I did was determine the pixel size of each image("layer") using Photoshop. I then used an online px to dp calculator/converter to determine the correct dp size of each layer. I set specific dp width and height for each layer in the layout. For each layer I needed to save to the main image, I calculated the x and y dimensions by taking the mainimageviews height and width and subtracting the pixels needed from the edge to the respective edge of that layer. Through trial and error, I ran the app a few times to get the exact positioning of each layer I wanted. The result of the saved image was exactly the way it looked to the user. Hope this helps someone else in the future.
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'
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.
As part of this application I am making, I want to change my wallpaper on my android device based on a bitmap that the user has selected.
I am able to change the background. However, the background is a completely distorted version of the bitmap. Any idea as to why this would happen?Here is my code:
Bitmap bitmap = ((BitmapDrawable)currentDrawable).getBitmap();
this.setWallpaper(bitmap);
My bitmap has a width of 240px and height of 180px. My emulator is of a size 480px by 800px.
I can scale my bitmap to 480px. However, the bitmap still ends up being distorted on the android wallpaper. Any thoughts on how this can be fixed?
Thanks in advance
You should create multiple images, for each potential (and common) resolution. You don't want any scaling, or the image will be distorted.
UPDATE:
Here is a ton of good information on support multiple screen sizes:
http://developer.android.com/guide/practices/screens_support.html
The wallpaper used by the default Launcher is bigger than the actual screen, that's why you can scroll the content and the wallpaper too.