How to load a high resolution Bitmap in Android? - android

I'm working on an android app, and in resources folder I have an image which is 8000x400px resolution. It is a .png that I'm using at my Sprite class to simulate the movement of an animal.
I display the portion of the png using drawBitmap() in my SurfaceView class.
Sprite class, SurfaceView and all the elements work perfect, but when working with that large images it doesnt display anything.
To fix this problem I would like to know.
What is the limit of maximum resolution of a Bitmap allowed in
Android?
How could I display in onDraw() a Sprite with that size?

Concerning the displaying / loading of Bitmaps:
You need to load the Bitmap properly and adjust the size of the Bitmap to your needs. In most cases, it makes no sense to load a Bitmap with higher resolution than the screen of the device supports.
Furthermore, this practice is very important to avoid OutOfMemoryErrors when working with Bitmaps that large.
As an example, a Bitmap with the size of 8000 x 4000 uses more than 100 Megabytes of RAM (in 32 Bit color), which is an enormous amount for a mobile device and much more than even high end devices are capable of handling.
This is how to load a Bitmap properly:
public abstract class BitmapResLoader {
public static Bitmap decodeBitmapFromResource(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);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
}
Example usage in code:
Bitmap b = BitmapResLoader.decodeBitmapFromResource(getResources(),
R.drawable.mybitmap, 500, 500);
Taken from the Google Android Developer guidelines here:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Concerning the maximum Bitmap size:
The maximum Bitmap size limit depends on the unterlying OpenGL implementation. When using OpenGL, this can be tested via (source: Android : Maximum allowed width & height of bitmap):
int[] maxSize = new int[1];
gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxSize, 0);
e.g. for the Galaxy S2, it is 2048x2048.

Related

Android: Problems loading images

I have bigs problems with the decodeSampleBitmap code and the final size of my images. I have my drawables in nodpi drawables folder for not multiply the same drawable in diferents folders with diferents sizes, and i resize after aply my decodeSampleBitmap.
I have this code for load a bitmap:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
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);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
The code i can get at the documentation. Ok i think this is for load images more eficiently but i have memory problems. I try explain this problem.
1-I want load a png, 2037x1440 pixels, 12.6kb.
2-I aply my code and load a png with 2037x1440 pixels and 11,73 MB. I dont undurstand what i'm doing wrong. Why android multiply for 1000 the size of my drawables?
10-12 12:06:07.842 12405-12822/com.viridesoft.kiseichuu V/control4: width=2037 height=1440 size=11733120
Edit: if i load the drawable with BitmapFactory.decodeResource the size is the same, 11733120.
The old question should be changed. I understand i cant reduce more my image size and i continue with the same problem.
I load a lot of images for create a dinamical background and charge all the characters(my player and zombies), in total until 111 bitmaps of 25%px x 10%px of the screen size and until 30 bitmaps of the 100% screen size. I load all this bitmaps in a loader activity and save for the later use in the game. I have a good quality of images and a drawable-nodpi folder with a lot of images for the best android resolution i see. My game run fluid in some devices (bq aquaris, nexus 5, etc) but is continually cleaning the memory and run slow in others (some tablets, some mid-range devices, etc )
The new question is:
How i can load eficiently a lot of images for a fluid game experiencie?

How to make a bitmap take less memory on Android?

I have a set of image files stored in internal storage, each file size is about 750 KB. I need to create a 360 degrees animation like with this images, so, I load each image in a list, while I'm doing this process an out of memory exception appears.
I have been reading about bitmap processing on Android, but in this case is not about resize bitmap dimensions, the dimensions are OK (600, 450) because its a tablet application, is about image quality I think.
Is there a way to reduce the memory each bitmap takes?.
It is not possible without reducing image dimensions.
All images with the same dimensions require same amount of RAM, regardless it size on disk and compression. Graphics adapter don't understand different image types and compression and it needs only uncompressed raw array of pixels. And it size is constant
For example
size = width * height * 4; // for RGBA_8888
size = width * height * 2; // for RGB_565
So you should reduce image dimensions or use caching on the disk and remove bitmaps from the RAM that are currently invisible and reload from disk when needed.
There is a great resource on how to do this here:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Basically you need to load a bitmap at a different resolution using these two functions:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
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);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Then set the image as follows:
imageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
R.drawable.my_image_resource,
imageView.getWidth(),
imageView.getHeight()));
Hope that helps!

BitmapFactory.decodeFile out of memory with images 2400x2400

