你是否曾想过,为什么用word打开一份文档他会正常的显示出里面的文字,为什么你用画图打开一张图片会正常的显示出一张完整的图片,你又是否尝试过把图片的扩展名改成txt格式,打开时会有什么情况呢,当然他不会显示正常的一张图片,这一切都说明每个应用程序都有自己的规则即协议,画图有就将图片完整打开的规则,word有将文件完整打开的规则,等等。从这可以看出协议的重要性,然而协议是由程序员自己规定的,你定什么样的规则程序将会执行什么样的动作,所以我们了解了这些协议就可以自己写出一个程序将文件翻译出来;下面我将以将bmp文件解释在自己定义的界面上重新画出来;
1.首先我们就要了解bmp(24位 )文件的格式:
以上图片是通过UltraEdit软件观察到的图片的十六进制的编码
其中(1)bmp文件头信息,即上0-d,前十四个字节,包括文件的大小格式等信息,我们要将图片重新画出
来所以这些信息不是很重要,当我们读数据时不必要再将其中的数据单取出来。
(2)位图信息头,提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息,其中我们需要把读到
的数据中的代表图片大小的数据单独读取出来;这一部分共占40个字节,而在这四十个字节中第八位即我们所需要的位图的宽,第十二位是位图的高,需要单独取出来,以备使用。
(3)调色板:我们一般见到的图像以24位图像为主,即R、G、B三种颜色各用8个bit来表示,这样的图像我们称为真彩色,这种情况下是不需要调色板的,也就是所位图信息头后面紧跟的就是位图数据了。因此,我们常常见到有这样一种说法:位图文件从文件头开始偏移54个字节就是位图数据了,这其实说的是24或32位图的情况。这也就解释了我们按照这种程序写出来的程序为什么对某些位图文件没用了。
(4)位图数据:即位图的颜色数据,即RGB,24位RGB按照BGR的顺序来存储每个像素的各颜色通道的值,一个像素的所有颜色分量值都存完后才存下一个下一个像素,不进行交织存储。即读到第一个字节是B代表蓝色,第二个字节为G代表绿色,第三个字节R代表红色,依次类推,然而读到这三个数据的存储方式用二维数组存储,可以清楚的表示在某个点上的RGB;
2.操作的流程大致为:(1)读取数据
(2)在自己规定的界面上画出数据(此部分可扩展,例如可以加菜单条进行画图)
3.下面是详细代码
界面设置
import java.awt.FlowLayout;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
//从文件中选择图片进行重画
public class JframeTest extends JFrame {
private JDesktopPane jdp=new JDesktopPane();
public static void main(String[] args) {
JframeTest jf=new JframeTest();
jf.showUI();
}
public void showUI(){
this.setTitle("重画图片");
this.setSize(500,500);
this.setLayout(new FlowLayout());
this.setDefaultCloseOperation(3);
JMenuBar mb=new JMenuBar();
this.setContentPane(jdp);
JMenu m_file=new JMenu("文件");
JMenuItem mi_open=new JMenuItem("打开");
mi_open.setActionCommand("open");
JMenuItem mi_delete=new JMenuItem("清除");
mi_delete.setActionCommand("delete");
JMenuItem mi_save=new JMenuItem("保存");
mi_save.setActionCommand("save");
JMenuItem mi_exit=new JMenuItem("退出");
mi_exit.setActionCommand("exit");
m_file.add(mi_open);
m_file.add(mi_delete);
m_file.add(mi_exit);
m_file.add(mi_save);
mb.add(m_file);
this.setJMenuBar(mb);
this.setVisible(true);
AListener aa=new AListener(this);
mi_open.addActionListener(aa);
}
}
读取数据以及画图
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
public class AListener implements ActionListener {
private JFrame jf;
// private JDesktopPane jdp;//内部窗体
int image_width;
int image_hight;
int[][] imageR;
int[][] imageG;
int[][] imageB;
public AListener(JFrame jf){
this.jf=jf;
//this.jdp=jdp;
}
//监听器
public void actionPerformed(ActionEvent e) {
String command=e.getActionCommand();
if(command.equals("open")){
JFileChooser jfchooser =new JFileChooser();
//设置 JFileChooser,以允许用户只选择文件、只选择目录,或者可选择文件和目录。默认值是 JFilesChooser.FILES_ONLY
jfchooser.setFileSelectionMode(jfchooser.FILES_AND_DIRECTORIES);
//显示 选择文件
jfchooser.showOpenDialog(jf);
File file=jfchooser.getSelectedFile();
readFile(file);
}
}
/**
* 读取数据
* @param file
*/
public void readFile(File file){
String ss=file.getAbsolutePath();
//System.out.print(ss);
try{
int flen=14;
int slen=40;
int skip_width=0;
java.io.FileInputStream fins=new java.io.FileInputStream(file);
java.io.DataInputStream bos=new java.io.DataInputStream(fins);
byte [] byte1=new byte[flen];
bos.read(byte1, 0, flen);
byte [] byte2=new byte[slen];
bos.read(byte2, 0, slen);
image_width=changeInt(byte2,7);//位图的宽
image_hight=changeInt(byte2,11);//位图的高
System.out.print(image_hight);
int nbitcount=(((int)byte2[15]&0xff)<<8)|(int)byte2[14]&0xff;//位数
int nsizeimage=changeInt(byte2,23);//位图大小
if(!(image_width*3%4==0)){
skip_width=4-image_width*3%4;
}
imageB=new int [image_hight][image_width];
imageG=new int [image_hight][image_width];
imageR=new int [image_hight][image_width];
//读取RGB
for(int h=image_hight-1;h>=0;h--){
for(int w=0;w<image_width;w++){
int green=bos.read();
int blue=bos.read();
int red=bos.read();
imageB[h][w]=blue;
imageG[h][w]=green;
imageR[h][w]=red;
if(w==0){//跳过补零项
bos.skipBytes(skip_width);
//System.out.println(bos.skipBytes(skip_width));
}
}
}
changePaint(jf);
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
* 位运算 将字节转化为整型
* @param bi
* @param start
* @return
*/
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;
}
/**
* 画图
* @param jf
*/
public void changePaint(JFrame jf){
Graphics g=jf.getGraphics();
for(int h=0;h<image_hight;h++){
for(int w=0;w<image_width;w++){
Color c =new Color(imageR[h][w],imageG[h][w],imageB[h][w]);
g.setColor(c);
g.drawLine(w+100, h+100, w+100, h+100);
}
System.out.println("--- :"+h);
}
}
}
提醒:一定要注意读取位图RGB数据时是以行来一一读取的,所以在画的时候也要以行来画,不然有些图片会画不出来,反正我出现过这种情况,改一下就画出来了,应该是这个原因吧!!!
在上述代码中仅实现了打开(重画),清除以及保存还需慢慢研究,以后写完可以来补上,呵呵!!!