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?
Related
I'm trying to have live tile images in my application. I tested the functionality on some devices (API>22) and they work. But now i'm testing on API 22 and i'm getting the error in the title. I've searched through the site and I found this to be particularly helpful OutOfMemoryExceptionGridView. But I'm loading my images (straight from the res folder) to an array, then using a viewflipper to make the slideshow
How do I change my block of code (to fix the main OOME) since what I linked above uses bitmaps, and I am directly calling the res id.
This is my code:
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
int[] images = {R.drawable.img1, R.drawable.img2, R.drawable.img3}; //store images into array
viewFlipper = view.findViewById(R.id.firstTile); //main tile to have slideshow
for (int image : images) {
flipperImages(image); //performs the slideshow per image
}
}
public void flipperImages(int image) {
ImageView imageViewFirstTile = new ImageView(getContext());
imageViewFirstTile.setBackgroundResource(image);
viewFlipper.addView(imageViewFirstTile);
viewFlipper.setFlipInterval(12500);
viewFlipper.setAutoStart(true);
viewFlipper.setInAnimation(getContext(), android.R.anim.slide_in_left);
viewFlipper.setOutAnimation(getContext(), android.R.anim.slide_out_right);
}
How can I fix the main error with this implementation (calling the res id of the images directly)?
If the images can be scaled down to avoid loading the full sized images, I would consider that as a first step. There is no need to load the full-sized Bitmaps into memory if they window in which they're displayed is a fraction of the size. In your current approach, the images are not subsampled and as a result the entire Bitmap is loaded into memory at it's full size. So you have 3 full sized Bitmaps in memory.
For subsampling an image, see the following code. (Source: https://developer.android.com/topic/performance/graphics/load-bitmap)
/* reqWidth and reqHeight would be the dimensions of your ImageView or some other preferred dimensions. For example, if your Bitmap is 500 X 500 and your ViewFlipper/ImageView is only 100 X 100, passing 100 as reqWidth and 100 as reqHeight will subsample the image until it's dimensions are close to the 100 X 100. This will largely shrink the size loaded into memory. */
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);
}
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;
}
I have problem with shadow of ActionBar it doesn't show when i add this : android:hardwareAccelerated="false" in my AndroidManifest.xml but when i delete this line it show shadow well and show other problem in my Activity which has Recycleview with items of images(small size images) it be very slow when i scroll the Recycleview, but when add android:hardwareAccelerated="false" scrolling be normal.
Please anyone could help me with this?
According to google documentation
Beginning in Android 3.0 (API level 11), the Android 2D rendering pipeline supports hardware acceleration, meaning that all drawing operations that are performed on a View's canvas use the GPU. Because of the increased resources required to enable hardware acceleration, your app will consume more RAM.
So i guess writing android:hardwareAccelerated="false" stops the app from making use of the GPU, hence reducing GPU rendered effects like shadows.
A couple of things that you can do is
Add the android:hardwareAccelerated="false" for the specific activity where you want to load heavy images. Click here to check that.
Try and reduce the size of the images that you are loading. This can be done using the following two functions as documented in the developer documentation for android. Click here for the complete documentation.
Function 1
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;
}
Function 2
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);
}
Set Image in ImageView
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Also refer this link for a another reference about Image Resizing.
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!
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.
I want to reproduce the image loading found in Google Plus Android app or in 9gag Android App where the image is not downloaded yet, but the ImageView already has the width and height. I thought about sending the dimensions with the URL like
{"image":{"url":"http://someurl.com/withanimage.jpg","width":"500","height":"300"}}
And then using that width and height to set a size to the ImageView before the image is downloaded, but the thing is that Android has many different sizes of screen and DPI that I don't really find a way to do this. Any help would be appreciate, thanks.
Short answer see 'decodeFile()' that calls BitmapFactory.decode 2X. First time just to get the bounds of the file which you can compare to the available pixels on the device, producing mediated BitmapImage size as below:
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) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
...
options.inSampleSize = calculateInSampleSize(options, metrics.widthPixels, metrics.heightPixels);
bmp_larg = BitmapFactory.decodeStream(getContentResolver().openInputStream(uris[0]), null, options);
Long answer spend time on this project looking how the ImageLoader is configured depending on the device.