用 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 中的空白。
![](/images/780-200-2.jpg)