안드로이드의 Bitmap은 기본적으로 16Bit(RGB_565)를 제공하며, 32Bit(ARGB_8888)을 지원한다.
그러나 이미지 처리를 위해서 Bitmap을 24Bit(RGB_888)로 변경을 해야하는 경우도 있다.
이런 경우는 기본 Android Platform에서는 지원을 하지 않고 있다. 변경이 필요하다면 소스코드에서 실제 데이터 값의 변경 작업으로 수행을 해야 하는 것 같다.
기본적으로 24Bit의 헤더 정보를 생성하야 하며, 16Bit 또는 32Bit Bitmap의 색상데이터 값에서 24Bit로 변경하는 작업을 수행해야 한다.
16Bit Bitmap : #RRRRRGGGGGGBBBBB (RGB_565)
24Bit Bitmap : #RRRRRRRRGGGGGGGGBBBBBBBB (RGB_888)
32Bit Bitmap : #AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB (ARGB_8888)
16Bit를 24Bit 또는 32Bit로의 변환은 헤더정보가 동일하며, Android에서 pixel 값을 int 형으로 추출하므로 동일하게 처리를 할 수 있다. 물론 int 형이 아닌 실 byte[]의 값으로 가져오는 것이 처리 속도면에서는 좋으나, byte에 대한 데이터형의 차이에 때문에 조금 번거로워 int 형의 픽셀값으로 처리를 한다.
* Bitmap 헤더 정보 생성
private static void encode24BMPHeader(DataOutputStream stream, int width,
int height, int fileSize) throws IOException {
// the magic number used to identify the BMP file: 0x42 0x4D
stream.writeByte(0x42);
stream.writeByte(0x4D);
stream.writeInt(swapEndian(fileSize));
// reserved
stream.writeInt(0);
// the offset, i.e. starting address of the bitmap data
stream.writeInt(swapEndian(14 + 40));
// INFORMATION HEADER (Windows V3 header) the size of this header (40 bytes)
stream.writeInt(swapEndian(40));
// the bitmap width in pixels (signed integer).
stream.writeInt(swapEndian(width));
// the bitmap height in pixels (signed integer).
stream.writeInt(swapEndian(height));
// the number of colour planes being used. Must be set to 1.
stream.writeShort(swapEndian((short) 1));
// the number of bits per pixel, which is the colour depth of the image.
stream.writeShort(swapEndian((short) 24));
// the compression method being used.
stream.writeInt(0);
// image size. The size of the raw bitmap data. 0 is valid for uncompressed.
stream.writeInt(0);
// the horizontal resolution of the image. (pixel per meter, signed integer)
stream.writeInt(0);
// the vertical resolution of the image. (pixel per meter, signed integer)
stream.writeInt(0);
// the number of colours in the colour palette, or 0 to default to 2n.
stream.writeInt(0);
// the number of important colours used, or 0 when every colour is important. generally ignored.
stream.writeInt(0);
}
실제 Bitmap의 헤더 정보에 대한 부분은 구글을 통해 손쉽게 알 수 있을 것이다.
* Bitmap 픽셀 값의 변경 (32/16Bit to 24Bit)
public static byte[] encodeBMPTo24(int[] rgb, int width, int height) throws IOException {
int padding = (4 - (width % 4)) % 4;
// the size of the BMP file in bytes
int fileSize = 14 + 40 + height * (padding + width * 3);
ByteArrayOutputStream bytes = new ByteArrayOutputStream(fileSize);
DataOutputStream out = new DataOutputStream(bytes);
// encode bitmap header (24bit over)
encode24BMPHeader(out, width, height, fileSize);
// PALETTE (none for 24 bit depth) IMAGE DATA starting in the bottom left, working right and then up
// a series of 3 bytes per pixel in the order B G R.
int colorValue = 0;
for (int j = height - 1; j >= 0; j--) {
for (int i = 0; i < width; i++) {
colorValue = rgb[i + width * j];
out.writeByte(colorValue & 0x000000FF);
out.writeByte((colorValue >>> 8) & 0x000000FF);
out.writeByte((colorValue >>> 16) & 0x000000FF);
}
// number of bytes in each row must be padded to multiple of 4
for (int i = 0; i < padding; i++) {
out.writeByte(0);
}
}
byte[] encodeBytes = bytes.toByteArray();
bytes.close();
// quick consistency check
if (encodeBytes.length != fileSize) {
throw new RuntimeException("bitmap file size bad match (32bit to 24bit)");
}
return encodeBytes;
}
위의 소스를 이용하여 실제 변경하는 소스는 아래와 같다.
public static byte[] decode24Bitmap(Bitmap bitmap) throws IOException {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] rgb = new int[width * height];
bitmap.getPixels(rgb, 0, width, 0, 0, width, height);
byte[] bitmap24Bytes = encodeBMPTo24(rgb, width, height);
bitmap.recycle();
bitmap = null;
rgb = null;
// system gc
//System.gc();
return bitmap24Bytes;
}