BMP格式总结
学完了画图板的文件保存,也才明白,这个文件的格式只是我们自己定义的,而按照我们的方式保存的图片用我们常用的图片查看器是打不开的,,因为图片查看器所采用的保存格式是另外一种,所以在读取数据的时候无法识别,所以为了改善我们画图板的通用性,在此基础上我们试写了BMP的图像文件格式。
首先,典型的BMP图像文件由四部分构成:位图文件头,文图信息头,颜色表和位图数据;其中文件头主要包括BMP图像文件的类型,文件大小和位图起始位置等信息;信息头数据主要用于说明位图的尺寸等信息;而颜色表则是说明位图中的颜色,不过24位图没有颜色表;最后位图数据中记录了位图的每一个像素值。
当我们需要打开一个BMP24位的图像文件时,就要读取图片的基本信息及数据:
// 读取图片宽度与高度
int len = 54;
// 定义一个数组 来存放读取到的字节
byte bd[] = new byte[len];
dis.read(bd, 0, len);
int width = ChangeInt(bd, 21);
int height = ChangeInt(bd, 25);
// 将宽度和高度存入数组中
DrawListener.area = new int[height][width];
int skipLen=0;
// 按行读取(从下往上,从左往右)
if (width * 3 % 4 != 0) {
skipLen = 4-width * 3 % 4;
}
//(从下往上,从左往右)
for (int i=height-1;i >=0;i--) {
for (int j=0;j<width;j++) {
// 读取三原色
int blue=dis.read();
int green=dis.read();
int red=dis.read();
if(j==width-1){
dis.skip(skipLen);
}
// 将三原色转换为颜色
Color color=new Color(red, green, blue);
// 将相对应的颜色转为整形存入数组中(下标极为重要)
DrawListener.area[i][j] = color.getRGB();
}
}
draw.repaint();// 调用重绘的方法
这里我一开始做的不太完整,关键就在于是否需要跳0,,因为Windows规定一个扫描行所占的字节行数必须是4的倍数,可是一个像素由三个字节组成,所以不足的就补0了,可是在读取的时候要把补的0给跳过,否则显示出来的BMP图片是倾斜的,所以要把补的0给跳过,而且每一行都要跳。最后在重绘的时候,可以自己再写一个方法去显示图片,也可以调用画布的再重绘的方法。
当把用我们的画板绘制的图片保存为BMP24位的格式的时候,只需要用别人定义好的格式将数据写入内存,同样的,要写文件的头文件,信息头,颜色表及图像数据区;但要注意的是因为我们写入的数据都是int,所以要通过自己定义的方法将其转换为byte:
private byte[] ChangeByte(int a) {
byte b4=(byte) ((a) >> 24);
byte b3=(byte) ((a) >> 16);
byte b2=(byte) ((a) >> 8);
byte b1=(byte) ((a)) ;
byte[] bytes={b1,b2,b3,b4};
return bytes;
}
至此,才终于可以将画板的文件保存为BMP,而且画板也可以打开BMP的文件,可是到这里,我的画板打不开他自己画的图片,却可以打开系统BMP图片,而画板画的图片却可以通过图片查看器来浏览,这让我们感到无比的神奇,几经周折,才发现最大的问题不是出在BMP的格式上,而是在将byte的数据转换为int时,写错了括号搞错了优先级(必须先进行与运算再移位),我也才明白之前能打开系统BMP图片只是刚好碰运气而已。唉,这让我更加发现,写代码绝对是个细活。。。。
private int ChangeInt(byte[] b, int start) {
// 必须 先与运算 再移位
return (((int) b[start] & 0xff) <<24)
| (((int) b[start - 1] & 0xff) << 16)
| (((int) b[start - 2] & 0xff )<< 8)
| ((int) b[start - 3]& 0xff);
}