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.
Related
I am learning how to use Bitmap to scale down images instead of having them crash the app. I am following the developer page for android at http://developer.android.com/training/displaying-bitmaps/load-bitmap.html. I have a couple of questions about the code.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
For the code above it creates a new bitmapFactory and decodes the widget. Why use R.id.myimage over R.drawable.image? I am assuming that R.id.myimage refers to a image view while R.drawable.image I added and it directly refers to the image I want rescaled. Finally what does outMimeType refer too?
You know, I'm pretty sure that's a mistake. The resource you want is indeed a drawable, referenced by R.drawable.image.
The options.outMimetype just tells you what the mimetype of the decoded resource is (a PNG, a JPEG, etc.), if that information is available.
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 );
Drawable image = Drawable.createFromPath(newImagepath);
defective_image.setBackgroundDrawable(image);
The image is stored in the newImagePath variable which is a String. The above throws an outOfMemory Exception.
I also tried retrieving the image into a Bitmap object.
Bitmap bitmapImg= BitmapFactory.decodeFile(newImagepath);
In the above case the bitmapImg is Null. (Without any exception)
However if it is retrieved as a file, it's successful. This doesn't serve my purpose because I want this image to be the background of a RelativeLayout.
File imageFile = new File(newImagepath);
String imgPath = imageFile.getAbsolutePath();
The imgPath is the same as the newImagePath, concluding the path is not wrong. I also verified the image's existence in the SDCard.
This image was captured from the device's camera. This code is working on the emulator successfully. When debugged on the device the above said faults were noticed.
I also tried:-
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
It seems to be a memory issue. So I suggest to decode bitmap size with the option
options.inJustDecodeBounds = true;
then getting the size of this bitmap by looking at fields (don't try to use bitmap : it will be null) :
options.outWidth;
options.outHeight;
Then creating a scaled bitmap (depending on the screen dimension). To do this, you can look at
BitmapFactory.Options.inSampleSize
I would rather use a BitmapFactory to decode the Image from the file-path:
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
jpgView.setImageDrawable(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/
I am downloading images from web and i use Gallery widget to display the images.
If the downloaded image size is huge, my application crashes with the below log.
"E/GraphicsJNI( 3378): VM won't let us allocate 5591040 bytes"
I want to scale down the downloaded image size only when the image size is more to an extent that it will crash the app. I have written the code to scale down the image size but i am not sure how to find the bitmap size so i can decide on whether to scale or not
BitmapFactory.Options o = new BitmapFactory.Options();
o.inSampleSize = 2;
Bitmap bit = BitmapFactory.decodeStream(inputStream,null,o);
Bitmap scaled = Bitmap.createScaledBitmap(bit, 200, 200, true);
bit.recycle();
return scaled;
To get bitmap dimensions, you can simply use:
To get height -> bitmap.getHeight()
To get width -> bitmap.getWidth()
Use inJustDecodeBounds field of BitmapFactory.Options to get bitmap dimensions.