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            output.close();
855            return true;
856          }
857        return false;
858      }
859    
860      /**
861       * Create a buffered image from an image input stream.  An image
862       * reader that supports the given image data is automatically
863       * selected from the collection of registered readers.  If no
864       * registered reader can handle the input format, null is returned.
865       *
866       * @param stream the image input stream from which to read image
867       * data
868       *
869       * @return a new buffered image created from the given image data,
870       * or null
871       *
872       * @exception IllegalArgumentException if stream is null
873       * @exception IOException if a reading error occurs
874       */
875      public static BufferedImage read(ImageInputStream stream)
876        throws IOException
877      {
878        if (stream == null)
879          throw new IllegalArgumentException("null argument");
880    
881        Iterator providers = getRegistry().getServiceProviders(ImageReaderSpi.class, true);
882        while (providers.hasNext())
883          {
884            ImageReaderSpi spi = (ImageReaderSpi) providers.next();
885            if (spi.canDecodeInput(stream))
886              {
887                ImageReader reader = spi.createReaderInstance();
888                reader.setInput(stream);
889                return reader.read(0, null);
890              }
891          }
892        return null;
893      }
894    
895      /**
896       * Create a buffered image from a URL.  An image reader that
897       * supports the given image data is automatically selected from the
898       * collection of registered readers.  If no registered reader can
899       * handle the input format, null is returned.
900       *
901       * The image data will be cached in the current cache directory if
902       * caching is enabled.
903       *
904       * This method does not locate readers that read data directly from
905       * a URL.  To locate such readers manually, use IIORegistry and
906       * ImageReaderSpi.
907       *
908       * @param input the URL from which to retrieve the image file
909       *
910       * @return a new buffered image created from the given image URL, or
911       * null
912       *
913       * @exception IllegalArgumentException if input is null
914       * @exception IOException if a reading error occurs
915       */
916      public static BufferedImage read(URL input)
917        throws IOException
918      {
919        if (input == null)
920          throw new IllegalArgumentException("null argument");
921    
922        return read(input.openStream());
923      }
924    
925      /**
926       * Create a buffered image from an input stream.  An image reader
927       * that supports the given image data is automatically selected from
928       * the collection of registered readers.  If no registered reader
929       * can handle the input format, null is returned.
930       *
931       * The image data will be cached in the current cache directory if
932       * caching is enabled.
933       *
934       * This method does not locate readers that read data directly from
935       * an input stream.  To locate such readers manually, use
936       * IIORegistry and ImageReaderSpi.
937       *
938       * @param input the input stream from which to read the image data
939       *
940       * @return a new buffered image created from the given input stream,
941       * or null
942       *
943       * @exception IllegalArgumentException if input is null
944       * @exception IOException if a reading error occurs
945       */
946      public static BufferedImage read(InputStream input)
947        throws IOException
948      {
949        if (input == null)
950          throw new IllegalArgumentException("null argument");
951    
952        return read(new MemoryCacheImageInputStream(input));
953      }
954    
955      /**
956       * Create a buffered image from a file.  An image reader that
957       * supports the given image data is automatically selected from the
958       * collection of registered readers.  If no registered reader can
959       * handle the input format, null is returned.
960       *
961       * The image data will be cached in the current cache directory if
962       * caching is enabled.
963       *
964       * This method does not locate readers that read data directly from
965       * a file.  To locate such readers manually, use IIORegistry and
966       * ImageReaderSpi.
967       *
968       * @param input the file from which to read image data
969       *
970       * @return a new buffered image created from the given image file,
971       * or null
972       *
973       * @exception IllegalArgumentException if input is null
974       * @exception IOException if a reading error occurs
975       */
976      public static BufferedImage read(File input)
977        throws IOException
978      {
979        if (input == null)
980          throw new IllegalArgumentException("null argument");
981    
982        return read(new FileInputStream(input));
983      }
984    
985      /**
986       * Create an image input stream from the given object.  The
987       * collection of ImageInputStreamSpis registered with the
988       * IIORegistry is searched for an image input stream that can take
989       * input from the given object.  null is returned if no such SPI is
990       * registered.
991       *
992       * The image data will be cached in the current cache directory if
993       * caching is enabled.
994       *
995       * @param input an object from which to read image data
996       *
997       * @return an ImageInputStream that can read data from input, or
998       * null
999       *
1000       * @exception IllegalArgumentException if input is null
1001       * @exception IOException if caching is required but not enabled
1002       */
1003      public static ImageInputStream createImageInputStream (Object input)
1004        throws IOException
1005      {
1006        if (input == null)
1007          throw new IllegalArgumentException ("null argument");
1008    
1009        Iterator spis = getRegistry().getServiceProviders
1010          (ImageInputStreamSpi.class, true);
1011    
1012        ImageInputStreamSpi foundSpi = null;
1013    
1014        while(spis.hasNext())
1015          {
1016            ImageInputStreamSpi spi = (ImageInputStreamSpi) spis.next();
1017    
1018            if (input.getClass().equals(spi.getInputClass()))
1019              {
1020                foundSpi = spi;
1021                break;
1022              }
1023          }
1024    
1025        return foundSpi == null ? null :
1026          foundSpi.createInputStreamInstance (input,
1027                                              getUseCache(),
1028                                              getCacheDirectory());
1029      }
1030    
1031      /**
1032       * Create an image output stream from the given object.  The
1033       * collection of ImageOutputStreamSpis registered with the
1034       * IIORegistry is searched for an image output stream that can send
1035       * output to the given object.  null is returned if no such SPI is
1036       * registered.
1037       *
1038       * The image data will be cached in the current cache directory if
1039       * caching is enabled.
1040       *
1041       * @param output an object to which to write image data
1042       *
1043       * @return an ImageOutputStream that can send data to output, or
1044       * null
1045       *
1046       * @exception IllegalArgumentException if output is null
1047       * @exception IOException if caching is required but not enabled
1048       */
1049      public static ImageOutputStream createImageOutputStream (Object output)
1050        throws IOException
1051      {
1052        if (output == null)
1053          throw new IllegalArgumentException ("null argument");
1054    
1055        Iterator spis = getRegistry().getServiceProviders
1056          (ImageOutputStreamSpi.class, true);
1057    
1058        ImageOutputStreamSpi foundSpi = null;
1059    
1060        while(spis.hasNext())
1061          {
1062            ImageOutputStreamSpi spi = (ImageOutputStreamSpi) spis.next();
1063    
1064            if (output.getClass().equals(spi.getOutputClass()))
1065              {
1066                foundSpi = spi;
1067                break;
1068              }
1069          }
1070    
1071        return foundSpi == null ? null :
1072          foundSpi.createOutputStreamInstance (output,
1073                                               getUseCache(),
1074                                               getCacheDirectory());
1075      }
1076    
1077      /**
1078       * Retrieve an image reader corresponding to an image writer, or
1079       * null if writer is not registered or if no corresponding reader is
1080       * registered.
1081       *
1082       * @param writer a registered image writer
1083       *
1084       * @return an image reader corresponding to writer, or null
1085       *
1086       * @exception IllegalArgumentException if writer is null
1087       */
1088      public static ImageReader getImageReader (ImageWriter writer)
1089      {
1090        if (writer == null)
1091          throw new IllegalArgumentException ("null argument");
1092    
1093        ImageWriterSpi spi = writer.getOriginatingProvider();
1094    
1095        String[] readerSpiNames = spi.getImageReaderSpiNames();
1096    
1097        ImageReader r = null;
1098    
1099        if (readerSpiNames != null)
1100          {
1101            try
1102              {
1103                Class readerClass = Class.forName (readerSpiNames[0]);
1104                r = (ImageReader) readerClass.newInstance ();
1105              }
1106            catch (Exception e)
1107              {
1108                return null;
1109              }
1110          }
1111        return r;
1112      }
1113    
1114      /**
1115       * Retrieve an iterator over the collection of registered image
1116       * readers that support reading data from the given object.
1117       *
1118       * @param input the object for which to retrieve image readers
1119       *
1120       * @return an iterator over a collection of image readers
1121       */
1122      public static Iterator<ImageReader> getImageReaders (Object input)
1123      {
1124        if (input == null)
1125          throw new IllegalArgumentException ("null argument");
1126    
1127        Iterator<ImageReaderSpi> spiIterator
1128          = getRegistry().getServiceProviders (ImageReaderSpi.class,
1129                                               new ReaderObjectFilter(input),
1130                                               true);
1131        return new ImageReaderIterator(spiIterator);
1132      }
1133    
1134      /**
1135       * Retrieve an iterator over the collection of registered image
1136       * writers that support writing images of the given type and in the
1137       * given format.
1138       *
1139       * @param type the output image's colour and sample models
1140       * @param formatName the output image format
1141       *
1142       * @return an iterator over a collection of image writers
1143       */
1144      public static Iterator<ImageWriter> getImageWriters (ImageTypeSpecifier type,
1145                                              String formatName)
1146      {
1147        if (type == null || formatName == null)
1148          throw new IllegalArgumentException ("null argument");
1149    
1150        final Iterator<ImageWriterSpi> spiIterator
1151          = getRegistry().getServiceProviders (ImageWriterSpi.class,
1152                                               new WriterObjectFilter(type,
1153                                                                      formatName),
1154                                                                      true);
1155        return new ImageWriterIterator(spiIterator);
1156      }
1157    
1158      /**
1159       * Retrieve an image writer corresponding to an image reader, or
1160       * null if reader is not registered or if no corresponding writer is
1161       * registered.  This method is useful for preserving metadata
1162       * without needing to understand its format, since the returned
1163       * writer will be able to write, unchanged, the metadata passed to
1164       * it by the reader.
1165       *
1166       * @param reader a registered image reader
1167       *
1168       * @return an image writer corresponding to reader, or null
1169       *
1170       * @exception IllegalArgumentException if reader is null
1171       */
1172      public static ImageWriter getImageWriter (ImageReader reader)
1173      {
1174        if (reader == null)
1175          throw new IllegalArgumentException ("null argument");
1176    
1177        ImageReaderSpi spi = reader.getOriginatingProvider();
1178    
1179        String[] writerSpiNames = spi.getImageWriterSpiNames();
1180    
1181        ImageWriter w = null;
1182    
1183        if (writerSpiNames != null)
1184          {
1185            try
1186              {
1187                Class writerClass = Class.forName (writerSpiNames[0]);
1188                w = (ImageWriter) writerClass.newInstance ();
1189              }
1190            catch (Exception e)
1191              {
1192                return null;
1193              }
1194          }
1195        return w;
1196      }
1197    
1198      /**
1199       * Retrieve an iterator over a collection of image transcoders that
1200       * support transcoding from the given image reader's metadata format
1201       * to the given writer's metadata format.
1202       *
1203       * @param reader an image reader
1204       * @param writer an image writer
1205       *
1206       * @return an iterator over a collection of image transcoders
1207       *
1208       * @exception IllegalArgumentException if either reader or writer is
1209       * null
1210       */
1211      public static Iterator<ImageTranscoder> getImageTranscoders (ImageReader reader,
1212                                                                   ImageWriter writer)
1213      {
1214        if (reader == null || writer == null)
1215          throw new IllegalArgumentException ("null argument");
1216    
1217        final Iterator<ImageTranscoderSpi> spiIterator
1218          = getRegistry().getServiceProviders (ImageTranscoderSpi.class,
1219                                               new TranscoderFilter (reader,
1220                                                                     writer),
1221                                               true);
1222        return new Iterator<ImageTranscoder>()
1223        {
1224          public boolean hasNext()
1225          {
1226            return spiIterator.hasNext();
1227          }
1228          
1229          public ImageTranscoder next()
1230          {
1231            return spiIterator.next().createTranscoderInstance();
1232          }
1233          
1234          public void remove()
1235          {
1236            throw new UnsupportedOperationException();
1237          }
1238        };
1239      }
1240    }