Android中把bitmap存成BMP格式圖片的方法
最近的項(xiàng)目,做圖片的另存為功能,需要把圖片存成jpg,png,bmp。對(duì)于jpg和png來說相對(duì)簡單,android提供了bitmap.compress()方法可以馬上解決。但是對(duì)于BMP這種格式,沒有很好的支持。我花了幾天時(shí)間在網(wǎng)上找了很久,都沒有找到有用的答案,同樣也發(fā)了疑問,沒有合適的解答。
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); 只能轉(zhuǎn)成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; } }
但是我按照這種方法使用之后,保存之后的圖片與原來的相比會(huì)有很大的顏色差距,詳細(xì)看附件。
所以我就遇到了新的麻煩,下載了一個(gè)UltraEdit軟件,然后把保存前的bmp圖片 和 保存后的bmp圖片 使用UltraEdit打開。分析bmp圖片的格式。我從網(wǎng)上找到了一份非常好的說明,詳細(xì)中文版分析請(qǐng)看附件。提供兩個(gè)網(wǎng)址,關(guān)于bmp格式的分析的:
http://chabaoo.cn/article/78186.htm
http://chabaoo.cn/article/78187.htm
圖像文件頭
1)1-2:(這里的數(shù)字代表的是"字",即兩個(gè)字節(jié),下同)圖像文件頭。0x4d42='BM',表示是Windows支持的BMP格式。(注意:查ascii表B 0x42,M0x4d,bfType 為兩個(gè)字節(jié),B為low字節(jié),M為high字節(jié)所以bfType=0x4D42,而不是0x424D,但注意)
2)3-6:整個(gè)文件大小。4690 0000,為00009046h=36934。
3)7-8:保留,必須設(shè)置為0。
4)9-10:保留,必須設(shè)置為0。
5)11-14:從文件開始到位圖數(shù)據(jù)之間的偏移量(14+40+4*(2^biBitCount))。4600 0000,為00000046h=70,上面的文件頭就是35字=70字節(jié)。
位圖信息頭
6)15-18:位圖圖信息頭長度。
7) 19-22:位圖寬度,以像素為單位。8000 0000,為00000080h=128。
8)23-26:位圖高度,以像素為單位。9000 0000,為00000090h=144。
9)27-28:位圖的位面數(shù),該值總是1。0100,為0001h=1。
10)29-30:每個(gè)像素的位數(shù)。有1(單色),4(16色),8(256色),16(64K色,高彩色),24(16M色,真彩色),32(4096M色,增強(qiáng)型真彩色)。1000為0010h=16。
11)31-34:壓縮說明:有0(不壓縮),1(RLE 8,8位RLE壓縮),2(RLE 4,4位RLE壓縮,3(Bitfields,位域存放)。RLE簡單地說是采用像素?cái)?shù)+像素值的方式進(jìn)行壓縮。T408采用的是位域存放方式,用兩個(gè)字節(jié)表示一個(gè)像素,位域分配為r5b6g5。圖中0300 0000為00000003h=3。
12)35-38:用字節(jié)數(shù)表示的位圖數(shù)據(jù)的大小,該數(shù)必須是4的倍數(shù),數(shù)值上等于(≥位圖寬度的最小的4的倍數(shù))×位圖高度×每個(gè)像素位數(shù)。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:位圖使用的顏色索引數(shù)。設(shè)為0的話,則說明使用所有調(diào)色板項(xiàng)。
16)51-54:對(duì)圖象顯示有重要影響的顏色索引的數(shù)目。如果是0,表示都重要。
這54位信息非常重要,所以我做了一個(gè)嘗試,驗(yàn)證是否是因?yàn)閎mp圖片的頭信息的不同,造成了顏色的偏差呢?實(shí)驗(yàn)方法如下:其實(shí)很簡單,第一步把保存前和保存后的bmp圖片使用UltraEdit打開,把保存前的bmp圖片的頭即前54位的值,復(fù)制,粘貼到保存后的圖片的頭部,這樣兩張圖的頭信息都是保存前的那張圖片的頭。然后ctrl+s一下保存后的那張圖片,再打開發(fā)現(xiàn)圖片并沒有變化。第一步,這回做相反的操作,即把保存后的bmp圖片的頭即前54位的值,復(fù)制,粘貼到保存前的圖片的頭部,發(fā)現(xiàn)也是沒有變化,說明文件頭不是影響顏色差異的原因。
此時(shí)只剩下 代碼中: byte[] rgb = addBMP_RGB_888(pixels,w,h);此處的問題了,進(jìn)入這個(gè)函數(shù),仔細(xì)閱讀,發(fā)現(xiàn)正是存儲(chǔ)顏色信息的地方。 讀下代碼發(fā)現(xiàn),原作者的代碼有個(gè)bug。此處: buffer[offset+1]=(byte)(b[j]>>16); 應(yīng)該是buffer[offset+2]=(byte)(b[j]>>16); 即offset應(yīng)該是+2,而非加1。更正錯(cuò)誤之后,重新運(yùn)行一下,發(fā)現(xiàn)保存后的圖片顏色恢復(fù)正常,與原圖片顏色相同,問題解決。
總結(jié):不知道為什么經(jīng)常遇到一些,網(wǎng)上很常見,但是卻找不到合適我的問題的解決辦法,很郁悶,網(wǎng)上很多的鏈接都是指向這個(gè)地址,但是進(jìn)去了卻沒有更詳細(xì)的說明。很遺憾,也很懊惱。希望網(wǎng)上能多分享些有用的,可行的解決方案,這對(duì)大家都有好處,免得浪費(fèi)大家的搜索時(shí)間。
這里珍惜感謝csdn的那個(gè)博主,解決了我的問題。當(dāng)然,使用別人的代碼,也要多思考,不要輕易完全相信。
- Android canvas drawBitmap方法詳解及實(shí)例
- android BitmapFactory.Options使用方法詳解
- Android讀取本地或網(wǎng)絡(luò)圖片并轉(zhuǎn)換為Bitmap
- Android App開發(fā)中將View或Drawable轉(zhuǎn)為Bitmap的方法
- Android中使用Bitmap類將矩形圖片轉(zhuǎn)為圓形的方法
- Android利用BitMap獲得圖片像素?cái)?shù)據(jù)的方法
- 解決Android 沉浸式狀態(tài)欄和華為虛擬按鍵沖突問題
- Android 沉浸式狀態(tài)欄與隱藏導(dǎo)航欄實(shí)例詳解
- Android Bitmap的截取及狀態(tài)欄的隱藏和顯示功能
相關(guān)文章
詳解Flutter手游操縱桿移動(dòng)的原理與實(shí)現(xiàn)
這篇文章將為大家詳細(xì)介紹一下Android?Flutter手游中操縱桿移動(dòng)角色的原理與實(shí)現(xiàn)過程,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-07-07Android源碼學(xué)習(xí)之觀察者模式應(yīng)用及優(yōu)點(diǎn)介紹
定義對(duì)象間一種一對(duì)多的依賴關(guān)系,使得當(dāng)一個(gè)對(duì)象改變狀態(tài),則所有依賴于它的對(duì)象都會(huì)得到通知并被自動(dòng)更新等等,需要了解的朋友可以參考下2013-01-01Android中的深度鏈接技術(shù)實(shí)戰(zhàn)
本文主要介紹了Android中的深度鏈接技術(shù)實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Thread、Handler和HandlerThread關(guān)系詳解
這篇文章主要介紹了Thread、Handler和HandlerThread關(guān)系詳解的相關(guān)資料,需要的朋友可以參考下2016-09-09Android開發(fā)RecyclerView單獨(dú)刷新使用技巧
本篇文章主要是分享下RecyclerView中子item如何單獨(dú)刷新以及子item的某一部分內(nèi)容如何實(shí)現(xiàn)單獨(dú)刷新,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Android編程實(shí)現(xiàn)在自定義對(duì)話框中獲取EditText中數(shù)據(jù)的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)在自定義對(duì)話框中獲取EditText中數(shù)據(jù)的方法,結(jié)合實(shí)例形式分析了Android對(duì)話框數(shù)據(jù)傳遞相關(guān)操作技巧,需要的朋友可以參考下2018-01-01