Bitmap is not being scaled correctly because of density - android

I am loading a bitmap that is stored in the drawable-hdpi folder (with all the other images).
options.inScaled = false;
Bitmap rawBMP = BitmapFactory.decodeResource(res, resId, options);
Bitmap finalBMP = Bitmap.createScaledBitmap(rawBMP, reqWidth, reqHeight, true);
the original bitmap is 512x512, thats also the size of rawBMP if i set inScaled to false.
The final bitmap only works on a device with less that 512 (my S2 with 480). On devices with higher res the final bmp size is reported as it should be, but the visible part of that image is the original size. So createScaledBitmap is creating a big bitmap but does not scale the original upwards.
If i set inScaled to true, i receive a rawBMP with LESS than original size but then the final bitmap seems to work but as you can imagine the quality suffers as it more than doubles in size.
On a Galaxy 10 tablet :
inScaled true : 512 original, 341 rawBMP, final 800 (density 160) final image is visible across the entire screen (height)
inScaled false: 510 original, 512 rawBMP, final 800 (density 240) final image visible part is still 512, the rest is filled with transparent, there was absolutely no scaling here.
I dont understand this behaviour. If i have a bmp with 512 pixels and want to scale it to 800 pixels and then just draw those pixels to the screen 1:1
edit
For arguments sake lets say i need the bitmap scaled this way, the code is a bit more than just this snippet

Related

Android allocates 9x memory for each ImageView as required by default

While adding a ViewPager and running into some OutOfMemory errors, I realized that my default method of ImageView.setImageResource(resId) was causing 9x more RAM to be allocated than need be. In my example, I had a 1920 x 1080 image which at native resolution was slightly bigger than the ImageView object it was placed in. I could expect some downsizing of the image but would not expect more than 12.6 MB to be allocated (1920 x 1080 x 4 bytes). However, 74.6 MB was allocated per image, and with 4 images loaded this quickly blew the VM memory budget away.
To solve this, I changed the method to the slightly longer code below - now each image is allocated exactly the native amount of memory expected (12.6 MB), and the images still load quickly and look great.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inSampleSize = 1;
Bitmap bm = BitmapFactory.decodeResource(getResources(), resId, options);
mImageView.setImageBitmap(bm);
For reference, my device is a Nexus 5 with 1920 x 1080, xxhdpi, 3x scaling factor (dp to pixels). I imagine that internally the OS is scaling the image up 3x3 (=9x) to match the scaling factor, but that does not make sense since the original image is at native resolution of the full screen size.
What is the reason that they are initially allocating so much memory, and is there a proper way of setting the image resource to avoid this memory waste?
Drawables in the drawable folder are treated as mdpi (i.e., 1x). Therefore the system will automatically upscale the image to the native density, growing it 3 times in size in each direction, for a total of 9x the memory usage.
You should move the image to drawable-xxhdpi if you want the system to use it as its native resolution and the system will downscale it for lower density devices.
If instead you want a fixed number of pixels on all densities (i.e., a different physical size on screen for different density devices), you can use the drawable-nodpi folder.

getWidth() function in bitmap library of android doesn't return true image height

I wanted to load an .PNG image through my android application by using android.graphics.Bitmap library. I have succeeded to load it but there is some problem i can't figure out.
My image pixel count is 144*144 but when i try to get the width of my loaded image by using getWidth() function, it returns 432 which is 144*3.
This is how i try to load my image:
mBitmapIn = loadBitmap(R.drawable.data);
Where the loadBitmap function is defined as:
private Bitmap loadBitmap(int resource) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
return BitmapFactory.decodeResource(getResources(), resource, options);
}
I would appreciate if anybody could help me to find out the reason of it.
Thanks
drawable folder is considered to have mdpi's density (which is the baseline density). Nexus 5 has a density of 445ppi which falls under xxhdpi category. Therefore if you read in a bitmap which is not present in drawable-xxhdpi directory, the system will have to find it in another directory and scale it. xxhdpi screens are approximately 3 times as dense as mdpi screens. Since the only drawable folder you have under res is drawable itself, android scales it 3 times along both the dimensions
You can get more information about this here

How to resize a bitmap image : Android

