当前位置: 首页 > 图灵资讯 > 技术篇> 用 Java 保存位图文件

用 Java 保存位图文件

来源:图灵教育
时间:2024-03-03 10:41:17

用 Java 保存位图文件 Jean-Pierre Dubé·jdeveloper

摘要   虽然 Java 它提供了几种打开图像的机制,但保存图像并不是它的强项。本技能将讨论如何保存图像 24 位置图文件。此外,Jean-Pierre 还提供了将图像文件写入位图文件所需的所有代码。

  这篇技巧是 "在 Java 在应用程序中加载位图文件的渐进指南” 那个技巧解释了补充 Java 在应用程序中加载位图文件的过程。本月,我将提供另一个教程,解释如何保存图像 24 在位位图文件中,还包括将图像对象写入位图文件的代码片断。

  如果您在 Microsoft Windows 在环境中工作,创建位图文件的功能将为您提供许多便利。举例来说,在我的最后一个项目中,我必须这样做 Java 与 Microsoft Access 对接。Java 允许用户在屏幕上绘图。这张照片随后打印出来 Microsoft Access 报表中。由于 Java 不支持 OLE,我唯一的选择就是创建一个位图文件,并通知它 Microsoft Access 这个位图文件在哪里可以找到报表?如果你写了一个向剪贴板发送图像的应用程序,这个技能可能对你有用 -- 特别是当您将此信息传递给另一个应用程序时。

  位图文件的格式

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

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

  第 1 部分:位图文件标头

  标头包含位图文件的类型、大小和布局信息。结构如下(摘自) C 语言结构定义):

 typedef struct tagBITMAPFILEHEADER {   UINT bfType;   DWORD bfSize;   UINT bfreserved1;   UINT bfreserved2;   DWORD bfOffBits;  }BITMAPFILEHEADER;

 以下是本列表中代码元素的说明:

  bfType:指定文件类型的值始终为 BM。

  bfSize:指定整个文件的大小(以字节为单位)。

  bfreserved1:保留 -- 必须为 0。

  Reserved2:保留 -- 必须为 0。

  bfOffBits:指定从 BitmapFileHeader 到图像首部的字节偏移量。

  现在你已经明白了位图标头的目的是识别位图文件。读取位图文件的每个程序都使用位图标头进行文件验证。

  第 2 部分:位图信息标头

  随后的标头称为信息标头,其中包含图像本身的属性。

  如何指定下面的说明 Windows 3.0(或更高版本)设备独立位图 (DIB) 尺寸及颜色格式:

  typedef struct tagBITMAPINFOHEADER {    DWORD biSize;    LONG biWidth;    LONG biHeight;    WORD biPlanes;    WORD biBitCount;    DWORD biCompression;    DWORD biSizeImage;    LONG biXPelsPerMeter;    LONG biYPelsPerMeter;    DWORD biClrUsed;    DWORD biClrImportant;  } BITMAPINFOHEADER;

     上述代码列表中的每个元素描述如下:

   biSize:指定 BITMAPINFOHEADER 结构所需的字节数。

   biWidth:指定位图的宽度(以象素为单位)。

   biHeight:指定位图的高度(以象素为单位)。

   biPlanes:指定目标设备的位数。该成员的变量值必须为 1。

   biBitCount:指定每个象素的位数。其值必须是 1、4、8 或 24。

   biCompression:指定压缩位图的压缩类型。在 24 该变量设置在位置格式中 0。

   biSizeImage:指定图像的大小(以字节为单位)。如果位图的格式是 BI_RGB,将成员变量设置为 0 是有效的。

   biXPelsPerMeter:指定目标设备的水平分辨率(以“象素/米”为单位)。应用程序可以从最符合当前设备特征的资源组中选择一个位图。

   biYPelsPerMeter:指定目标设备的垂直分辨率(以“象素/米”为单位)。

   biClrUsed:指定位图实际使用的颜色表中的颜色索引数。如果 biBitCount 设为 24,则 biClrUsed 指定用于优化 Windows 调色板性能参考颜色表。

   biClrImportant:颜色索引数对指定对位图的显示有重要影响。如果这个值是 所有的颜色都很重要。

  创建图像所需的所有信息都已定义。

  第 3 部分:图像

  在 24 图像中的每一个象素都存储在位格式中 BRG 的三字节 RGB 序列表示。每一个扫描行都得到了补充 4 位。为了使这个过程稍微复杂一点,图像自底而上存储,即第一个扫描线是图像中的最后一个扫描线。标头显示在下图中 (BITMAPHEADER) 和 (BITMAPINFOHEADER) 还有一些图像。各部分由垂线分隔:

0000000000 4D42 B536 0002 0000 0000 0036 0000 | 00280000000020 0000 0107 0000 00E0 0000 0001 0018 00000000000040 0000 B500 0002 0EC4 0000 0EC4 0000 00000000000060 0000 0000 0000 | FFFF FFFF FFFF FFFF FFFF0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF*

  现在,我们开始检查代码

  现在我们已经知道了 24 位图文件的结构,以下是您期待已久的内容:用于将图像对象写入位图文件的代码。 import java.awt.*;import java.io.*;import java.awt.image.*;

public class BMPFile extends Component {

file://--- 私有privatetet私有常量 final static int BITMAPFILEHEADER_SIZE = 14;private final static int BITMAPINFOHEADER_SIZE = 40;

file://--- 私有变量声明

file://--- 位图文件标头privatet byte bitmapFileHeader [] = new byte [14];private byte bfType [] = {'B', 'M'};private int bfSize = 0;private int bfreserved1 = 0;private int bfreserved2 = 0;private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;

file://--- 位图信息标头privatet byte bitmapInfoHeader [] = new byte [40];private int biSize = BITMAPINFOHEADER_SIZE;private int biWidth = 0;private int biHeight = 0;private int biPlanes = 1;private int biBitCount = 24;private int biCompression = 0;private int biSizeImage = 0x030000;private int biXPelsPerMeter = 0x0;private int biYPelsPerMeter = 0x0;private int biClrUsed = 0;private int biClrImportant = 0;

file://--- 原始数据privatete的位图 int bitmap [];

file://--- privatetet文件部分 FileOutputStream fo;

file://--- 缺乏构造函数publicic BMPFile() {

}

