天天看點

Android中如何把bitmap存成BMP格式的圖檔

最近的項目,做圖檔的另存為功能,需要把圖檔存成jpg,png,bmp。對于jpg和png來說相對簡單,android提供了bitmap.compress()方法可以馬上解決。但是對于BMP這種格式,沒有很好的支援。我花了幾天時間在網上找了很久,都沒有找到有用的答案,同樣也發了疑問,沒有合适的解答。

不過,無意間發現了一篇blog , http://blog.csdn.net/zhaokaidong3/article/details/7776238,代碼如下:

package com.test.bitmap;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import android.app.Activity;

import android.graphics.Bitmap;

import android.os.Bundle;

import android.os.Environment;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.ImageView;

public class Mainactivity extends Activity {

ImageView img;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

Button btn = (Button) findViewById(R.id.sd);

img = (ImageView) findViewById(R.id.img1);

btn.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

View view = v.getRootView();

view.setDrawingCacheEnabled(true);

view.buildDrawingCache();

Bitmap bitmap = view.getDrawingCache();

if (bitmap != null) {

// ByteArrayOutputStream bos = new ByteArrayOutputStream();

// bitmap.compress(CompressFormat.PNG, 90, bos); 隻能轉成PNG、JPEG

// byte[] data = bos.toByteArray();

// img.setImageBitmap(BitmapFactory.decodeByteArray(data, 0,

// data.length));

int w = bitmap.getWidth(), h = bitmap.getHeight();

int[] pixels=new int[w*h];

bitmap.getPixels(pixels, 0, w, 0, 0, w, h);

//  ByteBuffer dst = ByteBuffer.allocate(bitmap.getRowBytes()

//  * h);

//  bitmap.copyPixelsToBuffer(dst);

//  IntBuffer dst=IntBuffer.allocate(w*h);

//  bitmap.copyPixelsToBuffer(dst);

byte[] rgb = addBMP_RGB_888(pixels,w,h);

byte[] header = addBMPImageHeader(rgb.length);

byte[] infos = addBMPImageInfosHeader(w, h);

byte[] buffer = new byte[54 + rgb.length];

System.arraycopy(header, 0, buffer, 0, header.length);

System.arraycopy(infos, 0, buffer, 14, infos.length);

System.arraycopy(rgb, 0, buffer, 54, rgb.length);

try {

FileOutputStream fos = new FileOutputStream(Environment

.getExternalStorageDirectory().getPath()

+ "/hello.bmp");

fos.write(buffer);

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

});

}

//BMP檔案頭

private byte[] addBMPImageHeader(int size) {

byte[] buffer = new byte[14];

buffer[0] = 0x42;

buffer[1] = 0x4D;

buffer[2] = (byte) (size >> 0);

buffer[3] = (byte) (size >> 8);

buffer[4] = (byte) (size >> 16);

buffer[5] = (byte) (size >> 24);

buffer[6] = 0x00;

buffer[7] = 0x00;

buffer[8] = 0x00;

buffer[9] = 0x00;

buffer[10] = 0x36;

buffer[11] = 0x00;

buffer[12] = 0x00;

buffer[13] = 0x00;

return buffer;

}

//BMP檔案資訊頭

private byte[] addBMPImageInfosHeader(int w, int h) {

byte[] buffer = new byte[40];

buffer[0] = 0x28;

buffer[1] = 0x00;

buffer[2] = 0x00;

buffer[3] = 0x00;

buffer[4] = (byte) (w >> 0);

buffer[5] = (byte) (w >> 8);

buffer[6] = (byte) (w >> 16);

buffer[7] = (byte) (w >> 24);

buffer[8] = (byte) (h >> 0);

buffer[9] = (byte) (h >> 8);

buffer[10] = (byte) (h >> 16);

buffer[11] = (byte) (h >> 24);

buffer[12] = 0x01;

buffer[13] = 0x00;

buffer[14] = 0x18;

buffer[15] = 0x00;

buffer[16] = 0x00;

buffer[17] = 0x00;

buffer[18] = 0x00;

buffer[19] = 0x00;

buffer[20] = 0x00;

buffer[21] = 0x00;

buffer[22] = 0x00;

buffer[23] = 0x00;

buffer[24] = (byte) 0xE0;

buffer[25] = 0x01;

buffer[26] = 0x00;

buffer[27] = 0x00;

buffer[28] = 0x02;

buffer[29] = 0x03;

buffer[30] = 0x00;

buffer[31] = 0x00;

buffer[32] = 0x00;

buffer[33] = 0x00;

buffer[34] = 0x00;

buffer[35] = 0x00;

buffer[36] = 0x00;

buffer[37] = 0x00;

buffer[38] = 0x00;

buffer[39] = 0x00;

return buffer;

}

private byte[] addBMP_RGB_888(int[] b,int w, int h) {

int len = b.length;

System.out.println(b.length);

byte[] buffer = new byte[w*h * 3];

int offset=0;

for (int i = len-1; i>=w; i-=w) {

//DIB檔案格式最後一行為第一行,每行按從左到右順序

int end=i,start=i-w+1;

for(int j=start;j<=end;j++){

buffer[offset]=(byte)(b[j]>>0);

buffer[offset+1]=(byte)(b[j]>>8);

buffer[offset+1]=(byte)(b[j]>>16); 

offset += 3;

}

}

return buffer;

}

}