I need to send an image from a file to a server. The server request the image in a resolution of 2400x2400.
What I'm trying to do is:
1) Get a Bitmap using BitmapFactory.decodeFile using the correct inSampleSize.
2) Compress the image in JPEG with a quality of 40%
3) Encode the image in base64
4) Sent to the server
I cannot achieve the first step, it throws an out of memory exception. I'm sure the inSampleSize is correct but I suppose even with inSampleSize the Bitmap is huge (around 30 MB in DDMS).
Any ideas how can do it? Can I do these steps without created a bitmap object? I mean doing it on filesystem instead of RAM memory.
This is the current code:
// The following function calculate the correct inSampleSize
Bitmap image = Util.decodeSampledBitmapFromFile(imagePath, width,height);
// compressing the image
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 40, baos);
// encode image
String encodedImage = Base64.encodeToString(baos.toByteArray(),Base64.DEFAULT));
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromFile(String path,int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path,options);
}
you can skip ARGB_8888, and use RGB_565 instead, and then dither then images to preserve good quality
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
you have to use BitmapFactory.Options with inJustDecodeBounds set to true. This way you can load information about the bitmap and calculate the value for downsampling it (inSampleSize for instance)
Do NOT load the image as a bitmap, convert it to an array, then send it.
instead:
Read it as a file, in JPG format. Use the files byte array, to encode it, and send the file across.
Loading it to bitmap, is going to cause huge memory issues unnecessarily. An image, reperesented in Bitmap format, will take ~20x or more memory than neccessary.
On the server side, you will need to treat it as a file too. rather than a bitmap.
Here is a link to loading a file to byte[] : Elegant way to read file into byte[] array in Java

Scaling Icon to fit in Imageview Android

I have a list in my application that contains all the installed applications with their icons, I'm able to render the installed applications and the icons as well but it is consuming lot of memory as it loads the drawables(icons) of the installed applications into memory as well.
I want to scale down the icon and then load into memory just to reduce the memory usage of the application. Can anyone tell me how that can be achieved.
Note : if PNG format then it will not compress your image because PNG is a lossless format.
and applications icons are in PNG format
Any way to reduce the memory allocation for the icons??
Yeah it's all in the docs:
http://developer.android.com/training/displaying-bitmaps/index.html
Calculate your sample size, i.e. size of the bitmap you want:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
The decode your bitmap at this size:
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);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
This should all be done off the UI thread:
http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
You can then cache the bitmaps so you don't have to do it more times than necessary:
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Use inSampleSize from BitmapFactory.Options
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; //Downsample 10x

Android Bitmap OutOfMemoryError [duplicate]

This question already has answers here:
Android: BitmapFactory.decodeStream() out of memory with a 400KB file with 2MB free heap
(8 answers)
Closed 9 years ago.
I'm having an OutOfMemoryError in my VSD220 (It's a 22" Android based All in one)
for (ImageView img : listImages) {
System.gc();
Bitmap myBitmap = BitmapFactory.decodeFile(path);
img.setImageBitmap(myBitmap);
img.setOnClickListener(this);
}
I really don't know what to do, because this image is below the maximum resolution. The image size is something about (1000x1000), and the display it's 1920x1080.
Any help?
(That foreach cycle is for about 20 elements, it gots broken after 6, or 7 loops..)
Thanks a lot.
Ezequiel.
You should take a look at the training docs for Managing Bitmap Memory. Depending on your OS version, you could use different techniques to allow you to manage more Bitmaps, but you'll probably have to change your code anyway.
In particular, you're probably going to have to use an amended version of the code in "Load a Scaled Down Version into Memory", but I at least have found this section to be particularly useful:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
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);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
This method makes it easy to load a bitmap of arbitrarily large size
into an ImageView that displays a 100x100 pixel thumbnail, as shown in
the following example code:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Are you really sure you want to load the same Bitmap 20 times? Don't you want to load it once and set it inside the loop.
Still, loading a 1000x1000 pixel image is not guaranteed to work, regardless of screen resolution. Remember that a 1000x1000 pixel image takes up 1000x1000x4 bytes =~4MB (if you load it as ARGB_8888). If your heap memory is fragmented/too small you may not have enough space to load the bitmap. You may want to look into the BitmapFactory.Options class and experiment with inPreferredConfig and inSampleSize
I would suggest that you either use the suggestion by DigCamara and decide on a size and load a downsampled image of nearly that size (I say nearly because you won't get the exact size using that technique) or that you try to load the full size image and then recursively increase the sample size (by factors of two for best result) until you either reach a max sample size or the image is loaded:
/**
* Load a bitmap from a stream using a specific pixel configuration. If the image is too
* large (ie causes an OutOfMemoryError situation) the method will iteratively try to
* increase sample size up to a defined maximum sample size. The sample size will be doubled
* each try since this it is recommended that the sample size should be a factor of two
*/
public Bitmap getAsBitmap(InputStream in, BitmapFactory.Config config, int maxDownsampling) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inPreferredConfig = config;
Bitmap bitmap = null;
// repeatedly try to the load the bitmap until successful or until max downsampling has been reached
while(bitmap == null && options.inSampleSize <= maxDownsampling) {
try {
bitmap = BitmapFactory.decodeStream(in, null, options);
if(bitmap == null) {
// not sure if there's a point in continuing, might be better to exit early
options.inSampleSize *= 2;
}
}
catch(Exception e) {
// exit early if we catch an exception, for instance an IOException
break;
}
catch(OutOfMemoryError error) {
// double the sample size, thus reducing the memory needed by 50%
options.inSampleSize *= 2;
}
}
return bitmap;
}

Categories

Resources