Android resizes monochrome imageview creating grey pixels - android

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.

Related

Android still uses excessive memory when loading background image

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.

PNGs losing transparency in Android (if all its pixels are not transparent)

I have an app with two views - one on top of another. On the top one I use a Bitmap (ARGB_8888) loaded from a PNG resource and I play with its alpha channel to make some parts of it disappear so the one below becomes visible. All works fine if the source image has at least a single transparent pixel to start with. But if the source PNG has no transparent pixels then changing it alpha to 0 makes the pixel I changed black, not transparent.
Any ideas what could be done to fix it? anything like:
aaptOptions {
cruncherEnabled = false
}
but another option?
Currently I modify the source images before compiling by making a tiny area of it "semi-transparent" but would like to avoid that.
Ok. Finally got it.
I had to add one line. Instead of:
mBitmap = BitmapFactory.decodeResource(getResources(), getResourceID()).copy(Bitmap.Config.ARGB_8888, true);
I am now using:
mBitmap = BitmapFactory.decodeResource(getResources(), getResourceID()).copy(Bitmap.Config.ARGB_8888, true);
mBitmap.setHasAlpha(true);
and there is no need to add a transparent pixel on the source image!

Is this conceptually how Android resizes images within an ImageView?

I tried following some ImageView and BitmapDrawable source code to gain a better understanding of how the two classes operate on BitMaps. Is this generically how the process works?
I have an image which is a JPEG of size 500x500
I have ImageView A which is size 250x250, and ImageView B which is size 500x500.
Both have a BitmapDrawable which contains the JPEG
When its time to draw the Bitmap, A and B will both apply a matrix that will scale the bitmap appropriately to their respective canvas.
The JPEG itself will remain unscaled

drawBitmap where one pixel equals one pixel

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

Is there a way to load and draw partially a bitmap from file in Android?

Say I have a somewhat large (i.e. not fit in most phones' memory) bitmap on disk. I want to draw only parts of it on the screen in a way that isn't scaled (i.e. inSampleSize == 1)
Is there a way to load/draw just the part I want given a Rect specifying the area without loading the entire bitmap content?
I'm quite confident this is possible since you can load a really large bitmap file into an ImageView without problems so there must be some sort of a built-in way to handle large bitmaps... and after a few attempts, I've found a solution:
Instead of loading the entire bitmap and manually draw it yourself, load it as a Drawable instead:
InputStream mapInput = getResources().openRawResource(
R.drawable.transit_map);
_map = Drawable.createFromStream(mapInput, "transit_map");
_map.setBounds(0, 0, _mapDimension.width(), _mapDimension.height());
I'm using a resource file but since you can use Drawable.createFromStream to load image from any InputStream, it should works with arbitrary bitmap.
Then, use the Drawable.draw method to draw it onto the desired canvas like so:
int left = -(int) contentOffset.x;
int top = -(int) contentOffset.y;
int right = (int) (zoom * _mapDimension.width() - contentOffset.x);
int bottom = (int) (zoom * _mapDimension.height() - contentOffset.y);
_map.setBounds(left, top, right, bottom);
_map.draw(canvas);
As in the above case, You can also scale and translate the bitmap as well by manipulating the drawable's bounds and only the relevant parts of the bitmap will be loaded and drawn onto the Canvas.
The result is a pinch-zoomable view from just one single 200KB bitmap file. I've also tested this with a 22MB PNG file and it still works without any OutOfMemoryError including when screen orientation changes.
Now it's very relevant: BitmapRegionDecoder.
Note: available since Android SDK 10
It can easily be done by using RapidDecoder.
import rapid.decoder.BitmapDecoder;
Rect bounds = new Rect(10, 20, 30, 40);
Bitmap bitmap = BitmapDecoder.from("your-file.png")
.region(bounds)
.decode();
imageView.setImageBitmap(bitmap);
It supports down to Android 2.2 (API Level 8).
Generally speaking, that isn't possible, particularly since most image formats are compressed, so you don't even know which bytes to read until you've extracted the uncompressed form.
Break your image up into small tiles and load just the tiles you need to cover the region you want to display at runtime. To avoid jittery scrolling, you might also want to preload tiles that are just out of sight (the ones that border the visible tiles) on a background thread.

Categories

Resources