[/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);
}
}