用 Java 保存位圖文件
發(fā)表時(shí)間:2023-07-30 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]用 Java 保存位圖文件5/23/2001 9:28:10· Jean-Pierre Dubé·jdeveloper-------------------------...
用 Java 保存位圖文件
5/23/2001 9:28:10· Jean-Pierre Dubé·jdeveloper
--------------------------------------------------------------------------------
1 2下一頁
摘要
雖然 Java 提供了幾種打開圖像的機(jī)制,但保存圖像并不是它的強(qiáng)項(xiàng)。這篇技巧將講述如何將圖像保存在 24 位位圖文件中。另外,Jean-Pierre 還提供了將圖像文件寫入位圖文件所需的全部代碼。
這篇技巧是 "在 Java 應(yīng)用程序中加載位圖文件的逐步指南" 的補(bǔ)充,那篇技巧說明了在 Java 應(yīng)用程序中加載位圖文件的過程。本月我再提供一篇教程,說明如何將圖像保存在 24 位位圖文件中,其中還包含將圖像對象寫入位圖文件的代碼片斷。
如果您在 Microsoft Windows 環(huán)境中工作,那么創(chuàng)建位圖文件的功能將為您提供許多方便。例如,在我的上一個(gè)項(xiàng)目中,我必須將 Java 與 Microsoft Access 對接。Java 程序允許用戶在屏幕上繪圖。這幅圖隨后被打印到 Microsoft Access 報(bào)表中。由于 Java 不支持 OLE,我的唯一選擇就是創(chuàng)建該圖的一個(gè)位圖文件,并通知 Microsoft Access 報(bào)表在何處能找到這個(gè)位圖文件。如果您寫過向剪貼板發(fā)送圖像的應(yīng)用程序,則這個(gè)技巧可能對您有用 -- 尤其是當(dāng)您將這個(gè)信息傳遞給另一個(gè)應(yīng)用程序時(shí)。
位圖文件的格式
位圖文件格式支持 4 位 RLE(行程長度編碼)以及 8 位和 24 位編碼。因?yàn)槲覀冎惶幚?24 位格式,所以下面我們查看一下該文件的結(jié)構(gòu)。
位圖文件分為三個(gè)部分。我已將它們列在下面。
第 1 部分:位圖文件的標(biāo)頭
標(biāo)頭包含位圖文件的類型大小信息和版面信息。結(jié)構(gòu)如下(摘自 C 語言結(jié)構(gòu)定義):
typedef struct tagBITMAPFILEHEADER {
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
}BITMAPFILEHEADER;
下面是對這個(gè)清單中的代碼元素的說明:
bfType:指定文件類型,其值始終為 BM。
bfSize:指定整個(gè)文件的大。ㄒ宰止(jié)為單位)。
bfReserved1:保留 -- 必須為 0。
bfReserved2:保留 -- 必須為 0。
bfOffBits:指定從 BitmapFileHeader 到圖像首部的字節(jié)偏移量。
現(xiàn)在您已經(jīng)明白位圖標(biāo)頭的用途就是標(biāo)識(shí)位圖文件。讀取位圖文件的每個(gè)程序都使用位圖標(biāo)頭來進(jìn)行文件驗(yàn)證。
第 2 部分:位圖信息標(biāo)頭
隨后的標(biāo)頭稱為信息標(biāo)頭,其中包含圖像本身的屬性。
下面說明如何指定 Windows 3.0(或更高版本)設(shè)備獨(dú)立位圖 (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;
以上代碼清單的每個(gè)元素說明如下:
biSize:指定 BITMAPINFOHEADER 結(jié)構(gòu)所需的字節(jié)數(shù)。
biWidth:指定位圖的寬度(以象素為單位)。
biHeight:指定位圖的高度(以象素為單位)。
biPlanes:指定目標(biāo)設(shè)備的位面數(shù)。這個(gè)成員變量的值必須為 1。
biBitCount:指定每個(gè)象素的位數(shù)。其值必須為 1、4、8 或 24。
biCompression:指定壓縮位圖的壓縮類型。在 24 位格式中,該變量被設(shè)置為 0。
biSizeImage:指定圖像的大。ㄒ宰止(jié)為單位)。如果位圖的格式是 BI_RGB,則將此成員變量設(shè)置為 0 是有效的。
biXPelsPerMeter:為位圖指定目標(biāo)設(shè)備的水平分辨率(以“象素/米”為單位)。應(yīng)用程序可用該值從最符合當(dāng)前設(shè)備特征的資源群組中選擇一個(gè)位圖。
biYPelsPerMeter:為位圖指定目標(biāo)設(shè)備的垂直分辨率(以“象素/米”為單位)。
biClrUsed:指定位圖實(shí)際所用的顏色表中的顏色索引數(shù)。如果 biBitCount 設(shè)為 24,則 biClrUsed 指定用來優(yōu)化 Windows 調(diào)色板性能的參考顏色表。
biClrImportant:指定對位圖的顯示有重要影響的顏色索引數(shù)。如果此值為 0,則所有顏色都很重要。
現(xiàn)在已定義了創(chuàng)建圖像所需的全部信息。
第 3 部分:圖像
在 24 位格式中,圖像中的每個(gè)象素都由存儲(chǔ)為 BRG 的三字節(jié) RGB 序列表示。每個(gè)掃描行都被補(bǔ)足到 4 位。為了使這個(gè)過程稍復(fù)雜一點(diǎn),圖像是自底而上存儲(chǔ)的,即第一個(gè)掃描行是圖像中的最后一個(gè)掃描行。下圖顯示了標(biāo)頭 (BITMAPHEADER) 和 (BITMAPINFOHEADER) 以及部分圖像。各個(gè)部分由垂線分隔:
0000000000 4D42 B536 0002 0000 0000 0036 0000 0028
0000000020 0000 0107 0000 00E0 0000 0001 0018 0000
0000000040 0000 B500 0002 0EC4 0000 0EC4 0000 0000
0000000060 0000 0000 0000 FFFF FFFF FFFF FFFF FFFF
0000000100 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
*
在,我們開始檢視代碼
現(xiàn)在我們已經(jīng)知道了 24 位位圖文件的結(jié)構(gòu),下面就是您期待已久的內(nèi)容:用來將圖像對象寫入位圖文件的代碼。
import java.awt.*;
import java.io.*;
import java.awt.image.*;
public class BMPFile extends Component {
file://--- 私有常量
private final static int BITMAPFILEHEADER_SIZE = 14;
private final static int BITMAPINFOHEADER_SIZE = 40;
file://--- 私有變量聲明
file://--- 位圖文件標(biāo)頭
private 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://--- 位圖信息標(biāo)頭
private 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://--- 位圖原始數(shù)據(jù)
private int bitmap [];
file://--- 文件部分
private FileOutputStream fo;
file://--- 缺省構(gòu)造函數(shù)
public BMPFile() {
}
public void saveBitmap (String parFilename, Image parImage, int
parWidth, int parHeight) {
try {
fo = new FileOutputStream (parFilename);
save (parImage, parWidth, parHeight);
fo.close ();
}
catch (Exception saveEx) {
saveEx.printStackTrace ();
}
}
/*
* saveMethod 是該進(jìn)程的主方法。該方法
* 將調(diào)用 convertImage 方法以將內(nèi)存圖像轉(zhuǎn)換為
* 字節(jié)數(shù)組;writeBitmapFileHeader 方法創(chuàng)建并寫入
* 位圖文件標(biāo)頭;writeBitmapInfoHeader 創(chuàng)建
* 信息標(biāo)頭;writeBitmap 寫入圖像。
*
*/
private void save (Image parImage, int parWidth, int parHeight) {
try {
convertImage (parImage, parWidth, parHeight);
writeBitmapFileHeader ();
writeBitmapInfoHeader ();
writeBitmap ();
}
catch (Exception saveEx) {
saveEx.printStackTrace ();
}
}
/*
* convertImage 將內(nèi)存圖像轉(zhuǎn)換為位圖格式 (BRG)。
* 它還計(jì)算位圖信息標(biāo)頭所用的某些信息。
*
*/
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 將象素捕獲器返回的圖像轉(zhuǎn)換為
* 所需的格式。請記。簰呙栊性谖粓D文件中是
* 反向存儲(chǔ)的!
*
* 每個(gè)掃描行必須補(bǔ)足為 4 個(gè)字節(jié)。
*/
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) // <==== 錯(cuò)誤修正
pad = 0; // <==== 錯(cuò)誤修正
rowCount = 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;
}
else
rowCount++;
rowIndex++;
}
file://--- 更新文件大小
bfSize += padCount - pad;
biSizeImage += padCount - pad;
}
catch (Exception wb) {
wb.printStackTrace ();
}
}
/*
* writeBitmapFileHeader 將位圖文件標(biāo)頭寫入文件中。
*
*/
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 將位圖信息標(biāo)頭
* 寫入文件中。
*
*/
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 將整數(shù)轉(zhuǎn)換為單字,返回值
* 存儲(chǔ)在一個(gè)雙字節(jié)數(shù)組中。
*
*/
private byte [] intToWord (int parValue) {
byte retValue [] = new byte [2];
retValue [0] = (byte) (parValue & 0x00FF);
retValue [1] = (byte) ((parValue >> 8) & 0x00FF);
return (retValue);
}
/*
*
* intToDWord 將整數(shù)轉(zhuǎn)換為雙字,返回值
* 存儲(chǔ)在一個(gè) 4 字節(jié)數(shù)組中。
*
*/
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);
}
}
小結(jié)
這就是所要做的全部工作。我確信您將會(huì)發(fā)現(xiàn)這個(gè)類很有用,因?yàn)榈?JDK 1.1.6 為止,Java 不支持用任何常用的格式保存圖像。JDK 1.2 將支持創(chuàng)建 JPEG 圖像,但不支持創(chuàng)建位圖。所以這個(gè)類仍將填補(bǔ) JDK1.2 中的空白。