Javaのヒント60:ビットマップファイルをJavaで保存する

このヒントは、Javaアプリケーションにビットマップファイルをロードするプロセスを示したJavaヒント43を補完するものです。今月は、画像を24ビットビットマップファイルに保存する方法と、画像オブジェクトからビットマップファイルを書き込むために使用できるコードスニップについてのチュートリアルをフォローアップします。

Microsoft Windows環境で作業している場合、ビットマップファイルを作成する機能は多くの扉を開きます。たとえば、前回のプロジェクトでは、JavaをMicrosoftAccessとインターフェイスさせる必要がありました。Javaプログラムにより、ユーザーは画面に地図を描くことができました。その後、マップはMicrosoftAccessレポートに印刷されました。JavaはOLEをサポートしていないため、私の唯一の解決策は、マップのビットマップファイルを作成し、MicrosoftAccessレポートにそれを取得する場所を指示することでした。画像をクリップボードに送信するアプリケーションを作成する必要があった場合、特にこの情報が別のWindowsアプリケーションに渡される場合は、このヒントが役立つ場合があります。

ビットマップファイルの形式

ビットマップファイル形式は、4ビットRLE(ランレングスエンコーディング)、および8ビットおよび24ビットエンコーディングをサポートします。24ビット形式のみを扱っているので、ファイルの構造を見てみましょう。

ビットマップファイルは3つのセクションに分かれています。以下にそれらをレイアウトしました。

セクション1:ビットマップファイルヘッダー

このヘッダーには、ビットマップファイルのタイプサイズとレイアウトに関する情報が含まれています。構造は次のとおりです(C言語の構造定義から取得)。

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

上記のリストのコード要素の説明は次のとおりです。

  • bfType:ファイルのタイプを示し、常にBMに設定されます。
  • bfSize:ファイル全体のサイズをバイト単位で指定します。
  • bfReserved1:予約済み--0に設定する必要があります。
  • bfReserved2:予約済み--0に設定する必要があります。
  • bfOffBitsBitmapFileHeader画像の先頭から先頭までのバイトオフセットを指定します。

ここで、ビットマップヘッダーの目的がビットマップファイルを識別することであることがわかりました。ビットマップファイルを読み取るすべてのプログラムは、ファイル検証にビットマップヘッダーを使用します。

セクション2:ビットマップ情報ヘッダー

情報ヘッダーと呼ばれる次のヘッダーには、画像自体のすべてのプロパティが含まれています。

Windows 3.0(またはそれ以降)のデバイスに依存しないビットマップ(DIB)のサイズと色形式に関する情報を指定する方法は次のとおりです。

