要将BufferedImage实例保存为BMP文件,就需要知道BMP文件的格式,可以参考我转载的文章:
《BMP文件格式》。
      下面是我的将BufferedImage实例保存为24位色BMP文件的实现。
      首先是BMP文件相关的两个头结构:BMPFileHeader和BMPInfoHeader。

 /**//*
/**//*
 * Created on 2005-6-21
 * Created on 2005-6-21
 *
 *
 * TODO To change the template for this generated file go to
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 * Window - Preferences - Java - Code Style - Code Templates
 */
 */
 package org.lotus.image.codec.bmp;
package org.lotus.image.codec.bmp;


 /**//**
/**//**
 * <p> Title: BMP文件的头结构</p>
 * <p> Title: BMP文件的头结构</p>
 *
 * 
 * <p> Description: BMP文件的头结构固定是14个字节,其定义如下:</p>
 * <p> Description: BMP文件的头结构固定是14个字节,其定义如下:</p>
 * <p>
 * <p>
 * byte[2] bfType;                    指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM“
 * byte[2] bfType;                    指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM“
 * byte[4] bfSize;                    指定文件大小,包括这14个字节
 * byte[4] bfSize;                    指定文件大小,包括这14个字节
 * byte[2] bfReserved1;            保留字
 * byte[2] bfReserved1;            保留字
 * byte[2] bfReserved2;            保留字
 * byte[2] bfReserved2;            保留字
 * byte[4] bfOffBits;                为从文件头到实际的位图数据的偏移字节数
 * byte[4] bfOffBits;                为从文件头到实际的位图数据的偏移字节数
 * </p>
 * </p>
 *
 * 
 * <p> Copyright: Copyright (c) 2005</p>
 * <p> Copyright: Copyright (c) 2005</p>
 *
 * 
 * <p> Company: 21Lotus</p>
 * <p> Company: 21Lotus</p>
 *
 * 
 * @author George Hill
 * @author George Hill
 * @version 1.0
 * @version 1.0
 */
 */


 class BMPFileHeader
class BMPFileHeader  {
{

 // Header data
    // Header data
 private byte[] data = new byte[14];
    private byte[] data = new byte[14];


 public byte[] getData()
    public byte[] getData()  {
{
 return this.data;
        return this.data;
 }
    }
 
    
 // BMP file size
    // BMP file size
 private int size;
    private int size;
 
    

 public int getSize()
    public int getSize()  {
{
 return this.size;
        return this.size;
 }
    }
 
    
 private int offset;
    private int offset;
 
    

 public int getOffset()
    public int getOffset()  {
{
 return this.offset;
        return this.offset;
 }
    }
 
    

 BMPFileHeader(int size, int offset)
    BMPFileHeader(int size, int offset)  {
{
 this.size = size;
        this.size = size;
 this.offset = offset;
        this.offset = offset;
 
        
 data[0] = 'B';
        data[0] = 'B';
 data[1] = 'M';
        data[1] = 'M';

 int value = size;
        int value = size;
 data[2] = (byte) value;
        data[2] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[3] = (byte) value;
        data[3] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[4] = (byte) value;
        data[4] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[5] = (byte) value;
        data[5] = (byte) value;

 value = offset;
        value = offset;
 data[10] = (byte) value;
        data[10] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[11] = (byte) value;
        data[11] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[12] = (byte) value;
        data[12] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[13] = (byte) value;
        data[13] = (byte) value;
 }
    }
 
    
 }
}

 

 /**//*
/**//*
 * Created on 2005-6-21
 * Created on 2005-6-21
 *
 *
 * TODO To change the template for this generated file go to
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 * Window - Preferences - Java - Code Style - Code Templates
 */
 */
 package org.lotus.image.codec.bmp;
