`
剑晨java
  • 浏览: 23549 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

BMP读取及保存

阅读更多
在之前,我们要先了解BMP存储的格式,专业点也就是协议。
(1)BMP文件头(14个字节):
int bfType;//位图文件的类型,为'B'、'M'两个字母, (0-1字节)
int bfSize;//位图文件大小, (2-5字节)
int usignedshort bfReserved1;//位图文件保留字,必须为0 ,(6-7字节)
int usignedshort bfReserved2;//位图文件保留字,必须为0 ,(8-9字节)
int bfOffBits;//文件头的偏移量 ,(10-13字节)
(2)位图信息头(40个字节):
int Size;//本结构所占用的字节数 ,(14-17字节)
int image_width;//位图的宽度,单位为像素,(18-21字节)
int image_height;//位图的高度,单位为像素 ,(22-25字节)
int Planes;//目标设备的级别,为1 ,(26-27字节)
int biBitCount;//每个像素所需的位数,必须为1、4、8、24其中一个,分别代表双色、16色,256色和真彩色 ,(28-29字节)
int biCompression;//位图压缩类型,为0 ,(30-33字节)
int SizeImage;//位图大小 ,(34-37字节)
int biXPelsPerMeter;//位图水平分辨率 ,(38-41字节)
int biYPelsPerMeter;//位图垂直分辨率 ,(42-45字节)
int biClrUsed;//位图实际使用的颜色表中的颜色数 ,(46-49字节)
int biClrImportant;//位图显示过程中重要的颜色数 ,(50-53字节)
(3)颜色表:
class RGBQUAD{
   byte rgbBlue;//蓝色的亮度(值范围0~255)
    byte rgbGreen;//绿色的亮度(值范围0~255)
    byte rgbRed;//红色的亮度(值范围0~255)
    byte rgbReserved;//保留,必须为0
}
颜色表中RGBQUAD结构数据个数由biBitCount来确定,当biBitCount=1,4,8时,分别有2,16,256个表项,当biBitCount=24时,没有颜色表。
(4)位图数据:
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内从左到右,扫描行之间是从下到上,位图的一个像素值所占用的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=8时,2个像素占1个字节;
当biBitCount=16时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0补充,补在每行尾部。
知道协议后,就容易了,只不过用输入流把每个数据都读出来,然后按照协议可以得到各个数据,知道各个数据的意义后,就可以画出图片。我所做的24位BMP读取只需要图片的长度,宽度和各个点RGB即可。代码如下:
/**
	 * 读取方法
	 * @param file:文件
	 * @throws Exception:异常
	 */
	public void readBMP(File file) throws Exception{
		Graphics g=frame.getGraphics();
		FileInputStream ins=new FileInputStream(file);//输入流
		dins = new DataInputStream(ins);//包装为数据输入流
		/**BMP文件头*/
		int bf1len=14;
		byte bf1[]=new byte[bf1len];
		dins.read(bf1);
		/**位图信息头*/
		int bf2len=40;
		byte bf2[]=new byte[bf2len];
		dins.read(bf2);
		int image_width=changeInt(bf2,7);//位图的宽度
		int image_heith=changeInt(bf2,11);//位图的高度
		/**得到补0长度*/
		if(!(image_width*3%4==0)){
			skip_width=4-image_width*3%4;
		}
		/**读入RGB*/
		//分别用三个二维数组存储RGB
		int [][]imageB=new int[image_heith][image_width];
		int [][]imageG=new int[image_heith][image_width];
		int [][]imageR=new int[image_heith][image_width];
		for(int h=image_heith-1;h>=0;h--){
			for(int w=0;w<image_width;w++){
				//分别按照协议顺序读出字节
				int blue=dins.read();
				int green=dins.read();
				int red=dins.read();
				//由于颜色值范围超出byte正直范围,可能存储为负值,进行位运算
				int blue_temp = (blue & 0xff);
				int green_temp = (green & 0xff);
				int red_temp = (red & 0xff);
				imageB[h][w]=blue_temp;
				imageG[h][w]=green_temp;
				imageR[h][w]=red_temp;
				/**补0跳过*/
				if(w==image_width-1){
					byte bf3[]=new byte[skip_width];
					dins.read(bf3);//读出0项字节,不使用
				}
			}
		}
		/**画出图片*/
		for(int h=0;h<image_heith;h++){
			for(int w=0;w<image_width;w++){
				g.setColor(new Color(imageR[h][w],imageG[h][w],imageB[h][w]));
				g.fillRect(w+(frame.getWidth()-image_width)/2, h+(frame.getHeight()-image_heith)/2, 1, 1);
			}
		}
		
	}
	/**
	 * 位运算方法
	 * @param bi:数组
	 * @param start:开始位置
	 * @return:整形数据
	 */
	public int changeInt(byte[] bi,int start){
		return(((int)bi[start]&0xff<<24)|(((int)bi[start-1]&0xff)<<16)|(((int)bi[start-2]&0xff)<<8)|((int)bi[start-3]&0xff));
	
	}

这里想说的是由于输入流只能一个一个字节的读入,也就是就算数据你并不需要,也要把它读出来才能接着往下读。另外这里做的是24位的读取,无法读取其他位的BMP图片。
至于保存,只要按照协议一个字节一个字节的写入,按照电脑的协议写入,电脑才看的懂,读的出来。代码如下:
/**
	 * 保存BMP方法
	 * @param file:保存目标文件
	 * @throws Exception:异常
	 */
	public void saveBMP(File file) throws Exception{
		FileOutputStream ous=new FileOutputStream(file);//输出流
		dous = new DataOutputStream(ous);//包装为数据输出流
		/**写入BMP文件头*/
		byte []bfType={'B','M'};//位图文件类型,0-1字节
		dous.write(bfType);
		byte []bfSize=changeByte(580*490*3+54);//位图文件大小,2-5字节
		dous.write(bfSize);
		byte[]bfReserved1={0,0};//位图文件保留字,必须为0,6-7字节
		dous.write(bfReserved1);
		byte[]bfReserved2={0,0};//位图文件保留字,必须为0,8-9字节
		dous.write(bfReserved2);
		byte [] bfOffBits={54,0,0,0};//位图数据起始位置,以相对于位图,10-13字节
		dous.write(bfOffBits);
		/**写入位图信息头*/
		byte []size={40,0,0,0};//本结构所占字节数,14-17字节
		dous.write(size);
		byte[]image_width=changeByte(580);//位图的宽度,以像素为单位,18-21字节
		dous.write(image_width);
		byte[]image_heigh=changeByte(490);//位图的高度,以像素为单位,22-25字节
		dous.write(image_heigh);
		byte []planes={1,0};//目标设备的级别,必须为1,26-27字节
		dous.write(planes);
		byte[]biBitCount={24,0};//每个像素所需的位数,必须是1(真彩色)或4(16色)或8(256色)或24(真彩色),28-29字节
		dous.write(biBitCount);
		int biCompressin=0;//位图压缩类型,必须是0(不压缩)或1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型),30-34字节
		dous.writeInt(biCompressin);
		byte[] sizeImage=changeByte(580*490*3);//位图的大小,以字节为单位,34-37字节
		dous.write(sizeImage);
		int biXPelsPerMeter=0;//位图水平分辨率,每米像素数,38-41字节
		dous.writeInt(biXPelsPerMeter);
		int biYPelsPerMeter=0;//位图垂直分辨率,每米像素数,42-45字节
		dous.writeInt(biYPelsPerMeter);
		int biClrUsed=0;//位图实际使用的颜色表的颜色数,46-49字节
		dous.writeInt(biClrUsed);
		int biClrImportant=0;//位图显示过程中重要的颜色表,50-53字节
		dous.writeInt(biClrImportant);
		/**颜色表*/
		//用BufferedImage对象可以得到每个点的RGB,截取屏幕固定大小矩形,在窗体内部截取,这里的x,y为窗体此刻的坐标,其中580,490为截取的矩形大小,取宽度580完全是我的懒惰,定长不用去考虑补0
		BufferedImage image=new Robot().createScreenCapture(new Rectangle(x+10,y+100,580,490));
		for(int h=image.getHeight()-1;h>=0;h--){
			for(int w=0;w<image.getWidth();w++){
//分别得到截取的矩形内的每个点的RGB,按照协议顺序存储
				int rgb=image.getRGB(w, h);
				byte rgbs[]=new byte[3];
				rgbs[0]=(byte)rgb;
				rgb=rgb>>8;
				rgbs[1]=(byte)rgb;
				rgb=rgb>>8;
				rgbs[2]=(byte)rgb;
				dous.write(rgbs);
			}
		}
		
	}
	
	/**
	 * 将int转化为byte数组的方法、
	 * @param int:将转化的整数
	 * @return byte[]:返回得到的数组
	 */
	public byte[] changeByte(int num){
		byte[]b=new byte[4];
		b[0]=(byte)num;
		num=num>>8;
		b[1]=(byte)num;
		num=num>>8;
		b[2]=(byte)num;
		num=num>>8;
		b[3]=(byte)num;
		return b;
	}

BMP只是各种协议中的比较简单的一种,文件之所以能被各种软件打开,只不过是它们的协议符合,各种文件格式只不过是表象,只是按照各自的协议将byte组合而已。而各种软件也按照它们的协议读出数据,知道各个数据的意义,就能打开各种文件,就会出现各种图片,文字,视频等。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics