Reading and Writing Basics

In the previous chapter we saw that with the Java2D package, input images are converted to Images using the getImage methods of the Applet class and the Toolkit class. In this chapter we'll see that, with the Image I/O package, input images are converted to BufferedImages using the read methods of the javax.imageio.ImageReader class. Generally, more than one ImageReader subclass will be available, so the initial step in reading an image is to choose an ImageReader that can decode the format of the image of interest. This is done by providing information about that image's format to a set of ImageReader service providers (javax.imageio.ImageReaderSpis). This information can be in the following forms:

  • Image file suffix

  • Image MIME type

  • Image format

  • Image data

Using the provided information, the ImageReaderSpis respond as to whether their corresponding ImageReader can decode that format or not. One of the ImageReaders whose service provider responds positively will then be chosen to convert the image data into a BufferedImage.

An important point to understand in this arrangement is that the ImageReaders are available through plug-ins. Thus, while some will be part of the Java standard development kit, the rest can be downloaded from third party vendors, freeware, and shareware sites. If no appropriate ImageReader is available, then one can be written, as will be demonstrated in later sections.

The process for writing images is very similar, except in reverse. The available javax.imageio.ImageWriters have service providers that are given information about the potential output image's format, and they respond if they are able to convert the BufferedImage of interest into an output image using this format.

ImageIO

The ImageIO class contains static methods that are mainly used for locating ImageReaders and ImageWriters. For example, if you want to find out which image formats or image MIME types your JVM can currently decode or encode, you can use the following ImageIO methods:

static String[] getReaderFormatNames()
static String[] getReaderMIMETypes()
static String[] getWriterFormatNames()
static String[] getWriterMIMETypes()

In Listing 5.1 these ImageIO methods are used to display the available ImageReaders and ImageWriters according to image format and MIME type.

Listing 5.1 RWtypes.java
package ch5.imageio;
import javax.imageio.ImageIO;

/**
* RWtypes.java - a class to display available ImageReaders and
* ImageWriters by image format and MIME type
*/
public class RWtypes {
   public static void main(String[] args) {
       String[] readers, writers;

       System.out.println("For Reading:");
       readers = ImageIO.getReaderFormatNames();
       System.out.println("	By format:");
       for (int i=0; i<readers.length;i++)
           System.out.println("		" + readers[i]);

       readers = ImageIO.getReaderMIMETypes();
       System.out.println("	By MIME Types:");
       for (int i=0; i<readers.length;i++)
           System.out.println("		" + readers[i]);

       System.out.println("For Writing:");
       writers = ImageIO.getWriterFormatNames();
       System.out.println("	By format:");
       for (int i=0; i<writers.length;i++)
           System.out.println("		" + writers[i]);

       writers = ImageIO.getWriterMIMETypes();
       System.out.println("	By MIME Types:");
       for (int i=0; i<writers.length;i++)
           System.out.println("		" + writers[i]);
   }
}

If you were to run this application, the following output might appear:

For Reading:
        By format:
                png
                jpeg
                JPEG
                gif
                jpg
                JPG
        By MIME Types:
                image/jpeg
                image/png
                image/x-png
                image/gif
For Writing:
        By format:
                PNG
                png
                jpeg
                JPEG
                jpg
                JPG
        By MIME Types:
                image/jpeg
                image/png
                image/x-png

Tip

If you are using a version of the Java standard development kit (SDK) prior to 1.4.0, you'll need to have imageio.jar (from the Image I/O package) and crimson.jar (from the JAXP package) somewhere in your classpath.


Thus, without adding additional ImageReader and ImageWriter plug-ins, we are only able to read GIF, JPEG, and PNG images, and we can only write JPEG and PNG images.

Note

This output is dependent upon the version of the Java Image I/O package being used. In the early access version of this package, JPEG ImageReaders and ImageWriters are not provided.


While this type of output is useful to examine what kinds of image formats one can decode and encode, a more common concern is that of finding an appropriate ImageReader for a given image. This task can also be done by using static methods of the ImageIO class (see Figure 5.1). These methods enable one to find an ImageReader by specifying an image's format, its MIME type, or its file suffix, as in one of the following:

static Iterator getImageReadersByFormatName(String formatName)
static Iterator getImageReadersByMIMEType(String MIMEType)
static Iterator getImageReadersbySuffix(String fileSuffix)

Figure 5.1. When given information about an input image, IOImage static methods are used to discover which of the available ImageReaders can decode its format.


Any of the ImageReaders contained in the returned Iterator can be used to convert the input image into a BufferedImage.

