`
xiaozhouzhou
  • 浏览: 13943 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

简单的bmp文件打开与保存

 
阅读更多
简单的bmp文件打开与保存
          在谈bmp文件的打开、保存之前,我先说说在制作这一软件时的感受。在初次听到用输入输出流制作bmp格式文件的打开和保存时,有点不知所措,不知什么是bmp文件,他的格式是怎样的,我该如何制作一个程序去打开、保存这一文件。于是,就在网上找bmp文件格式的定义,让我感受特别深的是:你要用自己写的软件打开某一东西,必须知道他的协议是什么。你想要别人认同你制作的软件,你也必须定义一个完整规范的协议。协议是作为计算机之间,网络之间或者是软件、文件之间互通的一条通道规则。你不认同遵守协议你就寸步难行,你不制定规范协议,你自己制作的东西也无法得到认同。
下面就谈谈bmp格式的协议:

bmp文件有一个规范的格式:

他由四部分构成:
1、位图文件头(占14个字节):它包含BMP图像的文件类型,位图文件大小,文件头偏移量(偏移量是指从文件当前位置跳到指定位置的大小值)等。
2、位图信息头(占40个字节):包含位图宽高,位图大小,目标设备级别(必须为1),每个像素所需位数(下面用到的是24位),压缩类型等。
3、调色板:这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP文件)就不需要调色板。
4、位图数据:位图数据记录了位图的每个像素值,记录顺序的在扫描行内从左到右,从下到上。当位图像素所需位数为24位时,一个像素占三个字节,即R、G、B三个颜色值(0-255)。


对应的数据结构:
【BMP文件头(14个字节)】:
int bfType;//(0-1字节)位图文件的类型,为'B'、'M'两个字母
int bfSize;//(2-5字节)位图文件大小
int usignedshort bfReserved1;//(6-7字节)位图文件保留字,必须为0
int usignedshort bfReserved2;//(8-9字节)位图文件保留字,必须为0
int bfOffBits;//(10-13字节)文件头的偏移量


【位图信息头(40个字节)】:
int Size;//(14-17字节)本结构所占用的字节数
int image_width;//(18-21字节)位图的宽度,单位为像素
int image_height;//(22-25字节)位图的高度,单位为像素
int Planes;//(26-27字节)目标设备的级别,为1
int biBitCount;//(28-29字节)每个像素所需的位数,必须为1、4、8、24其中一个,分别代表双色、16色,256色和真彩色
int biCompression;//(30-33字节)位图压缩类型,为0
int SizeImage;//(34-37字节)位图大小
int biXPelsPerMeter;//(38-41字节)位图水平分辨率
int biYPelsPerMeter;//(42-45字节)位图垂直分辨率
int biClrUsed;//(46-49字节)位图实际使用的颜色表中的颜色数
int biClrImportant;//(50-53字节)位图显示过程中重要的颜色数

由于我们选择的是24位的BMP图片,所以今天不讨论颜色表的问题。

【位图数据】:
位图数据记录了位图的每一个像素值,记录顺序的在扫描行内从左到右,从下到上。位图的一个像素值所占用的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=8时,2个像素占1个字节;
当biBitCount=16时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;

【BMP文件读取步骤】:
1.利用DataInputStream流将文件中的信息按一定次序读入
2.获取文件中的重要信息
3.根据信息绘制图像

按一定次序:因为我们在文件中读入的是一个一个的字节,而实际操作需要的最少也是一个int(即四个字节),因此我们需要将byte数组转化成int型。
重要信息:BMP文件有很多信息,但是我们打开、保存该文件所需要的信息也就是那几个关键的数据。比如文件的宽高,文件的颜色数值。

【BMP文件读取的关键代码如下】:
				//输入流InputStream、DataInputStream的实例化
				InputStream in=new FileInputStream(f);
				dis=new DataInputStream(in);
				//读入文件头信息
				int len=14;
				byte[] b=new byte[len];
				dis.read(b);
				//读入位图信息头 
				int len1=40;
				byte[] b1=new byte[len1];
				dis.read(b1);
				//读取重要的数据宽高,调用位转换方法changeInt()
				w=changeInt(b1,7);
				h=changeInt(b1,11);
				//参数的直接传递
				bmp.w=w;  
				bmp.h=h;
				//创建数组用于保存rgb值
				imageR=new int[h][w];
				imageG=new int[h][w];
				imageB=new int[h][w];
				int skip_width=0;
				//计算补零的字节数
				int m=w%4;
				if(m!=0){
					skip_width=4-m;
				}
					/*
				 * 读取颜色数值的方法
				 */
				for(int i=h-1;i>=0;i--){
					for(int j=0;j<w;j++){
						Bmp bp=new Bmp();
						int blue=dis.read();
						int green=dis.read();
						int red=dis.read();
						imageB[i][j]=blue;
						imageG[i][j]=green;
						imageR[i][j]=red;
						//跳过补0字节
						if(j==w-1){
							byte bf3[]=new byte[skip_width];

							dis.read(bf3);
						}
                                            }
                                    }

其中调用的changeInt()方法定义如下:
/*
	 * 将字节转换为整形的方法
	 */
	private int changeInt(byte[] b1, int i) {
		return (((int)b1[i]&0xff)<<24)
				|(((int)b1[i-1]&0xff)<<16)
				|(((int)b1[i-2]&0xff)<<8)
				|(int)b1[i-3]&0xff;
	}

在解释之前,先给大家补一点关于Java的位运算符的知识
【Java位运算符】
按位与   &        两位同时为1,结果才为1,否则为0

            0000 1111 & 1111 0000 = 0000 0000

按位或    |   两位中至少一位为1,结果为1,否则为0

                0000 1111  |  1111 0000  =1111 1111

左移运算符    <<    将一个二进制运算单位的各二进制位全部左移若干位(左边二进制位丢弃,右边补0)
         
1111 0000 <<  2  =  1100 0000

右移运算符    >>    将一个二进制运算单位的各二进制位全部右移若干位(右边二进制位丢弃,左边补0)
          
0000 1111 >>  2  =  0000 0011
【疑问】:
w=changeInt(b1,7);

这句代码的意思是,将b1[7],b16],b1[5],b1[4]四个字节组合成一个Int型数。(((int)b1[i]&0xff)<<24):假如b1[7]中存的是【0000 1111】,现先将b1[7]与0xff进行与运算,然后强制转换为int形,再往前移24位。最后把b1[7],b16],b1[5],b1[4]进行或运算(如9|5=13),即为得到的宽。
int m=w%4;
if(m!=0){
     skip_width=4-m;
}

因系统在保存BMP格式文件时,为了效率,规定图片每行的字节数必须是4的倍数。也就是说,若不为四字节的整数倍时,我们要补上0。所以skip_width为4减去每行的字节数对4的余数。
--------------------------------------------------------------------

【BMP文件的保存关键代码】:
//得到当前窗体的左上顶点在电脑屏幕中的坐标值
			int x=bmp.getLocation().x;
			int y=bmp.getLocation().y;
			//System.out.println("x:"+x+"y:"+y);
			JFileChooser jfc=new JFileChooser();
			jfc.showSaveDialog(null);
			File f=jfc.getSelectedFile();
			//File f1=new File(f.getPath());
			try {
				//实例化输出流
				FileOutputStream out=new FileOutputStream(f);
				DataOutputStream dos=new DataOutputStream(out);
                                    //用于截取当前画板上的图画,并返回一张相应的位图
				BufferedImage bf_image=new Robot().createScreenCapture(new Rectangle(x+10,y+100,bmp.getWidth()-20,bmp.getHeight()-110));
				//得到图片的高度,宽度
				int image_w=bf_image.getWidth();
				int image_h=bf_image.getHeight();
				//文件的大小 
				int image_size=image_w*image_h*3;
				//判断文件的保存是否需要补0
				if(image_w%4!=0){
					image_size+=(image_w%4)*image_h*3;
				}
				//创建数组用于保存颜色RGB值
				byte[] rgbs=new byte[3];
				//创建数组用于补0
				byte[] b0=new byte[image_w%4];
				//实例化一个bmp文件头信息的写入类
				FileHead fh=new FileHead(image_size,54);
				//实例化一个bmp文件位图信息的写入类
				FileMid fm=new FileMid(image_w,image_h);
				//写入以上字节码
				dos.write(fh.data());
				dos.write(fm.getData());
				//获取当前像素的rgb值,并写入
				for(int h=image_h-1;h>=0;h--){
					for(int w=0;w<image_w;w++){
						int rgb=bf_image.getRGB(w, h);
						//将整形数据转换成字节保存,一个像素点为三个字节
						rgbs[0]=(byte)rgb;
						rgb=rgb>>8;
						rgbs[1]=(byte)rgb;
						rgb=rgb>>8;
						rgbs[2]=(byte)rgb;
						//写入rgb字节码
						dos.write(rgbs);
						//判断补零的方法
						if((image_w%4!=0)){
							dos.write(b0);
						}
					}
				}
				//清空并关闭输出流
				dos.flush();
				dos.close();
			} catch (Exception e1) {
				e1.printStackTrace();
			}

【疑问】:
rgbs[0]=(byte)rgb;
rgb=rgb>>8;
rgbs[1]=(byte)rgb;
rgb=rgb>>8;
rgbs[2]=(byte)rgb;

首先把rgb值强制转化为byte(强制转换后rgb值不变),赋给rgbs[0],然后将rgb右移8个位(将右八位抛弃,左边补0),强制转化为byte赋给rgbs[1],如此循环,就把rgb(int型)的值反序存在了rgb[0]、rgb[1]、rgb[2]三个字节中了。
【FileHead和FileMid类】:
public class FileHead {
	//存储头文件信息的数组,头文件大小为14个字节
	private byte[] hf=new byte[14];
	//文件的大小
	private int fsize;
	//头文件偏移量
	private int move;
	public FileHead(int fsize, int move){
		this.fsize=fsize;
		this.move=move;
	
	//头两个字节必写入BM,作为bmp文件的标识
	hf[0]='B';
	hf[1]='M';
	//文件大小的写入
	int value=fsize;
	hf[2]=(byte)value;
	value=value>>8;
	hf[3]=(byte)value;
	value=value>>8;
	hf[4]=(byte)value;
	value=value>>8;
	hf[5]=(byte)value;
	
	//头文件偏移量的写入
	value=move;
	hf[10]=(byte)value;
	value=value>>8;
	hf[11]=(byte)value;
	value=value>>8;
	hf[12]=(byte)value;
	value=value>>8;
	hf[13]=(byte)value;
	}
	//返回数组hf[]
	public byte[] data(){
		return hf;
	}
}

public class FileMid {
	//本结构所占用字节数
	int size=40;
	//目标设备的级别,必须为1
	private int level=1;
	//位图的宽度
	private int image_w;
	//位图的高度
	private int image_h;
	//每个像素所需的位数
	private int count=24;
	//位图压缩类型,这里不压缩为0
	private int compression=0;
	//位图的大小
	private int sizeimage;
	//创建数组
	private byte[] fm=new byte[40];
	public FileMid(int image_w, int image_h) {
		this.image_w=image_w;
		this.image_h=image_h;
		//保存字节占用数
		fm[0]=(byte)size;
		size=size>>8;
		fm[1]=(byte)size;
		size=size>>8;
		fm[2]=(byte)size;
		size=size>>8;
		fm[3]=(byte)size;
		
		//写入位图的宽
		int value=image_w;
		fm[4]=(byte)value;
		value=value>>8;
		fm[5]=(byte)value;
		value=value>>8;
		fm[6]=(byte)value;
		value=value>>8;
		fm[7]=(byte)value;
		
		//写入位图的高
		value=image_h;
		fm[8]=(byte)value;
		value=value>>8;
		fm[9]=(byte)value;
		value=value>>8;
		fm[10]=(byte)value;
		value=value>>8;
		fm[11]=(byte)value;
		
		//写入目标设备的级别
		fm[12]=(byte)level;
		level=level>>8;
		fm[13]=(byte)level;
		
		//写入每个像素所需的位数,24位
		fm[14]=(byte)count;
		count=count>>8;
		fm[15]=(byte)count;
		
		//写入压缩类型,这里为0
		fm[16]=(byte)compression;
		compression=compression>>8;
		fm[17]=(byte)compression;
		compression=compression>>8;
		fm[18]=(byte)compression;
		compression=compression>>8;
		fm[19]=(byte)compression;
		
		//得到位图大小,并写入
		sizeimage=image_w*image_h*3-54;
		if(image_w%4!=0){
			sizeimage+=(image_w%4)*image_h*3;
		}
		fm[20]=(byte)sizeimage;
		sizeimage=sizeimage>>8;
		fm[21]=(byte)sizeimage;
		sizeimage=sizeimage>>8;
		fm[22]=(byte)sizeimage;
		sizeimage=sizeimage>>8;
		fm[23]=(byte)sizeimage;
	}
	//返回数组fm[]
	public byte[] getData(){
		return fm;
	}

}
分享到:
评论

相关推荐

    bmp位图文件的读取与保存.rar_bmp_bmp save_c bmp_保存 bmp_文件保存bmp

    - 打开文件:使用C语言的`fopen`函数打开BMP文件,通常以二进制模式("rb")进行读取。 - 读取文件头:提取文件类型的标识(如'BM'),确保文件是BMP格式。然后读取文件大小和其他字段,确认文件完整。 - 读取...

    BMP文件的读取与保存

    首先,你需要打开文件并读取文件头以确认它是BMP文件,然后读取信息头以获取图像的尺寸和颜色深度。数据区的读取则根据颜色深度(8位灰度、24位RGB等)进行相应的解码。 4. **BMP保存** 保存BMP文件时,你需要创建...

    bmp位图文件的读取与保存

    读取BMP文件,我们需要打开文件,然后按照BMP文件的结构逐个读取这些部分。C++中可以使用`ifstream`类来实现文件的读取。例如: ```cpp ifstream file("image.bmp", ios::binary); ``` 接着,我们定义结构体来表示...

    BMP.zip_C++源码_C++读入bmp_打开保存BMP文件

    保存修改后的BMP文件则与读取过程相反: 1. 创建新的位图信息头:根据修改后的图像尺寸和颜色深度生成新的位图信息头。 2. 写文件头:将新的文件头写入输出文件。 3. 写位图信息头:接着写入修改后的位图信息头。 4...

    VC图像保存bmp文件

    在VC++编程环境中,将图像保存为BMP文件是一个常见的任务,这通常涉及到Windows图形设备接口(GDI)和GDI+的使用。BMP是一种位图文件格式,它存储了像素数据和图像的元信息,如宽度、高度、颜色深度等。下面,我们将...

    自制画板打开和保存BMP格式文件

    总的来说,自制画板打开和保存BMP格式文件涉及到的知识点包括:BMP文件格式的理解与解析,二进制文件操作,图像处理基础,GUI编程,以及事件驱动编程。这个项目既能够加深对图像文件格式的理解,也能锻炼实际编程...

    BMP文件的读取及保存

    在深入学习BMP文件的读取与保存时,我们需要了解以下几个关键知识点: 1. BMP文件结构:BMP文件分为文件头和图像数据两部分。文件头包括BITMAPFILEHEADER和BITMAPINFOHEADER,它们分别存储了文件的基本信息和图像的...

    vs2005 BMP文件读取保存

    接着,使用`ifstream`类打开BMP文件进行读取,通过`read`函数读取文件头和位图信息头的数据,解析出图像的尺寸和颜色深度。然后,读取像素数据到二维数组或自定义的图像类中。 读取BMP文件的代码示例: ```cpp ...

    BMP图片的打开,保存(MFC)

    这行代码会尝试打开指定路径的BMP文件,如果成功,图像数据将存储在`image`对象中。 4. **读取BMP图像数据** 读取BMP图像的数据包括获取图像的尺寸、颜色深度、以及访问像素值。可以通过`GetWidth()`和`GetHeight...

    vc++实现多文档预览dwg文件,并且可保存成bmp文件

    要将预览的图像保存为BMP文件,我们需要理解BMP文件格式,它是一个未经压缩的位图格式,包含图像的宽度、高度、色彩深度和像素数据。可以使用GDI+或者Windows API函数来创建BMP文件。首先,截取屏幕或MDI子窗口的...

    勾月画图程序(VB6.0源代码编写)可以打开一个*.bmp文件,也可以保存为另一个*.bmp文件,可以画出不同颜色和线条.

    - **保存文件**:同样地,程序可以将修改后的图像保存为新的BMP文件,这可能使用了`CreateObject`或`SavePicture`函数来完成文件写入。 4. **图形绘制**:VB6.0提供了Graphics对象,通过它可以实现对图像的绘制。...

    C++读取保存BMP图像

    首先,我们需要打开BMP文件并读取其头部信息,以获取图像的尺寸和色彩深度。然后,我们可以逐行读取像素数据,根据色彩深度转换成相应的颜色值。如果是灰度图转换,只需将红、绿、蓝三个分量取平均即可。 保存BMP...

    打开BMP文件带预览功能.zip_打开BMP_文件打开_预览

    总之,实现BMP文件的打开与预览功能,需要对图像文件格式有深入理解,熟悉至少一种编程语言的图像处理库,并且掌握基本的图形界面开发技巧。这样的功能对于图像处理软件、设计工具或是文件管理应用都是必不可少的,...

    bmp图像打开保存源代码

    在本文中,我们将深入探讨如何使用C++编程语言来实现BMP图像的打开、保存以及24位到8位色彩深度的自动转换。 BMP(Bitmap)是微软开发的一种位图图形文件格式,常用于Windows操作系统。C++是编程中常用的高级语言,...

    打开和保存bmp图片的小程序

    标题"打开和保存bmp图片的小程序"暗示我们要实现的功能是读取BMP文件的内容并将其显示出来,同时还能将用户编辑的图像保存为BMP格式。DIB句柄在此过程中起着至关重要的作用,因为它提供了与硬件无关的方式来表示图像...

    打开BMP文件C++代码

    8. **显示或保存图像**:读取并处理完BMP文件后,我们可以选择在控制台输出相关信息,或者将像素数据写入新的BMP文件,甚至可以利用图形库如OpenGL或SDL显示图像。 9. **异常处理**:在读取和处理文件时,应考虑到...

    BMP格式图像的读取与保存

    根据提供的文件信息,本文将详细解释BMP格式图像的读取与保存的相关知识点,包括BMP文件的基本结构、如何读取BMP文件以及如何保存BMP文件等内容。 ### BMP文件基本结构 BMP(Bitmap Image File Format)是一种常见...

    保存图像数据为bmp文件的实现函数

    本篇介绍了一个简单的C++函数,用于将内存中的RGB图像数据保存为BMP文件。该函数首先构建了BMP文件头和位图信息头,然后将这些数据写入新创建的文件中,最后关闭文件完成保存过程。理解BMP文件格式和掌握如何使用C/...

    隐藏ExE文件到bmp中

    5. 保存修改后的BMP文件:最后,将修改后的BMP文件保存,这样一个看似普通的图片文件就包含了隐藏的ExE文件。 标签中的“黑客”表明这种技术可能与网络安全相关,可能是为了隐藏恶意软件或者进行隐私保护。而"bmp...

    24为真彩色BMP文件的读和写

    在VC++环境下,开发人员经常需要处理BMP文件的读取和写入操作,以便在应用程序中显示或保存图像。以下是一些关于如何在VC++中进行这些操作的关键知识点: 1. **BMP文件结构**: - BMP文件由一个文件头(File ...

Global site tag (gtag.js) - Google Analytics