ch5ImageReaderSpi.java Source code

Java tutorial

Introduction

Here is the source code for ch5ImageReaderSpi.java

Source

/*
    
Java Media APIs: Cross-Platform Imaging, Media and Visualization
Alejandro Terrazas
Sams, Published November 2002, 
ISBN 0672320940
*/

import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;

import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormat;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;

import org.w3c.dom.Node;

/**
 * Simple, functional ImageReaderSpi used to understand how information
 * regarding format name, suffices and mime types get passed to ImageIO static
 * methods
 */
public class ch5ImageReaderSpi extends ImageReaderSpi {

    static final String[] suffixes = { "ch5", "CH5" };

    static final String[] names = { "ch5" };

    static final String[] MIMETypes = { "image/ch5" };

    static final String version = "1.00";

    static final String readerCN = "ch5.imageio.plugins.ch5ImageReader";

    static final String vendorName = "CompanyName";

    //writerSpiNames
    static final String[] wSN = { "ch5.imageio.plugins.ch5ImageWriterSpi" };

    //StreamMetadataFormatNames and StreamMetadataFormatClassNames
    static final boolean supportedStandardStreamMetadataFormat = false;

    static final String nativeStreamMFN = "ch5.imageio.ch5stream_1.00";

    static final String nativeStreamMFCN = "ch5.imageio.ch5stream";

    static final String[] extraStreamMFN = null;

    static final String[] extraStreamMFCN = null;

    //ImageMetadataFormatNames and ImageMetadataFormatClassNames
    static final boolean supportedStandardImageMetadataFormat = false;

    static final String nativeImageMFN = "ch5.imageio.ch5image1.00";

    static final String nativeImageMFCN = "ch5.imageio.ch5image";

    static final String[] extraImageMFN = null;

    static final String[] extraImageMFCN = null;

    public ch5ImageReaderSpi() {
        super(vendorName, version, names, suffixes, MIMETypes, readerCN, //readerClassName
                STANDARD_INPUT_TYPE, wSN, //writerSpiNames
                false, nativeStreamMFN, nativeStreamMFCN, extraStreamMFN, extraStreamMFCN, false, nativeImageMFN,
                nativeImageMFCN, extraImageMFN, extraImageMFCN);
    }

    public String getDescription(Locale locale) {
        return "Demo ch5 image reader, version " + version;
    }

    public ImageReader createReaderInstance(Object extension) {
        return new ch5ImageReader(this);
    }

    /**
     * This method gets called when an application wants to see if the input
     * image's format can be decoded by this ImageReader. In this case, we'll
     * simply check the first byte of data to see if its a 5 which is the format
     * type's magic number
     */
    public boolean canDecodeInput(Object input) {
        boolean reply = false;

        ImageInputStream iis = (ImageInputStream) input;
        iis.mark(); // mark where we are in ImageInputStream
        try {
            String magicNumber = iis.readLine().trim();
            iis.reset(); // reset stream back to marked location
            if (magicNumber.equals("5"))
                reply = true;
        } catch (IOException exception) {
        }
        return reply;
    }
}

class ch5ImageReader extends ImageReader {
    private ImageInputStream iis;

    private ch5ImageMetadata[] imagemd;

    private ch5StreamMetadata streammd;

    public ch5ImageReader(ImageReaderSpi originatingProvider) {
        super(originatingProvider);
    }

    /**
     * return the ch5StreamMetadata object instantiated in the setStreamMetadata
     * method
     */
    public IIOMetadata getStreamMetadata() {
        return streammd;
    }

    /**
     * return the ch5ImageMetadata object instantiated in the setImageMetadata
     * method
     */
    public IIOMetadata getImageMetadata(int imageIndex) {
        return imagemd[imageIndex];
    }

    /**
     * this method sets the input for this ImageReader and also calls the
     * setStreamMetadata method so that the numberImages field is available
     */
    public void setInput(Object object, boolean seekForwardOnly) {
        super.setInput(object, seekForwardOnly);
        if (object == null)
            throw new IllegalArgumentException("input is null");

        if (!(object instanceof ImageInputStream)) {
            String argString = "input not an ImageInputStream";
            throw new IllegalArgumentException(argString);
        }
        iis = (ImageInputStream) object;
        setStreamMetadata(iis);
    }

