Here is my problem: I'm asking my user to choose between different sizes of a same image in order to upload it. My interface shows 4 buttons with each giving informations about the width, the height of the image, and the length of the file.
I managed to produce the four versions of the image calling Bitmap.createScaledBitmap, then compressing those bitmaps to files again.
I'm well aware I need to recycle the bitmap and delete the files when I'm done.
However, when trying to compute a big file, the method createScaledBitmap throws an out of memory exception.
I was told to use the option inJustDecodeBounds to decode the file but it would return a null bitmap.
Does someone have an idea on how to solve this problem?
Here is the code:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bmp = getScaledBitmap(BitmapFactory.decodeFile(uri, options), ratio);
private Bitmap getScaledBitmap(Bitmap bmp, float ratio) {
int srcWidth = bmp.getWidth();
int srcHeight = bmp.getHeight();
int dstWidth = (int)(srcWidth * ratio);
int dstHeight = (int)(srcHeight * ratio);
Bitmap result = Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight, true);
bmp.recycle();
return result;
}
To answer your replies, I don't consider it to be a duplicate. I previously found the thread you linked but it's quite old and it didn't solve my problem.
Anyway, I solved the problem.
I first tried using Glide library which is said to be better for memory management. Though, there was an improvement but it wasn't perfect and the problem occured again.
Using android:largeHeap="true" put an end to my nightmare. This has to be set in the application field of your manifest.
I hope this will help someone else.
Related
About my app: Taking photos, saving them to the phone memory, using BitmapFactory.decodeFile, loading that bitmap into one fixed size, not changing, image view.
Now, i know this was asked a hundred times already. I've been all over google\android's manage memory guide , but for my task it's kind of an overkill to manage and recycle different bitmaps, since i'm only using one.
Here's my code so far:
..
Bitmap bm_thumb = null;
..
private void setPic() {
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the ImageView */
int targetW = imv_thumb.getWidth();
int targetH = imv_thumb.getHeight();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(PhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
bmOptions.inSampleSize = 1;
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((targetW > 0) || (targetH > 0)) {
scaleFactor = Math.min(photoW/targetW, photoH/targetH);
}
/* Set bitmap options to scale the image decode target */
bmOptions.inSampleSize = scaleFactor; //was after decode bounds
bmOptions.inJustDecodeBounds = false;
/* Decode the JPEG file into a Bitmap */
bm_thumb = BitmapFactory.decodeFile(PhotoPath, bmOptions);
/* Associate the Bitmap to the ImageView */
imv_thumb.setImageBitmap(bm_thumb);
imv_thumb.setVisibility(View.VISIBLE);
}
These lines are not in the code, as they currently confuse me.
BitmapFactory.decodeFile(PhotoPath, bmOptions);
mCurrentBitmap = Bitmap.createBitmap(bmOptions.outWidth, bmOptions.outHeight, Bitmap.Config.ARGB_8888)
bmOptions.inBitmap = bm_thumb;
Do I need to run decodeFile everytime I change my bitMapOptions?
Plus, If the decodeFile methods returns a bitMap, why do i need to also create one?
who am i supposed to give to inBitmap? i've tried both options, non seem to solve my OOM error.
My OOM started quite a long time after first installing and testing my app. My question is, how do I know I have solved the issue? If written correctly, will it fix its error, or do I need to select "clear cache" on the phone, and hope it will never happen again?
Sorry for the long post, I was really trying to remain focused..
I'm trying to implement an image gallery in android.
The code based on http://www.mobisoftinfotech.com/blog/android/android-gallery-widget-example-and-tutorial/ and i've changed some details.
I'm using WeakReference and it seems, that when i've too many bitmaps, the garbage collector destroys my weakreferences. How can i handle this?
I get my bitmaps via this function:
public static WeakReference<Bitmap> getBitmap(String imageName, int width,
int height) {
String pathToImage = getPathToImage(imageName);
Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathToImage, options);
/*
* Calculate inSampleSize
*/
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
WeakReference<Bitmap> scaledBitmap = new WeakReference<Bitmap>(
BitmapFactory.decodeFile(pathToImage, options));
return scaledBitmap;
}
And i've taken the Solution 320x480, so i think it is not this big...
When the gallery has more than 3 pictures, some of them aren't displayed.
Is the gallery-tutorial not that good? Are there other ways to implement this?
Thank you!
Instead of using soft references, you should take a look at the lrucache class (it became available in honeycomb, but is part of the android-support library.
You can read more about it here : http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
It's pretty convenient : use this and you won't have to handle the memory yourself with weak references :-)
I know very little about OpenGL so please be gentle.
The app needs to load a bitmap (from resources), resize it, and use it in an OpenGL texture. I have an implementation that works, but there was a bad banding issue on the Wildfire S. So I changed the implementation and fixed the banding issue (by switching to ARGB_8888) but that then broke the functionality on the Galaxy Nexus and the Nexus One.
I am seeing three visual presentations:
The bitmap (a smooth 24-bit gradient) shows correctly, with no banding.
The gradient shows, but with obvious banding
The texture shows as flat white, no bitmap (or issues in logcat)
Here are two versions of the method to load the bitmap, and notes on the results seen with each:
// White on Galaxy Nexus. White on Nexus One. Renders correct image (no banding) on Wildfire S
private Bitmap getBitmap1() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.outWidth = getTextureSize();
options.outHeight = getTextureSize();
final Bitmap bmp;
bmp = BitmapFactory.decodeResource(getResources(), bitmapResourceId, options);
return bmp;
}
// Renders correctly (no banding) on Galaxy Nexus. Renders on Nexus One and Wildfire S but with obvious banding.
private Bitmap getBitmap2() {
int textureSize = getTextureSize();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.outWidth = getTextureSize();
options.outHeight = getTextureSize();
final Bitmap bmp;
bmp = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), bitmapResourceId, options), textureSize, textureSize, true);
return bmp;
}
getTextureSize() returns 1024.
How do I build a single method that shows the bitmap without banding on all devices, and without any devices show a big white box?
getBitmap1
outHeight and outWidth are used in conjunction with inJustDecodeBounds. You cannot use them to load a scaled bitmap. So the reason you are seeing a white texture is that the bitmap is not a power of two.
getBitmap2
you should keep a reference to the bitmap returned by decodeResource so that you can recycle it later.
also use options.inScaled = false;to load an unscaled version of the bitmap. Also take note that createScaledBitmap may change the depth of the bitmap to RGB_565 if the original bitmap contains no alpha channel (Source);
Questions:
is the original Bitmap Resource square? If not your scaling code will change the aspect ratio which could result in artifacts.
EDIT:
so how do you scale a bitmap and preserve the bit depths?
Easiest solution is to pass a bitmap with alpha channel into createScaledBitmap.
You can also scale yourself like so:
Bitmap newBitmap = Bitmap.createBitmap(1024, 1024, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
final int width = src.getWidth();
final int height = src.getHeight();
final float sx = 1024 / (float)width;
final float sy = 1024 / (float)height;
Matrix m = new Matrix();
m.setScale(sx, sy);
canvas.drawBitmap(src,m,null );
src.recycle();
ANOTHER EDIT:
take a look at this Question for pointers on how to deal with that.
OpenGL.org has this to say about that error:
GL_INVALID_VALUE​, 0x0501: Given when a value parameter is not a leval
value for that function. This is only given for local problems; if the
spec allows the value in certain circumstances, and other parameters
or state dictate those circumstances, then GL_INVALID_OPERATION is the
result instead.
Step one is to find the exact opengl call that is causing the problem. You'll have to do trial and error to see which line is throwing that error. If you set up the program flow like this:
glSomeCallA()
glGetError() //returns 0
glSomeCallB()
glGetError() //returns 0
glSomeCallC()
glGetError() //returns 0x501
Then you'll know that glSomeCallC was the operation that caused the error. If you look at the man page for that particular call, it will enumerate everything that could cause a specific error to occur.
In your case I'll guess that the error will be after glTexImage call just to save you some time, though I'm not positive.
If you look at the glTexImage man page, at the bottom it will list everything that can cause an invalid value error. My guess will be that your texture is larger than the GL_MAX_TEXTURE_SIZE. You can confirm this by checking glGetIntegerv(GL_MAX_TEXTURE_SIZE);
Color Banding Solved ooooooooooyyyyyyyeaaaaaaaaaa
I solved color banding in two phases
1) * when we use the BitmapFactory to decode resources it decodes the resource in RGB565 which shows color banding, instead of using ARGB_8888, so i used BitmapFactory.Options for setting the decode options to ARGB_8888
second problem was whenever i scaled the bitmap it again got banded
2) This was the tough part and took a lot of searching and finally worked
* the method Bitmap.createScaledBitmap for scaling bitmaps also reduced the images to RGB565 format after scaling i got banded images(the old method for solving this was using at least one transparent pixel in a png but no other format like jpg or bmp worked)so here i created a method CreateScaledBitmap to scale the bitmap with the original bitmaps configurations in the resulting scale bitmap(actually i copied the method from a post by logicnet.dk and translated in java)
BitmapFactory.Options myOptions = new BitmapFactory.Options();
myOptions.inDither = true;
myOptions.inScaled = false;
myOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//important
//myOptions.inDither = false;
myOptions.inPurgeable = true;
Bitmap tempImage =
BitmapFactory.decodeResource(getResources(),R.drawable.defaultart, myOptions);//important
//this is important part new scale method created by someone else
tempImage = CreateScaledBitmap(tempImage,300,300,false);
ImageView v = (ImageView)findViewById(R.id.imageView1);
v.setImageBitmap(tempImage);
// the function
public static Bitmap CreateScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
{
Matrix m = new Matrix();
m.setScale(dstWidth / (float)src.getWidth(), dstHeight / (float)src.getHeight());
Bitmap result = Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig());
Canvas canvas = new Canvas(result);
//using (var canvas = new Canvas(result))
{
Paint paint = new Paint();
paint.setFilterBitmap(filter);
canvas.drawBitmap(src, m, paint);
}
return result;
}
Please correct me if i am wrong.
Also comment if it worked for you.
I am so happy i solved it, Hope it works for you.
Obviously this is an expensive/time-consuming operation. Any way to improve this?
Bitmap bm = BitmapFactory.decodeStream((InputStream) new URL(
someUrl).getContent());
I'm guessing there's really no way to avoid this relatively intense operation, but wanted to see if anyone had any tweaks they could recommend (aside from caching the actual bitmap, which for whatever reasons simply isn't relevant here)
If you don't need the full resolution you can have it only read ever nth pixel, where n is a power of 2. You do this by setting inSampleSize on an Options object which you pass to BitmapFactory.decodeFile. You can find the sample size by just reading the meta-data from the file in a first pass by setting inJustDecodeBounds on the Options object. Aside from that - no, I don't think there's an easy way to make to go any faster than it already does.
Edit, example:
Options opts = new Options();
// Get bitmap dimensions before reading...
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opts);
int width = opts.outWidth;
int height = opts.outHeight;
int largerSide = Math.max(width, height);
opts.inJustDecodeBounds = false; // This time it's for real!
int sampleSize = ??; // Calculate your sampleSize here
opts.inSampleSize = sampleSize;
Bitmap bmp = BitmapFactory.decodeFile(path, opts);
I'm trying to find the size of my image but not to load into memory. I use the flowing code
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(a.getResources(), R.drawable.icon, o);
int width1 = o.outWidt;
int height1 = o.outHeight;
Now, I make some comparison:
Bitmap icon = BitmapFactory.decodeResource(a.getResources(), R.drawable.icon);
int width = icon.getWidth();
int height = icon.getHeight();
Why width, height is not equal to width1 and height1 ?
I'm almost certain this is because referencing that image from resources with decode the image comes pre scaled for density.
Checkout #1 here on the docs: http://developer.android.com/guide/practices/screens_support.html#DensityConsiderations
I'm not sure what is going on, but it may be that BitmapFactory applies density scaling when actually returning a bitmap (as described here), but not when it is just decoding its size. (If this is what's going on, I'd consider filing a bug report.)
You can test this theory by moving your image from the drawables directory to drawables-nodpi.