package org.lotus.image.codec.bmp;


 /**//**
/**//**
 * <p>Title: BMP文件内容的头结构</p>
 * <p>Title: BMP文件内容的头结构</p>
 *
 *
 * <p>Description: BMP文件内容的头结构固定是40个字节,其定义如下:</p>
 * <p>Description: BMP文件内容的头结构固定是40个字节,其定义如下:</p>
 * <p>
 * <p>
 * byte[4] biSize;                            指定这个结构的长度,为40
 * byte[4] biSize;                            指定这个结构的长度,为40
 * byte[4] biWidth;                            指定图象的宽度,单位是象素
 * byte[4] biWidth;                            指定图象的宽度,单位是象素
 * byte[4] biHeight;                        指定图象的高度,单位是象素
 * byte[4] biHeight;                        指定图象的高度,单位是象素
 * byte[2] biPlanes;                        必须是1,不用考虑
 * byte[2] biPlanes;                        必须是1,不用考虑
 * byte[2] biBitCount;                    指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)
 * byte[2] biBitCount;                    指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)
 * byte[4] biCompression;                指定位图是否压缩
 * byte[4] biCompression;                指定位图是否压缩
 * byte[4] biSizeImage;                    指定实际的位图数据占用的字节数
 * byte[4] biSizeImage;                    指定实际的位图数据占用的字节数
 * byte[4] biXPelsPerMeter;            指定目标设备的水平分辨率,单位是每米的象素个数
 * byte[4] biXPelsPerMeter;            指定目标设备的水平分辨率,单位是每米的象素个数
 * byte[4] biYPelsPerMeter;            指定目标设备的垂直分辨率,单位是每米的象素个数
 * byte[4] biYPelsPerMeter;            指定目标设备的垂直分辨率,单位是每米的象素个数
 * byte[4] biClrUsed;                        指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2biBitCount
 * byte[4] biClrUsed;                        指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2biBitCount
 * byte[4] biClrImportant;            指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的
 * byte[4] biClrImportant;            指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的
 * </p>
 * </p>
 *
 *
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Copyright: Copyright (c) 2005</p>
 *
 *
 * <p>Company: 21Lotus</p>
 * <p>Company: 21Lotus</p>
 *
 *
 * @author George Hill
 * @author George Hill
 * @version 1.0
 * @version 1.0
 */
 */


 class BMPInfoHeader
class BMPInfoHeader  {
{

 private byte[] data = new byte[40];
    private byte[] data = new byte[40];
 
    

 public byte[] getData()
    public byte[] getData()  {
{
 return this.data;
        return this.data;
 }
    }
 
    
 private int width;
    private int width;
 
    

 public int getWidth()
    public int getWidth()  {
{
 return this.width;
        return this.width;
 }
    }
 
    
 private int height;
    private int height;
 
    

 public int getHeight()
    public int getHeight()  {
{
 return this.height;
        return this.height;
 }
    }
 
    
 public int bitCount;
    public int bitCount;
 
    

 public int getBitCount()
    public int getBitCount()  {
{
 return this.bitCount;
        return this.bitCount;
 }
    }
 
    

 public BMPInfoHeader(int width, int height, int bitCount)
    public BMPInfoHeader(int width, int height, int bitCount)  {
{
 this.width = width;
        this.width = width;
 this.height = height;
        this.height = height;
 this.bitCount = bitCount;
        this.bitCount = bitCount;
 
        
 data[0] = 40;
        data[0] = 40;

 int value = width;
        int value = width;
 data[4] = (byte) value;
        data[4] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[5] = (byte) value;
        data[5] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[6] = (byte) value;
        data[6] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[7] = (byte) value;
        data[7] = (byte) value;

 value = height;
        value = height;
 data[8] = (byte) value;
        data[8] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[9] = (byte) value;
        data[9] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[10] = (byte) value;
        data[10] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[11] = (byte) value;
        data[11] = (byte) value;

 data[12] = 1;
        data[12] = 1;

 data[14] = (byte) bitCount;
        data[14] = (byte) bitCount;

 value = width * height * 3;
        value = width * height * 3;
 if (width % 4 != 0)
        if (width % 4 != 0)
 value += (width % 4) * height;
          value += (width % 4) * height;
 data[20] = (byte) value;
        data[20] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[21] = (byte) value;
        data[21] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[22] = (byte) value;
        data[22] = (byte) value;
 value = value >>> 8;
        value = value >>> 8;
 data[23] = (byte) value;
        data[23] = (byte) value;
 }
    }
 
    
 }
}

       仿照com.sun.image.codec.jpeg.JPEGImageEncoder写的接口类BMPEncoder。

 /**//*
/**//*
 * Created on 2005-6-21
 * Created on 2005-6-21
 *
 *
 * TODO To change the template for this generated file go to
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 * Window - Preferences - Java - Code Style - Code Templates
 */
 */
 package org.lotus.image.codec.bmp;
package org.lotus.image.codec.bmp;

 import java.awt.image.*;
import java.awt.image.*;
 import java.io.IOException;
import java.io.IOException;


 /**//**
/**//**
 * <p>Title: </p>
 * <p>Title: </p>
 *
 *
 * <p>Description: </p>
 * <p>Description: </p>
 *
 *
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Copyright: Copyright (c) 2005</p>
 *
 *
 * <p>Company: 21Lotus</p>
 * <p>Company: 21Lotus</p>
 *
 *
 * @author George Hill
 * @author George Hill
 * @version 1.0
 * @version 1.0
 */
 */


 public interface BMPEncoder
