CONTENTS | PREV | NEXT

3.3 ImageReader

Rather than using the ImageIO class to perform the entire decoding operation, an application may use the ImageIO class to obtain an ImageReader object that may be used to perform the read:
Iterator readers = ImageIO.getImageReadersByFormatName("gif");
ImageReader reader = (ImageReader)readers.next();
Readers may also be retrieved based on file contents, file suffix, or MIME type. The mechanism for locating and instantiating the reader makes use of the javax.imageio.spi.ImageReaderSpi class, which allows information about a reader plug-in to be retrieved without actually instantiating the plug-in. The notion of "service provider interfaces" (SPIs) is described in detail in the following chapter.

Once a reader has been obtained, it must be supplied with an input source. Most readers are able to read from an ImageInputStream, which is a special input source that is defined by the Image I/O API. A special input source is used in order to make it simple for reader and writers to work with both file and streaming I/O.

Obtaining an ImageInputStream is straightforward. Given an input source in the form of a File or InputStream, an ImageInputStream is produced by the call:

Object source; // File or InputStream
ImageInputStream iis = ImageIO.createImageInputStream(source);
Once a source has been obtained, it may be attached to the reader by calling
reader.setInput(iis, true);
The second parameter should be set to false if the source file contains multiple images and the application is not guaranteed to read them in order. For file formats that allow only a single image to be stored in a file, it is always legal to pass in a value of true.

Once the reader has its input source set, we can use it to obtain information about the image without necessarily causing image data to be read into memory. For example, calling reader.getImageWidth(0) allows us to obtain the width of the first image stored in the file. A well-written plug-in will attempt to decode only as much of the file as is necessary to determine the image width, without reading any pixels.

To read the image, the application may call reader.read(imageIndex), where imageIndex is the index of the image within the file. This produces the same result as if it had called ImageIO.read, as shown above.


3.3.1 ImageReadParam

More control may be obtained by supplying the read method with an additional parameter of type ImageReadParam. An ImageReadParam allows the application to specify a destination image in which the decoded image data should be stored, allowing better control over memory use. It also allows a region of interest to be specified, as well as subsampling factors that may be used to obtain a scaled-down version of the image.

When a source region is set, the reader plug-in will attempt to decode only the desired region, to the extent that the file format allows partial decoding. In any case, no pixels outside the region will appear in the output. This capability makes it possible to work with extremely large images in a limited amount of memory.

For example, to decode only the upper-left quadrant of the image, the application first obtains an ImageReadParam that is suitable for use with the reader:

ImageReadParam param = reader.getDefaultReadParam();
Next, the source region of interest is set on the ImageReadParam:
import java.awt.Rectangle;
int imageIndex = 0;
int half_width = reader.getImageWidth(imageIndex)/2;
int half_height = reader.getImageHeight(imageIndex)/2;
Rectangle rect = new Rectangle(0, 0, half_width, half_height); 
param.setSourceRegion(rect);
Finally, the image is read using the ImageReadParam:
BufferedImage bi = reader.read(imageIndex, param);
The result is a new BufferedImage whose width and height are equal to half those of the original image (rounding down if the image had an odd width or height).

The lower-right quadrant of the image may then be read into the same BufferedImage that was created to hold the upper-left quadrant, overwriting the previous pixel data:

param.setDestination(bi);
rect = new Rectangle(half_width, half_height, half_width, half_height); 
param.setSourceRegion(rect);
BufferedImage bi2 = reader.read(0, param);
if (bi == bi2) {
        System.out.println("The same BufferedImage was used!");
} else {
        System.out.println("This can't happen!");
}
In practice, the application could simply call reader.read(0, param) without assigning the result anywhere, knowing that the pixels will be written into the existing BufferedImage bi.

As another example, to read every third pixel of the image, resulting in an image one-ninth the size of the original, subsampling factors may be set in the ImageReadParam:

param = reader.getDefaultImageParam();
param.setSourceSubsampling(3, 3, 0, 0);
BufferedImage bi3 = reader.read(0, param);


3.3.2 IIOParamController

A plug-in may optionally supply an IIOParamController object that may be used to set up an IIOReadParam (or IIOWriteParam) using a graphical user interface (GUI), or any other interface. A reader plug-in may attach an IIOParamController to any ImageReadParam objects that it creates:
ImageReadParam param = reader.getDefaultImageParam();
IIOParamController controller = param.getController();
if (controller != null) {
        controller.activate(param);
}
When the controller's activate method is called, it displays the GUI and handles user events such as slider movements and button presses. Typically the interface will contain an "OK" or "Apply" button, which when pressed will cause the activate method to return. The controller is responsible for calling methods on its associated ImageReadParam to update its state, either in response to each GUI event, or all at once prior to returning from activate.

3.3.3 Reading From Multi-Image Files

All the methods in the ImageReader class that deal with images take an imageIndex parameter. This parameter allows access to any of the images in a multi-image file.

The ImageReader.getNumImages method returns the number of images that are stored in the input file. This method takes a boolean parameter, allowSearch. Some image formats, notably GIF, do not provide any way to determine the number of images without reading the entire file. Since this may be costly, setting allowSearch to false will allow the reader to return a value of -1 instead of the actual number of images. If the parameter is true, the reader will always return the actual number of images.

Even if the number of images is not known by the application, it is still possible to call read(imageIndex); if the index is too large, the method will throw an IndexOutOfBoundsException. Thus, the application can request images with increasing indices until it receives an exception.


3.3.4 Reading "Thumbnail" Images

Some image formats allow a small preview image (or multiple previews) to be stored alongside the main image. These "thumbnail" images are useful for identifying image files quickly, without the need to decode the entire image.

Applications can determine how many thumbnail images associated with a particular image are available by calling:

reader.getNumThumbnails(imageIndex);
If a thumbnail image is present, it can be retrieved by calling:
int thumbailIndex = 0;
BufferedImage bi;
bi = reader.readThumbnail(imageIndex, thumbnailIndex);


CONTENTS | PREV | NEXT

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