    /**
     * this method provides suggestions for possible image types that will be
     * used to decode the image specified by index imageIndex. By default, the
     * first image type returned by this method will be the image type of the
     * BufferedImage returned by the ImageReader's getDestination method. In
     * this case, we are suggesting using an 8 bit grayscale image with no alpha
     * component.
     */
    public Iterator getImageTypes(int imageIndex) {
        java.util.List l = new java.util.ArrayList();
        ;
        int bits = 8;

        /*
         * can convert ch5 format into 8 bit grayscale image with no alpha
         */
        l.add(ImageTypeSpecifier.createGrayscale(bits, DataBuffer.TYPE_BYTE, false));
        return l.iterator();
    }

    /**
     * read in the input image specified by index imageIndex using the
     * parameters specified by the ImageReadParam object param
     */
    public BufferedImage read(int imageIndex, ImageReadParam param) {

        checkIndex(imageIndex);

        if (isSeekForwardOnly())
            minIndex = imageIndex;
        else
            minIndex = 0;

        BufferedImage bimage = null;
        WritableRaster raster = null;

        /*
         * this method sets the image metadata so that we can use the getWidth
         * and getHeight methods
         */
        setImageMetadata(iis, imageIndex);

        int srcWidth = getWidth(imageIndex);
        int srcHeight = getHeight(imageIndex);

        // initialize values to -1
        int dstWidth = -1;
        int dstHeight = -1;
        int srcRegionWidth = -1;
        int srcRegionHeight = -1;
        int srcRegionXOffset = -1;
        int srcRegionYOffset = -1;
        int xSubsamplingFactor = -1;
        int ySubsamplingFactor = -1;
        if (param == null)
            param = getDefaultReadParam();

        Iterator imageTypes = getImageTypes(imageIndex);
        try {
            /*
             * get the destination BufferedImage which will be filled using the
             * input image's pixel data
             */
            bimage = getDestination(param, imageTypes, srcWidth, srcHeight);

            /*
             * get Rectangle object which will be used to clip the source
             * image's dimensions.
             */
            Rectangle srcRegion = param.getSourceRegion();
            if (srcRegion != null) {
                srcRegionWidth = (int) srcRegion.getWidth();
                srcRegionHeight = (int) srcRegion.getHeight();
                srcRegionXOffset = (int) srcRegion.getX();
                srcRegionYOffset = (int) srcRegion.getY();

                /*
                 * correct for overextended source regions
                 */
                if (srcRegionXOffset + srcRegionWidth > srcWidth)
                    dstWidth = srcWidth - srcRegionXOffset;
                else
                    dstWidth = srcRegionWidth;

                if (srcRegionYOffset + srcRegionHeight > srcHeight)
                    dstHeight = srcHeight - srcRegionYOffset;
                else
                    dstHeight = srcRegionHeight;
            } else {
                dstWidth = srcWidth;
                dstHeight = srcHeight;
                srcRegionXOffset = srcRegionYOffset = 0;
            }
            /*
             * get subsampling factors
             */
            xSubsamplingFactor = param.getSourceXSubsampling();
            ySubsamplingFactor = param.getSourceYSubsampling();

            /**
             * dstWidth and dstHeight should be equal to bimage.getWidth() and
             * bimage.getHeight() after these next two instructions
             */
            dstWidth = (dstWidth - 1) / xSubsamplingFactor + 1;
            dstHeight = (dstHeight - 1) / ySubsamplingFactor + 1;
        } catch (IIOException e) {
            System.err.println("Can't create destination BufferedImage");
        }
        raster = bimage.getWritableTile(0, 0);

        /*
         * using the parameters specified by the ImageReadParam object, read the
         * image image data into the destination BufferedImage
         */
        byte[] srcBuffer = new byte[srcWidth];
        byte[] dstBuffer = new byte[dstWidth];
        int jj;
        int index;
        try {
            for (int j = 0; j < srcHeight; j++) {
                iis.readFully(srcBuffer, 0, srcWidth);

                jj = j - srcRegionYOffset;
                if (jj % ySubsamplingFactor == 0) {
                    jj /= ySubsamplingFactor;
                    if ((jj >= 0) && (jj < dstHeight)) {
                        for (int i = 0; i < dstWidth; i++) {
                            index = srcRegionXOffset + i * xSubsamplingFactor;
                            dstBuffer[i] = srcBuffer[index];
                        }
                        raster.setDataElements(0, jj, dstWidth, 1, dstBuffer);
                    }
                }
            }
        } catch (IOException e) {
            bimage = null;
        }
        return bimage;
    }