public interface BMPEncoder  {
{

 public void encode(BufferedImage bi) throws IOException;
      public void encode(BufferedImage bi) throws IOException;
 
      
 public static final int BIT_COUNT_BLACKWHITE = 1;
      public static final int BIT_COUNT_BLACKWHITE = 1;
 public static final int BIT_COUNT_16COLORS = 4;
      public static final int BIT_COUNT_16COLORS = 4;
 public static final int BIT_COUNT_256COLORS = 8;
      public static final int BIT_COUNT_256COLORS = 8;
 public static final int BIT_COUNT_TRUECOLORS = 24;
      public static final int BIT_COUNT_TRUECOLORS = 24;
 
      
 }
}

       BMPEncoder接口的实现BMPEncoderImpl。

 /**//*
/**//*
 * Created on 2005-6-21
 * Created on 2005-6-21
 *
 *
 * TODO To change the template for this generated file go to
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 * Window - Preferences - Java - Code Style - Code Templates
 */
 */
 package org.lotus.image.codec.bmp;
package org.lotus.image.codec.bmp;

 import java.awt.image.*;
import java.awt.image.*;
 import java.io.*;
import java.io.*;


 /**//**
/**//**
 * <p>Title: </p>
 * <p>Title: </p>
 *
 *
 * <p>Description: </p>
 * <p>Description: </p>
 *
 *
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Copyright: Copyright (c) 2005</p>
 *
 *
 * <p>Company: 21Lotus</p>
 * <p>Company: 21Lotus</p>
 *
 *
 * @author George Hill
 * @author George Hill
 * @version 1.0
 * @version 1.0
 */
 */


 class BMPEncoderImpl implements BMPEncoder
class BMPEncoderImpl implements BMPEncoder  {
{
 
    
 private OutputStream out;
    private OutputStream out;
 
    

 public BMPEncoderImpl(OutputStream out)
    public BMPEncoderImpl(OutputStream out)  {
{
 this.out = out;
        this.out = out;
 }
    }
 
    

 public void encode(BufferedImage bi) throws IOException
      public void encode(BufferedImage bi) throws IOException  {
{
 int width = bi.getWidth();
          int width = bi.getWidth();
 int height = bi.getHeight();
          int height = bi.getHeight();
 
          
 boolean needBlank = (width % 4 != 0);
          boolean needBlank = (width % 4 != 0);
 
          
 int size = width * height * 3;
          int size = width * height * 3;

 if (needBlank)
          if (needBlank)  {
{
 size += (width % 4) * height;
              size += (width % 4) * height;
 }
          }
 
          
 BMPFileHeader fileHeader = new BMPFileHeader(size, 54);
          BMPFileHeader fileHeader = new BMPFileHeader(size, 54);
 BMPInfoHeader infoHeader = new BMPInfoHeader(width, height, BIT_COUNT_TRUECOLORS);
          BMPInfoHeader infoHeader = new BMPInfoHeader(width, height, BIT_COUNT_TRUECOLORS);

 byte[] rgbs = new byte[3];
          byte[] rgbs = new byte[3];
 byte[] blank = new byte[width % 4];
          byte[] blank = new byte[width % 4];
 
          
 out.write(fileHeader.getData());
          out.write(fileHeader.getData());
 out.write(infoHeader.getData());
          out.write(infoHeader.getData());

 int index = 0;
          int index = 0;

 for (int y = height - 1; y >= 0; y--)
          for (int y = height - 1; y >= 0; y--)  {
{

 for (int x = 0; x < width; x++)
              for (int x = 0; x < width; x++)  {
{
 index += 3;
                  index += 3;

 int rgb = bi.getRGB(x, y);
                  int rgb = bi.getRGB(x, y);
 rgbs[0] = (byte) rgb;
                  rgbs[0] = (byte) rgb;
 rgb = rgb >>> 8;
                  rgb = rgb >>> 8;
 rgbs[1] = (byte) rgb;
                  rgbs[1] = (byte) rgb;
 rgb = rgb >>> 8;
                  rgb = rgb >>> 8;
 rgbs[2] = (byte) rgb;
                  rgbs[2] = (byte) rgb;

 out.write(rgbs);
                  out.write(rgbs);


 if (needBlank && (index % (width * 3) == 0))
                  if (needBlank && (index % (width * 3) == 0))  {
{
 out.write(blank);
                      out.write(blank);
 }
                  }
 }
              }
 }
          }
 }
      }

 }
}

       一个工厂类BMPCodec。

 /**//*
/**//*
 * Created on 2005-6-21
 * Created on 2005-6-21
 *
 *
 * TODO To change the template for this generated file go to
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 * Window - Preferences - Java - Code Style - Code Templates
 */
 */
 package org.lotus.image.codec.bmp;
