1 /*
2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
3  * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
4  *
5  *
6  *
7  *
8  *
9  *
10  *
11  *
12  *
13  *
14  *
15  *
16  *
17  *
18  *
19  *
20  *
21  *
22  *
23  *
24  */

25
26 package javax.imageio;
27
28 import java.awt.image.BufferedImage;
29 import java.awt.image.RenderedImage;
30 import java.io.File;
31 import java.io.FilePermission;
32 import java.io.InputStream;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.lang.reflect.Method;
36 import java.net.URL;
37 import java.security.AccessController;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.HashSet;
41 import java.util.Iterator;
42 import java.util.NoSuchElementException;
43 import java.util.Set;
44 import javax.imageio.spi.IIORegistry;
45 import javax.imageio.spi.ImageReaderSpi;
46 import javax.imageio.spi.ImageReaderWriterSpi;
47 import javax.imageio.spi.ImageWriterSpi;
48 import javax.imageio.spi.ImageInputStreamSpi;
49 import javax.imageio.spi.ImageOutputStreamSpi;
50 import javax.imageio.spi.ImageTranscoderSpi;
51 import javax.imageio.spi.ServiceRegistry;
52 import javax.imageio.stream.ImageInputStream;
53 import javax.imageio.stream.ImageOutputStream;
54 import sun.awt.AppContext;
55 import sun.security.action.GetPropertyAction;
56
57 /**
58  * A class containing static convenience methods for locating
59  * <code>ImageReader</code>s and <code>ImageWriter</code>s, and
60  * performing simple encoding and decoding.
61  *
62  */

