[/code]在实现BMP位图的读取与保存之前 我们要了解BMP位图的文件结构

BMP位图文件结构分为4个部分:

  1 . 位图文件 头数据结构 ,它包含BMP 图像文件的类型、显示内容等信息;  
 2 .位图信息数据结构 ,它包含有BMP 图像的宽、高、压缩方法,以及定义颜色等信息;   
  3. 调色板 ,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24 位的 BMP )就不需要调色板;  
  4. 位图数据 ,这部分的内容根据BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB ,而其他的小于 24 位的使用调色板中颜色索引值。  


① BMP文件头 (14 字节 )

Byte*2 bfType; // 位图文件的类型,必须为 ' B '' M '两个字母 (0-1字节 )   
int bfSize; // 位图文件的大小,以字节为单位 (2-5 字节 )   
int bfReserved1; // 位图文件保留字,必须为 0(6-7 字节 )    
int  bfReserved2; // 位图文件保留字,必须为 0(8-9 字节 )   
int  bfOffBits; // 位图数据的起始位置,以相对于位图 (10-13 字节 ) 


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


③ 颜色表  
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD 类型的结构,定义一种颜色。 
class  RGBQUAD {   
	byte  rgbBlue;// 蓝色的亮度 ( 值范围为 0-255)    
	byte  rgbGreen; // 绿色的亮度 ( 值范围为 0-255)   
	byte  rgbRed; // 红色的亮度 ( 值范围为 0-255)    
	byte  rgbReserved;// 保留,必须为 0    
}


由上面定义的RGBQUAD可知  每个表项占据4个字节

颜色表中RGBQUAD 结构数据的个数有 biBitCount 来确定。当biBitCount=1,4,8 时,分别有 2,16,256 个表项 ; 当biBitCount=24 时,没有颜色表项。、
所以当
biBitCount=1时  颜色表占8个字节
biBitCount=4时  颜色表占64个字节
biBitCount=8时  颜色表占1024个字节
biBitCount=24时  颜色表占0个字节

④ 位图数据    
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右, 扫描行之间是从下到上。位图的一个像素值所占的字节数:    
当biBitCount=1 时, 8 个像素占 1 个字节 ;    
当biBitCount=4 时, 2 个像素占 1 个字节 ;    
当biBitCount=8 时, 1 个像素占 1 个字节 ;    
当biBitCount=24 时 ,1 个像素占 3 个字节 ;    
Windows规定一个扫描行所占的字节数必须是4 的倍数  不足的以 0 填充 

注:bmp储存数据 都是低位在前 高位在后  而java中是高位在前 低位在后 所以写入读取时要注意转换


// 定义一个内部方法 用来读取bmp整型
	[code="java"]public int MyreadInt(BufferedInputStream bis) throws IOException {
		int a = bis.read();
		int b = bis.read();
		int c = bis.read();
		int d = bis.read();
		int e = (d << 24) + (c << 16) + (b << 8) + a;
		return e;
	}


// ***********************读入24 位BMP文件***************************


public void ReadBMP(BufferedInputStream bis) throws IOException {
		char head[] = new char[2];
		head[0] = (char) bis.read();
		head[1] = (char) bis.read();
		String isbmp = new String(head);
		if ("BM".equals(isbmp)) {
			bis.skip(8);
			// 得到 位图数据的起始位置,
			int loction = MyreadInt(bis);
			bis.skip(4);
			// 得到位图hight width
			int width = MyreadInt(bis);
			int height = MyreadInt(bis);
			drawPanel.setPreferredSize(new Dimension(width, height));
			DrawListener.array = new int[height][width];
			bis.skip(loction - 26);
			int v = 0;
			if (width * 3 % 4 != 0)
				v = 4 - width * 3 % 4;
			for (int i = height - 1; i >= 0; i--) {
				for (int j = 0; j < width; j++) {
					int blue = bis.read();
					int green = bis.read();
					int red = bis.read();
					int rgb = (red << 16) + (green << 8) + blue;
					DrawListener.array[i][j] = rgb;
				}
				bis.skip(v);
			}

		}

		bis.close();
	}





