JAVA如何自己写方法读取BMP文件
BMP图像解析
BMP是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此, BMP 文件所占用的空间很大。 BMP 文件的图像深度可选 lbit 、 4bit 、 8bit 及 24bit 。 BMP 文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。 由于 BMP 文件格式是 Windows 环境中交换与图有关的数据的一种标准,因此在 Windows 环境中运行的图形图像软件都支持 BMP 图像格式。
要解析文件,就必须知道他的文件结构:
6.3.1 BMP文件结构
典型的BMP 图像文件由四部分组成:
1 . 位图文件 头数据结构 ,它包含BMP 图像文件的类型、显示内容等信息;
2 .位图信息数据结构 ,它包含有BMP 图像的宽、高、压缩方法,以及定义颜色等信息;
3. 调色板 ,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24 位的 BMP )就不需要调色板;
4. 位图数据 ,这部分的内容根据BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB ,而其他的小于 24 位的使用调色板中颜色索引值。
6.3.2对应的数据结构
① BMP文件头 (14 字节 )
BMP文件头数据结构含有 BMP 文件的类型、文件大小和位图起始位置等信息。其结构定义如下:
int bfType; // 位图文件的类型,必须为 ' B '' M '两个字母 (0-1字节 ) int bfSize; // 位图文件的大小,以字节为单位 (2-5 字节 ) int bfReserved1; // 位图文件保留字,必须为 0(6-7 字节 ) int bfReserved2; // 位图文件保留字,必须为 0(8-9 字节 ) int bfOffBits; // 位图数据的起始位置,以相对于位图 (10-13 字节 ) |
② 位图信息头(40 字节 )
BMP 位图信息头数据用于说明位图的尺寸等信息。
int Size; // 本结构所占用字节数 (14-17 字节 ) int image_width; // 位图的宽度,以像素为单位 (18-21 字节 ) int image_heigh; // 位图的高度,以像素为单位 (22-25 字节 ) int Planes; // 目标设备的级别,必须为 1(26-27 字节 ) int biBitCount;// 每个像素所需的位数,必须是 1(双色),(28-29 字节) 4(16 色 ) , 8(256 色 ) 或 24(// 真彩色 ) 之一 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 结构数据的个数有 biBitCount 来确定。当biBitCount=1,4,8 时,分别有 2,16,256 个表项 ; 当biBitCount=24 时,没有颜色表项。
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下 :
class BITMAPINFO { BITMAPINFOHEADER bmiHeader; // 位图信息头 RGBQUAD bmiColors[1]; // 颜色表 } |
④ 位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右, 扫描行之间是从下到上。位图的一个像素值所占的字节数:
当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图片的查看器 (暂时只做24位BMP图像解析器,大多数BMP图像也是2位):
package data0605_bmp;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.filechooser.FileNameExtensionFilter;
/*
* bmp文件读取保存
*/
public class Projiect extends JFrame{
int map[][];//保存像素颜色的数组
MyPanel center;//绘图面板
File selectFile;//读取的文件
int width;//图像宽度
int height;//图像高度
byte temp1[];//读取BMP文件的前18个字节信息
byte temp2[];//读取BMP文件的后28个字节信息
JScrollPane scrollpane;//滚动面板
public Projiect() {
//观感必须为main方法的第一个操作,否则可能不起作用
try {
UIManager
.setLookAndFeel(new com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel());
} catch (Exception e) {
e.printStackTrace();
}
this.setLayout(new BorderLayout());//最好先设置布局再添加组件
//初始化画图面板
center=new MyPanel();
center.setBackground(Color.WHITE);
center.setPreferredSize(new Dimension(400,200));
scrollpane=new JScrollPane(center);//用center初始化滚动面板
scrollpane.setPreferredSize(new Dimension(500,300));
MyListener lis=new MyListener();
//初始化菜单栏
JMenuBar menuBar=new JMenuBar();
JMenu fileMenu=new JMenu("file");
JMenuItem open=new JMenuItem("open");
JMenuItem save=new JMenuItem("save");
open.addActionListener(lis);
save.addActionListener(lis);
fileMenu.add(open);
fileMenu.add(save);
menuBar.add(fileMenu);
//
JPanel left=new JPanel();
left.setBackground(Color.DARK_GRAY);
left.setPreferredSize(new Dimension(50,0));
this.add(left,BorderLayout.WEST);
this.setJMenuBar(menuBar);
this.add(scrollpane,BorderLayout.CENTER);//加入的是滚动面板
this.setTitle("bmp");
this.setLocation(300, 150);
this.setSize(800, 500);
this.setDefaultCloseOperation(3);
this.setVisible(true);
}
/**
* 读取bmp文件
*/
public void readBMP()
{
try {
FileInputStream fis=new FileInputStream(selectFile);
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] wb=new byte[4];//读取宽度的字节数组
byte[] hb=new byte[4];//读取高度的字节数组
temp1=new byte[18];
bis.read(temp1);//bis.skip(18);//跳过前18个byte
bis.read(wb);//读取宽度
bis.read(hb);//读取高度
width=byteToint(wb);
height=byteToint(hb);
System.out.println(width+"<>"+height);
map=new int[height][width];
int skip=4-width*3%4;//得到每行要跳过的数字(与windows 系统机制有关)
temp2=new byte[28];
bis.read(temp2);//bis.skip(28);
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
int blue=bis.read();
int green=bis.read();
int red=bis.read();
Color c=new Color(red,green,blue);
map[i][j]=c.getRGB();
}
if(skip!=4)
bis.skip(skip);
}
bis.close();
center.setPreferredSize(new Dimension(width,height));
javax.swing.SwingUtilities.updateComponentTreeUI(center);//这里必须要用updateComponentTreeUI(center)函数
//不能用repaint()函数
} catch (Exception e) {
e.printStackTrace();
}
}
public void writeBMP()
{
try {
FileOutputStream fos=new FileOutputStream(selectFile);
BufferedOutputStream bos=new BufferedOutputStream(fos);
bos.write(temp1);//
bos.write(intTobyte(width,4));//写入宽度
bos.write(intTobyte(height,4));//写入高度
bos.write(temp2);
int skip=0;
if(width*3/4!=0)
{
skip=4-width*3%4;
}
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
Color c=new Color(map[i][j]);
int blue=c.getBlue();
int green=c.getGreen();
int red=c.getRed();
bos.write(blue);
bos.write(green);
bos.write(red);
}
if(skip!=0)
bos.write(new byte[skip]);
}
bos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//字节转int
public static int byteToint(byte b[])
{
int t1=(b[3]&0xff)<<24;
int t2=(b[2]&0xff)<<16;
int t3=(b[1]&0xff)<<8;
int t4=b[0]&0xff;
//System.out.println(b[1]&0xff);//输出的是一个整形数据
//在java中,设计int和比int位数来的小的类型b,如byte,char等,都是先把小类型扩展成int再来运算,
//return( t1<<24)+(t2<<16)+(t3<<8)+t4;//必须加括号
return t1+t2+t3+t4;
}
//int 转byte
public static byte[] intTobyte(int a,int len)
{
byte []t=new byte[len];
t[0]=(byte) ((a&0xff));
if(len>1)
t[1]=(byte)((a&0xff00)>>8);
if(len>2)
t[2]=(byte)((a&0xff0000)>>16);
if(len>3)
t[3]=(byte)((a&0xff000000)>>24);
return t;
}
/**
* 绘图面板
* @author ZhangZunC
*
*/
class MyPanel extends JPanel{
public void paint(Graphics g) {
super.paint(g);
if(map!=null)
{
for(int i=0;i<map.length;i++)
{
for(int j=0;j<map[i].length;j++)
{
g.setColor(new Color(map[i][j]));
g.drawLine(j, height-i, j,height- i);
}
}
}
}
}
class MyListener implements ActionListener{
JFileChooser fileChosser;
MyListener()
{
FileNameExtensionFilter filter=new FileNameExtensionFilter("24位位图(*.bmp)", "bmp");
fileChosser=new JFileChooser();
fileChosser.setFileFilter(filter);
fileChosser.setCurrentDirectory(new File("\\"));
}
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("open"))//选择的是打开
{
int choose=fileChosser.showOpenDialog(null);
if(choose==JFileChooser.APPROVE_OPTION)//点击的是确定按钮
{
selectFile=fileChosser.getSelectedFile();
readBMP();
}
}
if(e.getActionCommand().equals("save"))//选择的是打开
{
int choose=fileChosser.showSaveDialog(null);
if(choose==JFileChooser.APPROVE_OPTION)
{
selectFile=fileChosser.getSelectedFile();
writeBMP();
System.out.println("save ");
}
}
}
}
public static void main(String[] args) {
new Projiect();
// byte[] b=new byte[4];
// b[0]=0;
// b[1]=(byte) 0xaf;
// b[2]=0;
// b[3]=0;
// System.out.println(byteToint(b));
// byte b1[]=intTobyte(byteToint(b),4);
// System.out.println(b1[0]+"<>"+b1[1]+"<>"+b1[2]+"<>"+b1[3]);
}
}