63 public final class ImageIO {
64
65     private static final IIORegistry theRegistry =
66         IIORegistry.getDefaultInstance();
67
68     /**
69      * Constructor is private to prevent instantiation.
70      */

71     private ImageIO() {}
72
73     /**
74      * Scans for plug-ins on the application class path,
75      * loads their service provider classes, and registers a service
76      * provider instance for each one found with the
77      * <code>IIORegistry</code>.
78      *
79      * <p>This method is needed because the application class path can
80      * theoretically change, or additional plug-ins may become available.
81      * Rather than re-scanning the classpath on every invocation of the
82      * API, the class path is scanned automatically only on the first
83      * invocation. Clients can call this method to prompt a re-scan.
84      * Thus this method need only be invoked by sophisticated applications
85      * which dynamically make new plug-ins available at runtime.
86      *
87      * <p> The <code>getResources</code> method of the context
88      * <code>ClassLoader</code> is used locate JAR files containing
89      * files named
90      * <code>META-INF/services/javax.imageio.spi.</code><i>classname</i>,
91      * where <i>classname</i> is one of <code>ImageReaderSpi</code>,
92      * <code>ImageWriterSpi</code>, <code>ImageTranscoderSpi</code>,
93      * <code>ImageInputStreamSpi</code>, or
94      * <code>ImageOutputStreamSpi</code>, along the application class
95      * path.
96      *
97      * <p> The contents of the located files indicate the names of
98      * actual implementation classes which implement the
99      * aforementioned service provider interfaces; the default class
100      * loader is then used to load each of these classes and to
101      * instantiate an instance of each class, which is then placed
102      * into the registry for later retrieval.
103      *
104      * <p> The exact set of locations searched depends on the
105      * implementation of the Java runtime environment.
106      *
107      * @see ClassLoader#getResources
108      */

109     public static void scanForPlugins() {
110         theRegistry.registerApplicationClasspathSpis();
111     }
112
113     // ImageInputStreams
114
115     /**
116      * A class to hold information about caching.  Each
117      * <code>ThreadGroup</code> will have its own copy
118      * via the <code>AppContext</code> mechanism.
119      */

120     static class CacheInfo {
121         boolean useCache = true;
122         File cacheDirectory = null;
123         Boolean hasPermission = null;
124
125         public CacheInfo() {}
126
127         public boolean getUseCache() {
128             return useCache;
129         }
130
131         public void setUseCache(boolean useCache) {
132             this.useCache = useCache;
133         }
134
135         public File getCacheDirectory() {
136             return cacheDirectory;
137         }
138
139         public void setCacheDirectory(File cacheDirectory) {
140             this.cacheDirectory = cacheDirectory;
141         }
142
143         public Boolean getHasPermission() {
144             return hasPermission;
145         }
146
147         public void setHasPermission(Boolean hasPermission) {
148             this.hasPermission = hasPermission;
149         }
150     }
151
152     /**
153      * Returns the <code>CacheInfo</code> object associated with this
154      * <code>ThreadGroup</code>.
155      */

156     private static synchronized CacheInfo getCacheInfo() {
157         AppContext context = AppContext.getAppContext();
158         CacheInfo info = (CacheInfo)context.get(CacheInfo.class);
159         if (info == null) {
160             info = new CacheInfo();
161             context.put(CacheInfo.class, info);
162         }
163         return info;
164     }
165
166     /**
167      * Returns the default temporary (cache) directory as defined by the
168      * java.io.tmpdir system property.
169      */

170     private static String getTempDir() {
171         GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
172         return (String)AccessController.doPrivileged(a);
173     }
174
175     /**
176      * Determines whether the caller has write access to the cache
177      * directory, stores the result in the <code>CacheInfo</code> object,
178      * and returns the decision.  This method helps to prevent mysterious
179      * SecurityExceptions to be thrown when this convenience class is used
180      * in an applet, for example.
181      */

182     private static boolean hasCachePermission() {
183         Boolean hasPermission = getCacheInfo().getHasPermission();
184
185         if (hasPermission != null) {
186             return hasPermission.booleanValue();
187         } else {
188             try {
189                 SecurityManager security = System.getSecurityManager();
190                 if (security != null) {
191                     File cachedir = getCacheDirectory();
192                     String cachepath;
193
194                     if (cachedir != null) {
195                         cachepath = cachedir.getPath();
196                     } else {
197                         cachepath = getTempDir();
198
199                         if (cachepath == null || cachepath.isEmpty()) {
200                             getCacheInfo().setHasPermission(Boolean.FALSE);
201                             return false;
202                         }
203                     }
204
205                     // we have to check whether we can read, write,
206                     // and delete cache files.
207                     // So, compose cache file path and check it.
208                     String filepath = cachepath;
209                     if (!filepath.endsWith(File.separator)) {
210                         filepath += File.separator;
211                     }
212                     filepath += "*";
213
214                     security.checkPermission(new FilePermission(filepath, "read, write, delete"));
215                 }
216             } catch (SecurityException e) {
217                 getCacheInfo().setHasPermission(Boolean.FALSE);
218                 return false;
219             }
220
221             getCacheInfo().setHasPermission(Boolean.TRUE);
222             return true;
223         }
224     }
225
226     /**
227      * Sets a flag indicating whether a disk-based cache file should
228      * be used when creating <code>ImageInputStream</code>s and
229      * <code>ImageOutputStream</code>s.
230      *
231      * <p> When reading from a standard <code>InputStream</code>, it
232      * may be necessary to save previously read information in a cache
233      * since the underlying stream does not allow data to be re-read.
234      * Similarly, when writing to a standard
235      * <code>OutputStream</code>, a cache may be used to allow a
236      * previously written value to be changed before flushing it to
237      * the final destination.
238      *
239      * <p> The cache may reside in main memory or on disk.  Setting
240      * this flag to <code>false</code> disallows the use of disk for
241      * future streams, which may be advantageous when working with
242      * small images, as the overhead of creating and destroying files
243      * is removed.
244      *
245      * <p> On startup, the value is set to <code>true</code>.
246      *
247      * @param useCache a <code>boolean</code> indicating whether a
248      * cache file should be used, in cases where it is optional.
249      *
250      * @see #getUseCache
251      */

252     public static void setUseCache(boolean useCache) {
253         getCacheInfo().setUseCache(useCache);
254     }
255
256     /**
257      * Returns the current value set by <code>setUseCache</code>, or
258      * <code>true</code> if no explicit setting has been made.
259      *
260      * @return true if a disk-based cache may be used for
261      * <code>ImageInputStream</code>s and
262      * <code>ImageOutputStream</code>s.
263      *
264      * @see #setUseCache
265      */

266     public static boolean getUseCache() {
267         return getCacheInfo().getUseCache();
268     }
269
270     /**
271      * Sets the directory where cache files are to be created.  A
272      * value of <code>null</code> indicates that the system-dependent
273      * default temporary-file directory is to be used.  If
274      * <code>getUseCache</code> returns falsethis value is ignored.
275      *
276      * @param cacheDirectory a <code>File</code> specifying a directory.
277      *
278      * @see File#createTempFile(String, String, File)
279      *
280      * @exception SecurityException if the security manager denies
281      * access to the directory.
282      * @exception IllegalArgumentException if <code>cacheDir</code> is
283      * non-<code>null</code> but is not a directory.
284      *
285      * @see #getCacheDirectory
286      */

287     public static void setCacheDirectory(File cacheDirectory) {
288         if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {
289             throw new IllegalArgumentException("Not a directory!");
290         }
291         getCacheInfo().setCacheDirectory(cacheDirectory);
292         getCacheInfo().setHasPermission(null);
293     }
294
295     /**
296      * Returns the current value set by
297      * <code>setCacheDirectory</code>, or <code>null</code> if no
298      * explicit setting has been made.
299      *
300      * @return a <code>File</code> indicating the directory where
301      * cache files will be created, or <code>null</code> to indicate
302      * the system-dependent default temporary-file directory.
303      *
304      * @see #setCacheDirectory
305      */

306     public static File getCacheDirectory() {
307         return getCacheInfo().getCacheDirectory();
308     }
309
310     /**
311      * Returns an <code>ImageInputStream</code> that will take its
312      * input from the given <code>Object</code>.  The set of
313      * <code>ImageInputStreamSpi</code>s registered with the
314      * <code>IIORegistry</code> class is queried and the first one
315      * that is able to take input from the supplied object is used to
316      * create the returned <code>ImageInputStream</code>.  If no
317      * suitable <code>ImageInputStreamSpi</code> exists,
318      * <code>null</code> is returned.
319      *
320      * <p> The current cache settings from <code>getUseCache</code>and
321      * <code>getCacheDirectory</code> will be used to control caching.
322      *
323      * @param input an <code>Object</code> to be used as an input
324      * source, such as a <code>File</code>, readable
325      * <code>RandomAccessFile</code>, or <code>InputStream</code>.
326      *
327      * @return an <code>ImageInputStream</code>, or <code>null</code>.
328      *
329      * @exception IllegalArgumentException if <code>input</code>
330      * is <code>null</code>.
331      * @exception IOException if a cache file is needed but cannot be
332      * created.
333      *
334      * @see javax.imageio.spi.ImageInputStreamSpi
335      */

336     public static ImageInputStream createImageInputStream(Object input)
337         throws IOException {
338         if (input == null) {
339             throw new IllegalArgumentException("input == null!");
340         }
341
342         Iterator iter;
343         // Ensure category is present
344         try {
345             iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class,
346                                                    true);
347         } catch (IllegalArgumentException e) {
348             return null;
349         }
350
351         boolean usecache = getUseCache() && hasCachePermission();
352
353         while (iter.hasNext()) {
354             ImageInputStreamSpi spi = (ImageInputStreamSpi)iter.next();
355             if (spi.getInputClass().isInstance(input)) {
356                 try {
357                     return spi.createInputStreamInstance(input,
358                                                          usecache,
359                                                          getCacheDirectory());
360                 } catch (IOException e) {
361                     throw new IIOException("Can't create cache file!", e);
362                 }
363             }
364         }
365
366         return null;
367     }
368
369     // ImageOutputStreams
370
371     /**
372      * Returns an <code>ImageOutputStream</code> that will send its
373      * output to the given <code>Object</code>.  The set of
374      * <code>ImageOutputStreamSpi</code>s registered with the
375      * <code>IIORegistry</code> class is queried and the first one
376      * that is able to send output from the supplied object is used to
377      * create the returned <code>ImageOutputStream</code>.  If no
378      * suitable <code>ImageOutputStreamSpi</code> exists,
379      * <code>null</code> is returned.
380      *
381      * <p> The current cache settings from <code>getUseCache</code>and
382      * <code>getCacheDirectory</code> will be used to control caching.
383      *
384      * @param output an <code>Object</code> to be used as an output
385      * destination, such as a <code>File</code>, writable
386      * <code>RandomAccessFile</code>, or <code>OutputStream</code>.
387      *
388      * @return an <code>ImageOutputStream</code>, or
389      * <code>null</code>.
390      *
391      * @exception IllegalArgumentException if <code>output</code> is
392      * <code>null</code>.
393      * @exception IOException if a cache file is needed but cannot be
394      * created.
395      *
396      * @see javax.imageio.spi.ImageOutputStreamSpi
397      */

