IIOParam Classes

By default, the ImageReader's read method does not offer much control over how the input image is read. Similarly, neither does the ImageWriter's write method offer much control over how the output image is written. One way to achieve more control in both of these situations is by using an object of the javax.imageio.IIOParam class. This class provides methods for describing how an image stream should be encoded and decoded. The IIOParam class contains one subclass for image reading, and one subclass for image writing. They are javax.imageio.ImageReadParams and javax.imageio.ImageWriteParams, respectively.

ImageReadParam

A javax.imageio.ImageReadParam object can be obtained using the following ImageReader method:

public ImageReadParam getDefaultReadParam()

After a reference to an ImageReadParam object is obtained, one can then make changes to the state of this object in order to specify how the input image should be read. After the ImageReadParam object is set appropriately, the following ImageReader read method should be used:

public BufferedImage read(int imageIndex, ImageReadParam imageReadParam)

One of the more common ImageReadParam methods used to control image reading is the following:

public void setSourceRegion(Rectangle sourceRegion)

where the parameter sourceRegion represents the image dimensions to be read. Thus, if you wanted to only read the top half of an image, you could use the following:

Rectangle rectangle;
rectangle = new Rectangle(imageReader.getImageWidth(imageIndex),
                          imageReader.getImageHeight(imageIndex)/2));
imageReadParam.setSourceRegion(rectangle);
BufferedImage bi = imageReader.read(imageIndex, imageReadParam);

Another useful method is setSourceSubSampling, which permits you to eliminate all pixels which are not multiples of provided x and y SubSamplingFactors. For example, if the xSubSamplingFactor is 2 and the ySubSamplingFactor is 1, then only columns 0, 2, 4, and so on will be kept.

public void setSourceSubsampling(int sourceXSubsampling,
                              int sourceYSubsampling,
                              int subsamplingXOffset,
                              int subsamplingYOffset)

Thus, if you wanted to use subsampling to reduce an image size by a factor of 16, you could use the following:

imageReadParam.setSourceSubSampling(4, 4, 0, 0);
BufferedImage bi = imageReader.read(imageIndex, imageReadParam);

For a user of the Image I/O API, the ImageReadParam class is primarily for controlling the reading of input images. On the other hand, if one is a plug-in designer, this class has two other important purposes. Its values are used in the ImageReader's getDestination method to instantiate the appropriate sized BufferedImage. After this BufferedImage has been instantiated the ImageReadParam parameters are used to correctly fill the BufferedImage in with the input image's data.

In more detail, there is a predefined ImageReader method that gets executed in the read method. This method returns the BufferedImage object in which the decoded input data should be placed. This getDestination method is as follows:

protected static BufferedImage getDestination(ImageReadParam param,
                                            Iterator imageTypes,
                                            int width, int height)

where the param parameter is the ImageReadParam object that was passed to the read method and imageTypes is an Iterator object containing the set of allowable ImageTypeSpecifiers (with the default one first). The width and height parameters are the true width and height of the input image. Thus, given this information, the ImageReader's getDestination method will return a BufferedImage of the appropriate size taking into account any clipping and subsampling. This BufferedImage must then be filled appropriately using the ImageReadParam settings and the input image.

Note

If the ImageReadParam object's setDestinationType method and setDestination method are not used, the BufferedImage type returned by the ImageReader's getDestination method will be the first ImageTypeSpecifier specified by the imageTypes parameter. Typically, this parameter will represent the return value of the ImageReader's getImageTypes method.


In Table 5.1, the results of using the ImageReadParam's setSourceRegion and setSubsampling methods on the size of the destination BufferedImage are shown. As illustrated in this table, the destination image size will be found by first clipping any image regions not common to both the original image region and the defined source region, and then subsampling the resulting area using the formulas

new width = (original width + xsubsamplingfactor-1)/xsubsamplingfactor

and

new height = (original height + ysubsamplingfactor-1)/ysubsamplingfactor