    /**
     * this method sets the image metadata for the image indexed by index
     * imageIndex. This method is specific for the ch5 format and thus only sets
     * the image width and image height
     */
    private void setImageMetadata(ImageInputStream iis, int imageIndex) {
        imagemd[imageIndex] = new ch5ImageMetadata();
        try {
            String s;
            s = iis.readLine();
            while (s.length() == 0)
                s = iis.readLine();
            imagemd[imageIndex].imageWidth = Integer.parseInt(s.trim());
            s = iis.readLine();
            imagemd[imageIndex].imageHeight = Integer.parseInt(s.trim());
        } catch (IOException exception) {
        }
    }

    /**
     * this method sets the stream metadata for the images represented by the
     * ImageInputStream iis. This method is specific for the ch5 format and thus
     * only sets the numberImages field.
     */
    private void setStreamMetadata(ImageInputStream iis) {
        streammd = new ch5StreamMetadata();
        try {
            String magicNumber = iis.readLine();
            int numImages = Integer.parseInt(iis.readLine().trim());
            streammd.numberImages = numImages;
            imagemd = new ch5ImageMetadata[streammd.numberImages];
        } catch (IOException exception) {
        }
    }

    /**
     * This method can only be used after the stream metadata has been set
     * (which occurs in the setInput method). Else it will return a -1
     */
    public int getNumImages(boolean allowSearch) {
        return streammd.numberImages;
    }

    /**
     * This method can only be used after the stream metadata has been set
     * (which occurs in the setInput method). Else it will return a -1
     */
    public int getHeight(int imageIndex) {
        if (imagemd == null)
            return -1;
        checkIndex(imageIndex);

        return imagemd[imageIndex].imageHeight;
    }

    /**
     * This method can only be used after the stream metadata has been set
     * (which occurs in the setInput method). Else it will return a -1
     */
    public int getWidth(int imageIndex) {
        if (imagemd == null)
            return -1;
        checkIndex(imageIndex);

        return imagemd[imageIndex].imageWidth;
    }

    private void checkIndex(int imageIndex) {
        if (imageIndex >= streammd.numberImages) {
            String argString = "imageIndex >= number of images";
            throw new IndexOutOfBoundsException(argString);
        }
        if (imageIndex < minIndex) {
            String argString = "imageIndex < minIndex";
            throw new IndexOutOfBoundsException(argString);
        }
    }
}

/**
 * ch5ImageMetadata.java -- holds image metadata for the ch5 format. The
 * internal tree for holding this metadata is read only
 */

class ch5ImageMetadata extends IIOMetadata {
    static final String nativeMetadataFormatName = "ch5.imageio.ch5image_1.00";

    static final String nativeMetadataFormatClassName = "ch5.imageio.ch5image";

    static final String[] extraMetadataFormatNames = null;

    static final String[] extraMetadataFormatClassNames = null;

    static final boolean standardMetadataFormatSupported = false;

    public int imageWidth;

    public int imageHeight;

    public ch5ImageMetadata() {
        super(standardMetadataFormatSupported, nativeMetadataFormatName, nativeMetadataFormatClassName,
                extraMetadataFormatNames, extraMetadataFormatClassNames);
        imageWidth = -1;
        imageHeight = -1;
    }

    public boolean isReadOnly() {
        return true;
    }

    /**
     * IIOMetadataFormat objects are meant to describe the structure of metadata
     * returned from the getAsTree method. In this case, no such description is
     * available
     */
    public IIOMetadataFormat getMetadataFormat(String formatName) {
        if (formatName.equals(nativeMetadataFormatName)) {
            return null;
        } else {
            throw new IllegalArgumentException("Unrecognized format!");
        }
    }

