java读取bitmap文件

读取24位bitmap(bmp)格式图片文件

 
 
//读取bmp图片文件
package pcm24;  
  
import java.awt.Color;  
import java.awt.Graphics;  
import java.io.IOException;  
  

public class BmpRead24 extends javax.swing.JFrame {  
    /** 
     *  
     */  
    private static final long serialVersionUID = 1L;  
  
    /** 
     * 位图的宽 
     */  
    private static int width;  
  
    /** 
     * 位图的高 
     */  
    private static int height;  
  
    /** 
     * 位图数据数组,即一个像素的三个分量的数据数组 
     */  
    private static int[][] red, green, blue;  
  
    Graphics g;  
  
    public static void main(String args[]) {  
        BmpRead24 bmp = new BmpRead24();  
        bmp.init();  
    }  
  
    public void init() {  
        try {  
            // 通过bmp文件地址创建文件输入流对象  
            java.io.FileInputStream fin = new java.io.FileInputStream(  
                    "C:\\Documents and Settings\\专属于我\\桌面\\未命名1.bmp");  
  
            // 根据文件输入流对象创建原始数据输入对象  
            // 这里既可以用原始数据输入流来读取数据,也可以用缓冲输入流来读取,后者速度相比较快点。  
//          java.io.DataInputStream bis = new java.io.DataInputStream(fin);  
  
            java.io.BufferedInputStream bis = new java.io.BufferedInputStream(  
                    fin);  
  
            // 建立两个字节数组来得到文件头和信息头的数据  
            byte[] array1 = new byte[14];  
            bis.read(array1, 0, 14);  
  
            byte[] array2 = new byte[40];  
            bis.read(array2, 0, 40);  
  
            // 翻译bmp文件的数据,即将字节数据转化为int数据  
            // 通过翻译得到位图数据的宽和高  
            width = ChangeInt(array2, 7);  
            height = ChangeInt(array2, 11);  
  
            // 调用可以将整个位图数据读取成byte数组的方法  
            getInf(bis);  
  
            fin.close();  
            bis.close();  
  
            // 创建BMP对象来显示图画  
            showUI();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    /** 
     * 实现可将四个字节翻译int数据的方法 
     *  
     * @param array2 
     *            存储字节的字节数组 
     * @param start 
     *            起始字节 
     * @return 返回翻译后的int数据 
     */  
    public int ChangeInt(byte[] array2, int start) {  
        // 因为char,byte,short这些数据类型经过运算符后会自动转为成int数据类,  
        // 所以array2[start]&0xff的实际意思就是通过&0xff将字符数据转化为正int数据,然后在进行位运算。  
        // 这里需要注意的是<<的优先级别比&高,所以必须加上括号。  
  
        int i = (int) ((array2[start] & 0xff) << 24)  
                | ((array2[start - 1] & 0xff) << 16)  
                | ((array2[start - 2] & 0xff) << 8)  
                | (array2[start - 3] & 0xff);  
        return i;  
    }  
  
    /** 
     * 得到位图数据的int数组 
     *  
     * @param dis 
     *            数据输入流对象 
     */  
    public void getInf(java.io.BufferedInputStream bis) {  
        // 给数组开辟空间  
        red = new int[height][width];  
        green = new int[height][width];  
        blue = new int[height][width];  
  
        // 通过计算得到每行计算机需要填充的字符数。  
        // 为什么要填充?这是因为windows系统在扫描数据的时候,每行都是按照4个字节的倍数来读取的。  
        // 因为图片是由每个像素点组成。而每个像素点都是由3个颜色分量来构成的,而每个分量占据1个字节。  
        // 因此在内存存储中实际图片数据每行的长度是width*3。  
        int skip_width = 0;  
        int m = width * 3 % 4;  
        if (m != 0) {  
            skip_width = 4 - m;  
        }  
  
        // 通过遍历给数组填值  
        // 这里需要注意,因为根据bmp的保存格式。  
        // 位图数据中height的值如果是正数的话:  
        // 那么数据就是按从下到上,从左到右的顺序来保存。这个称之为倒向位图。  
        // 反之就是按从上到下,从左到右的顺序来保存。这个则称之为正向位图。  
        for (int i = height - 1; i >= 0; i--) {  
            for (int j = 0; j < width; j++) {  
                try {  
                    // 这里遍历的时候,一定要注意本来像素是有RGB来表示,  
                    // 但是在存储的时候由于windows是小段存储,所以在内存中是BGR顺序。  
                    blue[i][j] = bis.read();  
                    green[i][j] = bis.read();  
                    red[i][j] = bis.read();  
  
                    // 这里一定要知道,其实系统在给位图数据中添加填充0的时候,都是加在每行的最后。  
                    // 但是我们在使用dis.skipBytes()这个方法的时候,却不一定要在最后一列。  
                    // 系统在填充数据的时候,在数据上加了标记。  
                    // 所以dis.skipBytes()这个方法只要调用了,那么系统就会自动不读取填充数据。  
                    if (j == 0) {  
                        bis.skip(skip_width);  
                    }  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
  
    public void showUI() {  
        // 对窗体的属性进行设置  
        this.setTitle("BMP解析");//设置标题  
        this.setSize(width, height);//设置窗体大小  
        this.setDefaultCloseOperation(3);//点击关闭,程序自动退出。  
        this.setResizable(false);//设置窗体大小不可以调节  
        this.setLocationRelativeTo(null);//设置窗体出现在屏幕中间  
  
        //创建自己的panel,用其来显示图形。  
        //因为如果将图片设置到窗体上显示时,因为jframe是一个复合组件,上面的组件有多个paint方法,所以在paint的时候会画两次,  
        //而panel是只需画一次。  
        MyPanel panel = new MyPanel();  
        java.awt.Dimension di = new java.awt.Dimension(width, height);//设置panel大小  
        panel.setPreferredSize(di);  
        this.add(panel);//窗体添加panel  
        this.setVisible(true);//使窗体可见。  
    }  
  
    public class MyPanel extends javax.swing.JPanel {  
        /** 
         *  
         */  
        private static final long serialVersionUID = 1L;  
  
        /** 
         * 重写paint方法 
         */  
        public void paint(Graphics g) {  
            // 这句话可写可不写,因为这句话是用来画jframe的contentPane的。   
            // 而这里我们已经在下面定义了contentPane的方法了  
            super.paint(g);  
            for (int i = 0; i < height; i++) {  
                for (int j = 0; j < width; j++) {  
                    g.setColor(new Color(red[i][j], green[i][j], blue[i][j]));  
                    // 如果这里画点的话,是不能使用下面注释掉的方法的,不行的话,亲,自己试试吧  
                    // 因为系统在画椭圆的时候,是先画出椭圆的外切矩形。而矩形的边框刚好是占据一个像素点。  
                    // 因此也就出现了,jdk api中说g.drawOval的像素点是width+1,height+1。  
                    // 如果亲,你有更好的理解,请告诉我们。欢迎交流!!!  
                    // g.fillOval(j, i, 1, 1);  
                    g.fillRect(j, i, 1, 1);// 这里可以使用画点的任何方法,除了上面那种特例。  
                }  
            }  
        }  
    }  
}  

编写bmp
package pcm24;  
  
import java.awt.Color;  
  
/** 
 *  
 * 类说明:实现BMP文件格式的保存 
 *  
 * @author 彭晨明 E-mail:[email protected] 
 *  
 * @version 创建时间:2012-2-5下午9:06:28 
 *  
 */  
public class BmpWrite24 {  
    /** 
     * 图形数据数组 
     */  
    private Color[][] pointArray;  
  
    /** 
     * 图形的宽 
     */  
    int width;  
  
    /** 
     * 图形的高 
     */  
    int height;  
  
    /** 
     * BMPWrite构造器的重载,传入图形数据数组 
     *  
     * @param pointArray 
     */  
    public BmpWrite24(Color[][] pointArray) {  
        this.pointArray = pointArray;  
        this.width = pointArray.length;  
        this.height = pointArray[0].length;  
        this.write();  
    }  
  
    /** 
     * 将数据传入内存 
     */  
    public void write() {  
        try {  
            // 创建输出流文件对象  
            java.io.FileOutputStream fos = new java.io.FileOutputStream(  
                    "C:\\Documents and Settings\\专属于我\\桌面\\未命名1.bmp");  
            // 创建原始数据输出流对象  
            java.io.DataOutputStream dos = new java.io.DataOutputStream(fos);  
  
            // 给文件头的变量赋值  
            int bfType = 0x424d; // 位图文件类型(0—1字节)  
            int bfSize = 54 + width * height * 3;// bmp文件的大小(2—5字节)  
            int bfReserved1 = 0;// 位图文件保留字,必须为0(6-7字节)  
            int bfReserved2 = 0;// 位图文件保留字,必须为0(8-9字节)  
            int bfOffBits = 54;// 文件头开始到位图实际数据之间的字节的偏移量(10-13字节)  
  
            // 输入数据的时候要注意输入的数据在内存中要占几个字节,  
            // 然后再选择相应的写入方法,而不是它自己本身的数据类型  
            // 输入文件头数据  
            dos.writeShort(bfType); // 输入位图文件类型'BM'  
            dos.write(changeByte(bfSize),0,4); // 输入位图文件大小  
            dos.write(changeByte(bfReserved1),0,2);// 输入位图文件保留字  
            dos.write(changeByte(bfReserved2),0,2);// 输入位图文件保留字  
            dos.write(changeByte(bfOffBits),0,4);// 输入位图文件偏移量  
  
            // 给信息头的变量赋值  
            int biSize = 40;// 信息头所需的字节数(14-17字节)  
            int biWidth = width;// 位图的宽(18-21字节)  
            int biHeight = height;// 位图的高(22-25字节)  
            int biPlanes = 1; // 目标设备的级别,必须是1(26-27字节)  
            int biBitcount = 24;// 每个像素所需的位数(28-29字节),必须是1位(双色)、4位(16色)、8位(256色)或者24位(真彩色)之一。  
            int biCompression = 0;// 位图压缩类型,必须是0(不压缩)(30-33字节)、1(BI_RLEB压缩类型)或2(BI_RLE4压缩类型)之一。  
            int biSizeImage = width * height;// 实际位图图像的大小,即整个实际绘制的图像大小(34-37字节)  
            int biXPelsPerMeter = 0;// 位图水平分辨率,每米像素数(38-41字节)这个数是系统默认值  
            int biYPelsPerMeter = 0;// 位图垂直分辨率,每米像素数(42-45字节)这个数是系统默认值  
            int biClrUsed = 0;// 位图实际使用的颜色表中的颜色数(46-49字节),如果为0的话,说明全部使用了  
            int biClrImportant = 0;// 位图显示过程中重要的颜色数(50-53字节),如果为0的话,说明全部重要  
              
            // 因为java是大端存储,那么也就是说同样会大端输出。  
            // 但计算机是按小端读取,如果我们不改变多字节数据的顺序的话,那么机器就不能正常读取。  
            // 所以首先调用方法将int数据转变为多个byte数据,并且按小端存储的顺序。  
              
            // 输入信息头数据  
            dos.write(changeByte(biSize),0,4);// 输入信息头数据的总字节数  
            dos.write(changeByte(biWidth),0,4);// 输入位图的宽  
            dos.write(changeByte(biHeight),0,4);// 输入位图的高  
            dos.write(changeByte(biPlanes),0,2);// 输入位图的目标设备级别  
            dos.write(changeByte(biBitcount),0,2);// 输入每个像素占据的字节数  
            dos.write(changeByte(biCompression),0,4);// 输入位图的压缩类型  
            dos.write(changeByte(biSizeImage),0,4);// 输入位图的实际大小  
            dos.write(changeByte(biXPelsPerMeter),0,4);// 输入位图的水平分辨率  
            dos.write(changeByte(biYPelsPerMeter),0,4);// 输入位图的垂直分辨率  
            dos.write(changeByte(biClrUsed),0,4);// 输入位图使用的总颜色数  
            dos.write(changeByte(biClrImportant),0,4);// 输入位图使用过程中重要的颜色数  
  
            // 因为是24位图,所以没有颜色表  
            // 通过遍历输入位图数据  
            // 这里遍历的时候注意,在计算机内存中位图数据是从左到右,从下到上来保存的,  
            // 也就是说实际图像的第一行的点在内存是最后一行  
            for (int i = height - 1; i >= 0; i--) {  
                for (int j = 0; j < width; j++) {  
                    // 这里还需要注意的是,每个像素是有三个RGB颜色分量组成的,  
                    // 而数据在windows操作系统下是小端存储,对多字节数据有用。  
                    int red = pointArray[i][j].getRed();// 得到位图点的红色分量  
                    int green = pointArray[i][j].getGreen();// 得到位图点的绿色分量  
                    int blue = pointArray[i][j].getBlue();// 得到位图点的蓝色分量  
                    byte[] red1 = changeByte(red);  
                    byte[] green1 = changeByte(green);  
                    byte[] blue1 = changeByte(blue);  
                    dos.write(blue1,0,1);  
                    dos.write(green1,0,1);  
                    dos.write(red1,0,1);  
                }  
            }  
            //关闭数据的传输  
            dos.flush();  
            dos.close();  
            fos.close();  
            System.out.println("success!!!");  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
      
    /** 
     * 将一个int数据转为按小端顺序排列的字节数组 
     * @param data int数据 
     * @return  按小端顺序排列的字节数组 
     */  
    public byte[] changeByte(int data){  
        byte b4 = (byte)((data)>>24);  
        byte b3 = (byte)(((data)<<8)>>24);  
        byte b2= (byte)(((data)<<16)>>24);  
        byte b1 = (byte)(((data)<<24)>>24);  
        byte[] bytes = {b1,b2,b3,b4};  
        return bytes;  
    }  
}  


猜你喜欢

转载自blog.csdn.net/qq_29048719/article/details/76050391