// ***********************读入 16色BMP文件***************************

public void Read16BMP(BufferedInputStream bis) throws IOException { 

 char head[] = new char[2]; 

 head[0] = (char) bis.read(); 

 head[1] = (char) bis.read(); 

 String isbmp = new String(head); 

 if ("BM".equals(isbmp)) { 

 bis.skip(8); 

 // 得到 位图数据的起始位置, 

 int loction = MyreadInt(bis); 

 bis.skip(4); 

 // 得到位图hight width 

 int width = MyreadInt(bis); 

 int height = MyreadInt(bis); 

 drawPanel.setPreferredSize(new Dimension(width, height)); 

 DrawListener.array = new int[height][width]; 

 bis.skip(loction - 26);


// *********************读位图数据******************************



int v = 0;
			if (width % 2 != 0)
				v = 4 - (width + 1) / 2 % 4;
			else
				v = 4 - width / 2 % 4;

			//当width为奇数时
			if (width % 2 != 0) {
				for (int i = height - 1; i >= 0; i--) {
					for (int j = 0; j < width; j++) {
						int a = bis.read();
						if (j == width - 1) {
							int c1 = (a >> 4) & 15;
							DrawListener.array[i][j] = colorArr[c1];
						} else {
							int c1 = (a >> 4) & 15;
							int c2 = a & 15;
							DrawListener.array[i][j] = colorArr[c1];
							j++;
							DrawListener.array[i][j] = colorArr[c2];
						}
					}
					bis.skip(v);
				}
			} else {
				//当width为偶数时
				for (int i = height - 1; i >= 0; i--) {
					for (int j = 0; j < width; j++) {
						int a = bis.read();
						int c1 = (a >> 4) & 15;
						int c2 = a & 15;
						DrawListener.array[i][j] = colorArr[c1];
						j++;
						DrawListener.array[i][j] = colorArr[c2];

					}
					bis.skip(v);
				}
			}

		}

		bis.close();
	}





// 创建16色位图颜色表


public void Creat16color() {
		colorArr[0] = new Color(0, 0, 0).getRGB();
		colorArr[1] = new Color(80, 0, 0).getRGB();
		colorArr[2] = new Color(0, 80, 0).getRGB();
		colorArr[3] = new Color(80, 80, 0).getRGB();
		colorArr[4] = new Color(0, 0, 80).getRGB();
		colorArr[5] = new Color(80, 0, 80).getRGB();
		colorArr[6] = new Color(0, 80, 80).getRGB();
		colorArr[7] = new Color(80, 80, 80).getRGB();
		colorArr[8] = new Color(192, 192, 192).getRGB();
		colorArr[9] = new Color(255, 0, 0).getRGB();
		colorArr[10] = new Color(0, 255, 0).getRGB();
		colorArr[11] = new Color(255, 255, 0).getRGB();
		colorArr[12] = new Color(0, 0, 255).getRGB();
		colorArr[13] = new Color(255, 0, 255).getRGB();
		colorArr[14] = new Color(0, 255, 255).getRGB();
		colorArr[15] = new Color(255, 255, 255).getRGB();

	}





// *******************得到位图文件大小********************


public int GetBMPSize(int type) {
		Dimension dim = drawPanel.getPreferredSize();
		int width = dim.width;
		int height = dim.height;

		int size = 0;
		size += 54;
		if (type == 24) {
			// 24位的时候

			// 计算每行要补充的字节数
			int v = 0;
			if (width * 3 % 4 != 0) {
				v = 4 - width * 3 % 4;
			}
			size += width * height * 3 + v * height;

		} else if (type == 4) {
			int v = 0;
			if ((width / 2) % 4 != 0)
				v = 4 - width / 2 % 4;
			size += width * height / 2 + v * height;
		}
		return size;
	}





