I try to load jpeg resource image to Bitmap of ARGB_8888 format:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap b = BitmapFactory.decodeResource(resources, resId, opts);
Log.d("test", b.getConfig().toString());
Here resId is the id of a jpeg image resource.
And the output is "RGB_565".
Tried this in emulators of android 2.2 and 2.3.
Docs of 'inPreferredConfig' say:
If this is non-null, the decoder will try to decode into this internal
configuration. If it is null, or the request cannot be met, the
decoder will try to pick the best matching config based on the
system's screen depth, and characteristics of the original image such
as if it has per-pixel alpha (requiring a config that also does).
Image are loaded with the ARGB_8888 config by default.
So am I hitting the case of "the request cannot be met"? :)
But I honestly can't see how it is very difficult to decode RGB_565 into an ARGB_8888.
So I thought maybe I am doing wrong or this is a bug in Android...
When loading jpeg you must set alpha channel to true:
bitmap.setHasAlpha(true);
After looking through the source I can only tell that the decision to follow the defined inPreferredConfig is done in native code. ref: source
I would assume because a jpg cannot have an alpha channel that it is decoding it RGB_565 because it is the most efficient config for a non-alpha image. If you really want it to be decoded to ARGB_8888 convert the image into a png.
Related
I need to compress an image to send it to my server. I am trying to do it this way:
private Bitmap compressImage(Bitmap bitmapImg){
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmapImg.compress(Bitmap.CompressFormat.JPEG, 50, out);
Bitmap compressed = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
return compressed;
}
But when I compare the Byte count of the original Bitmap object and the compressed one, I get the same number:
Log.e("UNCOMPRESSED", Integer.toString(mBitmapImg.getByteCount()));
E/UNCOMPRESSED: 23970816
Log.e("COMPRESSED", Integer.toString(compressedBitmapImg.getByteCount()));
E/COMPRESSED: 23970816
How can I fix this to have a smaller file?
But when I compare the Byte count of the original Bitmap object and the compressed one, I get the same number:
The size of a Bitmap in memory is based only on its resolution (width and height in pixels) and bit depth (the number of bytes per pixel, for controlling how many colors can be used per pixel).
How can I fix this to have a smaller file?
You do not have a file. You have a Bitmap object in memory. An image file is usually stored in a compressed form. In particular, this is true for JPEG, PNG, WebP, and GIF, the four major image formats used in Android. So, for example, out.toByteArray() will be smaller than 23,970,816 bytes.
Moreover, you are not sending a Bitmap to the server. You are sending an image to the server. You need to read the documentation for the server, or talk to the server developers, to determine what image format(s) they support and how to send the image to the server (ideally, something efficient like an HTTP PUT).
If you want to reduce the in-memory size of the Bitmap, scale it to a lower-resolution image (e.g., via createScaledBitmap()).
You can change your bitmap format to RGB_565 from ARGB_8888. That'll reduce your bitmap's memory footprint to half, but, would lead to loss of quality as well. Unfortunately, that's the most you can do with Bitmap.
Having said that, the compression method that you're using should work fine for most situations. It's also the advocated method for a number of platforms. An example for Firebase is this.
By following this link, I have written the following code to show a large image bitmap from sdcard.
try {
InputStream lStreamToImage = context.getContentResolver().openInputStream(Uri.parse(imagePath));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
options.inSampleSize = 8; //Decrease the size of decoded image
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(lStreamToImage, null, options);
} catch(Exception e){}
image.setImageBitmap(bitmap);
But it is not returning the bitmap(I mean it returns null). In logcat it is showing the below message repeatedly
08-02 17:21:04.389: D/skia(19359): --- SkImageDecoder::Factory returned null
If I will comment the options.inJustDecodeBounds line and rerun it, it works fine but slowly. The developer guide link I provided above says to use inJustDecodeBounds to load bitmaps efficiently.
Please tell me where I am doing wrong.
inJustDecodeBounds does not load bitmaps. That's the point of it. It loads the dimensions of the bitmap without loading the actual bitmap so you can do any pre-processing or checking on the bitmap before you actually load it. This is helpful is you, say, were having memory issues and you needed to check if loading a bitmap would crash you program.
The reason your bitmap might be loading slowly is because it's probably very large and SD cards are very slow.
EDIT:
From the documentation:
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
Edit 2:
Looking at your code with the example provided by Google, it looks like you are doing relatively the same thing. The reason it's returning null is possibly your InputStream has been modified in the first decoding and thus not starting at the beginning of the bitmap's memory address (they use a resource ID rather than InputStream.
From the code you supplied here, here's what I've figured. You are ALWAYS setting a sample size to 8 regardless of what the first decoding gives you. The reason Google decodes the first time is to figure out what the actual size of the bitmap is versus what they want. They determine that the bitmap is ZxZ dimensions and they want YxY dimensions, so they calculate the samplesize that they should use from the second decoding. You are not doing this. You are simply retrieving the dimensions of the bitmap and not using them. THEN, you set the sample size to a hard-coded 8, swapping it to a hard-coded ARGB_4444 bitmap, then decoding the full bitmap in to memory. In other words, these three lines are not being used:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
Setting inJustDecodeBounds merely gives you the bitmap's dimensions without putting the bitmap in to memory. It doesn't make it more efficient. It's meant to allow you to load bitmaps in a smaller memory space if they are too big because you can pre-decide what size it should be without decoding the whole thing).
The reason decoding the bitmap is slow might merely be a CPU thing. Depending on the size of your bitmap, you're loading the bitmap from an InputStream from the SDcard which is a slow operation in itself.
I'm making several calls to BitmapFactory.decodeFile() and BitmapFactory.decodeResource(), and I'd like to specify the format the bitmaps are decoded to, such as RGB_565 or RGBA_8888.
Currently, the decoded bitmap format seems to depend on the incoming image. Alternatively, is there a way to convert an existing bitmap to a specific format?
The reason this is important is that when I try to decode the image using jnigraphics, some images return an AndroidBitmapFormat of type ANDROID_BITMAP_FORMAT_NONE, which I assume is useless. Does anyone have more insight into why the format would be none of the known values? When this happens, the built-in image picker correctly displays images that are decoded this way, so I assume there has to be a way to deal with them.
Thanks for your input!
This might be what you are looking for:
BitmapFactory.Options op = new BitmapFactory.Options();
op.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap = BitmapFactory.decodeFile(path, op);
When you have bitmap you can call copy method on it specifying BitmapConfig which is basically what you want.
http://developer.android.com/reference/android/graphics/Bitmap.html#copy(android.graphics.Bitmap.Config,boolean)
I'm trying to read in a .jpg using BitmapFactory. I want to get a RGB888 formatted bitmap, but I keep seeming to get RGB565. Is there anyway to change this?
BitmapFactory methods let you pass a BitmapFactory.Options instance. You can use BitmapFactory.Options to specifcy the inPreferredConfig, which defines the format of the Bitmap returned after decoding, just set it to Bitmap.Config.ARGB_8888.
I have a single-channel PNG file I'd like to use as an alpha mask for Porter-Duff drawing operations. If I load it without any options, the resulting Bitmap has an RGB_565 config, i.e. treated as grayscale. If I set the preferred config to ALPHA_8, it loads it as a grayscale ARGB_8888 instead.
How can I convince Android to treat this file as an alpha mask instead of a grayscale image?
mask1 = BitmapFactory.decodeStream(pngStream);
// mask1.getConfig() is now RGB_565
BitmapFactory.Options maskOpts = new BitmapFactory.Options();
maskOpts.inPreferredConfig = Bitmap.Config.ALPHA_8;
mask2 = BitmapFactory.decodeStream(pngStream, null, maskOpts);
// mask2.getConfig() is now ARGB_8888 (the alpha channel is fully opaque)
More of a workaround than a solution:
I'm now including the alpha channel in an RGBA PNG file with the RGB channels all zeroes. I can load this file with a preferred config of ARGB_8888 and then extract its alpha channel. This wastes a few KB in the mask file, and a lot of memory while decoding the image.
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap source = BitmapFactory.decodeStream(pngStream, null, opts);
Bitmap mask = source.extractAlpha();
source.recycle();
// mask.getConfig() is now ALPHA_8