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 false, this 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 class, this 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, true, true);
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