398     public static ImageOutputStream createImageOutputStream(Object output)
399         throws IOException {
400         if (output == null) {
401             throw new IllegalArgumentException("output == null!");
402         }
403
404         Iterator iter;
405         // Ensure category is present
406         try {
407             iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,
408                                                    true);
409         } catch (IllegalArgumentException e) {
410             return null;
411         }
412
413         boolean usecache = getUseCache() && hasCachePermission();
414
415         while (iter.hasNext()) {
416             ImageOutputStreamSpi spi = (ImageOutputStreamSpi)iter.next();
417             if (spi.getOutputClass().isInstance(output)) {
418                 try {
419                     return spi.createOutputStreamInstance(output,
420                                                           usecache,
421                                                           getCacheDirectory());
422                 } catch (IOException e) {
423                     throw new IIOException("Can't create cache file!", e);
424                 }
425             }
426         }
427
428         return null;
429     }
430
431     private static enum SpiInfo {
432         FORMAT_NAMES {
433             @Override
434             String[] info(ImageReaderWriterSpi spi) {
435                 return spi.getFormatNames();
436             }
437         },
438         MIME_TYPES {
439             @Override
440             String[] info(ImageReaderWriterSpi spi) {
441                 return spi.getMIMETypes();
442             }
443         },
444         FILE_SUFFIXES {
445             @Override
446             String[] info(ImageReaderWriterSpi spi) {
447                 return spi.getFileSuffixes();
448             }
449         };
450
451         abstract String[] info(ImageReaderWriterSpi spi);
452     }
453
454     private static <S extends ImageReaderWriterSpi>
455         String[] getReaderWriterInfo(Class<S> spiClass, SpiInfo spiInfo)
456     {
457         // Ensure category is present
458         Iterator<S> iter;
459         try {
460             iter = theRegistry.getServiceProviders(spiClass, true);
461         } catch (IllegalArgumentException e) {
462             return new String[0];
463         }
464
465         HashSet<String> s = new HashSet<String>();
466         while (iter.hasNext()) {
467             ImageReaderWriterSpi spi = iter.next();
468             Collections.addAll(s, spiInfo.info(spi));
469         }
470
471         return s.toArray(new String[s.size()]);
472     }
473
474     // Readers
475
476     /**
477      * Returns an array of <code>String</code>s listing all of the
478      * informal format names understood by the current set of registered
479      * readers.
480      *
481      * @return an array of <code>String</code>s.
482      */

483     public static String[] getReaderFormatNames() {
484         return getReaderWriterInfo(ImageReaderSpi.class,
485                                    SpiInfo.FORMAT_NAMES);
486     }
487
488     /**
489      * Returns an array of <code>String</code>s listing all of the
490      * MIME types understood by the current set of registered
491      * readers.
492      *
493      * @return an array of <code>String</code>s.
494      */

495     public static String[] getReaderMIMETypes() {
496         return getReaderWriterInfo(ImageReaderSpi.class,
497                                    SpiInfo.MIME_TYPES);
498     }
499
500     /**
501      * Returns an array of <code>String</code>s listing all of the
502      * file suffixes associated with the formats understood
503      * by the current set of registered readers.
504      *
505      * @return an array of <code>String</code>s.
506      * @since 1.6
507      */

508     public static String[] getReaderFileSuffixes() {
509         return getReaderWriterInfo(ImageReaderSpi.class,
510                                    SpiInfo.FILE_SUFFIXES);
511     }
512
513     static class ImageReaderIterator implements Iterator<ImageReader> {
514         // Contains ImageReaderSpis
515         public Iterator iter;
516
517         public ImageReaderIterator(Iterator iter) {
518             this.iter = iter;
519         }
520
521         public boolean hasNext() {
522             return iter.hasNext();
523         }
524
525         public ImageReader next() {
526             ImageReaderSpi spi = null;
527             try {
528                 spi = (ImageReaderSpi)iter.next();
529                 return spi.createReaderInstance();
530             } catch (IOException e) {
531                 // Deregister the spi in this case, but only as
532                 // an ImageReaderSpi
533                 theRegistry.deregisterServiceProvider(spi, ImageReaderSpi.class);
534             }
535             return null;
536         }
537
538         public void remove() {
539             throw new UnsupportedOperationException();
540         }
541     }
542
543     static class CanDecodeInputFilter
544         implements ServiceRegistry.Filter {
545
546         Object input;
547
548         public CanDecodeInputFilter(Object input) {
549             this.input = input;
550         }
551
552         public boolean filter(Object elt) {
553             try {
554                 ImageReaderSpi spi = (ImageReaderSpi)elt;
555                 ImageInputStream stream = null;
556                 if (input instanceof ImageInputStream) {
557                     stream = (ImageInputStream)input;
558                 }
559
560                 // Perform mark/reset as a defensive measure
561                 // even though plug-ins are supposed to take
562                 // care of it.
563                 boolean canDecode = false;
564                 if (stream != null) {
565                     stream.mark();
566                 }
567                 canDecode = spi.canDecodeInput(input);
568                 if (stream != null) {
569                     stream.reset();
570                 }
571
572                 return canDecode;
573             } catch (IOException e) {
574                 return false;
575             }
576         }
577     }
578
579     static class CanEncodeImageAndFormatFilter
580         implements ServiceRegistry.Filter {
581
582         ImageTypeSpecifier type;
583         String formatName;
584
585         public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type,
586                                              String formatName) {
587             this.type = type;
588             this.formatName = formatName;
589         }
590
591         public boolean filter(Object elt) {
592             ImageWriterSpi spi = (ImageWriterSpi)elt;
593             return Arrays.asList(spi.getFormatNames()).contains(formatName) &&
594                 spi.canEncodeImage(type);
595         }
596     }
597
598     static class ContainsFilter
599         implements ServiceRegistry.Filter {
600
601         Method method;
602         String name;
603
604         // method returns an array of Strings
605         public ContainsFilter(Method method,
606                               String name) {
607             this.method = method;
608             this.name = name;
609         }
610
611         public boolean filter(Object elt) {
612             try {
613                 return contains((String[])method.invoke(elt), name);
614             } catch (Exception e) {
615                 return false;
616             }
617         }
618     }
619
620     /**
621      * Returns an <code>Iterator</code> containing all currently
622      * registered <code>ImageReader</code>s that claim to be able to
623      * decode the supplied <code>Object</code>, typically an
624      * <code>ImageInputStream</code>.
625      *
626      * <p> The stream position is left at its prior position upon
627      * exit from this method.
628      *
629      * @param input an <code>ImageInputStream</code> or other
630      * <code>Object</code> containing encoded image data.
631      *
632      * @return an <code>Iterator</code> containing <code>ImageReader</code>s.
633      *
634      * @exception IllegalArgumentException if <code>input</code> is
635      * <code>null</code>.
636      *
637      * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
638      */

