用 Java 保存位图文件

摘要
虽然 Java 提供了几种打开图像的机制,但保存图像并不是它的强项。这篇技巧将讲述如何将图像保存在 24 位位图文件中。另外,Jean-Pierre 还提供了将图像文件写入位图文件所需的全部代码。
这篇技巧是 "在 Java 应用程序中加载位图文件的逐步指南" 的补充,那篇技巧说明了在 Java 应用程序中加载位图文件的过程。本月我再提供一篇教程,说明如何将图像保存在 24 位位图文件中,其中还包含将图像对象写入位图文件的代码片断。
如果您在 Microsoft Windows 环境中工作,那么创建位图文件的功能将为您提供许多方便。例如,在我的上一个项目中,我必须将 Java 与 Microsoft Access 对接。Java 程序允许用户在屏幕上绘图。这幅图随后被打印到 Microsoft Access 报表中。由于 Java 不支持 OLE,我的唯一选择就是创建该图的一个位图文件,并通知 Microsoft Access 报表在何处能找到这个位图文件。如果您写过向剪贴板发送图像的应用程序,则这个技巧可能对您有用 -- 尤其是当您将这个信息传递给另一个应用程序时。

位图文件的格式
位图文件格式支持 4 位 RLE(行程长度编码)以及 8 位和 24 位编码。因为我们只处理 24 位格式,所以下面我们查看一下该文件的结构。

位图文件分为三个部分。我已将它们列在下面。

