1.BMP是一种与硬件无关的图像文件格式,它所占用的空间很大,不采用其他任何压缩,它的深度可选1个字节,4个字节,8个字节,24个字节,我们通常所说的真彩色就是24个字节,BMP图像的扫描方式是从下往上,从左到右,当文件的宽度不是4的倍数是,要通过补0来使文件是4的倍数,但是由于BMP图像文件占得内存太大,我们平常都不用这种格式。
2.BMP文件结构
位图文件 头数据结构,它包含BMP图像文件的内容,显示内容等信喜,大小是14个字节。
位图信息数据结构,它包含了图像的宽和高,通常我们要显示一张图片时,必须要把这两个数据从图像中读出来。
调色板 像我们说的24位真彩色图的BMP不需要调色板
位图数据 这一项比较重要,当要显示一张图片时,一定要遍历整个图片,从下往上,从左往右依次得到三种不同的颜色。
3.BMP文件解析实现
要将既定位置的图片显示出来,必须要用到输入流,因为输入流是将硬盘中的数据读到内存中来,不能只用输入流,还必须配合使用dataInputStream 或者是BufferedInputStream,
FileInputStream fis=new FileInputStream(path);//创建文件输入流 BufferedInputStream bis=new BufferedInputStream(fis);//将文件流打包
其实不用缓冲流而用dataInputStream也行,效果是一样的,当创建好输入流之后,就应该读取既定位置图像的头文件,以及图像的宽度和高度。
int Btop=14;//头部的长度 byte []b=new byte[Btop];//定义一个存入图像头部的数组 bis.read(b,0,Btop);//将图像头部的14个字节读到数组b中 int blien=40;//读入位图信息头 byte [] bc=new byte[blien];//定义一个存入位图信息头的数组 bis.read(bc,0,blien);//将位图信息头的40个字节读到数组bc中 image_width=changInt(bc,7);//获取图形的宽度 image_height=changInt(bc,11);//获取图形的高度 public static int changInt(byte []bi,int i){ return(((int)bi[i]&0xFF)<<24)|(((int)bi[i-1]&0xFF)<<16)|(((int)bi[i-2]&0xFF)<<8)|((int)bi[i-3]&0xFF); }
因为图像的宽和高是存在第7个和第11个位置,所以传的索引值为7和11,存在数组中的是字节,所以要将byte转化成int型,又因为它读出来的位置是倒序的,所以第i个位置需左移24位才能到高8位,依次类推,要得到int值,只需将它们每8个字节先与0xff进行逻辑与,在将它们逻辑或,就可得到想要的值。
现在要做的就是读取位图数据:
public void readRGB(BufferedInputStream bis){ if(!(image_width*3%4==0)){ skip_width = 4-image_width*3%4; } //装载RGB颜色的数据数组 imageR=new int[image_height][image_width]; imageG=new int[image_height][image_width]; imageB=new int[image_height][image_width]; //读取位图数据,按行读取,从下到上,从左到右 for(int h=image_height-1;h>=0;h--){ for(int w=0;w<image_width;w++){ //读入三原色 try{ int blue=bis.read(); int green=bis.read(); int red=bis.read(); imageR[h][w]=red; imageG[h][w]=green; imageB[h][w]=blue; // if(w==0){//跳过补0项 // System.out.println(dis.skipBytes(skip_width)); // } }catch(IOException e){ e.printStackTrace(); } if(w==0){ try{ bis.skip(skip_width); } catch (IOException e) { e.printStackTrace(); } } } }
当我们读取了位图数据之后,需要将图像显示出来,要显示图片肯定要需要窗体,所以我们再需要写一个方法,该方法是用来显示窗体的。
public void showUI(String path){ this.setTitle(path); this.setLocationRelativeTo(null); this.setSize(image_width,image_height);//弹出一个图片大的大小的窗体 this.setResizable(false); this.setDefaultCloseOperation(3); this.setVisible(true);//窗体可见 graphics=this.getGraphics();//得到画布 repaint(); }
我们将图像的信息读取出来之后,就要将它显示出来,故要写一个paint方法:
/** * 把的到得数据显示出来 */ public void paint(Graphics g){ for(int h=0;h<image_height;h++){ for(int w=0;w<image_width;w++){ g.setColor(new java.awt.Color(imageR[h][w],imageG[h][w],imageB[h][w])); g.drawLine(w, h, w, h); } } }
整个BMP文件格式解析就已经实现了,实验的结果是:
操作中遇到的问题:虽然BMP文件解析的思路比较明确,但是在实际操作中经常遇到问题:比如我所有程序写好之后,我图片没有改为BMP格式,出现java.lang.outOfmemorryError错误,还有当我画图时,用的是g.fillOval(w,h,w,h)方法时,出现的图形刚开始怪模怪样,到最后才呈现出源图的效果。还有就是那个不是4的倍数时,为什么是if(!(image_width*3%4==0))而不是if(!image_width%4==0),