639     public static Iterator<ImageReader> getImageReaders(Object input) {
640         if (input == null) {
641             throw new IllegalArgumentException("input == null!");
642         }
643         Iterator iter;
644         // Ensure category is present
645         try {
646             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
647                                               new CanDecodeInputFilter(input),
648                                               true);
649         } catch (IllegalArgumentException e) {
650             return Collections.emptyIterator();
651         }
652
653         return new ImageReaderIterator(iter);
654     }
655
656     private static Method readerFormatNamesMethod;
657     private static Method readerFileSuffixesMethod;
658     private static Method readerMIMETypesMethod;
659     private static Method writerFormatNamesMethod;
660     private static Method writerFileSuffixesMethod;
661     private static Method writerMIMETypesMethod;
662
663     static {
664         try {
665             readerFormatNamesMethod =
666                 ImageReaderSpi.class.getMethod("getFormatNames");
667             readerFileSuffixesMethod =
668                 ImageReaderSpi.class.getMethod("getFileSuffixes");
669             readerMIMETypesMethod =
670                 ImageReaderSpi.class.getMethod("getMIMETypes");
671
672             writerFormatNamesMethod =
673                 ImageWriterSpi.class.getMethod("getFormatNames");
674             writerFileSuffixesMethod =
675                 ImageWriterSpi.class.getMethod("getFileSuffixes");
676             writerMIMETypesMethod =
677                 ImageWriterSpi.class.getMethod("getMIMETypes");
678         } catch (NoSuchMethodException e) {
679             e.printStackTrace();
680         }
681     }
682
683     /**
684      * Returns an <code>Iterator</code> containing all currently
685      * registered <code>ImageReader</code>s that claim to be able to
686      * decode the named format.
687      *
688      * @param formatName a <code>String</code> containing the informal
689      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
690      *
691      * @return an <code>Iterator</code> containing
692      * <code>ImageReader</code>s.
693      *
694      * @exception IllegalArgumentException if <code>formatName</code>
695      * is <code>null</code>.
696      *
697      * @see javax.imageio.spi.ImageReaderSpi#getFormatNames
698      */

699     public static Iterator<ImageReader>
700         getImageReadersByFormatName(String formatName)
701     {
702         if (formatName == null) {
703             throw new IllegalArgumentException("formatName == null!");
704         }
705         Iterator iter;
706         // Ensure category is present
707         try {
708             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
709                                     new ContainsFilter(readerFormatNamesMethod,
710                                                        formatName),
711                                                 true);
712         } catch (IllegalArgumentException e) {
713             return Collections.emptyIterator();
714         }
715         return new ImageReaderIterator(iter);
716     }
717
718     /**
719      * Returns an <code>Iterator</code> containing all currently
720      * registered <code>ImageReader</code>s that claim to be able to
721      * decode files with the given suffix.
722      *
723      * @param fileSuffix a <code>String</code> containing a file
724      * suffix (<i>e.g.</i>, "jpg" or "tiff").
725      *
726      * @return an <code>Iterator</code> containing
727      * <code>ImageReader</code>s.
728      *
729      * @exception IllegalArgumentException if <code>fileSuffix</code>
730      * is <code>null</code>.
731      *
732      * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
733      */

734     public static Iterator<ImageReader>
735         getImageReadersBySuffix(String fileSuffix)
736     {
737         if (fileSuffix == null) {
738             throw new IllegalArgumentException("fileSuffix == null!");
739         }
740         // Ensure category is present
741         Iterator iter;
742         try {
743             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
744                                    new ContainsFilter(readerFileSuffixesMethod,
745                                                       fileSuffix),
746                                               true);
747         } catch (IllegalArgumentException e) {
748             return Collections.emptyIterator();
749         }
750         return new ImageReaderIterator(iter);
751     }
752
753     /**
754      * Returns an <code>Iterator</code> containing all currently
755      * registered <code>ImageReader</code>s that claim to be able to
756      * decode files with the given MIME type.
757      *
758      * @param MIMEType a <code>String</code> containing a file
759      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
760      *
761      * @return an <code>Iterator</code> containing
762      * <code>ImageReader</code>s.
763      *
764      * @exception IllegalArgumentException if <code>MIMEType</code> is
765      * <code>null</code>.
766      *
767      * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
768      */

769     public static Iterator<ImageReader>
770         getImageReadersByMIMEType(String MIMEType)
771     {
772         if (MIMEType == null) {
773             throw new IllegalArgumentException("MIMEType == null!");
774         }
775         // Ensure category is present
776         Iterator iter;
777         try {
778             iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
779                                       new ContainsFilter(readerMIMETypesMethod,
780                                                          MIMEType),
781                                               true);
782         } catch (IllegalArgumentException e) {
783             return Collections.emptyIterator();
784         }
785         return new ImageReaderIterator(iter);
786     }
787
788     // Writers
789
790     /**
791      * Returns an array of <code>String</code>s listing all of the
792      * informal format names understood by the current set of registered
793      * writers.
794      *
795      * @return an array of <code>String</code>s.
796      */

797     public static String[] getWriterFormatNames() {
798         return getReaderWriterInfo(ImageWriterSpi.class,
799                                    SpiInfo.FORMAT_NAMES);
800     }
801
802     /**
803      * Returns an array of <code>String</code>s listing all of the
804      * MIME types understood by the current set of registered
805      * writers.
806      *
807      * @return an array of <code>String</code>s.
808      */

809     public static String[] getWriterMIMETypes() {
810         return getReaderWriterInfo(ImageWriterSpi.class,
811                                    SpiInfo.MIME_TYPES);
812     }
813
814     /**
815      * Returns an array of <code>String</code>s listing all of the
816      * file suffixes associated with the formats understood
817      * by the current set of registered writers.
818      *
819      * @return an array of <code>String</code>s.
820      * @since 1.6
821      */

822     public static String[] getWriterFileSuffixes() {
823         return getReaderWriterInfo(ImageWriterSpi.class,
824                                    SpiInfo.FILE_SUFFIXES);
825     }
826
827     static class ImageWriterIterator implements Iterator<ImageWriter> {
828         // Contains ImageWriterSpis
829         public Iterator iter;
830
831         public ImageWriterIterator(Iterator iter) {
832             this.iter = iter;
833         }
834
835         public boolean hasNext() {
836             return iter.hasNext();
837         }
838
839         public ImageWriter next() {
840             ImageWriterSpi spi = null;
841             try {
842                 spi = (ImageWriterSpi)iter.next();
843                 return spi.createWriterInstance();
844             } catch (IOException e) {
845                 // Deregister the spi in this case, but only as a writerSpi
846                 theRegistry.deregisterServiceProvider(spi, ImageWriterSpi.class);
847             }
848             return null;
849         }
850
851         public void remove() {
852             throw new UnsupportedOperationException();
853         }
854     }
855
856     private static boolean contains(String[] names, String name) {
857         for (int i = 0; i < names.length; i++) {
858             if (name.equalsIgnoreCase(names[i])) {
859                 return true;
860             }
861         }
862
863         return false;
864     }
865
866     /**
867      * Returns an <code>Iterator</code> containing all currently
868      * registered <code>ImageWriter</code>s that claim to be able to
869      * encode the named format.
870      *
871      * @param formatName a <code>String</code> containing the informal
872      * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
873      *
874      * @return an <code>Iterator</code> containing
875      * <code>ImageWriter</code>s.
876      *
877      * @exception IllegalArgumentException if <code>formatName</code> is
878      * <code>null</code>.
879      *
880      * @see javax.imageio.spi.ImageWriterSpi#getFormatNames
881      */

