blob: 77f2a6b65e88550d3cd969af76b8837d9ca8ac45 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.imageio;
27
28import java.awt.image.BufferedImage;
29import java.awt.image.RenderedImage;
30import java.io.File;
31import java.io.InputStream;
32import java.io.IOException;
33import java.io.OutputStream;
34import java.lang.reflect.Method;
35import java.net.URL;
36import java.security.AccessController;
37import java.util.Arrays;
38import java.util.Collections;
39import java.util.HashSet;
40import java.util.Iterator;
41import java.util.NoSuchElementException;
42import java.util.Set;
43import javax.imageio.spi.IIORegistry;
44import javax.imageio.spi.ImageReaderSpi;
45import javax.imageio.spi.ImageReaderWriterSpi;
46import javax.imageio.spi.ImageWriterSpi;
47import javax.imageio.spi.ImageInputStreamSpi;
48import javax.imageio.spi.ImageOutputStreamSpi;
49import javax.imageio.spi.ImageTranscoderSpi;
50import javax.imageio.spi.ServiceRegistry;
51import javax.imageio.stream.ImageInputStream;
52import javax.imageio.stream.ImageOutputStream;
53import sun.awt.AppContext;
54import sun.security.action.GetPropertyAction;
55
56/**
57 * A class containing static convenience methods for locating
58 * <code>ImageReader</code>s and <code>ImageWriter</code>s, and
59 * performing simple encoding and decoding.
60 *
61 */
62public final class ImageIO {
63
64 private static final IIORegistry theRegistry =
65 IIORegistry.getDefaultInstance();
66
67 /**
68 * Constructor is private to prevent instantiation.
69 */
70 private ImageIO() {}
71
72 /**
73 * Scans for plug-ins on the application class path,
74 * loads their service provider classes, and registers a service
75 * provider instance for each one found with the
76 * <code>IIORegistry</code>.
77 *
78 * <p>This method is needed because the application class path can
79 * theoretically change, or additional plug-ins may become available.
80 * Rather than re-scanning the classpath on every invocation of the
81 * API, the class path is scanned automatically only on the first
82 * invocation. Clients can call this method to prompt a re-scan.
83 * Thus this method need only be invoked by sophisticated applications
84 * which dynamically make new plug-ins available at runtime.
85 *
86 * <p> The <code>getResources</code> method of the context
87 * <code>ClassLoader</code> is used locate JAR files containing
88 * files named
89 * <code>META-INF/services/javax.imageio.spi.</code><i>classname</i>,
90 * where <i>classname</i> is one of <code>ImageReaderSpi</code>,
91 * <code>ImageWriterSpi</code>, <code>ImageTranscoderSpi</code>,
92 * <code>ImageInputStreamSpi</code>, or
93 * <code>ImageOutputStreamSpi</code>, along the application class
94 * path.
95 *
96 * <p> The contents of the located files indicate the names of
97 * actual implementation classes which implement the
98 * aforementioned service provider interfaces; the default class
99 * loader is then used to load each of these classes and to
100 * instantiate an instance of each class, which is then placed
101 * into the registry for later retrieval.
102 *
103 * <p> The exact set of locations searched depends on the
104 * implementation of the Java runtime enviroment.
105 *
106 * @see ClassLoader#getResources
107 */
108 public static void scanForPlugins() {
109 theRegistry.registerApplicationClasspathSpis();
110 }
111
112 // ImageInputStreams
113
114 /**
115 * A class to hold information about caching. Each
116 * <code>ThreadGroup</code> will have its own copy
117 * via the <code>AppContext</code> mechanism.
118 */
119 static class CacheInfo {
120 boolean useCache = true;
121 File cacheDirectory = null;
122 Boolean hasPermission = null;
123
124 public CacheInfo() {}
125
126 public boolean getUseCache() {
127 return useCache;
128 }
129
130 public void setUseCache(boolean useCache) {
131 this.useCache = useCache;
132 }
133
134 public File getCacheDirectory() {
135 return cacheDirectory;
136 }
137
138 public void setCacheDirectory(File cacheDirectory) {
139 this.cacheDirectory = cacheDirectory;
140 }
141
142 public Boolean getHasPermission() {
143 return hasPermission;
144 }
145
146 public void setHasPermission(Boolean hasPermission) {
147 this.hasPermission = hasPermission;
148 }
149 }
150
151 /**
152 * Returns the <code>CacheInfo</code> object associated with this
153 * <code>ThreadGroup</code>.
154 */
155 private static synchronized CacheInfo getCacheInfo() {
156 AppContext context = AppContext.getAppContext();
157 CacheInfo info = (CacheInfo)context.get(CacheInfo.class);
158 if (info == null) {
159 info = new CacheInfo();
160 context.put(CacheInfo.class, info);
161 }
162 return info;
163 }
164
165 /**
166 * Returns the default temporary (cache) directory as defined by the
167 * java.io.tmpdir system property.
168 */
169 private static String getTempDir() {
170 GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
171 return (String)AccessController.doPrivileged(a);
172 }
173
174 /**
175 * Determines whether the caller has write access to the cache
176 * directory, stores the result in the <code>CacheInfo</code> object,
177 * and returns the decision. This method helps to prevent mysterious
178 * SecurityExceptions to be thrown when this convenience class is used
179 * in an applet, for example.
180 */
181 private static boolean hasCachePermission() {
182 Boolean hasPermission = getCacheInfo().getHasPermission();
183
184 if (hasPermission != null) {
185 return hasPermission.booleanValue();
186 } else {
187 try {
188 SecurityManager security = System.getSecurityManager();
189 if (security != null) {
190 File cachedir = getCacheDirectory();
191 String cachepath;
192
193 if (cachedir != null) {
194 cachepath = cachedir.getPath();
195 } else {
196 cachepath = getTempDir();
197
198 if (cachepath == null) {
199 getCacheInfo().setHasPermission(Boolean.FALSE);
200 return false;
201 }
202 }
203
204 security.checkWrite(cachepath);
205 }
206 } catch (SecurityException e) {
207 getCacheInfo().setHasPermission(Boolean.FALSE);
208 return false;
209 }
210
211 getCacheInfo().setHasPermission(Boolean.TRUE);
212 return true;
213 }
214 }
215
216 /**
217 * Sets a flag indicating whether a disk-based cache file should
218 * be used when creating <code>ImageInputStream</code>s and
219 * <code>ImageOutputStream</code>s.
220 *
221 * <p> When reading from a standard <code>InputStream</code>>, it
222 * may be necessary to save previously read information in a cache
223 * since the underlying stream does not allow data to be re-read.
224 * Similarly, when writing to a standard
225 * <code>OutputStream</code>, a cache may be used to allow a
226 * previously written value to be changed before flushing it to
227 * the final destination.
228 *
229 * <p> The cache may reside in main memory or on disk. Setting
230 * this flag to <code>false</code> disallows the use of disk for
231 * future streams, which may be advantageous when working with
232 * small images, as the overhead of creating and destroying files
233 * is removed.
234 *
235 * <p> On startup, the value is set to <code>true</code>.
236 *
237 * @param useCache a <code>boolean</code> indicating whether a
238 * cache file should be used, in cases where it is optional.
239 *
240 * @see #getUseCache
241 */
242 public static void setUseCache(boolean useCache) {
243 getCacheInfo().setUseCache(useCache);
244 }
245
246 /**
247 * Returns the current value set by <code>setUseCache</code>, or
248 * <code>true</code> if no explicit setting has been made.
249 *
250 * @return true if a disk-based cache may be used for
251 * <code>ImageInputStream</code>s and
252 * <code>ImageOutputStream</code>s.
253 *
254 * @see #setUseCache
255 */
256 public static boolean getUseCache() {
257 return getCacheInfo().getUseCache();
258 }
259
260 /**
261 * Sets the directory where cache files are to be created. A
262 * value of <code>null</code> indicates that the system-dependent
263 * default temporary-file directory is to be used. If
264 * <code>getUseCache</code> returns false, this value is ignored.
265 *
266 * @param cacheDirectory a <code>File</code> specifying a directory.
267 *
268 * @see File#createTempFile(String, String, File)
269 *
270 * @exception SecurityException if the security manager denies
271 * access to the directory.
272 * @exception IllegalArgumentException if <code>cacheDir</code> is
273 * non-<code>null</code> but is not a directory.
274 *
275 * @see #getCacheDirectory
276 */
277 public static void setCacheDirectory(File cacheDirectory) {
278 if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {
279 throw new IllegalArgumentException("Not a directory!");
280 }
281 getCacheInfo().setCacheDirectory(cacheDirectory);
282 getCacheInfo().setHasPermission(null);
283 }
284
285 /**
286 * Returns the current value set by
287 * <code>setCacheDirectory</code>, or <code>null</code> if no
288 * explicit setting has been made.
289 *
290 * @return a <code>File</code> indicating the directory where
291 * cache files will be created, or <code>null</code> to indicate
292 * the system-dependent default temporary-file directory.
293 *
294 * @see #setCacheDirectory
295 */
296 public static File getCacheDirectory() {
297 return getCacheInfo().getCacheDirectory();
298 }
299
300 /**
301 * Returns an <code>ImageInputStream</code> that will take its
302 * input from the given <code>Object</code>. The set of
303 * <code>ImageInputStreamSpi</code>s registered with the
304 * <code>IIORegistry</code> class is queried and the first one
305 * that is able to take input from the supplied object is used to
306 * create the returned <code>ImageInputStream</code>. If no
307 * suitable <code>ImageInputStreamSpi</code> exists,
308 * <code>null</code> is returned.
309 *
310 * <p> The current cache settings from <code>getUseCache</code>and
311 * <code>getCacheDirectory</code> will be used to control caching.
312 *
313 * @param input an <code>Object</code> to be used as an input
314 * source, such as a <code>File</code>, readable
315 * <code>RandomAccessFile</code>, or <code>InputStream</code>.
316 *
317 * @return an <code>ImageInputStream</code>, or <code>null</code>.
318 *
319 * @exception IllegalArgumentException if <code>input</code>
320 * is <code>null</code>.
321 * @exception IOException if a cache file is needed but cannot be
322 * created.
323 *
324 * @see javax.imageio.spi.ImageInputStreamSpi
325 */
326 public static ImageInputStream createImageInputStream(Object input)
327 throws IOException {
328 if (input == null) {
329 throw new IllegalArgumentException("input == null!");
330 }
331
332 Iterator iter;
333 // Ensure category is present
334 try {
335 iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class,
336 true);
337 } catch (IllegalArgumentException e) {
338 return null;
339 }
340
341 boolean usecache = getUseCache() && hasCachePermission();
342
343 while (iter.hasNext()) {
344 ImageInputStreamSpi spi = (ImageInputStreamSpi)iter.next();
345 if (spi.getInputClass().isInstance(input)) {
346 try {
347 return spi.createInputStreamInstance(input,
348 usecache,
349 getCacheDirectory());
350 } catch (IOException e) {
351 throw new IIOException("Can't create cache file!", e);
352 }
353 }
354 }
355
356 return null;
357 }
358
359 // ImageOutputStreams
360
361 /**
362 * Returns an <code>ImageOutputStream</code> that will send its
363 * output to the given <code>Object</code>. The set of
364 * <code>ImageOutputStreamSpi</code>s registered with the
365 * <code>IIORegistry</code> class is queried and the first one
366 * that is able to send output from the supplied object is used to
367 * create the returned <code>ImageOutputStream</code>. If no
368 * suitable <code>ImageOutputStreamSpi</code> exists,
369 * <code>null</code> is returned.
370 *
371 * <p> The current cache settings from <code>getUseCache</code>and
372 * <code>getCacheDirectory</code> will be used to control caching.
373 *
374 * @param output an <code>Object</code> to be used as an output
375 * destination, such as a <code>File</code>, writable
376 * <code>RandomAccessFile</code>, or <code>OutputStream</code>.
377 *
378 * @return an <code>ImageOutputStream</code>, or
379 * <code>null</code>.
380 *
381 * @exception IllegalArgumentException if <code>output</code> is
382 * <code>null</code>.
383 * @exception IOException if a cache file is needed but cannot be
384 * created.
385 *
386 * @see javax.imageio.spi.ImageOutputStreamSpi
387 */
388 public static ImageOutputStream createImageOutputStream(Object output)
389 throws IOException {
390 if (output == null) {
391 throw new IllegalArgumentException("output == null!");
392 }
393
394 Iterator iter;
395 // Ensure category is present
396 try {
397 iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,
398 true);
399 } catch (IllegalArgumentException e) {
400 return null;
401 }
402
403 boolean usecache = getUseCache() && hasCachePermission();
404
405 while (iter.hasNext()) {
406 ImageOutputStreamSpi spi = (ImageOutputStreamSpi)iter.next();
407 if (spi.getOutputClass().isInstance(output)) {
408 try {
409 return spi.createOutputStreamInstance(output,
410 usecache,
411 getCacheDirectory());
412 } catch (IOException e) {
413 throw new IIOException("Can't create cache file!", e);
414 }
415 }
416 }
417
418 return null;
419 }
420
421 private static enum SpiInfo {
422 FORMAT_NAMES {
423 @Override
424 String[] info(ImageReaderWriterSpi spi) {
425 return spi.getFormatNames();
426 }
427 },
428 MIME_TYPES {
429 @Override
430 String[] info(ImageReaderWriterSpi spi) {
431 return spi.getMIMETypes();
432 }
433 },
434 FILE_SUFFIXES {
435 @Override
436 String[] info(ImageReaderWriterSpi spi) {
437 return spi.getFileSuffixes();
438 }
439 };
440
441 abstract String[] info(ImageReaderWriterSpi spi);
442 }
443
444 private static <S extends ImageReaderWriterSpi>
445 String[] getReaderWriterInfo(Class<S> spiClass, SpiInfo spiInfo)
446 {
447 // Ensure category is present
448 Iterator<S> iter;
449 try {
450 iter = theRegistry.getServiceProviders(spiClass, true);
451 } catch (IllegalArgumentException e) {
452 return new String[0];
453 }
454
455 HashSet<String> s = new HashSet<String>();
456 while (iter.hasNext()) {
457 ImageReaderWriterSpi spi = iter.next();
458 Collections.addAll(s, spiInfo.info(spi));
459 }
460
461 return s.toArray(new String[s.size()]);
462 }
463
464 // Readers
465
466 /**
467 * Returns an array of <code>String</code>s listing all of the
468 * informal format names understood by the current set of registered
469 * readers.
470 *
471 * @return an array of <code>String</code>s.
472 */
473 public static String[] getReaderFormatNames() {
474 return getReaderWriterInfo(ImageReaderSpi.class,
475 SpiInfo.FORMAT_NAMES);
476 }
477
478 /**
479 * Returns an array of <code>String</code>s listing all of the
480 * MIME types understood by the current set of registered
481 * readers.
482 *
483 * @return an array of <code>String</code>s.
484 */
485 public static String[] getReaderMIMETypes() {
486 return getReaderWriterInfo(ImageReaderSpi.class,
487 SpiInfo.MIME_TYPES);
488 }
489
490 /**
491 * Returns an array of <code>String</code>s listing all of the
492 * file suffixes associated with the formats understood
493 * by the current set of registered readers.
494 *
495 * @return an array of <code>String</code>s.
496 * @since 1.6
497 */
498 public static String[] getReaderFileSuffixes() {
499 return getReaderWriterInfo(ImageReaderSpi.class,
500 SpiInfo.FILE_SUFFIXES);
501 }
502
503 static class ImageReaderIterator implements Iterator<ImageReader> {
504 // Contains ImageReaderSpis
505 public Iterator iter;
506
507 public ImageReaderIterator(Iterator iter) {
508 this.iter = iter;
509 }
510
511 public boolean hasNext() {
512 return iter.hasNext();
513 }
514
515 public ImageReader next() {
516 ImageReaderSpi spi = null;
517 try {
518 spi = (ImageReaderSpi)iter.next();
519 return spi.createReaderInstance();
520 } catch (IOException e) {
521 // Deregister the spi in this case, but only as
522 // an ImageReaderSpi
523 theRegistry.deregisterServiceProvider(spi, ImageReaderSpi.class);
524 }
525 return null;
526 }
527
528 public void remove() {
529 throw new UnsupportedOperationException();
530 }
531 }
532
533 static class CanDecodeInputFilter
534 implements ServiceRegistry.Filter {
535
536 Object input;
537
538 public CanDecodeInputFilter(Object input) {
539 this.input = input;
540 }
541
542 public boolean filter(Object elt) {
543 try {
544 ImageReaderSpi spi = (ImageReaderSpi)elt;
545 ImageInputStream stream = null;
546 if (input instanceof ImageInputStream) {
547 stream = (ImageInputStream)input;
548 }
549
550 // Perform mark/reset as a defensive measure
551 // even though plug-ins are supposed to take
552 // care of it.
553 boolean canDecode = false;
554 if (stream != null) {
555 stream.mark();
556 }
557 canDecode = spi.canDecodeInput(input);
558 if (stream != null) {
559 stream.reset();
560 }
561
562 return canDecode;
563 } catch (IOException e) {
564 return false;
565 }
566 }
567 }
568
569 static class CanEncodeImageAndFormatFilter
570 implements ServiceRegistry.Filter {
571
572 ImageTypeSpecifier type;
573 String formatName;
574
575 public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type,
576 String formatName) {
577 this.type = type;
578 this.formatName = formatName;
579 }
580
581 public boolean filter(Object elt) {
582 ImageWriterSpi spi = (ImageWriterSpi)elt;
583 return Arrays.asList(spi.getFormatNames()).contains(formatName) &&
584 spi.canEncodeImage(type);
585 }
586 }
587
588 static class ContainsFilter
589 implements ServiceRegistry.Filter {
590
591 Method method;
592 String name;
593
594 // method returns an array of Strings
595 public ContainsFilter(Method method,
596 String name) {
597 this.method = method;
598 this.name = name;
599 }
600
601 public boolean filter(Object elt) {
602 try {
603 return contains((String[])method.invoke(elt), name);
604 } catch (Exception e) {
605 return false;
606 }
607 }
608 }
609
610 /**
611 * Returns an <code>Iterator</code> containing all currently
612 * registered <code>ImageReader</code>s that claim to be able to
613 * decode the supplied <code>Object</code>, typically an
614 * <code>ImageInputStream</code>.
615 *
616 * <p> The stream position is left at its prior position upon
617 * exit from this method.
618 *
619 * @param input an <code>ImageInputStream</code> or other
620 * <code>Object</code> containing encoded image data.
621 *
622 * @return an <code>Iterator</code> containing <code>ImageReader</code>s.
623 *
624 * @exception IllegalArgumentException if <code>input</code> is
625 * <code>null</code>.
626 *
627 * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
628 */
629 public static Iterator<ImageReader> getImageReaders(Object input) {
630 if (input == null) {
631 throw new IllegalArgumentException("input == null!");
632 }
633 Iterator iter;
634 // Ensure category is present
635 try {
636 iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
637 new CanDecodeInputFilter(input),
638 true);
639 } catch (IllegalArgumentException e) {
640 return Collections.emptyIterator();
641 }
642
643 return new ImageReaderIterator(iter);
644 }
645
646 private static Method readerFormatNamesMethod;
647 private static Method readerFileSuffixesMethod;
648 private static Method readerMIMETypesMethod;
649 private static Method writerFormatNamesMethod;
650 private static Method writerFileSuffixesMethod;
651 private static Method writerMIMETypesMethod;
652
653 static {
654 try {
655 readerFormatNamesMethod =
656 ImageReaderSpi.class.getMethod("getFormatNames");
657 readerFileSuffixesMethod =
658 ImageReaderSpi.class.getMethod("getFileSuffixes");
659 readerMIMETypesMethod =
660 ImageReaderSpi.class.getMethod("getMIMETypes");
661
662 writerFormatNamesMethod =
663 ImageWriterSpi.class.getMethod("getFormatNames");
664 writerFileSuffixesMethod =
665 ImageWriterSpi.class.getMethod("getFileSuffixes");
666 writerMIMETypesMethod =
667 ImageWriterSpi.class.getMethod("getMIMETypes");
668 } catch (NoSuchMethodException e) {
669 e.printStackTrace();
670 }
671 }
672
673 /**
674 * Returns an <code>Iterator</code> containing all currently
675 * registered <code>ImageReader</code>s that claim to be able to
676 * decode the named format.
677 *
678 * @param formatName a <code>String</code> containing the informal
679 * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
680 *
681 * @return an <code>Iterator</code> containing
682 * <code>ImageReader</code>s.
683 *
684 * @exception IllegalArgumentException if <code>formatName</code>
685 * is <code>null</code>.
686 *
687 * @see javax.imageio.spi.ImageReaderSpi#getFormatNames
688 */
689 public static Iterator<ImageReader>
690 getImageReadersByFormatName(String formatName)
691 {
692 if (formatName == null) {
693 throw new IllegalArgumentException("formatName == null!");
694 }
695 Iterator iter;
696 // Ensure category is present
697 try {
698 iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
699 new ContainsFilter(readerFormatNamesMethod,
700 formatName),
701 true);
702 } catch (IllegalArgumentException e) {
703 return Collections.emptyIterator();
704 }
705 return new ImageReaderIterator(iter);
706 }
707
708 /**
709 * Returns an <code>Iterator</code> containing all currently
710 * registered <code>ImageReader</code>s that claim to be able to
711 * decode files with the given suffix.
712 *
713 * @param fileSuffix a <code>String</code> containing a file
714 * suffix (<i>e.g.</i>, "jpg" or "tiff").
715 *
716 * @return an <code>Iterator</code> containing
717 * <code>ImageReader</code>s.
718 *
719 * @exception IllegalArgumentException if <code>fileSuffix</code>
720 * is <code>null</code>.
721 *
722 * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
723 */
724 public static Iterator<ImageReader>
725 getImageReadersBySuffix(String fileSuffix)
726 {
727 if (fileSuffix == null) {
728 throw new IllegalArgumentException("fileSuffix == null!");
729 }
730 // Ensure category is present
731 Iterator iter;
732 try {
733 iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
734 new ContainsFilter(readerFileSuffixesMethod,
735 fileSuffix),
736 true);
737 } catch (IllegalArgumentException e) {
738 return Collections.emptyIterator();
739 }
740 return new ImageReaderIterator(iter);
741 }
742
743 /**
744 * Returns an <code>Iterator</code> containing all currently
745 * registered <code>ImageReader</code>s that claim to be able to
746 * decode files with the given MIME type.
747 *
748 * @param MIMEType a <code>String</code> containing a file
749 * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
750 *
751 * @return an <code>Iterator</code> containing
752 * <code>ImageReader</code>s.
753 *
754 * @exception IllegalArgumentException if <code>MIMEType</code> is
755 * <code>null</code>.
756 *
757 * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
758 */
759 public static Iterator<ImageReader>
760 getImageReadersByMIMEType(String MIMEType)
761 {
762 if (MIMEType == null) {
763 throw new IllegalArgumentException("MIMEType == null!");
764 }
765 // Ensure category is present
766 Iterator iter;
767 try {
768 iter = theRegistry.getServiceProviders(ImageReaderSpi.class,
769 new ContainsFilter(readerMIMETypesMethod,
770 MIMEType),
771 true);
772 } catch (IllegalArgumentException e) {
773 return Collections.emptyIterator();
774 }
775 return new ImageReaderIterator(iter);
776 }
777
778 // Writers
779
780 /**
781 * Returns an array of <code>String</code>s listing all of the
782 * informal format names understood by the current set of registered
783 * writers.
784 *
785 * @return an array of <code>String</code>s.
786 */
787 public static String[] getWriterFormatNames() {
788 return getReaderWriterInfo(ImageWriterSpi.class,
789 SpiInfo.FORMAT_NAMES);
790 }
791
792 /**
793 * Returns an array of <code>String</code>s listing all of the
794 * MIME types understood by the current set of registered
795 * writers.
796 *
797 * @return an array of <code>String</code>s.
798 */
799 public static String[] getWriterMIMETypes() {
800 return getReaderWriterInfo(ImageWriterSpi.class,
801 SpiInfo.MIME_TYPES);
802 }
803
804 /**
805 * Returns an array of <code>String</code>s listing all of the
806 * file suffixes associated with the formats understood
807 * by the current set of registered writers.
808 *
809 * @return an array of <code>String</code>s.
810 * @since 1.6
811 */
812 public static String[] getWriterFileSuffixes() {
813 return getReaderWriterInfo(ImageWriterSpi.class,
814 SpiInfo.FILE_SUFFIXES);
815 }
816
817 static class ImageWriterIterator implements Iterator<ImageWriter> {
818 // Contains ImageWriterSpis
819 public Iterator iter;
820
821 public ImageWriterIterator(Iterator iter) {
822 this.iter = iter;
823 }
824
825 public boolean hasNext() {
826 return iter.hasNext();
827 }
828
829 public ImageWriter next() {
830 ImageWriterSpi spi = null;
831 try {
832 spi = (ImageWriterSpi)iter.next();
833 return spi.createWriterInstance();
834 } catch (IOException e) {
835 // Deregister the spi in this case, but only as a writerSpi
836 theRegistry.deregisterServiceProvider(spi, ImageWriterSpi.class);
837 }
838 return null;
839 }
840
841 public void remove() {
842 throw new UnsupportedOperationException();
843 }
844 }
845
846 private static boolean contains(String[] names, String name) {
847 for (int i = 0; i < names.length; i++) {
848 if (name.equalsIgnoreCase(names[i])) {
849 return true;
850 }
851 }
852
853 return false;
854 }
855
856 /**
857 * Returns an <code>Iterator</code> containing all currently
858 * registered <code>ImageWriter</code>s that claim to be able to
859 * encode the named format.
860 *
861 * @param formatName a <code>String</code> containing the informal
862 * name of a format (<i>e.g.</i>, "jpeg" or "tiff".
863 *
864 * @return an <code>Iterator</code> containing
865 * <code>ImageWriter</code>s.
866 *
867 * @exception IllegalArgumentException if <code>formatName</code> is
868 * <code>null</code>.
869 *
870 * @see javax.imageio.spi.ImageWriterSpi#getFormatNames
871 */
872 public static Iterator<ImageWriter>
873 getImageWritersByFormatName(String formatName)
874 {
875 if (formatName == null) {
876 throw new IllegalArgumentException("formatName == null!");
877 }
878 Iterator iter;
879 // Ensure category is present
880 try {
881 iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
882 new ContainsFilter(writerFormatNamesMethod,
883 formatName),
884 true);
885 } catch (IllegalArgumentException e) {
886 return Collections.emptyIterator();
887 }
888 return new ImageWriterIterator(iter);
889 }
890
891 /**
892 * Returns an <code>Iterator</code> containing all currently
893 * registered <code>ImageWriter</code>s that claim to be able to
894 * encode files with the given suffix.
895 *
896 * @param fileSuffix a <code>String</code> containing a file
897 * suffix (<i>e.g.</i>, "jpg" or "tiff").
898 *
899 * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
900 *
901 * @exception IllegalArgumentException if <code>fileSuffix</code> is
902 * <code>null</code>.
903 *
904 * @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes
905 */
906 public static Iterator<ImageWriter>
907 getImageWritersBySuffix(String fileSuffix)
908 {
909 if (fileSuffix == null) {
910 throw new IllegalArgumentException("fileSuffix == null!");
911 }
912 Iterator iter;
913 // Ensure category is present
914 try {
915 iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
916 new ContainsFilter(writerFileSuffixesMethod,
917 fileSuffix),
918 true);
919 } catch (IllegalArgumentException e) {
920 return Collections.emptyIterator();
921 }
922 return new ImageWriterIterator(iter);
923 }
924
925 /**
926 * Returns an <code>Iterator</code> containing all currently
927 * registered <code>ImageWriter</code>s that claim to be able to
928 * encode files with the given MIME type.
929 *
930 * @param MIMEType a <code>String</code> containing a file
931 * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
932 *
933 * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
934 *
935 * @exception IllegalArgumentException if <code>MIMEType</code> is
936 * <code>null</code>.
937 *
938 * @see javax.imageio.spi.ImageWriterSpi#getMIMETypes
939 */
940 public static Iterator<ImageWriter>
941 getImageWritersByMIMEType(String MIMEType)
942 {
943 if (MIMEType == null) {
944 throw new IllegalArgumentException("MIMEType == null!");
945 }
946 Iterator iter;
947 // Ensure category is present
948 try {
949 iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
950 new ContainsFilter(writerMIMETypesMethod,
951 MIMEType),
952 true);
953 } catch (IllegalArgumentException e) {
954 return Collections.emptyIterator();
955 }
956 return new ImageWriterIterator(iter);
957 }
958
959 /**
960 * Returns an <code>ImageWriter</code>corresponding to the given
961 * <code>ImageReader</code>, if there is one, or <code>null</code>
962 * if the plug-in for this <code>ImageReader</code> does not
963 * specify a corresponding <code>ImageWriter</code>, or if the
964 * given <code>ImageReader</code> is not registered. This
965 * mechanism may be used to obtain an <code>ImageWriter</code>
966 * that will understand the internal structure of non-pixel
967 * metadata (as encoded by <code>IIOMetadata</code> objects)
968 * generated by the <code>ImageReader</code>. By obtaining this
969 * data from the <code>ImageReader</code> and passing it on to the
970 * <code>ImageWriter</code> obtained with this method, a client
971 * program can read an image, modify it in some way, and write it
972 * back out preserving all metadata, without having to understand
973 * anything about the structure of the metadata, or even about
974 * the image format. Note that this method returns the
975 * "preferred" writer, which is the first in the list returned by
976 * <code>javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()</code>.
977 *
978 * @param reader an instance of a registered <code>ImageReader</code>.
979 *
980 * @return an <code>ImageWriter</code>, or null.
981 *
982 * @exception IllegalArgumentException if <code>reader</code> is
983 * <code>null</code>.
984 *
985 * @see #getImageReader(ImageWriter)
986 * @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()
987 */
988 public static ImageWriter getImageWriter(ImageReader reader) {
989 if (reader == null) {
990 throw new IllegalArgumentException("reader == null!");
991 }
992
993 ImageReaderSpi readerSpi = reader.getOriginatingProvider();
994 if (readerSpi == null) {
995 Iterator readerSpiIter;
996 // Ensure category is present
997 try {
998 readerSpiIter =
999 theRegistry.getServiceProviders(ImageReaderSpi.class,
1000 false);
1001 } catch (IllegalArgumentException e) {
1002 return null;
1003 }
1004
1005 while (readerSpiIter.hasNext()) {
1006 ImageReaderSpi temp = (ImageReaderSpi) readerSpiIter.next();
1007 if (temp.isOwnReader(reader)) {
1008 readerSpi = temp;
1009 break;
1010 }
1011 }
1012 if (readerSpi == null) {
1013 return null;
1014 }
1015 }
1016
1017 String[] writerNames = readerSpi.getImageWriterSpiNames();
1018 if (writerNames == null) {
1019 return null;
1020 }
1021
1022 Class writerSpiClass = null;
1023 try {
1024 writerSpiClass = Class.forName(writerNames[0], true,
1025 ClassLoader.getSystemClassLoader());
1026 } catch (ClassNotFoundException e) {
1027 return null;
1028 }
1029
1030 ImageWriterSpi writerSpi = (ImageWriterSpi)
1031 theRegistry.getServiceProviderByClass(writerSpiClass);
1032 if (writerSpi == null) {
1033 return null;
1034 }
1035
1036 try {
1037 return writerSpi.createWriterInstance();
1038 } catch (IOException e) {
1039 // Deregister the spi in this case, but only as a writerSpi
1040 theRegistry.deregisterServiceProvider(writerSpi,
1041 ImageWriterSpi.class);
1042 return null;
1043 }
1044 }
1045
1046 /**
1047 * Returns an <code>ImageReader</code>corresponding to the given
1048 * <code>ImageWriter</code>, if there is one, or <code>null</code>
1049 * if the plug-in for this <code>ImageWriter</code> does not
1050 * specify a corresponding <code>ImageReader</code>, or if the
1051 * given <code>ImageWriter</code> is not registered. This method
1052 * is provided principally for symmetry with
1053 * <code>getImageWriter(ImageReader)</code>. Note that this
1054 * method returns the "preferred" reader, which is the first in
1055 * the list returned by
1056 * javax.imageio.spi.ImageWriterSpi.<code>getImageReaderSpiNames()</code>.
1057 *
1058 * @param writer an instance of a registered <code>ImageWriter</code>.
1059 *
1060 * @return an <code>ImageReader</code>, or null.
1061 *
1062 * @exception IllegalArgumentException if <code>writer</code> is
1063 * <code>null</code>.
1064 *
1065 * @see #getImageWriter(ImageReader)
1066 * @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()
1067 */
1068 public static ImageReader getImageReader(ImageWriter writer) {
1069 if (writer == null) {
1070 throw new IllegalArgumentException("writer == null!");
1071 }
1072
1073 ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1074 if (writerSpi == null) {
1075 Iterator writerSpiIter;
1076 // Ensure category is present
1077 try {
1078 writerSpiIter =
1079 theRegistry.getServiceProviders(ImageWriterSpi.class,
1080 false);
1081 } catch (IllegalArgumentException e) {
1082 return null;
1083 }
1084
1085 while (writerSpiIter.hasNext()) {
1086 ImageWriterSpi temp = (ImageWriterSpi) writerSpiIter.next();
1087 if (temp.isOwnWriter(writer)) {
1088 writerSpi = temp;
1089 break;
1090 }
1091 }
1092 if (writerSpi == null) {
1093 return null;
1094 }
1095 }
1096
1097 String[] readerNames = writerSpi.getImageReaderSpiNames();
1098 if (readerNames == null) {
1099 return null;
1100 }
1101
1102 Class readerSpiClass = null;
1103 try {
1104 readerSpiClass = Class.forName(readerNames[0], true,
1105 ClassLoader.getSystemClassLoader());
1106 } catch (ClassNotFoundException e) {
1107 return null;
1108 }
1109
1110 ImageReaderSpi readerSpi = (ImageReaderSpi)
1111 theRegistry.getServiceProviderByClass(readerSpiClass);
1112 if (readerSpi == null) {
1113 return null;
1114 }
1115
1116 try {
1117 return readerSpi.createReaderInstance();
1118 } catch (IOException e) {
1119 // Deregister the spi in this case, but only as a readerSpi
1120 theRegistry.deregisterServiceProvider(readerSpi,
1121 ImageReaderSpi.class);
1122 return null;
1123 }
1124 }
1125
1126 /**
1127 * Returns an <code>Iterator</code> containing all currently
1128 * registered <code>ImageWriter</code>s that claim to be able to
1129 * encode images of the given layout (specified using an
1130 * <code>ImageTypeSpecifier</code>) in the given format.
1131 *
1132 * @param type an <code>ImageTypeSpecifier</code> indicating the
1133 * layout of the image to be written.
1134 * @param formatName the informal name of the <code>format</code>.
1135 *
1136 * @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
1137 *
1138 * @exception IllegalArgumentException if any parameter is
1139 * <code>null</code>.
1140 *
1141 * @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)
1142 */
1143 public static Iterator<ImageWriter>
1144 getImageWriters(ImageTypeSpecifier type, String formatName)
1145 {
1146 if (type == null) {
1147 throw new IllegalArgumentException("type == null!");
1148 }
1149 if (formatName == null) {
1150 throw new IllegalArgumentException("formatName == null!");
1151 }
1152
1153 Iterator iter;
1154 // Ensure category is present
1155 try {
1156 iter = theRegistry.getServiceProviders(ImageWriterSpi.class,
1157 new CanEncodeImageAndFormatFilter(type,
1158 formatName),
1159 true);
1160 } catch (IllegalArgumentException e) {
1161 return Collections.emptyIterator();
1162 }
1163
1164 return new ImageWriterIterator(iter);
1165 }
1166
1167 static class ImageTranscoderIterator
1168 implements Iterator<ImageTranscoder>
1169 {
1170 // Contains ImageTranscoderSpis
1171 public Iterator iter;
1172
1173 public ImageTranscoderIterator(Iterator iter) {
1174 this.iter = iter;
1175 }
1176
1177 public boolean hasNext() {
1178 return iter.hasNext();
1179 }
1180
1181 public ImageTranscoder next() {
1182 ImageTranscoderSpi spi = null;
1183 spi = (ImageTranscoderSpi)iter.next();
1184 return spi.createTranscoderInstance();
1185 }
1186
1187 public void remove() {
1188 throw new UnsupportedOperationException();
1189 }
1190 }
1191
1192 static class TranscoderFilter
1193 implements ServiceRegistry.Filter {
1194
1195 String readerSpiName;
1196 String writerSpiName;
1197
1198 public TranscoderFilter(ImageReaderSpi readerSpi,
1199 ImageWriterSpi writerSpi) {
1200 this.readerSpiName = readerSpi.getClass().getName();
1201 this.writerSpiName = writerSpi.getClass().getName();
1202 }
1203
1204 public boolean filter(Object elt) {
1205 ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;
1206 String readerName = spi.getReaderServiceProviderName();
1207 String writerName = spi.getWriterServiceProviderName();
1208 return (readerName.equals(readerSpiName) &&
1209 writerName.equals(writerSpiName));
1210 }
1211 }
1212
1213 /**
1214 * Returns an <code>Iterator</code> containing all currently
1215 * registered <code>ImageTranscoder</code>s that claim to be
1216 * able to transcode between the metadata of the given
1217 * <code>ImageReader</code> and <code>ImageWriter</code>.
1218 *
1219 * @param reader an <code>ImageReader</code>.
1220 * @param writer an <code>ImageWriter</code>.
1221 *
1222 * @return an <code>Iterator</code> containing
1223 * <code>ImageTranscoder</code>s.
1224 *
1225 * @exception IllegalArgumentException if <code>reader</code> or
1226 * <code>writer</code> is <code>null</code>.
1227 */
1228 public static Iterator<ImageTranscoder>
1229 getImageTranscoders(ImageReader reader, ImageWriter writer)
1230 {
1231 if (reader == null) {
1232 throw new IllegalArgumentException("reader == null!");
1233 }
1234 if (writer == null) {
1235 throw new IllegalArgumentException("writer == null!");
1236 }
1237 ImageReaderSpi readerSpi = reader.getOriginatingProvider();
1238 ImageWriterSpi writerSpi = writer.getOriginatingProvider();
1239 ServiceRegistry.Filter filter =
1240 new TranscoderFilter(readerSpi, writerSpi);
1241
1242 Iterator iter;
1243 // Ensure category is present
1244 try {
1245 iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,
1246 filter, true);
1247 } catch (IllegalArgumentException e) {
1248 return Collections.emptyIterator();
1249 }
1250 return new ImageTranscoderIterator(iter);
1251 }
1252
1253 // All-in-one methods
1254
1255 /**
1256 * Returns a <code>BufferedImage</code> as the result of decoding
1257 * a supplied <code>File</code> with an <code>ImageReader</code>
1258 * chosen automatically from among those currently registered.
1259 * The <code>File</code> is wrapped in an
1260 * <code>ImageInputStream</code>. If no registered
1261 * <code>ImageReader</code> claims to be able to read the
1262 * resulting stream, <code>null</code> is returned.
1263 *
1264 * <p> The current cache settings from <code>getUseCache</code>and
1265 * <code>getCacheDirectory</code> will be used to control caching in the
1266 * <code>ImageInputStream</code> that is created.
1267 *
1268 * <p> Note that there is no <code>read</code> method that takes a
1269 * filename as a <code>String</code>; use this method instead after
1270 * creating a <code>File</code> from the filename.
1271 *
1272 * <p> This method does not attempt to locate
1273 * <code>ImageReader</code>s that can read directly from a
1274 * <code>File</code>; that may be accomplished using
1275 * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1276 *
1277 * @param input a <code>File</code> to read from.
1278 *
1279 * @return a <code>BufferedImage</code> containing the decoded
1280 * contents of the input, or <code>null</code>.
1281 *
1282 * @exception IllegalArgumentException if <code>input</code> is
1283 * <code>null</code>.
1284 * @exception IOException if an error occurs during reading.
1285 */
1286 public static BufferedImage read(File input) throws IOException {
1287 if (input == null) {
1288 throw new IllegalArgumentException("input == null!");
1289 }
1290 if (!input.canRead()) {
1291 throw new IIOException("Can't read input file!");
1292 }
1293
1294 ImageInputStream stream = createImageInputStream(input);
1295 if (stream == null) {
1296 throw new IIOException("Can't create an ImageInputStream!");
1297 }
1298 BufferedImage bi = read(stream);
1299 if (bi == null) {
1300 stream.close();
1301 }
1302 return bi;
1303 }
1304
1305 /**
1306 * Returns a <code>BufferedImage</code> as the result of decoding
1307 * a supplied <code>InputStream</code> with an <code>ImageReader</code>
1308 * chosen automatically from among those currently registered.
1309 * The <code>InputStream</code> is wrapped in an
1310 * <code>ImageInputStream</code>. If no registered
1311 * <code>ImageReader</code> claims to be able to read the
1312 * resulting stream, <code>null</code> is returned.
1313 *
1314 * <p> The current cache settings from <code>getUseCache</code>and
1315 * <code>getCacheDirectory</code> will be used to control caching in the
1316 * <code>ImageInputStream</code> that is created.
1317 *
1318 * <p> This method does not attempt to locate
1319 * <code>ImageReader</code>s that can read directly from an
1320 * <code>InputStream</code>; that may be accomplished using
1321 * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1322 *
1323 * <p> This method <em>does not</em> close the provided
1324 * <code>InputStream</code> after the read operation has completed;
1325 * it is the responsibility of the caller to close the stream, if desired.
1326 *
1327 * @param input an <code>InputStream</code> to read from.
1328 *
1329 * @return a <code>BufferedImage</code> containing the decoded
1330 * contents of the input, or <code>null</code>.
1331 *
1332 * @exception IllegalArgumentException if <code>input</code> is
1333 * <code>null</code>.
1334 * @exception IOException if an error occurs during reading.
1335 */
1336 public static BufferedImage read(InputStream input) throws IOException {
1337 if (input == null) {
1338 throw new IllegalArgumentException("input == null!");
1339 }
1340
1341 ImageInputStream stream = createImageInputStream(input);
1342 BufferedImage bi = read(stream);
1343 if (bi == null) {
1344 stream.close();
1345 }
1346 return bi;
1347 }
1348
1349 /**
1350 * Returns a <code>BufferedImage</code> as the result of decoding
1351 * a supplied <code>URL</code> with an <code>ImageReader</code>
1352 * chosen automatically from among those currently registered. An
1353 * <code>InputStream</code> is obtained from the <code>URL</code>,
1354 * which is wrapped in an <code>ImageInputStream</code>. If no
1355 * registered <code>ImageReader</code> claims to be able to read
1356 * the resulting stream, <code>null</code> is returned.
1357 *
1358 * <p> The current cache settings from <code>getUseCache</code>and
1359 * <code>getCacheDirectory</code> will be used to control caching in the
1360 * <code>ImageInputStream</code> that is created.
1361 *
1362 * <p> This method does not attempt to locate
1363 * <code>ImageReader</code>s that can read directly from a
1364 * <code>URL</code>; that may be accomplished using
1365 * <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
1366 *
1367 * @param input a <code>URL</code> to read from.
1368 *
1369 * @return a <code>BufferedImage</code> containing the decoded
1370 * contents of the input, or <code>null</code>.
1371 *
1372 * @exception IllegalArgumentException if <code>input</code> is
1373 * <code>null</code>.
1374 * @exception IOException if an error occurs during reading.
1375 */
1376 public static BufferedImage read(URL input) throws IOException {
1377 if (input == null) {
1378 throw new IllegalArgumentException("input == null!");
1379 }
1380
1381 InputStream istream = null;
1382 try {
1383 istream = input.openStream();
1384 } catch (IOException e) {
1385 throw new IIOException("Can't get input stream from URL!", e);
1386 }
1387 ImageInputStream stream = createImageInputStream(istream);
1388 BufferedImage bi;
1389 try {
1390 bi = read(stream);
1391 if (bi == null) {
1392 stream.close();
1393 }
1394 } finally {
1395 istream.close();
1396 }
1397 return bi;
1398 }
1399
1400 /**
1401 * Returns a <code>BufferedImage</code> as the result of decoding
1402 * a supplied <code>ImageInputStream</code> with an
1403 * <code>ImageReader</code> chosen automatically from among those
1404 * currently registered. If no registered
1405 * <code>ImageReader</code> claims to be able to read the stream,
1406 * <code>null</code> is returned.
1407 *
1408 * <p> Unlike most other methods in this class, this method <em>does</em>
1409 * close the provided <code>ImageInputStream</code> after the read
1410 * operation has completed, unless <code>null</code> is returned,
1411 * in which case this method <em>does not</em> close the stream.
1412 *
1413 * @param stream an <code>ImageInputStream</code> to read from.
1414 *
1415 * @return a <code>BufferedImage</code> containing the decoded
1416 * contents of the input, or <code>null</code>.
1417 *
1418 * @exception IllegalArgumentException if <code>stream</code> is
1419 * <code>null</code>.
1420 * @exception IOException if an error occurs during reading.
1421 */
1422 public static BufferedImage read(ImageInputStream stream)
1423 throws IOException {
1424 if (stream == null) {
1425 throw new IllegalArgumentException("stream == null!");
1426 }
1427
1428 Iterator iter = getImageReaders(stream);
1429 if (!iter.hasNext()) {
1430 return null;
1431 }
1432
1433 ImageReader reader = (ImageReader)iter.next();
1434 ImageReadParam param = reader.getDefaultReadParam();
1435 reader.setInput(stream, true, true);
1436 BufferedImage bi;
1437 try {
1438 bi = reader.read(0, param);
1439 } finally {
1440 reader.dispose();
1441 stream.close();
1442 }
1443 return bi;
1444 }
1445
1446 /**
1447 * Writes an image using the an arbitrary <code>ImageWriter</code>
1448 * that supports the given format to an
1449 * <code>ImageOutputStream</code>. The image is written to the
1450 * <code>ImageOutputStream</code> starting at the current stream
1451 * pointer, overwriting existing stream data from that point
1452 * forward, if present.
1453 *
1454 * <p> This method <em>does not</em> close the provided
1455 * <code>ImageOutputStream</code> after the write operation has completed;
1456 * it is the responsibility of the caller to close the stream, if desired.
1457 *
1458 * @param im a <code>RenderedImage</code> to be written.
1459 * @param formatName a <code>String</code> containg the informal
1460 * name of the format.
1461 * @param output an <code>ImageOutputStream</code> to be written to.
1462 *
1463 * @return <code>false</code> if no appropriate writer is found.
1464 *
1465 * @exception IllegalArgumentException if any parameter is
1466 * <code>null</code>.
1467 * @exception IOException if an error occurs during writing.
1468 */
1469 public static boolean write(RenderedImage im,
1470 String formatName,
1471 ImageOutputStream output) throws IOException {
1472 if (im == null) {
1473 throw new IllegalArgumentException("im == null!");
1474 }
1475 if (formatName == null) {
1476 throw new IllegalArgumentException("formatName == null!");
1477 }
1478 if (output == null) {
1479 throw new IllegalArgumentException("output == null!");
1480 }
1481
1482 return doWrite(im, getWriter(im, formatName), output);
1483 }
1484
1485 /**
1486 * Writes an image using an arbitrary <code>ImageWriter</code>
1487 * that supports the given format to a <code>File</code>. If
1488 * there is already a <code>File</code> present, its contents are
1489 * discarded.
1490 *
1491 * @param im a <code>RenderedImage</code> to be written.
1492 * @param formatName a <code>String</code> containg the informal
1493 * name of the format.
1494 * @param output a <code>File</code> to be written to.
1495 *
1496 * @return <code>false</code> if no appropriate writer is found.
1497 *
1498 * @exception IllegalArgumentException if any parameter is
1499 * <code>null</code>.
1500 * @exception IOException if an error occurs during writing.
1501 */
1502 public static boolean write(RenderedImage im,
1503 String formatName,
1504 File output) throws IOException {
1505 if (output == null) {
1506 throw new IllegalArgumentException("output == null!");
1507 }
1508 ImageOutputStream stream = null;
1509
1510 ImageWriter writer = getWriter(im, formatName);
1511 if (writer == null) {
1512 /* Do not make changes in the file system if we have
1513 * no appropriate writer.
1514 */
1515 return false;
1516 }
1517
1518 try {
1519 output.delete();
1520 stream = createImageOutputStream(output);
1521 } catch (IOException e) {
1522 throw new IIOException("Can't create output stream!", e);
1523 }
1524
1525 try {
1526 return doWrite(im, writer, stream);
1527 } finally {
1528 stream.close();
1529 }
1530 }
1531
1532 /**
1533 * Writes an image using an arbitrary <code>ImageWriter</code>
1534 * that supports the given format to an <code>OutputStream</code>.
1535 *
1536 * <p> This method <em>does not</em> close the provided
1537 * <code>OutputStream</code> after the write operation has completed;
1538 * it is the responsibility of the caller to close the stream, if desired.
1539 *
1540 * <p> The current cache settings from <code>getUseCache</code>and
1541 * <code>getCacheDirectory</code> will be used to control caching.
1542 *
1543 * @param im a <code>RenderedImage</code> to be written.
1544 * @param formatName a <code>String</code> containg the informal
1545 * name of the format.
1546 * @param output an <code>OutputStream</code> to be written to.
1547 *
1548 * @return <code>false</code> if no appropriate writer is found.
1549 *
1550 * @exception IllegalArgumentException if any parameter is
1551 * <code>null</code>.
1552 * @exception IOException if an error occurs during writing.
1553 */
1554 public static boolean write(RenderedImage im,
1555 String formatName,
1556 OutputStream output) throws IOException {
1557 if (output == null) {
1558 throw new IllegalArgumentException("output == null!");
1559 }
1560 ImageOutputStream stream = null;
1561 try {
1562 stream = createImageOutputStream(output);
1563 } catch (IOException e) {
1564 throw new IIOException("Can't create output stream!", e);
1565 }
1566
1567 try {
1568 return doWrite(im, getWriter(im, formatName), stream);
1569 } finally {
1570 stream.close();
1571 }
1572 }
1573
1574 /**
1575 * Returns <code>ImageWriter</code> instance according to given
1576 * rendered image and image format or <code>null</code> if there
1577 * is no appropriate writer.
1578 */
1579 private static ImageWriter getWriter(RenderedImage im,
1580 String formatName) {
1581 ImageTypeSpecifier type =
1582 ImageTypeSpecifier.createFromRenderedImage(im);
1583 Iterator<ImageWriter> iter = getImageWriters(type, formatName);
1584
1585 if (iter.hasNext()) {
1586 return iter.next();
1587 } else {
1588 return null;
1589 }
1590 }
1591
1592 /**
1593 * Writes image to output stream using given image writer.
1594 */
1595 private static boolean doWrite(RenderedImage im, ImageWriter writer,
1596 ImageOutputStream output) throws IOException {
1597 if (writer == null) {
1598 return false;
1599 }
1600 writer.setOutput(output);
1601 try {
1602 writer.write(im);
1603 } finally {
1604 writer.dispose();
1605 output.flush();
1606 }
1607 return true;
1608 }
1609}