Java tutorial
/* * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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} objects are normally instantiated by * the service provider interface (SPI) class for the specific format. * Service provider classes (e.g., instances of * {@code ImageReaderSpi}) are registered with the * {@code IIORegistry}, 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} * 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} that instantiated this object, * or {@code null} if its identity is not known or none * exists. By default it is initialized to {@code null}. */ protected ImageReaderSpi originatingProvider; /** * The {@code ImageInputStream} or other * {@code Object} by {@code setInput} and retrieved * by {@code getInput}. By default it is initialized to * {@code null}. */ protected Object input = null; /** * {@code true} if the current input source has been marked * as allowing only forward seeking by {@code setInput}. By * default, the value is {@code false}. * * @see #minIndex * @see #setInput */ protected boolean seekForwardOnly = false; /** * {@code true} if the current input source has been marked * as allowing metadata to be ignored by {@code setInput}. * By default, the value is {@code false}. * * @see #setInput */ protected boolean ignoreMetadata = false; /** * The smallest valid index for reading, initially 0. When * {@code seekForwardOnly} is {@code true}, various methods * may throw an {@code IndexOutOfBoundsException} 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}s which may be used to localize * warning messages, or {@code null} if localization is not * supported. */ protected Locale[] availableLocales = null; /** * The current {@code Locale} to be used for localization, or * {@code null} if none has been set. */ protected Locale locale = null; /** * A {@code List} of currently registered * {@code IIOReadWarningListener}s, initialized by default to * {@code null}, which is synonymous with an empty * {@code List}. */ protected List<IIOReadWarningListener> warningListeners = null; /** * A {@code List} of the {@code Locale}s associated with * each currently registered {@code IIOReadWarningListener}, * initialized by default to {@code null}, which is * synonymous with an empty {@code List}. */ protected List<Locale> warningLocales = null; /** * A {@code List} of currently registered * {@code IIOReadProgressListener}s, initialized by default * to {@code null}, which is synonymous with an empty * {@code List}. */ protected List<IIOReadProgressListener> progressListeners = null; /** * A {@code List} of currently registered * {@code IIOReadUpdateListener}s, initialized by default to * {@code null}, which is synonymous with an empty * {@code List}. */ protected List<IIOReadUpdateListener> updateListeners = null; /** * If {@code true}, the current read operation should be * aborted. */ private boolean abortFlag = false; /** * Constructs an {@code ImageReader} and sets its * {@code originatingProvider} field to the supplied value. * * <p> Subclasses that make use of extensions should provide a * constructor with signature {@code (ImageReaderSpi,Object)} * in order to retrieve the extension object. If * the extension object is unsuitable, an * {@code IllegalArgumentException} should be thrown. * * @param originatingProvider the {@code ImageReaderSpi} that is * invoking this constructor, or {@code null}. */ protected ImageReader(ImageReaderSpi originatingProvider) { this.originatingProvider = originatingProvider; } /** * Returns a {@code String} identifying the format of the * input source. * * <p> The default implementation returns * {@code originatingProvider.getFormatNames()[0]}. * 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}. */ public String getFormatName() throws IOException { return originatingProvider.getFormatNames()[0]; } /** * Returns the {@code ImageReaderSpi} that was passed in on * the constructor. Note that this value may be {@code null}. * * @return an {@code ImageReaderSpi}, or {@code null}. * * @see ImageReaderSpi */ public ImageReaderSpi getOriginatingProvider() { return originatingProvider; } /** * Sets the input source to use to the given * {@code ImageInputStream} or other {@code Object}. * The input source must be set before any of the query or read * methods are used. If {@code input} is {@code null}, * any currently set input source will be removed. In any case, * the value of {@code minIndex} will be initialized to 0. * * <p> The {@code seekForwardOnly} parameter controls whether * the value returned by {@code getMinIndex} will be * increased as each image (or thumbnail, or image metadata) is * read. If {@code seekForwardOnly} is true, then a call to * {@code read(index)} will throw an * {@code IndexOutOfBoundsException} if {@code index < this.minIndex}; * otherwise, the value of * {@code minIndex} will be set to {@code index}. If * {@code seekForwardOnly} is {@code false}, the value of * {@code minIndex} will remain 0 regardless of any read * operations. * * <p> The {@code ignoreMetadata} parameter, if set to * {@code true}, allows the reader to disregard any metadata * encountered during the read. Subsequent calls to the * {@code getStreamMetadata} and * {@code getImageMetadata} methods may return * {@code null}, and an {@code IIOImage} returned from * {@code readAll} may return {@code null} from their * {@code getMetadata} 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} other than an * {@code ImageInputStream} 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} method; most readers * will return a single-element array containing only * {@code ImageInputStream.class} to indicate that they * accept only an {@code ImageInputStream}. * * <p> The default implementation checks the {@code input} * argument against the list returned by * {@code originatingProvider.getInputTypes()} 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}, the input is accepted only if it is an * {@code ImageInputStream}. * * @param input the {@code ImageInputStream} or other * {@code Object} to use for future decoding. * @param seekForwardOnly if {@code true}, images and metadata * may only be read in ascending order from this input source. * @param ignoreMetadata if {@code true}, metadata * may be ignored during reads. * * @exception IllegalArgumentException if {@code input} is * not an instance of one of the classes returned by the * originating service provider's {@code getInputTypes} * method, or is not an {@code ImageInputStream}. * * @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} or other {@code Object}. * The input source must be set before any of the query or read * methods are used. If {@code input} is {@code null}, * any currently set input source will be removed. In any case, * the value of {@code minIndex} will be initialized to 0. * * <p> The {@code seekForwardOnly} parameter controls whether * the value returned by {@code getMinIndex} will be * increased as each image (or thumbnail, or image metadata) is * read. If {@code seekForwardOnly} is true, then a call to * {@code read(index)} will throw an * {@code IndexOutOfBoundsException} if {@code index < this.minIndex}; * otherwise, the value of * {@code minIndex} will be set to {@code index}. If * {@code seekForwardOnly} is {@code false}, the value of * {@code minIndex} will remain 0 regardless of any read * operations. * * <p> This method is equivalent to * {@code setInput(input, seekForwardOnly, false)}. * * @param input the {@code ImageInputStream} or other * {@code Object} to use for future decoding. * @param seekForwardOnly if {@code true}, images and metadata * may only be read in ascending order from this input source. * * @exception IllegalArgumentException if {@code input} is * not an instance of one of the classes returned by the * originating service provider's {@code getInputTypes} * method, or is not an {@code ImageInputStream}. * * @see #getInput */ public void setInput(Object input, boolean seekForwardOnly) { setInput(input, seekForwardOnly, false); } /** * Sets the input source to use to the given * {@code ImageInputStream} or other {@code Object}. * The input source must be set before any of the query or read * methods are used. If {@code input} is {@code null}, * any currently set input source will be removed. In any case, * the value of {@code minIndex} will be initialized to 0. * * <p> This method is equivalent to * {@code setInput(input, false, false)}. * * @param input the {@code ImageInputStream} or other * {@code Object} to use for future decoding. * * @exception IllegalArgumentException if {@code input} is * not an instance of one of the classes returned by the * originating service provider's {@code getInputTypes} * method, or is not an {@code ImageInputStream}. * * @see #getInput */ public void setInput(Object input) { setInput(input, false, false); } /** * Returns the {@code ImageInputStream} or other * {@code Object} previously set as the input source. If the * input source has not been set, {@code null} is returned. * * @return the {@code Object} that will be used for future * decoding, or {@code null}. * * @see ImageInputStream * @see #setInput */ public Object getInput() { return input; } /** * Returns {@code true} if the current input source has been * marked as seek forward only by passing {@code true} as the * {@code seekForwardOnly} argument to the * {@code setInput} method. * * @return {@code true} if the input source is seek forward * only. * * @see #setInput */ public boolean isSeekForwardOnly() { return seekForwardOnly; } /** * Returns {@code true} if the current input source has been * marked as allowing metadata to be ignored by passing * {@code true} as the {@code ignoreMetadata} argument * to the {@code setInput} method. * * @return {@code true} 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()} is * {@code false}, 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}s that may be used to * localize warning listeners and compression settings. A return * value of {@code null} indicates that localization is not * supported. * * <p> The default implementation returns a clone of the * {@code availableLocales} instance variable if it is * non-{@code null}, or else returns {@code null}. * * @return an array of {@code Locale}s that may be used as * arguments to {@code setLocale}, or {@code null}. */ public Locale[] getAvailableLocales() { if (availableLocales == null) { return null; } else { return availableLocales.clone(); } } /** * Sets the current {@code Locale} of this * {@code ImageReader} to the given value. A value of * {@code null} removes any previous setting, and indicates * that the reader should localize as it sees fit. * * @param locale the desired {@code Locale}, or * {@code null}. * * @exception IllegalArgumentException if {@code locale} is * non-{@code null} but is not one of the values returned by * {@code getAvailableLocales}. * * @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}, or * {@code null} if none has been set. * * @return the current {@code Locale}, or {@code null}. * * @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} with increasing indices until an * {@code IndexOutOfBoundsException} is thrown to indicate * that no more images are available. The * {@code allowSearch} parameter may be set to * {@code false} to indicate that an exhaustive search is not * desired; the return value will be {@code -1} to indicate * that a search is necessary. If the input has been specified * with {@code seekForwardOnly} set to {@code true}, * this method throws an {@code IllegalStateException} if * {@code allowSearch} is set to {@code true}. * * @param allowSearch if {@code true}, the true number of * images will be returned even if a search is required. If * {@code false}, the reader may return {@code -1} * without performing the search. * * @return the number of images, as an {@code int}, or * {@code -1} if {@code allowSearch} is * {@code false} 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} * set to {@code true}. * @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}. * * @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}. * * @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} 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}, 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}. * * <p> Note that formats that return {@code false} 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} or * {@code false} 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}. * * @param imageIndex the index of the image to be queried. * * @return {@code true} 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}. 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)}. * * @param imageIndex the index of the image to be queried. * * @return a {@code float} 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. If * there is no close match then a type which preserves the most * information from the image should be returned. The returned value * should also be included in the list of values returned by * {@code getImageTypes}. * * <p> The default implementation simply returns the first entry * from the list provided by {@code getImageType}. * * @param imageIndex the index of the image to be queried. * * @return an {@code ImageTypeSpecifier}. * * @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 getImageTypes(imageIndex).next(); } /** * Returns an {@code Iterator} containing possible image * types to which the given image may be decoded, in the form of * {@code ImageTypeSpecifiers}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}. * * @return an {@code Iterator} containing at least one * {@code ImageTypeSpecifier} 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} 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} object that does not allow source * scaling (<i>i.e.</i>, it returns * {@code new ImageReadParam()}. * * @return an {@code ImageReadParam} 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} object representing the * metadata associated with the input source as a whole (i.e., not * associated with any particular image), or {@code null} if * the reader does not support reading metadata, is set to ignore * metadata, or if no metadata is available. * * @return an {@code IIOMetadata} object, or {@code null}. * * @exception IOException if an error occurs during reading. */ public abstract IIOMetadata getStreamMetadata() throws IOException; /** * Returns an {@code IIOMetadata} 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} is returned. * * <p> The resulting metadata object is only responsible for * returning documents in the format named by * {@code formatName}. Within any documents that are * returned, only nodes whose names are members of * {@code nodeNames} 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} is not the name of a supported * metadata format, {@code null} 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()}, after checking that * the format name is supported. If it is not, * {@code null} is returned. * * @param formatName a metadata format name that may be used to retrieve * a document from the returned {@code IIOMetadata} object. * @param nodeNames a {@code Set} containing the names of * nodes that may be contained in a retrieved document. * * @return an {@code IIOMetadata} object, or {@code null}. * * @exception IllegalArgumentException if {@code formatName} * is {@code null}. * @exception IllegalArgumentException if {@code nodeNames} * is {@code null}. * @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<String> 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} object containing metadata * associated with the given image, or {@code null} 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} object, or * {@code null}. * * @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} object representing the * metadata associated with the given image, or {@code null} * 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}. Within any documents that are * returned, only nodes whose names are members of * {@code nodeNames} 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} is not the name of a supported * metadata format, {@code null} 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)}, after * checking that the format name is supported. If it is not, * {@code null} 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} object. * @param nodeNames a {@code Set} containing the names of * nodes that may be contained in a retrieved document. * * @return an {@code IIOMetadata} object, or {@code null}. * * @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} * is {@code null}. * @exception IllegalArgumentException if {@code nodeNames} * is {@code null}. * @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} and returns * it as a complete {@code BufferedImage}, using a default * {@code ImageReadParam}. This is a convenience method * that calls {@code read(imageIndex, null)}. * * <p> The image returned will be formatted according to the first * {@code ImageTypeSpecifier} returned from * {@code getImageTypes}. * * <p> Any registered {@code IIOReadProgressListener} objects * will be notified by calling their {@code imageStarted} * method, followed by calls to their {@code imageProgress} * method as the read progresses. Finally their * {@code imageComplete} method will be called. * {@code IIOReadUpdateListener} objects may be updated at * other times during the read as pixels are decoded. Finally, * {@code IIOReadWarningListener} 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}. * * @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} and returns * it as a complete {@code BufferedImage}, using a supplied * {@code ImageReadParam}. * * <p> The actual {@code BufferedImage} returned will be * chosen using the algorithm defined by the * {@code getDestination} method. * * <p> Any registered {@code IIOReadProgressListener} objects * will be notified by calling their {@code imageStarted} * method, followed by calls to their {@code imageProgress} * method as the read progresses. Finally their * {@code imageComplete} method will be called. * {@code IIOReadUpdateListener} objects may be updated at * other times during the read as pixels are decoded. Finally, * {@code IIOReadWarningListener} 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} * and {@code getDestinationBands} on the supplied * {@code ImageReadParam}. 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} is thrown. * * <p> If the supplied {@code ImageReadParam} 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} used to control * the reading process, or {@code null}. * * @return the desired portion of the image as a * {@code BufferedImage}. * * @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} and * {@code param.getDestinationBands} 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} and returns * an {@code IIOImage} containing the image, thumbnails, and * associated image metadata, using a supplied * {@code ImageReadParam}. * * <p> The actual {@code BufferedImage} referenced by the * returned {@code IIOImage} will be chosen using the * algorithm defined by the {@code getDestination} method. * * <p> Any registered {@code IIOReadProgressListener} objects * will be notified by calling their {@code imageStarted} * method, followed by calls to their {@code imageProgress} * method as the read progresses. Finally their * {@code imageComplete} method will be called. * {@code IIOReadUpdateListener} objects may be updated at * other times during the read as pixels are decoded. Finally, * {@code IIOReadWarningListener} 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} * and {@code getDestinationBands} on the supplied * {@code ImageReadParam}. 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} is thrown. * * <p> Thumbnails will be returned in their entirety regardless of * the region settings. * * <p> If the supplied {@code ImageReadParam} 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} used to control * the reading process, or {@code null}. * * @return an {@code IIOImage} 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} and * {@code param.getDestinationBands} 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<BufferedImage> 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} containing all the images, * thumbnails, and metadata, starting at the index given by * {@code getMinIndex}, from the input source in the form of * {@code IIOImage} objects. An {@code Iterator} * containing {@code ImageReadParam} 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} 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} is {@code null}, a default read * param will be used for all images. * * <p> The actual {@code BufferedImage} referenced by the * returned {@code IIOImage} will be chosen using the * algorithm defined by the {@code getDestination} method. * * <p> Any registered {@code IIOReadProgressListener} objects * will be notified by calling their {@code sequenceStarted} * method once. Then, for each image decoded, there will be a * call to {@code imageStarted}, followed by calls to * {@code imageProgress} as the read progresses, and finally * to {@code imageComplete}. The * {@code sequenceComplete} method will be called after the * last image has been decoded. * {@code IIOReadUpdateListener} objects may be updated at * other times during the read as pixels are decoded. Finally, * {@code IIOReadWarningListener} 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} * and {@code getDestinationBands} on the supplied * {@code ImageReadParam}. 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} is thrown. * * <p> Thumbnails will be returned in their entirety regardless of the * region settings. * * <p> If any of the supplied {@code ImageReadParam}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} containing * {@code ImageReadParam} objects. * * @return an {@code Iterator} representing the * contents of the input source as {@code IIOImage}s. * * @exception IllegalStateException if the input source has not been * set. * @exception IllegalArgumentException if any * non-{@code null} element of {@code params} is not an * {@code ImageReadParam}. * @exception IllegalArgumentException if the set of source and * destination bands specified by * {@code param.getSourceBands} and * {@code param.getDestinationBands} 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<IIOImage> 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<BufferedImage> 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} if this plug-in supports reading * just a {@link java.awt.image.Raster Raster} of pixel data. * If this method returns {@code false}, calls to * {@link #readRaster readRaster} or {@link #readTileRaster readTileRaster} * will throw an {@code UnsupportedOperationException}. * * <p> The default implementation returns {@code false}. * * @return {@code true} if this plug-in supports reading raw * {@code Raster}s. * * @see #readRaster * @see #readTileRaster */ public boolean canReadRaster() { return false; } /** * Returns a new {@code Raster} 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} 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} * 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}s are ignored, as * there is no {@code BufferedImage}, but all other listeners are * called exactly as they are for the {@link #read read} method. * * <p> If {@link #canReadRaster canReadRaster()} returns * {@code false}, this method throws an * {@code UnsupportedOperationException}. * * <p> If the supplied {@code ImageReadParam} 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}. * * @param imageIndex the index of the image to be read. * @param param an {@code ImageReadParam} used to control * the reading process, or {@code null}. * * @return the desired portion of the image as a * {@code Raster}. * * @exception UnsupportedOperationException if this plug-in does not * support reading raw {@code Raster}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} 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}. * * <p> A reader for which all images are guaranteed to be tiled, * or are guaranteed not to be tiled, may return {@code true} * or {@code false} 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}. * * @param imageIndex the index of the image to be queried. * * @return {@code true} 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)}, 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)}, 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} and * {@code tileY} arguments, returning it as a * {@code BufferedImage}. If the arguments are out of range, * an {@code IllegalArgumentException} 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} to be thrown. * * <p> This method is merely a convenience equivalent to calling * {@code read(int, ImageReadParam)} with a read param * specifying a source region having offsets of * {@code tileX*getTileWidth(imageIndex)}, * {@code tileY*getTileHeight(imageIndex)} and width and * height of {@code getTileWidth(imageIndex)}, * {@code getTileHeight(imageIndex)}; and subsampling * factors of 1 and offsets of 0. To subsample a tile, call * {@code read} with a read param specifying this region * and different subsampling parameters. * * <p> The default implementation returns the entire image if * {@code tileX} and {@code tileY} are 0, or throws * an {@code IllegalArgumentException} 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}. * * @exception IllegalStateException if the input source has not been * set. * @exception IndexOutOfBoundsException if {@code imageIndex} * 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} 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}, this method throws an * {@code UnsupportedOperationException}. * * <p> The default implementation checks if reading * {@code Raster}s is supported, and if so calls {@link * #readRaster readRaster(imageIndex, null)} if * {@code tileX} and {@code tileY} are 0, or throws an * {@code IllegalArgumentException} 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}. * * @exception UnsupportedOperationException if this plug-in does not * support reading raw {@code Raster}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} * 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} object that contains the * contents of the image indexed by {@code imageIndex}. By * default, the returned image is simply the * {@code BufferedImage} returned by * {@code read(imageIndex, param)}. * * <p> The semantics of this method may differ from those of the * other {@code read} methods in several ways. First, any * destination image and/or image type set in the * {@code ImageReadParam} 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} 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} used to control * the reading process, or {@code null}. * * @return a {@code RenderedImage} 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} and * {@code param.getDestinationBands} 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} if the image format understood by * this reader supports thumbnail preview images associated with * it. The default implementation returns {@code false}. * * <p> If this method returns {@code false}, * {@code hasThumbnails} and {@code getNumThumbnails} * will return {@code false} and {@code 0}, * respectively, and {@code readThumbnail} will throw an * {@code UnsupportedOperationException}, regardless of their * arguments. * * <p> A reader that does not support thumbnails need not * implement any of the thumbnail-related methods. * * @return {@code true} if thumbnails are supported. */ public boolean readerSupportsThumbnails() { return false; } /** * Returns {@code true} if the given image has thumbnail * preview images associated with it. If the format does not * support thumbnails ({@code readerSupportsThumbnails} * returns {@code false}), {@code false} will be * returned regardless of whether an input source has been set or * whether {@code imageIndex} is in bounds. * * <p> The default implementation returns {@code true} if * {@code getNumThumbnails} returns a value greater than 0. * * @param imageIndex the index of the image being queried. * * @return {@code true} 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} 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} returns * {@code false}), {@code 0} will be returned regardless * of whether an input source has been set or whether * {@code imageIndex} 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} 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}, associated with the image indexed * by {@code ImageIndex}. * * <p> If the reader does not support thumbnails, * ({@code readerSupportsThumbnails} returns * {@code false}), an {@code UnsupportedOperationException} * will be thrown. * * <p> The default implementation simply returns * {@code readThumbnail(imageindex, thumbnailIndex).getWidth()}. * 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}. * * @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}, associated with the image indexed * by {@code ImageIndex}. * * <p> If the reader does not support thumbnails, * ({@code readerSupportsThumbnails} returns * {@code false}), an {@code UnsupportedOperationException} * will be thrown. * * <p> The default implementation simply returns * {@code readThumbnail(imageindex, thumbnailIndex).getHeight()}. * 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}. * * @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}, associated with the image indexed * by {@code ImageIndex} as a {@code BufferedImage}. * * <p> Any registered {@code IIOReadProgressListener} objects * will be notified by calling their * {@code thumbnailStarted}, {@code thumbnailProgress}, * and {@code thumbnailComplete} methods. * * <p> If the reader does not support thumbnails, * ({@code readerSupportsThumbnails} returns * {@code false}), an {@code UnsupportedOperationException} * 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}. * * @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}. * * @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} at the * beginning of each read operation, and poll the value of * {@code abortRequested} regularly during the read. */ public synchronized void abort() { this.abortFlag = true; } /** * Returns {@code true} if a request to abort the current * read operation has been made since the reader was instantiated or * {@code clearAbortRequest} was called. * * @return {@code true} 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} will return * {@code false}. * * @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 <T> List<T> addToList(List<T> l, T 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 <T> List<T> removeFromList(List<T> l, T elt) { if (l == null) { return l; } l.remove(elt); if (l.size() == 0) { l = null; } return l; } /** * Adds an {@code IIOReadWarningListener} to the list of * registered warning listeners. If {@code listener} is * {@code null}, 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}. If no {@code Locale} has been set, * warning messages may be localized as the reader sees fit. * * @param listener an {@code IIOReadWarningListener} 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} from the list of * registered error listeners. If the listener was not previously * registered, or if {@code listener} is {@code null}, * 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} objects. * * <p> The default implementation sets the * {@code warningListeners} and {@code warningLocales} * instance variables to {@code null}. */ public void removeAllIIOReadWarningListeners() { warningListeners = null; warningLocales = null; } /** * Adds an {@code IIOReadProgressListener} to the list of * registered progress listeners. If {@code listener} is * {@code null}, 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} from the list * of registered progress listeners. If the listener was not * previously registered, or if {@code listener} is * {@code null}, 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} objects. * * <p> The default implementation sets the * {@code progressListeners} instance variable to * {@code null}. */ public void removeAllIIOReadProgressListeners() { progressListeners = null; } /** * Adds an {@code IIOReadUpdateListener} to the list of * registered update listeners. If {@code listener} is * {@code null}, 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}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} from the list of * registered update listeners. If the listener was not * previously registered, or if {@code listener} is * {@code null}, 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} objects. * * <p> The default implementation sets the * {@code updateListeners} instance variable to * {@code null}. */ public void removeAllIIOReadUpdateListeners() { updateListeners = null; } /** * Broadcasts the start of an sequence of image reads to all * registered {@code IIOReadProgressListener}s by calling * their {@code sequenceStarted} 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 = progressListeners.get(i); listener.sequenceStarted(this, minIndex); } } /** * Broadcasts the completion of an sequence of image reads to all * registered {@code IIOReadProgressListener}s by calling * their {@code sequenceComplete} 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 = progressListeners.get(i); listener.sequenceComplete(this); } } /** * Broadcasts the start of an image read to all registered * {@code IIOReadProgressListener}s by calling their * {@code imageStarted} 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 = progressListeners.get(i); listener.imageStarted(this, imageIndex); } } /** * Broadcasts the current percentage of image completion to all * registered {@code IIOReadProgressListener}s by calling * their {@code imageProgress} method. Subclasses may use * this method as a convenience. * * @param percentageDone the current percentage of completion, * as a {@code float}. */ protected void processImageProgress(float percentageDone) { if (progressListeners == null) { return; } int numListeners = progressListeners.size(); for (int i = 0; i < numListeners; i++) { IIOReadProgressListener listener = progressListeners.get(i); listener.imageProgress(this, percentageDone); } } /** * Broadcasts the completion of an image read to all registered * {@code IIOReadProgressListener}s by calling their * {@code imageComplete} 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 = progressListeners.get(i); listener.imageComplete(this); } } /** * Broadcasts the start of a thumbnail read to all registered * {@code IIOReadProgressListener}s by calling their * {@code thumbnailStarted} 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 = progressListeners.get(i); listener.thumbnailStarted(this, imageIndex, thumbnailIndex); } } /** * Broadcasts the current percentage of thumbnail completion to * all registered {@code IIOReadProgressListener}s by calling * their {@code thumbnailProgress} method. Subclasses may * use this method as a convenience. * * @param percentageDone the current percentage of completion, * as a {@code float}. */ protected void processThumbnailProgress(float percentageDone) { if (progressListeners == null) { return; } int numListeners = progressListeners.size(); for (int i = 0; i < numListeners; i++) { IIOReadProgressListener listener = progressListeners.get(i); listener.thumbnailProgress(this, percentageDone); } } /** * Broadcasts the completion of a thumbnail read to all registered * {@code IIOReadProgressListener}s by calling their * {@code thumbnailComplete} 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 = progressListeners.get(i); listener.thumbnailComplete(this); } } /** * Broadcasts that the read has been aborted to all registered * {@code IIOReadProgressListener}s by calling their * {@code readAborted} 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 = progressListeners.get(i); listener.readAborted(this); } } /** * Broadcasts the beginning of a progressive pass to all * registered {@code IIOReadUpdateListener}s by calling their * {@code passStarted} method. Subclasses may use this * method as a convenience. * * @param theImage the {@code BufferedImage} 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}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 = 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}s by calling their * {@code imageUpdate} method. Subclasses may use this * method as a convenience. * * @param theImage the {@code BufferedImage} 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}. * @param height the total height of the area being updated, * including pixels being skipped if {@code periodY > 1}. * @param periodX the horizontal separation between pixels. * @param periodY the vertical separation between pixels. * @param bands an array of {@code int}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 = 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}s by calling their * {@code passComplete} method. Subclasses may use this * method as a convenience. * * @param theImage the {@code BufferedImage} being updated. */ protected void processPassComplete(BufferedImage theImage) { if (updateListeners == null) { return; } int numListeners = updateListeners.size(); for (int i = 0; i < numListeners; i++) { IIOReadUpdateListener listener = updateListeners.get(i); listener.passComplete(this, theImage); } } /** * Broadcasts the beginning of a thumbnail progressive pass to all * registered {@code IIOReadUpdateListener}s by calling their * {@code thumbnailPassStarted} method. Subclasses may use this * method as a convenience. * * @param theThumbnail the {@code BufferedImage} 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}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 = 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}s by * calling their {@code thumbnailUpdate} method. Subclasses may * use this method as a convenience. * * @param theThumbnail the {@code BufferedImage} 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}. * @param height the total height of the area being updated, * including pixels being skipped if {@code periodY > 1}. * @param periodX the horizontal separation between pixels. * @param periodY the vertical separation between pixels. * @param bands an array of {@code int}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 = 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}s by calling their * {@code thumbnailPassComplete} method. Subclasses may use this * method as a convenience. * * @param theThumbnail the {@code BufferedImage} 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 = updateListeners.get(i); listener.thumbnailPassComplete(this, theThumbnail); } } /** * Broadcasts a warning message to all registered * {@code IIOReadWarningListener}s by calling their * {@code warningOccurred} method. Subclasses may use this * method as a convenience. * * @param warning the warning message to send. * * @exception IllegalArgumentException if {@code warning} * is {@code null}. */ 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 = warningListeners.get(i); listener.warningOccurred(this, warning); } } /** * Broadcasts a localized warning message to all registered * {@code IIOReadWarningListener}s by calling their * {@code warningOccurred} method with a string taken * from a {@code ResourceBundle}. Subclasses may use this * method as a convenience. * * @param baseName the base name of a set of * {@code ResourceBundle}s containing localized warning * messages. * @param keyword the keyword used to index the warning message * within the set of {@code ResourceBundle}s. * * @exception IllegalArgumentException if {@code baseName} * is {@code null}. * @exception IllegalArgumentException if {@code keyword} * is {@code null}. * @exception IllegalArgumentException if no appropriate * {@code ResourceBundle} may be located. * @exception IllegalArgumentException if the named resource is * not found in the located {@code ResourceBundle}. * @exception IllegalArgumentException if the object retrieved * from the {@code ResourceBundle} is not a * {@code String}. */ 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 = warningListeners.get(i); Locale locale = warningLocales.get(i); if (locale == null) { locale = Locale.getDefault(); } /* * Only the plugin knows the messages that are provided, so we * can always locate the resource bundles from the same loader * as that for the plugin code itself. */ ResourceBundle bundle = null; try { bundle = ResourceBundle.getBundle(baseName, locale, this.getClass().getModule()); } catch (MissingResourceException mre) { throw new IllegalArgumentException("Bundle not found!", mre); } String warning = null; try { warning = bundle.getString(keyword); } catch (ClassCastException cce) { throw new IllegalArgumentException("Resource is not a String!", cce); } catch (MissingResourceException mre) { throw new IllegalArgumentException("Resource is missing!", mre); } listener.warningOccurred(this, warning); } } // State management /** * Restores the {@code ImageReader} to its initial state. * * <p> The default implementation calls * {@code setInput(null, false)}, * {@code setLocale(null)}, * {@code removeAllIIOReadUpdateListeners()}, * {@code removeAllIIOReadWarningListeners()}, * {@code removeAllIIOReadProgressListeners()}, and * {@code clearAbortRequest}. */ 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}) 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}. * 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}. 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} being used, or * {@code null}. * @param srcWidth the width of the source image. * @param srcHeight the height of the source image. * * @return the source region as a {@code Rectangle}. */ 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} into account. The source region * begins with the entire source image. Then that is clipped to * the source region specified in the {@code ImageReadParam}, * 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} 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} * is thrown. * * <p> The {@link #getSourceRegion getSourceRegion>} * method may be used if only source clipping is desired. * * @param param an {@code ImageReadParam}, or {@code null}. * @param srcWidth the width of the source image. * @param srcHeight the height of the source image. * @param image a {@code BufferedImage} that will be the * destination image, or {@code null}. * @param srcRegion a {@code Rectangle} that will be filled with * the source region of interest. * @param destRegion a {@code Rectangle} that will be filled with * the destination region of interest. * @exception IllegalArgumentException if {@code srcRegion} * is {@code null}. * @exception IllegalArgumentException if {@code dstRegion} * is {@code null}. * @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}. 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} * and {@code getDestinationBands} methods (or considers them * to be {@code null} if {@code param} is * {@code null}). If the source band setting array is * {@code null}, it is considered to be equal to the array * {@code { 0, 1, ..., numSrcBands - 1 }}, 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} being thrown; success * results in the method returning silently. * * @param param the {@code ImageReadParam} 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} * 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} to which decoded pixel * data should be written. The image is determined by inspecting * the supplied {@code ImageReadParam} if it is * non-{@code null}; if its {@code getDestination} * method returns a non-{@code null} value, that image is * simply returned. Otherwise, * {@code param.getDestinationType} method is called to * determine if a particular image type has been specified. If * so, the returned {@code ImageTypeSpecifier} is used after * checking that it is equal to one of those included in * {@code imageTypes}. * * <p> If {@code param} is {@code null} or the above * steps have not yielded an image or an * {@code ImageTypeSpecifier}, the first value obtained from * the {@code imageTypes} parameter is used. Typically, the * caller will set {@code imageTypes} to the value of * {@code getImageTypes(imageIndex)}. * * <p> Next, the dimensions of the image are determined by a call * to {@code computeRegions}. The actual width and height of * the image being decoded are passed in as the {@code width} * and {@code height} parameters. * * @param param an {@code ImageReadParam} to be used to get * the destination image or image type, or {@code null}. * @param imageTypes an {@code Iterator} of * {@code ImageTypeSpecifier}s indicating the legal image * types, with the default first. * @param width the true width of the image or tile being decoded. * @param height the true width of the image or tile being decoded. * * @return the {@code BufferedImage} to which decoded pixel * data should be written. * * @exception IIOException if the {@code ImageTypeSpecifier} * specified by {@code param} does not match any of the legal * ones from {@code imageTypes}. * @exception IllegalArgumentException if {@code imageTypes} * is {@code null} or empty, or if an object not of type * {@code ImageTypeSpecifier} 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} and {@code height} is greater than * {@code Integer.MAX_VALUE}. */ 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 = 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); } }