Thus, there are various ways to find an appropriate ImageReader, but most of those ways do not involve examining the input stream. This is because the methods relying upon image format, image suffix, and image MIME type are based on assumptions that if given an image with that property, then a particular ImageReader can decode it. A more reliable method is to just let the image input stream be examined, or more exactly, the object representing the image input stream (javax.imageio.stream.ImageInputStream) be examined, as seen in

static Iterator getImageReaders(Object input)

where input is usually an ImageInputStream.

Some examples of ImageInputStream creation are as follows:

URL url = new URL(imageURL);
ImageInputStream iis = ImageIO.createImageInputStream(url.openStream());

or

Socket s = new Socket(imageHost, imagePort);
ImageInputSream iis = ImageIO.createImageInputStream(s.getInputStream());

or

FileInputStream fis = new FileInputStream(imageFileName);
ImageInputStream iis = ImageIO.createImageInputStream(fis);

Lastly, if you already have an ImageWriter, you can use it to get a corresponding ImageReader (assuming that the plug-in which defined the ImageWriter also defined an ImageReader), as in

static ImageReader getImageReader(ImageWriter writer)

Note

Most image formats begin with something called a magic number, which is the part of the ImageInputStream that most ImageReaderSpis use to decide whether they can decode an image format.


Many of the ImageIO static methods pertaining to ImageWriters are analogous to those pertaining to ImageReaders, so an ImageWriter can be found using any one of the following methods:

static Iterator getImageWritersByFormatName(String formatName)
static Iterator getImageWritersByMIMEType(String MIMEType)
static Iterator getImageWritersbySuffix(String fileSuffix)
static ImageWriter getImageWriter(ImageReader reader)

One last method to obtain an ImageWriter, which is loosely analogous to the

static Iterator getImageReaders(Object input)

method, is the following:

static Iterator getImageWriters(ImageTypeSpecifier type, String format)

where the javax.imageio.ImageTypeSpecifier class is a convenience class that specifies a ColorModel/SampleModel combination (in this case, of the BufferedImage to be written) and the format parameter specifies an output image format.

ImageReader Usage

Although a more detailed discussion of ImageReaders will be provided later in this chapter, it is necessary at this point to understand the basics of how an ImageReader is used. The two main reasons that an application will interact with an ImageReader are:

  • To provide it with an input source

  • To use it to read the source image(s)

To give the ImageReader an input source, the following method is used:

public void setInput(Object input, boolean seekForwardOnly)

where the input parameter is usually an ImageInputStream and the seekForwardOnly parameter is used to specify whether an application can go backwards in the input stream. Thus, if an image stream consists of two images and you want to allow the application to read the first image after the second image has been read, the seekForwardOnly parameter would need to be false.

After an image source is defined, the ImageReader can read source images using either of the following two methods:

public void read(int imageIndex)

or

public void read(int imageIndex, ImageReadParam param)

where imageIndex is the index of the image that will be read and the param parameter provides control over how this image is to be read. The ImageReadParam class will be discussed in an upcoming section entitled “ImageReadParam,” so for now it's enough to know that it can provide functionality such as clipping and subsampling of the input image.

Note

There is a field called minIndex in the ImageReader that is initialized to 0. Whenever an imageIndex is passed to the read method, it is checked to make sure it is not less than minIndex. If it is, an IndexOutOfBoundsException is thrown. If imageIndex is an allowable value and seekForwardOnly is true, minIndex takes the value of the last imageIndex. If seekForwardOnly is false, the value of minIndex remains at 0.


Because an image index is involved in image reading, one often needs to know how many images are available from a particular source. This information is obtained using the following ImageReader method:

public int getNumImages(boolean allowSearch)

where the allowSearch parameter specifies whether you want the entire ImageInputStream examined to determine the number of available images. In other words, in some image formats, the number of images is immediately available, while in other formats this can only be discovered by searching the entire image input stream. If the allowSearch parameter is false, the number of images available will be returned only if it is immediately available. If it's not, then a -1 will be returned. This parameter permits the programmer to specify that finding the number of images is required for the application and, if necessary, it should wait for the entire ImageInputStream to be searched before continuing. In many cases the allowSearch parameter can be set to false because it is possible to read all the available images without knowing how many there are by simply catching any IndexOutofBounds exceptions, illustrated by the following:

int imageIndex = 0;
BufferedImage bi;
try {
    while (bi=reader.read(imageIndex++)) {
        /* process image here */
							}
}
catch (IndexOutOfBoundsException exception) {
    // no more images left
}

An example of ImageReader usage is provided in Listing 5.2. In this listing, an ImageReader is found by examining the input image's ImageInputStream. This application takes an image URL and uses it to create a BufferedImage that is displayed.

Listing 5.2 displayImage.java
package ch5.imageio;

