Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/imageio/ImageIO.java
38829 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.imageio;2627import java.awt.image.BufferedImage;28import java.awt.image.RenderedImage;29import java.io.File;30import java.io.FilePermission;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;5556/**57* A class containing static convenience methods for locating58* <code>ImageReader</code>s and <code>ImageWriter</code>s, and59* performing simple encoding and decoding.60*61*/62public final class ImageIO {6364private static final IIORegistry theRegistry =65IIORegistry.getDefaultInstance();6667/**68* Constructor is private to prevent instantiation.69*/70private ImageIO() {}7172/**73* Scans for plug-ins on the application class path,74* loads their service provider classes, and registers a service75* provider instance for each one found with the76* <code>IIORegistry</code>.77*78* <p>This method is needed because the application class path can79* theoretically change, or additional plug-ins may become available.80* Rather than re-scanning the classpath on every invocation of the81* API, the class path is scanned automatically only on the first82* invocation. Clients can call this method to prompt a re-scan.83* Thus this method need only be invoked by sophisticated applications84* which dynamically make new plug-ins available at runtime.85*86* <p> The <code>getResources</code> method of the context87* <code>ClassLoader</code> is used locate JAR files containing88* files named89* <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>, or93* <code>ImageOutputStreamSpi</code>, along the application class94* path.95*96* <p> The contents of the located files indicate the names of97* actual implementation classes which implement the98* aforementioned service provider interfaces; the default class99* loader is then used to load each of these classes and to100* instantiate an instance of each class, which is then placed101* into the registry for later retrieval.102*103* <p> The exact set of locations searched depends on the104* implementation of the Java runtime environment.105*106* @see ClassLoader#getResources107*/108public static void scanForPlugins() {109theRegistry.registerApplicationClasspathSpis();110}111112// ImageInputStreams113114/**115* A class to hold information about caching. Each116* <code>ThreadGroup</code> will have its own copy117* via the <code>AppContext</code> mechanism.118*/119static class CacheInfo {120boolean useCache = true;121File cacheDirectory = null;122Boolean hasPermission = null;123124public CacheInfo() {}125126public boolean getUseCache() {127return useCache;128}129130public void setUseCache(boolean useCache) {131this.useCache = useCache;132}133134public File getCacheDirectory() {135return cacheDirectory;136}137138public void setCacheDirectory(File cacheDirectory) {139this.cacheDirectory = cacheDirectory;140}141142public Boolean getHasPermission() {143return hasPermission;144}145146public void setHasPermission(Boolean hasPermission) {147this.hasPermission = hasPermission;148}149}150151/**152* Returns the <code>CacheInfo</code> object associated with this153* <code>ThreadGroup</code>.154*/155private static synchronized CacheInfo getCacheInfo() {156AppContext context = AppContext.getAppContext();157CacheInfo info = (CacheInfo)context.get(CacheInfo.class);158if (info == null) {159info = new CacheInfo();160context.put(CacheInfo.class, info);161}162return info;163}164165/**166* Returns the default temporary (cache) directory as defined by the167* java.io.tmpdir system property.168*/169private static String getTempDir() {170GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");171return (String)AccessController.doPrivileged(a);172}173174/**175* Determines whether the caller has write access to the cache176* directory, stores the result in the <code>CacheInfo</code> object,177* and returns the decision. This method helps to prevent mysterious178* SecurityExceptions to be thrown when this convenience class is used179* in an applet, for example.180*/181private static boolean hasCachePermission() {182Boolean hasPermission = getCacheInfo().getHasPermission();183184if (hasPermission != null) {185return hasPermission.booleanValue();186} else {187try {188SecurityManager security = System.getSecurityManager();189if (security != null) {190File cachedir = getCacheDirectory();191String cachepath;192193if (cachedir != null) {194cachepath = cachedir.getPath();195} else {196cachepath = getTempDir();197198if (cachepath == null || cachepath.isEmpty()) {199getCacheInfo().setHasPermission(Boolean.FALSE);200return false;201}202}203204// we have to check whether we can read, write,205// and delete cache files.206// So, compose cache file path and check it.207String filepath = cachepath;208if (!filepath.endsWith(File.separator)) {209filepath += File.separator;210}211filepath += "*";212213security.checkPermission(new FilePermission(filepath, "read, write, delete"));214}215} catch (SecurityException e) {216getCacheInfo().setHasPermission(Boolean.FALSE);217return false;218}219220getCacheInfo().setHasPermission(Boolean.TRUE);221return true;222}223}224225/**226* Sets a flag indicating whether a disk-based cache file should227* be used when creating <code>ImageInputStream</code>s and228* <code>ImageOutputStream</code>s.229*230* <p> When reading from a standard <code>InputStream</code>, it231* may be necessary to save previously read information in a cache232* since the underlying stream does not allow data to be re-read.233* Similarly, when writing to a standard234* <code>OutputStream</code>, a cache may be used to allow a235* previously written value to be changed before flushing it to236* the final destination.237*238* <p> The cache may reside in main memory or on disk. Setting239* this flag to <code>false</code> disallows the use of disk for240* future streams, which may be advantageous when working with241* small images, as the overhead of creating and destroying files242* is removed.243*244* <p> On startup, the value is set to <code>true</code>.245*246* @param useCache a <code>boolean</code> indicating whether a247* cache file should be used, in cases where it is optional.248*249* @see #getUseCache250*/251public static void setUseCache(boolean useCache) {252getCacheInfo().setUseCache(useCache);253}254255/**256* Returns the current value set by <code>setUseCache</code>, or257* <code>true</code> if no explicit setting has been made.258*259* @return true if a disk-based cache may be used for260* <code>ImageInputStream</code>s and261* <code>ImageOutputStream</code>s.262*263* @see #setUseCache264*/265public static boolean getUseCache() {266return getCacheInfo().getUseCache();267}268269/**270* Sets the directory where cache files are to be created. A271* value of <code>null</code> indicates that the system-dependent272* default temporary-file directory is to be used. If273* <code>getUseCache</code> returns false, this value is ignored.274*275* @param cacheDirectory a <code>File</code> specifying a directory.276*277* @see File#createTempFile(String, String, File)278*279* @exception SecurityException if the security manager denies280* access to the directory.281* @exception IllegalArgumentException if <code>cacheDir</code> is282* non-<code>null</code> but is not a directory.283*284* @see #getCacheDirectory285*/286public static void setCacheDirectory(File cacheDirectory) {287if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {288throw new IllegalArgumentException("Not a directory!");289}290getCacheInfo().setCacheDirectory(cacheDirectory);291getCacheInfo().setHasPermission(null);292}293294/**295* Returns the current value set by296* <code>setCacheDirectory</code>, or <code>null</code> if no297* explicit setting has been made.298*299* @return a <code>File</code> indicating the directory where300* cache files will be created, or <code>null</code> to indicate301* the system-dependent default temporary-file directory.302*303* @see #setCacheDirectory304*/305public static File getCacheDirectory() {306return getCacheInfo().getCacheDirectory();307}308309/**310* Returns an <code>ImageInputStream</code> that will take its311* input from the given <code>Object</code>. The set of312* <code>ImageInputStreamSpi</code>s registered with the313* <code>IIORegistry</code> class is queried and the first one314* that is able to take input from the supplied object is used to315* create the returned <code>ImageInputStream</code>. If no316* suitable <code>ImageInputStreamSpi</code> exists,317* <code>null</code> is returned.318*319* <p> The current cache settings from <code>getUseCache</code>and320* <code>getCacheDirectory</code> will be used to control caching.321*322* @param input an <code>Object</code> to be used as an input323* source, such as a <code>File</code>, readable324* <code>RandomAccessFile</code>, or <code>InputStream</code>.325*326* @return an <code>ImageInputStream</code>, or <code>null</code>.327*328* @exception IllegalArgumentException if <code>input</code>329* is <code>null</code>.330* @exception IOException if a cache file is needed but cannot be331* created.332*333* @see javax.imageio.spi.ImageInputStreamSpi334*/335public static ImageInputStream createImageInputStream(Object input)336throws IOException {337if (input == null) {338throw new IllegalArgumentException("input == null!");339}340341Iterator iter;342// Ensure category is present343try {344iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class,345true);346} catch (IllegalArgumentException e) {347return null;348}349350boolean usecache = getUseCache() && hasCachePermission();351352while (iter.hasNext()) {353ImageInputStreamSpi spi = (ImageInputStreamSpi)iter.next();354if (spi.getInputClass().isInstance(input)) {355try {356return spi.createInputStreamInstance(input,357usecache,358getCacheDirectory());359} catch (IOException e) {360throw new IIOException("Can't create cache file!", e);361}362}363}364365return null;366}367368// ImageOutputStreams369370/**371* Returns an <code>ImageOutputStream</code> that will send its372* output to the given <code>Object</code>. The set of373* <code>ImageOutputStreamSpi</code>s registered with the374* <code>IIORegistry</code> class is queried and the first one375* that is able to send output from the supplied object is used to376* create the returned <code>ImageOutputStream</code>. If no377* suitable <code>ImageOutputStreamSpi</code> exists,378* <code>null</code> is returned.379*380* <p> The current cache settings from <code>getUseCache</code>and381* <code>getCacheDirectory</code> will be used to control caching.382*383* @param output an <code>Object</code> to be used as an output384* destination, such as a <code>File</code>, writable385* <code>RandomAccessFile</code>, or <code>OutputStream</code>.386*387* @return an <code>ImageOutputStream</code>, or388* <code>null</code>.389*390* @exception IllegalArgumentException if <code>output</code> is391* <code>null</code>.392* @exception IOException if a cache file is needed but cannot be393* created.394*395* @see javax.imageio.spi.ImageOutputStreamSpi396*/397public static ImageOutputStream createImageOutputStream(Object output)398throws IOException {399if (output == null) {400throw new IllegalArgumentException("output == null!");401}402403Iterator iter;404// Ensure category is present405try {406iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,407true);408} catch (IllegalArgumentException e) {409return null;410}411412boolean usecache = getUseCache() && hasCachePermission();413414while (iter.hasNext()) {415ImageOutputStreamSpi spi = (ImageOutputStreamSpi)iter.next();416if (spi.getOutputClass().isInstance(output)) {417try {418return spi.createOutputStreamInstance(output,419usecache,420getCacheDirectory());421} catch (IOException e) {422throw new IIOException("Can't create cache file!", e);423}424}425}426427return null;428}429430private static enum SpiInfo {431FORMAT_NAMES {432@Override433String[] info(ImageReaderWriterSpi spi) {434return spi.getFormatNames();435}436},437MIME_TYPES {438@Override439String[] info(ImageReaderWriterSpi spi) {440return spi.getMIMETypes();441}442},443FILE_SUFFIXES {444@Override445String[] info(ImageReaderWriterSpi spi) {446return spi.getFileSuffixes();447}448};449450abstract String[] info(ImageReaderWriterSpi spi);451}452453private static <S extends ImageReaderWriterSpi>454String[] getReaderWriterInfo(Class<S> spiClass, SpiInfo spiInfo)455{456// Ensure category is present457Iterator<S> iter;458try {459iter = theRegistry.getServiceProviders(spiClass, true);460} catch (IllegalArgumentException e) {461return new String[0];462}463464HashSet<String> s = new HashSet<String>();465while (iter.hasNext()) {466ImageReaderWriterSpi spi = iter.next();467Collections.addAll(s, spiInfo.info(spi));468}469470return s.toArray(new String[s.size()]);471}472473// Readers474475/**476* Returns an array of <code>String</code>s listing all of the477* informal format names understood by the current set of registered478* readers.479*480* @return an array of <code>String</code>s.481*/482public static String[] getReaderFormatNames() {483return getReaderWriterInfo(ImageReaderSpi.class,484SpiInfo.FORMAT_NAMES);485}486487/**488* Returns an array of <code>String</code>s listing all of the489* MIME types understood by the current set of registered490* readers.491*492* @return an array of <code>String</code>s.493*/494public static String[] getReaderMIMETypes() {495return getReaderWriterInfo(ImageReaderSpi.class,496SpiInfo.MIME_TYPES);497}498499/**500* Returns an array of <code>String</code>s listing all of the501* file suffixes associated with the formats understood502* by the current set of registered readers.503*504* @return an array of <code>String</code>s.505* @since 1.6506*/507public static String[] getReaderFileSuffixes() {508return getReaderWriterInfo(ImageReaderSpi.class,509SpiInfo.FILE_SUFFIXES);510}511512static class ImageReaderIterator implements Iterator<ImageReader> {513// Contains ImageReaderSpis514public Iterator iter;515516public ImageReaderIterator(Iterator iter) {517this.iter = iter;518}519520public boolean hasNext() {521return iter.hasNext();522}523524public ImageReader next() {525ImageReaderSpi spi = null;526try {527spi = (ImageReaderSpi)iter.next();528return spi.createReaderInstance();529} catch (IOException e) {530// Deregister the spi in this case, but only as531// an ImageReaderSpi532theRegistry.deregisterServiceProvider(spi, ImageReaderSpi.class);533}534return null;535}536537public void remove() {538throw new UnsupportedOperationException();539}540}541542static class CanDecodeInputFilter543implements ServiceRegistry.Filter {544545Object input;546547public CanDecodeInputFilter(Object input) {548this.input = input;549}550551public boolean filter(Object elt) {552try {553ImageReaderSpi spi = (ImageReaderSpi)elt;554ImageInputStream stream = null;555if (input instanceof ImageInputStream) {556stream = (ImageInputStream)input;557}558559// Perform mark/reset as a defensive measure560// even though plug-ins are supposed to take561// care of it.562boolean canDecode = false;563if (stream != null) {564stream.mark();565}566canDecode = spi.canDecodeInput(input);567if (stream != null) {568stream.reset();569}570571return canDecode;572} catch (IOException e) {573return false;574}575}576}577578static class CanEncodeImageAndFormatFilter579implements ServiceRegistry.Filter {580581ImageTypeSpecifier type;582String formatName;583584public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type,585String formatName) {586this.type = type;587this.formatName = formatName;588}589590public boolean filter(Object elt) {591ImageWriterSpi spi = (ImageWriterSpi)elt;592return Arrays.asList(spi.getFormatNames()).contains(formatName) &&593spi.canEncodeImage(type);594}595}596597static class ContainsFilter598implements ServiceRegistry.Filter {599600Method method;601String name;602603// method returns an array of Strings604public ContainsFilter(Method method,605String name) {606this.method = method;607this.name = name;608}609610public boolean filter(Object elt) {611try {612return contains((String[])method.invoke(elt), name);613} catch (Exception e) {614return false;615}616}617}618619/**620* Returns an <code>Iterator</code> containing all currently621* registered <code>ImageReader</code>s that claim to be able to622* decode the supplied <code>Object</code>, typically an623* <code>ImageInputStream</code>.624*625* <p> The stream position is left at its prior position upon626* exit from this method.627*628* @param input an <code>ImageInputStream</code> or other629* <code>Object</code> containing encoded image data.630*631* @return an <code>Iterator</code> containing <code>ImageReader</code>s.632*633* @exception IllegalArgumentException if <code>input</code> is634* <code>null</code>.635*636* @see javax.imageio.spi.ImageReaderSpi#canDecodeInput637*/638public static Iterator<ImageReader> getImageReaders(Object input) {639if (input == null) {640throw new IllegalArgumentException("input == null!");641}642Iterator iter;643// Ensure category is present644try {645iter = theRegistry.getServiceProviders(ImageReaderSpi.class,646new CanDecodeInputFilter(input),647true);648} catch (IllegalArgumentException e) {649return Collections.emptyIterator();650}651652return new ImageReaderIterator(iter);653}654655private static Method readerFormatNamesMethod;656private static Method readerFileSuffixesMethod;657private static Method readerMIMETypesMethod;658private static Method writerFormatNamesMethod;659private static Method writerFileSuffixesMethod;660private static Method writerMIMETypesMethod;661662static {663try {664readerFormatNamesMethod =665ImageReaderSpi.class.getMethod("getFormatNames");666readerFileSuffixesMethod =667ImageReaderSpi.class.getMethod("getFileSuffixes");668readerMIMETypesMethod =669ImageReaderSpi.class.getMethod("getMIMETypes");670671writerFormatNamesMethod =672ImageWriterSpi.class.getMethod("getFormatNames");673writerFileSuffixesMethod =674ImageWriterSpi.class.getMethod("getFileSuffixes");675writerMIMETypesMethod =676ImageWriterSpi.class.getMethod("getMIMETypes");677} catch (NoSuchMethodException e) {678e.printStackTrace();679}680}681682/**683* Returns an <code>Iterator</code> containing all currently684* registered <code>ImageReader</code>s that claim to be able to685* decode the named format.686*687* @param formatName a <code>String</code> containing the informal688* name of a format (<i>e.g.</i>, "jpeg" or "tiff".689*690* @return an <code>Iterator</code> containing691* <code>ImageReader</code>s.692*693* @exception IllegalArgumentException if <code>formatName</code>694* is <code>null</code>.695*696* @see javax.imageio.spi.ImageReaderSpi#getFormatNames697*/698public static Iterator<ImageReader>699getImageReadersByFormatName(String formatName)700{701if (formatName == null) {702throw new IllegalArgumentException("formatName == null!");703}704Iterator iter;705// Ensure category is present706try {707iter = theRegistry.getServiceProviders(ImageReaderSpi.class,708new ContainsFilter(readerFormatNamesMethod,709formatName),710true);711} catch (IllegalArgumentException e) {712return Collections.emptyIterator();713}714return new ImageReaderIterator(iter);715}716717/**718* Returns an <code>Iterator</code> containing all currently719* registered <code>ImageReader</code>s that claim to be able to720* decode files with the given suffix.721*722* @param fileSuffix a <code>String</code> containing a file723* suffix (<i>e.g.</i>, "jpg" or "tiff").724*725* @return an <code>Iterator</code> containing726* <code>ImageReader</code>s.727*728* @exception IllegalArgumentException if <code>fileSuffix</code>729* is <code>null</code>.730*731* @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes732*/733public static Iterator<ImageReader>734getImageReadersBySuffix(String fileSuffix)735{736if (fileSuffix == null) {737throw new IllegalArgumentException("fileSuffix == null!");738}739// Ensure category is present740Iterator iter;741try {742iter = theRegistry.getServiceProviders(ImageReaderSpi.class,743new ContainsFilter(readerFileSuffixesMethod,744fileSuffix),745true);746} catch (IllegalArgumentException e) {747return Collections.emptyIterator();748}749return new ImageReaderIterator(iter);750}751752/**753* Returns an <code>Iterator</code> containing all currently754* registered <code>ImageReader</code>s that claim to be able to755* decode files with the given MIME type.756*757* @param MIMEType a <code>String</code> containing a file758* suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").759*760* @return an <code>Iterator</code> containing761* <code>ImageReader</code>s.762*763* @exception IllegalArgumentException if <code>MIMEType</code> is764* <code>null</code>.765*766* @see javax.imageio.spi.ImageReaderSpi#getMIMETypes767*/768public static Iterator<ImageReader>769getImageReadersByMIMEType(String MIMEType)770{771if (MIMEType == null) {772throw new IllegalArgumentException("MIMEType == null!");773}774// Ensure category is present775Iterator iter;776try {777iter = theRegistry.getServiceProviders(ImageReaderSpi.class,778new ContainsFilter(readerMIMETypesMethod,779MIMEType),780true);781} catch (IllegalArgumentException e) {782return Collections.emptyIterator();783}784return new ImageReaderIterator(iter);785}786787// Writers788789/**790* Returns an array of <code>String</code>s listing all of the791* informal format names understood by the current set of registered792* writers.793*794* @return an array of <code>String</code>s.795*/796public static String[] getWriterFormatNames() {797return getReaderWriterInfo(ImageWriterSpi.class,798SpiInfo.FORMAT_NAMES);799}800801/**802* Returns an array of <code>String</code>s listing all of the803* MIME types understood by the current set of registered804* writers.805*806* @return an array of <code>String</code>s.807*/808public static String[] getWriterMIMETypes() {809return getReaderWriterInfo(ImageWriterSpi.class,810SpiInfo.MIME_TYPES);811}812813/**814* Returns an array of <code>String</code>s listing all of the815* file suffixes associated with the formats understood816* by the current set of registered writers.817*818* @return an array of <code>String</code>s.819* @since 1.6820*/821public static String[] getWriterFileSuffixes() {822return getReaderWriterInfo(ImageWriterSpi.class,823SpiInfo.FILE_SUFFIXES);824}825826static class ImageWriterIterator implements Iterator<ImageWriter> {827// Contains ImageWriterSpis828public Iterator iter;829830public ImageWriterIterator(Iterator iter) {831this.iter = iter;832}833834public boolean hasNext() {835return iter.hasNext();836}837838public ImageWriter next() {839ImageWriterSpi spi = null;840try {841spi = (ImageWriterSpi)iter.next();842return spi.createWriterInstance();843} catch (IOException e) {844// Deregister the spi in this case, but only as a writerSpi845theRegistry.deregisterServiceProvider(spi, ImageWriterSpi.class);846}847return null;848}849850public void remove() {851throw new UnsupportedOperationException();852}853}854855private static boolean contains(String[] names, String name) {856for (int i = 0; i < names.length; i++) {857if (name.equalsIgnoreCase(names[i])) {858return true;859}860}861862return false;863}864865/**866* Returns an <code>Iterator</code> containing all currently867* registered <code>ImageWriter</code>s that claim to be able to868* encode the named format.869*870* @param formatName a <code>String</code> containing the informal871* name of a format (<i>e.g.</i>, "jpeg" or "tiff".872*873* @return an <code>Iterator</code> containing874* <code>ImageWriter</code>s.875*876* @exception IllegalArgumentException if <code>formatName</code> is877* <code>null</code>.878*879* @see javax.imageio.spi.ImageWriterSpi#getFormatNames880*/881public static Iterator<ImageWriter>882getImageWritersByFormatName(String formatName)883{884if (formatName == null) {885throw new IllegalArgumentException("formatName == null!");886}887Iterator iter;888// Ensure category is present889try {890iter = theRegistry.getServiceProviders(ImageWriterSpi.class,891new ContainsFilter(writerFormatNamesMethod,892formatName),893true);894} catch (IllegalArgumentException e) {895return Collections.emptyIterator();896}897return new ImageWriterIterator(iter);898}899900/**901* Returns an <code>Iterator</code> containing all currently902* registered <code>ImageWriter</code>s that claim to be able to903* encode files with the given suffix.904*905* @param fileSuffix a <code>String</code> containing a file906* suffix (<i>e.g.</i>, "jpg" or "tiff").907*908* @return an <code>Iterator</code> containing <code>ImageWriter</code>s.909*910* @exception IllegalArgumentException if <code>fileSuffix</code> is911* <code>null</code>.912*913* @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes914*/915public static Iterator<ImageWriter>916getImageWritersBySuffix(String fileSuffix)917{918if (fileSuffix == null) {919throw new IllegalArgumentException("fileSuffix == null!");920}921Iterator iter;922// Ensure category is present923try {924iter = theRegistry.getServiceProviders(ImageWriterSpi.class,925new ContainsFilter(writerFileSuffixesMethod,926fileSuffix),927true);928} catch (IllegalArgumentException e) {929return Collections.emptyIterator();930}931return new ImageWriterIterator(iter);932}933934/**935* Returns an <code>Iterator</code> containing all currently936* registered <code>ImageWriter</code>s that claim to be able to937* encode files with the given MIME type.938*939* @param MIMEType a <code>String</code> containing a file940* suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").941*942* @return an <code>Iterator</code> containing <code>ImageWriter</code>s.943*944* @exception IllegalArgumentException if <code>MIMEType</code> is945* <code>null</code>.946*947* @see javax.imageio.spi.ImageWriterSpi#getMIMETypes948*/949public static Iterator<ImageWriter>950getImageWritersByMIMEType(String MIMEType)951{952if (MIMEType == null) {953throw new IllegalArgumentException("MIMEType == null!");954}955Iterator iter;956// Ensure category is present957try {958iter = theRegistry.getServiceProviders(ImageWriterSpi.class,959new ContainsFilter(writerMIMETypesMethod,960MIMEType),961true);962} catch (IllegalArgumentException e) {963return Collections.emptyIterator();964}965return new ImageWriterIterator(iter);966}967968/**969* Returns an <code>ImageWriter</code>corresponding to the given970* <code>ImageReader</code>, if there is one, or <code>null</code>971* if the plug-in for this <code>ImageReader</code> does not972* specify a corresponding <code>ImageWriter</code>, or if the973* given <code>ImageReader</code> is not registered. This974* mechanism may be used to obtain an <code>ImageWriter</code>975* that will understand the internal structure of non-pixel976* metadata (as encoded by <code>IIOMetadata</code> objects)977* generated by the <code>ImageReader</code>. By obtaining this978* data from the <code>ImageReader</code> and passing it on to the979* <code>ImageWriter</code> obtained with this method, a client980* program can read an image, modify it in some way, and write it981* back out preserving all metadata, without having to understand982* anything about the structure of the metadata, or even about983* the image format. Note that this method returns the984* "preferred" writer, which is the first in the list returned by985* <code>javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()</code>.986*987* @param reader an instance of a registered <code>ImageReader</code>.988*989* @return an <code>ImageWriter</code>, or null.990*991* @exception IllegalArgumentException if <code>reader</code> is992* <code>null</code>.993*994* @see #getImageReader(ImageWriter)995* @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()996*/997public static ImageWriter getImageWriter(ImageReader reader) {998if (reader == null) {999throw new IllegalArgumentException("reader == null!");1000}10011002ImageReaderSpi readerSpi = reader.getOriginatingProvider();1003if (readerSpi == null) {1004Iterator readerSpiIter;1005// Ensure category is present1006try {1007readerSpiIter =1008theRegistry.getServiceProviders(ImageReaderSpi.class,1009false);1010} catch (IllegalArgumentException e) {1011return null;1012}10131014while (readerSpiIter.hasNext()) {1015ImageReaderSpi temp = (ImageReaderSpi) readerSpiIter.next();1016if (temp.isOwnReader(reader)) {1017readerSpi = temp;1018break;1019}1020}1021if (readerSpi == null) {1022return null;1023}1024}10251026String[] writerNames = readerSpi.getImageWriterSpiNames();1027if (writerNames == null) {1028return null;1029}10301031Class writerSpiClass = null;1032try {1033writerSpiClass = Class.forName(writerNames[0], true,1034ClassLoader.getSystemClassLoader());1035} catch (ClassNotFoundException e) {1036return null;1037}10381039ImageWriterSpi writerSpi = (ImageWriterSpi)1040theRegistry.getServiceProviderByClass(writerSpiClass);1041if (writerSpi == null) {1042return null;1043}10441045try {1046return writerSpi.createWriterInstance();1047} catch (IOException e) {1048// Deregister the spi in this case, but only as a writerSpi1049theRegistry.deregisterServiceProvider(writerSpi,1050ImageWriterSpi.class);1051return null;1052}1053}10541055/**1056* Returns an <code>ImageReader</code>corresponding to the given1057* <code>ImageWriter</code>, if there is one, or <code>null</code>1058* if the plug-in for this <code>ImageWriter</code> does not1059* specify a corresponding <code>ImageReader</code>, or if the1060* given <code>ImageWriter</code> is not registered. This method1061* is provided principally for symmetry with1062* <code>getImageWriter(ImageReader)</code>. Note that this1063* method returns the "preferred" reader, which is the first in1064* the list returned by1065* javax.imageio.spi.ImageWriterSpi.<code>getImageReaderSpiNames()</code>.1066*1067* @param writer an instance of a registered <code>ImageWriter</code>.1068*1069* @return an <code>ImageReader</code>, or null.1070*1071* @exception IllegalArgumentException if <code>writer</code> is1072* <code>null</code>.1073*1074* @see #getImageWriter(ImageReader)1075* @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()1076*/1077public static ImageReader getImageReader(ImageWriter writer) {1078if (writer == null) {1079throw new IllegalArgumentException("writer == null!");1080}10811082ImageWriterSpi writerSpi = writer.getOriginatingProvider();1083if (writerSpi == null) {1084Iterator writerSpiIter;1085// Ensure category is present1086try {1087writerSpiIter =1088theRegistry.getServiceProviders(ImageWriterSpi.class,1089false);1090} catch (IllegalArgumentException e) {1091return null;1092}10931094while (writerSpiIter.hasNext()) {1095ImageWriterSpi temp = (ImageWriterSpi) writerSpiIter.next();1096if (temp.isOwnWriter(writer)) {1097writerSpi = temp;1098break;1099}1100}1101if (writerSpi == null) {1102return null;1103}1104}11051106String[] readerNames = writerSpi.getImageReaderSpiNames();1107if (readerNames == null) {1108return null;1109}11101111Class readerSpiClass = null;1112try {1113readerSpiClass = Class.forName(readerNames[0], true,1114ClassLoader.getSystemClassLoader());1115} catch (ClassNotFoundException e) {1116return null;1117}11181119ImageReaderSpi readerSpi = (ImageReaderSpi)1120theRegistry.getServiceProviderByClass(readerSpiClass);1121if (readerSpi == null) {1122return null;1123}11241125try {1126return readerSpi.createReaderInstance();1127} catch (IOException e) {1128// Deregister the spi in this case, but only as a readerSpi1129theRegistry.deregisterServiceProvider(readerSpi,1130ImageReaderSpi.class);1131return null;1132}1133}11341135/**1136* Returns an <code>Iterator</code> containing all currently1137* registered <code>ImageWriter</code>s that claim to be able to1138* encode images of the given layout (specified using an1139* <code>ImageTypeSpecifier</code>) in the given format.1140*1141* @param type an <code>ImageTypeSpecifier</code> indicating the1142* layout of the image to be written.1143* @param formatName the informal name of the <code>format</code>.1144*1145* @return an <code>Iterator</code> containing <code>ImageWriter</code>s.1146*1147* @exception IllegalArgumentException if any parameter is1148* <code>null</code>.1149*1150* @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)1151*/1152public static Iterator<ImageWriter>1153getImageWriters(ImageTypeSpecifier type, String formatName)1154{1155if (type == null) {1156throw new IllegalArgumentException("type == null!");1157}1158if (formatName == null) {1159throw new IllegalArgumentException("formatName == null!");1160}11611162Iterator iter;1163// Ensure category is present1164try {1165iter = theRegistry.getServiceProviders(ImageWriterSpi.class,1166new CanEncodeImageAndFormatFilter(type,1167formatName),1168true);1169} catch (IllegalArgumentException e) {1170return Collections.emptyIterator();1171}11721173return new ImageWriterIterator(iter);1174}11751176static class ImageTranscoderIterator1177implements Iterator<ImageTranscoder>1178{1179// Contains ImageTranscoderSpis1180public Iterator iter;11811182public ImageTranscoderIterator(Iterator iter) {1183this.iter = iter;1184}11851186public boolean hasNext() {1187return iter.hasNext();1188}11891190public ImageTranscoder next() {1191ImageTranscoderSpi spi = null;1192spi = (ImageTranscoderSpi)iter.next();1193return spi.createTranscoderInstance();1194}11951196public void remove() {1197throw new UnsupportedOperationException();1198}1199}12001201static class TranscoderFilter1202implements ServiceRegistry.Filter {12031204String readerSpiName;1205String writerSpiName;12061207public TranscoderFilter(ImageReaderSpi readerSpi,1208ImageWriterSpi writerSpi) {1209this.readerSpiName = readerSpi.getClass().getName();1210this.writerSpiName = writerSpi.getClass().getName();1211}12121213public boolean filter(Object elt) {1214ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;1215String readerName = spi.getReaderServiceProviderName();1216String writerName = spi.getWriterServiceProviderName();1217return (readerName.equals(readerSpiName) &&1218writerName.equals(writerSpiName));1219}1220}12211222/**1223* Returns an <code>Iterator</code> containing all currently1224* registered <code>ImageTranscoder</code>s that claim to be1225* able to transcode between the metadata of the given1226* <code>ImageReader</code> and <code>ImageWriter</code>.1227*1228* @param reader an <code>ImageReader</code>.1229* @param writer an <code>ImageWriter</code>.1230*1231* @return an <code>Iterator</code> containing1232* <code>ImageTranscoder</code>s.1233*1234* @exception IllegalArgumentException if <code>reader</code> or1235* <code>writer</code> is <code>null</code>.1236*/1237public static Iterator<ImageTranscoder>1238getImageTranscoders(ImageReader reader, ImageWriter writer)1239{1240if (reader == null) {1241throw new IllegalArgumentException("reader == null!");1242}1243if (writer == null) {1244throw new IllegalArgumentException("writer == null!");1245}1246ImageReaderSpi readerSpi = reader.getOriginatingProvider();1247ImageWriterSpi writerSpi = writer.getOriginatingProvider();1248ServiceRegistry.Filter filter =1249new TranscoderFilter(readerSpi, writerSpi);12501251Iterator iter;1252// Ensure category is present1253try {1254iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,1255filter, true);1256} catch (IllegalArgumentException e) {1257return Collections.emptyIterator();1258}1259return new ImageTranscoderIterator(iter);1260}12611262// All-in-one methods12631264/**1265* Returns a <code>BufferedImage</code> as the result of decoding1266* a supplied <code>File</code> with an <code>ImageReader</code>1267* chosen automatically from among those currently registered.1268* The <code>File</code> is wrapped in an1269* <code>ImageInputStream</code>. If no registered1270* <code>ImageReader</code> claims to be able to read the1271* resulting stream, <code>null</code> is returned.1272*1273* <p> The current cache settings from <code>getUseCache</code>and1274* <code>getCacheDirectory</code> will be used to control caching in the1275* <code>ImageInputStream</code> that is created.1276*1277* <p> Note that there is no <code>read</code> method that takes a1278* filename as a <code>String</code>; use this method instead after1279* creating a <code>File</code> from the filename.1280*1281* <p> This method does not attempt to locate1282* <code>ImageReader</code>s that can read directly from a1283* <code>File</code>; that may be accomplished using1284* <code>IIORegistry</code> and <code>ImageReaderSpi</code>.1285*1286* @param input a <code>File</code> to read from.1287*1288* @return a <code>BufferedImage</code> containing the decoded1289* contents of the input, or <code>null</code>.1290*1291* @exception IllegalArgumentException if <code>input</code> is1292* <code>null</code>.1293* @exception IOException if an error occurs during reading or when not1294* able to create required ImageInputStream.1295*/1296public static BufferedImage read(File input) throws IOException {1297if (input == null) {1298throw new IllegalArgumentException("input == null!");1299}1300if (!input.canRead()) {1301throw new IIOException("Can't read input file!");1302}13031304ImageInputStream stream = createImageInputStream(input);1305if (stream == null) {1306throw new IIOException("Can't create an ImageInputStream!");1307}1308BufferedImage bi = read(stream);1309if (bi == null) {1310stream.close();1311}1312return bi;1313}13141315/**1316* Returns a <code>BufferedImage</code> as the result of decoding1317* a supplied <code>InputStream</code> with an <code>ImageReader</code>1318* chosen automatically from among those currently registered.1319* The <code>InputStream</code> is wrapped in an1320* <code>ImageInputStream</code>. If no registered1321* <code>ImageReader</code> claims to be able to read the1322* resulting stream, <code>null</code> is returned.1323*1324* <p> The current cache settings from <code>getUseCache</code>and1325* <code>getCacheDirectory</code> will be used to control caching in the1326* <code>ImageInputStream</code> that is created.1327*1328* <p> This method does not attempt to locate1329* <code>ImageReader</code>s that can read directly from an1330* <code>InputStream</code>; that may be accomplished using1331* <code>IIORegistry</code> and <code>ImageReaderSpi</code>.1332*1333* <p> This method <em>does not</em> close the provided1334* <code>InputStream</code> after the read operation has completed;1335* it is the responsibility of the caller to close the stream, if desired.1336*1337* @param input an <code>InputStream</code> to read from.1338*1339* @return a <code>BufferedImage</code> containing the decoded1340* contents of the input, or <code>null</code>.1341*1342* @exception IllegalArgumentException if <code>input</code> is1343* <code>null</code>.1344* @exception IOException if an error occurs during reading or when not1345* able to create required ImageInputStream.1346*/1347public static BufferedImage read(InputStream input) throws IOException {1348if (input == null) {1349throw new IllegalArgumentException("input == null!");1350}13511352ImageInputStream stream = createImageInputStream(input);1353if (stream == null) {1354throw new IIOException("Can't create an ImageInputStream!");1355}1356BufferedImage bi = read(stream);1357if (bi == null) {1358stream.close();1359}1360return bi;1361}13621363/**1364* Returns a <code>BufferedImage</code> as the result of decoding1365* a supplied <code>URL</code> with an <code>ImageReader</code>1366* chosen automatically from among those currently registered. An1367* <code>InputStream</code> is obtained from the <code>URL</code>,1368* which is wrapped in an <code>ImageInputStream</code>. If no1369* registered <code>ImageReader</code> claims to be able to read1370* the resulting stream, <code>null</code> is returned.1371*1372* <p> The current cache settings from <code>getUseCache</code>and1373* <code>getCacheDirectory</code> will be used to control caching in the1374* <code>ImageInputStream</code> that is created.1375*1376* <p> This method does not attempt to locate1377* <code>ImageReader</code>s that can read directly from a1378* <code>URL</code>; that may be accomplished using1379* <code>IIORegistry</code> and <code>ImageReaderSpi</code>.1380*1381* @param input a <code>URL</code> to read from.1382*1383* @return a <code>BufferedImage</code> containing the decoded1384* contents of the input, or <code>null</code>.1385*1386* @exception IllegalArgumentException if <code>input</code> is1387* <code>null</code>.1388* @exception IOException if an error occurs during reading or when not1389* able to create required ImageInputStream.1390*/1391public static BufferedImage read(URL input) throws IOException {1392if (input == null) {1393throw new IllegalArgumentException("input == null!");1394}13951396InputStream istream = null;1397try {1398istream = input.openStream();1399} catch (IOException e) {1400throw new IIOException("Can't get input stream from URL!", e);1401}1402ImageInputStream stream = createImageInputStream(istream);1403if (stream == null) {1404/* close the istream when stream is null so that if user has1405* given filepath as URL he can delete it, otherwise stream will1406* be open to that file and he will not be able to delete it.1407*/1408istream.close();1409throw new IIOException("Can't create an ImageInputStream!");1410}1411BufferedImage bi;1412try {1413bi = read(stream);1414if (bi == null) {1415stream.close();1416}1417} finally {1418istream.close();1419}1420return bi;1421}14221423/**1424* Returns a <code>BufferedImage</code> as the result of decoding1425* a supplied <code>ImageInputStream</code> with an1426* <code>ImageReader</code> chosen automatically from among those1427* currently registered. If no registered1428* <code>ImageReader</code> claims to be able to read the stream,1429* <code>null</code> is returned.1430*1431* <p> Unlike most other methods in this class, this method <em>does</em>1432* close the provided <code>ImageInputStream</code> after the read1433* operation has completed, unless <code>null</code> is returned,1434* in which case this method <em>does not</em> close the stream.1435*1436* @param stream an <code>ImageInputStream</code> to read from.1437*1438* @return a <code>BufferedImage</code> containing the decoded1439* contents of the input, or <code>null</code>.1440*1441* @exception IllegalArgumentException if <code>stream</code> is1442* <code>null</code>.1443* @exception IOException if an error occurs during reading.1444*/1445public static BufferedImage read(ImageInputStream stream)1446throws IOException {1447if (stream == null) {1448throw new IllegalArgumentException("stream == null!");1449}14501451Iterator iter = getImageReaders(stream);1452if (!iter.hasNext()) {1453return null;1454}14551456ImageReader reader = (ImageReader)iter.next();1457ImageReadParam param = reader.getDefaultReadParam();1458reader.setInput(stream, true, true);1459BufferedImage bi;1460try {1461bi = reader.read(0, param);1462} finally {1463reader.dispose();1464stream.close();1465}1466return bi;1467}14681469/**1470* Writes an image using the an arbitrary <code>ImageWriter</code>1471* that supports the given format to an1472* <code>ImageOutputStream</code>. The image is written to the1473* <code>ImageOutputStream</code> starting at the current stream1474* pointer, overwriting existing stream data from that point1475* forward, if present.1476*1477* <p> This method <em>does not</em> close the provided1478* <code>ImageOutputStream</code> after the write operation has completed;1479* it is the responsibility of the caller to close the stream, if desired.1480*1481* @param im a <code>RenderedImage</code> to be written.1482* @param formatName a <code>String</code> containing the informal1483* name of the format.1484* @param output an <code>ImageOutputStream</code> to be written to.1485*1486* @return <code>false</code> if no appropriate writer is found.1487*1488* @exception IllegalArgumentException if any parameter is1489* <code>null</code>.1490* @exception IOException if an error occurs during writing.1491*/1492public static boolean write(RenderedImage im,1493String formatName,1494ImageOutputStream output) throws IOException {1495if (im == null) {1496throw new IllegalArgumentException("im == null!");1497}1498if (formatName == null) {1499throw new IllegalArgumentException("formatName == null!");1500}1501if (output == null) {1502throw new IllegalArgumentException("output == null!");1503}15041505return doWrite(im, getWriter(im, formatName), output);1506}15071508/**1509* Writes an image using an arbitrary <code>ImageWriter</code>1510* that supports the given format to a <code>File</code>. If1511* there is already a <code>File</code> present, its contents are1512* discarded.1513*1514* @param im a <code>RenderedImage</code> to be written.1515* @param formatName a <code>String</code> containing the informal1516* name of the format.1517* @param output a <code>File</code> to be written to.1518*1519* @return <code>false</code> if no appropriate writer is found.1520*1521* @exception IllegalArgumentException if any parameter is1522* <code>null</code>.1523* @exception IOException if an error occurs during writing or when not1524* able to create required ImageOutputStream.1525*/1526public static boolean write(RenderedImage im,1527String formatName,1528File output) throws IOException {1529if (output == null) {1530throw new IllegalArgumentException("output == null!");1531}15321533ImageWriter writer = getWriter(im, formatName);1534if (writer == null) {1535/* Do not make changes in the file system if we have1536* no appropriate writer.1537*/1538return false;1539}15401541output.delete();1542ImageOutputStream stream = createImageOutputStream(output);1543if (stream == null) {1544throw new IIOException("Can't create an ImageOutputStream!");1545}1546try {1547return doWrite(im, writer, stream);1548} finally {1549stream.close();1550}1551}15521553/**1554* Writes an image using an arbitrary <code>ImageWriter</code>1555* that supports the given format to an <code>OutputStream</code>.1556*1557* <p> This method <em>does not</em> close the provided1558* <code>OutputStream</code> after the write operation has completed;1559* it is the responsibility of the caller to close the stream, if desired.1560*1561* <p> The current cache settings from <code>getUseCache</code>and1562* <code>getCacheDirectory</code> will be used to control caching.1563*1564* @param im a <code>RenderedImage</code> to be written.1565* @param formatName a <code>String</code> containing the informal1566* name of the format.1567* @param output an <code>OutputStream</code> to be written to.1568*1569* @return <code>false</code> if no appropriate writer is found.1570*1571* @exception IllegalArgumentException if any parameter is1572* <code>null</code>.1573* @exception IOException if an error occurs during writing or when not1574* able to create required ImageOutputStream.1575*/1576public static boolean write(RenderedImage im,1577String formatName,1578OutputStream output) throws IOException {1579if (output == null) {1580throw new IllegalArgumentException("output == null!");1581}1582ImageOutputStream stream = createImageOutputStream(output);1583if (stream == null) {1584throw new IIOException("Can't create an ImageOutputStream!");1585}1586try {1587return doWrite(im, getWriter(im, formatName), stream);1588} finally {1589stream.close();1590}1591}15921593/**1594* Returns <code>ImageWriter</code> instance according to given1595* rendered image and image format or <code>null</code> if there1596* is no appropriate writer.1597*/1598private static ImageWriter getWriter(RenderedImage im,1599String formatName) {1600ImageTypeSpecifier type =1601ImageTypeSpecifier.createFromRenderedImage(im);1602Iterator<ImageWriter> iter = getImageWriters(type, formatName);16031604if (iter.hasNext()) {1605return iter.next();1606} else {1607return null;1608}1609}16101611/**1612* Writes image to output stream using given image writer.1613*/1614private static boolean doWrite(RenderedImage im, ImageWriter writer,1615ImageOutputStream output) throws IOException {1616if (writer == null) {1617return false;1618}1619writer.setOutput(output);1620try {1621writer.write(im);1622} finally {1623writer.dispose();1624output.flush();1625}1626return true;1627}1628}162916301631