I want to increase size of image. i get image with this code BitmapFactory.decodeResource(getResources(), R.drawable.car);
But this image has different size on different screens, so i want to resize image according to screen size.
Of course you can scale:
Bitmap bm = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter);
Yes you can do this by using the following code snippets, the first section retrns the width and height of the current screen, the second section re-sizes the image.
// This will return the screen size in pixels
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
// Use this to bind and re-size the image
Bitmap car;
car = BitmapFactory.decodeResource(getResources(), R.drawable.car);
car = Bitmap.createScaledBitmap(car, width, height, true);
In android, one should store each resources in at least 3 formats, xhdpi, hdpi and mdpi
considering that you have stored a resource's copy in all the 3 formats at their respective folders.
res/drawable-mdpi/my_icon.png // bitmap for medium density
res/drawable-hdpi/my_icon.png // bitmap for high density
res/drawable-xhdpi/my_icon.png // bitmap for extra high density
The platform is enough smart to pick up right resources according to right screen size on its own.
I don't think you'll face any issues, if you have proper images in all 3 folders.
http://developer.android.com/guide/practices/screens_support.html
Look at http://square.github.io/picasso/ you can download, set where you want the image and resize it, just in one line. It also allows disk caching.

Bitmap width/height different after loading from Resource

1st. i'am new to Android coding :)
What I do is I load an Bitmap from my res/drawable-mdpi with
BitmapFactory.decodeResource(getResources(), R.drawable.cat_ground_01);
after I Log out the width/height of the Bitmap it tells me an other value then the Bitmap realy is.
That's kinda difficult to place Bitmaps pixelperfect e.g. overlap a bitmap of an face with an bitmap of mouth.
maybe I'am just missing some knowledge for this Topic :9
I hope you can help.
When you do Bitmapfactory.decodeResource(), Android by default will choose the "matched" dpi version to decode, what happen in your mentioned code will yields:
You can't specify whether it is in mdpi, hdpi or whatever, it will choose the version that match your running System. i.e., if you are running on a mdpi device, it will decode the mdpi version; in ldpi, then the ldpi version.
Suppose you are using a hdpi device, but no mdpi resource is defined, what it will do is take your mdpi resource, and during decode, it will make it into hdpi (i.e., it enlarge your mdpi bitmap to about 1.5x larger); again, if your device has lower resolution then it will shrink the image
I guess this is what happens to you. For BitmapFactory, it actually has the option to NOT scaling the image:
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
Set the inScaled to false will do.
Can't you just put the resource in the nodpi folder?
i.e. res/drawable-nodpi
This is what I've done in the past.
after I Log out the width/height of the Bitmap it tells me an other value then the Bitmap realy is.
Check your options.inTargetDensity and options.inDensity after loading the Bitmap from /drawable. They are not equal (160 and 240 for example). If options.inScaled set to true (default) - that's why the Bitmap is being automatically rescaled.
Another way is to use Bitmap.createScaledBitmap in order to rescale an image after loading. Because sometimes you need inScaled=true
//Target dimensions
int iW = 300;
int iH = 200;
Bitmap mOriginalBitmap = new Bitmap;
BitmapFactory.Options options = new BitmapFactory.Options();
//Load image from resource
mOriginalBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.sample_image_900px, options);
//Scale to target size
mOriginalBitmap = Bitmap.createScaledBitmap(mOriginalBitmap, iW, iH, true);

Android - scaling image after I did it programmatically

Strange thing happens - at least I don't get it.
I have an image (w: 120px, h: 63px) which is presented on one ImageButton. The only variant of that image that I have is placed in drawable-hdpi (and there is no other drawable directory).
Everything is ok with this image and every resolution (density), Android takes care of it very nicely.
But, trouble is when I take a photo from an album (some very big photo, for example), scale it to dimensions of a button (previously mentioned, w: 120px h: 63px) and set that small image as ImageButton background.
In case when I am testing on emulator with medium density (160) it is ok, but when testing on device with high density button gets resized since scaled image appears smaller.
Does anyone has an idea what is happening and why is size of image changed once more after I scaled it?
Any clue will be highly appreciated.
Here is the code I use for resizing the image:
public static void resizeAndSaveImage(String pathToResize, int newWidth, int newHeight, FileOutputStream output) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(pathToResize), null, options);
if (bitmap != null) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, output);
output.close();
}
} catch (Exception e) {
// ...
}
Dimension parameters are passed in as follows: newWidth = 120, newHeight = 63.
In order to achieve what you want you need to use density-independent pixels (dp) and not physical pixels. Reading from here:
The density-independent pixel is
equivalent to one physical pixel on a
160 dpi screen, the baseline density
assumed by the platform (as described
later in this document). At run time,
the platform transparently handles any
scaling of the dp units needed, based
on the actual density of the screen in
use. The conversion of dp units to
screen pixels is simple: pixels = dps
* (density / 160). For example, on 240 dpi screen, 1 dp would equal 1.5
physical pixels. Using dp units to
define your application's UI is highly
recommended, as a way of ensuring
proper display of your UI on different
screens.
In order to explain in more detail why this is happening with one image (album photo) and not with an image resource, you need to post the code you are using for the scaling.

Categories

Resources