public void saveBitmap (String parFilename, Image parImage, intparWidth, int parHeight) {

try {fo = new FileOutputStream (parFilename);save (parImage, parWidth, parHeight);fo.close (); }catch (Exception saveEx) {saveEx.printStackTrace ();}

}

/** saveMethod 是这个过程的主要方法。该方法* 将调用 convertImage 该方法将内存图像转换为* 字节数组;writeBitmapFileHeader 创建和写入方法* 位图文件标头;writeBitmapInfoHeader 创建 * 信息标头;writeBitmap 写入图像。**/private void save (Image parImage, int parWidth, int parHeight) {

try {convertImage (parImage, parWidth, parHeight);writeBitmapFileHeader ();writeBitmapInfoHeader ();writeBitmap ();}catch (Exception saveEx) {saveEx.printStackTrace ();}}

/** convertImage 将内存图像转换为位图格式 (BRG)。* 它还计算位图信息标头中使用的一些信息。**/private boolean convertImage (Image parImage, int parWidth, int parHeight) {

int pad;bitmap = new int [parWidth * parHeight];

PixelGrabber pg = new PixelGrabber (parImage, 0, 0, parWidth, parHeight,bitmap, 0, parWidth);

try {pg.grabPixels ();}catch (InterruptedException e) {e.printStackTrace ();return (false);}

pad = (4 - ((parWidth * 3) % 4)) * parHeight;biSizeImage = ((parWidth * parHeight) * 3) + pad;bfSize = biSizeImage + BITMAPFILEHEADER_SIZE +BITMAPINFOHEADER_SIZE;biWidth = parWidth;biHeight = parHeight;

return (true);}

/** writeBitmap 将象素捕获器返回的图像转换为* 所需格式。请记住:扫描行在位图文件中* 反向存储!** 每一个扫描行必须补充 4 */字节。private void writeBitmap () {

int size;int value;int j;int i;int rowCount;int rowIndex;int lastRowIndex;int pad;int padCount;byte rgb [] = new byte [3];

size = (biWidth * biHeight) - 1;pad = 4 - ((biWidth * 3) % 4);if (pad == 4) // <==== 对pad进行错误修正 = 0; // <==== 修正rowcountt错误 = 1;padCount = 0;rowIndex = size - biWidth;lastRowIndex = rowIndex;

try {for (j = 0; j < size; j++) {value = bitmap [rowIndex];rgb [0] = (byte) (value & 0xFF);rgb [1] = (byte) ((value >> 8) & 0xFF);rgb [2] = (byte) ((value >> 16) & 0xFF);fo.write (rgb);if (rowCount == biWidth) {padCount += pad;for (i = 1; i <= pad; i++) {fo.write (0x00);}rowCount = 1;rowIndex = lastRowIndex - biWidth;lastRowIndex = rowIndex;}elserowCount++;rowIndex++;}

file://--- bfsizeee更新文件大小 += padCount - pad;biSizeImage += padCount - pad;}catch (Exception wb) {wb.printStackTrace ();}

}

/** writeBitmapFileHeader 将位图文件标头写入文件中**/private void writeBitmapFileHeader () {

try {fo.write (bfType);fo.write (intToDWord (bfSize));fo.write (intToWord (bfreserved1);fo.write (intToWord (bfReserved2);fo.write (intToDWord (bfOffBits));

}catch (Exception wbfh) {wbfh.printStackTrace ();}

}

/*** writeBitmapInfoHeader 将位图信息标头* 写入文件。**/

private void writeBitmapInfoHeader () {

try {fo.write (intToDWord (biSize));fo.write (intToDWord (biWidth));fo.write (intToDWord (biHeight));fo.write (intToWord (biPlanes));fo.write (intToWord (biBitCount));fo.write (intToDWord (biCompression));fo.write (intToDWord (biSizeImage));fo.write (intToDWord (biXPelsPerMeter));fo.write (intToDWord (biYPelsPerMeter));fo.write (intToDWord (biClrUsed));fo.write (intToDWord (biClrImportant));}catch (Exception wbih) {wbih.printStackTrace ();}

}

/*** intToWord 将整数转换为单词,返回值* 存储在双字节数组中。**/private byte [] intToWord (int parValue) {

byte retValue [] = new byte [2];

retValue [0] = (byte) (parValue & 0x00FF);retValue [1] = (byte) ((parValue >> 8) & 0x00FF);

return (retValue);

}

/*** intToDWord 将整数转换为双字,返回值* 存储在一个 4 字节数组中**/private byte [] intToDWord (int parValue) {

byte retValue [] = new byte [4];

retValue [0] = (byte) (parValue & 0x00FF);retValue [1] = (byte) ((parValue >> 8) & 0x000000FF);retValue [2] = (byte) ((parValue >> 16) & 0x000000FF);retValue [3] = (byte) ((parValue >> 24) & 0x000000FF);

return (retValue);

}

}

  小结

  这就是你要做的所有工作。我相信你会发现这个类很有用,因为到了 JDK 1.1.6 为止,Java 图像不支持以任何常用格式保存。JDK 1.2 将支持创建 JPEG 图像,但不支持创建位图。因此,这一类仍将被填写 JDK1.2 中的空白。