Reputation: 437
I'm trying to load a byte array containing the data of a 24-bit bitmap file into a Bitmap object in Android but I'm having trouble since Bitmap only supports 32-bit and 16-bit formats. Here's what I came up with in order to convert the 24-bit data into 32-bit:
byte[] file = new byte[is.available()];
is.read(file);
is.close();
byte[] paddedFile = new byte[file.length + file.length / 3]; //we will be adding 1 more byte (alpha value) for each RGB triplet
for (int i = 0; i < file.length / 3; i++) {
paddedFile[i * 4] = file[i * 3];
paddedFile[i * 4 + 1] = file[i * 3 + 1];
paddedFile[i * 4 + 2] = file[i * 3 + 2];
paddedFile[i * 4 + 3] = -1; //manually added alpha channel
}
Bitmap screen = Bitmap.createBitmap(479, 616, Bitmap.Config.ARGB_8888); //exact dimensions of the 24-bit bitmap file
screen.copyPixelsFromBuffer(ByteBuffer.wrap(paddedFile));
iv.setImageBitmap(screen);
It kind of/almost works. Here's the bitmap I'm working with:
And here's what shows up after the code above:
Why is it distorted? Any leads on how to fix this problem is greatly appreciated.
Upvotes: 5
Views: 1635
Reputation: 27113
If I'm right about the padding, you should be able to do this:
int w = 479;
int h = 616;
byte[] file = /* as before */;
// Convert interleaved byte RGB to packed int ARGB
int[] paddedFile = new int[file.length / 3];
for (int i = 0; i < file.length / 3; i++) {
paddedFile[i] = 0xff << 24 // Alpha (all opaque)
| ((file[i * 3] & 0xff) << 16)
| ((file[i * 3 + 1] & 0xff) << 8)
| ((file[i * 3 + 2] & 0xff))
}
int stride = w + (w % 4 == 0 ? 0 : 4 - (w % 4));
Bitmap screen = Bitmap.createBitmap(paddedFile, 0, stride, w, h, Bitmap.Config.ARGB_8888);
Upvotes: 2
Reputation: 437
After haraldK's comment, I've landed on the Wikipedia page regarding the bitmap format where I've learned that a bitmap byte array is basically row * height. The amount of bytes a row contains is calculated via the following formula:
int paddedRow = (int) Math.floor((24 * w + 31) / 32) * 4; //1440 bytes
Considering this is a 24-bit (3 byte) bitmap, the actual amount of data per row is:
int actualRow = 3 * w; //1437 bytes
So the amount of padding per row is:
int padding = paddedRow - actualRow; //3 bytes
This means every last 3 bytes of rows of 1440 are padding (there are 616 of them). Knowing these values, we can extract the true color data from the byte array, then properly append the alpha channel afterwards:
byte[] file = new byte[is.available()];
is.read(file);
is.close();
int w = 479;
int h = 616;
int paddedRow = (int) Math.floor((24 * w + 31) / 32) * 4;
int actualRow = 3 * w;
int padding = paddedRow - actualRow;
byte[] removedPadding = new byte[file.length - padding * h];
for (int j = 0, k = 0; j < h; j++) {
int tmp = j * paddedRow;
for (int i = 0 + tmp; i < actualRow + tmp; i++, k++) {
removedPadding[k] = file[i];
}
}
byte[] rgbaFile = new byte[removedPadding.length + removedPadding.length / 3];
for (int i = 0; i < removedPadding.length / 3; i++) {
rgbaFile[i * 4] = removedPadding[i * 3];
rgbaFile[i * 4 + 1] = removedPadding[i * 3 + 1];
rgbaFile[i * 4 + 2] = removedPadding[i * 3 + 2];
rgbaFile[i * 4 + 3] = -1;
}
Bitmap screen = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
screen.copyPixelsFromBuffer(ByteBuffer.wrap(rgbaFile));
iv.setImageBitmap(screen);
Upvotes: 0