SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

JavaにおけるGIFファイルの取り扱い

Image I/OでGIF画像の読み込み・保存を行う


  • X ポスト
  • このエントリーをはてなブックマークに追加

Image I/OでGIFを読み込む

 Image I/Oの機能を確認するために、画像ファイルを読み書きするプログラム「GraphicsExplorer」を作成しました。どうせなら読み込むだけでなく、表示もできた方が良いだろうということで、レイアウトやコンポーネントを手軽に扱えるNetBeansで作ってみました。実行するのとソースを読むだけならば、NetBeansは必要ありません。

 NetBeansを使わずに実行する際には、「dist」ディレクトリの「GraphicsExplorer.jar」を実行することになりますが、実行時ディレクトリに「lib」ディレクトリが存在し、その中に「AbsoluteLayout.jar」と「swing-layout-1.0.jar」が入っていなければなりません。注意してください。

 GraphicsExplorerは、実行ディレクトリにある、認識できる拡張子を持った画像ファイルを、読み込んで表示することができます。同時に表示画像のメタ情報も表示します。

 GraphicsExplorerでは、まず、Image I/Oで読み込める画像形式の名前すべてをImageIO.getReaderFormatNamesメソッドで取得し、その名前に対応する拡張子を持った、実行ディレクトリのファイルを読み込み対象とします。ファイル名の一覧は、プルダウンリストで選択できます。

GraphicsExplorer#createFileList()
/** 読み込める画像形式の一覧から、読み込める画像の
    一覧を生成し、choiceFilenameに設定する */
private void createFileList() {
    String[] loadTypes = ImageIO.getReaderFormatNames();
    String regex = "(?i)^\\w*\\.(";
    for(String type : loadTypes)
        regex += "(" + type + ")|";
    regex = regex.substring(0, regex.length() - 1) + ")$";
    choiceFilename.removeAll();
    for(File graphic : 
        new File(".").listFiles(new RegexFileFilter(regex, false)))
        choiceFilename.add(graphic.getName());
    if(choiceFilename.getItemCount() > 0)
        getImageReaders(choiceFilename.getSelectedItem());
}

 選択された画像を読み込むためには、対象となる画像形式を読み込めるplugin(を表すImageReader型オブジェクト)のリストをImageIO.getImageReadersByFormatNameメソッドで取得し、その中の1つを選ぶことになります。画像形式の指定には画像ファイルの拡張子を使い、pluginの一覧はプルダウンリストで選択できるようになります。といっても、標準ライブラリに組み込まれているpluginは、1つの画像形式につき1つなので、外部のpluginを導入しない限り、選択の余地はありません。

GraphicsExplorer#getImageReaders(String filename)
HashMap<String, ImageReader> imageReaders = null;
/** 指定された型を保存できるImageReaderの一覧を取得する */
private void getImageReaders(String filename) {
    imageReaders = new HashMap<String, ImageReader>();
    Iterator<ImageReader> tmpReaders =
        ImageIO.getImageReadersByFormatName(
        filename.substring(filename.lastIndexOf('.') + 1));
    ImageReader imageReader = null;
    choiceIR.removeAll();
    while(tmpReaders.hasNext()) {
        imageReader = tmpReaders.next();
        imageReaders.put(
            imageReader.getClass().getName(), imageReader);
        choiceIR.add(imageReader.getClass().getName());
    }
    if(imageReaders.size() > 0)
        getImage(filename,
            imageReaders.get(choiceIR.getSelectedItem()));
}

 どのpluginを使って、どのファイルを読み込むかが決定したら、画像ファイルを読み込みます。pluginであるImageReader型オブジェクトに、ImageReader#setInputメソッドでファイルを指定し、読み込み準備をします。Image I/Oでは、1つの画像ファイル内に、複数の画像が含まれることを想定しているので、画像を読み込む際には、何番目の画像を読み込むかを指定する必要があります。ImageReader型オブジェクトのImageReader#readメソッドの引数に、読み込みたい画像番号を渡すことで、指定した画像の読み込みが行われます。

 また、ImageReader型オブジェクトでは、読み込む画像ファイルを指定したあとに、そのファイルに含まれるメタ情報を取り出すことができます。ImageReader#getStreamMetadataメソッドでファイルが持っているメタ情報を、#getImageMetadataメソッドで個々の画像が持っているメタ情報を、それぞれIIOMetadata型オブジェクトとして取り出すことができます。

 メタ情報の表現形式は複数用意されており、IIOMetadata#getMetadataFormatNamesメソッドで表現形式名一覧を取得でき、取得した表現形式の名前を引数にIIOMetadata#getAsTreeメソッドを呼び出すことで、指定した表現形式のメタ情報を得られます。基本的には、標準形式である「javax_imageio_1.0」と、画像ファイルの形式それぞれに固有な表現形式の2つがあるようです。画像ファイル固有の表現形式では、構造やデータの名称が、画像ファイル形式の仕様に基づいています。標準形式の方は、画像ファイル形式の違いを吸収した、表現形式になっています。

