Resize images and setCompoundDrawablesWithIntrinsicBounds - android

My app contains buttons with images on them, set by using setCompoundDrawablesWithIntrinsicBounds. I use images from the app's drawables folder, but also use images downloaded from the web and stored on SD card. I found that I needed to upscale the SD card images so they'd render as the same size as the images in drawables. I did this using:
Options opts = new BitmapFactory.Options();
opts.inDensity = 160;
Bitmap bm = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory() +
context.getResources().getString(R.string.savefolder) + iconfile, opts);
myIcon = new BitmapDrawable(context.getResources(), bm);
btn.setCompoundDrawablesWithIntrinsicBounds(myIcon, null, null, null );
This has worked with no problems, until I updated my phone to Android 4.1.1 and noticed that the downloaded images were now appearing at a much smaller size than those from the drawable folder.
I messed around with the inDensity value to little effect, but had more success with scaling the bitmap based on a btnheight value (just the height of the button the image sits on):
int intoffset=bm.getHeight() - bm.getWidth();
myIcon = new BitmapDrawable(context.getResources(),
Bitmap.createScaledBitmap(bm, btnheight - (((btnheight/100)*10) +
intoffset) , btnheight - ((btnheight/100)*10), true));
This sort of works, but the image is still a little bigger than the button it sits on (which shouldn't be the case, based on the above, as it should scale the image height to 90% of the button height.) I did this as a test. I can't use this method in my app as the button height changes in accordance with the font size displayed on the buttons, and the user can change this font size in the app preferences.
As an aside, I found that, oddly(?), by scaling the bitmap to twice it's original height using
Bitmap.createScaledBitmap(bm, bm.getWidth() * 2
, bm.getHeight() * 2, true));
It rendered correctly, (well, it was displayed at the same size as the drawable icons) in 4.0.3 and 4.1.1, but behaved as you'd expect (rendered bigger than the button it sits on) in 2.1.
If anyone has any insights as to why this happens in 4.1.1, and what I can do so my decodeFile bitmaps render at the same size as my drawable bitmaps, without having to code for 4.1.1 separately, it would be much appreciated!

Modifying my original code to be as below works on 4.1.1 as well as the previous versions I tested it on...
Options opts = new BitmapFactory.Options();
DisplayMetrics dm = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(dm);
int dpiClassification = dm.densityDpi;
opts.inDensity = dm.DENSITY_MEDIUM;
opts.inTargetDensity = dpiClassification;
opts.inScaled =true;
Bitmap bm = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory() +
context.getResources().getString(R.string.savefolder) + iconfile, opts);
myIcon = new BitmapDrawable(context.getResources(), bm);
btn.setCompoundDrawablesWithIntrinsicBounds(myIcon, null, null, null );

Related

how to reduce chace on android application (using eclipse)?

I'm developing application for my android tablet that using many images.
in my activity, I use effect splash that show image (3840 x 2108 70 KB) for the opening theme.
I use another background image (3840 x 2108 69 KB).
but, when I test it on my tablet, this application's cache reached 80 MB!
For your information, I load the image from my xml file.
Can anyone help me to reduce it?
Is it wrong to load image from xml file?
Is it the size of my images that cause this problem?
//UPDATE
at the end, i didn't found a really good way to my question. I'm using xml (I put at dawable) file to replace my big size image, and this method really reduce the cache alot. My conclusion is avoid using big image, instead, just replace the color image using color.
The advantage to use color is "faste to load", it reduce lag of my application.
Android default color model is ARGB_8888. It takes 4 byte for 1 pixel. So splash and background bitmap images in memory takes 3840*2108*4*2 = 61.76MB. You could resize the images for different device dpi and put them in proper drawable folder. For example, drawable-hdpi is suitable for 240 dpi, drawable-xxhdpi is suitable for 480 dpi.
In addition, you could manually load the images with java code. The following is a method to resolve the image safely:
protected Bitmap getBitmapSafely(Resources res, int id, int sampleSize) {
// res = youractivity.getResources, id = R.drawable.yourimageid
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inSampleSize = sampleSize;
try {
bitmap = BitmapFactory.decodeResource(res,
id, options);
} catch (OutOfMemoryError oom) {
Log.w("ImageView", "OOM with sampleSize " + sampleSize, oom);
System.gc();
bitmap = getBitmapSafely(res, id, sampleSize + 1);
}
return bitmap;
}
Your could use this method with sampleSize = 1 at first time. If there is not enough memory, it will catch OOM and increase the sampleSize until the image can be resolved.

Android: Quality loss when downscaling Bitmap

I'm getting an image (.png) from SQLiteDatabase and using this code to decode the bytearray into a bitmap:
Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = true;
options.inScaled = true;
options.inDensity = 240;
options.inTargetDensity = metrics.densityDpi;
Bitmap bmp = BitmapFactory.decodeStream(new ByteArrayInputStream(imageAsBytes), null, options);
As you can see, image (3) should be like (2), but it doesn't.
1) = Image with no scale (metrics.densityDpi = 240);
2) = same .png above, but compiled in res/drawable;
3) = Image with down scale (with metrics.densityDpi = 120);
I also tried options.inDither = false;, but I see no difference.
So what's wrong with my code?
There a few other things I would try:
Load the png with no scale, when you come to draw the Image (either from within an ImageView or directly onto the canvas) set a Matrix to scale the image
Alternatively, load the image in the required density and try drawing the Bitmap directly to the canvas with a Paint object. After instantiating your Paint, enable Bitmap filtering (this will increase the image quality)
setFilterBitmap(true)
Finally, you could always load the Bitmap (density independent) and resize the Bitmap manually using Bitmap.createScaledBitmap, make sure you set the third paramenter to true (this enabled bitmap filtering for increased quality). Below is an example of scaling a bitmap where 100 is the desired size:
Bitmap.createScaledBitmap ( original_bitmap, 100, 100, true);
Briefly, the best quality downscaling algorithm consists of 2 steps:
downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()
Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/

