|
Image I/O |
||
画像ファイルのロードとセーブなら |
||
Java でのグラフィック環境は AWT の頃はむちゃくちゃ不便というか、全然機能がありませんでした。たとえば、線を書くにしても太さを変えることはできません。しょうがないので、1 dot づつずらして複数回書くなどということを行っていました。 そんな不便さも、Java 2 で導入された Java 2D を使えば過去のものになりました。その当時、Java 2 SDK に付属している Java 2D のデモを見て、驚いたものです。 しかし、それだけでは終わりませんでした。その後、グラフィックを強化させるために Java Adavance Imaging (JAI) が JCP の JSR 34 で策定されています。JAI はプルモデルを採用したグラフィックライブラリで、とても興味深いのですが、書籍や雑誌などで取り上げられたことがほとんどないためマイナーな存在になってしまっています。機会があれば、ぜひ取上げてみたいと思っています。 さて、今回取り上げる Image I/O はもともとは、この JAI の一部だったのですが、それが独立して JSR 15 になったという経緯を持ちます。なぜ、番号が Image I/O の方が若いのかは謎です ^^;; そして、Image I/O は本家を差し置いて J2SE v1.4 に標準バンドルされてしまったのです。 Image I/O は I/O という言葉が表しているように、イメージのロードとセーブを行うための API です。ロードとセーブを行うには画像ファイルのフォーマットに応じたエンコード、デコード処理も含まれます。 Image I/O の特徴の 1 つにプラガビリティがあります。ある画像ファイルフォーマットを扱うためには、そのフォーマットを扱えるプラグインが必要になります。しかし、ユーザはそれを意識することなく使用することができます。 標準で提供されているプラグインは、イメージのロードが JPEG, PNG, GIF、セーブが JPEG, PNG になっています。GIF のセーブはライセンスの問題があるのでサポートされていないようです。その他のフォーマットを扱う場合にはプラグインを自作する必要があります。 ただ、プラグインを書こうと思われる方は非常に小数だと思うので、今回は扱わないことにします。ということで、イメージのロードとセーブに的を絞って解説していきたいと思います。
|
なにはともロードとセーブをしてみる |
||||||||||||||||||||
とりあえず、なにも考えずに画像ファイルを読みこんでみましょう。 ImageIOTest1 では、ある画像ファイルを読みこんで、それをフレームで表示するというサンプルアプリケーションです。
Image I/O で中心になるのは javax.imageio.ImageIO クラスです。このクラスだけで画像ファイルのロード/セーブが行えます。ImageIOTest1 はこの ImageIO クラスだけを使用して、イメージのロードを行っています。 ImageIOTest1 はたかだか 40 行ぐらいのプログラムなので全文を示しておきます。
全文は示したのですが、この中で Image I/O を使用しているのは、たったの 1 行です。それは 15 行目の ImageIO.read(f); という部分です。 ImageIO クラスは前述したように Image I/O クラスの中心となるクラスです。Image I/O の static メソッドの read メソッドは引数の違いにより次のように 4 種類あります。戻り値はすべて java.awt.image.BufferedImage オブジェクトとなります。
ImageIOTest1 では 1 の引数が File オブジェクトのものを使用しましたが、それ以外にストリームと URL が使用できます。2 番目の javax.imageio.stream.ImageInputStream は Image I/O で提供されている画像用のストリームです。URL が使えるということはネットワーク上にある画像ファイルも扱うことができるということです。 read メソッドはまずファイルの先頭部分を読み込んで、そのファイルがどのような画像フォーマットか調べます。そして、その画像フォーマットに応じたプラグインを利用してイメージをロードします。このため、拡張子が異なっていても、正確にロードを行うことができるのです。 29 行目からの initFrame は読みこんだイメージを表示しているだけです。 ところで、ここまで見てきて、今までの画像ファイルの取り扱いとそんなに変わっていないと思われる方も多いと思います。 今まではアプレットであれば java.applet.Applet#loadImage メソッド、それ以外なら java.awt.Toolkit#getImage メソッド、もしくは javax.swing.ImageIcon クラスを使用されると思います。 さて、何がちがうのでしょう。 たいして変わりません ^^;; あえて違いをあげるとすれば、Applet#loadImage メソッドや Toolkit#getImage メソッドはいつ画像のロードが終わったのかは java.awt.MediaTracker を使用しないと分からないという点ぐらいです。ImageIcon クラスは表面上は MediaTracker を使用していないようですが、内部で MediaTracker を使用しています。 Image I/O の read メソッドは画像ファイルを最後まで読みこむまでブロッキングされます。ですから、read メソッドの次の行からロードしたイメージを使用することができます。 ここまでではありがたみが分からないのでイメージのセーブをしてみましょう。 今まで、イメージをセーブするには com.sun.image.codec.jpeg パッケージのクラス群を使用することができましたが、パッケージ名が表すように Java の標準的な API ではありませんでした。また、JPEG 以外の画像ファイルフォーマットも扱うこともできませんでした。 そこで、Image I/O の登場です。
ImageIOTest2 は引数を 2 つとり、最初の引数で示された画像ファイルを第 2 引数で示されたファイルにセーブするアプリケーションです。ImageIOTest2 でイメージのセーブを行っているコンストラクタとユーティリティメソッドの getExtension メソッドを次に示します。
7 行目でイメージのロードを行ったら、それを 8 行目でセーブしています。write メソッドの第 1 引数は java.awt.image.RenderedImage オブジェクト、第 2 引数が画像ファイルフォーマット、第 3 引数がセーブするファイルになります。read メソッドと同様に、write メソッドも出力先として java.io.File オブジェクト、java.io.OutputStream オブジェクト、javax.imageio.stream.ImageOutputStream オブジェクトを指定することができます。 第 1 引数の RenderedImage インタフェースはあまりなじみがないかもしれませんが、ビットマップイメージを扱うためのインタフェースです。BufferedImage クラスは RenderedImage インタフェースを実装しているので、ImageIO クラスの write メソッドで使用することができます。 第 2 引数の画像ファイルフォーマットは JPEG であれば "JPG"、"JPEG", "jpg", "jpeg" のどれかになります。同じように PNG であれば "PNG" もしくは "png" です。 ImageIOTest2 クラスではフォーマットにはファイルの拡張子を利用しています。ファイルの拡張子を抽出しているのが getExtension メソッドです。ただ、単に拡張子を使っているだけで、その拡張子に応じた画像ファイルフォーマットを使用できるかどうかは調べていません。 どのような画像ファイルフォーマットを扱うことができるかを調べるためのメソッドも ImageIO クラスには用意されています。それを使ってみたのが ImageIOTest3 です。
ここでは扱うことができる画像ファイルフォーマットを調べるために 4 種類のメソッドを使用しています。
これを実行してみましょう。
この結果から標準でロードできるのは JPEG, PNG, GIF の 3 種類のフォーマット、セーブできるのは JPEG と PNG だということがお分かりになると思います。 このように ImageIO クラスを使えば、ほとんど何も考えずにイメージのロード・セーブができるのですが、簡単にできるということは逆にいえば痒いところには手が届かないわけです。そこで、次からはもう少し痒いところに手が届くような使い方をみていきましょう。
|
イメージのロード |
|||||
イメージをロードするには javax.imageio.ImageReader クラスを使用します。さっそく、このクラスを使用したサンプルを見てみましょう。
イメージをロードしている部分を次に示します。
ImageReader オブジェクトを生成するには ImageIO クラスのファクトリメソッドを使用します。ImageIO クラスの read メソッドでは画像のフォーマットを自動的に調べてくれましたが、ImageReader は画像ファイルフォーマットに 1 対 1 に対応しているクラス (実際には ImageReader クラスの派生クラスが画像フォーマットと対応します) なので明示的に画像ファイルのフォーマットを指定しなくてはなりません。フォーマットを指定するにはフォーマットの名前か、MIME タイプで行います。 フォーマット名を使用するには public Iterator getImageReadersByFormatName(String type) MIME で指定する場合は public Iterator getImageReadersByFormatName(String mimeType) を使用します。どちらのメソッドも戻り値は Iterator オブジェクトになります。これはその画像フォーマットを扱うことのできるプラグインが複数あるかもしれないからです。 そこで 4 行目からの while ループで Iterator オブジェクトから 1 つづつ ImageReader オブジェクトを取り出して使用するようにします。 もし、画像フォーマットを扱えない場合は、空の Iterator オブジェクトが返されます (null が返されるわけではありません)。 ImageReader オブジェクトを取得できたら、次に ImageReader オブジェクトへの入力を指定します。入力には ImageIO クラスの read でも使用できた javax.imageio.stream.ImageInputStream クラスを使用します。ImageInputStream オブジェクトは 8 行目のように ImageIO クラスの createImageInputStream メソッドを使用して生成します。このメソッドは引数が Object オブジェクトなのですが、通常は java.io.File, java.io.RandomAcessFile, java.io.InputStream などのオブジェクトを使用することができます。 ImageInputStream は実際にはインタフェースで、プラグインを追加することによって他の入力媒体を扱うことができるようになります。Image I/O はこんなところもプラガブルになっているわけです。 生成した ImageInputStream オブジェクトを ImageReader オブジェクトに設定するために 9 行目の setInput メソッドを使用します。そして、11 行目で示したように read メソッドを使用してイメージをロードします。 画像フォーマットによれば、複数の画像を 1 つのファイルに保持することも可能です。例えば、Animated GIF などが複数の画像をまとめることができます。read メソッドの引数は、そんな複数の画像を持つ入力に対して、何枚目の画像をロードするかを指定します。 ImageReader オブジェクトを使用しおえたら、最後に ImageReader#dispose メソッドをコールして ImageReader オブジェクトが使用したリソースを解放させるのを忘れないようにしましょう。 read メソッドの戻り値は先ほどと同じように BufferedImage オブジェクトです。
|
画像ファイルの情報とサムネイル |
||||||||||||||||||||||||||||||||||||
単に画像をロードすることができたのですが、ImageReader クラスではイメージのロードを行わずに画像ファイルの情報を得ることができます。通常、画像ファイルにはヘッダと呼ばれるイメージの情報を保持した部分があります。そこの部分だけを読み込むことで、イメージのロードをせずに情報だけ取得することができるのです。
ImageReader クラスで取得できる画像情報には次のようなものがあります。
これらの情報の中で画像の枚数だけはヘッダだけだと取得できない場合があります。例えば、Animated GIF はファイルをすべて読み込まないと画像の枚数が分かりません。このため、getNumImages メソッドにはファイルの最後までサーチするかどうかを示すフラグを引数として与えるようにします。 ただし、ファイルが順方向にアクセスできないような場合、最後までサーチするように指定しても IllegalStateException が発生します。順方向にアクセスしかできないかどうかは isSeekForwardOnly メソッドを使用して調べることができます。また、入力を設定する setInput メソッドで順方向アクセスのみかどうかを指定することができます。 さて、これらのメソッドを使用してみたのが ImageReaderTest2 です。
画像フォーマットによってはサムネイルをつけられるものがありますが、ImageReader クラスではサムネイルを扱うこともできます。サムネイルは readThumbnail メソッドを使用してロードできます。また、同じようにタイルも扱うことができ、こちらは readTile メソッドを使用してロードできます。 ImageReaderTest2 ではサムネイルがある場合にはサムネイルのロードも行っています。
1 枚の画像に複数のサムネイルが含まれることがあるので、サムネイルの表示には 3 行目に示すように JLabel の配列を使用してみました。その後、枚数分だけループを繰り返し、5 行目の readThumbnail メソッドでサムネイルのロードを行っています。readThumbnail メソッドの第 1 引数がイメージのインデックス、第 2 引数が第 1 引数で示されるイメージのサムネイルのインデックスになります。 読み込んだサムネイルはパネルに並べて (9 〜 12 行目)、ダイアログで表示しています (14, 15 行目)。 実際に複数のイメージやサムネイルを扱えるかどうかはプラグイン次第です。標準で提供されている GIF のプラグインは複数イメージが扱えるようです。
|
条件付きのイメージのロード | |||||||||||||||
普通のイメージのロードではなく、イメージの一部分だけロードしたり、縮小イメージのロードを行ったりすることも ImageReader クラスを使えば可能です。 画像ビューアなどのアプリケーションでは、普通はサムネイルだけ表示されており、イメージを指定するとオリジナルのイメージを表示するものが多くあります。このときにサムネイルだけ表示するのに、イメージをロードしてから縮小イメージを作成し、ロードしたオリジナルイメージは保持しておくにはメモリの消費が大きいので捨ててしまうということがよく行われています。そして、オリジナルの表示の時には画像を再ロードします。 しかし、この方法だとロードと縮小イメージを作るコストはバカになりません。それだったらはじめから縮小イメージをロードするようにすればメモリの使用量も減ります。 そこで登場するのが ImageReadParam クラスです。これを利用してイメージのロードを行ったのが ImageReaderTest3.java になります。
ImageReadParam クラスはイメージのロードのいろいろなパラメータを設定するために使用します。
ImageReadParam クラスのインスタンス化は new でできますが、実際には画像フォーマットに応じた ImageReadParam クラスの派生クラスを使用するために ImageReader クラスの getDefaultReadParam メソッドを使用するほうが望ましいようです。11 行目で ImageReadParam オブジェクトをこの方法で取得しています。 今回は、パラメータとして読み込む範囲 (13 行目)、生成するイメージのオフセット (14 行目)、間引きロードの条件 (15 行目) を使用してみました。 SourceRegion はイメージの一部だけを読み込むときに使用します。13 行目では座標でいうと (200, 200) から (400, 400) の縦 200 pixel 横 200 pixel の部分を読み込むように設定しています。 DestinationOffset は生成するイメージのオフセットです。14 行目は (50, 0) から画像の表示を行うようにオフセットを使用しています。オフセットされた部分は黒になります。 15 行目が縮小イメージをロードするための、間引きロードです。setSourceSubsampling メソッドの第 1 引数が x 軸方向の間引き量、第 2 引数が y 軸方向の間引き量、第 3 引数が x 軸方向の間引きのオフセット、第 4 引数が y 軸方向のオフセットになります。15 行目では x 軸方向には 4 pixel に 1 pixel だけロードを行い、そのときのオフセットは 2 になります。y 軸方向は 2 pixel に 1 pixel 間引きし、そのオフセットは 1です。 ようするに、ロードする pixel は x 軸方向には (2, 1), (6, 1), (10, 1) となり、y 軸方向では (2, 1), (2, 3), (2, 5) のようになり、縦長のイメージになってしまいます。 ちなみに setSourceSubsampling の引数が 1, 1, 0, 0 であれば、間引きを行わないオリジナルの画像をロードできます。 パラメータを設定したら 17 行目のように、read メソッドに引数として渡すことで、パラメータに応じたロードを行います。 さて、条件付でロードができるようになったので、イメージをサブサンプリングでロードするのと、通常のイメージをロードして縮小するのではどのくらいパフォーマンスが異なるか測ってみましょう。
イメージの縮小にはいろいろな方法があるのですが、ImageReaderTest4 では BufferedImage オブジェクトに対するイメージ処理を行う java.awt.image.BufferedImageOp インタフェースを使用してみました。BufferedImageOp はインタフェースなので、実際にはイメージ処理に応じた実装クラスを使用します。イメージのスケーリングを行うには java.awt.image.AffineTransformOp クラスを使用します (RescaleOp というクラスもありますが、こちらは色のスケーリングです)。
6 行目でイメージのロードを行っているのは今までと同じです。その後、ロードしたイメージの縮小サイズの BufferedImage オブジェクトを生成します。イメージのタイプ (BufferedImage.TYPE_INT_RBG など) が異なっているとスケーリングができないので注意が必要です。 次に 10 行目で AffineTransformOp オブジェクトを生成しています。AffineTransformOp オブジェクトを生成するには java.awt.geom.AffineTransform オブジェクトが必要なので、AffineTransform クラスの static メソッドでスケーリングを行うオブジェクトを生成できる getScaleInstance メソッドを使用しました。このメソッドは第 1 引数が x 軸方向のスケーリングファクター、第 2 引数が y 軸方向のスケーリングファクターになります。11 行目では x 軸、y 軸とも 1/10 にするようにしました。AffineTransformOp のコンストラクタの最後の引数はスケーリングの方式を指定するものです。null の場合はニアレストネイバーという補間アルゴリズムが使用されます。 そして、12 行目でロードしたイメージから縮小イメージを生成しています。filter メソッドの第 1 引数がオリジナルのイメージ、第 2 引数に処理が施されたイメージになります。 さて、実行してみましょう。筆者の環境は CPU が Athron 1.2 GHz, メモリが 512MB で、OS が Windows 2000 です。使用した画像ファイルは 600 万画素のデジカメの画像ファイルを使用してみました。
結果的にはサブサンプリングでロードしたものの方が半分ぐらいの時間で済んでいます。 どちらか一方だけを使用するようにしてメモリ使用量も調べてみました。メモリ使用量は Windows のタスクマネージャを使用して最大メモリ使用量を調べました。画像ファイルは先ほどのものと同じもので、画像ファイルのサイズは 2.52 MB です。
メモリ使用量も時間と同じように、サブサンプリングの方が約半分のメモリ量で済んでいます。 とはいうものの、縮小イメージを得るためのアルゴリズムにはいろいろあります。この中でも、サブサンプリングで行っている間引きは一番単純な方法になります。そのため、サムネイルに使用するためのイメージを生成するぐらいであればいいのですが、高度なイメージ処理を行うには適さないと思います。 実際、両方の方法で表示されたイメージを見ると少し色が異なっていたり、コントラストが違っているのが分かると思います。どちらがいいかは一概にはいえないので、適材適所で使う手法を変えるのがいいのでしょう。
|
画像のロードに関するイベント | |||||||
イメージのロードを行っているとき、今どこまでロードできているかが知りたくなることはないですか。 従来であれば MediaTracker クラスを使って、ロードが完了したかどうかは知ることができました。しかし、今どのくらいまでロードが終わっているかを調べることはできませんでした。 しかし、例えばプログレスバーでどの程度までロードされているか表示できれば、ロード中であることだけ表示するよりも格段にユーザフレンドリーになると思いませんか。 こう書いてくれば Image I/O ではこれができると言っているも同じですね ^^;; 確かにできます。 どうやるかというと、Image I/O では画像のロードに関するイベントがあるのでこれを利用して行います。とはいうものの、実際にはリスナーはあるのですが、イベントクラスは定義されていません。なぜなんでしょう。とても不思議です。
ImageReader クラスでは使用できるのは
の 3 種類があります。ImageReaderTest4 では進行度合いを利用して、ロードがどのくらい進んだかをプログレスバーで表示するようにしてみました。 リスナーの登録は addIIOReadXXXXXListener メソッドを使用します。進行度合いが addIIOReadProgressListener メソッド、アップデートが addIIOReadUpdateListener メソッド、ワーニングが addIIOReadWarningListener になります。 ImageReaderTest4 では IIOReadProgressListener メソッドを使用しています。
ReadProgressDialog クラスが IIOReadProgressListener インタフェースを実装しているクラスです。IIOReadProgressListener インタフェースには 9 種類のメソッドが定義されています。例えば、進行度合いが進むとコールされるのが imageProgress メソッドで、第 2 引数がパーセンテージになります。 ReadProgressDialog クラスは状態を表示する JTextField オブジェクトと進行度合いを表示する JProgressBar オブジェクトがダイアログに配置されています。先ほどの imageProgress メソッドは ReadProgressDialog クラスでは次のようになっています。
パーセンテージをそのままプログレスバーに設定しているだけです ^^; ImageReaderTest4 を実行すると下図のようなダイアログが表示されるはずです。あまり小さい画像ファイルをロードしてもあっという間に終わってしまうので、なるべく大きいイメージのロードをしてみてください。 |
イメージのセーブ |
|||||
ロードの次はセーブです。セーブもロードと同じように行うことができます。 イメージのセーブは Reader が Writer になっただけで、javax.imageio.ImageWriter クラスを使用します。使い方も ImageReader に似ています。
イメージのセーブは writeImage メソッドで行っています。
ImageWriter オブジェクトの生成も ImageReader と同じように ImageIO クラスのファクトリメソッドを使用します。やはり、画像ファイルフォーマットを指定して行います。フォーマットのしても ImageReader の時と同じでフォーマットの名前か、MIME タイプです。 フォーマット名 : public Iterator getImageWritersByFormatName(String type) MIME : public Iterator getImageWritersByFormatName(String mimeType) これらの戻り値は Iterator オブジェクトなので、4 行目からの while ループで ImageWriter オブジェクトを取り出します。もし、画像フォーマットを扱えない時は、ImageReader クラスの時と同様に空の Iterator オブジェクトになります。ImageWriter オブジェクトを取得できたら、出力を指定します。出力は javax.imageio.stream.ImageOutputStream クラスを使用します。 ImageOutputStream オブジェクトを ImageWriter オブジェクトに設定しているのが 9 行目の setOutput メソッドです。最後に、11 行目の write メソッドを使用して引数で指定されたイメージのセーブを行います。 ImageWriter クラスは複数のイメージをセーブする機能もあるのですが、標準で提供されている PNG のプラグインは複数イメージを扱えません。JPEG は複数イメージはないのでこれでいいのですが、PNG で複数イメージを扱えないのは痛いです。 結局、サポートされていないのも同然なので、ここでは複数イメージのセーブの説明は割愛させていただきます。 最後に、ImageReader クラスと同様に dispose メソッドを用いて、ImageWriter オブジェクトが使用したリソースを解放させます。
|
サムネイルも含めたセーブ |
|||||
次はサムネイルもセーブしてみましょう。
サムネイルをセーブするには、イメージとサムネイルとイメージの情報 (メタデータ) を一緒に扱うためのクラス IIOImage を使用します。
IIOImage クラスのコンストラクタの第 1 引数がイメージ、第 2 引数がサムネイル、第 3 引数がメタデータになります。 ロードのところで説明したように、1 つのイメージに複数のサムネイルをつけることができます。IIOImage クラスのコンストラクタでは複数のサムネイルを List オブジェクトで指定するようにします。 メタデータは今まで説明してきませんでしたが、イメージの情報を表しており、IIOMetadata クラスで表されます。IIOMetadata クラスはプロパティに XML の DOM オブジェクトを持っており、ここにイメージ情報を記述するようになっています。こうなっているのも、画像ファイルフォーマットによってメタデータの記述方が異なるからです。 IIOImage のコンストラクタの第 3 引数に null を指定すると、イメージをセーブするときにデフォルトのメタデータが使用されます。 IIOImage オブジェクトが生成できれば、後は write するだけです。
|
条件付きセーブ |
||||||||||||||||||||||
ImageReader クラスと同じ流れですが、ImageWriter クラスもセーブのパラメーターを使用することができます。このパラメーターには圧縮に関するパラメータなどが含まれています。どのようなパラメータがあるのか調べてみるのが ImageWriterTest3 です。
パラメータは画像ファイルフォーマットによって異なり、例えば圧縮やタイリングができるかどうかで設定できるものが異なります。いろいろあるので詳しくは ImageWriteParam クラスの JavaDoc を見ていただきたいと思います。 さて、実行してみましょう。ImageWriterTest3 は画像ファイルを指定するのではなく、画像ファイルフォーマットを指定します。
これを見ると JPEG は圧縮ができ、圧縮率はデフォルトで 0.75 になっていることが分かります。一方、PNG はプログレッシブだけサポートされています。 どのようなパラメータが使えるか分かったところで、実際に設定してみましょう。
ImageWriterTest4 は圧縮率を変更できるようにしてみました。実行時の第 3 引数に圧縮率を指定して実行します。圧縮率を設定している部分を次に示します。
赤字の部分が圧縮率を設定している部分です。そのすぐ前の if 文で圧縮が可能かどうかチェックして、可能であれば圧縮率を設定するようにしています。 まず、setCompressionMode メソッドで圧縮を実行するかどうか、また圧縮を実行するならばその条件をどのように指定するかを設定します。このメソッドでは 4 つのモードを指定することができます。
setCompressionMode メソッドで指定しないと、MODE_COPY_FROM_METADATA がデフォルトで使用されます。 ここでは MODE_EXPLICIT を使用して ImageWriteParam オブジェクトで設定するようにしました。次の行で圧縮率を設定しています。圧縮率の設定には setCompressionQuality メソッドを使用します。引数は float で 0 から 1 の値です。1 に近いほど圧縮率が低くなり、高画質になります。 実際に実行してみましょう。第 3 引数が圧縮率になります。例として、サンフランシスコのアルカトラズ島の写真を示しておきます。圧縮率が 0.8 のものはオリジナルとそれほど変わらないのですが、圧縮率 0.2 だとかなりブロックノイズが出てしまっています。
|
イメージのセーブに関するイベント |
||||||
イメージをセーブするときにも、ロードと同様にイベントを使用することができます。しかし、やはりロードの時と同様にリスナーはありますが、イベントクラスは定義されていません。このイベントを使用してセーブの進行度を表わすようにしたのが ImageWriterTest5 クラスです。
ImageWriter クラスではロードの時とは異なりイメージの更新に関するリスナはありません。したがって、
の 2 種類のリスナが使用できます。リスナーの登録はそれぞれ addIIOWriteProgressListener メソッド、addIIOReadWarning メソッドです。 それでは、ロードのときと同じように進行度合いをプログレスバーで表示してみましょう。使用するのは IIOWriteProgressListener インタフェースなので次のようになります。
赤字の部分でリスナを登録しています。WriteProgressDialog クラスが IIOWriteProgressListener インタフェースを実装しているクラスです。IIOReadProgressListener インタフェースには 7 種類のメソッドが定義されています。 WriteProgressDialog クラスはダイアログの初期化などはロードのイベントのサンプルで使用した ReadProgressDialog クラスと同様で、イベントの処理もほとんど同じです。たとえば、進行度イベントでコールされる imageProgress メソッドは次のようになります。
ロードの時と同様に、プログレスバーに進行度のパーセンテージをセットしています。 ImageWriterTest5 は実行すると ImageReaderTest4 と同様にラベルとプログレスバーが描画されているダイアログが表示されるはずです。プログレスバーが進行度を表わしています。画像本体のセーブだけでなく、サムネールのセーブの進行度も表わせるようになっています。
|
Applet や Java Web Start で使うには |
|||||||||||
そういえば、Applet や Java Web Start で Image I/O を単純に使おうとするとセキュリティで引っかかります。というのも、Image I/O ではテンポラリのキャッシュをローカルファイルに作るからです。もし、Applet などで Image I/O を使う場合は、ImageIO クラスの setUseCache メソッドで false をセットするようにします。 と書きましたが、J2SE v1.4.1 beta ではローカルのキャッシュを使わなくなったようです。v1.4.0 と v1.4.1 の両方を試せる方がいらっしゃったら、次のアプレットを試してみてください。v1.4.1 では両方とも動作することが確認できると思います。
Java Web Start の JNLP ファイルや Java Plug-in の object タグもしくは embed タグだと JRE のバージョンを記述できるのでいいのですが、HTML の applet タグだと JRE のバージョンを記述できないのでキャッシュは使わないようにしておいたほうがいいかもしれません。その前に v1.4 が入れていない人の方が多いかもしれませんが....
|
おまけ |
||
Image I/O の登場以前は、イメージのロードは java.awt.Toolkit クラスの createImage メソッドか getImage メソッドを使用するか、com.sun.image.codec.jpeg パッケージのクラスを使用しなければなりませんでした。Toolkit クラスを使用する場合はいいのですが、com.sun.image.codec.jpeg パッケージは com.sun のパッケージなのでいつ消えてなくなるか分かりません (J2SE v1.4.1 ではまだあるようです。イメージのセーブはコアライブラリではない拡張ライブラリの JIMI などを使えば可能でしたが、やはりコアにないと使いにくい場合が多いのも確かです。 Image I/O がコアライブラリに入ったので、心おきなくロードとセーブが行えます。しかし、今のところプラグインが少ない & 提供されているプラグインでも機能が十分でないという気がかりなところもあります。今後、だんだんと充実されていくことを望んでいます。えっ、自分で書け、そりゃごもっともでございます。 今回使用したサンプルはここからダウンロードできます。 参考 URL
(Aug. 2002) |
|