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整型
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); } }