882     public static Iterator<ImageWriter>
883         getImageWritersByFormatName(String formatName)
884     {
885         if (formatName == null) {
886             throw new IllegalArgumentException("formatName == null!");
887         }
888         Iterator iter;
889         // Ensure category is present
890         try {
891             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
892                                     new ContainsFilter(writerFormatNamesMethod,
893                                                        formatName),
894                                             true);
895         } catch (IllegalArgumentException e) {
896             return Collections.emptyIterator();
897         }
898         return new ImageWriterIterator(iter);
899     }
900
901     /**
902      * Returns an <code>Iterator</code> containing all currently
903      * registered <code>ImageWriter</code>s that claim to be able to
904      * encode files with the given suffix.
905      *
906      * @param fileSuffix a <code>String</code> containing a file
907      * suffix (<i>e.g.</i>, "jpg" or "tiff").
908      *
909      * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
910      *
911      * @exception IllegalArgumentException if <code>fileSuffix</code> is
912      * <code>null</code>.
913      *
914      * @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes
915      */

916     public static Iterator<ImageWriter>
917         getImageWritersBySuffix(String fileSuffix)
918     {
919         if (fileSuffix == null) {
920             throw new IllegalArgumentException("fileSuffix == null!");
921         }
922         Iterator iter;
923         // Ensure category is present
924         try {
925             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
926                                    new ContainsFilter(writerFileSuffixesMethod,
927                                                       fileSuffix),
928                                             true);
929         } catch (IllegalArgumentException e) {
930             return Collections.emptyIterator();
931         }
932         return new ImageWriterIterator(iter);
933     }
934
935     /**
936      * Returns an <code>Iterator</code> containing all currently
937      * registered <code>ImageWriter</code>s that claim to be able to
938      * encode files with the given MIME type.
939      *
940      * @param MIMEType a <code>String</code> containing a file
941      * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
942      *
943      * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
944      *
945      * @exception IllegalArgumentException if <code>MIMEType</code> is
946      * <code>null</code>.
947      *
948      * @see javax.imageio.spi.ImageWriterSpi#getMIMETypes
949      */

950     public static Iterator<ImageWriter>
951         getImageWritersByMIMEType(String MIMEType)
952     {
953         if (MIMEType == null) {
954             throw new IllegalArgumentException("MIMEType == null!");
955         }
956         Iterator iter;
957         // Ensure category is present
958         try {
959             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
960                                       new ContainsFilter(writerMIMETypesMethod,
961                                                          MIMEType),
962                                             true);
963         } catch (IllegalArgumentException e) {
964             return Collections.emptyIterator();
965         }
966         return new ImageWriterIterator(iter);
967     }
968
969     /**
970      * Returns an <code>ImageWriter</code>corresponding to the given
971      * <code>ImageReader</code>, if there is one, or <code>null</code>
972      * if the plug-in for this <code>ImageReader</code> does not
973      * specify a corresponding <code>ImageWriter</code>, or if the
974      * given <code>ImageReader</code> is not registered.  This
975      * mechanism may be used to obtain an <code>ImageWriter</code>
976      * that will understand the internal structure of non-pixel
977      * metadata (as encoded by <code>IIOMetadata</code> objects)
978      * generated by the <code>ImageReader</code>.  By obtaining this
979      * data from the <code>ImageReader</code> and passing it on to the
980      * <code>ImageWriter</code> obtained with this method, a client
981      * program can read an image, modify it in some way, and write it
982      * back out preserving all metadata, without having to understand
983      * anything about the structure of the metadata, or even about
984      * the image format.  Note that this method returns the
985      * "preferred" writer, which is the first in the list returned by
986      * <code>javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()</code>.
987      *
988      * @param reader an instance of a registered <code>ImageReader</code>.
989      *
990      * @return an <code>ImageWriter</code>, or null.
991      *
992      * @exception IllegalArgumentException if <code>reader</code> is
993      * <code>null</code>.
994      *
995      * @see #getImageReader(ImageWriter)
996      * @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()
997      */

998     public static ImageWriter getImageWriter(ImageReader reader) {
999         if (reader == null) {
1000             throw new IllegalArgumentException("reader == null!");
1001         }
1002
1003         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1004         if (readerSpi == null) {
1005             Iterator readerSpiIter;
1006             // Ensure category is present
1007             try {
1008                 readerSpiIter =
1009                     theRegistry.getServiceProviders(ImageReaderSpi.class,
1010                                                     false);
1011             } catch (IllegalArgumentException e) {
1012                 return null;
1013             }
1014
1015             while (readerSpiIter.hasNext()) {
1016                 ImageReaderSpi temp = (ImageReaderSpi) readerSpiIter.next();
1017                 if (temp.isOwnReader(reader)) {
1018                     readerSpi = temp;
1019                     break;
1020                 }
1021             }
1022             if (readerSpi == null) {
1023                 return null;
1024             }
1025         }
1026
1027         String[] writerNames = readerSpi.getImageWriterSpiNames();
1028         if (writerNames == null) {
1029             return null;
1030         }
1031
1032         Class writerSpiClass = null;
1033         try {
1034             writerSpiClass = Class.forName(writerNames[0], true,
1035                                            ClassLoader.getSystemClassLoader());
1036         } catch (ClassNotFoundException e) {
1037             return null;
1038         }
1039
1040         ImageWriterSpi writerSpi = (ImageWriterSpi)
1041             theRegistry.getServiceProviderByClass(writerSpiClass);
1042         if (writerSpi == null) {
1043             return null;
1044         }
1045
1046         try {
1047             return writerSpi.createWriterInstance();
1048         } catch (IOException e) {
1049             // Deregister the spi in this case, but only as a writerSpi
1050             theRegistry.deregisterServiceProvider(writerSpi,
1051                                                   ImageWriterSpi.class);
1052             return null;
1053         }
1054     }
1055
1056     /**
1057      * Returns an <code>ImageReader</code>corresponding to the given
1058      * <code>ImageWriter</code>, if there is one, or <code>null</code>
1059      * if the plug-in for this <code>ImageWriter</code> does not
1060      * specify a corresponding <code>ImageReader</code>, or if the
1061      * given <code>ImageWriter</code> is not registered.  This method
1062      * is provided principally for symmetry with
1063      * <code>getImageWriter(ImageReader)</code>.  Note that this
1064      * method returns the "preferred" reader, which is the first in
1065      * the list returned by
1066      * javax.imageio.spi.ImageWriterSpi.<code>getImageReaderSpiNames()</code>.
1067      *
1068      * @param writer an instance of a registered <code>ImageWriter</code>.
1069      *
1070      * @return an <code>ImageReader</code>, or null.
1071      *
1072      * @exception IllegalArgumentException if <code>writer</code> is
1073      * <code>null</code>.
1074      *
1075      * @see #getImageWriter(ImageReader)
1076      * @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()
1077      */