但是我按照這種方法使用之後,儲存之後的圖檔與原來的相比會有很大的顔色差距,詳細看附件。

是以我就遇到了新的麻煩,下載下傳了一個UltraEdit軟體,然後把儲存前的bmp圖檔 和 儲存後的bmp圖檔 使用UltraEdit打開。分析bmp圖檔的格式。我從網上找到了一份非常好的說明,詳細中文版分析請看附件。提供兩個網址,關于bmp格式的分析的:http://blog.csdn.net/q673327335/article/details/8432384, 

http://blog.csdn.net/wen0006/article/details/6195311

圖像檔案頭

  1)1-2:(這裡的數字代表的是"字",即兩個位元組,下同)圖像檔案頭。0x4d42=’BM’,表示是Windows支援的BMP格式。(注意:查ascii表B 0x42,M0x4d,bfType 為兩個位元組,B為low位元組,M為high位元組是以bfType=0x4D42,而不是0x424D,但注意)

  2)3-6:整個檔案大小。4690 0000,為00009046h=36934。

  3)7-8:保留,必須設定為0。

  4)9-10:保留,必須設定為0。

  5)11-14:從檔案開始到位圖資料之間的偏移量(14+40+4*(2^biBitCount))。4600 0000,為00000046h=70,上面的檔案頭就是35字=70位元組。

位圖資訊頭

  6)15-18:位圖圖資訊頭長度。

  7) 19-22:位圖寬度,以像素為機關。8000 0000,為00000080h=128。

  8)23-26:位圖高度,以像素為機關。9000 0000,為00000090h=144。

  9)27-28:位圖的位面數,該值總是1。0100,為0001h=1。

  10)29-30:每個像素的位數。有1(單色),4(16色),8(256色),16(64K色,高彩色),24(16M色,真彩色),32(4096M色,增強型真彩色)。1000為0010h=16。

  11)31-34:壓縮說明:有0(不壓縮),1(RLE 8,8位RLE壓縮),2(RLE 4,4位RLE壓縮,3(Bitfields,位域存放)。RLE簡單地說是采用像素數+像素值的方式進行壓縮。T408采用的是位域存放方式,用兩個位元組表示一個像素,位域配置設定為r5b6g5。圖中0300 0000為00000003h=3。

  12)35-38:用位元組數表示的位圖資料的大小,該數必須是4的倍數,數值上等于(≥位圖寬度的最小的4的倍數)×位圖高度×每個像素位數。0090 0000為00009000h=80×90×2h=36864。

  13)39-42:用象素/米表示的水準分辨率。A00F 0000為0000 0FA0h=4000。

  14)43-46:用象素/米表示的垂直分辨率。A00F 0000為0000 0FA0h=4000。

  15)47-50:位圖使用的顔色索引數。設為0的話,則說明使用所有調色闆項。

  16)51-54:對圖象顯示有重要影響的顔色索引的數目。如果是0,表示都重要。

這54位資訊非常重要,是以我做了一個嘗試,驗證是否是因為bmp圖檔的頭資訊的不同,造成了顔色的偏差呢?實驗方法如下:其實很簡單,第一步把儲存前和儲存後的bmp圖檔使用UltraEdit打開,把儲存前的bmp圖檔的頭即前54位的值,複制,粘貼到儲存後的圖檔的頭部,這樣兩張圖的頭資訊都是儲存前的那張圖檔的頭。然後ctrl+s一下儲存後的那張圖檔,再打開發現圖檔并沒有變化。第一步,這回做相反的操作,即把儲存後的bmp圖檔的頭即前54位的值,複制,粘貼到儲存前的圖檔的頭部,發現也是沒有變化,說明檔案頭不是影響顔色差異的原因。

此時隻剩下  代碼中:  byte[] rgb = addBMP_RGB_888(pixels,w,h);此處的問題了,進入這個函數,仔細閱讀,發現正是存儲顔色資訊的地方。 讀下代碼發現,原作者的代碼有個bug。此處:  buffer[offset+1]=(byte)(b[j]>>16);  應該是buffer[offset+2]=(byte)(b[j]>>16);  即offset應該是+2,而非加1。更正錯誤之後,重新運作一下,發現儲存後的圖檔顔色恢複正常,與原圖檔顔色相同,問題解決。

總結:不知道為什麼經常遇到一些,網上很常見,但是卻找不到合适我的問題的解決辦法,很郁悶,網上很多的連結都是指向這個http://www.android123.com.cn/kaifafaq/703.html位址,但是進去了卻沒有更詳細的說明。很遺憾,也很懊惱。希望網上能多分享些有用的,可行的解決方案,這對大家都有好處,免得浪費大家的搜尋時間。

這裡珍惜感謝csdn的那個部落客,解決了我的問題。當然,使用别人的代碼,也要多思考,不要輕易完全相信。