目次 | 前へ | 次へ

4.4 書き込みプラグインの作成

MyFormatImageWriterSpi MyFormatImageWriterSpi クラスは、前のセクションで説明した MyFormatImageReaderSpi クラスと同様の役割を果たします。ただし、特定のストリームを読み込めるかどうかを判別するのではなく、メモリー内のイメージを書き込めるかどうかを判別しなければなりません。また、実際のイメージを入手する前に書き込みプラグインを選択できるようにするため、イメージそのものを調べる代わりに、ImageTypeSpecifier を使用します。
package com.mycompany.imageio;

import java.io.IOException;
import java.util.Locale;
import javax.imageio.ImageWriter;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream;

public class MyFormatImageWriterSpi extends ImageWriterSpi {

        static final String vendorName = "My Company";
        static final String version = "1.0_beta33_build9467";
        static final String writerClassName =
                "com.mycompany.imageio.MyFormatImageWriter";
        static final String[] names = { "myformat" };
        static final String[] suffixes = { "myf" };
        static final String[] MIMETypes = { "image/x-myformat" };
        static final String[] readerSpiNames = {
                "com.mycompany.imageio.MyFormatImageReaderSpi" };
    
        static final boolean supportsStandardStreamMetadataFormat = false;
        static final String nativeStreamMetadataFormatName = null;
        static final String nativeStreamMetadataFormatClassName = null;
        static final String[] extraStreamMetadataFormatNames = null;
        static final String[] extraStreamMetadataFormatClassNames = null;
        static final boolean supportsStandardImageMetadataFormat = false;
        static final String nativeImageMetadataFormatName =
                "com.mycompany.imageio.MyFormatMetadata_1.0";
        static final String nativeImageMetadataFormatClassName =
                "com.mycompany.imageio.MyFormatMetadata";
        static final String[] extraImageMetadataFormatNames = null;
        static final String[] extraImageMetadataFormatClassNames = null;
    
        public MyFormatImageWriterSpi() {
                super(vendorName, version,
                      names, suffixes, MIMETypes,
                      writerClassName,
                      STANDARD_OUTPUT_TYPE, // Write to ImageOutputStreams
                      readerSpiNames,
                      supportsStandardStreamMetadataFormat,
                      nativeStreamMetadataFormatName,
                      nativeStreamMetadataFormatClassName,
                      extraStreamMetadataFormatNames,
                      extraStreamMetadataFormatClassNames,
                      supportsStandardImageMetadataFormat,
                      nativeImageMetadataFormatName,
                      nativeImageMetadataFormatClassName,
                      extraImageMetadataFormatNames,
                      extraImageMetadataFormatClassNames);
        }

        public boolean canEncodeImage(ImageTypeSpecifier imageType) {
                int bands = imageType.getNumBands();
                return bands == 1 || bands == 3;
        }
    
        public String getDescription(Locale locale) {
                // Localize as appropriate
                return "Description goes here";
        }

        public ImageWriter createWriterInstance(Object extension) {
            return new MyFormatImageWriter(this);
        }
}
MyFormatImageWriter
package com.mycompany.imageio;

import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;

public class MyFormatImageWriter extends ImageWriter {

        ImageOutputStream stream = null;

        public MyFormatImageWriter(ImageWriterSpi originatingProvider) {
                super(originatingProvider);
        }
 
        public void setOutput(Object output) {
                super.setOutput(output);
                if (output != null) {
                        if (!(output instanceof ImageOutputStream)) {
                                throw new IllegalArgumentException
                                        ("output not an ImageOutputStream!");
                        }
                        this.stream = (ImageOutputStream)output;
                } else {
                        this.stream = null;
                }
        }

getDefaultWriteParam から返される ImageWriteParam は、書き込みプラグインの機能に基づいてカスタマイズしなければなりません。この書き込みプラグインは、タイリング、プログレシブ方式のエンコーディング、および圧縮をサポートしていないため、それぞれ false または null という値を渡します。
        // Tiling, progressive encoding, compression are disabled by default
        public ImageWriteParam getDefaultWriteParam() {
                return new ImageWriteParam(getLocale());
        }

このファイル形式は、イメージメタデータだけを取り扱います。convertImageMetadata メソッドは、ほとんど何も実行しませんが、必要なら、ほかのプラグインによって使用されるメタデータクラスを解釈するように定義することもできます。

  public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
                return null;
        }

        public IIOMetadata
                getDefaultImageMetadata(ImageTypeSpecifier imageType,
                                        ImageWriteParam param) {
                return new MyFormatMetadata();
        }

        public IIOMetadata convertStreamMetadata(IIOMetadata inData,
                                                 ImageWriteParam param) {
                return null;
        }

        public IIOMetadata convertImageMetadata(IIOMetadata inData,
                                                ImageTypeSpecifier imageType,
                                                ImageWriteParam param) {
                // We only understand our own metadata
                if (inData instanceof MyFormatMetadata) {
                        return inData;
                } else {
                        return null;
                }
        }

