--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/imageio/ImageIO.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1609 @@
+/*
+ * Copyright 2000-2005 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.imageio;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.AccessController;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import javax.imageio.spi.IIORegistry;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.spi.ImageReaderWriterSpi;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.spi.ImageInputStreamSpi;
+import javax.imageio.spi.ImageOutputStreamSpi;
+import javax.imageio.spi.ImageTranscoderSpi;
+import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+import sun.awt.AppContext;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * A class containing static convenience methods for locating
+ * <code>ImageReader</code>s and <code>ImageWriter</code>s, and
+ * performing simple encoding and decoding.
+ *
+ */
+public final class ImageIO {
+
+ private static final IIORegistry theRegistry =
+ IIORegistry.getDefaultInstance();
+
+ /**
+ * Constructor is private to prevent instantiation.
+ */
+ private ImageIO() {}
+
+ /**
+ * Scans for plug-ins on the application class path,
+ * loads their service provider classes, and registers a service
+ * provider instance for each one found with the
+ * <code>IIORegistry</code>.
+ *
+ * <p>This method is needed because the application class path can
+ * theoretically change, or additional plug-ins may become available.
+ * Rather than re-scanning the classpath on every invocation of the
+ * API, the class path is scanned automatically only on the first
+ * invocation. Clients can call this method to prompt a re-scan.
+ * Thus this method need only be invoked by sophisticated applications
+ * which dynamically make new plug-ins available at runtime.
+ *
+ * <p> The <code>getResources</code> method of the context
+ * <code>ClassLoader</code> is used locate JAR files containing
+ * files named
+ * <code>META-INF/services/javax.imageio.spi.</code><i>classname</i>,
+ * where <i>classname</i> is one of <code>ImageReaderSpi</code>,
+ * <code>ImageWriterSpi</code>, <code>ImageTranscoderSpi</code>,
+ * <code>ImageInputStreamSpi</code>, or
+ * <code>ImageOutputStreamSpi</code>, along the application class
+ * path.
+ *
+ * <p> The contents of the located files indicate the names of
+ * actual implementation classes which implement the
+ * aforementioned service provider interfaces; the default class
+ * loader is then used to load each of these classes and to
+ * instantiate an instance of each class, which is then placed
+ * into the registry for later retrieval.
+ *
+ * <p> The exact set of locations searched depends on the
+ * implementation of the Java runtime enviroment.
+ *
+ * @see ClassLoader#getResources
+ */
+ public static void scanForPlugins() {
+ theRegistry.registerApplicationClasspathSpis();
+ }
+
+ // ImageInputStreams
+
+ /**
+ * A class to hold information about caching. Each
+ * <code>ThreadGroup</code> will have its own copy
+ * via the <code>AppContext</code> mechanism.
+ */
+ static class CacheInfo {
+ boolean useCache = true;
+ File cacheDirectory = null;
+ Boolean hasPermission = null;
+
+ public CacheInfo() {}
+
+ public boolean getUseCache() {
+ return useCache;
+ }
+
+ public void setUseCache(boolean useCache) {
+ this.useCache = useCache;
+ }
+
+ public File getCacheDirectory() {
+ return cacheDirectory;
+ }
+
+ public void setCacheDirectory(File cacheDirectory) {
+ this.cacheDirectory = cacheDirectory;
+ }
+
+ public Boolean getHasPermission() {
+ return hasPermission;
+ }
+
+ public void setHasPermission(Boolean hasPermission) {
+ this.hasPermission = hasPermission;
+ }
+ }
+
+ /**
+ * Returns the <code>CacheInfo</code> object associated with this
+ * <code>ThreadGroup</code>.
+ */
+ private static synchronized CacheInfo getCacheInfo() {
+ AppContext context = AppContext.getAppContext();
+ CacheInfo info = (CacheInfo)context.get(CacheInfo.class);
+ if (info == null) {
+ info = new CacheInfo();
+ context.put(CacheInfo.class, info);
+ }
+ return info;
+ }
+
+ /**
+ * Returns the default temporary (cache) directory as defined by the
+ * java.io.tmpdir system property.
+ */
+ private static String getTempDir() {
+ GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
+ return (String)AccessController.doPrivileged(a);
+ }
+
+ /**
+ * Determines whether the caller has write access to the cache
+ * directory, stores the result in the <code>CacheInfo</code> object,
+ * and returns the decision. This method helps to prevent mysterious
+ * SecurityExceptions to be thrown when this convenience class is used
+ * in an applet, for example.
+ */
+ private static boolean hasCachePermission() {
+ Boolean hasPermission = getCacheInfo().getHasPermission();
+
+ if (hasPermission != null) {
+ return hasPermission.booleanValue();
+ } else {
+ try {
+ SecurityManager security = System.getSecurityManager();
+ if (security != null) {
+ File cachedir = getCacheDirectory();
+ String cachepath;
+
+ if (cachedir != null) {
+ cachepath = cachedir.getPath();
+ } else {
+ cachepath = getTempDir();
+
+ if (cachepath == null) {
+ getCacheInfo().setHasPermission(Boolean.FALSE);
+ return false;
+ }
+ }
+
+ security.checkWrite(cachepath);
+ }
+ } catch (SecurityException e) {
+ getCacheInfo().setHasPermission(Boolean.FALSE);
+ return false;
+ }
+
+ getCacheInfo().setHasPermission(Boolean.TRUE);
+ return true;
+ }
+ }
+
+ /**
+ * Sets a flag indicating whether a disk-based cache file should
+ * be used when creating <code>ImageInputStream</code>s and
+ * <code>ImageOutputStream</code>s.
+ *
+ * <p> When reading from a standard <code>InputStream</code>>, it
+ * may be necessary to save previously read information in a cache
+ * since the underlying stream does not allow data to be re-read.
+ * Similarly, when writing to a standard
+ * <code>OutputStream</code>, a cache may be used to allow a
+ * previously written value to be changed before flushing it to
+ * the final destination.
+ *
+ * <p> The cache may reside in main memory or on disk. Setting
+ * this flag to <code>false</code> disallows the use of disk for
+ * future streams, which may be advantageous when working with
+ * small images, as the overhead of creating and destroying files
+ * is removed.
+ *
+ * <p> On startup, the value is set to <code>true</code>.
+ *
+ * @param useCache a <code>boolean</code> indicating whether a
+ * cache file should be used, in cases where it is optional.
+ *
+ * @see #getUseCache
+ */
+ public static void setUseCache(boolean useCache) {
+ getCacheInfo().setUseCache(useCache);
+ }
+
+ /**
+ * Returns the current value set by <code>setUseCache</code>, or
+ * <code>true</code> if no explicit setting has been made.
+ *
+ * @return true if a disk-based cache may be used for
+ * <code>ImageInputStream</code>s and
+ * <code>ImageOutputStream</code>s.
+ *
+ * @see #setUseCache
+ */
+ public static boolean getUseCache() {
+ return getCacheInfo().getUseCache();
+ }
+
+ /**
+ * Sets the directory where cache files are to be created. A
+ * value of <code>null</code> indicates that the system-dependent
+ * default temporary-file directory is to be used. If
+ * <code>getUseCache</code> returns false, this value is ignored.
+ *
+ * @param cacheDirectory a <code>File</code> specifying a directory.
+ *
+ * @see File#createTempFile(String, String, File)
+ *
+ * @exception SecurityException if the security manager denies
+ * access to the directory.
+ * @exception IllegalArgumentException if <code>cacheDir</code> is
+ * non-<code>null</code> but is not a directory.
+ *
+ * @see #getCacheDirectory
+ */
+ public static void setCacheDirectory(File cacheDirectory) {
+ if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {
+ throw new IllegalArgumentException("Not a directory!");
+ }
+ getCacheInfo().setCacheDirectory(cacheDirectory);
+ getCacheInfo().setHasPermission(null);
+ }
+
+ /**
+ * Returns the current value set by
+ * <code>setCacheDirectory</code>, or <code>null</code> if no
+ * explicit setting has been made.
+ *
+ * @return a <code>File</code> indicating the directory where
+ * cache files will be created, or <code>null</code> to indicate
+ * the system-dependent default temporary-file directory.
+ *
+ * @see #setCacheDirectory
+ */
+ public static File getCacheDirectory() {
+ return getCacheInfo().getCacheDirectory();
+ }
+
+ /**
+ * Returns an <code>ImageInputStream</code> that will take its
+ * input from the given <code>Object</code>. The set of
+ * <code>ImageInputStreamSpi</code>s registered with the
+ * <code>IIORegistry</code> class is queried and the first one
+ * that is able to take input from the supplied object is used to
+ * create the returned <code>ImageInputStream</code>. If no
+ * suitable <code>ImageInputStreamSpi</code> exists,
+ * <code>null</code> is returned.
+ *
+ * <p> The current cache settings from <code>getUseCache</code>and
+ * <code>getCacheDirectory</code> will be used to control caching.
+ *
+ * @param input an <code>Object</code> to be used as an input
+ * source, such as a <code>File</code>, readable
+ * <code>RandomAccessFile</code>, or <code>InputStream</code>.
+ *
+ * @return an <code>ImageInputStream</code>, or <code>null</code>.
+ *
+ * @exception IllegalArgumentException if <code>input</code>
+ * is <code>null</code>.
+ * @exception IOException if a cache file is needed but cannot be
+ * created.
+ *
+ * @see javax.imageio.spi.ImageInputStreamSpi
+ */
+ public static ImageInputStream createImageInputStream(Object input)
+ throws IOException {
+ if (input == null) {
+ throw new IllegalArgumentException("input == null!");
+ }
+
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class,
+ true);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ boolean usecache = getUseCache() && hasCachePermission();
+
+ while (iter.hasNext()) {
+ ImageInputStreamSpi spi = (ImageInputStreamSpi)iter.next();
+ if (spi.getInputClass().isInstance(input)) {
+ try {
+ return spi.createInputStreamInstance(input,
+ usecache,
+ getCacheDirectory());
+ } catch (IOException e) {
+ throw new IIOException("Can't create cache file!", e);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ // ImageOutputStreams
+
+ /**
+ * Returns an <code>ImageOutputStream</code> that will send its
+ * output to the given <code>Object</code>. The set of
+ * <code>ImageOutputStreamSpi</code>s registered with the
+ * <code>IIORegistry</code> class is queried and the first one
+ * that is able to send output from the supplied object is used to
+ * create the returned <code>ImageOutputStream</code>. If no
+ * suitable <code>ImageOutputStreamSpi</code> exists,
+ * <code>null</code> is returned.
+ *
+ * <p> The current cache settings from <code>getUseCache</code>and
+ * <code>getCacheDirectory</code> will be used to control caching.
+ *
+ * @param output an <code>Object</code> to be used as an output
+ * destination, such as a <code>File</code>, writable
+ * <code>RandomAccessFile</code>, or <code>OutputStream</code>.
+ *
+ * @return an <code>ImageOutputStream</code>, or
+ * <code>null</code>.
+ *
+ * @exception IllegalArgumentException if <code>output</code> is
+ * <code>null</code>.
+ * @exception IOException if a cache file is needed but cannot be
+ * created.
+ *
+ * @see javax.imageio.spi.ImageOutputStreamSpi
+ */
+ public static ImageOutputStream createImageOutputStream(Object output)
+ throws IOException {
+ if (output == null) {
+ throw new IllegalArgumentException("output == null!");
+ }
+
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,
+ true);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ boolean usecache = getUseCache() && hasCachePermission();
+
+ while (iter.hasNext()) {
+ ImageOutputStreamSpi spi = (ImageOutputStreamSpi)iter.next();
+ if (spi.getOutputClass().isInstance(output)) {
+ try {
+ return spi.createOutputStreamInstance(output,
+ usecache,
+ getCacheDirectory());
+ } catch (IOException e) {
+ throw new IIOException("Can't create cache file!", e);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static enum SpiInfo {
+ FORMAT_NAMES {
+ @Override
+ String[] info(ImageReaderWriterSpi spi) {
+ return spi.getFormatNames();
+ }
+ },
+ MIME_TYPES {
+ @Override
+ String[] info(ImageReaderWriterSpi spi) {
+ return spi.getMIMETypes();
+ }
+ },
+ FILE_SUFFIXES {
+ @Override
+ String[] info(ImageReaderWriterSpi spi) {
+ return spi.getFileSuffixes();
+ }
+ };
+
+ abstract String[] info(ImageReaderWriterSpi spi);
+ }
+
+ private static <S extends ImageReaderWriterSpi>
+ String[] getReaderWriterInfo(Class<S> spiClass, SpiInfo spiInfo)
+ {
+ // Ensure category is present
+ Iterator<S> iter;
+ try {
+ iter = theRegistry.getServiceProviders(spiClass, true);
+ } catch (IllegalArgumentException e) {
+ return new String[0];
+ }
+
+ HashSet<String> s = new HashSet<String>();
+ while (iter.hasNext()) {
+ ImageReaderWriterSpi spi = iter.next();
+ Collections.addAll(s, spiInfo.info(spi));
+ }
+
+ return s.toArray(new String[s.size()]);
+ }
+
+ // Readers
+
+ /**
+ * Returns an array of <code>String</code>s listing all of the
+ * informal format names understood by the current set of registered
+ * readers.
+ *
+ * @return an array of <code>String</code>s.
+ */
+ public static String[] getReaderFormatNames() {
+ return getReaderWriterInfo(ImageReaderSpi.class,
+ SpiInfo.FORMAT_NAMES);
+ }
+
+ /**
+ * Returns an array of <code>String</code>s listing all of the
+ * MIME types understood by the current set of registered
+ * readers.
+ *
+ * @return an array of <code>String</code>s.
+ */
+ public static String[] getReaderMIMETypes() {
+ return getReaderWriterInfo(ImageReaderSpi.class,
+ SpiInfo.MIME_TYPES);
+ }
+
+ /**
+ * Returns an array of <code>String</code>s listing all of the
+ * file suffixes associated with the formats understood
+ * by the current set of registered readers.
+ *
+ * @return an array of <code>String</code>s.
+ * @since 1.6
+ */
+ public static String[] getReaderFileSuffixes() {
+ return getReaderWriterInfo(ImageReaderSpi.class,
+ SpiInfo.FILE_SUFFIXES);
+ }
+
+ static class ImageReaderIterator implements Iterator<ImageReader> {
+ // Contains ImageReaderSpis
+ public Iterator iter;
+
+ public ImageReaderIterator(Iterator iter) {
+ this.iter = iter;
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public ImageReader next() {
+ ImageReaderSpi spi = null;
+ try {
+ spi = (ImageReaderSpi)iter.next();
+ return spi.createReaderInstance();
+ } catch (IOException e) {
+ // Deregister the spi in this case, but only as
+ // an ImageReaderSpi
+ theRegistry.deregisterServiceProvider(spi, ImageReaderSpi.class);
+ }
+ return null;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static class CanDecodeInputFilter
+ implements ServiceRegistry.Filter {
+
+ Object input;
+
+ public CanDecodeInputFilter(Object input) {
+ this.input = input;
+ }
+
+ public boolean filter(Object elt) {
+ try {
+ ImageReaderSpi spi = (ImageReaderSpi)elt;
+ ImageInputStream stream = null;
+ if (input instanceof ImageInputStream) {
+ stream = (ImageInputStream)input;
+ }
+
+ // Perform mark/reset as a defensive measure
+ // even though plug-ins are supposed to take
+ // care of it.
+ boolean canDecode = false;
+ if (stream != null) {
+ stream.mark();
+ }
+ canDecode = spi.canDecodeInput(input);
+ if (stream != null) {
+ stream.reset();
+ }
+
+ return canDecode;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+ }
+
+ static class CanEncodeImageAndFormatFilter
+ implements ServiceRegistry.Filter {
+
+ ImageTypeSpecifier type;
+ String formatName;
+
+ public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type,
+ String formatName) {
+ this.type = type;
+ this.formatName = formatName;
+ }
+
+ public boolean filter(Object elt) {
+ ImageWriterSpi spi = (ImageWriterSpi)elt;
+ return Arrays.asList(spi.getFormatNames()).contains(formatName) &&
+ spi.canEncodeImage(type);
+ }
+ }
+
+ static class ContainsFilter
+ implements ServiceRegistry.Filter {
+
+ Method method;
+ String name;
+
+ // method returns an array of Strings
+ public ContainsFilter(Method method,
+ String name) {
+ this.method = method;
+ this.name = name;
+ }
+
+ public boolean filter(Object elt) {
+ try {
+ return contains((String[])method.invoke(elt), name);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageReader</code>s that claim to be able to
+ * decode the supplied <code>Object</code>, typically an
+ * <code>ImageInputStream</code>.
+ *
+ * <p> The stream position is left at its prior position upon
+ * exit from this method.
+ *
+ * @param input an <code>ImageInputStream</code> or other
+ * <code>Object</code> containing encoded image data.
+ *
+ * @return an <code>Iterator</code> containing <code>ImageReader</code>s.
+ *
+ * @exception IllegalArgumentException if <code>input</code> is
+ * <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
+ */
+ public static Iterator<ImageReader> getImageReaders(Object input) {
+ if (input == null) {
+ throw new IllegalArgumentException("input == null!");
+ }
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
+ new CanDecodeInputFilter(input),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+
+ return new ImageReaderIterator(iter);
+ }
+
+ private static Method readerFormatNamesMethod;
+ private static Method readerFileSuffixesMethod;
+ private static Method readerMIMETypesMethod;
+ private static Method writerFormatNamesMethod;
+ private static Method writerFileSuffixesMethod;
+ private static Method writerMIMETypesMethod;
+
+ static {
+ try {
+ readerFormatNamesMethod =
+ ImageReaderSpi.class.getMethod("getFormatNames");
+ readerFileSuffixesMethod =
+ ImageReaderSpi.class.getMethod("getFileSuffixes");
+ readerMIMETypesMethod =
+ ImageReaderSpi.class.getMethod("getMIMETypes");
+
+ writerFormatNamesMethod =
+ ImageWriterSpi.class.getMethod("getFormatNames");
+ writerFileSuffixesMethod =
+ ImageWriterSpi.class.getMethod("getFileSuffixes");
+ writerMIMETypesMethod =
+ ImageWriterSpi.class.getMethod("getMIMETypes");
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageReader</code>s that claim to be able to
+ * decode the named format.
+ *
+ * @param formatName a <code>String</code> containing the informal
+ * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
+ *
+ * @return an <code>Iterator</code> containing
+ * <code>ImageReader</code>s.
+ *
+ * @exception IllegalArgumentException if <code>formatName</code>
+ * is <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageReaderSpi#getFormatNames
+ */
+ public static Iterator<ImageReader>
+ getImageReadersByFormatName(String formatName)
+ {
+ if (formatName == null) {
+ throw new IllegalArgumentException("formatName == null!");
+ }
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
+ new ContainsFilter(readerFormatNamesMethod,
+ formatName),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+ return new ImageReaderIterator(iter);
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageReader</code>s that claim to be able to
+ * decode files with the given suffix.
+ *
+ * @param fileSuffix a <code>String</code> containing a file
+ * suffix (<i>e.g.</i>, "jpg" or "tiff").
+ *
+ * @return an <code>Iterator</code> containing
+ * <code>ImageReader</code>s.
+ *
+ * @exception IllegalArgumentException if <code>fileSuffix</code>
+ * is <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
+ */
+ public static Iterator<ImageReader>
+ getImageReadersBySuffix(String fileSuffix)
+ {
+ if (fileSuffix == null) {
+ throw new IllegalArgumentException("fileSuffix == null!");
+ }
+ // Ensure category is present
+ Iterator iter;
+ try {
+ iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
+ new ContainsFilter(readerFileSuffixesMethod,
+ fileSuffix),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+ return new ImageReaderIterator(iter);
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageReader</code>s that claim to be able to
+ * decode files with the given MIME type.
+ *
+ * @param MIMEType a <code>String</code> containing a file
+ * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
+ *
+ * @return an <code>Iterator</code> containing
+ * <code>ImageReader</code>s.
+ *
+ * @exception IllegalArgumentException if <code>MIMEType</code> is
+ * <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
+ */
+ public static Iterator<ImageReader>
+ getImageReadersByMIMEType(String MIMEType)
+ {
+ if (MIMEType == null) {
+ throw new IllegalArgumentException("MIMEType == null!");
+ }
+ // Ensure category is present
+ Iterator iter;
+ try {
+ iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
+ new ContainsFilter(readerMIMETypesMethod,
+ MIMEType),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+ return new ImageReaderIterator(iter);
+ }
+
+ // Writers
+
+ /**
+ * Returns an array of <code>String</code>s listing all of the
+ * informal format names understood by the current set of registered
+ * writers.
+ *
+ * @return an array of <code>String</code>s.
+ */
+ public static String[] getWriterFormatNames() {
+ return getReaderWriterInfo(ImageWriterSpi.class,
+ SpiInfo.FORMAT_NAMES);
+ }
+
+ /**
+ * Returns an array of <code>String</code>s listing all of the
+ * MIME types understood by the current set of registered
+ * writers.
+ *
+ * @return an array of <code>String</code>s.
+ */
+ public static String[] getWriterMIMETypes() {
+ return getReaderWriterInfo(ImageWriterSpi.class,
+ SpiInfo.MIME_TYPES);
+ }
+
+ /**
+ * Returns an array of <code>String</code>s listing all of the
+ * file suffixes associated with the formats understood
+ * by the current set of registered writers.
+ *
+ * @return an array of <code>String</code>s.
+ * @since 1.6
+ */
+ public static String[] getWriterFileSuffixes() {
+ return getReaderWriterInfo(ImageWriterSpi.class,
+ SpiInfo.FILE_SUFFIXES);
+ }
+
+ static class ImageWriterIterator implements Iterator<ImageWriter> {
+ // Contains ImageWriterSpis
+ public Iterator iter;
+
+ public ImageWriterIterator(Iterator iter) {
+ this.iter = iter;
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public ImageWriter next() {
+ ImageWriterSpi spi = null;
+ try {
+ spi = (ImageWriterSpi)iter.next();
+ return spi.createWriterInstance();
+ } catch (IOException e) {
+ // Deregister the spi in this case, but only as a writerSpi
+ theRegistry.deregisterServiceProvider(spi, ImageWriterSpi.class);
+ }
+ return null;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static boolean contains(String[] names, String name) {
+ for (int i = 0; i < names.length; i++) {
+ if (name.equalsIgnoreCase(names[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageWriter</code>s that claim to be able to
+ * encode the named format.
+ *
+ * @param formatName a <code>String</code> containing the informal
+ * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
+ *
+ * @return an <code>Iterator</code> containing
+ * <code>ImageWriter</code>s.
+ *
+ * @exception IllegalArgumentException if <code>formatName</code> is
+ * <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageWriterSpi#getFormatNames
+ */
+ public static Iterator<ImageWriter>
+ getImageWritersByFormatName(String formatName)
+ {
+ if (formatName == null) {
+ throw new IllegalArgumentException("formatName == null!");
+ }
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
+ new ContainsFilter(writerFormatNamesMethod,
+ formatName),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+ return new ImageWriterIterator(iter);
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageWriter</code>s that claim to be able to
+ * encode files with the given suffix.
+ *
+ * @param fileSuffix a <code>String</code> containing a file
+ * suffix (<i>e.g.</i>, "jpg" or "tiff").
+ *
+ * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
+ *
+ * @exception IllegalArgumentException if <code>fileSuffix</code> is
+ * <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes
+ */
+ public static Iterator<ImageWriter>
+ getImageWritersBySuffix(String fileSuffix)
+ {
+ if (fileSuffix == null) {
+ throw new IllegalArgumentException("fileSuffix == null!");
+ }
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
+ new ContainsFilter(writerFileSuffixesMethod,
+ fileSuffix),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+ return new ImageWriterIterator(iter);
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageWriter</code>s that claim to be able to
+ * encode files with the given MIME type.
+ *
+ * @param MIMEType a <code>String</code> containing a file
+ * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
+ *
+ * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
+ *
+ * @exception IllegalArgumentException if <code>MIMEType</code> is
+ * <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageWriterSpi#getMIMETypes
+ */
+ public static Iterator<ImageWriter>
+ getImageWritersByMIMEType(String MIMEType)
+ {
+ if (MIMEType == null) {
+ throw new IllegalArgumentException("MIMEType == null!");
+ }
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
+ new ContainsFilter(writerMIMETypesMethod,
+ MIMEType),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+ return new ImageWriterIterator(iter);
+ }
+
+ /**
+ * Returns an <code>ImageWriter</code>corresponding to the given
+ * <code>ImageReader</code>, if there is one, or <code>null</code>
+ * if the plug-in for this <code>ImageReader</code> does not
+ * specify a corresponding <code>ImageWriter</code>, or if the
+ * given <code>ImageReader</code> is not registered. This
+ * mechanism may be used to obtain an <code>ImageWriter</code>
+ * that will understand the internal structure of non-pixel
+ * metadata (as encoded by <code>IIOMetadata</code> objects)
+ * generated by the <code>ImageReader</code>. By obtaining this
+ * data from the <code>ImageReader</code> and passing it on to the
+ * <code>ImageWriter</code> obtained with this method, a client
+ * program can read an image, modify it in some way, and write it
+ * back out preserving all metadata, without having to understand
+ * anything about the structure of the metadata, or even about
+ * the image format. Note that this method returns the
+ * "preferred" writer, which is the first in the list returned by
+ * <code>javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()</code>.
+ *
+ * @param reader an instance of a registered <code>ImageReader</code>.
+ *
+ * @return an <code>ImageWriter</code>, or null.
+ *
+ * @exception IllegalArgumentException if <code>reader</code> is
+ * <code>null</code>.
+ *
+ * @see #getImageReader(ImageWriter)
+ * @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()
+ */
+ public static ImageWriter getImageWriter(ImageReader reader) {
+ if (reader == null) {
+ throw new IllegalArgumentException("reader == null!");
+ }
+
+ ImageReaderSpi readerSpi = reader.getOriginatingProvider();
+ if (readerSpi == null) {
+ Iterator readerSpiIter;
+ // Ensure category is present
+ try {
+ readerSpiIter =
+ theRegistry.getServiceProviders(ImageReaderSpi.class,
+ false);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ while (readerSpiIter.hasNext()) {
+ ImageReaderSpi temp = (ImageReaderSpi) readerSpiIter.next();
+ if (temp.isOwnReader(reader)) {
+ readerSpi = temp;
+ break;
+ }
+ }
+ if (readerSpi == null) {
+ return null;
+ }
+ }
+
+ String[] writerNames = readerSpi.getImageWriterSpiNames();
+ if (writerNames == null) {
+ return null;
+ }
+
+ Class writerSpiClass = null;
+ try {
+ writerSpiClass = Class.forName(writerNames[0], true,
+ ClassLoader.getSystemClassLoader());
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+
+ ImageWriterSpi writerSpi = (ImageWriterSpi)
+ theRegistry.getServiceProviderByClass(writerSpiClass);
+ if (writerSpi == null) {
+ return null;
+ }
+
+ try {
+ return writerSpi.createWriterInstance();
+ } catch (IOException e) {
+ // Deregister the spi in this case, but only as a writerSpi
+ theRegistry.deregisterServiceProvider(writerSpi,
+ ImageWriterSpi.class);
+ return null;
+ }
+ }
+
+ /**
+ * Returns an <code>ImageReader</code>corresponding to the given
+ * <code>ImageWriter</code>, if there is one, or <code>null</code>
+ * if the plug-in for this <code>ImageWriter</code> does not
+ * specify a corresponding <code>ImageReader</code>, or if the
+ * given <code>ImageWriter</code> is not registered. This method
+ * is provided principally for symmetry with
+ * <code>getImageWriter(ImageReader)</code>. Note that this
+ * method returns the "preferred" reader, which is the first in
+ * the list returned by
+ * javax.imageio.spi.ImageWriterSpi.<code>getImageReaderSpiNames()</code>.
+ *
+ * @param writer an instance of a registered <code>ImageWriter</code>.
+ *
+ * @return an <code>ImageReader</code>, or null.
+ *
+ * @exception IllegalArgumentException if <code>writer</code> is
+ * <code>null</code>.
+ *
+ * @see #getImageWriter(ImageReader)
+ * @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()
+ */
+ public static ImageReader getImageReader(ImageWriter writer) {
+ if (writer == null) {
+ throw new IllegalArgumentException("writer == null!");
+ }
+
+ ImageWriterSpi writerSpi = writer.getOriginatingProvider();
+ if (writerSpi == null) {
+ Iterator writerSpiIter;
+ // Ensure category is present
+ try {
+ writerSpiIter =
+ theRegistry.getServiceProviders(ImageWriterSpi.class,
+ false);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ while (writerSpiIter.hasNext()) {
+ ImageWriterSpi temp = (ImageWriterSpi) writerSpiIter.next();
+ if (temp.isOwnWriter(writer)) {
+ writerSpi = temp;
+ break;
+ }
+ }
+ if (writerSpi == null) {
+ return null;
+ }
+ }
+
+ String[] readerNames = writerSpi.getImageReaderSpiNames();
+ if (readerNames == null) {
+ return null;
+ }
+
+ Class readerSpiClass = null;
+ try {
+ readerSpiClass = Class.forName(readerNames[0], true,
+ ClassLoader.getSystemClassLoader());
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+
+ ImageReaderSpi readerSpi = (ImageReaderSpi)
+ theRegistry.getServiceProviderByClass(readerSpiClass);
+ if (readerSpi == null) {
+ return null;
+ }
+
+ try {
+ return readerSpi.createReaderInstance();
+ } catch (IOException e) {
+ // Deregister the spi in this case, but only as a readerSpi
+ theRegistry.deregisterServiceProvider(readerSpi,
+ ImageReaderSpi.class);
+ return null;
+ }
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageWriter</code>s that claim to be able to
+ * encode images of the given layout (specified using an
+ * <code>ImageTypeSpecifier</code>) in the given format.
+ *
+ * @param type an <code>ImageTypeSpecifier</code> indicating the
+ * layout of the image to be written.
+ * @param formatName the informal name of the <code>format</code>.
+ *
+ * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
+ *
+ * @exception IllegalArgumentException if any parameter is
+ * <code>null</code>.
+ *
+ * @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)
+ */
+ public static Iterator<ImageWriter>
+ getImageWriters(ImageTypeSpecifier type, String formatName)
+ {
+ if (type == null) {
+ throw new IllegalArgumentException("type == null!");
+ }
+ if (formatName == null) {
+ throw new IllegalArgumentException("formatName == null!");
+ }
+
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
+ new CanEncodeImageAndFormatFilter(type,
+ formatName),
+ true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+
+ return new ImageWriterIterator(iter);
+ }
+
+ static class ImageTranscoderIterator
+ implements Iterator<ImageTranscoder>
+ {
+ // Contains ImageTranscoderSpis
+ public Iterator iter;
+
+ public ImageTranscoderIterator(Iterator iter) {
+ this.iter = iter;
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public ImageTranscoder next() {
+ ImageTranscoderSpi spi = null;
+ spi = (ImageTranscoderSpi)iter.next();
+ return spi.createTranscoderInstance();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static class TranscoderFilter
+ implements ServiceRegistry.Filter {
+
+ String readerSpiName;
+ String writerSpiName;
+
+ public TranscoderFilter(ImageReaderSpi readerSpi,
+ ImageWriterSpi writerSpi) {
+ this.readerSpiName = readerSpi.getClass().getName();
+ this.writerSpiName = writerSpi.getClass().getName();
+ }
+
+ public boolean filter(Object elt) {
+ ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;
+ String readerName = spi.getReaderServiceProviderName();
+ String writerName = spi.getWriterServiceProviderName();
+ return (readerName.equals(readerSpiName) &&
+ writerName.equals(writerSpiName));
+ }
+ }
+
+ /**
+ * Returns an <code>Iterator</code> containing all currently
+ * registered <code>ImageTranscoder</code>s that claim to be
+ * able to transcode between the metadata of the given
+ * <code>ImageReader</code> and <code>ImageWriter</code>.
+ *
+ * @param reader an <code>ImageReader</code>.
+ * @param writer an <code>ImageWriter</code>.
+ *
+ * @return an <code>Iterator</code> containing
+ * <code>ImageTranscoder</code>s.
+ *
+ * @exception IllegalArgumentException if <code>reader</code> or
+ * <code>writer</code> is <code>null</code>.
+ */
+ public static Iterator<ImageTranscoder>
+ getImageTranscoders(ImageReader reader, ImageWriter writer)
+ {
+ if (reader == null) {
+ throw new IllegalArgumentException("reader == null!");
+ }
+ if (writer == null) {
+ throw new IllegalArgumentException("writer == null!");
+ }
+ ImageReaderSpi readerSpi = reader.getOriginatingProvider();
+ ImageWriterSpi writerSpi = writer.getOriginatingProvider();
+ ServiceRegistry.Filter filter =
+ new TranscoderFilter(readerSpi, writerSpi);
+
+ Iterator iter;
+ // Ensure category is present
+ try {
+ iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,
+ filter, true);
+ } catch (IllegalArgumentException e) {
+ return Collections.emptyIterator();
+ }
+ return new ImageTranscoderIterator(iter);
+ }
+
+ // All-in-one methods
+
+ /**
+ * Returns a <code>BufferedImage</code> as the result of decoding
+ * a supplied <code>File</code> with an <code>ImageReader</code>
+ * chosen automatically from among those currently registered.
+ * The <code>File</code> is wrapped in an
+ * <code>ImageInputStream</code>. If no registered
+ * <code>ImageReader</code> claims to be able to read the
+ * resulting stream, <code>null</code> is returned.
+ *
+ * <p> The current cache settings from <code>getUseCache</code>and
+ * <code>getCacheDirectory</code> will be used to control caching in the
+ * <code>ImageInputStream</code> that is created.
+ *
+ * <p> Note that there is no <code>read</code> method that takes a
+ * filename as a <code>String</code>; use this method instead after
+ * creating a <code>File</code> from the filename.
+ *
+ * <p> This method does not attempt to locate
+ * <code>ImageReader</code>s that can read directly from a
+ * <code>File</code>; that may be accomplished using
+ * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
+ *
+ * @param input a <code>File</code> to read from.
+ *
+ * @return a <code>BufferedImage</code> containing the decoded
+ * contents of the input, or <code>null</code>.
+ *
+ * @exception IllegalArgumentException if <code>input</code> is
+ * <code>null</code>.
+ * @exception IOException if an error occurs during reading.
+ */
+ public static BufferedImage read(File input) throws IOException {
+ if (input == null) {
+ throw new IllegalArgumentException("input == null!");
+ }
+ if (!input.canRead()) {
+ throw new IIOException("Can't read input file!");
+ }
+
+ ImageInputStream stream = createImageInputStream(input);
+ if (stream == null) {
+ throw new IIOException("Can't create an ImageInputStream!");
+ }
+ BufferedImage bi = read(stream);
+ if (bi == null) {
+ stream.close();
+ }
+ return bi;
+ }
+
+ /**
+ * Returns a <code>BufferedImage</code> as the result of decoding
+ * a supplied <code>InputStream</code> with an <code>ImageReader</code>
+ * chosen automatically from among those currently registered.
+ * The <code>InputStream</code> is wrapped in an
+ * <code>ImageInputStream</code>. If no registered
+ * <code>ImageReader</code> claims to be able to read the
+ * resulting stream, <code>null</code> is returned.
+ *
+ * <p> The current cache settings from <code>getUseCache</code>and
+ * <code>getCacheDirectory</code> will be used to control caching in the
+ * <code>ImageInputStream</code> that is created.
+ *
+ * <p> This method does not attempt to locate
+ * <code>ImageReader</code>s that can read directly from an
+ * <code>InputStream</code>; that may be accomplished using
+ * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
+ *
+ * <p> This method <em>does not</em> close the provided
+ * <code>InputStream</code> after the read operation has completed;
+ * it is the responsibility of the caller to close the stream, if desired.
+ *
+ * @param input an <code>InputStream</code> to read from.
+ *
+ * @return a <code>BufferedImage</code> containing the decoded
+ * contents of the input, or <code>null</code>.
+ *
+ * @exception IllegalArgumentException if <code>input</code> is
+ * <code>null</code>.
+ * @exception IOException if an error occurs during reading.
+ */
+ public static BufferedImage read(InputStream input) throws IOException {
+ if (input == null) {
+ throw new IllegalArgumentException("input == null!");
+ }
+
+ ImageInputStream stream = createImageInputStream(input);
+ BufferedImage bi = read(stream);
+ if (bi == null) {
+ stream.close();
+ }
+ return bi;
+ }
+
+ /**
+ * Returns a <code>BufferedImage</code> as the result of decoding
+ * a supplied <code>URL</code> with an <code>ImageReader</code>
+ * chosen automatically from among those currently registered. An
+ * <code>InputStream</code> is obtained from the <code>URL</code>,
+ * which is wrapped in an <code>ImageInputStream</code>. If no
+ * registered <code>ImageReader</code> claims to be able to read
+ * the resulting stream, <code>null</code> is returned.
+ *
+ * <p> The current cache settings from <code>getUseCache</code>and
+ * <code>getCacheDirectory</code> will be used to control caching in the
+ * <code>ImageInputStream</code> that is created.
+ *
+ * <p> This method does not attempt to locate
+ * <code>ImageReader</code>s that can read directly from a
+ * <code>URL</code>; that may be accomplished using
+ * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
+ *
+ * @param input a <code>URL</code> to read from.
+ *
+ * @return a <code>BufferedImage</code> containing the decoded
+ * contents of the input, or <code>null</code>.
+ *
+ * @exception IllegalArgumentException if <code>input</code> is
+ * <code>null</code>.
+ * @exception IOException if an error occurs during reading.
+ */
+ public static BufferedImage read(URL input) throws IOException {
+ if (input == null) {
+ throw new IllegalArgumentException("input == null!");
+ }
+
+ InputStream istream = null;
+ try {
+ istream = input.openStream();
+ } catch (IOException e) {
+ throw new IIOException("Can't get input stream from URL!", e);
+ }
+ ImageInputStream stream = createImageInputStream(istream);
+ BufferedImage bi;
+ try {
+ bi = read(stream);
+ if (bi == null) {
+ stream.close();
+ }
+ } finally {
+ istream.close();
+ }
+ return bi;
+ }
+
+ /**
+ * Returns a <code>BufferedImage</code> as the result of decoding
+ * a supplied <code>ImageInputStream</code> with an
+ * <code>ImageReader</code> chosen automatically from among those
+ * currently registered. If no registered
+ * <code>ImageReader</code> claims to be able to read the stream,
+ * <code>null</code> is returned.
+ *
+ * <p> Unlike most other methods in this class, this method <em>does</em>
+ * close the provided <code>ImageInputStream</code> after the read
+ * operation has completed, unless <code>null</code> is returned,
+ * in which case this method <em>does not</em> close the stream.
+ *
+ * @param stream an <code>ImageInputStream</code> to read from.
+ *
+ * @return a <code>BufferedImage</code> containing the decoded
+ * contents of the input, or <code>null</code>.
+ *
+ * @exception IllegalArgumentException if <code>stream</code> is
+ * <code>null</code>.
+ * @exception IOException if an error occurs during reading.
+ */
+ public static BufferedImage read(ImageInputStream stream)
+ throws IOException {
+ if (stream == null) {
+ throw new IllegalArgumentException("stream == null!");
+ }
+
+ Iterator iter = getImageReaders(stream);
+ if (!iter.hasNext()) {
+ return null;
+ }
+
+ ImageReader reader = (ImageReader)iter.next();
+ ImageReadParam param = reader.getDefaultReadParam();
+ reader.setInput(stream, true, true);
+ BufferedImage bi;
+ try {
+ bi = reader.read(0, param);
+ } finally {
+ reader.dispose();
+ stream.close();
+ }
+ return bi;
+ }
+
+ /**
+ * Writes an image using the an arbitrary <code>ImageWriter</code>
+ * that supports the given format to an
+ * <code>ImageOutputStream</code>. The image is written to the
+ * <code>ImageOutputStream</code> starting at the current stream
+ * pointer, overwriting existing stream data from that point
+ * forward, if present.
+ *
+ * <p> This method <em>does not</em> close the provided
+ * <code>ImageOutputStream</code> after the write operation has completed;
+ * it is the responsibility of the caller to close the stream, if desired.
+ *
+ * @param im a <code>RenderedImage</code> to be written.
+ * @param formatName a <code>String</code> containg the informal
+ * name of the format.
+ * @param output an <code>ImageOutputStream</code> to be written to.
+ *
+ * @return <code>false</code> if no appropriate writer is found.
+ *
+ * @exception IllegalArgumentException if any parameter is
+ * <code>null</code>.
+ * @exception IOException if an error occurs during writing.
+ */
+ public static boolean write(RenderedImage im,
+ String formatName,
+ ImageOutputStream output) throws IOException {
+ if (im == null) {
+ throw new IllegalArgumentException("im == null!");
+ }
+ if (formatName == null) {
+ throw new IllegalArgumentException("formatName == null!");
+ }
+ if (output == null) {
+ throw new IllegalArgumentException("output == null!");
+ }
+
+ return doWrite(im, getWriter(im, formatName), output);
+ }
+
+ /**
+ * Writes an image using an arbitrary <code>ImageWriter</code>
+ * that supports the given format to a <code>File</code>. If
+ * there is already a <code>File</code> present, its contents are
+ * discarded.
+ *
+ * @param im a <code>RenderedImage</code> to be written.
+ * @param formatName a <code>String</code> containg the informal
+ * name of the format.
+ * @param output a <code>File</code> to be written to.
+ *
+ * @return <code>false</code> if no appropriate writer is found.
+ *
+ * @exception IllegalArgumentException if any parameter is
+ * <code>null</code>.
+ * @exception IOException if an error occurs during writing.
+ */
+ public static boolean write(RenderedImage im,
+ String formatName,
+ File output) throws IOException {
+ if (output == null) {
+ throw new IllegalArgumentException("output == null!");
+ }
+ ImageOutputStream stream = null;
+
+ ImageWriter writer = getWriter(im, formatName);
+ if (writer == null) {
+ /* Do not make changes in the file system if we have
+ * no appropriate writer.
+ */
+ return false;
+ }
+
+ try {
+ output.delete();
+ stream = createImageOutputStream(output);
+ } catch (IOException e) {
+ throw new IIOException("Can't create output stream!", e);
+ }
+
+ try {
+ return doWrite(im, writer, stream);
+ } finally {
+ stream.close();
+ }
+ }
+
+ /**
+ * Writes an image using an arbitrary <code>ImageWriter</code>
+ * that supports the given format to an <code>OutputStream</code>.
+ *
+ * <p> This method <em>does not</em> close the provided
+ * <code>OutputStream</code> after the write operation has completed;
+ * it is the responsibility of the caller to close the stream, if desired.
+ *
+ * <p> The current cache settings from <code>getUseCache</code>and
+ * <code>getCacheDirectory</code> will be used to control caching.
+ *
+ * @param im a <code>RenderedImage</code> to be written.
+ * @param formatName a <code>String</code> containg the informal
+ * name of the format.
+ * @param output an <code>OutputStream</code> to be written to.
+ *
+ * @return <code>false</code> if no appropriate writer is found.
+ *
+ * @exception IllegalArgumentException if any parameter is
+ * <code>null</code>.
+ * @exception IOException if an error occurs during writing.
+ */
+ public static boolean write(RenderedImage im,
+ String formatName,
+ OutputStream output) throws IOException {
+ if (output == null) {
+ throw new IllegalArgumentException("output == null!");
+ }
+ ImageOutputStream stream = null;
+ try {
+ stream = createImageOutputStream(output);
+ } catch (IOException e) {
+ throw new IIOException("Can't create output stream!", e);
+ }
+
+ try {
+ return doWrite(im, getWriter(im, formatName), stream);
+ } finally {
+ stream.close();
+ }
+ }
+
+ /**
+ * Returns <code>ImageWriter</code> instance according to given
+ * rendered image and image format or <code>null</code> if there
+ * is no appropriate writer.
+ */
+ private static ImageWriter getWriter(RenderedImage im,
+ String formatName) {
+ ImageTypeSpecifier type =
+ ImageTypeSpecifier.createFromRenderedImage(im);
+ Iterator<ImageWriter> iter = getImageWriters(type, formatName);
+
+ if (iter.hasNext()) {
+ return iter.next();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Writes image to output stream using given image writer.
+ */
+ private static boolean doWrite(RenderedImage im, ImageWriter writer,
+ ImageOutputStream output) throws IOException {
+ if (writer == null) {
+ return false;
+ }
+ writer.setOutput(output);
+ try {
+ writer.write(im);
+ } finally {
+ writer.dispose();
+ output.flush();
+ }
+ return true;
+ }
+}