001    /* ImageIO.java --
002       Copyright (C) 2004, 2005  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.imageio;
040    
041    import java.awt.image.BufferedImage;
042    import java.awt.image.RenderedImage;
043    import java.io.File;
044    import java.io.FileInputStream;
045    import java.io.FileOutputStream;
046    import java.io.IOException;
047    import java.io.InputStream;
048    import java.io.OutputStream;
049    import java.net.URL;
050    import java.util.ArrayList;
051    import java.util.Collections;
052    import java.util.Iterator;
053    
054    import javax.imageio.spi.IIORegistry;
055    import javax.imageio.spi.ImageInputStreamSpi;
056    import javax.imageio.spi.ImageOutputStreamSpi;
057    import javax.imageio.spi.ImageReaderSpi;
058    import javax.imageio.spi.ImageTranscoderSpi;
059    import javax.imageio.spi.ImageWriterSpi;
060    import javax.imageio.spi.ServiceRegistry;
061    import javax.imageio.stream.ImageInputStream;
062    import javax.imageio.stream.ImageOutputStream;
063    import javax.imageio.stream.MemoryCacheImageInputStream;
064    import javax.imageio.stream.MemoryCacheImageOutputStream;
065    
066    /**
067     * An uninstantiable class that provides static methods for locating
068     * and using image readers and writers.
069     */
070    public final class ImageIO
071    {
072      /**
073       * Construct an ImageIO.  Private since ImageIO is not instantiable.
074       */
075      private ImageIO()
076      {
077      }
078    
079      private static final class ReaderFormatFilter implements ServiceRegistry.Filter
080      {
081        private String formatName;
082    
083        public ReaderFormatFilter(String formatName)
084        {
085          this.formatName = formatName;
086        }
087    
088        public boolean filter (Object provider)
089        {
090          if (provider instanceof ImageReaderSpi)
091            {
092              ImageReaderSpi spi = (ImageReaderSpi) provider;
093              String[] formatNames = spi.getFormatNames();
094    
095              for (int i = formatNames.length - 1; i >= 0; --i)
096                if (formatName.equals(formatNames[i]))
097                  return true;
098            }
099    
100          return false;
101        }
102      }
103    
104      private static final class ReaderMIMETypeFilter implements ServiceRegistry.Filter
105      {
106        private String MIMEType;
107    
108        public ReaderMIMETypeFilter(String MIMEType)
109        {
110          this.MIMEType = MIMEType;
111        }
112    
113        public boolean filter(Object provider)
114        {
115          if (provider instanceof ImageReaderSpi)
116            {
117              ImageReaderSpi spi = (ImageReaderSpi) provider;
118              String[] mimetypes = spi.getMIMETypes();
119    
120              for (int i = mimetypes.length - 1; i >= 0; --i)
121                if (MIMEType.equals(mimetypes[i]))
122                  return true;
123            }
124    
125          return false;
126        }
127      }
128    
129      private static final class ReaderObjectFilter implements ServiceRegistry.Filter
130      {
131        private Object object;
132    
133        public ReaderObjectFilter(Object object)
134        {
135          this.object = object;
136        }
137    
138        public boolean filter(Object provider)
139        {
140          if (provider instanceof ImageReaderSpi)
141            {
142              ImageReaderSpi spi = (ImageReaderSpi) provider;
143    
144              try
145                {
146                  if (spi.canDecodeInput(object))
147                    return true;
148                }
149              catch (IOException e)
150                {
151                  // Return false in this case
152                }
153            }
154          return false;
155        }
156      }
157    
158      private static final class ReaderSuffixFilter implements ServiceRegistry.Filter
159      {
160        private String fileSuffix;
161    
162        public ReaderSuffixFilter(String fileSuffix)
163        {
164          this.fileSuffix = fileSuffix;
165        }
166    
167        public boolean filter(Object provider)
168        {
169          if (provider instanceof ImageReaderSpi)
170            {
171              ImageReaderSpi spi = (ImageReaderSpi) provider;
172              String[] suffixes = spi.getFileSuffixes();
173    
174              for (int i = suffixes.length - 1; i >= 0; --i)
175                if (fileSuffix.equals(suffixes[i]))
176                  return true;
177            }
178    
179          return false;
180        }
181      }
182    
183      private static final class WriterFormatFilter implements ServiceRegistry.Filter
184      {
185        private String formatName;
186    
187        public WriterFormatFilter(String formatName)
188        {
189          this.formatName = formatName;
190        }
191    
192        public boolean filter(Object provider)
193        {
194          if (provider instanceof ImageWriterSpi)
195            {
196              ImageWriterSpi spi = (ImageWriterSpi) provider;
197              String[] formatNames = spi.getFormatNames();
198    
199              for (int i = formatNames.length - 1; i >= 0; --i)
200                if (formatName.equals(formatNames[i]))
201                  return true;
202            }
203    
204          return false;
205        }
206      }
207    
208      private static final class WriterMIMETypeFilter implements ServiceRegistry.Filter
209      {
210        private String MIMEType;
211    
212        public WriterMIMETypeFilter(String MIMEType)
213        {
214          this.MIMEType = MIMEType;
215        }
216    
217        public boolean filter(Object provider)
218        {
219          if (provider instanceof ImageWriterSpi)
220            {
221              ImageWriterSpi spi = (ImageWriterSpi) provider;
222              String[] mimetypes = spi.getMIMETypes();
223    
224              for (int i = mimetypes.length - 1; i >= 0; --i)
225                if (MIMEType.equals(mimetypes[i]))
226                  return true;
227            }
228    
229          return false;
230        }
231      }
232    
233      private static final class WriterSuffixFilter implements ServiceRegistry.Filter
234      {
235        private String fileSuffix;
236    
237        public WriterSuffixFilter(String fileSuffix)
238        {
239          this.fileSuffix = fileSuffix;
240        }
241    
242        public boolean filter(Object provider)
243        {
244          if (provider instanceof ImageWriterSpi)
245            {
246              ImageWriterSpi spi = (ImageWriterSpi) provider;
247              String[] suffixes = spi.getFileSuffixes();
248    
249              for (int i = suffixes.length - 1; i >= 0; --i)
250                if (fileSuffix.equals(suffixes[i]))
251                  return true;
252            }
253    
254          return false;
255        }
256      }
257    
258      private static final class WriterObjectFilter implements ServiceRegistry.Filter
259      {
260        private ImageTypeSpecifier type;
261        private String formatName;
262    
263        public WriterObjectFilter(ImageTypeSpecifier type,
264                                  String formatName)
265        {
266          this.type = type;
267          this.formatName = formatName;
268        }
269    
270        public boolean filter(Object provider)
271        {
272          if (provider instanceof ImageWriterSpi)
273            {
274              ImageWriterSpi spi = (ImageWriterSpi) provider;
275    
276              if (spi.canEncodeImage(type))
277                {
278                  String[] formatNames = spi.getFormatNames();
279                  for (int i = formatNames.length - 1; i >= 0; --i)
280                    if (formatName.equals(formatNames[i]))
281                      return true;
282                }
283            }
284    
285          return false;
286        }
287      }
288    
289      private static final class TranscoderFilter implements ServiceRegistry.Filter
290      {
291        private ImageReader reader;
292        private ImageWriter writer;
293    
294        public TranscoderFilter(ImageReader reader,
295                                ImageWriter writer)
296        {
297          this.reader = reader;
298          this.writer = writer;
299        }
300    
301        public boolean filter(Object provider)
302        {
303          if (provider instanceof ImageTranscoderSpi)
304            {
305              ImageTranscoderSpi spi = (ImageTranscoderSpi) provider;
306    
307              if (spi.getReaderServiceProviderName().equals
308                  (reader.getOriginatingProvider().getClass().getName())
309                  && spi.getWriterServiceProviderName().equals
310                  (writer.getOriginatingProvider().getClass().getName()))
311                return true;
312            }
313    
314          return false;
315        }
316      }
317    
318      private static final class ImageReaderIterator
319        implements Iterator<ImageReader>
320      {
321        Iterator<ImageReaderSpi> it;
322        Object readerExtension;
323    
324        public ImageReaderIterator(Iterator<ImageReaderSpi> it,
325                                   Object readerExtension)
326        {
327          this.it = it;
328          this.readerExtension = readerExtension;
329        }
330    
331        public ImageReaderIterator(Iterator<ImageReaderSpi> it)
332        {
333          this.it = it;
334        }
335    
336        public boolean hasNext()
337        {
338          return it.hasNext();
339        }
340    
341        public ImageReader next()
342        {
343          try
344            {
345              ImageReaderSpi spi = it.next();
346              return (readerExtension == null
347                  ? spi.createReaderInstance()
348                  : spi.createReaderInstance(readerExtension));
349            }
350          catch (IOException e)
351            {
352              return null;
353            }
354        }
355    
356        public void remove()
357        {
358          throw new UnsupportedOperationException();
359        }
360      }
361    
362      private static final class ImageWriterIterator
363        implements Iterator<ImageWriter>
364      {
365        Iterator<ImageWriterSpi> it;
366        Object writerExtension;
367    
368        public ImageWriterIterator(Iterator<ImageWriterSpi> it,
369                                   Object writerExtension)
370        {
371          this.it = it;
372          this.writerExtension = writerExtension;
373        }
374    
375        public ImageWriterIterator(Iterator<ImageWriterSpi> it)
376        {
377          this.it = it;
378        }
379    
380        public boolean hasNext()
381        {
382          return it.hasNext();
383        }
384    
385        public ImageWriter next()
386        {
387          try
388            {
389              ImageWriterSpi spi = it.next();
390              return (writerExtension == null
391                  ? spi.createWriterInstance()
392                  : spi.createWriterInstance(writerExtension));
393            }
394          catch (IOException e)
395            {
396              return null;
397            }
398        }
399    
400        public void remove()
401        {
402          throw new UnsupportedOperationException();
403        }
404      }
405    
406      private static File cacheDirectory;
407      private static boolean useCache = true;
408    
409      private static Iterator<ImageReader> getReadersByFilter(Class<ImageReaderSpi> type,
410                                                              ServiceRegistry.Filter filter,
411                                                              Object readerExtension)
412      {
413        try
414          {
415            Iterator<ImageReaderSpi> it
416              = getRegistry().getServiceProviders(type, filter, true);
417            return new ImageReaderIterator(it, readerExtension);
418          }
419        catch (IllegalArgumentException e)
420          {
421            return Collections.EMPTY_SET.iterator();
422          }
423      }
424    
425      private static Iterator<ImageWriter> getWritersByFilter(Class<ImageWriterSpi> type,
426                                                              ServiceRegistry.Filter filter,
427                                                              Object writerExtension)
428      {
429        try
430          {
431            Iterator<ImageWriterSpi> it
432              = getRegistry().getServiceProviders(type, filter, true);
433            return new ImageWriterIterator(it, writerExtension);
434          }
435        catch (IllegalArgumentException e)
436          {
437            return Collections.EMPTY_SET.iterator();
438          }
439      }
440    
441      /**
442       * Retrieve the current cache directory.
443       *
444       * @return the current cache directory or null if none is set.
445       */
446      public static File getCacheDirectory()
447      {
448        return cacheDirectory;
449      }
450    
451      /**
452       * Retrieve an iterator over all registered readers for the given
453       * format.
454       *
455       * @param formatName an infomal format name (e.g. "jpeg" or "bmp")
456       *
457       * @return an iterator over a collection of image readers
458       *
459       * @exception IllegalArgumentException if formatName is null
460       */
461      public static Iterator<ImageReader> getImageReadersByFormatName(String formatName)
462      {
463        if (formatName == null)
464          throw new IllegalArgumentException("formatName may not be null");
465    
466        return getReadersByFilter(ImageReaderSpi.class,
467                                  new ReaderFormatFilter(formatName),
468                                  formatName);
469      }
470    
471      /**
472       * Retrieve an iterator over all registered readers for the given
473       * MIME type.
474       *
475       * @param MIMEType a MIME specification for an image type
476       * (e.g. "image/jpeg" or "image/x-bmp")
477       *
478       * @return an iterator over a collection of image readers
479       *
480       * @exception IllegalArgumentException if MIMEType is null
481       */
482      public static Iterator<ImageReader> getImageReadersByMIMEType(String MIMEType)
483      {
484        if (MIMEType == null)
485          throw new IllegalArgumentException("MIMEType may not be null");
486    
487        return getReadersByFilter(ImageReaderSpi.class,
488                                  new ReaderMIMETypeFilter(MIMEType),
489                                  MIMEType);
490      }
491    
492      /**
493       * Retrieve an iterator over all registered readers for the given
494       * file suffix.
495       *
496       * @param fileSuffix an image file suffix (e.g. "jpg" or "bmp")
497       *
498       * @return an iterator over a collection of image readers
499       *
500       * @exception IllegalArgumentException if fileSuffix is null
501       */
502      public static Iterator<ImageReader> getImageReadersBySuffix(String fileSuffix)
503      {
504        if (fileSuffix == null)
505          throw new IllegalArgumentException("formatName may not be null");
506    
507        return getReadersByFilter(ImageReaderSpi.class,
508                                  new ReaderSuffixFilter(fileSuffix),
509                                  fileSuffix);
510      }
511    
512      /**
513       * Retrieve an iterator over all registered writers for the given
514       * format.
515       *
516       * @param formatName an infomal format name (e.g. "jpeg" or "bmp")
517       *
518       * @return an iterator over a collection of image writers
519       *
520       * @exception IllegalArgumentException if formatName is null
521       */
522      public static Iterator<ImageWriter> getImageWritersByFormatName(String formatName)
523      {
524        if (formatName == null)
525          throw new IllegalArgumentException("formatName may not be null");
526    
527        return getWritersByFilter(ImageWriterSpi.class,
528                                  new WriterFormatFilter(formatName),
529                                  formatName);
530      }
531    
532      /**
533       * Retrieve an iterator over all registered writers for the given
534       * MIME type.
535       *
536       * @param MIMEType a MIME specification for an image type
537       * (e.g. "image/jpeg" or "image/x-bmp")
538       *
539       * @return an iterator over a collection of image writers
540       *
541       * @exception IllegalArgumentException if MIMEType is null
542       */
543      public static Iterator<ImageWriter> getImageWritersByMIMEType(String MIMEType)
544      {
545        if (MIMEType == null)
546          throw new IllegalArgumentException("MIMEType may not be null");
547    
548        return getWritersByFilter(ImageWriterSpi.class,
549                                  new WriterMIMETypeFilter(MIMEType),
550                                  MIMEType);
551      }
552    
553      /**
554       * Retrieve an iterator over all registered writers for the given
555       * file suffix.
556       *
557       * @param fileSuffix an image file suffix (e.g. "jpg" or "bmp")
558       *
559       * @return an iterator over a collection of image writers
560       *
561       * @exception IllegalArgumentException if fileSuffix is null
562       */
563      public static Iterator<ImageWriter> getImageWritersBySuffix(String fileSuffix)
564      {
565        if (fileSuffix == null)
566          throw new IllegalArgumentException("fileSuffix may not be null");
567    
568        return getWritersByFilter(ImageWriterSpi.class,
569                                  new WriterSuffixFilter(fileSuffix),
570                                  fileSuffix);
571      }
572    
573      /**
574       * Retrieve all the informal format names supported by the
575       * collection of registered image readers.
576       *
577       * @return an array of format names
578       */
579      public static String[] getReaderFormatNames()
580      {
581        try
582          {
583            Iterator it =
584              getRegistry().getServiceProviders(ImageReaderSpi.class, true);
585            ArrayList result = new ArrayList();
586    
587            while (it.hasNext())
588              {
589                ImageReaderSpi spi = (ImageReaderSpi) it.next();
590                String[] names = spi.getFormatNames();
591    
592                for (int i = names.length - 1; i >= 0; --i)
593                  result.add(names[i]);
594              }
595    
596            return (String[]) result.toArray(new String[result.size()]);
597          }
598        catch (IllegalArgumentException e)
599          {
600            return new String[0];
601          }
602      }
603    
604      /**
605       * Retrieve all the MIME types supported by the collection of
606       * registered image readers.
607       *
608       * @return an array of MIME types
609       */
610      public static String[] getReaderMIMETypes()
611      {
612        try
613          {
614            Iterator it =
615              getRegistry().getServiceProviders(ImageReaderSpi.class, true);
616            ArrayList result = new ArrayList();
617    
618            while (it.hasNext())
619              {
620                ImageReaderSpi spi = (ImageReaderSpi) it.next();
621                String[] names = spi.getMIMETypes();
622    
623                for (int i = names.length - 1; i >= 0; --i)
624                  result.add(names[i]);
625              }
626    
627            return (String[]) result.toArray(new String[result.size()]);
628          }
629        catch (IllegalArgumentException e)
630          {
631            return new String[0];
632          }
633      }
634    
635      private static IIORegistry getRegistry()
636      {
637        return IIORegistry.getDefaultInstance();
638      }
639    
640      /**
641       * Check whether or not an on-disk cache is used for image input and
642       * output streams.
643       *
644       * @return true if an on-disk cache is available, false otherwise
645       */
646      public static boolean getUseCache()
647      {
648        return useCache;
649      }
650    
651      /**
652       * Retrieve all the informal format names supported by the
653       * collection of registered image writers.
654       *
655       * @return an array of format names
656       */
657      public static String[] getWriterFormatNames()
658      {
659        try
660          {
661            Iterator it =
662              getRegistry().getServiceProviders(ImageWriterSpi.class, true);
663            ArrayList result = new ArrayList();
664    
665            while (it.hasNext())
666              {
667                ImageWriterSpi spi = (ImageWriterSpi) it.next();
668                String[] names = spi.getFormatNames();
669    
670                for (int i = names.length - 1; i >= 0; --i)
671                  result.add(names[i]);
672              }
673    
674            return (String[]) result.toArray(new String[result.size()]);
675          }
676        catch (IllegalArgumentException e)
677          {
678            return new String[0];
679          }
680      }
681    
682      /**
683       * Retrieve all the MIME types supported by the collection of
684       * registered image writers.
685       *
686       * @return an array of MIME types
687       */
688      public static String[] getWriterMIMETypes()
689      {
690        try
691          {
692            Iterator it =
693              getRegistry().getServiceProviders(ImageWriterSpi.class, true);
694            ArrayList result = new ArrayList();
695    
696            while (it.hasNext())
697              {
698                ImageWriterSpi spi = (ImageWriterSpi) it.next();
699                String[] names = spi.getMIMETypes();
700    
701                for (int i = names.length - 1; i >= 0; --i)
702                  result.add(names[i]);
703              }
704    
705            return (String[]) result.toArray(new String[result.size()]);
706          }
707        catch (IllegalArgumentException e)
708          {
709            return new String[0];
710          }
711      }
712    
713      /**
714       * Rescans the application classpath for ImageIO service providers
715       * and registers them.
716       */
717      public static void scanForPlugins()
718      {
719        IIORegistry.getDefaultInstance().registerApplicationClasspathSpis();
720      }
721    
722      /**
723       * Set the directory to be used for caching image data.  A null
724       * argument means to use the default system temporary directory.
725       * This cache directory is only used if getUseCache returns true.
726       *
727       * @param cacheDirectory the directory where image data should be
728       * cached
729       *
730       * @exception IllegalArgumentException if cacheDirectory is not a
731       * directory
732       */
733      public static void setCacheDirectory(File cacheDirectory)
734      {
735        // FIXME: add SecurityManager call
736        if (cacheDirectory != null)
737          {
738            if (!cacheDirectory.isDirectory())
739              throw new IllegalArgumentException("cacheDirectory must be a directory");
740    
741            cacheDirectory.canWrite();
742          }
743    
744        ImageIO.cacheDirectory = cacheDirectory;
745      }
746    
747      /**
748       * Control whether or not an on-disk cache is used.  This cache is
749       * used to store input or output data from an image data stream when
750       * data in the stream needs to be re-processed.
751       *
752       * If useCache is false the cache will be stored in memory.  Doing
753       * so eliminates file creation and deletion overhead.  The default
754       * is to use an on-disk cache.
755       *
756       * @param useCache true to use an on-disk cache, false otherwise
757       */
758      public static void setUseCache(boolean useCache)
759      {
760        ImageIO.useCache = useCache;
761      }
762    
763      /**
764       * Write an image to a file using a registered writer that supports
765       * the given format, overwriting the file if it already exists.
766       *
767       * @param im the image data to write
768       * @param formatName an informal description of the output format
769       * @param output the file to which the image will be written
770       *
771       * @return false if no registered writer supports the given format,
772       * true otherwise
773       *
774       * @exception IllegalArgumentException if any argument is null
775       * @exception IOException if a writing error occurs
776       */
777      public static boolean write(RenderedImage im,
778                                  String formatName,
779                                  File output)
780        throws IOException
781      {
782        if (im == null || formatName == null || output == null)
783          throw new IllegalArgumentException ("null argument");
784    
785        return write(im, formatName, new FileOutputStream(output));
786      }
787    
788      /**
789       * Write an image to an output stream using a registered writer that
790       * supports the given format.
791       *
792       * @param im the image data to write
793       * @param formatName an informal description of the output format
794       * @param output the output stream to which the image will be
795       * written
796       *
797       * @return false if no registered writer supports the given format,
798       * true otherwise
799       *
800       * @exception IllegalArgumentException if any argument is null
801       * @exception IOException if a writing error occurs
802       */
803      public static boolean write(RenderedImage im,
804                                  String formatName,
805                                  OutputStream output)
806        throws IOException
807      {
808        if (im == null || formatName == null || output == null)
809          throw new IllegalArgumentException ("null argument");
810    
811        return write(im, formatName, new MemoryCacheImageOutputStream(output));
812      }
813    
814      /**
815       * Write an image to an ImageOutputStream using a registered writer
816       * that supports the given format.  Image data is written starting
817       * at the ImageOutputStream's current stream pointer, overwriting
818       * any existing data.
819       *
820       * @param im the image data to write
821       * @param formatName an informal description of the output format
822       * @param output the image output stream to which the image will be
823       * written
824       *
825       * @return false if no registered writer supports the given format,
826       * true otherwise
827       *
828       * @exception IllegalArgumentException if any argument is null
829       * @exception IOException if a writing error occurs
830       */
831      public static boolean write(RenderedImage im,
832                                  String formatName,
833                                  ImageOutputStream output)
834        throws IOException
835      {
836        if (im == null || formatName == null || output == null)
837          throw new IllegalArgumentException ("null argument");
838    
839        Iterator writers = getImageWritersByFormatName(formatName);
840        IIOImage img = new IIOImage(im, null, null);
841        while (writers.hasNext())
842          {
843            ImageWriter w = (ImageWriter) writers.next();
844            try
845              {
846                w.setOutput(output);
847              }
848            catch (IllegalArgumentException e)
849              {
850                continue;
851              }
852    
853            w.write(null, img, null);
854            w.dispose();
855            output.close();
856            return true;
857          }
858        return false;
859      }
860    
861      /**
862       * Create a buffered image from an image input stream.  An image
863       * reader that supports the given image data is automatically
864       * selected from the collection of registered readers.  If no
865       * registered reader can handle the input format, null is returned.
866       *
867       * @param stream the image input stream from which to read image
868       * data
869       *
870       * @return a new buffered image created from the given image data,
871       * or null
872       *
873       * @exception IllegalArgumentException if stream is null
874       * @exception IOException if a reading error occurs
875       */
876      public static BufferedImage read(ImageInputStream stream)
877        throws IOException
878      {
879        if (stream == null)
880          throw new IllegalArgumentException("null argument");
881    
882        Iterator providers = getRegistry().getServiceProviders(ImageReaderSpi.class, true);
883        while (providers.hasNext())
884          {
885            ImageReaderSpi spi = (ImageReaderSpi) providers.next();
886            if (spi.canDecodeInput(stream))
887              {
888                ImageReader reader = spi.createReaderInstance();
889                reader.setInput(stream);
890                return reader.read(0, null);
891              }
892          }
893        return null;
894      }
895    
896      /**
897       * Create a buffered image from a URL.  An image reader that
898       * supports the given image data is automatically selected from the
899       * collection of registered readers.  If no registered reader can
900       * handle the input format, null is returned.
901       *
902       * The image data will be cached in the current cache directory if
903       * caching is enabled.
904       *
905       * This method does not locate readers that read data directly from
906       * a URL.  To locate such readers manually, use IIORegistry and
907       * ImageReaderSpi.
908       *
909       * @param input the URL from which to retrieve the image file
910       *
911       * @return a new buffered image created from the given image URL, or
912       * null
913       *
914       * @exception IllegalArgumentException if input is null
915       * @exception IOException if a reading error occurs
916       */
917      public static BufferedImage read(URL input)
918        throws IOException
919      {
920        if (input == null)
921          throw new IllegalArgumentException("null argument");
922    
923        return read(input.openStream());
924      }
925    
926      /**
927       * Create a buffered image from an input stream.  An image reader
928       * that supports the given image data is automatically selected from
929       * the collection of registered readers.  If no registered reader
930       * can handle the input format, null is returned.
931       *
932       * The image data will be cached in the current cache directory if
933       * caching is enabled.
934       *
935       * This method does not locate readers that read data directly from
936       * an input stream.  To locate such readers manually, use
937       * IIORegistry and ImageReaderSpi.
938       *
939       * @param input the input stream from which to read the image data
940       *
941       * @return a new buffered image created from the given input stream,
942       * or null
943       *
944       * @exception IllegalArgumentException if input is null
945       * @exception IOException if a reading error occurs
946       */
947      public static BufferedImage read(InputStream input)
948        throws IOException
949      {
950        if (input == null)
951          throw new IllegalArgumentException("null argument");
952    
953        return read(new MemoryCacheImageInputStream(input));
954      }
955    
956      /**
957       * Create a buffered image from a file.  An image reader that
958       * supports the given image data is automatically selected from the
959       * collection of registered readers.  If no registered reader can
960       * handle the input format, null is returned.
961       *
962       * The image data will be cached in the current cache directory if
963       * caching is enabled.
964       *
965       * This method does not locate readers that read data directly from
966       * a file.  To locate such readers manually, use IIORegistry and
967       * ImageReaderSpi.
968       *
969       * @param input the file from which to read image data
970       *
971       * @return a new buffered image created from the given image file,
972       * or null
973       *
974       * @exception IllegalArgumentException if input is null
975       * @exception IOException if a reading error occurs
976       */
977      public static BufferedImage read(File input)
978        throws IOException
979      {
980        if (input == null)
981          throw new IllegalArgumentException("null argument");
982    
983        return read(new FileInputStream(input));
984      }
985    
986      /**
987       * Create an image input stream from the given object.  The
988       * collection of ImageInputStreamSpis registered with the
989       * IIORegistry is searched for an image input stream that can take
990       * input from the given object.  null is returned if no such SPI is
991       * registered.
992       *
993       * The image data will be cached in the current cache directory if
994       * caching is enabled.
995       *
996       * @param input an object from which to read image data
997       *
998       * @return an ImageInputStream that can read data from input, or
999       * null
1000       *
1001       * @exception IllegalArgumentException if input is null
1002       * @exception IOException if caching is required but not enabled
1003       */
1004      public static ImageInputStream createImageInputStream (Object input)
1005        throws IOException
1006      {
1007        if (input == null)
1008          throw new IllegalArgumentException ("null argument");
1009    
1010        Iterator spis = getRegistry().getServiceProviders
1011          (ImageInputStreamSpi.class, true);
1012    
1013        ImageInputStreamSpi foundSpi = null;
1014    
1015        while(spis.hasNext())
1016          {
1017            ImageInputStreamSpi spi = (ImageInputStreamSpi) spis.next();
1018    
1019            if (input.getClass().equals(spi.getInputClass()))
1020              {
1021                foundSpi = spi;
1022                break;
1023              }
1024          }
1025    
1026        return foundSpi == null ? null :
1027          foundSpi.createInputStreamInstance (input,
1028                                              getUseCache(),
1029                                              getCacheDirectory());
1030      }
1031    
1032      /**
1033       * Create an image output stream from the given object.  The
1034       * collection of ImageOutputStreamSpis registered with the
1035       * IIORegistry is searched for an image output stream that can send
1036       * output to the given object.  null is returned if no such SPI is
1037       * registered.
1038       *
1039       * The image data will be cached in the current cache directory if
1040       * caching is enabled.
1041       *
1042       * @param output an object to which to write image data
1043       *
1044       * @return an ImageOutputStream that can send data to output, or
1045       * null
1046       *
1047       * @exception IllegalArgumentException if output is null
1048       * @exception IOException if caching is required but not enabled
1049       */
1050      public static ImageOutputStream createImageOutputStream (Object output)
1051        throws IOException
1052      {
1053        if (output == null)
1054          throw new IllegalArgumentException ("null argument");
1055    
1056        Iterator spis = getRegistry().getServiceProviders
1057          (ImageOutputStreamSpi.class, true);
1058    
1059        ImageOutputStreamSpi foundSpi = null;
1060    
1061        while(spis.hasNext())
1062          {
1063            ImageOutputStreamSpi spi = (ImageOutputStreamSpi) spis.next();
1064    
1065            if (output.getClass().equals(spi.getOutputClass()))
1066              {
1067                foundSpi = spi;
1068                break;
1069              }
1070          }
1071    
1072        return foundSpi == null ? null :
1073          foundSpi.createOutputStreamInstance (output,
1074                                               getUseCache(),
1075                                               getCacheDirectory());
1076      }
1077    
1078      /**
1079       * Retrieve an image reader corresponding to an image writer, or
1080       * null if writer is not registered or if no corresponding reader is
1081       * registered.
1082       *
1083       * @param writer a registered image writer
1084       *
1085       * @return an image reader corresponding to writer, or null
1086       *
1087       * @exception IllegalArgumentException if writer is null
1088       */
1089      public static ImageReader getImageReader (ImageWriter writer)
1090      {
1091        if (writer == null)
1092          throw new IllegalArgumentException ("null argument");
1093    
1094        ImageWriterSpi spi = writer.getOriginatingProvider();
1095    
1096        String[] readerSpiNames = spi.getImageReaderSpiNames();
1097    
1098        ImageReader r = null;
1099    
1100        if (readerSpiNames != null)
1101          {
1102            try
1103              {
1104                Class readerClass = Class.forName (readerSpiNames[0]);
1105                r = (ImageReader) readerClass.newInstance ();
1106              }
1107            catch (Exception e)
1108              {
1109                return null;
1110              }
1111          }
1112        return r;
1113      }
1114    
1115      /**
1116       * Retrieve an iterator over the collection of registered image
1117       * readers that support reading data from the given object.
1118       *
1119       * @param input the object for which to retrieve image readers
1120       *
1121       * @return an iterator over a collection of image readers
1122       */
1123      public static Iterator<ImageReader> getImageReaders (Object input)
1124      {
1125        if (input == null)
1126          throw new IllegalArgumentException ("null argument");
1127    
1128        Iterator<ImageReaderSpi> spiIterator
1129          = getRegistry().getServiceProviders (ImageReaderSpi.class,
1130                                               new ReaderObjectFilter(input),
1131                                               true);
1132        return new ImageReaderIterator(spiIterator);
1133      }
1134    
1135      /**
1136       * Retrieve an iterator over the collection of registered image
1137       * writers that support writing images of the given type and in the
1138       * given format.
1139       *
1140       * @param type the output image's colour and sample models
1141       * @param formatName the output image format
1142       *
1143       * @return an iterator over a collection of image writers
1144       */
1145      public static Iterator<ImageWriter> getImageWriters (ImageTypeSpecifier type,
1146                                              String formatName)
1147      {
1148        if (type == null || formatName == null)
1149          throw new IllegalArgumentException ("null argument");
1150    
1151        final Iterator<ImageWriterSpi> spiIterator
1152          = getRegistry().getServiceProviders (ImageWriterSpi.class,
1153                                               new WriterObjectFilter(type,
1154                                                                      formatName),
1155                                                                      true);
1156        return new ImageWriterIterator(spiIterator);
1157      }
1158    
1159      /**
1160       * Retrieve an image writer corresponding to an image reader, or
1161       * null if reader is not registered or if no corresponding writer is
1162       * registered.  This method is useful for preserving metadata
1163       * without needing to understand its format, since the returned
1164       * writer will be able to write, unchanged, the metadata passed to
1165       * it by the reader.
1166       *
1167       * @param reader a registered image reader
1168       *
1169       * @return an image writer corresponding to reader, or null
1170       *
1171       * @exception IllegalArgumentException if reader is null
1172       */
1173      public static ImageWriter getImageWriter (ImageReader reader)
1174      {
1175        if (reader == null)
1176          throw new IllegalArgumentException ("null argument");
1177    
1178        ImageReaderSpi spi = reader.getOriginatingProvider();
1179    
1180        String[] writerSpiNames = spi.getImageWriterSpiNames();
1181    
1182        ImageWriter w = null;
1183    
1184        if (writerSpiNames != null)
1185          {
1186            try
1187              {
1188                Class writerClass = Class.forName (writerSpiNames[0]);
1189                w = (ImageWriter) writerClass.newInstance ();
1190              }
1191            catch (Exception e)
1192              {
1193                return null;
1194              }
1195          }
1196        return w;
1197      }
1198    
1199      /**
1200       * Retrieve an iterator over a collection of image transcoders that
1201       * support transcoding from the given image reader's metadata format
1202       * to the given writer's metadata format.
1203       *
1204       * @param reader an image reader
1205       * @param writer an image writer
1206       *
1207       * @return an iterator over a collection of image transcoders
1208       *
1209       * @exception IllegalArgumentException if either reader or writer is
1210       * null
1211       */
1212      public static Iterator<ImageTranscoder> getImageTranscoders (ImageReader reader,
1213                                                                   ImageWriter writer)
1214      {
1215        if (reader == null || writer == null)
1216          throw new IllegalArgumentException ("null argument");
1217    
1218        final Iterator<ImageTranscoderSpi> spiIterator
1219          = getRegistry().getServiceProviders (ImageTranscoderSpi.class,
1220                                               new TranscoderFilter (reader,
1221                                                                     writer),
1222                                               true);
1223        return new Iterator<ImageTranscoder>()
1224        {
1225          public boolean hasNext()
1226          {
1227            return spiIterator.hasNext();
1228          }
1229    
1230          public ImageTranscoder next()
1231          {
1232            return spiIterator.next().createTranscoderInstance();
1233          }
1234    
1235          public void remove()
1236          {
1237            throw new UnsupportedOperationException();
1238          }
1239        };
1240      }
1241    }