用 Java 儲存點陣圖檔案 (轉)

worldblog發表於2007-12-08
用 Java 儲存點陣圖檔案 (轉)[@more@]

  用 儲存點陣圖
  Jean-Pierre Dubé·jdeveloper

 

摘要
  雖然 Java 提供了幾種開啟影像的機制,但儲存影像並不是它的強項。這篇技巧將講述如何將影像儲存在 24 位位件中。另外,Jean-Pierre 還提供了將影像檔案寫入點陣圖檔案所需的全部程式碼。

  這篇技巧是 "在 Java 應用中載入點陣圖檔案的逐步指南" 的補充,那篇技巧說明了在 Java 應用程式中載入點陣圖檔案的過程。本月我再提供一篇教程,說明如何將影像儲存在 24 位點陣圖檔案中,其中還包含將影像寫入點陣圖檔案的程式碼片斷。

  如果您在 環境中工作,那麼建立點陣圖檔案的功能將為您提供許多方便。例如,在我的上一個專案中,我必須將 Java 與 Microsoft Access 對接。Java 程式允許在螢幕上繪圖。這幅圖隨後被列印到 Microsoft Access 報表中。由於 Java 不支援 OLE,我的唯一選擇就是建立該圖的一個點陣圖檔案,並通知 Microsoft Access 報表在何處能找到這個點陣圖檔案。如果您寫過向剪貼簿傳送影像的應用程式,則這個技巧可能對您有用 -- 尤其是當您將這個資訊傳遞給另一個應用程式時。

  點陣圖檔案的格式

  點陣圖檔案格式支援 4 位 RLE(行程長度編碼)以及 8 位和 24 位編碼。因為我們只處理 24 位格式,所以下面我們檢視一下該檔案的結構。

  點陣圖檔案分為三個部分。我已將它們列在下面。

  第 1 部分:點陣圖檔案的標頭

  標頭包含點陣圖檔案的型別大小資訊和版面資訊。結構如下(摘自 C 語言結構定義):

 
typedef struct tagBITMAPFILEHEADER {
   UINT bfType;
   D bfSize;
   UINT bfReserved1;
   UINT bfReserved2;
   DWORD bfOffBits;
  }BITMAPFILEHEADER;

 下面是對這個清單中的程式碼元素的說明:

  bfType:指定檔案型別,其值始終為 BM。

  bfSize:指定整個檔案的大小(以位元組為單位)。

  bfReserved1:保留 -- 必須為 0。

  bfReserved2:保留 -- 必須為 0。

  bfOffBits:指定從 BitmapFileHeader 到影像首部的位元組偏移量。

  現在您已經明白點陣圖標頭的用途就是標識點陣圖檔案。讀取點陣圖檔案的每個程式都使用點陣圖標頭來進行檔案驗證。

  第 2 部分:點陣圖資訊標頭

  隨後的標頭稱為資訊標頭,其中包含影像本身的屬性。

  下面說明如何指定 Windows 3.0(或更高版本)裝置獨立點陣圖 (DIB) 的大小和顏色格式:

  typedef struct tagBITMNFOHEADER {
    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:指定對點陣圖的顯示有重要影響的顏色索引數。如果此值為 0,則所有顏色都很重要。

  現在已定義了建立影像所需的全部資訊。

  第 3 部分:影像

  在 24 位格式中,影像中的每個象素都由為 BRG 的三位元組 RGB 序列表示。每個掃描行都被補足到 4 位。為了使這個過程稍複雜一點,影像是自底而上儲存的,即第一個掃描行是影像中的最後一個掃描行。下圖顯示了標頭 (BITMAPHEADER) 和 (BITMAPINFOHEADER) 以及部分影像。各個部分由垂線分隔:

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
*

 

  現在,我們開始檢視程式碼

  現在我們已經知道了 24 位點陣圖檔案的結構,下面就是您期待已久的內容:用來將影像物件寫入點陣圖檔案的程式碼。
import java.awt.*;
import java.io.*;
import java.awt.image.*;

public class BMPFile extends Component {

私有常量
private final static int BITMAPFILEHEADER_SIZE = 14;
private final static int BITMAPINFOHEADER_SIZE = 40;

私有變數宣告

點陣圖檔案標頭
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;

點陣圖資訊標頭
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;

點陣圖原始資料
private int bitmap [];

檔案部分
private FileOutputStream fo;

預設構造
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 是該程式的主方法。該方法
* 將 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; // <==== 錯誤修正
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++;
}

檔案大小
bfSize += 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);

}

}
 

  小結

  這就是所要做的全部工作。我確信您將會發現這個類很有用,因為到 1.1.6 為止,Java 不支援用任何常用的格式儲存影像。JDK 1.2 將支援建立 JPEG 影像,但不支援建立點陣圖。所以這個類仍將填補 JDK1.2 中的空白。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-989743/,如需轉載,請註明出處,否則將追究法律責任。

相關文章