Table 5.1. Imagereadparam Settings Versus Resulting Destination BufferedImage Size for a 256 x 256 Input Image
Source Rectangle Dimensions SubSample x, y Values Resulting Destination BufferedImage Width, Height
0, 0, 256, 256 1, 1 256, 256
0, 0, 256, 256 2, 3 128, 86
50, 75, 256, 256 1, 1 206, 181
50, 75, 200, 200 2, 3 100, 61

In Listing 5.5, a simple version of an ImageReader is shown. The part to note in this listing is that the ImageReadParam object dictates the dimensions of the destination BufferedImage that is returned from the ImageReader's getDestination method. Also note that it is up to the plug-in designer to correctly read in the pixel data and to fill this BufferedImage using these ImageReadParam values.

Listing 5.5 ch5v1ImageReader.java
package ch5.imageio.plugins;

import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.IIOException;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageReadParam;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;

/**
 * ch5v1ImageReader.java -- this class provides the functionality to
 * read an image of format ch5.  This class does not make use of
 * IIOMetadata classes for representing metadata.  A second version of
 * this class will be provided later in this chapter which will
 * correctly represent the metadata
*/
public class ch5v1ImageReader extends ImageReader {
    private ImageInputStream iis;
    private int[] width = null;
    private int[] height = null;
    private int numberImages = -1;


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

    /**
     * this method returns null for now.  We will revisit it at the
     * end of this chapter after metadata has been discussed.
     */
    public IIOMetadata getStreamMetadata() {
    return null;
    }

    /**
     * this method returns null for now.  We will revisit it at the
     * end of this chapter after metadata has been discussed.
     */
    public IIOMetadata getImageMetadata(int imageIndex) {
    return null;
    }

    /**
     * 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) {
    try {
        String s;
        s = iis.readLine();
        width[imageIndex] = Integer.parseInt(s.trim());
        s = iis.readLine();
        height[imageIndex] = Integer.parseInt(s);
    }
    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) {
    try {
        String magicNumber = iis.readLine();
        numberImages = Integer.parseInt(iis.readLine().trim());
        width = new int[numberImages];
        height = new int[numberImages];
        for (int i=0;i<numberImages;i++)
        width[i] = height[i] = -1;
    }
    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 numberImages;
    }

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

    return height[imageIndex];
    }

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

    return width[imageIndex];
    }

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

ImageWriteParam

The javax.imageio.ImageWriteParam object dictates the dimensions of the output image just as the ImageReadParam dictated the dimensions of the input BufferedImage. Also, just as it was up to the plug-in designer to correctly use the ImageReadParam values to clip and subsample the input image to fill a BufferedImage, the plug-in designer must use the ImageWriteParam values to correctly clip and subsample the output BufferedImage to produce the correct output image. So just like the ImageReadParam, the ImageWriteParam has two different roles. One role is to allow the user to specify how an image should be written out and the other is to provide these values to the ImageWriter's write method.

Note

Besides a BufferedImage, an ImageWriter might also use a Raster for an output image source.


A ImageWriteParam object can be obtained using the following ImageWriter method:

public ImageWriteParam getDefaultWriteParam()

After a reference to an ImageWriteParam object is obtained, a user then makes changes to the state of this object in order to specify how the output image should be saved. The two most common methods of the ImageWriteParam class are the same as for the ImageReadParam class, namely

public void setSourceRegion(Rectangle sourceRegion)

and

public void setSourceSubsampling(int sourceXSubsampling,
                              int sourceYSubsampling,
                              int subsamplingXOffset,
                              int subsamplingYOffset)

In the end of this chapter, an ImageWriter will be presented so that the use of the ImageWriteParam object in writing the output image can be better understood.

IIOParamController

Besides obtaining an IIOParam (superclass of ImageReadParam and ImageWriteParam) object and changing its state through method calls, there is another way to control image reading and writing. That way is by using an javax.imageio.IIOParamController. An IIOParamController is used to set the IIOParam object to the correct state by using a controlling class provided by the plug-in, such as

ImageReadParam param = reader.getDefaultReadParam();
IIOParamController controller = param.getController();
if (controller != null)
    controller.activate(param);

Typically, this controlling class is a graphical user interface (GUI), but it could be any class that implements the following method:

public void activate(IIOParam param)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset