Android中把bitmap存成BMP格式图片的方法

时间:2021-05-20

最近的项目,做图片的另存为功能,需要把图片存成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;@Overridepublic 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() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubView 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 blocke.printStackTrace();}catch (IOException e) {// TODO Auto-generated catch blocke.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格式的分析的:


图像文件头
  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的那个博主,解决了我的问题。当然,使用别人的代码,也要多思考,不要轻易完全相信。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章