CONTENTS | PREV | NEXT | JavaTM Image I/O API Guide |
ImageReader
Rather than using theImageIO
class to perform the entire decoding operation, an application may use theImageIO
class to obtain anImageReader
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 thejavax.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 aFile
orInputStream
, anImageInputStream
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)
, whereimageIndex
is the index of the image within the file. This produces the same result as if it had calledImageIO.read
, as shown above.
ImageReadParam
More control may be obtained by supplying theread
method with an additional parameter of typeImageReadParam
. AnImageReadParam
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 theImageReadParam
:
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 theImageReadParam
:
BufferedImage bi = reader.read(imageIndex, param);
The result is a newBufferedImage
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 callreader.read(0, param)
without assigning the result anywhere, knowing that the pixels will be written into the existingBufferedImage
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);
IIOParamController
A plug-in may optionally supply anIIOParamController
object that may be used to set up anIIOReadParam
(orIIOWriteParam
) using a graphical user interface (GUI), or any other interface. A reader plug-in may attach anIIOParamController
to anyImageReadParam
objects that it creates:
ImageReadParam param = reader.getDefaultImageParam(); IIOParamController controller = param.getController(); if (controller != null) { controller.activate(param); }
When the controller'sactivate
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 associatedImageReadParam
to update its state, either in response to each GUI event, or all at once prior to returning fromactivate
.
All the methods in theImageReader
class that deal with images take animageIndex
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, settingallowSearch
tofalse
will allow the reader to return a value of-1
instead of the actual number of images. If the parameter istrue
, 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 anIndexOutOfBoundsException
. Thus, the application can request images with increasing indices until it receives an exception.
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);