ImageIOクラスで画像ファイルを読み書きする

広告

ここでは外部の画像ファイルなどをBufferedImageに読み込んで色々と加工してみます。

まずは画像の読み込みと書き込みから見ておきます。現在はImageIOクラスという便利なクラスがありますので、このクラスの使い方から見てみましょう。

まずjavax.imageio.ImageIOクラスのクラス図を見て下さい。

  • java.lang.Object
  • javax.imageio.ImageIO
  • public final class ImageIO extends Object

このクラスはfinalクラスです。コンストラクタはありません。いくつかのメソッドが用意されていますが、ファイルからの読み書きにはreadメソッドとwriteメソッドを使います。

とりあえずreadメソッドからです。

現在登録されているものの中から自動的に選択された 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メソッドです。

File に指定された形式をサポートする、任意の 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:
BMP
jpeg
bmp
wbmp
gif
JPG
png
jpg
JPEG
WBMP

WriterFormatNames:
BMP
bmp
jpeg
wbmp
png
JPG
PNG
jpg
WBMP
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);
}

ではサンプルプログラムを見て下さい。

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 Java2dTest extends JFrame{
  public static void main(String[] args){
    Java2dTest test = new Java2dTest();

    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("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メソッドを使って描画しています。

実行結果は下記のようになります。

p1

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);
}

ではサンプルプログラムを見てください。

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 Java2dTest extends JFrame{
  public static void main(String[] args){
    Java2dTest test = new Java2dTest();

    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("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オブジェクトに描画しています。

実行結果は下記のようになります。

p2

ちなみ画像ファイルが見つからないなどの理由によりBufferedImageを新規に作った場合は下記のような実行結果になりました。BufferedImageを新規に作った場合は背景が黒になるようです。

p2

ファイルへの書き出し

次に色々と加工した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 Java2dTest extends JFrame{
  public static void main(String[] args){
    Java2dTest test = new Java2dTest();

    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("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("sample2.png"));
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
}

上記を実行するとsample2.pngというファイルが出来ているはずです。それを開いてみた結果が下記となります。

p2

見ていただくと分かるとおり、先ほどまではフレームの大きさまでしか表示されていませんでしたが、実際には最初に読み込んだ画像の大きさと同じサイズのBufferedImageが作られていることがわかります。

( Written by Tatsuo Ikura )