// ********************写入int类型**********************
	public void MywriteInt(BufferedOutputStream bos, int data)
			throws IOException {
		int a = data >> 24 & 0xFF;
		byte b = (byte) (data >> 16 & 0xFF);
		byte c = (byte) (data >> 8 & 0xFF);
		byte d = (byte) (data & 0xFF);
		// System.out.println(a+" "+b+" "+c+" "+d);
		// 因为写入时值写入8个字节 所以 只写入 int的最低位 所以无需转型byte
		bos.write(d);
		bos.write(c);
		bos.write(b);
		bos.write(a);
	}

	// ********************写入short类型****************************
	public void MywriteShort(BufferedOutputStream bos, short data)
			throws IOException {
		int a = data >> 8 & 0xFF;
		int b = data & 0xFF;
		bos.write(b);
		bos.write(a);
	}

	// *******************写入BMP文件头 (14 字节 )********************
	public void WriteHead(BufferedOutputStream bos, int type)
			throws IOException {
		byte a = 'B';
		byte b = 'M';
		// 将'B' 'M'写入文件 2个字节
		bos.write(a);
		bos.write(b);
		// 写入位图文件大小 4个字节
		int size = this.GetBMPSize(type);
		this.MywriteInt(bos, size);

		// 位图文件保留字 4个字节 都为0
		bos.write(0);
		bos.write(0);
		bos.write(0);
		bos.write(0);
		// 位图数据的起始位置 4个字节 24位 颜色表为空
		if (type == 24) {
			this.MywriteInt(bos, 54);
		} else if (type == 4) {
			this.MywriteInt(bos, 54 + 64);
		}

	}

	// *******************写入位图信息头(40 字节 ) ********************
	public void WriteMessageHead(BufferedOutputStream bos, int type)
			throws IOException {
		// 本结构所占用字节数 (14-17 字节 )
		this.MywriteInt(bos, 40);
		// 位图的宽度,以像素为单位 (18-21 字节 )
		Dimension dim = drawPanel.getPreferredSize();
		int width = dim.width;
		int height = dim.height;
		// System.out.println(width+"  "+height);
		this.MywriteInt(bos, width);
		this.MywriteInt(bos, height);
		// 目标设备的级别,必须为 1(26-27 字节 )
		this.MywriteShort(bos, (short) 1);
		// 每个像素所需的位数
		this.MywriteShort(bos, (short) type);
		// 位图压缩类型
		this.MywriteInt(bos, 0);
		// 位图数据的大小
		if (type == 24) {
			int size = this.GetBMPSize(type) - 54;
			this.MywriteInt(bos, size);
		} else if (type == 4) {
			int size = this.GetBMPSize(type) - 54 - 64;
			this.MywriteInt(bos, size);
		}
		// 位图水平分辨率 4个字节 位图垂直分辨率 4个字节
		this.MywriteInt(bos, 0);
		this.MywriteInt(bos, 0);
		// 位图实际使用的颜色表中的颜色数
		this.MywriteInt(bos, 16);
		// 位图显示过程中重要的颜色数
		this.MywriteInt(bos, 0);

	}

	// *******************写入位图颜色表 ********************
	public void WriteColor(BufferedOutputStream bos, int type)
			throws IOException {
		if (type == 4) {
			for (int i = 0; i < 16; i++) {
				Color c = new Color(colorArr[i]);
				int red = c.getRed();
				int green = c.getGreen();
				int blue = c.getBlue();
				bos.write(blue);
				bos.write(green);
				bos.write(red);
				bos.write(0);
			}
		}
	}

	// *******************写入位图位图数据********************
	public void WriteData(BufferedOutputStream bos) throws IOException {
		Dimension dim = drawPanel.getPreferredSize();
		int width = dim.width;
		int height = dim.height;
		int v = 0;
		if (width * 3 % 4 != 0)
			v = 4 - width * 3 % 4;
		for (int i = DrawListener.array.length - 1; i >= 0; i--) {
			for (int j = 0; j < DrawListener.array[i].length; j++) {
				int RGB = DrawListener.array[i][j];
				bos.write(RGB & 0xFF);
				bos.write((RGB >> 8) & 0xFF);
				bos.write((RGB >> 16) & 0xFF);

			}
			for (int k = 0; k < v; k++)
				bos.write(0);
		}
	}