BMP格式存储的自制画图板

1.我们先来了解BMP文件的格式:

(很重要的一点就是,存储完特定的BMP文件格式之后,记下来便是位图数据存储,这个时候我们只需要保存图片每一个点的颜色信息,这样就可以完成了保存过程)

1.(格式参考学长的)  
2.①    BMP文件头(14)字节  
3.byte bfType1;//位图文件的类型,必须位’B’’1个字母(’B ‘ 1个字节)  
4.byte bfType2;//位图文件的类型,必须位’B’1个个字母(’M ‘ 1个字节)  
5.  
6.int bfSize;//位图文件的大小,以字节位单位(4个字节)  
7.short usignedshort bfReserved1;//位图文件保留字,必须为0(2个字节)  
8.short usignedshort bfReserved2;//位图文件保留字,必须为0(2个字节)  
9.int bfOffBits;//位图数据的起始位置,以相对于位图(文件头的偏移量表示,以字节为单位)(4个字节)  
10.①   BMP位图信息头(40)字节  
11.BMP位图信息头数据用于说明位图的尺寸等信息  
12.int Size;//本结构所占用字节数(4个字节)  
13.int image_width;//位图的宽度,以像素为单位(4个字节)  
14.int image_heigh;//位图的高度,以像素位单位(4个字节)  
15.short Planes;//目标设备的级别,必须位1(2个字节)  
16.short biBitCount;//每个像素所需的位数,必须是1(双色),4(16色),  
17.8(256色)或24(//真彩色)之一(2个字节)  
18.int biCompression;//位图压缩类型,必须是0(不压缩),1(BI_RLE8 压缩类型)或2(BI_RLE4)之一(4个字节)  
19.int SizeImage;//位图的大小,以字节位单位(4个字节)  
20.int biXPelsPerMeter;//位图水平分辨率,每米像素数(4个字节)  
21.int biYPelsPerMeter;//位图垂直分辨率,每米像素数(4个字节)  
22.int biClrUsed;//位图实际使用的颜色表中的颜色数(4个字节)  
23.int biClrImportant;//位图显示过程中重要的颜色数(4个字节)  

 

2.我先给出保存图片的代码然后再对照上面的格式要求来分析:

		String command = e.getActionCommand();
		JFileChooser chooser = new JFileChooser();
		if (command.equals("保存")) {
			int t = chooser.showSaveDialog(null);
			if (t == JFileChooser.APPROVE_OPTION) {
				String path = chooser.getSelectedFile().getAbsolutePath();
				try {
					FileOutputStream fos = new FileOutputStream(path);
					DataOutputStream dos = new DataOutputStream(fos);
					savebmpTop(dos);// 存储文件头
					savebmpInfo(dos);// 存储位图信息头
					savebmpDate(dos);//// 图像数据阵列
					fos.flush();
					fos.close();
				} catch (Exception ef) {
					JOptionPane.showMessageDialog(null, "文件保存失败!!");
					ef.printStackTrace();
				}
			}
		}

 我们会发现,保存图片调用了三个方法:

savebmpTop(dos);// 存储文件头
 savebmpInfo(dos);// 存储位图信息头
  savebmpDate(dos);// 图像数据阵列

分别用来按顺序存储文件头,位图信息头,就像开始给出的bmp图片格式要求,,,,接下来就是存我们图像数据阵列了;

savebmpTop(dos);// 存储文件头的方法:

// bmp文件头
	public void savebmpTop(OutputStream ops) throws Exception {
		// 位图文件的类型,必须为BM
		ops.write('B');
		ops.write('M');
		int height = DrawListener.array.length;
		int width = DrawListener.array[0].length;
		// 位图文件的大小,以字节为单位
		int size = 14 + 40 + height * width * 3 + (4 - width * 3 % 4) * 3 * height;
		writeInt(ops, size);
		// 保留字节,必须为零
		writeShort(ops, (short) 0);
		writeShort(ops, (short) 0);
		// 位图偏移量
		writeInt(ops, 54);
	}

 

 savebmpInfo(dos);// 存储位图信息头

	// 位图信息头
	public void savebmpInfo(OutputStream ops) throws Exception {
		int height = DrawListener.array.length;
		int width = DrawListener.array[0].length;
		// 位图信息头长度
		writeInt(ops, 40);
		// 位图宽
		writeInt(ops, width);
		// 位图高
		writeInt(ops, height);
		// 位图位面数总是为1
		writeShort(ops, (short) 1);
		// 位图24位像素
		writeShort(ops, (short) 24);
		// 位图是否被压缩,0为不压缩
		writeInt(ops, 0);
		// 字节数代表位图大小
		writeInt(ops, height * width * 3 + (4 - width * 3 % 4) * 3 * height);
		// 水平分辨率 每米像素数
		writeInt(ops, 0);
		// 垂直分辨率 每米像素数
		writeInt(ops, 0);
		// 颜色索引,0为所有调色板
		writeInt(ops, 0);
		// 对图象显示有重要影响的颜色索引的数目。如果是0,表示都重要
		writeInt(ops, 0);
	}

 

  savebmpDate(dos);// 图像数据阵列方法(其中还有一些数据进制转换的方法我没有吧代码列出来,文件里面有完整代码,在后面我会写这个进制转换)

	// 图像数据阵列
	public void savebmpDate(OutputStream ops) throws Exception {
		int height = DrawListener.array.length;
		int width = DrawListener.array[0].length;
		int m = 0;
		// 进行补0
		if (width * 3 % 4 > 0) {
			m = 4 - width * 3 % 4;
		}
		for (int i = height - 1; i >= 0; i--) {
			for (int j = 0; j < width; j++) {
				int t = DrawListener.array[i][j];
				writeColor(ops, t);
			}
			for (int k = 0; k < m; k++) {
				ops.write(0);
			}
		}
	}

 

到这里我们的存储过程就完成了

然而我们存储Int类型的数据需要用到这个代码:

// 传入一个int值,转化成4个8位的二进制,刚好四个字节
	public void writeInt(OutputStream ops, int t) throws Exception {
		int a = (t >> 24) & 0xff;
		int b = (t >> 16) & 0xff;
		int c = (t >> 8) & 0xff;
		int d = t & 0xff;
		ops.write(d);
		ops.write(c);
		ops.write(b);
		ops.write(a);
	}

上面的t是一个int类型(32位的)的,0xff是16进制它的二进制是

1111 1111 t>>24表示t的二进制右移24位与1111 1111做位运算,得到t的二进制的前8位,同理t>>16 右移16得到t的次8位,这里右移16,剩下16位怎么会是得到t的次8位呢,注意啊0xff的二进制是1111 1111 只有8位,在计算的b的时候它会把0xff前面用0补成16位就是0000 00

00 1111 1111,前面8个0在进行与位运算的时候不管怎么样都是0,所以是得到t的次8位的数据,就这样将一个int值的t按照8位8位的一次取出数据。

为什么要进行位运算呢?

Windows规定一个扫描行所占的字节数必须是4的倍数,不足的以0补充。关键就是要搞清楚Windows里面数据是怎么存的?Windows储存数据是从地位到高位来储存的,我们一般都是从高位到地位进行保存。所以就需要我们进行位运算将数据来转化成Windows储存数据的方式。

然而:

如果不进行位运算取出4个8位的byte类型,int是32位不会进行补0,而8位的byte会进行补0;

整个位图数据存储,就只需要存储每一个点的颜色:

// 总共是24位的色图,分成3个字节来存储(每8位二进制数据为一个字节)
	public void writeColor(OutputStream ops, int t) throws Exception {
		int b = (t >> 16) & 0xff;
		int c = (t >> 8) & 0xff;
		int d = t & 0xff;
		ops.write(d);
		ops.write(c);
		ops.write(b);
	}

保存文件最重要的步骤都在上面了;

-------------------------------------------------------------------------

3.怎么读取BMP格式的文件呢?

这个最简单了,就是你怎么存的数据,就按那个顺序来读取,当然还有一些位运算需要进行,就是存储的时候我们把int转化成byte类型,现在我们要转换回来:

// 由于读取的是字节,把读取到的4个byte转化成1个int
	public int changeInt(InputStream ips) throws IOException {
		int t1 = ips.read() & 0xff;
		int t2 = ips.read() & 0xff;
		int t3 = ips.read() & 0xff;
		int t4 = ips.read() & 0xff;
		int num = (t4 << 24) + (t3 << 16) + (t2 << 8) + t1;
		System.out.println(num);
		return num;
	}

	// 24位的图片是1个像素3个字节。
	public int readColor(InputStream ips) throws IOException {
		int b = ips.read() & 0xff;
		int g = ips.read() & 0xff;
		int r = ips.read() & 0xff;
		int c = (r << 16) + (g << 8) + b;
		return c;
	}

4.使用的时候要注意(如图):保存图片后缀名为 .bmp  这样其他的照片查看器都可以打开,你自己也可以在菜单栏里面打开(还有一些其他的功能没有实现)!



 

 

其他全部完整可以运行的代码在上传的文件里面;

猜你喜欢

转载自qq-24665727.iteye.com/blog/2283713
今日推荐