`
stchou
  • 浏览: 202443 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

JAVA BMP解码 超详细解释

阅读更多

首先,对于BMP 格式的图片大家都不感觉到陌生吧。

简单的说明下:

BMP是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此, BMP 文件所占用的空间很大。 BMP 文件的图像深度可选 lbit 4bit 8bit 24bit BMP 文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。 由于 BMP 文件格式是 Windows 环境中交换与图有关的数据的一种标准,因此在 Windows 环境中运行的图形图像软件都支持 BMP 图像格式。

 

 

要解析文件,就必须知道他的文件结构

文件结构

组成

典型的BMP 图像文件由四部分组成:  

 1 位图文件 头数据结构 ,它包含BMP 图像文件的类型、显示内容等信息; 

 2 位图信息数据结构 ,它包含有BMP 图像的宽、高、压缩方法,以及定义颜色等信息;  

3 调色板 ,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24 位的 BMP )就不需要调色板; 

 4 位图数据 ,这部分的内容根据BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB ,而其他的小于 24 位的使用调色板中颜色索引值。

 

 

对应的数据结构

 

1. BMP文件头 (14 字节 )    

BMP文件头数据结构含有 BMP 文件的类型、文件大小和位图起始位置等信息。 

 其结构定义如下:   

Int bfType; // 位图文件的类型,必须为 ' B '' M '两个字母 (0-1字节 )  

  Int bfSize; // 位图文件的大小,以字节为单位 (2-5 字节 )  

  usignedshort bfReserved1; // 位图文件保留字,必须为 0(6-7 字节 )   

usignedshort bfReserved2; // 位图文件保留字,必须为 0(8-9 字节 )  

  Int bfOffBits; // 位图数据的起始位置,以相对于位图 (10-13 字节 )   

Int bfOffBits ; / / 文件头的偏移量表示,以字节为单位 

 

 

2 :位图信息头(40 字节 )  

 BMP 位图信息头数据用于说明位图的尺寸等信息。 

  Int Size ; // 本结构所占用字节数 (14-17 字节 )  

  Int image_width ; // 位图的宽度,以像素为单位 (18-21 字节 )   

int image_heigh ; // 位图的高度,以像素为单位 (22-25 字节 )  

  Int Planes; // 目标设备的级别,必须为 1(26-27 字节 )   

int n biBitCount;// 每个像素所需的位数,必须是 1( 双色 ),(28-29 字节 )    // 4(16 ) 8(256 ) 24( 真彩色 ) 之一  

Int biCompression; // 位图压缩类型,必须是 0( 不压缩 ),(30-33 字节 )    // 1(BI_RLE8 压缩类型 ) 2(BI_RLE4 压缩类型 ) 之一  

Int n SizeImage; // 位图的大小,以字节为单位 (34-37 字节 )   

Int biXPelsPerMeter; // 位图水平分辨率,每米像素数 (38-41 字节 )   

Int biYPelsPerMeter; // 位图垂直分辨率,每米像素数 (42-45 字节 )   

Int biClrUsed;// 位图实际使用的颜色表中的颜色数 (46-49 字节 )   

Int biClrImportant;// 位图显示过程中重要的颜色数 (50-53 字节 )  

 

3 :颜色表   

颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD 类型的结构,定义一种颜色。

class RGBQUAD

{  

byte rgbBlue;// 蓝色的亮度 ( 值范围为 0-255)   

byte rgbGreen; // 绿色的亮度 ( 值范围为 0-255)  

  byte rgbRed; // 红色的亮度 ( 值范围为 0-255)   

byte rgbReserved;// 保留,必须为 0   

}

颜色表中RGBQUAD 结构数据的个数有 biBitCount 来确定 :   

biBitCount=1,4,8 时,分别有 2,16,256 个表项 ;

biBitCount=24 时,没有颜色表项。  

位图信息头和颜色表组成位图信息,

BITMAPINFO结构定义如下 :   

class BITMAPINFO

{  

BITMAPINFOHEADER bmiHeader; // 位图信息头  

RGBQUAD bmiColors[1]; // 颜色表  

}

 

 

4 :位图数据   

位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右, 扫描行之间是从下到上。

位图的一个像素值所占的字节数:   

biBitCount=1 时, 8 个像素占 1 个字节 ;   

biBitCount=4 时, 2 个像素占 1 个字节 ;   

biBitCount=8 时, 1 个像素占 1 个字节 ;   

biBitCount=24 ,1 个像素占 3 个字节 ;   

Windows规定一个扫描行所占的字节数必须是   4 的倍数 ( 即以 long 为单位 ), 不足的以 0 填充,  

 

具体数据举例:  如某BMP 文件开头:   424D 4690 0000 0000 0000 4600 0000 2800 0000 8000 0000 9000 0000 0100*1000 0300 0000

0090 0000 A00F 0000 A00F 0000 0000 0000 0000 0000*00F8 E007 1F00 0000*02F1 84F1

04F1 84F1 84F1 06F2 84F1 06F2 04F2 86F2 06F2 86F2 86F2 .... ....   

BMP 文件可分为四个部分:位图文件头、位图信息头、彩色板、图像数据阵列,在上图中已用 * 分隔。

 

-----------------------------------猥琐分割线-------------------------------------------------------------

 

比方说,我们就可以做一个BMP图片的查看器

 

1、打开BMP文件时,我们这里选择使用dataInputstream 读取一个最常见的24位真彩色BMP图片

			// 创建文件输入流
			java.io.FileInputStream fis = new java.io.FileInputStream(path);
			// 将文件流包装成一个可以写基本数据类型的输出流
			java.io.DataInputStream dis = new java.io.DataInputStream(fis);

2、读入BMP头文件的基本信息

 

		    int bflen=14;                            
		    byte bf[]=new byte[bflen];             
		    dis.read(bf,0,bflen); //读取14字节BMP文件头

因为看到了BMP头文件中没有说明显示图片重要的信息,我只是开一个BF的数组,把头文件信息读取了出来,不做任何的处理。

 

3、读入位图信息头

int bilen=40;                  
		    byte bi[]=new byte[bilen];
		    dis.read(bi,0,bilen);//读取40字节BMP信息头
			    
		     // 获取一些重要数据
		     image_width=ChangeInt(bi,7);        		//源图宽度

		     System.out.println("宽:"+image_width);
		     
		      image_heigh=ChangeInt(bi,11);       	//源图高度
		     System.out.println("高:"+image_heigh);
		            											//位数
		     int nbitcount=(((int)bi[15]&0xff)<<8) | (int)bi[14]&0xff;
		     System.out.println("位数:"+nbitcount);
		            											//源图大小
		     int nsizeimage=ChangeInt(bi,23);
		     System.out.println("源图大小:"+nsizeimage);

由位图信息头中我们也可以看出来,要显示图片,重要的信息也就只有几个,其他都是一些不重要的,我们直接忽略掉。

 

 

因为我是直接读取40位的信息头

所以要将一些byte转为int 即ChangeInt

即 4个byte -------> 一个int

 

	//转成int
	public int ChangeInt(byte[] bi,int start){
		return (((int)bi[start]&0xff)<<24)         		
				| (((int)bi[start-1]&0xff)<<16)
				| (((int)bi[start-2]&0xff)<<8)
				| (int)bi[start-3]&0xff;
	}

因为24为的没有颜色表,所以我们直接读位图数据

最后,最关键的就是,读取位图数据

 

    public void showRGB24(DataInputStream dis) throws IOException{
       this.setTitle(path);
 	    //弹出一个图片的窗口一个大小
  	   this.setSize(image_width, image_heigh);
       this.setResizable(false);
 	   this.setVisible(true);
 	   g=this.getGraphics();
 	   
 	  
 	  if(!(image_width*3 % 4==0)){//图片的宽度不为0
 		 skip_width =4-image_width*3%4;
 	  }//判断是否后面有补0 的情况
 	  //装载RGB颜色的数据数组
 	  imageR = new int[image_heigh][image_width];
 	  imageG = new int[image_heigh][image_width];
 	  imageB = new int[image_heigh][image_width];
 	  
 	  
 	   //按行读取 如果H,W为正则倒着来
 	   for (int h=image_heigh-1;h>=0;h--){
 	      for (int w=0;w<image_width;w++){
 	 //  读入三原色
 	          int blue  = dis.read();
 	          int green = dis.read();
 	          int red = dis.read();
 	        	  imageB[h][w]=blue;
 	        	  imageG[h][w]=green;
 	        	  imageR[h][w]=red;
 	    	  if(w==0){//跳过补0项
 	    		  System.out.println(dis.skipBytes(skip_width));
 	    		 
 	    	  }
 	      }
 	   }
 	   repaint();
    }

关键就是在于 位图是否有补0

 

有则要跳过,没有就直接读,不然显示出来的BMP图像会倾斜。

 

即注释掉这句话得到的效果

 	    	  if(w==0){
 	    		  System.out.println(dis.skipBytes(skip_width));
 	    		 
 	    	  }

 




 

最后paint()中显示就可以看见图片了

 

 

	public void paint(java.awt.Graphics g){
     	    for (int h=0;h<image_heigh;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.fillOval(w, h, 1, 1);
		}
	     }
	}

 

 

如果是黑白的图,只是一个bit表示白或者黑

/**
* 黑白 图
* @param dis
* @throws IOException
*/
public void showRGB2(DataInputStream dis) throws IOException{
this.setTitle(path);
//弹出一个图片的窗口一个大小
this.setSize(image_width, image_heigh);
this.setResizable(false);
this.setVisible(true);
g=this.getGraphics();
if(!(image_width*3 % 4==0)){//图片的宽度不为0
skip_width =4-image_width*3%4;
}

imageR = new int[image_heigh][image_width];
imageG = new int[image_heigh][image_width];
imageB = new int[image_heigh][image_width];

//按行读取 如果H,W为正则倒着来
for (int h=image_heigh-1;h>=0;h--){
for (int w=0;w<image_width;w++){
int black = dis.read();
//System.out.println("read black is "+black);
if(black==0){
imageB[h][w]=0;
imageG[h][w]=0;
imageR[h][w]=0;
}else{
imageB[h][w]=255;
imageG[h][w]=255;
imageR[h][w]=255;
}

if(w==0){
System.out.println(dis.skipBytes(skip_width));

}
}
}
}

 

附上自己的测试代码:

  • 大小: 78 KB
  • 大小: 70 KB
  • ver1.rar (3 KB)
  • 描述: 源代码
  • 下载次数: 402
15
0
分享到:
评论
7 楼 chasegh 2014-10-17  
博主你好,为什么我用你的代码读取图片后不显示呢,只是生成了一个跟所读取图片相同大小的jframe窗口,但窗口里并不现实图片的内容。
6 楼 晓子你好 2011-04-20  
由衷觉得楼主很强大~~~
5 楼 stchou 2010-12-06  
simonyo 写道
可以学习下数字水印,更好玩

不错的建议~
4 楼 simonyo 2010-12-06  
可以学习下数字水印,更好玩
3 楼 javafound 2010-11-24  
这么简单是
2 楼 stchou 2010-11-22  
只是做了24位的,其他位还没有实现
1 楼 王者无敌Java 2010-11-21  
这么快!!!

相关推荐

    Base64编解码程序 附带源码 可以把文件编码为bmp图片

    可以把文件编码为bmp图片 当然编码出来的图片当然是很不美观的~~~ 注意:不要编码或解码过大的文件~~ 因为该程序的所有操作都是在内存中进行的~~ 且是单线程的~~ 编码出来的位图是BITMAPCOREHEADER类型的~~ 并且是8...

    BMP文件处理

    BMP文件的解析和显示,代码比较多,初学者不要轻易下载,不好理解

    JAVA_API1.6文档(中文)

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    二维码编码解码器

    二维码编码解码器,支持对jpg、bmp、png等图片的解码操作; Java语言开发;

    Java 1.6 API 中文 New

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象激活...

    java api最新7.0

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象提供...

    JavaAPI1.6中文chm文档 part1

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    JavaAPI中文chm文档 part2

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    java jdk-api-1.6 中文 chmd

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    Java Windows Bitmap decoder and encoder

    Java 编写的Windows Bitmap编码和解码程序。编码程序没有压缩,支持24位和256色。

    [Java参考文档]

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    [Java参考文档].JDK_API 1.6

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象激活...

    声音采集源码java-transform-swf:用于读取和写入Flash文件的Java库。完全支持Flash10

    声音采集资源java 转换 SWF 转换 SWF 是组成 Adob​​e Flash 文件格式规范的每个数据结构和标签的类的集合。 这些类提供了一个完全面向对象的 API 来编码和解码 Flash (.swf) 文件。 转换 SWF 支持最新发布的版本 -...

    ANSI,Unicode,BMP,UTF等编码概念实例讲解

    主要介绍了ANSI,Unicode,BMP,UTF等编码概念实例讲解,具有一定借鉴价值,需要的朋友可以参考下。

    JDK_1_6 API

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象激活...

Global site tag (gtag.js) - Google Analytics