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
メソッドで取得し、その名前に対応する拡張子を持った、実行ディレクトリのファイルを読み込み対象とします。ファイル名の一覧は、プルダウンリストで選択できます。
/** 読み込める画像形式の一覧から、読み込める画像の 一覧を生成し、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を導入しない限り、選択の余地はありません。
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つがあるようです。画像ファイル固有の表現形式では、構造やデータの名称が、画像ファイル形式の仕様に基づいています。標準形式の方は、画像ファイル形式の違いを吸収した、表現形式になっています。
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
メソッドで、画像が透過をどのようにサポートするかを知ることができます。
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が複数あった場合の選択基準や、指定されたファイル内に複数の画像が存在する場合にどれが読み込まれるかなどの挙動については、ドキュメントに記述がないので、注意が必要かも知れません。
FileFilter
インターフェイスを実装した、正規表現によるファイルフィルタRegexFileFilter
クラスを作って使っています。ファイルの中身を覗いて、通すかどうか決めるフィルタなど、複雑なフィルタリングは特殊な存在でしょう。多くの場合、ファイル名に着目してフィルタリングすると考えられます。
RegexFileFilter
は、ファイル名が、与えられた正規表現にマッチするかどうか判定します。使い回しが利くと思いますので、ぜひ、使ってみてください(と、言うほどのコード量でもありませんが)。