編輯:關於Android編程
最近的項目,做圖片的另存為功能,需要把圖片存成jpg,png,bmp。對於jpg和png來說相對簡單,android提供了bitmap.compress()方法可以馬上解決。但是對於BMP這種格式,沒有很好的支持。我花了幾天時間在網上找了很久,都沒有找到有用的答案,同樣也發了疑問,沒有合適的解答。
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://www.jb51.net/article/78186.htm
http://www.jb51.net/article/78187.htm
圖像文件頭
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。更正錯誤之後,重新運行一下,發現保存後的圖片顏色恢復正常,與原圖片顏色相同,問題解決。
總結:不知道為什麼經常遇到一些,網上很常見,但是卻找不到合適我的問題的解決辦法,很郁悶,網上很多的鏈接都是指向這個地址,但是進去了卻沒有更詳細的說明。很遺憾,也很懊惱。希望網上能多分享些有用的,可行的解決方案,這對大家都有好處,免得浪費大家的搜索時間。
這裡珍惜感謝csdn的那個博主,解決了我的問題。當然,使用別人的代碼,也要多思考,不要輕易完全相信。
runtime從入門到精通(二)—— 官方文檔翻譯
本文把runtime的官方文檔給大家翻譯過來,官方文檔的語言比較晦澀難懂,但是我們還是要在正式學習之前閱讀以下,有些名詞不懂不要緊哦,接著往下讀。前言OC是一種面向對象的
Android中ProgressDialog的用法
本文的代碼接著上一篇獲取聯系人信息寫的。在獲取聯系人信息的時候,我發現遍歷Cursor來獲取所有聯系人的信息比較慢,比如我手機上有大約不到四百人的聯系方式,全部遍歷一次大
Android底層開發之音頻輸入通道的軟硬件分析
Android底層開發之音頻輸入通道的軟硬件分析 我們都知道耳機Mic集成在一直的那種四段耳機Mic插頭是Android設備上比較常用。但是也會有分開的情
android 打造炫酷導航欄(仿UC頭條)
年後開始上班甚是清閒,所以想搗鼓一些東西。在翻閱大神傑作Android 教你打造炫酷的ViewPagerIndicator 不僅僅是高仿MIUI的時候看到下面有一條評論說