|
|
BufferedImageの使い方 |
H.Kamifuji . |
ここでは、BufferedImageの使い方について、事例にて説明します。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
BufferedImageクラスについて色々調べてみたいと思います。BufferedImageクラスは簡単に言えばメモリ上に領域を作って、そこに画像を読み込んだり、物体を描画したりといったことを行う為のものかと思います。その結果をファイルに書き出したり、実際に表示したりといったこともできます。 まずクラス図から見てみましょう。 java.lang.Object ⇒ java.awt.Image ⇒ java.awt.image.BufferedImage public class BufferedImage extends Image implements WritableRenderedImage, Transparencyコンストラクタは下記の3つが用意されています。
この中で使い方が現在分かるのは2番目のものだけです。 BufferedImage public BufferedImage(int width, int height, int imageType)定義済みイメージ型の中の 1 つで BufferedImage を構築します。イメージのColorSpace は、デフォルトの sRGB 領域になります。 パラメータ: width - 作成されたイメージの幅 height - 作成されたイメージの高さ imageType - 作成されたイメージの形式 幅と高さ、そしてimageTypeを指定してBufferedImageを作成します。imageTypeは取りうる値が決まっていて、下記の中から1つ選択します。
色々な種類がありますが、透明度などを考えないでいいならBufferedImage.TYPE_INT_BGRを指定しておけばいいかと思います。 使い方としては下記のようになります。 BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_BGR); |
ここでは外部の画像ファイルなどをBufferedImageに読み込んで色々と加工してみます。 まずは画像の読み込みと書き込みから見ておきます。現在はImageIOクラスという便利なクラスがありますので、このクラスの使い方から見てみましょう。 まずjavax.imageio.ImageIOクラスのクラス図を見て下さい。 java.lang.Object ⇒ javax.imageio.ImageIO public final class ImageIO extends Objectこのクラスはfinalクラスです。コンストラクタはありません。いくつかのメソッドが用意されていますが、ファイルからの読み書きにはreadメソッドとwriteメソッドを使います。 とりあえずreadメソッドからです。 read public static BufferedImage read(File input) throws IOException現在登録されているものの中から自動的に選択された ImageReader を使用して、指定された File を復号化した結果として、BufferedImage を返します。File は ImageInputStream にラップされます。登録された ImageReader が、結果のストリームを読み込みできないような場合、null が返されます。 getUseCache および getCacheDirectory からの現在のキャッシュの設定は、作成された ImageInputStream のキャッシュの制御に使用します。 ファイル名として String をとる read メソッドは存在しないことに注意してください。かわりに、ファイル名から File を作成したあとにこのメソッドを使用します。 このメソッドは、File から直接読み取れる ImageReader を検索しません。この場合は、IIORegistry と ImageReaderSpi を使用します。 パラメータ: input - 読み込み元の File 戻り値: 復号化された入力内容を保持する BufferedImage または null 例外: IllegalArgumentException - input が null の場合 IOException - 読み込み中にエラーが発生した場合 readメソッドでは引数にFileクラスを指定することで、指定された画像ファイルを読み込みBufferedImageオブジェクトとして返してくれます。使い方としては下記のようになります。 BufferedImage readImage = null; try { readImage = ImageIO.read(new File("sample.png")); } catch (Exception e) { e.printStackTrace(); readImage = null; } 次にwriteメソッドです。 write public static boolean write(RenderedImage im, String formatName, File output) throws IOExceptionFile に指定された形式をサポートする、任意の ImageWriter を使用してイメージを書き込みます。すでに File が存在する場合、その内容は破棄されます。 パラメータ: im - 書き込まれる RenderedImage formatName - 形式の非公式な名前を保持する String output - 書き込まれる File 戻り値: 適切なライターが見つからない場合は false 例外: IllegalArgumentException - 任意のパラメータが null の場合 IOException - 書き込み中にエラーが発生した場合 writeメソッドではBufferedImageから画像ファイルを生成します。 ここで最初のパラメータで指定されているRenderedImageはインターフェースであり、このインターフェースを実装しているクラスがBufferedImageクラスとなります。ここにファイルに保存したいBufferedImageオブジェクトを指定します。 また2番目のパラメータで指定されているformatNameは画像ファイルのフォーマットを指定します。例えば"jpeg"とか"gif"とかです。 3番目のパラメータには出力する画像ファイルを表わすFileクラスを指定します。 使い方としては下記のようになります。 BufferedImage writeImage; ... boolean result = false; try { result = ImageIO.write(writeImage, "jpeg", new File("sample.jpeg")); } catch (Exception e) { e.printStackTrace(); result = false; } 利用可能なformatNameについてここで、現在利用できるフォーマットにどんなものがあるのか確認しておきましょう。getReaderFormatNamesメソッドとgetWriterFormatNamesメソッドを使うことで、現在利用できるフォーマットの種類を見ることができます。下記のサンプルを実行してみよう。 import javax.imageio.ImageIO; class FormatNamePrint { public static void main(String[] args) throws Exception { String[] formatNames = ImageIO.getReaderFormatNames(); System.out.println("ReaderFormatNames:"); for(int i = 0; i < formatNames.length; i++) { System.out.println(formatNames[i]); } formatNames = ImageIO.getWriterFormatNames(); System.out.println("WriterFormatNames:"); for(int i = 0; i < formatNames.length; i++) { System.out.println(formatNames[i]); } } }このサンプルプログラムは、読み込みと書き込みの双方で現在利用できるフォーマットを書き出します。実行すると下記のような結果となりました。 ReaderFormatNames: JPG jpg bmp BMP gif GIF WBMP png PNG wbmp jpeg JPEG WriterFormatNames: JPG jpg bmp BMP gif GIF WBMP png PNG wbmp jpeg JPEG以上の結果から、読み書き共にbmp/jpeg/wbmp/png/gifなどに対応しているようです。 背景に読み込んだ画像を表示させてみるではここで読み込んだ画像を背景のように表示させてみましょう。下記のような画像ファイルを用意しました。sample.png: ![]() では、この写真を読み込み背景として画面に描いて見ます。さらにその上に円などを描いてみました。 使い方としては下記のようになります。 Graphics2D g2 = (Graphics2D)g; BufferedImage readImage = null; try { readImage = ImageIO.read(new File("sample.png")); } catch (Exception e) { e.printStackTrace(); readImage = null; } if (readImage != null){ g2.drawImage(readImage, 0, 0, this); } 下記のサンプルを実行してみよう。 /** * ImageIOクラスで画像ファイルを読み書きする */ import java.awt.*; import java.awt.geom.*; import java.awt.event.*; import javax.swing.*; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import java.io.IOException; public class Java2dTest1 extends JFrame{ public static void main(String[] args){ Java2dTest1 test = new Java2dTest1(); test.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){System.exit(0);} }); test.setBounds( 0, 0, 200, 200); test.setVisible(true); } public void paint(Graphics g){ Graphics2D g2 = (Graphics2D)g; BufferedImage readImage = null; try { // readImage = ImageIO.read(new File("hana02.png")); readImage = ImageIO.read(new File("sample.png")); } catch (Exception e) { e.printStackTrace(); readImage = null; } if (readImage != null){ g2.drawImage(readImage, 0, 0, this); } g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); BasicStroke wideStroke = new BasicStroke(4.0f); g2.setStroke(wideStroke); g2.setPaint(Color.black); g2.draw(new Ellipse2D.Double(30, 40, 50, 50)); g2.setPaint(Color.blue); g2.draw(new Ellipse2D.Double(70, 40, 50, 50)); g2.setPaint(Color.red); g2.draw(new Ellipse2D.Double(110, 40, 50, 50)); g2.setPaint(Color.yellow); g2.fill(new Arc2D.Double(50, 100, 110, 110, 330, 100, Arc2D.PIE)); g2.setPaint(Color.gray); g2.draw(new Arc2D.Double(50, 100, 110, 110, 330, 100, Arc2D.PIE)); } }上記の例では、読み込んだ画像が含まれるBufferedImageオブジェクトをGraphics2Dクラス(正確にはGraphics2Dクラスの親クラスであるGraphicsクラスのメソッド)のdrawImageメソッドを使って描画しています。 実行結果は下記のようになります。 ![]() BufferedImageへの描画先ほどの例ではBufferedImageに読み込んだイメージを描画して、その後は普通に図形を描いていましたが、BufferedImageに直接図形の描画を行い、完了してから実際に表示させてみます。手順としてはBufferedImageからGraphics2Dオブジェクトを取得し、その取得したGraphics2Dに対して図形の描画を行います。最後にBufferedImageを本来のGraphics2Dに描画してやればO.K.です。 使い方としては下記のようになります。 BufferedImage readImage = null; if (readImage == null){ readImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_BGR); } Graphics2D off = readImage.createGraphics(); off.setPaint(Color.white); off.draw(new Ellipse2D.Double(30, 40, 50, 50)); if (readImage != null){ g2.drawImage(readImage, 0, 0, this); } 下記のサンプルを実行してみよう。 /** * BufferedImageへの描画 */ import java.awt.*; import java.awt.geom.*; import java.awt.event.*; import javax.swing.*; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import java.io.IOException; public class Java2dTest2 extends JFrame{ public static void main(String[] args){ Java2dTest2 test = new Java2dTest2(); test.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){System.exit(0);} }); test.setBounds( 0, 0, 200, 200); test.setVisible(true); } public void paint(Graphics g){ Graphics2D g2 = (Graphics2D)g; BufferedImage readImage = null; try { // readImage = ImageIO.read(new File("hana02.png")); readImage = ImageIO.read(new File("sample.png")); } catch (Exception e) { e.printStackTrace(); readImage = null; } if (readImage == null){ readImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_BGR); } Graphics2D off = readImage.createGraphics(); off.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); BasicStroke wideStroke = new BasicStroke(4.0f); off.setStroke(wideStroke); off.setPaint(Color.white); off.draw(new Ellipse2D.Double(30, 40, 50, 50)); off.setPaint(Color.blue); off.draw(new Ellipse2D.Double(70, 40, 50, 50)); off.setPaint(Color.red); off.draw(new Ellipse2D.Double(110, 40, 50, 50)); off.setPaint(Color.yellow); off.fill(new Arc2D.Double(50, 100, 110, 110, 330, 100, Arc2D.PIE)); off.setPaint(Color.gray); off.draw(new Arc2D.Double(50, 100, 110, 110, 330, 100, Arc2D.PIE)); if (readImage != null){ g2.drawImage(readImage, 0, 0, this); } } }上記の例では、読み込んだ画像が含まれるBufferedImageオブジェクトからGraphics2Dオブジェクトを取り出す、様々な処理を行ってから最後に元のGraphics2Dオブジェクトに描画しています。 実行結果は下記のようになります。 ![]() ちなみ画像ファイルが見つからないなどの理由によりBufferedImageを新規に作った場合は下記のような実行結果になりました。BufferedImageを新規に作った場合は背景が黒になるようです。 ![]() ファイルへの書き出し次に色々と加工したBufferedImageオブジェクトをファイルに画像ファイルとして書き出してみましょう。事前に説明したwriteメソッドを使います。ではサンプルプログラムを見てください。先ほどと基本的に同じですが、ファイルにも新しいファイル名で書き出すようにしています。 下記のサンプルを実行してみよう。 /** * ファイルへの書き出し */ import java.awt.*; import java.awt.geom.*; import java.awt.event.*; import javax.swing.*; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import java.io.IOException; public class Java2dTest3 extends JFrame{ public static void main(String[] args){ Java2dTest3 test = new Java2dTest3(); test.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){System.exit(0);} }); test.setBounds( 0, 0, 200, 200); test.setVisible(true); } public void paint(Graphics g){ Graphics2D g2 = (Graphics2D)g; BufferedImage readImage = null; try { // readImage = ImageIO.read(new File("hana03.png")); readImage = ImageIO.read(new File("sample.png")); } catch (Exception e) { e.printStackTrace(); readImage = null; } if (readImage == null){ readImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_BGR); } Graphics2D off = readImage.createGraphics(); off.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); BasicStroke wideStroke = new BasicStroke(4.0f); off.setStroke(wideStroke); off.setPaint(Color.white); off.draw(new Ellipse2D.Double(30, 40, 50, 50)); off.setPaint(Color.blue); off.draw(new Ellipse2D.Double(70, 40, 50, 50)); off.setPaint(Color.red); off.draw(new Ellipse2D.Double(110, 40, 50, 50)); off.setPaint(Color.yellow); off.fill(new Arc2D.Double(50, 100, 110, 110, 330, 100, Arc2D.PIE)); off.setPaint(Color.gray); off.draw(new Arc2D.Double(50, 100, 110, 110, 330, 100, Arc2D.PIE)); if (readImage != null){ g2.drawImage(readImage, 0, 0, this); } try { boolean result = ImageIO.write(readImage, "png", new File("sample3.png")); } catch (Exception e) { e.printStackTrace(); } } }上記を実行するとsample2.pngというファイルが出来ているはずです。それを開いてみた結果が下記となります。 ![]() 見ていただくと分かるとおり、先ほどまではフレームの大きさまでしか表示されていませんでしたが、実際には最初に読み込んだ画像の大きさと同じサイズのBufferedImageが作られていることがわかります。 |
|