I'm trying to access the raw pixel data of an image in Android.
The code looks something like this:
Bitmap bitmap = BitmapFactory.decodeFile("image.png");
// assert valid input
if ((bitmap.getConfig() == null) || bitmap.getConfig() == Config.ARGB_8888)
throw new Exception("bad config");
ByteBuffer buffer = ByteBuffer.allocate(4 * bitmap.getWidth() * bitmap.getHeight());
bitmap.copyPixelsToBuffer(buffer);
return buffer.array();
How are the pixels in the linear 1D buffer.array() stored?
To get the offset into buffer.array() for a given pixel x,y in an image of size widthxheight with bytesPerPixel bytes per pixel, use this formula:
offsetForPixel = (y * width + x) * bytesPerPixel
In other words, the first element in the array is the top-left pixel, and the following elements are row-major. All data for a pixel are stored in adjacent bytes and are not spread out based on channel. This is the answer to 1, 2 and 4 above. Now let's discuss 3, which is where things become complicated.
What you get with Bitmap.copyPixelsToBuffer() is the raw bitmap data representation used by Android's low-level drawing library skia. This has three significant consequences:
The last point makes it awkward to use Bitmap.copyPixelsToBuffer() at all if you want to inspect individual pixels, because you simply can't know how skia has been configured to pack channels. As an experiment, try this code:
int inputPixel = 0x336699cc;
int[] pixels = new int[] { inputPixel };
Bitmap bm = Bitmap.createBitmap(pixels, 1, 1, Config.ARGB_8888);
ByteBuffer bb = ByteBuffer.allocate(4);
bm.copyPixelsToBuffer(bb);
Log.i("TAG", "inputPixel = 0x" + Integer.toHexString(inputPixel));
for (int i = 0; i < 4; i++) {
String byteString = "0x" + Integer.toHexString(bb.array()[i] & 0xff);
Log.i("TAG", "outputPixel byte " + i + " = " + byteString);
}
When I run that, I get this output:
I/TAG ( 1995): inputPixel = 0x336699cc
I/TAG ( 1995): outputPixel byte 0 = 0x14
I/TAG ( 1995): outputPixel byte 1 = 0x1f
I/TAG ( 1995): outputPixel byte 2 = 0x29
I/TAG ( 1995): outputPixel byte 3 = 0x33
We can see that we are dealing with big endian, that the in-memory representation is pre-multiplied and that channels have been re-arranged from ARGB to RGBA (motivated in the skia source code by that that is the same in-memory representation as OpenGL).
If you want to read the pixel data, I suggest you use Bitmap.getPixels() instead. There is some copying involved, but at least the API specifies how the returned data is formated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With