    /**
     * returns the image metadata in a tree corresponding to the provided
     * formatName
     */
    public Node getAsTree(String formatName) {
        if (formatName.equals(nativeMetadataFormatName)) {
            return getNativeTree();
        } else {
            throw new IllegalArgumentException("Unrecognized format!");
        }
    }

    /**
     * returns the image metadata in a tree using the following format <!ELEMENT
     * ch5.imageio.ch5image_1.00 (imageDimensions)> <!ATTLIST imageDimensions
     * imageWidth CDATA #REQUIRED imageHeight CDATA #REQUIRED
     */
    private Node getNativeTree() {
        IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);

        IIOMetadataNode node = new IIOMetadataNode("imageDimensions");
        node.setAttribute("imageWidth", Integer.toString(imageWidth));
        node.setAttribute("imageHeight", Integer.toString(imageHeight));
        root.appendChild(node);

        return root;
    }

    public void setFromTree(String formatName, Node root) {
        throw new IllegalStateException("Metadata is read-only!");
    }

    public void mergeTree(String formatName, Node root) {
        throw new IllegalStateException("Metadata is read-only!");
    }

    public void reset() {
        throw new IllegalStateException("Metadata is read-only!");
    }

    /**
     * initialize the image metadata elements width and height
     */
    public void initialize(int width, int height) {
        imageWidth = width;
        imageHeight = height;
    }
}

/**
 * ch5StreamMetadata.java -- holds stream metadata for the ch5 format. The
 * internal tree for holding this metadata is read only
 */

class ch5StreamMetadata extends IIOMetadata {
    static final String nativeMetadataFormatName = "ch5.imageio.ch5stream_1.00";

    static final String nativeMetadataFormatClassName = "ch5.imageio.ch5stream";

    static final String[] extraMetadataFormatNames = null;

    static final String[] extraMetadataFormatClassNames = null;

    static final boolean standardMetadataFormatSupported = false;

    public int numberImages;

    public ch5StreamMetadata() {
        super(standardMetadataFormatSupported, nativeMetadataFormatName, nativeMetadataFormatClassName,
                extraMetadataFormatNames, extraMetadataFormatClassNames);
        numberImages = -1;
    }

    public boolean isReadOnly() {
        return true;
    }

    /**
     * IIOMetadataFormat objects are meant to describe the structure of metadata
     * returned from the getAsTree method. In this case, no such description is
     * available
     */
    public IIOMetadataFormat getMetadataFormat(String formatName) {
        if (formatName.equals(nativeMetadataFormatName)) {
            return null;
        } else {
            throw new IllegalArgumentException("Unrecognized format!");
        }
    }

    /**
     * returns the stream metadata in a tree corresponding to the provided
     * formatName
     */
    public Node getAsTree(String formatName) {
        if (formatName.equals(nativeMetadataFormatName)) {
            return getNativeTree();
        } else {
            throw new IllegalArgumentException("Unrecognized format!");
        }
    }

    /**
     * returns the stream metadata in a tree using the following format
     * <!ELEMENT ch5.imageio.ch5stream_1.00 (imageDimensions)> <!ATTLIST
     * imageDimensions numberImages CDATA #REQUIRED
     */
    private Node getNativeTree() {
        IIOMetadataNode node; // scratch node

        IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);

        // Image descriptor
        node = new IIOMetadataNode("imageDimensions");
        node.setAttribute("numberImages", Integer.toString(numberImages));
        root.appendChild(node);

        return root;
    }

    public void setFromTree(String formatName, Node root) {
        throw new IllegalStateException("Metadata is read-only!");
    }

    public void mergeTree(String formatName, Node root) {
        throw new IllegalStateException("Metadata is read-only!");
    }

    public void reset() {
        throw new IllegalStateException("Metadata is read-only!");
    }

    /**
     * initialize the stream metadata element numberImages
     */
    public void initialize(int numberImages) {
        this.numberImages = numberImages;
    }
}