If I draw a bitmap on a view canvas using drawBitmap(), the images will be resampled so that 1 pixel in the image will be 1 dip on the screen. On a device I have with high pixel density, that means each image pixel is spread across 1.5 screen pixels, degrading the image. Handy in general, but in some cases I want to carefully select the images I want to draw, then draw them explicitly at their native size, so they won't degrade. How do I do this?
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inScaled = false;
Bitmap mBitmap = BitmapFactory.decodeResource(mResource, R.drawable.resource, opts);
alternatively you could store your resources inside a res/drawable-nodpi folder
Related
I have read multiple posts like this about memory usage of background image.
my background image is 2048x1365 59KB JPEG; its uncompressed bitmap is 11MB
the background on the view for the particular device would be 480x605, so usage would be 1.1MB (480x605x4)
my app originally uses 12MB without background image
placing the image in drawable-nodpi/ and set it in the layout XML cause the memory usage to 23MB; so exactly base + BMP size
Using BitmapFactory to decode the image (moved to raw/) according to the advice results in 33MB of memory usage. (See codes below.)
Codes to set the background
View view = findViewById(R.id.main_content);
Rect rect = new Rect();
view.getLocalVisibleRect(rect);
BitmapFactory.Options options = new BitmapFactory.Options();
options.outHeight = rect.height();
options.outWidth = rect.width();
options.inScaled = false;
Bitmap backgroundBitmap = BitmapFactory.decodeResource(getResources(), backgroundId, options);
view.setBackgroundDrawable(new BitmapDrawable(getResources(), backgroundBitmap));
What goes wrong? What else can I do to shrink the memory usage?
The trick to getting BitmapFactory to give you a low-memory image is to fill in inSampleSize on the BitmapFactory.Options. This tells BitmapFactory to downsample the image as it loads, giving you a lower-resolution image, but one that is better tuned to whatever use you plan to put it to. You would need to calculate the desired inSampleSize that you want, based on the resolution of the ImageView (or whatever) that you are using the image for.
This sample app demonstrates loading some images out of assets/ with different inSampleSize values.
I have experienced this too but with much smaller images. I found out of that this was happening because I was using the same image size for all screen resolutions. I recommend you have different sizes of the same image and put them in the appropriate folders.
I have an image which is monochrome, meaning only white and black pixels. I have made several versions of the image in order to cover all dpi folders. I am using a monochrome image since I want to apply the floodfill algorithm.
The problem is that in some devices, android uses resized versions of the images and while scaling there are some grey pixels. In order to deal with this I tried correcting the pixels and converting the grey to either white or black, but this takes significant time.
Is it possible to force android to generate monochrome images while resizing the imageview or to apply a quick filter to regenerate the monochrome image?
While I haven't tried it, should I generate different versions of the images in the nodpi folder and use them without scaling, perhaps with center crop?
You can load the Drawable as Bitmap.
final BitmapFactory.Options options = new BitmapFactory.Options();
// Load the bitmap as mutable object
options.inMutable = true;
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.my_image, options);
Then you can manipulate it with your algorithm and finally you will set the result to your ImageView
imageView.setImageBitmap(bitmap);
This way you should not get any grey pixels caused by Android Scaling.
For more options:
See here,
And here.
I have a PNG image file with 2236x971px dimensions as a resource.
I want to scale it down by a factor of two (to its half). However, when i use this code:
BitmapFactory.Options bo = new BitmapFactory.Options();
bo.inSampleSize = 2;
Bitmap decodedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, bo);
the decodedBitmap.getWidth() and decodedBitmap.getHeight() show width: 1677, height:728 → Only 25% reduction in size instead of expected 50%. Why is that so?
I am running the code on API 19.
The reason is that, your resource gets loaded according to your screen metrics. Put your image in the drawable-nodpi Folder or open an input stream to your resource and decode that input stream.
I'm making a game that is Pixel-based in Android. I have several 8x8 sprites that need to be resized to fit on a 100x100 area. As a test to see if it would work, I tried to just make the image fill the entire canvas. It kind of worked, but it made the 8x8 sprite turn into a 12x12 sprite, making the pixels look really odd and distorted. Here's what I have so far:
Bitmap grass = BitmapFactory.decodeResource(getResources(), R.drawable.small);
Paint configuration = new Paint();
configuration.setDither(false);
configuration.setAntiAlias(false);
Matrix myMatrix = new Matrix();
myMatrix.postScale(canvas.getWidth() / grass.getWidth(), canvas.getWidth() / grass.getHeight());
Bitmap resizedBitmap = Bitmap.createBitmap(grass, 0, 0, grass.getWidth(), grass.getHeight(), myMatrix, false);
canvas.drawBitmap(resizedBitmap, 0, 0, null);
If you work on bitmaps then you simply can't. You'd minimize the distortion by scaling up by non-fractional factor so each pixed would be repeated same number of times (i.e. image 10x10 to 20x20 is scaled by factor of two, but 8x8 to 12x12 is 1,5 so no luck). The solution would be to have all assets in vector form (i.e. SVG) and then render them on run-time according to device density and other specs or prepare separate assets for various type of devices (which would blow application size up a bit)
I just can't figure out how to draw on a bitmap (with the help of a canvas) so that the result won't be device density dependent.
Here's the code that makes the drawing:
ImageView iv = (ImageView)findViewById(R.id.container);
Bitmap result = BitmapFactory.decodeResource(getResources(), R.drawable.big_picture).copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(result);
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.small_picture), actualX, actualY, null);
iv.setImageBitmap(result);
I load a big_picture in the Bitmap result and i want to draw a small_picture on that but at a specified position. If i set actualX and actualY it's ok on a device with the same density. However devices with different densities "scale" the canvas.
The interesting part is that only the small_picture is got "scaled" and goes out of the screen but the pig_picture behind it just fits the screen well on any density screen.
Why is that? How can i draw the small_picture on the big_picture densitiy independently?
Thank you!
I've found out what's the case after debugging the Bitmap. Unless you're putting images to drawable-nodpi the image will be re-sized to match the nominal device density. (Which are 120, 160, 240, 320) If you "load" an image to a Bitmap and Canvas they will have one of these densities regardless of the original image's.
So one solution is to put the images to drawable-nodpi. After that they will behave the same on all densities.
Another solution is to multiply the coordinates based on the ratios of the densities above.
...or you can just make custom images and coordinates for all qualifiers.