Android bitmap quality issues

In my app, the bitmap is drawn as if the color is some lower quality type. If i load up the background image using the gallery app, it loads just fine and does not look like it's super low quality. The code i am using to load and draw my images is simple:
//Code for initializing the Bitmap
Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.none), (int) (canvas.getWidth() * compression), (int) (canvas.getHeight() * compression), true);
//...
//Code for drawing this Bitmap
canvas.drawBitmap(bitmap, null, new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), null);
If nothing in the code tells you what is wrong, i made an image comparing what the image actually looks like on a computer or other image viewer, and what it looks like in the app.
question is somewhat similar to Bad image quality after resizing/scaling bitmap
try disabling scaling, resize in an offscreen bitmap and make sure that Bitmap is 32 bits (ARGB888):
Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap source = BitmapFactory.decodeResource(a.getResources(), path, options);
another good and complete answer about image scaling/processing can be found at Quality problems when resizing an image at runtime

Incorrect image dimensions in android when using Bitmap

I have.png image file stored as a resource in my android application.
In my code, i am allocationg new Bitmap instance from that image as follow:
Bitmap img = BitmapFactory.decodeResource(getResources(), R.drawable.imgName);
But when I read the image dimensions from the Bitmap object using getWight() and getHeight() methods,
int width = img.getWidth();
int height = img.getHeight();
I am getting different results from the original image... Can some one explain me what am I missing, and how can I retreive the image size?
(My project is complied with android 2.2 - API 8)
Edit:
Ok - found out how to get the real dimensions:
setting inJustDecodeBounds property of the BitmapFactory.Options class to true as follow:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.imgName, options);
width = options.outWidth;
height = options.outHeight;
The problem now is that the decoder returns null when we send Options argument, so I need to decode again like I did before (without Options argument...) to retrieve Bitmap instance -bizarre, isnt it?
To get exact resource image use:
BitmapFactory.Options o = new Options();
o.inScaled = false;
Bitmap watermark = BitmapFactory.decodeResource(context.getResources(), id, o);
This turns off the automatic screen density scaling.
Update:
I'm sure you realized this by now, but inJustDecodeBounds does just that, it finds the dimensions. You will not get an image. That option is generally for doing custom scaling. You end up calling decodeResource twice, the second time setting:
options.inJustDecodeBounds = false;
and making any adjustments to the options based on your:
width = options.outWidth;
height = options.outHeight;
Android scales your image for different densities (in a way for different screen resolutions and sizes). Place a separate copy of your image in drawable-ldpi, drawable-hdpi,drawable-xhdpi , drawable folders.

Android load big images from external storage

I need to load lots of big images (500 .png files) from the SD card to my app. Do I always have to convert images to Bitmap and make Bitmap files? I don't want to resize the Heap.
Is there another way to read the images from SD card?
If you're displaying them in a view, then you have to load them into memory in their entirety.
You didn't mention how large your images will get, but what we do in our photo gallery is to keep a list of SoftReferences to these bitmaps, so that the garbage collector can throw them away when they're not visible (i.e. when the view displaying them gets discarded--make sure that this actually happens, e.g. by using AdapterView). Combine this with lazy loading of these bitmaps and you should be good.
The internal representation of the image in your app is a collection of bits and bytes - not an image of any specific format (png, bmp, etc).
The image is converted to this internal representation when the image is loaded by the BitmapFactory.
It is usually not a good idea to load all the bitmaps at once, you will quickly run out of memory...
If your image's dimension is very big, you must to resize them before loading in to ImageView. Otherwise, even one picture can easily cause out of memory problem. I don't know how many images you want to display concurrently and how big they are. But I suggest you to resize them before displaying them.
To resize image and show it, you can use this code:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(fileInputStream, null, bitmapOptions);
//reduce the image size
int imageWidth = bitmapOptions.outWidth;
int imageHeight = bitmapOptions.outHeight;
int scale = 1;
while (imageWidth/scale >= screenWidth && imageHeight/scale >= screenHeight) {
imageWidth = imageWidth / 2;
imageHeight = imageHeight / 2;
scale = scale * 2;
}
//decode the image with necessary size
fileInputStream = new FileInputStream(cacheFile);
bitmapOptions.inSampleSize = scale;
bitmapOptions.inJustDecodeBounds = false;
imageBitmap = BitmapFactory.decodeStream(fileInputStream, null, bitmapOptions);
ImageView imageView = (ImageView)this.findViewById(R.id.preview);
imageView.setImageBitmap(imageBitmap);
In my android project, I am using this piece of code to resize my HD wallpaper to review it.
Android Save And Load Downloading File Locally

Categories

Resources