1078     public static ImageReader getImageReader(ImageWriter writer) {
1079         if (writer == null) {
1080             throw new IllegalArgumentException("writer == null!");
1081         }
1082
1083         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1084         if (writerSpi == null) {
1085             Iterator writerSpiIter;
1086             // Ensure category is present
1087             try {
1088                 writerSpiIter =
1089                     theRegistry.getServiceProviders(ImageWriterSpi.class,
1090                                                     false);
1091             } catch (IllegalArgumentException e) {
1092                 return null;
1093             }
1094
1095             while (writerSpiIter.hasNext()) {
1096                 ImageWriterSpi temp = (ImageWriterSpi) writerSpiIter.next();
1097                 if (temp.isOwnWriter(writer)) {
1098                     writerSpi = temp;
1099                     break;
1100                 }
1101             }
1102             if (writerSpi == null) {
1103                 return null;
1104             }
1105         }
1106
1107         String[] readerNames = writerSpi.getImageReaderSpiNames();
1108         if (readerNames == null) {
1109             return null;
1110         }
1111
1112         Class readerSpiClass = null;
1113         try {
1114             readerSpiClass = Class.forName(readerNames[0], true,
1115                                            ClassLoader.getSystemClassLoader());
1116         } catch (ClassNotFoundException e) {
1117             return null;
1118         }
1119
1120         ImageReaderSpi readerSpi = (ImageReaderSpi)
1121             theRegistry.getServiceProviderByClass(readerSpiClass);
1122         if (readerSpi == null) {
1123             return null;
1124         }
1125
1126         try {
1127             return readerSpi.createReaderInstance();
1128         } catch (IOException e) {
1129             // Deregister the spi in this case, but only as a readerSpi
1130             theRegistry.deregisterServiceProvider(readerSpi,
1131                                                   ImageReaderSpi.class);
1132             return null;
1133         }
1134     }
1135
1136     /**
1137      * Returns an <code>Iterator</code> containing all currently
1138      * registered <code>ImageWriter</code>s that claim to be able to
1139      * encode images of the given layout (specified using an
1140      * <code>ImageTypeSpecifier</code>) in the given format.
1141      *
1142      * @param type an <code>ImageTypeSpecifier</code> indicating the
1143      * layout of the image to be written.
1144      * @param formatName the informal name of the <code>format</code>.
1145      *
1146      * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
1147      *
1148      * @exception IllegalArgumentException if any parameter is
1149      * <code>null</code>.
1150      *
1151      * @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)
1152      */

1153     public static Iterator<ImageWriter>
1154         getImageWriters(ImageTypeSpecifier type, String formatName)
1155     {
1156         if (type == null) {
1157             throw new IllegalArgumentException("type == null!");
1158         }
1159         if (formatName == null) {
1160             throw new IllegalArgumentException("formatName == null!");
1161         }
1162
1163         Iterator iter;
1164         // Ensure category is present
1165         try {
1166             iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
1167                                  new CanEncodeImageAndFormatFilter(type,
1168                                                                    formatName),
1169                                             true);
1170         } catch (IllegalArgumentException e) {
1171             return Collections.emptyIterator();
1172         }
1173
1174         return new ImageWriterIterator(iter);
1175     }
1176
1177     static class ImageTranscoderIterator
1178         implements Iterator<ImageTranscoder>
1179     {
1180         // Contains ImageTranscoderSpis
1181         public Iterator iter;
1182
1183         public ImageTranscoderIterator(Iterator iter) {
1184             this.iter = iter;
1185         }
1186
1187         public boolean hasNext() {
1188             return iter.hasNext();
1189         }
1190
1191         public ImageTranscoder next() {
1192             ImageTranscoderSpi spi = null;
1193             spi = (ImageTranscoderSpi)iter.next();
1194             return spi.createTranscoderInstance();
1195         }
1196
1197         public void remove() {
1198             throw new UnsupportedOperationException();
1199         }
1200     }
1201
1202     static class TranscoderFilter
1203         implements ServiceRegistry.Filter {
1204
1205         String readerSpiName;
1206         String writerSpiName;
1207
1208         public TranscoderFilter(ImageReaderSpi readerSpi,
1209                                 ImageWriterSpi writerSpi) {
1210             this.readerSpiName = readerSpi.getClass().getName();
1211             this.writerSpiName = writerSpi.getClass().getName();
1212         }
1213
1214         public boolean filter(Object elt) {
1215             ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;
1216             String readerName = spi.getReaderServiceProviderName();
1217             String writerName = spi.getWriterServiceProviderName();
1218             return (readerName.equals(readerSpiName) &&
1219                     writerName.equals(writerSpiName));
1220         }
1221     }
1222
1223     /**
1224      * Returns an <code>Iterator</code> containing all currently
1225      * registered <code>ImageTranscoder</code>s that claim to be
1226      * able to transcode between the metadata of the given
1227      * <code>ImageReader</code> and <code>ImageWriter</code>.
1228      *
1229      * @param reader an <code>ImageReader</code>.
1230      * @param writer an <code>ImageWriter</code>.
1231      *
1232      * @return an <code>Iterator</code> containing
1233      * <code>ImageTranscoder</code>s.
1234      *
1235      * @exception IllegalArgumentException if <code>reader</code> or
1236      * <code>writer</code> is <code>null</code>.
1237      */

1238     public static Iterator<ImageTranscoder>
1239         getImageTranscoders(ImageReader reader, ImageWriter writer)
1240     {
1241         if (reader == null) {
1242             throw new IllegalArgumentException("reader == null!");
1243         }
1244         if (writer == null) {
1245             throw new IllegalArgumentException("writer == null!");
1246         }
1247         ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1248         ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1249         ServiceRegistry.Filter filter =
1250             new TranscoderFilter(readerSpi, writerSpi);
1251
1252         Iterator iter;
1253         // Ensure category is present
1254         try {
1255             iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,
1256                                             filter, true);
1257         } catch (IllegalArgumentException e) {
1258             return Collections.emptyIterator();
1259         }
1260         return new ImageTranscoderIterator(iter);
1261     }
1262
1263     // All-in-one methods
1264
1265     /**
1266      * Returns a <code>BufferedImage</code> as the result of decoding
1267      * a supplied <code>File</code> with an <code>ImageReader</code>
1268      * chosen automatically from among those currently registered.
1269      * The <code>File</code> is wrapped in an
1270      * <code>ImageInputStream</code>.  If no registered
1271      * <code>ImageReader</code> claims to be able to read the
1272      * resulting stream, <code>null</code> is returned.
1273      *
1274      * <p> The current cache settings from <code>getUseCache</code>and
1275      * <code>getCacheDirectory</code> will be used to control caching in the
1276      * <code>ImageInputStream</code> that is created.
1277      *
1278      * <p> Note that there is no <code>read</code> method that takes a
1279      * filename as a <code>String</code>; use this method instead after
1280      * creating a <code>File</code> from the filename.
1281      *
1282      * <p> This method does not attempt to locate
1283      * <code>ImageReader</code>s that can read directly from a
1284      * <code>File</code>; that may be accomplished using
1285      * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1286      *
1287      * @param input a <code>File</code> to read from.
1288      *
1289      * @return a <code>BufferedImage</code> containing the decoded
1290      * contents of the input, or <code>null</code>.
1291      *
1292      * @exception IllegalArgumentException if <code>input</code> is
1293      * <code>null</code>.
1294      * @exception IOException if an error occurs during reading.
1295      */

1296     public static BufferedImage read(File input) throws IOException {
1297         if (input == null) {
1298             throw new IllegalArgumentException("input == null!");
1299         }
1300         if (!input.canRead()) {
1301             throw new IIOException("Can't read input file!");
1302         }
1303
1304         ImageInputStream stream = createImageInputStream(input);
1305         if (stream == null) {
1306             throw new IIOException("Can't create an ImageInputStream!");
1307         }
1308         BufferedImage bi = read(stream);
1309         if (bi == null) {
1310             stream.close();
1311         }
1312         return bi;
1313     }
1314
1315     /**
1316      * Returns a <code>BufferedImage</code> as the result of decoding
1317      * a supplied <code>InputStream</code> with an <code>ImageReader</code>
1318      * chosen automatically from among those currently registered.
1319      * The <code>InputStream</code> is wrapped in an
1320      * <code>ImageInputStream</code>.  If no registered
1321      * <code>ImageReader</code> claims to be able to read the
1322      * resulting stream, <code>null</code> is returned.
1323      *
1324      * <p> The current cache settings from <code>getUseCache</code>and
1325      * <code>getCacheDirectory</code> will be used to control caching in the
1326      * <code>ImageInputStream</code> that is created.
1327      *
1328      * <p> This method does not attempt to locate
1329      * <code>ImageReader</code>s that can read directly from an
1330      * <code>InputStream</code>; that may be accomplished using
1331      * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1332      *
1333      * <p> This method <em>does not</em> close the provided
1334      * <code>InputStream</code> after the read operation has completed;
1335      * it is the responsibility of the caller to close the stream, if desired.
1336      *
1337      * @param input an <code>InputStream</code> to read from.
1338      *
1339      * @return a <code>BufferedImage</code> containing the decoded
1340      * contents of the input, or <code>null</code>.
1341      *
1342      * @exception IllegalArgumentException if <code>input</code> is
1343      * <code>null</code>.
1344      * @exception IOException if an error occurs during reading.
1345      */

1346     public static BufferedImage read(InputStream input) throws IOException {
1347         if (input == null) {
1348             throw new IllegalArgumentException("input == null!");
1349         }
1350
1351         ImageInputStream stream = createImageInputStream(input);
1352         BufferedImage bi = read(stream);
1353         if (bi == null) {
1354             stream.close();
1355         }
1356         return bi;
1357     }
1358
1359     /**
1360      * Returns a <code>BufferedImage</code> as the result of decoding
1361      * a supplied <code>URL</code> with an <code>ImageReader</code>
1362      * chosen automatically from among those currently registered.  An
1363      * <code>InputStream</code> is obtained from the <code>URL</code>,
1364      * which is wrapped in an <code>ImageInputStream</code>.  If no
1365      * registered <code>ImageReader</code> claims to be able to read
1366      * the resulting stream, <code>null</code> is returned.
1367      *
1368      * <p> The current cache settings from <code>getUseCache</code>and
1369      * <code>getCacheDirectory</code> will be used to control caching in the
1370      * <code>ImageInputStream</code> that is created.
1371      *
1372      * <p> This method does not attempt to locate
1373      * <code>ImageReader</code>s that can read directly from a
1374      * <code>URL</code>; that may be accomplished using
1375      * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1376      *
1377      * @param input a <code>URL</code> to read from.
1378      *
1379      * @return a <code>BufferedImage</code> containing the decoded
1380      * contents of the input, or <code>null</code>.
1381      *
1382      * @exception IllegalArgumentException if <code>input</code> is
1383      * <code>null</code>.
1384      * @exception IOException if an error occurs during reading.
1385      */

1386     public static BufferedImage read(URL input) throws IOException {
1387         if (input == null) {
1388             throw new IllegalArgumentException("input == null!");
1389         }
1390
1391         InputStream istream = null;
1392         try {
1393             istream = input.openStream();
1394         } catch (IOException e) {
1395             throw new IIOException("Can't get input stream from URL!", e);
1396         }
1397         ImageInputStream stream = createImageInputStream(istream);
1398         BufferedImage bi;
1399         try {
1400             bi = read(stream);
1401             if (bi == null) {
1402                 stream.close();
1403             }
1404         } finally {
1405             istream.close();
1406         }
1407         return bi;
1408     }
1409
1410     /**
1411      * Returns a <code>BufferedImage</code> as the result of decoding
1412      * a supplied <code>ImageInputStream</code> with an
1413      * <code>ImageReader</code> chosen automatically from among those
1414      * currently registered.  If no registered
1415      * <code>ImageReader</code> claims to be able to read the stream,
1416      * <code>null</code> is returned.
1417      *
1418      * <p> Unlike most other methods in this classthis method <em>does</em>
1419      * close the provided <code>ImageInputStream</code> after the read
1420      * operation has completed, unless <code>null</code> is returned,
1421      * in which case this method <em>does not</em> close the stream.
1422      *
1423      * @param stream an <code>ImageInputStream</code> to read from.
1424      *
1425      * @return a <code>BufferedImage</code> containing the decoded
1426      * contents of the input, or <code>null</code>.
1427      *
1428      * @exception IllegalArgumentException if <code>stream</code> is
1429      * <code>null</code>.
1430      * @exception IOException if an error occurs during reading.
1431      */

1432     public static BufferedImage read(ImageInputStream stream)
1433         throws IOException {
1434         if (stream == null) {
1435             throw new IllegalArgumentException("stream == null!");
1436         }
1437
1438         Iterator iter = getImageReaders(stream);
1439         if (!iter.hasNext()) {
1440             return null;
1441         }
1442
1443         ImageReader reader = (ImageReader)iter.next();
1444         ImageReadParam param = reader.getDefaultReadParam();
1445         reader.setInput(stream, truetrue);
1446         BufferedImage bi;
1447         try {
1448             bi = reader.read(0, param);
1449         } finally {
1450             reader.dispose();
1451             stream.close();
1452         }
1453         return bi;
1454     }
1455
1456     /**
1457      * Writes an image using the an arbitrary <code>ImageWriter</code>
1458      * that supports the given format to an
1459      * <code>ImageOutputStream</code>.  The image is written to the
1460      * <code>ImageOutputStream</code> starting at the current stream
1461      * pointer, overwriting existing stream data from that point
1462      * forward, if present.
1463      *
1464      * <p> This method <em>does not</em> close the provided
1465      * <code>ImageOutputStream</code> after the write operation has completed;
1466      * it is the responsibility of the caller to close the stream, if desired.
1467      *
1468      * @param im a <code>RenderedImage</code> to be written.
1469      * @param formatName a <code>String</code> containing the informal
1470      * name of the format.
1471      * @param output an <code>ImageOutputStream</code> to be written to.
1472      *
1473      * @return <code>false</code> if no appropriate writer is found.
1474      *
1475      * @exception IllegalArgumentException if any parameter is
1476      * <code>null</code>.
1477      * @exception IOException if an error occurs during writing.
1478      */

1479     public static boolean write(RenderedImage im,
1480                                 String formatName,
1481                                 ImageOutputStream output) throws IOException {
1482         if (im == null) {
1483             throw new IllegalArgumentException("im == null!");
1484         }
1485         if (formatName == null) {
1486             throw new IllegalArgumentException("formatName == null!");
1487         }
1488         if (output == null) {
1489             throw new IllegalArgumentException("output == null!");
1490         }
1491
1492         return doWrite(im, getWriter(im, formatName), output);
1493     }
1494
1495     /**
1496      * Writes an image using an arbitrary <code>ImageWriter</code>
1497      * that supports the given format to a <code>File</code>.  If
1498      * there is already a <code>File</code> present, its contents are
1499      * discarded.
1500      *
1501      * @param im a <code>RenderedImage</code> to be written.
1502      * @param formatName a <code>String</code> containing the informal
1503      * name of the format.
1504      * @param output a <code>File</code> to be written to.
1505      *
1506      * @return <code>false</code> if no appropriate writer is found.
1507      *
1508      * @exception IllegalArgumentException if any parameter is
1509      * <code>null</code>.
1510      * @exception IOException if an error occurs during writing.
1511      */

1512     public static boolean write(RenderedImage im,
1513                                 String formatName,
1514                                 File output) throws IOException {
1515         if (output == null) {
1516             throw new IllegalArgumentException("output == null!");
1517         }
1518         ImageOutputStream stream = null;
1519
1520         ImageWriter writer = getWriter(im, formatName);
1521         if (writer == null) {
1522             /* Do not make changes in the file system if we have
1523              * no appropriate writer.
1524              */

1525             return false;
1526         }
1527
1528         try {
1529             output.delete();
1530             stream = createImageOutputStream(output);
1531         } catch (IOException e) {
1532             throw new IIOException("Can't create output stream!", e);
1533         }
1534
1535         try {
1536             return doWrite(im, writer, stream);
1537         } finally {
1538             stream.close();
1539         }
1540     }
1541
1542     /**
1543      * Writes an image using an arbitrary <code>ImageWriter</code>
1544      * that supports the given format to an <code>OutputStream</code>.
1545      *
1546      * <p> This method <em>does not</em> close the provided
1547      * <code>OutputStream</code> after the write operation has completed;
1548      * it is the responsibility of the caller to close the stream, if desired.
1549      *
1550      * <p> The current cache settings from <code>getUseCache</code>and
1551      * <code>getCacheDirectory</code> will be used to control caching.
1552      *
1553      * @param im a <code>RenderedImage</code> to be written.
1554      * @param formatName a <code>String</code> containing the informal
1555      * name of the format.
1556      * @param output an <code>OutputStream</code> to be written to.
1557      *
1558      * @return <code>false</code> if no appropriate writer is found.
1559      *
1560      * @exception IllegalArgumentException if any parameter is
1561      * <code>null</code>.
1562      * @exception IOException if an error occurs during writing.
1563      */

1564     public static boolean write(RenderedImage im,
1565                                 String formatName,
1566                                 OutputStream output) throws IOException {
1567         if (output == null) {
1568             throw new IllegalArgumentException("output == null!");
1569         }
1570         ImageOutputStream stream = null;
1571         try {
1572             stream = createImageOutputStream(output);
1573         } catch (IOException e) {
1574             throw new IIOException("Can't create output stream!", e);
1575         }
1576
1577         try {
1578             return doWrite(im, getWriter(im, formatName), stream);
1579         } finally {
1580             stream.close();
1581         }
1582     }
1583
1584     /**
1585      * Returns <code>ImageWriter</code> instance according to given
1586      * rendered image and image format or <code>null</code> if there
1587      * is no appropriate writer.
1588      */

1589     private static ImageWriter getWriter(RenderedImage im,
1590                                          String formatName) {
1591         ImageTypeSpecifier type =
1592             ImageTypeSpecifier.createFromRenderedImage(im);
1593         Iterator<ImageWriter> iter = getImageWriters(type, formatName);
1594
1595         if (iter.hasNext()) {
1596             return iter.next();
1597         } else {
1598             return null;
1599         }
1600     }
1601
1602     /**
1603      * Writes image to output stream  using given image writer.
1604      */

1605     private static boolean doWrite(RenderedImage im, ImageWriter writer,
1606                                  ImageOutputStream output) throws IOException {
1607         if (writer == null) {
1608             return false;
1609         }
1610         writer.setOutput(output);
1611         try {
1612             writer.write(im);
1613         } finally {
1614             writer.dispose();
1615             output.flush();
1616         }
1617         return true;
1618     }
1619 }
1620
Powered by JavaMelody