None.gif 第 1 部分:位图文件的标头
None.gif标头包含位图文件的类型大小信息和版面信息。结构如下(摘自 C 语言结构定义):
None.gif
ExpandedBlockStart.gifContractedBlock.giftypedef struct tagBITMAPFILEHEADER dot.gif {
InBlock.gif UINT bfType;
InBlock.gif DWORD bfSize;
InBlock.gif UINT bfReserved1;
InBlock.gif UINT bfReserved2;
InBlock.gif DWORD bfOffBits;
ExpandedBlockEnd.gif}
BITMAPFILEHEADER;
None.gif
None.gif
None.gif下面是对这个清单中的代码元素的说明:
None.gif
None.gif
None.gifbfType:指定文件类型,其值始终为 BM。
None.gif
None.gifbfSize:指定整个文件的大小(以字节为单位)。
None.gif
None.gifbfReserved1:保留 -- 必须为 0 。
None.gif
None.gifbfReserved2:保留 -- 必须为 0 。
None.gif
None.gifbfOffBits:指定从 BitmapFileHeader 到图像首部的字节偏移量。
None.gif现在您已经明白位图标头的用途就是标识位图文件。读取位图文件的每个程序都使用位图标头来进行文件验证。
None.gif
None.gif第 2 部分:位图信息标头
None.gif随后的标头称为信息标头,其中包含图像本身的属性。
None.gif
None.gif下面说明如何指定 Windows 3.0 (或更高版本)设备独立位图 (DIB) 的大小和颜色格式:
None.gif
ExpandedBlockStart.gifContractedBlock.giftypedef struct tagBITMAPINFOHEADER dot.gif {
InBlock.gif DWORD biSize;
InBlock.gif LONG biWidth;
InBlock.gif LONG biHeight;
InBlock.gif WORD biPlanes;
InBlock.gif WORD biBitCount;
InBlock.gif DWORD biCompression;
InBlock.gif DWORD biSizeImage;
InBlock.gif LONG biXPelsPerMeter;
InBlock.gif LONG biYPelsPerMeter;
InBlock.gif DWORD biClrUsed;
InBlock.gif DWORD biClrImportant;
ExpandedBlockEnd.gif}
BITMAPINFOHEADER;
None.gif
None.gif
None.gif以上代码清单的每个元素说明如下:
None.gif
None.gif
None.gifbiSize:指定 BITMAPINFOHEADER 结构所需的字节数。
None.gif
None.gifbiWidth:指定位图的宽度(以象素为单位)。
None.gif
None.gifbiHeight:指定位图的高度(以象素为单位)。
None.gif
None.gifbiPlanes:指定目标设备的位面数。这个成员变量的值必须为 1 。
None.gif
None.gifbiBitCount:指定每个象素的位数。其值必须为 1 、 4 、 8 或 24 。
None.gif
None.gifbiCompression:指定压缩位图的压缩类型。在 24 位格式中,该变量被设置为 0 。
None.gif
None.gifbiSizeImage:指定图像的大小(以字节为单位)。如果位图的格式是 BI_RGB,则将此成员变量设置为 0 是有效的。
None.gif
None.gifbiXPelsPerMeter:为位图指定目标设备的水平分辨率(以“象素 / 米”为单位)。应用程序可用该值从最符合当前设备特征的资源群组中选择一个位图。
None.gif
None.gifbiYPelsPerMeter:为位图指定目标设备的垂直分辨率(以“象素 / 米”为单位)。
None.gif
None.gifbiClrUsed:指定位图实际所用的颜色表中的颜色索引数。如果 biBitCount 设为 24 ,则 biClrUsed 指定用来优化 Windows 调色板性能的参考颜色表。
None.gif
None.gifbiClrImportant:指定对位图的显示有重要影响的颜色索引数。如果此值为 0 ,则所有颜色都很重要。
None.gif现在已定义了创建图像所需的全部信息。
None.gif
None.gif第 3 部分:图像
None.gif在 24 位格式中,图像中的每个象素都由存储为 BRG 的三字节 RGB 序列表示。每个扫描行都被补足到 4 位。为了使这个过程稍复杂一点,图像是自底而上存储的,即第一个扫描行是图像中的最后一个扫描行。下图显示了标头 (BITMAPHEADER) 和 (BITMAPINFOHEADER) 以及部分图像。各个部分由垂线分隔:
None.gif
None.gif 0000000000 4D42 B536 0002 0000 0000 0036 0000 | 0028
None.gif 0000000020 0000 0107 0000 00E0 0000 0001 0018 0000
None.gif 0000000040 0000 B500 0002 0EC4 0000 0EC4 0000 0000
None.gif 0000000060 0000 0000 0000 | FFFF FFFF FFFF FFFF FFFF
None.gif 0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
None.gif *
None.gif
None.gif
None.gif现在,我们开始检视代码
None.gif现在我们已经知道了 24 位位图文件的结构,下面就是您期待已久的内容:用来将图像对象写入位图文件的代码。
None.gif
None.gif import java.awt. * ;
None.gif import java.io. * ;
None.gif import java.awt.image. * ;
None.gif
ExpandedBlockStart.gifContractedBlock.gif public class BMPFile extends Component dot.gif {
InBlock.gif
InBlock.gif//--- 私有常量
InBlock.gif private final static int BITMAPFILEHEADER_SIZE = 14;
InBlock.gifprivate final static int BITMAPINFOHEADER_SIZE = 40;
InBlock.gif
InBlock.gif//--- 私有变量声明
InBlock.gif
InBlock.gif//--- 位图文件标头
InBlock.gif private byte bitmapFileHeader [] = new byte [14];
ExpandedSubBlockStart.gifContractedSubBlock.gifprivate byte bfType [] = dot.gif{'B', 'M'};
InBlock.gifprivate int bfSize = 0;
InBlock.gifprivate int bfReserved1 = 0;
InBlock.gifprivate int bfReserved2 = 0;
InBlock.gifprivate int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;
InBlock.gif
InBlock.gif//--- 位图信息标头
InBlock.gif private byte bitmapInfoHeader [] = new byte [40];
InBlock.gifprivate int biSize = BITMAPINFOHEADER_SIZE;
InBlock.gifprivate int biWidth = 0;
InBlock.gifprivate int biHeight = 0;
InBlock.gifprivate int biPlanes = 1;
InBlock.gifprivate int biBitCount = 24;
InBlock.gifprivate int biCompression = 0;
InBlock.gifprivate int biSizeImage = 0x030000;
InBlock.gifprivate int biXPelsPerMeter = 0x0;
InBlock.gifprivate int biYPelsPerMeter = 0x0;
InBlock.gifprivate int biClrUsed = 0;
InBlock.gifprivate int biClrImportant = 0;
InBlock.gif
InBlock.gif//--- 位图原始数据
InBlock.gif private int bitmap [];
InBlock.gif
InBlock.gif//--- 文件部分
InBlock.gif private FileOutputStream fo;
InBlock.gif
InBlock.gif//--- 缺省构造函数
ExpandedSubBlockStart.gifContractedSubBlock.gif public BMPFile() dot.gif{
InBlock.gif
ExpandedSubBlockEnd.gif }

InBlock.gif
InBlock.gif
InBlock.gifpublic void saveBitmap (String parFilename, Image parImage, int
ExpandedSubBlockStart.gifContractedSubBlock.gifparWidth, int parHeight) dot.gif{
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.giftry dot.gif{
InBlock.gif fo = new FileOutputStream (parFilename);
InBlock.gif save (parImage, parWidth, parHeight);
InBlock.gif fo.close ();
ExpandedSubBlockEnd.gif }

ExpandedSubBlockStart.gifContractedSubBlock.gifcatch (Exception saveEx) dot.gif{
InBlock.gif saveEx.printStackTrace ();
ExpandedSubBlockEnd.gif }

InBlock.gif
ExpandedSubBlockEnd.gif }

InBlock.gif
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif/**//*
InBlock.gif * saveMethod 是该进程的主方法。该方法
InBlock.gif * 将调用 convertImage 方法以将内存图像转换为
InBlock.gif * 字节数组;writeBitmapFileHeader 方法创建并写入
InBlock.gif * 位图文件标头;writeBitmapInfoHeader 创建
InBlock.gif * 信息标头;writeBitmap 写入图像。
InBlock.gif *
ExpandedSubBlockEnd.gif*/

ExpandedSubBlockStart.gifContractedSubBlock.gifprivate void save (Image parImage, int parWidth, int parHeight) dot.gif{
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.giftry dot.gif{
InBlock.gif convertImage (parImage, parWidth, parHeight);
InBlock.gif writeBitmapFileHeader ();
InBlock.gif writeBitmapInfoHeader ();
InBlock.gif writeBitmap ();
ExpandedSubBlockEnd.gif }

ExpandedSubBlockStart.gifContractedSubBlock.gifcatch (Exception saveEx) dot.gif{
InBlock.gif saveEx.printStackTrace ();
ExpandedSubBlockEnd.gif }

ExpandedSubBlockEnd.gif }

InBlock.gif
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif/**//*
InBlock.gif * convertImage 将内存图像转换为位图格式 (BRG)。
InBlock.gif * 它还计算位图信息标头所用的某些信息。
InBlock.gif *
ExpandedSubBlockEnd.gif*/

ExpandedSubBlockStart.gifContractedSubBlock.gifprivate boolean convertImage (Image parImage, int parWidth, int parHeight) dot.gif{
InBlock.gif
InBlock.gifint pad;
InBlock.gif bitmap = new int [parWidth * parHeight];
InBlock.gif
InBlock.gif PixelGrabber pg = new PixelGrabber (parImage, 0, 0, parWidth, parHeight,
InBlock.gif bitmap, 0, parWidth);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.giftry dot.gif{
InBlock.gif pg.grabPixels ();
ExpandedSubBlockEnd.gif }

ExpandedSubBlockStart.gifContractedSubBlock.gifcatch (InterruptedException e) dot.gif{
InBlock.gif e.printStackTrace ();
InBlock.gifreturn (false);
ExpandedSubBlockEnd.gif }

InBlock.gif
InBlock.gif pad = (4 - ((parWidth * 3) % 4)) * parHeight;
InBlock.gif biSizeImage = ((parWidth * parHeight) * 3) + pad;
InBlock.gif bfSize = biSizeImage + BITMAPFILEHEADER_SIZE +
InBlock.gifBITMAPINFOHEADER_SIZE;
InBlock.gif biWidth = parWidth;
InBlock.gif biHeight = parHeight;
InBlock.gif
InBlock.gifreturn (true);
ExpandedSubBlockEnd.gif }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif/**//*
InBlock.gif * writeBitmap 将象素捕获器返回的图像转换为
InBlock.gif * 所需的格式。请记住:扫描行在位图文件中是
InBlock.gif * 反向存储的!
InBlock.gif *
InBlock.gif * 每个扫描行必须补足为 4 个字节。
ExpandedSubBlockEnd.gif*/

ExpandedSubBlockStart.gifContractedSubBlock.gifprivate void writeBitmap () dot.gif{
InBlock.gif
InBlock.gifint size;
InBlock.gifint value;
InBlock.gifint j;
InBlock.gifint i;
InBlock.gifint rowCount;
InBlock.gifint rowIndex;
InBlock.gifint lastRowIndex;
InBlock.gifint pad;
InBlock.gifint padCount;
InBlock.gifbyte rgb [] = new byte [3];
InBlock.gif
InBlock.gif
InBlock.gif size = (biWidth * biHeight) - 1;
InBlock.gif pad = 4 - ((biWidth * 3) % 4);
InBlock.gifif (pad == 4) // <==== 错误修正
InBlock.gif pad = 0; // <==== 错误修正
InBlock.gif rowCount = 1;
InBlock.gif padCount = 0;
InBlock.gif rowIndex = size - biWidth;
InBlock.gif lastRowIndex = rowIndex;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.giftry dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.giffor (j = 0; j < size; j++) dot.gif{
InBlock.gif value = bitmap [rowIndex];
InBlock.gif rgb [

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/21107256/viewspace-1018760/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/21107256/viewspace-1018760/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值