typedef struct tagBITMAPINFOHEADER {DWORD biSize; LONG biWidth; 長いbiHeight; WORD複葉機; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; 長いbiXPelsPerMeter; 長いbiYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;

上記のコードリストの各要素について、以下で説明します。

  • biSizeBITMAPINFOHEADER構造体に必要なバイト数を指定します。
  • 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として保存された一連の3バイトの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ビットビットマップファイルの構造についてすべて理解できたので、これがあなたが待ち望んでいたことです。画像オブジェクトからビットマップファイルを書き込むコードです。

importjava.awt。*;インポートjava.io. *; importjava.awt.image。*; public class BMPFile extends Component {// ---プライベート定数privatefinal static int BITMAPFILEHEADER_SIZE = 14; private final static int BITMAPINFOHEADER_SIZE = 40; // ---プライベート変数宣言// ---ビットマップファイルヘッダープライベートバイトbitmapFileHeader [] =新しいバイト[14];プライベートバイトbfType [] = {'B'、 'M'}; private int bfSize = 0; private int bfReserved1 = 0; private int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // ---ビットマップ情報ヘッダープライベートバイトbitmapInfoHeader [] =新しいバイト[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; // ---ビットマップ生データprivateintビットマップ[]; // ---ファイルセクションprivateFileOutputStream fo; // ---デフォルトコンストラクタpublicBMPFile(){} public void saveBitmap(String parFilename、Image parImage、int parWidth、int parHeight){try {fo = new FileOutputStream(parFilename);保存(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;ビットマップ= new int [parWidth * parHeight]; PixelGrabber pg =新しいPixelGrabber(parImage、0、0、parWidth、parHeight、ビットマップ、0、parWidth); {pg.grabPixels();を試してください。 } catch(InterruptedException e){e.printStackTrace();戻り値(false); } pad =(4-((parWidth * 3)%4))* parHeight; biSizeImage =((parWidth * parHeight)* 3)+パッド;bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; biWidth = parWidth; biHeight = parHeight;戻り値(true); } / * * writeBitmapは、ピクセルグラバーから返された画像を*必要な形式に変換します。覚えておいてください:スキャンラインはビットマップファイルで反転されます*! * *各スキャンラインは、偶数の4バイト境界までパディングする必要があります。 * / private void writeBitmap(){int size; int値; int j; int i; int rowCount; int rowIndex; int lastRowIndex; intパッド; int padCount;バイトrgb [] =新しいバイト[3];サイズ=(biWidth * biHeight)-1;パッド= 4-((biWidth * 3)%4); if(pad == 4)// <====バグ修正パッド= 0; // <====バグ修正rowCount = 1; padCount = 0; rowIndex = size-biWidth; lastRowIndex = rowIndex; {for(j = 0; j> 8)&0xFF);を試してください。 rgb [2] =(バイト)((値>> 16)&0xFF); fo.write(rgb);if(rowCount == biWidth){padCount + = pad; for(i = 1; i> 8)&0x00FF); return(retValue); } / * * * intToDWordはintをダブルワードに変換し、戻り値は4バイト配列に格納されます。 * * /プライベートバイト[] intToDWord(int parValue){バイトretValue [] =新しいバイト[4]; retValue [0] =(バイト)(parValue&0x00FF); retValue [1] =(バイト)((parValue >> 8)&0x000000FF); retValue [2] =(バイト)((parValue >> 16)&0x000000FF); retValue [3] =(バイト)((parValue >> 24)&0x000000FF); return(retValue); }}0x00FF); retValue [1] =(バイト)((parValue >> 8)&0x000000FF); retValue [2] =(バイト)((parValue >> 16)&0x000000FF); retValue [3] =(バイト)((parValue >> 24)&0x000000FF); return(retValue); }}0x00FF); retValue [1] =(バイト)((parValue >> 8)&0x000000FF); retValue [2] =(バイト)((parValue >> 16)&0x000000FF); retValue [3] =(バイト)((parValue >> 24)&0x000000FF); return(retValue); }}

結論

これですべてです。JDK 1.1.6以降、Javaは一般的な形式での画像の保存をサポートしていないため、このクラスが非常に役立つと確信しています。JDK 1.2は、JPEGイメージの作成をサポートしますが、ビットマップはサポートしません。したがって、このクラスはJDK1.2のギャップを埋めます。

このクラスで遊んで、それを改善する方法を見つけたら、私に知らせてください!私の電子メールは私の経歴とともに以下に表示されます。

Jean-PierreDubéは独立したJavaコンサルタントです。彼は1988年に登録されたInfocomを設立しました。それ以来、Infocomは製造、ドキュメント管理、大規模な電力線管理に至るまで、いくつかのカスタムアプリケーションを開発してきました。彼は、C、Visual Basic、そして最近ではJavaで幅広いプログラミング経験があり、現在は彼の会社で使用されている主要言語です。Infocomの最近のプロジェクトの1つは、まもなくベータリリースとして利用可能になる予定のダイアグラムAPIです。

このストーリー「Javaのヒント60:ビットマップファイルをJavaに保存する」は、もともとJavaWorldによって公開されました。