CONTENTS | PREV | NEXT |
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.
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);
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
.
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.
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);