package org.lotus.image.codec.bmp;

 import java.io.OutputStream;
import java.io.OutputStream;


 /**//**
/**//**
 * <p>Title: </p>
 * <p>Title: </p>
 *
 *
 * <p>Description: </p>
 * <p>Description: </p>
 *
 *
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Copyright: Copyright (c) 2005</p>
 *
 *
 * <p>Company: 21Lotus</p>
 * <p>Company: 21Lotus</p>
 *
 *
 * @author George Hill
 * @author George Hill
 * @version 1.0
 * @version 1.0
 */
 */


 public class BMPCodec
public class BMPCodec  {
{


 public static BMPEncoder createBMPEncoder(OutputStream dest)
    public static BMPEncoder createBMPEncoder(OutputStream dest)  {
{
 return new BMPEncoderImpl(dest);
        return new BMPEncoderImpl(dest);
 }
    }
 
    
 }
}

       下面是我的测试用例:

 /**//*
/**//*
 * Created on 2005-6-22
 * Created on 2005-6-22
 *
 *
 * TODO To change the template for this generated file go to
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 * Window - Preferences - Java - Code Style - Code Templates
 */
 */
 package org.lotus.test;
package org.lotus.test;

 import java.awt.*;
import java.awt.*;
 import java.awt.image.*;
import java.awt.image.*;
 import java.io.*;
import java.io.*;

 import junit.framework.TestCase;
import junit.framework.TestCase;

 import org.lotus.image.codec.bmp.*;
import org.lotus.image.codec.bmp.*;


 /**//**
/**//**
 * <p>Title: </p>
 * <p>Title: </p>
 *
 *
 * <p>Description: </p>
 * <p>Description: </p>
 *
 *
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Copyright: Copyright (c) 2005</p>
 *
 *
 * <p>Company: 21Lotus</p>
 * <p>Company: 21Lotus</p>
 *
 *
 * @author George Hill
 * @author George Hill
 * @version 1.0
 * @version 1.0
 */
 */

 public class BMPCodecTest extends TestCase
public class BMPCodecTest extends TestCase  {
{


 /**//*
    /**//*
 * @see TestCase#setUp()
     * @see TestCase#setUp()
 */
     */

 protected void setUp() throws Exception
    protected void setUp() throws Exception  {
{
 super.setUp();
        super.setUp();
 }
    }


 /**//*
    /**//*
 * @see TestCase#tearDown()
     * @see TestCase#tearDown()
 */
     */

 protected void tearDown() throws Exception
    protected void tearDown() throws Exception  {
{
 super.tearDown();
        super.tearDown();
 }
    }


 public void testCreateBMPEncoder() throws Exception
    public void testCreateBMPEncoder() throws Exception  {
{
 int width = 104;
        int width = 104;
 int height = 100;
        int height = 100;
 int size = width * height * 3;
        int size = width * height * 3;
 if (width % 4 != 0)
        if (width % 4 != 0)
 size += (width % 4) * height;
          size += (width % 4) * height;
 size += 54;
        size += 54;
 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
 Graphics g = image.getGraphics();
        Graphics g = image.getGraphics();
 g.setColor(Color.WHITE);
        g.setColor(Color.WHITE);
 g.fillRect(0, 0, width, height);
        g.fillRect(0, 0, width, height);

 g.setColor(Color.BLUE);
        g.setColor(Color.BLUE);
 g.setFont(new Font("宋体", Font.ITALIC, 20));
        g.setFont(new Font("宋体", Font.ITALIC, 20));
 g.drawString("Hello", 30, 30);
        g.drawString("Hello", 30, 30);
 
        
 BMPCodec.createBMPEncoder(new FileOutputStream("C:\\house\\test.bmp")).encode(image);
        BMPCodec.createBMPEncoder(new FileOutputStream("C:\\house\\test.bmp")).encode(image);
 }
    }

 }
}

       保存的BMP文件如图:
 
	posted on 2005-07-04 17:33 
小米 阅读(3462) 
评论(6)  编辑  收藏  所属分类: 
Java