GraphicsExplorer#getImage(String filename, ImageReader imageReader)
Vector<BufferedImage> images = null;
Vector<IIOMetadata> imageMetadatas = null;
String metadata = "";
String streamMetadata = "";
/** 指定された画像を、指定されたImageReaderで読み込む */
private void getImage(String filename, ImageReader imageReader) {
    images = null;
    imageMetadatas = null;
    // 読み込むファイルを指定
    try {
        imageReader.setInput(
            ImageIO.createImageInputStream(new File(filename)));
    } catch (IOException e) {
        metaInfo.setText(filename+"を読み込めませんでした");
        return;
    }
    IIOMetadata md;
    streamMetadata = "";
    try {
        md = imageReader.getStreamMetadata();
    } catch (IOException e) {
        metaInfo.setText(filename+"のメタ情報を読み込めませんでした");
        return;
    }
    if(md != null) {
        String formats[] = md.getMetadataFormatNames();
        metadata =
            "streamMetadata--------------------------------------\n";
        for(String format : formats)
            makeMetadataXML(md.getAsTree(format));
        metadata +=
            "streamMetadata--------------------------------------\n";
        streamMetadata = metadata;
        metadata = "";
    }
    // 画像の取得
    images = new Vector<BufferedImage>();
    imageMetadatas = new Vector<IIOMetadata>();
    boolean isReaded = false;
    for(int i = 0; !isReaded; i++) {
        BufferedImage tmpImage = null;
        try {
            tmpImage = imageReader.read(i);
            imageMetadatas.add(i, imageReader.getImageMetadata(i));
        } catch(IndexOutOfBoundsException e) { isReaded = true;break;
        } catch(IOException e) { 
            metaInfo.setText(filename+"を読み込めませんでした");
            return;
        }
        images.add(tmpImage);
    }
    if(images.size() > 0) {
        spinnerGraphicsNumber.setModel(
            new SpinnerNumberModel(0, 0, images.size() - 1, 1));
        viewImage(0);
    }
}

 読み込まれた画像とメタ情報は、GraphicsExplorer#viewImageメソッドによって画面に反映されます。

 この時、読み込まれた画像の保持形式の情報を提供するため、BufferedImage#getTypeメソッドを使ってパレット形式のイメージタイプかどうかを判断します。パレット形式だった場合には、BufferedImage#getColorModelメソッドでIndexColorModel型オブジェクトを取得し、IndexColorModel#getRGBsメソッドでパレットを取得して表示します。また、image#getTransparencyメソッドで、画像が透過をどのようにサポートするかを知ることができます。

GraphicsExplorer#getPaletteMessage()
int transparentColor = 0;
private String getPaletteMessage() {
    IndexColorModel icm = (IndexColorModel)image.getColorModel();
    int[] palette = new int[icm.getMapSize()];
    icm.getRGBs(palette);
    String message = "";
    for(int i = 0; i < palette.length; i++) {
        if((palette[i] & 0xff000000) == 0)
            transparentColor = palette[i];
        StringBuilder builder = new StringBuilder();
        Formatter formatter = new Formatter(builder);
        formatter.format("\nNo.%3d : 0x%08x", i, palette[i]);
        message += formatter.out();
    }
    return message;
}

 GraphicsExplorerでは、明示的にpluginを指定することで、Image I/Oの仕組みが見えるような書き方をしています。もっと手軽なImage I/Oの利用方法は、ImageIOクラスのreadメソッドを使うことです。読み込む画像を指定するだけで、その画像形式を読み込めるpluginを自動的に選択し、画像の読み込みを行います。ただし、条件を満たすpluginが複数あった場合の選択基準や、指定されたファイル内に複数の画像が存在する場合にどれが読み込まれるかなどの挙動については、ドキュメントに記述がないので、注意が必要かも知れません。

ファイルフィルタ
 GraphicsExplorerでは、FileFilterインターフェイスを実装した、正規表現によるファイルフィルタRegexFileFilterクラスを作って使っています。
 ファイルの中身を覗いて、通すかどうか決めるフィルタなど、複雑なフィルタリングは特殊な存在でしょう。多くの場合、ファイル名に着目してフィルタリングすると考えられます。
 RegexFileFilterは、ファイル名が、与えられた正規表現にマッチするかどうか判定します。使い回しが利くと思いますので、ぜひ、使ってみてください(と、言うほどのコード量でもありませんが)。

次のページ
Image I/Oで画像を書き込む

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

長久 勝(ナガク マサル)

1972年1月10日生まれ。京都府出身。1994年龍谷大学理工学部数理情報学科卒業。ゲーム会社に入社。「ワンダースワンゲームプログラミング」「Javaゲームプログラミング」等を執筆。早稲田大学MNC非常勤講師。神楽坂酔っ払い研究所所長。ホームページはhttp://www6.plala.or.jp/mnagaku/。blog「mnagakuのx86_64地獄」も毎日更新中。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/497 2007/10/03 00:14

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング