Issue
For my android app I am getting a ByteBuffer
from native code. It contains the pixel color values to create a bitmap.
Original image –
I used copyPixelsFromBuffer
on bitmap, but I am getting incorrect color on displaying the bitmap.
Here is the code for this approach –
Approach 1
ByteBuffer buffer = ...
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();
bitmap.copyPixelsFromBuffer(buffer);
Approx. time – ~0.4 ms
Result – Wrong colors –
Approach 2
Next I tried setPixels
. It still gives wrong colors and is more than 10 times slower and uses extra memory for int[]
. Please note that buffer.hasArray()
is false
, so I can’t get array from buffer.
ByteBuffer buffer = ...
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();
int[] pixels = new int[width * height];
for (int i = 0; i < width * height; i++) {
int a = buffer.get();
int r = buffer.get();
int g = buffer.get();
int b = buffer.get();
pixels[i] = a << 24 | r << 16 | g << 8 | b;
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
Approx. time – ~4.0 ms
Result – Wrong colors –
Approach 3
This time I used setPixels
but with the pixel values taken from IntBuffer
representation of ByteBuffer
. The colors are correct but the time is still high and there is extra memory allocation.
ByteBuffer buffer = ...
IntBuffer intBuffer = buffer.asIntBuffer();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
buffer.rewind();
int[] pixels = new int[width * height];
for (int i = 0; i < width * height; i++) {
pixels[i] = intBuffer.get();
}
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
Approx. time – ~3.0 ms
Result – Correct colors –
Any hints on why I am getting wrong colors with copyPixelsFromBuffer
? I want to use it instead of setPixels
as it is faster and does not require extra memory allocation.
Solution
I figured out the problem – even though the Bitmap.Config
is said to be ARGB_8888
, it really is RGBA
. I think it is a huge bug in android developer documentation and code.
The same issue has been noted in this question – Is Android's ARGB_8888 Bitmap internal format always RGBA?
And the ndk documentation correctly notes the format to be ANDROID_BITMAP_FORMAT_RGBA_8888
Solution is simple – create the buffer with RGBA format. Or on the java side switch the channels, something like below –
for (int i = 0; i < width * height; i++) {
Byte a = buffer.get();
Byte r = buffer.get();
Byte g = buffer.get();
Byte b = buffer.get();
bufferCopy.put(r);
bufferCopy.put(g);
bufferCopy.put(b);
bufferCopy.put(a);
}
This is not very efficient code, but gets the job done.
Answered By – Vinayak Garg
Answer Checked By – David Goodson (BugsFixing Volunteer)