/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.imageio;
import java.awt.
Point;
import java.awt.
Rectangle;
import java.awt.image.
BufferedImage;
import java.awt.image.
Raster;
import java.awt.image.
RenderedImage;
import java.io.
IOException;
import java.util.
ArrayList;
import java.util.
Iterator;
import java.util.
List;
import java.util.
Locale;
import java.util.
MissingResourceException;
import java.util.
ResourceBundle;
import java.util.
Set;
import javax.imageio.spi.
ImageReaderSpi;
import javax.imageio.event.
IIOReadWarningListener;
import javax.imageio.event.
IIOReadProgressListener;
import javax.imageio.event.
IIOReadUpdateListener;
import javax.imageio.metadata.
IIOMetadata;
import javax.imageio.metadata.
IIOMetadataFormatImpl;
import javax.imageio.stream.
ImageInputStream;
/**
* An abstract superclass for parsing and decoding of images. This
* class must be subclassed by classes that read in images in the
* context of the Java Image I/O framework.
*
* <p> <code>ImageReader</code> objects are normally instantiated by
* the service provider interface (SPI) class for the specific format.
* Service provider classes (e.g., instances of
* <code>ImageReaderSpi</code>) are registered with the
* <code>IIORegistry</code>, which uses them for format recognition
* and presentation of available format readers and writers.
*
* <p> When an input source is set (using the <code>setInput</code>
* method), it may be marked as "seek forward only". This setting
* means that images contained within the input source will only be
* read in order, possibly allowing the reader to avoid caching
* portions of the input containing data associated with images that
* have been read previously.
*
* @see ImageWriter
* @see javax.imageio.spi.IIORegistry
* @see javax.imageio.spi.ImageReaderSpi
*
*/
public abstract class
ImageReader {
/**
* The <code>ImageReaderSpi</code> that instantiated this object,
* or <code>null</code> if its identity is not known or none
* exists. By default it is initialized to <code>null</code>.
*/
protected
ImageReaderSpi originatingProvider;
/**
* The <code>ImageInputStream</code> or other
* <code>Object</code> by <code>setInput</code> and retrieved
* by <code>getInput</code>. By default it is initialized to
* <code>null</code>.
*/
protected
Object input = null;
/**
* <code>true</code> if the current input source has been marked
* as allowing only forward seeking by <code>setInput</code>. By
* default, the value is <code>false</code>.
*
* @see #minIndex
* @see #setInput
*/
protected boolean
seekForwardOnly = false;
/**
* <code>true</code> if the current input source has been marked
* as allowing metadata to be ignored by <code>setInput</code>.
* By default, the value is <code>false</code>.
*
* @see #setInput
*/
protected boolean
ignoreMetadata = false;
/**
* The smallest valid index for reading, initially 0. When
* <code>seekForwardOnly</code> is <code>true</code>, various methods
* may throw an <code>IndexOutOfBoundsException</code> on an
* attempt to access data associate with an image having a lower
* index.
*
* @see #seekForwardOnly
* @see #setInput
*/
protected int
minIndex = 0;
/**
* An array of <code>Locale</code>s which may be used to localize
* warning messages, or <code>null</code> if localization is not
* supported.
*/
protected
Locale[]
availableLocales = null;
/**
* The current <code>Locale</code> to be used for localization, or
* <code>null</code> if none has been set.
*/
protected
Locale locale = null;
/**
* A <code>List</code> of currently registered
* <code>IIOReadWarningListener</code>s, initialized by default to
* <code>null</code>, which is synonymous with an empty
* <code>List</code>.
*/
protected
List<
IIOReadWarningListener>
warningListeners = null;
/**
* A <code>List</code> of the <code>Locale</code>s associated with
* each currently registered <code>IIOReadWarningListener</code>,
* initialized by default to <code>null</code>, which is
* synonymous with an empty <code>List</code>.
*/
protected
List<
Locale>
warningLocales = null;
/**
* A <code>List</code> of currently registered
* <code>IIOReadProgressListener</code>s, initialized by default
* to <code>null</code>, which is synonymous with an empty
* <code>List</code>.
*/
protected
List<
IIOReadProgressListener>
progressListeners = null;
/**
* A <code>List</code> of currently registered
* <code>IIOReadUpdateListener</code>s, initialized by default to
* <code>null</code>, which is synonymous with an empty
* <code>List</code>.
*/
protected
List<
IIOReadUpdateListener>
updateListeners = null;
/**
* If <code>true</code>, the current read operation should be
* aborted.
*/
private boolean
abortFlag = false;
/**
* Constructs an <code>ImageReader</code> and sets its
* <code>originatingProvider</code> field to the supplied value.
*
* <p> Subclasses that make use of extensions should provide a
* constructor with signature <code>(ImageReaderSpi,
* Object)</code> in order to retrieve the extension object. If
* the extension object is unsuitable, an
* <code>IllegalArgumentException</code> should be thrown.
*
* @param originatingProvider the <code>ImageReaderSpi</code> that is
* invoking this constructor, or <code>null</code>.
*/
protected
ImageReader(
ImageReaderSpi originatingProvider) {
this.
originatingProvider =
originatingProvider;
}
/**
* Returns a <code>String</code> identifying the format of the
* input source.
*
* <p> The default implementation returns
* <code>originatingProvider.getFormatNames()[0]</code>.
* Implementations that may not have an originating service
* provider, or which desire a different naming policy should
* override this method.
*
* @exception IOException if an error occurs reading the
* information from the input source.
*
* @return the format name, as a <code>String</code>.
*/
public
String getFormatName() throws
IOException {
return
originatingProvider.
getFormatNames()[0];
}
/**
* Returns the <code>ImageReaderSpi</code> that was passed in on
* the constructor. Note that this value may be <code>null</code>.
*
* @return an <code>ImageReaderSpi</code>, or <code>null</code>.
*
* @see ImageReaderSpi
*/
public
ImageReaderSpi getOriginatingProvider() {
return
originatingProvider;
}
/**
* Sets the input source to use to the given
* <code>ImageInputStream</code> or other <code>Object</code>.
* The input source must be set before any of the query or read
* methods are used. If <code>input</code> is <code>null</code>,
* any currently set input source will be removed. In any case,
* the value of <code>minIndex</code> will be initialized to 0.
*
* <p> The <code>seekForwardOnly</code> parameter controls whether
* the value returned by <code>getMinIndex</code> will be
* increased as each image (or thumbnail, or image metadata) is
* read. If <code>seekForwardOnly</code> is true, then a call to
* <code>read(index)</code> will throw an
* <code>IndexOutOfBoundsException</code> if {@code index < this.minIndex};
* otherwise, the value of
* <code>minIndex</code> will be set to <code>index</code>. If
* <code>seekForwardOnly</code> is <code>false</code>, the value of
* <code>minIndex</code> will remain 0 regardless of any read
* operations.
*
* <p> The <code>ignoreMetadata</code> parameter, if set to
* <code>true</code>, allows the reader to disregard any metadata
* encountered during the read. Subsequent calls to the
* <code>getStreamMetadata</code> and
* <code>getImageMetadata</code> methods may return
* <code>null</code>, and an <code>IIOImage</code> returned from
* <code>readAll</code> may return <code>null</code> from their
* <code>getMetadata</code> method. Setting this parameter may
* allow the reader to work more efficiently. The reader may
* choose to disregard this setting and return metadata normally.
*
* <p> Subclasses should take care to remove any cached
* information based on the previous stream, such as header
* information or partially decoded image data.
*
* <p> Use of a general <code>Object</code> other than an
* <code>ImageInputStream</code> is intended for readers that
* interact directly with a capture device or imaging protocol.
* The set of legal classes is advertised by the reader's service
* provider's <code>getInputTypes</code> method; most readers
* will return a single-element array containing only
* <code>ImageInputStream.class</code> to indicate that they
* accept only an <code>ImageInputStream</code>.
*
* <p> The default implementation checks the <code>input</code>
* argument against the list returned by
* <code>originatingProvider.getInputTypes()</code> and fails
* if the argument is not an instance of one of the classes
* in the list. If the originating provider is set to
* <code>null</code>, the input is accepted only if it is an
* <code>ImageInputStream</code>.
*
* @param input the <code>ImageInputStream</code> or other
* <code>Object</code> to use for future decoding.
* @param seekForwardOnly if <code>true</code>, images and metadata
* may only be read in ascending order from this input source.
* @param ignoreMetadata if <code>true</code>, metadata
* may be ignored during reads.
*
* @exception IllegalArgumentException if <code>input</code> is
* not an instance of one of the classes returned by the
* originating service provider's <code>getInputTypes</code>
* method, or is not an <code>ImageInputStream</code>.
*
* @see ImageInputStream
* @see #getInput
* @see javax.imageio.spi.ImageReaderSpi#getInputTypes
*/
public void
setInput(
Object input,
boolean
seekForwardOnly,
boolean
ignoreMetadata) {
if (
input != null) {
boolean
found = false;
if (
originatingProvider != null) {
Class[]
classes =
originatingProvider.
getInputTypes();
for (int
i = 0;
i <
classes.length;
i++) {
if (
classes[
i].
isInstance(
input)) {
found = true;
break;
}
}
} else {
if (
input instanceof
ImageInputStream) {
found = true;
}
}
if (!
found) {
throw new
IllegalArgumentException("Incorrect input type!");
}
this.
seekForwardOnly =
seekForwardOnly;
this.
ignoreMetadata =
ignoreMetadata;
this.
minIndex = 0;
}
this.
input =
input;
}
/**
* Sets the input source to use to the given
* <code>ImageInputStream</code> or other <code>Object</code>.
* The input source must be set before any of the query or read
* methods are used. If <code>input</code> is <code>null</code>,
* any currently set input source will be removed. In any case,
* the value of <code>minIndex</code> will be initialized to 0.
*
* <p> The <code>seekForwardOnly</code> parameter controls whether
* the value returned by <code>getMinIndex</code> will be
* increased as each image (or thumbnail, or image metadata) is
* read. If <code>seekForwardOnly</code> is true, then a call to
* <code>read(index)</code> will throw an
* <code>IndexOutOfBoundsException</code> if {@code index < this.minIndex};
* otherwise, the value of
* <code>minIndex</code> will be set to <code>index</code>. If
* <code>seekForwardOnly</code> is <code>false</code>, the value of
* <code>minIndex</code> will remain 0 regardless of any read
* operations.
*
* <p> This method is equivalent to <code>setInput(input,
* seekForwardOnly, false)</code>.
*
* @param input the <code>ImageInputStream</code> or other
* <code>Object</code> to use for future decoding.
* @param seekForwardOnly if <code>true</code>, images and metadata
* may only be read in ascending order from this input source.
*
* @exception IllegalArgumentException if <code>input</code> is
* not an instance of one of the classes returned by the
* originating service provider's <code>getInputTypes</code>
* method, or is not an <code>ImageInputStream</code>.
*
* @see #getInput
*/
public void
setInput(
Object input,
boolean
seekForwardOnly) {
setInput(
input,
seekForwardOnly, false);
}
/**
* Sets the input source to use to the given
* <code>ImageInputStream</code> or other <code>Object</code>.
* The input source must be set before any of the query or read
* methods are used. If <code>input</code> is <code>null</code>,
* any currently set input source will be removed. In any case,
* the value of <code>minIndex</code> will be initialized to 0.
*
* <p> This method is equivalent to <code>setInput(input, false,
* false)</code>.
*
* @param input the <code>ImageInputStream</code> or other
* <code>Object</code> to use for future decoding.
*
* @exception IllegalArgumentException if <code>input</code> is
* not an instance of one of the classes returned by the
* originating service provider's <code>getInputTypes</code>
* method, or is not an <code>ImageInputStream</code>.
*
* @see #getInput
*/
public void
setInput(
Object input) {
setInput(
input, false, false);
}
/**
* Returns the <code>ImageInputStream</code> or other
* <code>Object</code> previously set as the input source. If the
* input source has not been set, <code>null</code> is returned.
*
* @return the <code>Object</code> that will be used for future
* decoding, or <code>null</code>.
*
* @see ImageInputStream
* @see #setInput
*/
public
Object getInput() {
return
input;
}
/**
* Returns <code>true</code> if the current input source has been
* marked as seek forward only by passing <code>true</code> as the
* <code>seekForwardOnly</code> argument to the
* <code>setInput</code> method.
*
* @return <code>true</code> if the input source is seek forward
* only.
*
* @see #setInput
*/
public boolean
isSeekForwardOnly() {
return
seekForwardOnly;
}
/**
* Returns <code>true</code> if the current input source has been
* marked as allowing metadata to be ignored by passing
* <code>true</code> as the <code>ignoreMetadata</code> argument
* to the <code>setInput</code> method.
*
* @return <code>true</code> if the metadata may be ignored.
*
* @see #setInput
*/
public boolean
isIgnoringMetadata() {
return
ignoreMetadata;
}
/**
* Returns the lowest valid index for reading an image, thumbnail,
* or image metadata. If <code>seekForwardOnly()</code> is
* <code>false</code>, this value will typically remain 0,
* indicating that random access is possible. Otherwise, it will
* contain the value of the most recently accessed index, and
* increase in a monotonic fashion.
*
* @return the minimum legal index for reading.
*/
public int
getMinIndex() {
return
minIndex;
}
// Localization
/**
* Returns an array of <code>Locale</code>s that may be used to
* localize warning listeners and compression settings. A return
* value of <code>null</code> indicates that localization is not
* supported.
*
* <p> The default implementation returns a clone of the
* <code>availableLocales</code> instance variable if it is
* non-<code>null</code>, or else returns <code>null</code>.
*
* @return an array of <code>Locale</code>s that may be used as
* arguments to <code>setLocale</code>, or <code>null</code>.
*/
public
Locale[]
getAvailableLocales() {
if (
availableLocales == null) {
return null;
} else {
return (
Locale[])
availableLocales.
clone();
}
}
/**
* Sets the current <code>Locale</code> of this
* <code>ImageReader</code> to the given value. A value of
* <code>null</code> removes any previous setting, and indicates
* that the reader should localize as it sees fit.
*
* @param locale the desired <code>Locale</code>, or
* <code>null</code>.
*
* @exception IllegalArgumentException if <code>locale</code> is
* non-<code>null</code> but is not one of the values returned by
* <code>getAvailableLocales</code>.
*
* @see #getLocale
*/
public void
setLocale(
Locale locale) {
if (
locale != null) {
Locale[]
locales =
getAvailableLocales();
boolean
found = false;
if (
locales != null) {
for (int
i = 0;
i <
locales.length;
i++) {
if (
locale.
equals(
locales[
i])) {
found = true;
break;
}
}
}
if (!
found) {
throw new
IllegalArgumentException("Invalid locale!");
}
}
this.
locale =
locale;
}
/**
* Returns the currently set <code>Locale</code>, or
* <code>null</code> if none has been set.
*
* @return the current <code>Locale</code>, or <code>null</code>.
*
* @see #setLocale
*/
public
Locale getLocale() {
return
locale;
}
// Image queries
/**
* Returns the number of images, not including thumbnails, available
* from the current input source.
*
* <p> Note that some image formats (such as animated GIF) do not
* specify how many images are present in the stream. Thus
* determining the number of images will require the entire stream
* to be scanned and may require memory for buffering. If images
* are to be processed in order, it may be more efficient to
* simply call <code>read</code> with increasing indices until an
* <code>IndexOutOfBoundsException</code> is thrown to indicate
* that no more images are available. The
* <code>allowSearch</code> parameter may be set to
* <code>false</code> to indicate that an exhaustive search is not
* desired; the return value will be <code>-1</code> to indicate
* that a search is necessary. If the input has been specified
* with <code>seekForwardOnly</code> set to <code>true</code>,
* this method throws an <code>IllegalStateException</code> if
* <code>allowSearch</code> is set to <code>true</code>.
*
* @param allowSearch if <code>true</code>, the true number of
* images will be returned even if a search is required. If
* <code>false</code>, the reader may return <code>-1</code>
* without performing the search.
*
* @return the number of images, as an <code>int</code>, or
* <code>-1</code> if <code>allowSearch</code> is
* <code>false</code> and a search would be required.
*
* @exception IllegalStateException if the input source has not been set,
* or if the input has been specified with <code>seekForwardOnly</code>
* set to <code>true</code>.
* @exception IOException if an error occurs reading the
* information from the input source.
*
* @see #setInput
*/
public abstract int
getNumImages(boolean
allowSearch) throws
IOException;
/**
* Returns the width in pixels of the given image within the input
* source.
*
* <p> If the image can be rendered to a user-specified size, then
* this method returns the default width.
*
* @param imageIndex the index of the image to be queried.
*
* @return the width of the image, as an <code>int</code>.
*
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs reading the width
* information from the input source.
*/
public abstract int
getWidth(int
imageIndex) throws
IOException;
/**
* Returns the height in pixels of the given image within the
* input source.
*
* <p> If the image can be rendered to a user-specified size, then
* this method returns the default height.
*
* @param imageIndex the index of the image to be queried.
*
* @return the height of the image, as an <code>int</code>.
*
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs reading the height
* information from the input source.
*/
public abstract int
getHeight(int
imageIndex) throws
IOException;
/**
* Returns <code>true</code> if the storage format of the given
* image places no inherent impediment on random access to pixels.
* For most compressed formats, such as JPEG, this method should
* return <code>false</code>, as a large section of the image in
* addition to the region of interest may need to be decoded.
*
* <p> This is merely a hint for programs that wish to be
* efficient; all readers must be able to read arbitrary regions
* as specified in an <code>ImageReadParam</code>.
*
* <p> Note that formats that return <code>false</code> from
* this method may nonetheless allow tiling (<i>e.g.</i> Restart
* Markers in JPEG), and random access will likely be reasonably
* efficient on tiles. See {@link #isImageTiled isImageTiled}.
*
* <p> A reader for which all images are guaranteed to support
* easy random access, or are guaranteed not to support easy
* random access, may return <code>true</code> or
* <code>false</code> respectively without accessing any image
* data. In such cases, it is not necessary to throw an exception
* even if no input source has been set or the image index is out
* of bounds.
*
* <p> The default implementation returns <code>false</code>.
*
* @param imageIndex the index of the image to be queried.
*
* @return <code>true</code> if reading a region of interest of
* the given image is likely to be efficient.
*
* @exception IllegalStateException if an input source is required
* to determine the return value, but none has been set.
* @exception IndexOutOfBoundsException if an image must be
* accessed to determine the return value, but the supplied index
* is out of bounds.
* @exception IOException if an error occurs during reading.
*/
public boolean
isRandomAccessEasy(int
imageIndex) throws
IOException {
return false;
}
/**
* Returns the aspect ratio of the given image (that is, its width
* divided by its height) as a <code>float</code>. For images
* that are inherently resizable, this method provides a way to
* determine the appropriate width given a desired height, or vice
* versa. For non-resizable images, the true width and height
* are used.
*
* <p> The default implementation simply returns
* <code>(float)getWidth(imageIndex)/getHeight(imageIndex)</code>.
*
* @param imageIndex the index of the image to be queried.
*
* @return a <code>float</code> indicating the aspect ratio of the
* given image.
*
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs during reading.
*/
public float
getAspectRatio(int
imageIndex) throws
IOException {
return (float)
getWidth(
imageIndex)/
getHeight(
imageIndex);
}
/**
* Returns an <code>ImageTypeSpecifier</code> indicating the
* <code>SampleModel</code> and <code>ColorModel</code> which most
* closely represents the "raw" internal format of the image. For
* example, for a JPEG image the raw type might have a YCbCr color
* space even though the image would conventionally be transformed
* into an RGB color space prior to display. The returned value
* should also be included in the list of values returned by
* <code>getImageTypes</code>.
*
* <p> The default implementation simply returns the first entry
* from the list provided by <code>getImageType</code>.
*
* @param imageIndex the index of the image to be queried.
*
* @return an <code>ImageTypeSpecifier</code>.
*
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs reading the format
* information from the input source.
*/
public
ImageTypeSpecifier getRawImageType(int
imageIndex)
throws
IOException {
return (
ImageTypeSpecifier)
getImageTypes(
imageIndex).
next();
}
/**
* Returns an <code>Iterator</code> containing possible image
* types to which the given image may be decoded, in the form of
* <code>ImageTypeSpecifiers</code>s. At least one legal image
* type will be returned.
*
* <p> The first element of the iterator should be the most
* "natural" type for decoding the image with as little loss as
* possible. For example, for a JPEG image the first entry should
* be an RGB image, even though the image data is stored
* internally in a YCbCr color space.
*
* @param imageIndex the index of the image to be
* <code>retrieved</code>.
*
* @return an <code>Iterator</code> containing at least one
* <code>ImageTypeSpecifier</code> representing suggested image
* types for decoding the current given image.
*
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs reading the format
* information from the input source.
*
* @see ImageReadParam#setDestination(BufferedImage)
* @see ImageReadParam#setDestinationType(ImageTypeSpecifier)
*/
public abstract
Iterator<
ImageTypeSpecifier>
getImageTypes(int
imageIndex) throws
IOException;
/**
* Returns a default <code>ImageReadParam</code> object
* appropriate for this format. All subclasses should define a
* set of default values for all parameters and return them with
* this call. This method may be called before the input source
* is set.
*
* <p> The default implementation constructs and returns a new
* <code>ImageReadParam</code> object that does not allow source
* scaling (<i>i.e.</i>, it returns <code>new
* ImageReadParam()</code>.
*
* @return an <code>ImageReadParam</code> object which may be used
* to control the decoding process using a set of default settings.
*/
public
ImageReadParam getDefaultReadParam() {
return new
ImageReadParam();
}
/**
* Returns an <code>IIOMetadata</code> object representing the
* metadata associated with the input source as a whole (i.e., not
* associated with any particular image), or <code>null</code> if
* the reader does not support reading metadata, is set to ignore
* metadata, or if no metadata is available.
*
* @return an <code>IIOMetadata</code> object, or <code>null</code>.
*
* @exception IOException if an error occurs during reading.
*/
public abstract
IIOMetadata getStreamMetadata() throws
IOException;
/**
* Returns an <code>IIOMetadata</code> object representing the
* metadata associated with the input source as a whole (i.e.,
* not associated with any particular image). If no such data
* exists, <code>null</code> is returned.
*
* <p> The resulting metadata object is only responsible for
* returning documents in the format named by
* <code>formatName</code>. Within any documents that are
* returned, only nodes whose names are members of
* <code>nodeNames</code> are required to be returned. In this
* way, the amount of metadata processing done by the reader may
* be kept to a minimum, based on what information is actually
* needed.
*
* <p> If <code>formatName</code> is not the name of a supported
* metadata format, <code>null</code> is returned.
*
* <p> In all cases, it is legal to return a more capable metadata
* object than strictly necessary. The format name and node names
* are merely hints that may be used to reduce the reader's
* workload.
*
* <p> The default implementation simply returns the result of
* calling <code>getStreamMetadata()</code>, after checking that
* the format name is supported. If it is not,
* <code>null</code> is returned.
*
* @param formatName a metadata format name that may be used to retrieve
* a document from the returned <code>IIOMetadata</code> object.
* @param nodeNames a <code>Set</code> containing the names of
* nodes that may be contained in a retrieved document.
*
* @return an <code>IIOMetadata</code> object, or <code>null</code>.
*
* @exception IllegalArgumentException if <code>formatName</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>nodeNames</code>
* is <code>null</code>.
* @exception IOException if an error occurs during reading.
*/
public
IIOMetadata getStreamMetadata(
String formatName,
Set<
String>
nodeNames)
throws
IOException
{
return
getMetadata(
formatName,
nodeNames, true, 0);
}
private
IIOMetadata getMetadata(
String formatName,
Set nodeNames,
boolean
wantStream,
int
imageIndex) throws
IOException {
if (
formatName == null) {
throw new
IllegalArgumentException("formatName == null!");
}
if (
nodeNames == null) {
throw new
IllegalArgumentException("nodeNames == null!");
}
IIOMetadata metadata =
wantStream
?
getStreamMetadata()
:
getImageMetadata(
imageIndex);
if (
metadata != null) {
if (
metadata.
isStandardMetadataFormatSupported() &&
formatName.
equals
(
IIOMetadataFormatImpl.
standardMetadataFormatName)) {
return
metadata;
}
String nativeName =
metadata.
getNativeMetadataFormatName();
if (
nativeName != null &&
formatName.
equals(
nativeName)) {
return
metadata;
}
String[]
extraNames =
metadata.
getExtraMetadataFormatNames();
if (
extraNames != null) {
for (int
i = 0;
i <
extraNames.length;
i++) {
if (
formatName.
equals(
extraNames[
i])) {
return
metadata;
}
}
}
}
return null;
}
/**
* Returns an <code>IIOMetadata</code> object containing metadata
* associated with the given image, or <code>null</code> if the
* reader does not support reading metadata, is set to ignore
* metadata, or if no metadata is available.
*
* @param imageIndex the index of the image whose metadata is to
* be retrieved.
*
* @return an <code>IIOMetadata</code> object, or
* <code>null</code>.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs during reading.
*/
public abstract
IIOMetadata getImageMetadata(int
imageIndex)
throws
IOException;
/**
* Returns an <code>IIOMetadata</code> object representing the
* metadata associated with the given image, or <code>null</code>
* if the reader does not support reading metadata or none
* is available.
*
* <p> The resulting metadata object is only responsible for
* returning documents in the format named by
* <code>formatName</code>. Within any documents that are
* returned, only nodes whose names are members of
* <code>nodeNames</code> are required to be returned. In this
* way, the amount of metadata processing done by the reader may
* be kept to a minimum, based on what information is actually
* needed.
*
* <p> If <code>formatName</code> is not the name of a supported
* metadata format, <code>null</code> may be returned.
*
* <p> In all cases, it is legal to return a more capable metadata
* object than strictly necessary. The format name and node names
* are merely hints that may be used to reduce the reader's
* workload.
*
* <p> The default implementation simply returns the result of
* calling <code>getImageMetadata(imageIndex)</code>, after
* checking that the format name is supported. If it is not,
* <code>null</code> is returned.
*
* @param imageIndex the index of the image whose metadata is to
* be retrieved.
* @param formatName a metadata format name that may be used to retrieve
* a document from the returned <code>IIOMetadata</code> object.
* @param nodeNames a <code>Set</code> containing the names of
* nodes that may be contained in a retrieved document.
*
* @return an <code>IIOMetadata</code> object, or <code>null</code>.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IllegalArgumentException if <code>formatName</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>nodeNames</code>
* is <code>null</code>.
* @exception IOException if an error occurs during reading.
*/
public
IIOMetadata getImageMetadata(int
imageIndex,
String formatName,
Set<
String>
nodeNames)
throws
IOException {
return
getMetadata(
formatName,
nodeNames, false,
imageIndex);
}
/**
* Reads the image indexed by <code>imageIndex</code> and returns
* it as a complete <code>BufferedImage</code>, using a default
* <code>ImageReadParam</code>. This is a convenience method
* that calls <code>read(imageIndex, null)</code>.
*
* <p> The image returned will be formatted according to the first
* <code>ImageTypeSpecifier</code> returned from
* <code>getImageTypes</code>.
*
* <p> Any registered <code>IIOReadProgressListener</code> objects
* will be notified by calling their <code>imageStarted</code>
* method, followed by calls to their <code>imageProgress</code>
* method as the read progresses. Finally their
* <code>imageComplete</code> method will be called.
* <code>IIOReadUpdateListener</code> objects may be updated at
* other times during the read as pixels are decoded. Finally,
* <code>IIOReadWarningListener</code> objects will receive
* notification of any non-fatal warnings that occur during
* decoding.
*
* @param imageIndex the index of the image to be retrieved.
*
* @return the desired portion of the image as a
* <code>BufferedImage</code>.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs during reading.
*/
public
BufferedImage read(int
imageIndex) throws
IOException {
return
read(
imageIndex, null);
}
/**
* Reads the image indexed by <code>imageIndex</code> and returns
* it as a complete <code>BufferedImage</code>, using a supplied
* <code>ImageReadParam</code>.
*
* <p> The actual <code>BufferedImage</code> returned will be
* chosen using the algorithm defined by the
* <code>getDestination</code> method.
*
* <p> Any registered <code>IIOReadProgressListener</code> objects
* will be notified by calling their <code>imageStarted</code>
* method, followed by calls to their <code>imageProgress</code>
* method as the read progresses. Finally their
* <code>imageComplete</code> method will be called.
* <code>IIOReadUpdateListener</code> objects may be updated at
* other times during the read as pixels are decoded. Finally,
* <code>IIOReadWarningListener</code> objects will receive
* notification of any non-fatal warnings that occur during
* decoding.
*
* <p> The set of source bands to be read and destination bands to
* be written is determined by calling <code>getSourceBands</code>
* and <code>getDestinationBands</code> on the supplied
* <code>ImageReadParam</code>. If the lengths of the arrays
* returned by these methods differ, the set of source bands
* contains an index larger that the largest available source
* index, or the set of destination bands contains an index larger
* than the largest legal destination index, an
* <code>IllegalArgumentException</code> is thrown.
*
* <p> If the supplied <code>ImageReadParam</code> contains
* optional setting values not supported by this reader (<i>e.g.</i>
* source render size or any format-specific settings), they will
* be ignored.
*
* @param imageIndex the index of the image to be retrieved.
* @param param an <code>ImageReadParam</code> used to control
* the reading process, or <code>null</code>.
*
* @return the desired portion of the image as a
* <code>BufferedImage</code>.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IllegalArgumentException if the set of source and
* destination bands specified by
* <code>param.getSourceBands</code> and
* <code>param.getDestinationBands</code> differ in length or
* include indices that are out of bounds.
* @exception IllegalArgumentException if the resulting image would
* have a width or height less than 1.
* @exception IOException if an error occurs during reading.
*/
public abstract
BufferedImage read(int
imageIndex,
ImageReadParam param)
throws
IOException;
/**
* Reads the image indexed by <code>imageIndex</code> and returns
* an <code>IIOImage</code> containing the image, thumbnails, and
* associated image metadata, using a supplied
* <code>ImageReadParam</code>.
*
* <p> The actual <code>BufferedImage</code> referenced by the
* returned <code>IIOImage</code> will be chosen using the
* algorithm defined by the <code>getDestination</code> method.
*
* <p> Any registered <code>IIOReadProgressListener</code> objects
* will be notified by calling their <code>imageStarted</code>
* method, followed by calls to their <code>imageProgress</code>
* method as the read progresses. Finally their
* <code>imageComplete</code> method will be called.
* <code>IIOReadUpdateListener</code> objects may be updated at
* other times during the read as pixels are decoded. Finally,
* <code>IIOReadWarningListener</code> objects will receive
* notification of any non-fatal warnings that occur during
* decoding.
*
* <p> The set of source bands to be read and destination bands to
* be written is determined by calling <code>getSourceBands</code>
* and <code>getDestinationBands</code> on the supplied
* <code>ImageReadParam</code>. If the lengths of the arrays
* returned by these methods differ, the set of source bands
* contains an index larger that the largest available source
* index, or the set of destination bands contains an index larger
* than the largest legal destination index, an
* <code>IllegalArgumentException</code> is thrown.
*
* <p> Thumbnails will be returned in their entirety regardless of
* the region settings.
*
* <p> If the supplied <code>ImageReadParam</code> contains
* optional setting values not supported by this reader (<i>e.g.</i>
* source render size or any format-specific settings), those
* values will be ignored.
*
* @param imageIndex the index of the image to be retrieved.
* @param param an <code>ImageReadParam</code> used to control
* the reading process, or <code>null</code>.
*
* @return an <code>IIOImage</code> containing the desired portion
* of the image, a set of thumbnails, and associated image
* metadata.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IllegalArgumentException if the set of source and
* destination bands specified by
* <code>param.getSourceBands</code> and
* <code>param.getDestinationBands</code> differ in length or
* include indices that are out of bounds.
* @exception IllegalArgumentException if the resulting image
* would have a width or height less than 1.
* @exception IOException if an error occurs during reading.
*/
public
IIOImage readAll(int
imageIndex,
ImageReadParam param)
throws
IOException {
if (
imageIndex <
getMinIndex()) {
throw new
IndexOutOfBoundsException("imageIndex < getMinIndex()!");
}
BufferedImage im =
read(
imageIndex,
param);
ArrayList thumbnails = null;
int
numThumbnails =
getNumThumbnails(
imageIndex);
if (
numThumbnails > 0) {
thumbnails = new
ArrayList();
for (int
j = 0;
j <
numThumbnails;
j++) {
thumbnails.
add(
readThumbnail(
imageIndex,
j));
}
}
IIOMetadata metadata =
getImageMetadata(
imageIndex);
return new
IIOImage(
im,
thumbnails,
metadata);
}
/**
* Returns an <code>Iterator</code> containing all the images,
* thumbnails, and metadata, starting at the index given by
* <code>getMinIndex</code>, from the input source in the form of
* <code>IIOImage</code> objects. An <code>Iterator</code>
* containing <code>ImageReadParam</code> objects is supplied; one
* element is consumed for each image read from the input source
* until no more images are available. If the read param
* <code>Iterator</code> runs out of elements, but there are still
* more images available from the input source, default read
* params are used for the remaining images.
*
* <p> If <code>params</code> is <code>null</code>, a default read
* param will be used for all images.
*
* <p> The actual <code>BufferedImage</code> referenced by the
* returned <code>IIOImage</code> will be chosen using the
* algorithm defined by the <code>getDestination</code> method.
*
* <p> Any registered <code>IIOReadProgressListener</code> objects
* will be notified by calling their <code>sequenceStarted</code>
* method once. Then, for each image decoded, there will be a
* call to <code>imageStarted</code>, followed by calls to
* <code>imageProgress</code> as the read progresses, and finally
* to <code>imageComplete</code>. The
* <code>sequenceComplete</code> method will be called after the
* last image has been decoded.
* <code>IIOReadUpdateListener</code> objects may be updated at
* other times during the read as pixels are decoded. Finally,
* <code>IIOReadWarningListener</code> objects will receive
* notification of any non-fatal warnings that occur during
* decoding.
*
* <p> The set of source bands to be read and destination bands to
* be written is determined by calling <code>getSourceBands</code>
* and <code>getDestinationBands</code> on the supplied
* <code>ImageReadParam</code>. If the lengths of the arrays
* returned by these methods differ, the set of source bands
* contains an index larger that the largest available source
* index, or the set of destination bands contains an index larger
* than the largest legal destination index, an
* <code>IllegalArgumentException</code> is thrown.
*
* <p> Thumbnails will be returned in their entirety regardless of the
* region settings.
*
* <p> If any of the supplied <code>ImageReadParam</code>s contain
* optional setting values not supported by this reader (<i>e.g.</i>
* source render size or any format-specific settings), they will
* be ignored.
*
* @param params an <code>Iterator</code> containing
* <code>ImageReadParam</code> objects.
*
* @return an <code>Iterator</code> representing the
* contents of the input source as <code>IIOImage</code>s.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IllegalArgumentException if any
* non-<code>null</code> element of <code>params</code> is not an
* <code>ImageReadParam</code>.
* @exception IllegalArgumentException if the set of source and
* destination bands specified by
* <code>param.getSourceBands</code> and
* <code>param.getDestinationBands</code> differ in length or
* include indices that are out of bounds.
* @exception IllegalArgumentException if a resulting image would
* have a width or height less than 1.
* @exception IOException if an error occurs during reading.
*
* @see ImageReadParam
* @see IIOImage
*/
public
Iterator<
IIOImage>
readAll(
Iterator<? extends
ImageReadParam>
params)
throws
IOException
{
List output = new
ArrayList();
int
imageIndex =
getMinIndex();
// Inform IIOReadProgressListeners we're starting a sequence
processSequenceStarted(
imageIndex);
while (true) {
// Inform IIOReadProgressListeners and IIOReadUpdateListeners
// that we're starting a new image
ImageReadParam param = null;
if (
params != null &&
params.
hasNext()) {
Object o =
params.
next();
if (
o != null) {
if (
o instanceof
ImageReadParam) {
param = (
ImageReadParam)
o;
} else {
throw new
IllegalArgumentException
("Non-ImageReadParam supplied as part of params!");
}
}
}
BufferedImage bi = null;
try {
bi =
read(
imageIndex,
param);
} catch (
IndexOutOfBoundsException e) {
break;
}
ArrayList thumbnails = null;
int
numThumbnails =
getNumThumbnails(
imageIndex);
if (
numThumbnails > 0) {
thumbnails = new
ArrayList();
for (int
j = 0;
j <
numThumbnails;
j++) {
thumbnails.
add(
readThumbnail(
imageIndex,
j));
}
}
IIOMetadata metadata =
getImageMetadata(
imageIndex);
IIOImage im = new
IIOImage(
bi,
thumbnails,
metadata);
output.
add(
im);
++
imageIndex;
}
// Inform IIOReadProgressListeners we're ending a sequence
processSequenceComplete();
return
output.
iterator();
}
/**
* Returns <code>true</code> if this plug-in supports reading
* just a {@link java.awt.image.Raster Raster} of pixel data.
* If this method returns <code>false</code>, calls to
* {@link #readRaster readRaster} or {@link #readTileRaster readTileRaster}
* will throw an <code>UnsupportedOperationException</code>.
*
* <p> The default implementation returns <code>false</code>.
*
* @return <code>true</code> if this plug-in supports reading raw
* <code>Raster</code>s.
*
* @see #readRaster
* @see #readTileRaster
*/
public boolean
canReadRaster() {
return false;
}
/**
* Returns a new <code>Raster</code> object containing the raw pixel data
* from the image stream, without any color conversion applied. The
* application must determine how to interpret the pixel data by other
* means. Any destination or image-type parameters in the supplied
* <code>ImageReadParam</code> object are ignored, but all other
* parameters are used exactly as in the {@link #read read}
* method, except that any destination offset is used as a logical rather
* than a physical offset. The size of the returned <code>Raster</code>
* will always be that of the source region clipped to the actual image.
* Logical offsets in the stream itself are ignored.
*
* <p> This method allows formats that normally apply a color
* conversion, such as JPEG, and formats that do not normally have an
* associated colorspace, such as remote sensing or medical imaging data,
* to provide access to raw pixel data.
*
* <p> Any registered <code>readUpdateListener</code>s are ignored, as
* there is no <code>BufferedImage</code>, but all other listeners are
* called exactly as they are for the {@link #read read} method.
*
* <p> If {@link #canReadRaster canReadRaster()} returns
* <code>false</code>, this method throws an
* <code>UnsupportedOperationException</code>.
*
* <p> If the supplied <code>ImageReadParam</code> contains
* optional setting values not supported by this reader (<i>e.g.</i>
* source render size or any format-specific settings), they will
* be ignored.
*
* <p> The default implementation throws an
* <code>UnsupportedOperationException</code>.
*
* @param imageIndex the index of the image to be read.
* @param param an <code>ImageReadParam</code> used to control
* the reading process, or <code>null</code>.
*
* @return the desired portion of the image as a
* <code>Raster</code>.
*
* @exception UnsupportedOperationException if this plug-in does not
* support reading raw <code>Raster</code>s.
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs during reading.
*
* @see #canReadRaster
* @see #read
* @see java.awt.image.Raster
*/
public
Raster readRaster(int
imageIndex,
ImageReadParam param)
throws
IOException {
throw new
UnsupportedOperationException("readRaster not supported!");
}
/**
* Returns <code>true</code> if the image is organized into
* <i>tiles</i>, that is, equal-sized non-overlapping rectangles.
*
* <p> A reader plug-in may choose whether or not to expose tiling
* that is present in the image as it is stored. It may even
* choose to advertise tiling when none is explicitly present. In
* general, tiling should only be advertised if there is some
* advantage (in speed or space) to accessing individual tiles.
* Regardless of whether the reader advertises tiling, it must be
* capable of reading an arbitrary rectangular region specified in
* an <code>ImageReadParam</code>.
*
* <p> A reader for which all images are guaranteed to be tiled,
* or are guaranteed not to be tiled, may return <code>true</code>
* or <code>false</code> respectively without accessing any image
* data. In such cases, it is not necessary to throw an exception
* even if no input source has been set or the image index is out
* of bounds.
*
* <p> The default implementation just returns <code>false</code>.
*
* @param imageIndex the index of the image to be queried.
*
* @return <code>true</code> if the image is tiled.
*
* @exception IllegalStateException if an input source is required
* to determine the return value, but none has been set.
* @exception IndexOutOfBoundsException if an image must be
* accessed to determine the return value, but the supplied index
* is out of bounds.
* @exception IOException if an error occurs during reading.
*/
public boolean
isImageTiled(int
imageIndex) throws
IOException {
return false;
}
/**
* Returns the width of a tile in the given image.
*
* <p> The default implementation simply returns
* <code>getWidth(imageIndex)</code>, which is correct for
* non-tiled images. Readers that support tiling should override
* this method.
*
* @return the width of a tile.
*
* @param imageIndex the index of the image to be queried.
*
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs during reading.
*/
public int
getTileWidth(int
imageIndex) throws
IOException {
return
getWidth(
imageIndex);
}
/**
* Returns the height of a tile in the given image.
*
* <p> The default implementation simply returns
* <code>getHeight(imageIndex)</code>, which is correct for
* non-tiled images. Readers that support tiling should override
* this method.
*
* @return the height of a tile.
*
* @param imageIndex the index of the image to be queried.
*
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IOException if an error occurs during reading.
*/
public int
getTileHeight(int
imageIndex) throws
IOException {
return
getHeight(
imageIndex);
}
/**
* Returns the X coordinate of the upper-left corner of tile (0,
* 0) in the given image.
*
* <p> A reader for which the tile grid X offset always has the
* same value (usually 0), may return the value without accessing
* any image data. In such cases, it is not necessary to throw an
* exception even if no input source has been set or the image
* index is out of bounds.
*
* <p> The default implementation simply returns 0, which is
* correct for non-tiled images and tiled images in most formats.
* Readers that support tiling with non-(0, 0) offsets should
* override this method.
*
* @return the X offset of the tile grid.
*
* @param imageIndex the index of the image to be queried.
*
* @exception IllegalStateException if an input source is required
* to determine the return value, but none has been set.
* @exception IndexOutOfBoundsException if an image must be
* accessed to determine the return value, but the supplied index
* is out of bounds.
* @exception IOException if an error occurs during reading.
*/
public int
getTileGridXOffset(int
imageIndex) throws
IOException {
return 0;
}
/**
* Returns the Y coordinate of the upper-left corner of tile (0,
* 0) in the given image.
*
* <p> A reader for which the tile grid Y offset always has the
* same value (usually 0), may return the value without accessing
* any image data. In such cases, it is not necessary to throw an
* exception even if no input source has been set or the image
* index is out of bounds.
*
* <p> The default implementation simply returns 0, which is
* correct for non-tiled images and tiled images in most formats.
* Readers that support tiling with non-(0, 0) offsets should
* override this method.
*
* @return the Y offset of the tile grid.
*
* @param imageIndex the index of the image to be queried.
*
* @exception IllegalStateException if an input source is required
* to determine the return value, but none has been set.
* @exception IndexOutOfBoundsException if an image must be
* accessed to determine the return value, but the supplied index
* is out of bounds.
* @exception IOException if an error occurs during reading.
*/
public int
getTileGridYOffset(int
imageIndex) throws
IOException {
return 0;
}
/**
* Reads the tile indicated by the <code>tileX</code> and
* <code>tileY</code> arguments, returning it as a
* <code>BufferedImage</code>. If the arguments are out of range,
* an <code>IllegalArgumentException</code> is thrown. If the
* image is not tiled, the values 0, 0 will return the entire
* image; any other values will cause an
* <code>IllegalArgumentException</code> to be thrown.
*
* <p> This method is merely a convenience equivalent to calling
* <code>read(int, ImageReadParam)</code> with a read param
* specifying a source region having offsets of
* <code>tileX*getTileWidth(imageIndex)</code>,
* <code>tileY*getTileHeight(imageIndex)</code> and width and
* height of <code>getTileWidth(imageIndex)</code>,
* <code>getTileHeight(imageIndex)</code>; and subsampling
* factors of 1 and offsets of 0. To subsample a tile, call
* <code>read</code> with a read param specifying this region
* and different subsampling parameters.
*
* <p> The default implementation returns the entire image if
* <code>tileX</code> and <code>tileY</code> are 0, or throws
* an <code>IllegalArgumentException</code> otherwise.
*
* @param imageIndex the index of the image to be retrieved.
* @param tileX the column index (starting with 0) of the tile
* to be retrieved.
* @param tileY the row index (starting with 0) of the tile
* to be retrieved.
*
* @return the tile as a <code>BufferedImage</code>.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if <code>imageIndex</code>
* is out of bounds.
* @exception IllegalArgumentException if the tile indices are
* out of bounds.
* @exception IOException if an error occurs during reading.
*/
public
BufferedImage readTile(int
imageIndex,
int
tileX, int
tileY) throws
IOException {
if ((
tileX != 0) || (
tileY != 0)) {
throw new
IllegalArgumentException("Invalid tile indices");
}
return
read(
imageIndex);
}
/**
* Returns a new <code>Raster</code> object containing the raw
* pixel data from the tile, without any color conversion applied.
* The application must determine how to interpret the pixel data by other
* means.
*
* <p> If {@link #canReadRaster canReadRaster()} returns
* <code>false</code>, this method throws an
* <code>UnsupportedOperationException</code>.
*
* <p> The default implementation checks if reading
* <code>Raster</code>s is supported, and if so calls {@link
* #readRaster readRaster(imageIndex, null)} if
* <code>tileX</code> and <code>tileY</code> are 0, or throws an
* <code>IllegalArgumentException</code> otherwise.
*
* @param imageIndex the index of the image to be retrieved.
* @param tileX the column index (starting with 0) of the tile
* to be retrieved.
* @param tileY the row index (starting with 0) of the tile
* to be retrieved.
*
* @return the tile as a <code>Raster</code>.
*
* @exception UnsupportedOperationException if this plug-in does not
* support reading raw <code>Raster</code>s.
* @exception IllegalArgumentException if the tile indices are
* out of bounds.
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if <code>imageIndex</code>
* is out of bounds.
* @exception IOException if an error occurs during reading.
*
* @see #readTile
* @see #readRaster
* @see java.awt.image.Raster
*/
public
Raster readTileRaster(int
imageIndex,
int
tileX, int
tileY) throws
IOException {
if (!
canReadRaster()) {
throw new
UnsupportedOperationException
("readTileRaster not supported!");
}
if ((
tileX != 0) || (
tileY != 0)) {
throw new
IllegalArgumentException("Invalid tile indices");
}
return
readRaster(
imageIndex, null);
}
// RenderedImages
/**
* Returns a <code>RenderedImage</code> object that contains the
* contents of the image indexed by <code>imageIndex</code>. By
* default, the returned image is simply the
* <code>BufferedImage</code> returned by <code>read(imageIndex,
* param)</code>.
*
* <p> The semantics of this method may differ from those of the
* other <code>read</code> methods in several ways. First, any
* destination image and/or image type set in the
* <code>ImageReadParam</code> may be ignored. Second, the usual
* listener calls are not guaranteed to be made, or to be
* meaningful if they are. This is because the returned image may
* not be fully populated with pixel data at the time it is
* returned, or indeed at any time.
*
* <p> If the supplied <code>ImageReadParam</code> contains
* optional setting values not supported by this reader (<i>e.g.</i>
* source render size or any format-specific settings), they will
* be ignored.
*
* <p> The default implementation just calls
* {@link #read read(imageIndex, param)}.
*
* @param imageIndex the index of the image to be retrieved.
* @param param an <code>ImageReadParam</code> used to control
* the reading process, or <code>null</code>.
*
* @return a <code>RenderedImage</code> object providing a view of
* the image.
*
* @exception IllegalStateException if the input source has not been
* set.
* @exception IndexOutOfBoundsException if the supplied index is
* out of bounds.
* @exception IllegalArgumentException if the set of source and
* destination bands specified by
* <code>param.getSourceBands</code> and
* <code>param.getDestinationBands</code> differ in length or
* include indices that are out of bounds.
* @exception IllegalArgumentException if the resulting image
* would have a width or height less than 1.
* @exception IOException if an error occurs during reading.
*/
public
RenderedImage readAsRenderedImage(int
imageIndex,
ImageReadParam param)
throws
IOException {
return
read(
imageIndex,
param);
}
// Thumbnails
/**
* Returns <code>true</code> if the image format understood by
* this reader supports thumbnail preview images associated with
* it. The default implementation returns <code>false</code>.
*
* <p> If this method returns <code>false</code>,
* <code>hasThumbnails</code> and <code>getNumThumbnails</code>
* will return <code>false</code> and <code>0</code>,
* respectively, and <code>readThumbnail</code> will throw an
* <code>UnsupportedOperationException</code>, regardless of their
* arguments.
*
* <p> A reader that does not support thumbnails need not
* implement any of the thumbnail-related methods.
*
* @return <code>true</code> if thumbnails are supported.
*/
public boolean
readerSupportsThumbnails() {
return false;
}
/**
* Returns <code>true</code> if the given image has thumbnail
* preview images associated with it. If the format does not
* support thumbnails (<code>readerSupportsThumbnails</code>
* returns <code>false</code>), <code>false</code> will be
* returned regardless of whether an input source has been set or
* whether <code>imageIndex</code> is in bounds.
*
* <p> The default implementation returns <code>true</code> if
* <code>getNumThumbnails</code> returns a value greater than 0.
*
* @param imageIndex the index of the image being queried.
*
* @return <code>true</code> if the given image has thumbnails.
*
* @exception IllegalStateException if the reader supports
* thumbnails but the input source has not been set.
* @exception IndexOutOfBoundsException if the reader supports
* thumbnails but <code>imageIndex</code> is out of bounds.
* @exception IOException if an error occurs during reading.
*/
public boolean
hasThumbnails(int
imageIndex) throws
IOException {
return
getNumThumbnails(
imageIndex) > 0;
}
/**
* Returns the number of thumbnail preview images associated with
* the given image. If the format does not support thumbnails,
* (<code>readerSupportsThumbnails</code> returns
* <code>false</code>), <code>0</code> will be returned regardless
* of whether an input source has been set or whether
* <code>imageIndex</code> is in bounds.
*
* <p> The default implementation returns 0 without checking its
* argument.
*
* @param imageIndex the index of the image being queried.
*
* @return the number of thumbnails associated with the given
* image.
*
* @exception IllegalStateException if the reader supports
* thumbnails but the input source has not been set.
* @exception IndexOutOfBoundsException if the reader supports
* thumbnails but <code>imageIndex</code> is out of bounds.
* @exception IOException if an error occurs during reading.
*/
public int
getNumThumbnails(int
imageIndex)
throws
IOException {
return 0;
}
/**
* Returns the width of the thumbnail preview image indexed by
* <code>thumbnailIndex</code>, associated with the image indexed
* by <code>ImageIndex</code>.
*
* <p> If the reader does not support thumbnails,
* (<code>readerSupportsThumbnails</code> returns
* <code>false</code>), an <code>UnsupportedOperationException</code>
* will be thrown.
*
* <p> The default implementation simply returns
* <code>readThumbnail(imageindex,
* thumbnailIndex).getWidth()</code>. Subclasses should therefore
* override this method if possible in order to avoid forcing the
* thumbnail to be read.
*
* @param imageIndex the index of the image to be retrieved.
* @param thumbnailIndex the index of the thumbnail to be retrieved.
*
* @return the width of the desired thumbnail as an <code>int</code>.
*
* @exception UnsupportedOperationException if thumbnails are not
* supported.
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if either of the supplied
* indices are out of bounds.
* @exception IOException if an error occurs during reading.
*/
public int
getThumbnailWidth(int
imageIndex, int
thumbnailIndex)
throws
IOException {
return
readThumbnail(
imageIndex,
thumbnailIndex).
getWidth();
}
/**
* Returns the height of the thumbnail preview image indexed by
* <code>thumbnailIndex</code>, associated with the image indexed
* by <code>ImageIndex</code>.
*
* <p> If the reader does not support thumbnails,
* (<code>readerSupportsThumbnails</code> returns
* <code>false</code>), an <code>UnsupportedOperationException</code>
* will be thrown.
*
* <p> The default implementation simply returns
* <code>readThumbnail(imageindex,
* thumbnailIndex).getHeight()</code>. Subclasses should
* therefore override this method if possible in order to avoid
* forcing the thumbnail to be read.
*
* @param imageIndex the index of the image to be retrieved.
* @param thumbnailIndex the index of the thumbnail to be retrieved.
*
* @return the height of the desired thumbnail as an <code>int</code>.
*
* @exception UnsupportedOperationException if thumbnails are not
* supported.
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if either of the supplied
* indices are out of bounds.
* @exception IOException if an error occurs during reading.
*/
public int
getThumbnailHeight(int
imageIndex, int
thumbnailIndex)
throws
IOException {
return
readThumbnail(
imageIndex,
thumbnailIndex).
getHeight();
}
/**
* Returns the thumbnail preview image indexed by
* <code>thumbnailIndex</code>, associated with the image indexed
* by <code>ImageIndex</code> as a <code>BufferedImage</code>.
*
* <p> Any registered <code>IIOReadProgressListener</code> objects
* will be notified by calling their
* <code>thumbnailStarted</code>, <code>thumbnailProgress</code>,
* and <code>thumbnailComplete</code> methods.
*
* <p> If the reader does not support thumbnails,
* (<code>readerSupportsThumbnails</code> returns
* <code>false</code>), an <code>UnsupportedOperationException</code>
* will be thrown regardless of whether an input source has been
* set or whether the indices are in bounds.
*
* <p> The default implementation throws an
* <code>UnsupportedOperationException</code>.
*
* @param imageIndex the index of the image to be retrieved.
* @param thumbnailIndex the index of the thumbnail to be retrieved.
*
* @return the desired thumbnail as a <code>BufferedImage</code>.
*
* @exception UnsupportedOperationException if thumbnails are not
* supported.
* @exception IllegalStateException if the input source has not been set.
* @exception IndexOutOfBoundsException if either of the supplied
* indices are out of bounds.
* @exception IOException if an error occurs during reading.
*/
public
BufferedImage readThumbnail(int
imageIndex,
int
thumbnailIndex)
throws
IOException {
throw new
UnsupportedOperationException("Thumbnails not supported!");
}
// Abort
/**
* Requests that any current read operation be aborted. The
* contents of the image following the abort will be undefined.
*
* <p> Readers should call <code>clearAbortRequest</code> at the
* beginning of each read operation, and poll the value of
* <code>abortRequested</code> regularly during the read.
*/
public synchronized void
abort() {
this.
abortFlag = true;
}
/**
* Returns <code>true</code> if a request to abort the current
* read operation has been made since the reader was instantiated or
* <code>clearAbortRequest</code> was called.
*
* @return <code>true</code> if the current read operation should
* be aborted.
*
* @see #abort
* @see #clearAbortRequest
*/
protected synchronized boolean
abortRequested() {
return this.
abortFlag;
}
/**
* Clears any previous abort request. After this method has been
* called, <code>abortRequested</code> will return
* <code>false</code>.
*
* @see #abort
* @see #abortRequested
*/
protected synchronized void
clearAbortRequest() {
this.
abortFlag = false;
}
// Listeners
// Add an element to a list, creating a new list if the
// existing list is null, and return the list.
static
List addToList(
List l,
Object elt) {
if (
l == null) {
l = new
ArrayList();
}
l.
add(
elt);
return
l;
}
// Remove an element from a list, discarding the list if the
// resulting list is empty, and return the list or null.
static
List removeFromList(
List l,
Object elt) {
if (
l == null) {
return
l;
}
l.
remove(
elt);
if (
l.
size() == 0) {
l = null;
}
return
l;
}
/**
* Adds an <code>IIOReadWarningListener</code> to the list of
* registered warning listeners. If <code>listener</code> is
* <code>null</code>, no exception will be thrown and no action
* will be taken. Messages sent to the given listener will be
* localized, if possible, to match the current
* <code>Locale</code>. If no <code>Locale</code> has been set,
* warning messages may be localized as the reader sees fit.
*
* @param listener an <code>IIOReadWarningListener</code> to be registered.
*
* @see #removeIIOReadWarningListener
*/
public void
addIIOReadWarningListener(
IIOReadWarningListener listener) {
if (
listener == null) {
return;
}
warningListeners =
addToList(
warningListeners,
listener);
warningLocales =
addToList(
warningLocales,
getLocale());
}
/**
* Removes an <code>IIOReadWarningListener</code> from the list of
* registered error listeners. If the listener was not previously
* registered, or if <code>listener</code> is <code>null</code>,
* no exception will be thrown and no action will be taken.
*
* @param listener an IIOReadWarningListener to be unregistered.
*
* @see #addIIOReadWarningListener
*/
public void
removeIIOReadWarningListener(
IIOReadWarningListener listener) {
if (
listener == null ||
warningListeners == null) {
return;
}
int
index =
warningListeners.
indexOf(
listener);
if (
index != -1) {
warningListeners.
remove(
index);
warningLocales.
remove(
index);
if (
warningListeners.
size() == 0) {
warningListeners = null;
warningLocales = null;
}
}
}
/**
* Removes all currently registered
* <code>IIOReadWarningListener</code> objects.
*
* <p> The default implementation sets the
* <code>warningListeners</code> and <code>warningLocales</code>
* instance variables to <code>null</code>.
*/
public void
removeAllIIOReadWarningListeners() {
warningListeners = null;
warningLocales = null;
}
/**
* Adds an <code>IIOReadProgressListener</code> to the list of
* registered progress listeners. If <code>listener</code> is
* <code>null</code>, no exception will be thrown and no action
* will be taken.
*
* @param listener an IIOReadProgressListener to be registered.
*
* @see #removeIIOReadProgressListener
*/
public void
addIIOReadProgressListener(
IIOReadProgressListener listener) {
if (
listener == null) {
return;
}
progressListeners =
addToList(
progressListeners,
listener);
}
/**
* Removes an <code>IIOReadProgressListener</code> from the list
* of registered progress listeners. If the listener was not
* previously registered, or if <code>listener</code> is
* <code>null</code>, no exception will be thrown and no action
* will be taken.
*
* @param listener an IIOReadProgressListener to be unregistered.
*
* @see #addIIOReadProgressListener
*/
public void
removeIIOReadProgressListener (
IIOReadProgressListener listener) {
if (
listener == null ||
progressListeners == null) {
return;
}
progressListeners =
removeFromList(
progressListeners,
listener);
}
/**
* Removes all currently registered
* <code>IIOReadProgressListener</code> objects.
*
* <p> The default implementation sets the
* <code>progressListeners</code> instance variable to
* <code>null</code>.
*/
public void
removeAllIIOReadProgressListeners() {
progressListeners = null;
}
/**
* Adds an <code>IIOReadUpdateListener</code> to the list of
* registered update listeners. If <code>listener</code> is
* <code>null</code>, no exception will be thrown and no action
* will be taken. The listener will receive notification of pixel
* updates as images and thumbnails are decoded, including the
* starts and ends of progressive passes.
*
* <p> If no update listeners are present, the reader may choose
* to perform fewer updates to the pixels of the destination
* images and/or thumbnails, which may result in more efficient
* decoding.
*
* <p> For example, in progressive JPEG decoding each pass
* contains updates to a set of coefficients, which would have to
* be transformed into pixel values and converted to an RGB color
* space for each pass if listeners are present. If no listeners
* are present, the coefficients may simply be accumulated and the
* final results transformed and color converted one time only.
*
* <p> The final results of decoding will be the same whether or
* not intermediate updates are performed. Thus if only the final
* image is desired it may be preferable not to register any
* <code>IIOReadUpdateListener</code>s. In general, progressive
* updating is most effective when fetching images over a network
* connection that is very slow compared to local CPU processing;
* over a fast connection, progressive updates may actually slow
* down the presentation of the image.
*
* @param listener an IIOReadUpdateListener to be registered.
*
* @see #removeIIOReadUpdateListener
*/
public void
addIIOReadUpdateListener(
IIOReadUpdateListener listener) {
if (
listener == null) {
return;
}
updateListeners =
addToList(
updateListeners,
listener);
}
/**
* Removes an <code>IIOReadUpdateListener</code> from the list of
* registered update listeners. If the listener was not
* previously registered, or if <code>listener</code> is
* <code>null</code>, no exception will be thrown and no action
* will be taken.
*
* @param listener an IIOReadUpdateListener to be unregistered.
*
* @see #addIIOReadUpdateListener
*/
public void
removeIIOReadUpdateListener(
IIOReadUpdateListener listener) {
if (
listener == null ||
updateListeners == null) {
return;
}
updateListeners =
removeFromList(
updateListeners,
listener);
}
/**
* Removes all currently registered
* <code>IIOReadUpdateListener</code> objects.
*
* <p> The default implementation sets the
* <code>updateListeners</code> instance variable to
* <code>null</code>.
*/
public void
removeAllIIOReadUpdateListeners() {
updateListeners = null;
}
/**
* Broadcasts the start of an sequence of image reads to all
* registered <code>IIOReadProgressListener</code>s by calling
* their <code>sequenceStarted</code> method. Subclasses may use
* this method as a convenience.
*
* @param minIndex the lowest index being read.
*/
protected void
processSequenceStarted(int
minIndex) {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
sequenceStarted(this,
minIndex);
}
}
/**
* Broadcasts the completion of an sequence of image reads to all
* registered <code>IIOReadProgressListener</code>s by calling
* their <code>sequenceComplete</code> method. Subclasses may use
* this method as a convenience.
*/
protected void
processSequenceComplete() {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
sequenceComplete(this);
}
}
/**
* Broadcasts the start of an image read to all registered
* <code>IIOReadProgressListener</code>s by calling their
* <code>imageStarted</code> method. Subclasses may use this
* method as a convenience.
*
* @param imageIndex the index of the image about to be read.
*/
protected void
processImageStarted(int
imageIndex) {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
imageStarted(this,
imageIndex);
}
}
/**
* Broadcasts the current percentage of image completion to all
* registered <code>IIOReadProgressListener</code>s by calling
* their <code>imageProgress</code> method. Subclasses may use
* this method as a convenience.
*
* @param percentageDone the current percentage of completion,
* as a <code>float</code>.
*/
protected void
processImageProgress(float
percentageDone) {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
imageProgress(this,
percentageDone);
}
}
/**
* Broadcasts the completion of an image read to all registered
* <code>IIOReadProgressListener</code>s by calling their
* <code>imageComplete</code> method. Subclasses may use this
* method as a convenience.
*/
protected void
processImageComplete() {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
imageComplete(this);
}
}
/**
* Broadcasts the start of a thumbnail read to all registered
* <code>IIOReadProgressListener</code>s by calling their
* <code>thumbnailStarted</code> method. Subclasses may use this
* method as a convenience.
*
* @param imageIndex the index of the image associated with the
* thumbnail.
* @param thumbnailIndex the index of the thumbnail.
*/
protected void
processThumbnailStarted(int
imageIndex,
int
thumbnailIndex) {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
thumbnailStarted(this,
imageIndex,
thumbnailIndex);
}
}
/**
* Broadcasts the current percentage of thumbnail completion to
* all registered <code>IIOReadProgressListener</code>s by calling
* their <code>thumbnailProgress</code> method. Subclasses may
* use this method as a convenience.
*
* @param percentageDone the current percentage of completion,
* as a <code>float</code>.
*/
protected void
processThumbnailProgress(float
percentageDone) {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
thumbnailProgress(this,
percentageDone);
}
}
/**
* Broadcasts the completion of a thumbnail read to all registered
* <code>IIOReadProgressListener</code>s by calling their
* <code>thumbnailComplete</code> method. Subclasses may use this
* method as a convenience.
*/
protected void
processThumbnailComplete() {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
thumbnailComplete(this);
}
}
/**
* Broadcasts that the read has been aborted to all registered
* <code>IIOReadProgressListener</code>s by calling their
* <code>readAborted</code> method. Subclasses may use this
* method as a convenience.
*/
protected void
processReadAborted() {
if (
progressListeners == null) {
return;
}
int
numListeners =
progressListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadProgressListener listener =
(
IIOReadProgressListener)
progressListeners.
get(
i);
listener.
readAborted(this);
}
}
/**
* Broadcasts the beginning of a progressive pass to all
* registered <code>IIOReadUpdateListener</code>s by calling their
* <code>passStarted</code> method. Subclasses may use this
* method as a convenience.
*
* @param theImage the <code>BufferedImage</code> being updated.
* @param pass the index of the current pass, starting with 0.
* @param minPass the index of the first pass that will be decoded.
* @param maxPass the index of the last pass that will be decoded.
* @param minX the X coordinate of the upper-left pixel included
* in the pass.
* @param minY the X coordinate of the upper-left pixel included
* in the pass.
* @param periodX the horizontal separation between pixels.
* @param periodY the vertical separation between pixels.
* @param bands an array of <code>int</code>s indicating the
* set of affected bands of the destination.
*/
protected void
processPassStarted(
BufferedImage theImage,
int
pass,
int
minPass, int
maxPass,
int
minX, int
minY,
int
periodX, int
periodY,
int[]
bands) {
if (
updateListeners == null) {
return;
}
int
numListeners =
updateListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadUpdateListener listener =
(
IIOReadUpdateListener)
updateListeners.
get(
i);
listener.
passStarted(this,
theImage,
pass,
minPass,
maxPass,
minX,
minY,
periodX,
periodY,
bands);
}
}
/**
* Broadcasts the update of a set of samples to all registered
* <code>IIOReadUpdateListener</code>s by calling their
* <code>imageUpdate</code> method. Subclasses may use this
* method as a convenience.
*
* @param theImage the <code>BufferedImage</code> being updated.
* @param minX the X coordinate of the upper-left pixel included
* in the pass.
* @param minY the X coordinate of the upper-left pixel included
* in the pass.
* @param width the total width of the area being updated, including
* pixels being skipped if <code>periodX > 1</code>.
* @param height the total height of the area being updated,
* including pixels being skipped if <code>periodY > 1</code>.
* @param periodX the horizontal separation between pixels.
* @param periodY the vertical separation between pixels.
* @param bands an array of <code>int</code>s indicating the
* set of affected bands of the destination.
*/
protected void
processImageUpdate(
BufferedImage theImage,
int
minX, int
minY,
int
width, int
height,
int
periodX, int
periodY,
int[]
bands) {
if (
updateListeners == null) {
return;
}
int
numListeners =
updateListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadUpdateListener listener =
(
IIOReadUpdateListener)
updateListeners.
get(
i);
listener.
imageUpdate(this,
theImage,
minX,
minY,
width,
height,
periodX,
periodY,
bands);
}
}
/**
* Broadcasts the end of a progressive pass to all
* registered <code>IIOReadUpdateListener</code>s by calling their
* <code>passComplete</code> method. Subclasses may use this
* method as a convenience.
*
* @param theImage the <code>BufferedImage</code> being updated.
*/
protected void
processPassComplete(
BufferedImage theImage) {
if (
updateListeners == null) {
return;
}
int
numListeners =
updateListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadUpdateListener listener =
(
IIOReadUpdateListener)
updateListeners.
get(
i);
listener.
passComplete(this,
theImage);
}
}
/**
* Broadcasts the beginning of a thumbnail progressive pass to all
* registered <code>IIOReadUpdateListener</code>s by calling their
* <code>thumbnailPassStarted</code> method. Subclasses may use this
* method as a convenience.
*
* @param theThumbnail the <code>BufferedImage</code> thumbnail
* being updated.
* @param pass the index of the current pass, starting with 0.
* @param minPass the index of the first pass that will be decoded.
* @param maxPass the index of the last pass that will be decoded.
* @param minX the X coordinate of the upper-left pixel included
* in the pass.
* @param minY the X coordinate of the upper-left pixel included
* in the pass.
* @param periodX the horizontal separation between pixels.
* @param periodY the vertical separation between pixels.
* @param bands an array of <code>int</code>s indicating the
* set of affected bands of the destination.
*/
protected void
processThumbnailPassStarted(
BufferedImage theThumbnail,
int
pass,
int
minPass, int
maxPass,
int
minX, int
minY,
int
periodX, int
periodY,
int[]
bands) {
if (
updateListeners == null) {
return;
}
int
numListeners =
updateListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadUpdateListener listener =
(
IIOReadUpdateListener)
updateListeners.
get(
i);
listener.
thumbnailPassStarted(this,
theThumbnail,
pass,
minPass,
maxPass,
minX,
minY,
periodX,
periodY,
bands);
}
}
/**
* Broadcasts the update of a set of samples in a thumbnail image
* to all registered <code>IIOReadUpdateListener</code>s by
* calling their <code>thumbnailUpdate</code> method. Subclasses may
* use this method as a convenience.
*
* @param theThumbnail the <code>BufferedImage</code> thumbnail
* being updated.
* @param minX the X coordinate of the upper-left pixel included
* in the pass.
* @param minY the X coordinate of the upper-left pixel included
* in the pass.
* @param width the total width of the area being updated, including
* pixels being skipped if <code>periodX > 1</code>.
* @param height the total height of the area being updated,
* including pixels being skipped if <code>periodY > 1</code>.
* @param periodX the horizontal separation between pixels.
* @param periodY the vertical separation between pixels.
* @param bands an array of <code>int</code>s indicating the
* set of affected bands of the destination.
*/
protected void
processThumbnailUpdate(
BufferedImage theThumbnail,
int
minX, int
minY,
int
width, int
height,
int
periodX, int
periodY,
int[]
bands) {
if (
updateListeners == null) {
return;
}
int
numListeners =
updateListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadUpdateListener listener =
(
IIOReadUpdateListener)
updateListeners.
get(
i);
listener.
thumbnailUpdate(this,
theThumbnail,
minX,
minY,
width,
height,
periodX,
periodY,
bands);
}
}
/**
* Broadcasts the end of a thumbnail progressive pass to all
* registered <code>IIOReadUpdateListener</code>s by calling their
* <code>thumbnailPassComplete</code> method. Subclasses may use this
* method as a convenience.
*
* @param theThumbnail the <code>BufferedImage</code> thumbnail
* being updated.
*/
protected void
processThumbnailPassComplete(
BufferedImage theThumbnail) {
if (
updateListeners == null) {
return;
}
int
numListeners =
updateListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadUpdateListener listener =
(
IIOReadUpdateListener)
updateListeners.
get(
i);
listener.
thumbnailPassComplete(this,
theThumbnail);
}
}
/**
* Broadcasts a warning message to all registered
* <code>IIOReadWarningListener</code>s by calling their
* <code>warningOccurred</code> method. Subclasses may use this
* method as a convenience.
*
* @param warning the warning message to send.
*
* @exception IllegalArgumentException if <code>warning</code>
* is <code>null</code>.
*/
protected void
processWarningOccurred(
String warning) {
if (
warningListeners == null) {
return;
}
if (
warning == null) {
throw new
IllegalArgumentException("warning == null!");
}
int
numListeners =
warningListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadWarningListener listener =
(
IIOReadWarningListener)
warningListeners.
get(
i);
listener.
warningOccurred(this,
warning);
}
}
/**
* Broadcasts a localized warning message to all registered
* <code>IIOReadWarningListener</code>s by calling their
* <code>warningOccurred</code> method with a string taken
* from a <code>ResourceBundle</code>. Subclasses may use this
* method as a convenience.
*
* @param baseName the base name of a set of
* <code>ResourceBundle</code>s containing localized warning
* messages.
* @param keyword the keyword used to index the warning message
* within the set of <code>ResourceBundle</code>s.
*
* @exception IllegalArgumentException if <code>baseName</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>keyword</code>
* is <code>null</code>.
* @exception IllegalArgumentException if no appropriate
* <code>ResourceBundle</code> may be located.
* @exception IllegalArgumentException if the named resource is
* not found in the located <code>ResourceBundle</code>.
* @exception IllegalArgumentException if the object retrieved
* from the <code>ResourceBundle</code> is not a
* <code>String</code>.
*/
protected void
processWarningOccurred(
String baseName,
String keyword) {
if (
warningListeners == null) {
return;
}
if (
baseName == null) {
throw new
IllegalArgumentException("baseName == null!");
}
if (
keyword == null) {
throw new
IllegalArgumentException("keyword == null!");
}
int
numListeners =
warningListeners.
size();
for (int
i = 0;
i <
numListeners;
i++) {
IIOReadWarningListener listener =
(
IIOReadWarningListener)
warningListeners.
get(
i);
Locale locale = (
Locale)
warningLocales.
get(
i);
if (
locale == null) {
locale =
Locale.
getDefault();
}
/**
* If an applet supplies an implementation of ImageReader and
* resource bundles, then the resource bundle will need to be
* accessed via the applet class loader. So first try the context
* class loader to locate the resource bundle.
* If that throws MissingResourceException, then try the
* system class loader.
*/
ClassLoader loader = (
ClassLoader)
java.security.
AccessController.
doPrivileged(
new java.security.
PrivilegedAction() {
public
Object run() {
return
Thread.
currentThread().
getContextClassLoader();
}
});
ResourceBundle bundle = null;
try {
bundle =
ResourceBundle.
getBundle(
baseName,
locale,
loader);
} catch (
MissingResourceException mre) {
try {
bundle =
ResourceBundle.
getBundle(
baseName,
locale);
} catch (
MissingResourceException mre1) {
throw new
IllegalArgumentException("Bundle not found!");
}
}
String warning = null;
try {
warning =
bundle.
getString(
keyword);
} catch (
ClassCastException cce) {
throw new
IllegalArgumentException("Resource is not a String!");
} catch (
MissingResourceException mre) {
throw new
IllegalArgumentException("Resource is missing!");
}
listener.
warningOccurred(this,
warning);
}
}
// State management
/**
* Restores the <code>ImageReader</code> to its initial state.
*
* <p> The default implementation calls <code>setInput(null,
* false)</code>, <code>setLocale(null)</code>,
* <code>removeAllIIOReadUpdateListeners()</code>,
* <code>removeAllIIOReadWarningListeners()</code>,
* <code>removeAllIIOReadProgressListeners()</code>, and
* <code>clearAbortRequest</code>.
*/
public void
reset() {
setInput(null, false, false);
setLocale(null);
removeAllIIOReadUpdateListeners();
removeAllIIOReadProgressListeners();
removeAllIIOReadWarningListeners();
clearAbortRequest();
}
/**
* Allows any resources held by this object to be released. The
* result of calling any other method (other than
* <code>finalize</code>) subsequent to a call to this method
* is undefined.
*
* <p>It is important for applications to call this method when they
* know they will no longer be using this <code>ImageReader</code>.
* Otherwise, the reader may continue to hold on to resources
* indefinitely.
*
* <p>The default implementation of this method in the superclass does
* nothing. Subclass implementations should ensure that all resources,
* especially native resources, are released.
*/
public void
dispose() {
}
// Utility methods
/**
* A utility method that may be used by readers to compute the
* region of the source image that should be read, taking into
* account any source region and subsampling offset settings in
* the supplied <code>ImageReadParam</code>. The actual
* subsampling factors, destination size, and destination offset
* are <em>not</em> taken into consideration, thus further
* clipping must take place. The {@link #computeRegions computeRegions}
* method performs all necessary clipping.
*
* @param param the <code>ImageReadParam</code> being used, or
* <code>null</code>.
* @param srcWidth the width of the source image.
* @param srcHeight the height of the source image.
*
* @return the source region as a <code>Rectangle</code>.
*/
protected static
Rectangle getSourceRegion(
ImageReadParam param,
int
srcWidth,
int
srcHeight) {
Rectangle sourceRegion = new
Rectangle(0, 0,
srcWidth,
srcHeight);
if (
param != null) {
Rectangle region =
param.
getSourceRegion();
if (
region != null) {
sourceRegion =
sourceRegion.
intersection(
region);
}
int
subsampleXOffset =
param.
getSubsamplingXOffset();
int
subsampleYOffset =
param.
getSubsamplingYOffset();
sourceRegion.
x +=
subsampleXOffset;
sourceRegion.
y +=
subsampleYOffset;
sourceRegion.
width -=
subsampleXOffset;
sourceRegion.
height -=
subsampleYOffset;
}
return
sourceRegion;
}
/**
* Computes the source region of interest and the destination
* region of interest, taking the width and height of the source
* image, an optional destination image, and an optional
* <code>ImageReadParam</code> into account. The source region
* begins with the entire source image. Then that is clipped to
* the source region specified in the <code>ImageReadParam</code>,
* if one is specified.
*
* <p> If either of the destination offsets are negative, the
* source region is clipped so that its top left will coincide
* with the top left of the destination image, taking subsampling
* into account. Then the result is clipped to the destination
* image on the right and bottom, if one is specified, taking
* subsampling and destination offsets into account.
*
* <p> Similarly, the destination region begins with the source
* image, is translated to the destination offset given in the
* <code>ImageReadParam</code> if there is one, and finally is
* clipped to the destination image, if there is one.
*
* <p> If either the source or destination regions end up having a
* width or height of 0, an <code>IllegalArgumentException</code>
* is thrown.
*
* <p> The {@link #getSourceRegion getSourceRegion>}
* method may be used if only source clipping is desired.
*
* @param param an <code>ImageReadParam</code>, or <code>null</code>.
* @param srcWidth the width of the source image.
* @param srcHeight the height of the source image.
* @param image a <code>BufferedImage</code> that will be the
* destination image, or <code>null</code>.
* @param srcRegion a <code>Rectangle</code> that will be filled with
* the source region of interest.
* @param destRegion a <code>Rectangle</code> that will be filled with
* the destination region of interest.
* @exception IllegalArgumentException if <code>srcRegion</code>
* is <code>null</code>.
* @exception IllegalArgumentException if <code>dstRegion</code>
* is <code>null</code>.
* @exception IllegalArgumentException if the resulting source or
* destination region is empty.
*/
protected static void
computeRegions(
ImageReadParam param,
int
srcWidth,
int
srcHeight,
BufferedImage image,
Rectangle srcRegion,
Rectangle destRegion) {
if (
srcRegion == null) {
throw new
IllegalArgumentException("srcRegion == null!");
}
if (
destRegion == null) {
throw new
IllegalArgumentException("destRegion == null!");
}
// Start with the entire source image
srcRegion.
setBounds(0, 0,
srcWidth,
srcHeight);
// Destination also starts with source image, as that is the
// maximum extent if there is no subsampling
destRegion.
setBounds(0, 0,
srcWidth,
srcHeight);
// Clip that to the param region, if there is one
int
periodX = 1;
int
periodY = 1;
int
gridX = 0;
int
gridY = 0;
if (
param != null) {
Rectangle paramSrcRegion =
param.
getSourceRegion();
if (
paramSrcRegion != null) {
srcRegion.
setBounds(
srcRegion.
intersection(
paramSrcRegion));
}
periodX =
param.
getSourceXSubsampling();
periodY =
param.
getSourceYSubsampling();
gridX =
param.
getSubsamplingXOffset();
gridY =
param.
getSubsamplingYOffset();
srcRegion.
translate(
gridX,
gridY);
srcRegion.
width -=
gridX;
srcRegion.
height -=
gridY;
destRegion.
setLocation(
param.
getDestinationOffset());
}
// Now clip any negative destination offsets, i.e. clip
// to the top and left of the destination image
if (
destRegion.
x < 0) {
int
delta = -
destRegion.
x*
periodX;
srcRegion.
x +=
delta;
srcRegion.
width -=
delta;
destRegion.
x = 0;
}
if (
destRegion.
y < 0) {
int
delta = -
destRegion.
y*
periodY;
srcRegion.
y +=
delta;
srcRegion.
height -=
delta;
destRegion.
y = 0;
}
// Now clip the destination Region to the subsampled width and height
int
subsampledWidth = (
srcRegion.
width +
periodX - 1)/
periodX;
int
subsampledHeight = (
srcRegion.
height +
periodY - 1)/
periodY;
destRegion.
width =
subsampledWidth;
destRegion.
height =
subsampledHeight;
// Now clip that to right and bottom of the destination image,
// if there is one, taking subsampling into account
if (
image != null) {
Rectangle destImageRect = new
Rectangle(0, 0,
image.
getWidth(),
image.
getHeight());
destRegion.
setBounds(
destRegion.
intersection(
destImageRect));
if (
destRegion.
isEmpty()) {
throw new
IllegalArgumentException
("Empty destination region!");
}
int
deltaX =
destRegion.
x +
subsampledWidth -
image.
getWidth();
if (
deltaX > 0) {
srcRegion.
width -=
deltaX*
periodX;
}
int
deltaY =
destRegion.
y +
subsampledHeight -
image.
getHeight();
if (
deltaY > 0) {
srcRegion.
height -=
deltaY*
periodY;
}
}
if (
srcRegion.
isEmpty() ||
destRegion.
isEmpty()) {
throw new
IllegalArgumentException("Empty region!");
}
}
/**
* A utility method that may be used by readers to test the
* validity of the source and destination band settings of an
* <code>ImageReadParam</code>. This method may be called as soon
* as the reader knows both the number of bands of the source
* image as it exists in the input stream, and the number of bands
* of the destination image that being written.
*
* <p> The method retrieves the source and destination band
* setting arrays from param using the <code>getSourceBands</code>
* and <code>getDestinationBands</code>methods (or considers them
* to be <code>null</code> if <code>param</code> is
* <code>null</code>). If the source band setting array is
* <code>null</code>, it is considered to be equal to the array
* <code>{ 0, 1, ..., numSrcBands - 1 }</code>, and similarly for
* the destination band setting array.
*
* <p> The method then tests that both arrays are equal in length,
* and that neither array contains a value larger than the largest
* available band index.
*
* <p> Any failure results in an
* <code>IllegalArgumentException</code> being thrown; success
* results in the method returning silently.
*
* @param param the <code>ImageReadParam</code> being used to read
* the image.
* @param numSrcBands the number of bands of the image as it exists
* int the input source.
* @param numDstBands the number of bands in the destination image
* being written.
*
* @exception IllegalArgumentException if <code>param</code>
* contains an invalid specification of a source and/or
* destination band subset.
*/
protected static void
checkReadParamBandSettings(
ImageReadParam param,
int
numSrcBands,
int
numDstBands) {
// A null param is equivalent to srcBands == dstBands == null.
int[]
srcBands = null;
int[]
dstBands = null;
if (
param != null) {
srcBands =
param.
getSourceBands();
dstBands =
param.
getDestinationBands();
}
int
paramSrcBandLength =
(
srcBands == null) ?
numSrcBands :
srcBands.length;
int
paramDstBandLength =
(
dstBands == null) ?
numDstBands :
dstBands.length;
if (
paramSrcBandLength !=
paramDstBandLength) {
throw new
IllegalArgumentException("ImageReadParam num source & dest bands differ!");
}
if (
srcBands != null) {
for (int
i = 0;
i <
srcBands.length;
i++) {
if (
srcBands[
i] >=
numSrcBands) {
throw new
IllegalArgumentException("ImageReadParam source bands contains a value >= the number of source bands!");
}
}
}
if (
dstBands != null) {
for (int
i = 0;
i <
dstBands.length;
i++) {
if (
dstBands[
i] >=
numDstBands) {
throw new
IllegalArgumentException("ImageReadParam dest bands contains a value >= the number of dest bands!");
}
}
}
}
/**
* Returns the <code>BufferedImage</code> to which decoded pixel
* data should be written. The image is determined by inspecting
* the supplied <code>ImageReadParam</code> if it is
* non-<code>null</code>; if its <code>getDestination</code>
* method returns a non-<code>null</code> value, that image is
* simply returned. Otherwise,
* <code>param.getDestinationType</code> method is called to
* determine if a particular image type has been specified. If
* so, the returned <code>ImageTypeSpecifier</code> is used after
* checking that it is equal to one of those included in
* <code>imageTypes</code>.
*
* <p> If <code>param</code> is <code>null</code> or the above
* steps have not yielded an image or an
* <code>ImageTypeSpecifier</code>, the first value obtained from
* the <code>imageTypes</code> parameter is used. Typically, the
* caller will set <code>imageTypes</code> to the value of
* <code>getImageTypes(imageIndex)</code>.
*
* <p> Next, the dimensions of the image are determined by a call
* to <code>computeRegions</code>. The actual width and height of
* the image being decoded are passed in as the <code>width</code>
* and <code>height</code> parameters.
*
* @param param an <code>ImageReadParam</code> to be used to get
* the destination image or image type, or <code>null</code>.
* @param imageTypes an <code>Iterator</code> of
* <code>ImageTypeSpecifier</code>s indicating the legal image
* types, with the default first.
* @param width the true width of the image or tile begin decoded.
* @param height the true width of the image or tile being decoded.
*
* @return the <code>BufferedImage</code> to which decoded pixel
* data should be written.
*
* @exception IIOException if the <code>ImageTypeSpecifier</code>
* specified by <code>param</code> does not match any of the legal
* ones from <code>imageTypes</code>.
* @exception IllegalArgumentException if <code>imageTypes</code>
* is <code>null</code> or empty, or if an object not of type
* <code>ImageTypeSpecifier</code> is retrieved from it.
* @exception IllegalArgumentException if the resulting image would
* have a width or height less than 1.
* @exception IllegalArgumentException if the product of
* <code>width</code> and <code>height</code> is greater than
* <code>Integer.MAX_VALUE</code>.
*/
protected static
BufferedImage
getDestination(
ImageReadParam param,
Iterator<
ImageTypeSpecifier>
imageTypes,
int
width, int
height)
throws
IIOException {
if (
imageTypes == null || !
imageTypes.
hasNext()) {
throw new
IllegalArgumentException("imageTypes null or empty!");
}
if ((long)
width*
height >
Integer.
MAX_VALUE) {
throw new
IllegalArgumentException
("width*height > Integer.MAX_VALUE!");
}
BufferedImage dest = null;
ImageTypeSpecifier imageType = null;
// If param is non-null, use it
if (
param != null) {
// Try to get the image itself
dest =
param.
getDestination();
if (
dest != null) {
return
dest;
}
// No image, get the image type
imageType =
param.
getDestinationType();
}
// No info from param, use fallback image type
if (
imageType == null) {
Object o =
imageTypes.
next();
if (!(
o instanceof
ImageTypeSpecifier)) {
throw new
IllegalArgumentException
("Non-ImageTypeSpecifier retrieved from imageTypes!");
}
imageType = (
ImageTypeSpecifier)
o;
} else {
boolean
foundIt = false;
while (
imageTypes.
hasNext()) {
ImageTypeSpecifier type =
(
ImageTypeSpecifier)
imageTypes.
next();
if (
type.
equals(
imageType)) {
foundIt = true;
break;
}
}
if (!
foundIt) {
throw new
IIOException
("Destination type from ImageReadParam does not match!");
}
}
Rectangle srcRegion = new
Rectangle(0,0,0,0);
Rectangle destRegion = new
Rectangle(0,0,0,0);
computeRegions(
param,
width,
height,
null,
srcRegion,
destRegion);
int
destWidth =
destRegion.
x +
destRegion.
width;
int
destHeight =
destRegion.
y +
destRegion.
height;
// Create a new image based on the type specifier
return
imageType.
createBufferedImage(
destWidth,
destHeight);
}
}