BMP总结
对于我来说画图板的BMP格式的打开与保存这一项目做得还是比较艰难,听冠毅同学简单的向我们介绍了一下BMP,有格式的转换和它的语法规则等,但也仅仅是有了初步的了解,当时觉得用BMP做画图板的保存与打开还是一头雾水。所以我先另外创建了一个类单做打开与保存这一部分,再加到画图板上,在左哥的帮助下有了大概的思路:
打开:
创建文件输入流——>读入BMP文件头:
byte[] bf = new byte[14];
dos.read(bf);
——>读取位图信息头:
byte[] bi = new byte[40];
dos.read(bi);
//调用ChangeInt方法,得到图形的长与宽,是倒着调用的,7为下面int x4=(int)bi[start-3]&0xff;中的,
chang=ChangeInt(bi,7);
//后三个为宽度
kuan=ChangeInt(bi,11);
int biSizeImage =ChangeInt(bi,23);
——>将读取到的信息存到监听器中创建的area数组中:
DrawListener.area=new int[kuan][chang];
——>读取图像像素:
//一个像素占三个字节,windows规定扫描行所占的字节数,必须是4的倍数
//如果不是4的倍数就要补零
if((chang*3%4)!=0){
skip_chang = 4-chang*3%4;
}
//装载RGB颜色数据数组
imageR = new int[kuan][chang];
imageG=new int[kuan][chang];
imageB=new int[kuan][chang];
//按行读取——从左到右,从下到上
for(int i=kuan-1;i>=0;i--){
for(int j=0;j<chang;j++){
//读取三原色
int blue=dos.read();
int green=dos.read();
int red=dos.read();
System.out.println("blu="+blue+"green="+green+"red="+red);
//将读取出来的颜色放入RGB数组中
//倒着读
imageB[i][j]=blue;
imageG[i][j]=green;
imageR[i][j]=red;
if(j==0){
//跳过补0的项
dos.skipBytes(skip_chang);
}
——>将撒按原色转为颜色:
Color color=new Color(red,green,blue);
//将相对应的颜色转为整形存入数组中
DrawListener.area[i][j]=color.getRGB();
——>最后调用repaint方法,将此方法加到界面的按钮监听器上:else
if (command.equals("open")) {
int t = chooser.showOpenDialog(null);
if (t == 0) {
String path = chooser.getSelectedFile().getAbsolutePath();
// 读取文件,得到队列,作为画板要使用的队列
openShapes(path);
// openShapes(dos,chang,kuan);
System.out.println("path"+path);
jp.repaint();
}
实现对BMP格式图像的打开。
保存:
再来说说保存吧,其实搞清楚了BMP格式文件的结构,保存便很简单,在一个方法中写入文件的各个结构大体思路为:
先得到画布的宽度与长度:
//得到画布的宽度与高度
int chang=jp.getWidth();
int kuan=jp.getHeight();
——>创建一个文件输出流对象:java.io.FileOutputStream output=new java.io.FileOutputStream(path);
java.io.DataOutputStream output1=new java.io.DataOutputStream(output);
——>写入图片的头文件:
/**//**
* Title: BMP文件的头结构
*
* Description: BMP文件的头结构固定是14个字节,其定义如下:
*
* byte[2] bfType; 指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM“
* byte[4] bfSize; 指定文件大小,包括这14个字节
* byte[2] bfReserved1; 保留字
* byte[2] bfReserved2; 保留字
* byte[4] bfOffBits; 为从文件头到实际的位图数据的偏移字节数
*/
//位图文件类型(BM为String,所以将字符串转为字节)
byte[] bt=new byte[2];
bt[0]='B';
bt[1]='M';
output1.write(bt,0,2);
//文件大小(加上跳过的字节)
int bSize=54+chang*kuan*3;
output1.write(ChangByte(bSize),0,4);
//文件保留字
int Reserved1=0;
output1.write(ChangByte(Reserved1),0,2);
int Reserved2=0;
output1.write(ChangByte(Reserved2),0,2);
//偏移量
int bOffbits=54;
output1.write(ChangByte(bOffbits),0,4);
——>写入文件信息头:
/**//**
* Title: BMP文件内容的头结构
*
* Description: BMP文件内容的头结构固定是40个字节,其定义如下:
*
* byte[4] biSize; 指定这个结构的长度,为40
* byte[4] biWidth; 指定图象的宽度,单位是象素
* byte[4] biHeight; 指定图象的高度,单位是象素
* byte[2] biPlanes; 必须是1,不用考虑
* byte[2] biBitCount; 指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)
* byte[4] biCompression; 指定位图是否压缩
* byte[4] biSizeImage; 指定实际的位图数据占用的字节数
* byte[4] biXPelsPerMeter; 指定目标设备的水平分辨率,单位是每米的象素个数
* byte[4] biYPelsPerMeter; 指定目标设备的垂直分辨率,单位是每米的象素个数
* byte[4] biClrUsed; 指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2biBitCount
* byte[4] biClrImportant; 指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的
*
*/
//本结构所占用的字节数
int Size=40;
output1.write(ChangByte(Size),0,4);
//位图宽度
int chang1=chang;
output1.write(ChangByte(chang1),0,4);
int kuan1=kuan;
output1.write(ChangByte(kuan1),0,4);
//plane总数
int plane=1;
output1.write(ChangByte(plane),0,2);
//记录颜色的位数
int Countbit=24;
output1.write(ChangByte(Countbit),0,2);
//数据压缩方式(不压缩)
int Compression=0;
output1.write(ChangByte(Compression),0,4);
//图像区数据的大小
int SizeImage=chang*kuan;
output1.write(ChangByte(SizeImage),0,4);
//水平每米有多少像素
int XPelsPerMeter=0;
output1.write(ChangByte(XPelsPerMeter),0,4);
//垂直每米有多少像素
int YPelsPerMeter=0;
output1.write(ChangByte(YPelsPerMeter),0,4);
//所用颜色数
int ClrUsed=0;
output1.write(ChangByte(ClrUsed),0,4);
//重要颜色数
int ClrImportant=0;
output1.write(ChangByte(ClrImportant),0,4);
——>因为24位的不需要调色区所以我们不需要写入
——>写入图像数据区:
for(int i=kuan-1;i>=0;i--){
for(int j=0;j<chang;j++){
int Cnum=DrawListener.area[i][j];
Color color=new Color(Cnum);
//将颜色RGB输出
output1.write(ChangByte(color.getBlue()),0,1);
output1.write(ChangByte(color.getGreen()),0,1);
output1.write(ChangByte(color.getRed()),0,1);
}
}
——>最后将这一方法加到界面的按钮监听器按钮上:
if (command.equals("save")) {
int t = chooser.showSaveDialog(null);
if (t == 0) {// 如果点击的是保存
String path = chooser.getSelectedFile().getAbsolutePath();
// 保存文件
save(path);
System.out.println("path"+path);
}
实现以BMP格式的保存。
在整个过程中我遇到的最大的问题首先是打开和保存的方法写了,也没报错但在画图板上操作时没反应,其实这个问题不是出在方法本身而是根本就没调用这个方法,没有实现这个方法,就是在按钮的监听器条件设置是方法没调用对;第二个问题是所要打开的图形和画布大小的设置,我最先的错误思路是先要把huabu这个对象传到我创建的实现打开与保存的这个监听器中,再将读取到的图像的大小赋予画布,让画布的大小和将要打开的图像的大小一样,这样做明显是不正确的,其实这个问题的解决方法也很简单,不用传只要画布的比图片大就行而在保存时只要在JPanel jp对象上得到画布的长和宽就可以了。
其实现在回过头去再看看,它的思路很简单,有时就是自己讲问题看得太复杂了,还没开始做就开始打退堂鼓,所以以后要像东哥和龙哥常说的要敢于做。