実際にイメージを書き込むには、まず、ソース領域、ソースバンド、およびサブサンプリング係数を ImageWriteParam から適用する必要があります。ソース領域とソースバンドは、子 Raster を作成することで処理できます。簡単にするため、ソースイメージから 1 つの Raster を抽出します。ソースイメージがタイリングされている場合は、必要に応じて小さい Raster を抽出することでメモリーを節約できます。
        public void write(IIOMetadata streamMetadata,
                          IIOImage image,
                          ImageWriteParam param) throws IIOException {
                RenderedImage im = image.getRenderedImage();

                Rectangle sourceRegion =
                        new Rectangle(0, 0, im.getWidth(), im.getHeight());
                int sourceXSubsampling = 1;
                int sourceYSubsampling = 1;
                int[] sourceBands = null;
                if (param != null) {
                        sourceRegion =
                                sourceRegion.intersection(param.getSourceRegion());
                        sourceXSubsampling = param.getSourceXSubsampling();
                        sourceYSubsampling = param.getSourceYSubsampling();
                        sourceBands = param.getSourceBands();

                        int subsampleXOffset = param.getSubsamplingXOffset();
                        int subsampleYOffset = param.getSubsamplingYOffset();
                        sourceRegion.x += subsampleXOffset;
                        sourceRegion.y += subsampleYOffset;
                        sourceRegion.width -= subsampleXOffset;
                        sourceRegion.height -= subsampleYOffset;
                }

                // Grab a Raster containing the region of interest
                int width = sourceRegion.width;
                int height = sourceRegion.height;
                Raster imRas = im.getData(sourceRegion);
                int numBands = imRas.getNumBands();

                // Check that sourceBands values are in range
                if (sourceBands != null) {
                        for (int i = 0; i < sourceBands.length; i++) {
                                if (sourceBands[i] >= numBands) {
                                        throw new IllegalArgumentException("bad band!");
                                }
                        }
                }

                // Translate imRas to start at (0, 0) and subset the bands
                imRas = imRas.createChild(sourceRegion.x, sourceRegion.y,
                                          width, height,
                                          0, 0,
                                          sourceBands);

                // Reduce width and height according to subsampling factors
                width = (width + sourceXSubsampling - 1)/sourceXSubsampling;
                height = (height + sourceYSubsampling - 1)/sourceYSubsampling;

                // Assume 1 band image is grayscale, 3 band image is RGB
                int colorType;
                if (numBands == 1) {
                        colorType = MyFormatImageReader.COLOR_TYPE_GRAY;
                } else if (numBands == 3) {
                        colorType = MyFormatImageReader.COLOR_TYPE_RGB;
                } else {
                        throw new IIOException("Image must have 1 or 3 bands!");
                }
イメージの寸法とカラータイプを確認できたら、プラグインは、いつでもファイルヘッダーを書き込むことができます。
                try {
                        byte[] signature = {
                                (byte)'m', (byte)'y',  (byte)'f', (byte)'o',
                                (byte)'r', (byte)'m',  (byte)'a', (byte)'t'
                        };
                        // Output header information
                        stream.write(signature);
                        stream.write(`\n');
                        stream.writeInt(width);
                        stream.writeInt(height);
                        stream.writeByte(colorType);
                        stream.write(`\n');
                        
次に、write メソッドの IIOImage 引数からイメージメタデータを抽出し、convertImageMetadata を呼び出してメタデータを MyFormatMetadata オブジェクトに変換してみます。その結果が null 以外であれば、メタデータからキーワードと値を抽出して、出力に書き込みます。
                        // Attempt to convert metadata, if present
                        IIOMetadata imd = image.getMetadata();
                        MyFormatMetadata metadata = null;
                        if (imd != null) {
                                ImageTypeSpecifier type =
                                        ImageTypeSpecifier.createFromRenderedImage(im);
                                metadata =
                                        (MyFormatMetadata)convertImageMetadata(imd,
                                                                               type,
                                                                               null);
                        }

                        // Output metadata if present
                        if (metadata != null) {
                                Iterator keywordIter = metadata.keywords.iterator();
                                Iterator valueIter = metadata.values.iterator();
                                while (keywordIter.hasNext()) {
                                        String keyword = (String)keywordIter.next();
                                        String value = (String)valueIter.next();
                                        
                                        stream.writeUTF(keyword);
                                        stream.write(`\n');
                                        stream.writeUTF(value);
                                        stream.write(`\n');
                                }
                        }
                        stream.writeUTF("END");
                        stream.write(`\n');
これで、ピクセルデータを書き込む準備ができました。イメージ Raster を、getPixels メソッドを使用して一度に 1 行ずつ int 配列にコピーします。その後、それらの値を水平サブサンプリング係数を使ってサブサンプリングし、その結果を byte 配列にコピーし、その配列を 1 回の write 呼び出しで出力に書き込みます。そして、ソース行を垂直サブサンプリング係数の分だけインクリメントし、ソース領域の最後まで繰り返します。最後に、出力ストリームをフラッシュします。
                        // Output (subsampled) pixel values
                        int rowLength = width*numBands;
                        int xSkip = sourceXSubsampling*numBands;
                        int[] rowPixels = imRas.getPixels(0, 0, width, 1,
                                                          (int[])null);
                        byte[] rowSamples = new byte[rowLength];

                        // Output every (sourceYSubsampling)^th row
                        for (int y = 0; y < height; y += sourceYSubsampling) {
                                imRas.getPixels(0, y, width, 1, rowPixels);

                                // Subsample horizontally and convert to bytes
                                int count = 0;
                                for (int x = 0; x < width; x += xSkip) {
                                        if (colorType ==
                                                MyFormatImageReader.COLOR_TYPE_GRAY) {
                                                rowSamples[count++] = (byte)rowPixels[x];
                                        } else {
                                                rowSamples[count++] = (byte)rowPixels[x];
                                                rowSamples[count++] =
                                                        (byte)rowPixels[x + 1];
                                                rowSamples[count++] =
                                                        (byte)rowPixels[x + 2];
                                        }
                                }

                                // Output a row's worth of bytes
                                stream.write(rowSamples, 0, width*numBands);
                        }
                        stream.flush();
                } catch (IOException e) {
                        throw new IIOException("I/O error!", e);
                }
        }
}


目次 | 前へ | 次へ

Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.