import java.io.*;
import java.util.*;
import java.awt.*;
import java.net.*;
import javax.swing.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;

/**
 * displayImage.java -- displays an image or a series of images contained
 * at the URL provided on the command line.
 */
public class displayImage extends JFrame {
    private BufferedImage bi;
    private Insets insets;
    private ImageReader reader;
    private ImageInputStream iis;
    private URL url;
    private int imageIndex = 0;

    public displayImage(String inputURL) {
        /*
         * The following line looks for plug-ins on the application
         * classpath -- this will be discussed in a later section
         */
        ImageIO.scanForPlugins();

        try {
            url = new URL(inputURL);
        }
        catch (MalformedURLException mue) {
            System.out.print("MalformedURLException: ");
            System.out.println(mue.getMessage());
            System.exit(1);
        }

        try {
            iis = ImageIO.createImageInputStream(url.openStream());
        }
        catch (IIOException ie) {
            System.out.println("IIOException:  " + ie.getMessage());
            System.exit(1);
        }
        catch (IOException ie) {
            System.out.println("IOException:  " + ie.getMessage());
            System.exit(1);
        }

        /*
         * get ImageReaders which can decode the given ImageInputStream
         */
        Iterator readers = ImageIO.getImageReaders(iis);

        /* if there is a set of appropriate ImageReaders, then take
         * the first one
         */
        if(readers.hasNext()) {
            reader = (ImageReader)readers.next();
            reader.setInput(iis, true);
        }
        if (reader == null) {
            System.err.print("No Available ImageReader can ");
            System.err.println("decode: " + url);
            System.exit(1);
        }

        addNotify();
        insets = getInsets();

        show();
        showImage();
    }

    /**
     * This method iteratively displays all images in the given
     * ImageInputStream
     */
    private void showImage() {
        imageIndex = 0;

        reader.setInput(iis, true);
        /*
         * read and display all images
         */
        while(true) {
            try {
                bi = reader.read(imageIndex);
                setSize(bi.getWidth()+insets.left+insets.right,
                        bi.getHeight()+insets.top+insets.bottom);
                imageIndex++;
                repaint();
            }
            catch (IOException ie) {
                System.out.println("IIOException " + ie.getMessage());
                System.exit(1);
            }
            catch (IndexOutOfBoundsException iobe) {
                // all of the images have been read
            }
        }
    }

    /**
     * simple image paint routine which double buffers display
     */
    public void paint(Graphics g) {
        Image buffer;
        Graphics g2d;

        if (bi != null) {
            buffer = createImage(bi.getWidth(), bi.getHeight());
            g2d = buffer.getGraphics();

            /*
             * first clear viewing area
             * then draw image on buffered image
             * then draw buffered image on JFrame
             */
            g2d.clearRect(0, 0, bi.getWidth(), bi.getHeight());
            g2d.drawImage(bi, 0, 0, null);
            g.drawImage(buffer, insets.left, insets.top, null);
        }
    }

    public static void main(String[] args) {
        if (args.length == 0)
            new displayImage("file:images/fruits.png");
        else
            new displayImage(args[0]);
    }
}

ImageWriter Usage

When we discussed ImageReader usage, we were primarily interested in its setInput and read methods. For ImageWriter usage, we will discuss the analogous setOutput and write methods.

The most common setOutput method is

public void setOutput(Object output)

where output is typically an ImageOutputStream, as in

Socket s = new Socket(imageHost, imagePort);
ImageOutputStream ios = ImageIO.createImageOutputStream(s.getOutputStream());

or

FileOutputStream fos = new FileOutputStream(imageFileName);
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);

The main write method is as follows:

public void write(IIOMetadata streamMetadata, 
                  IIOImage iioimage,
                  ImageWriteParam param)

where the streamMetadata parameter represents the stream metadata to be included in the output stream, and the param parameter provides control over how the output image is written to the output stream. Because both of these topics will be considered later in this chapter, we'll ignore them for now. In practice, these values can be set to null if there is no stream metadata, and/or no special control is required for writing the images to the output stream. The middle parameter, iioimage, is an object of type javax.imageio.IIOImage, which is a container class used for holding the following information:

  • The image

  • The image's associated thumbnail images, represented as a java.util.List of BufferedImages

  • The image's metadata (note that this is different than the stream's metadata, which was a parameter in the write method)

If the output image format does not support thumbnail images or image metadata, these can both be set to null. The constructors for instantiating an IIOImage object are as follows:

IIOImage(Raster raster, List thumbnails, IIOMetadata metadata)
IIOImage(RenderedImage image, List thumbnails, IIOMetadata metadata)

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

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