Path: blob/master/src/java.desktop/share/classes/javax/imageio/ImageIO.java
41152 views
/*1* Copyright (c) 2000, 2021, 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}s and {@code ImageWriter}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}.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} method of the context87* {@code ClassLoader} is used locate JAR files containing88* files named89* {@code META-INF/services/javax.imageio.spi.}<i>classname</i>,90* where <i>classname</i> is one of {@code ImageReaderSpi},91* {@code ImageWriterSpi}, {@code ImageTranscoderSpi},92* {@code ImageInputStreamSpi}, or93* {@code ImageOutputStreamSpi}, 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} will have its own copy117* via the {@code AppContext} 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} object associated with this153* {@code ThreadGroup}.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*/169@SuppressWarnings("removal")170private static String getTempDir() {171GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");172return AccessController.doPrivileged(a);173}174175/**176* Determines whether the caller has write access to the cache177* directory, stores the result in the {@code CacheInfo} object,178* and returns the decision. This method helps to prevent mysterious179* SecurityExceptions to be thrown when this convenience class is used180* in an applet, for example.181*/182private static boolean hasCachePermission() {183Boolean hasPermission = getCacheInfo().getHasPermission();184185if (hasPermission != null) {186return hasPermission.booleanValue();187} else {188try {189@SuppressWarnings("removal")190SecurityManager security = System.getSecurityManager();191if (security != null) {192File cachedir = getCacheDirectory();193String cachepath;194195if (cachedir != null) {196cachepath = cachedir.getPath();197} else {198cachepath = getTempDir();199200if (cachepath == null || cachepath.isEmpty()) {201getCacheInfo().setHasPermission(Boolean.FALSE);202return false;203}204}205206// we have to check whether we can read, write,207// and delete cache files.208// So, compose cache file path and check it.209String filepath = cachepath;210if (!filepath.endsWith(File.separator)) {211filepath += File.separator;212}213filepath += "*";214215security.checkPermission(new FilePermission(filepath, "read, write, delete"));216}217} catch (SecurityException e) {218getCacheInfo().setHasPermission(Boolean.FALSE);219return false;220}221222getCacheInfo().setHasPermission(Boolean.TRUE);223return true;224}225}226227/**228* Sets a flag indicating whether a disk-based cache file should229* be used when creating {@code ImageInputStream}s and230* {@code ImageOutputStream}s.231*232* <p> When reading from a standard {@code InputStream}, it233* may be necessary to save previously read information in a cache234* since the underlying stream does not allow data to be re-read.235* Similarly, when writing to a standard236* {@code OutputStream}, a cache may be used to allow a237* previously written value to be changed before flushing it to238* the final destination.239*240* <p> The cache may reside in main memory or on disk. Setting241* this flag to {@code false} disallows the use of disk for242* future streams, which may be advantageous when working with243* small images, as the overhead of creating and destroying files244* is removed.245*246* <p> On startup, the value is set to {@code true}.247*248* @param useCache a {@code boolean} indicating whether a249* cache file should be used, in cases where it is optional.250*251* @see #getUseCache252*/253public static void setUseCache(boolean useCache) {254getCacheInfo().setUseCache(useCache);255}256257/**258* Returns the current value set by {@code setUseCache}, or259* {@code true} if no explicit setting has been made.260*261* @return true if a disk-based cache may be used for262* {@code ImageInputStream}s and263* {@code ImageOutputStream}s.264*265* @see #setUseCache266*/267public static boolean getUseCache() {268return getCacheInfo().getUseCache();269}270271/**272* Sets the directory where cache files are to be created. A273* value of {@code null} indicates that the system-dependent274* default temporary-file directory is to be used. If275* {@code getUseCache} returns false, this value is ignored.276*277* @param cacheDirectory a {@code File} specifying a directory.278*279* @see File#createTempFile(String, String, File)280*281* @exception SecurityException if the security manager denies282* access to the directory.283* @exception IllegalArgumentException if {@code cacheDir} is284* non-{@code null} but is not a directory.285*286* @see #getCacheDirectory287*/288public static void setCacheDirectory(File cacheDirectory) {289if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) {290throw new IllegalArgumentException("Not a directory!");291}292getCacheInfo().setCacheDirectory(cacheDirectory);293getCacheInfo().setHasPermission(null);294}295296/**297* Returns the current value set by298* {@code setCacheDirectory}, or {@code null} if no299* explicit setting has been made.300*301* @return a {@code File} indicating the directory where302* cache files will be created, or {@code null} to indicate303* the system-dependent default temporary-file directory.304*305* @see #setCacheDirectory306*/307public static File getCacheDirectory() {308return getCacheInfo().getCacheDirectory();309}310311/**312* Returns an {@code ImageInputStream} that will take its313* input from the given {@code Object}. The set of314* {@code ImageInputStreamSpi}s registered with the315* {@code IIORegistry} class is queried and the first one316* that is able to take input from the supplied object is used to317* create the returned {@code ImageInputStream}. If no318* suitable {@code ImageInputStreamSpi} exists,319* {@code null} is returned.320*321* <p> The current cache settings from {@code getUseCache} and322* {@code getCacheDirectory} will be used to control caching.323*324* @param input an {@code Object} to be used as an input325* source, such as a {@code File}, readable326* {@code RandomAccessFile}, or {@code InputStream}.327*328* @return an {@code ImageInputStream}, or {@code null}.329*330* @exception IllegalArgumentException if {@code input}331* is {@code null}.332* @exception IOException if a cache file is needed but cannot be333* created.334*335* @see javax.imageio.spi.ImageInputStreamSpi336*/337public static ImageInputStream createImageInputStream(Object input)338throws IOException {339if (input == null) {340throw new IllegalArgumentException("input == null!");341}342343Iterator<ImageInputStreamSpi> iter;344// Ensure category is present345try {346iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class,347true);348} catch (IllegalArgumentException e) {349return null;350}351352boolean usecache = getUseCache() && hasCachePermission();353354while (iter.hasNext()) {355ImageInputStreamSpi spi = iter.next();356if (spi.getInputClass().isInstance(input)) {357try {358return spi.createInputStreamInstance(input,359usecache,360getCacheDirectory());361} catch (IOException e) {362throw new IIOException("Can't create cache file!", e);363}364}365}366367return null;368}369370// ImageOutputStreams371372/**373* Returns an {@code ImageOutputStream} that will send its374* output to the given {@code Object}. The set of375* {@code ImageOutputStreamSpi}s registered with the376* {@code IIORegistry} class is queried and the first one377* that is able to send output from the supplied object is used to378* create the returned {@code ImageOutputStream}. If no379* suitable {@code ImageOutputStreamSpi} exists,380* {@code null} is returned.381*382* <p> The current cache settings from {@code getUseCache} and383* {@code getCacheDirectory} will be used to control caching.384*385* @param output an {@code Object} to be used as an output386* destination, such as a {@code File}, writable387* {@code RandomAccessFile}, or {@code OutputStream}.388*389* @return an {@code ImageOutputStream}, or390* {@code null}.391*392* @exception IllegalArgumentException if {@code output} is393* {@code null}.394* @exception IOException if a cache file is needed but cannot be395* created.396*397* @see javax.imageio.spi.ImageOutputStreamSpi398*/399public static ImageOutputStream createImageOutputStream(Object output)400throws IOException {401if (output == null) {402throw new IllegalArgumentException("output == null!");403}404405Iterator<ImageOutputStreamSpi> iter;406// Ensure category is present407try {408iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,409true);410} catch (IllegalArgumentException e) {411return null;412}413414boolean usecache = getUseCache() && hasCachePermission();415416while (iter.hasNext()) {417ImageOutputStreamSpi spi = iter.next();418if (spi.getOutputClass().isInstance(output)) {419try {420return spi.createOutputStreamInstance(output,421usecache,422getCacheDirectory());423} catch (IOException e) {424throw new IIOException("Can't create cache file!", e);425}426}427}428429return null;430}431432private static enum SpiInfo {433FORMAT_NAMES {434@Override435String[] info(ImageReaderWriterSpi spi) {436return spi.getFormatNames();437}438},439MIME_TYPES {440@Override441String[] info(ImageReaderWriterSpi spi) {442return spi.getMIMETypes();443}444},445FILE_SUFFIXES {446@Override447String[] info(ImageReaderWriterSpi spi) {448return spi.getFileSuffixes();449}450};451452abstract String[] info(ImageReaderWriterSpi spi);453}454455private static <S extends ImageReaderWriterSpi>456String[] getReaderWriterInfo(Class<S> spiClass, SpiInfo spiInfo)457{458// Ensure category is present459Iterator<S> iter;460try {461iter = theRegistry.getServiceProviders(spiClass, true);462} catch (IllegalArgumentException e) {463return new String[0];464}465466HashSet<String> s = new HashSet<>();467while (iter.hasNext()) {468ImageReaderWriterSpi spi = iter.next();469String[] info = spiInfo.info(spi);470if (info != null) {471Collections.addAll(s, info);472}473}474475return s.toArray(new String[s.size()]);476}477478// Readers479480/**481* Returns an array of {@code String}s listing all of the482* informal format names understood by the current set of registered483* readers.484*485* @return an array of {@code String}s.486*/487public static String[] getReaderFormatNames() {488return getReaderWriterInfo(ImageReaderSpi.class,489SpiInfo.FORMAT_NAMES);490}491492/**493* Returns an array of {@code String}s listing all of the494* MIME types understood by the current set of registered495* readers.496*497* @return an array of {@code String}s.498*/499public static String[] getReaderMIMETypes() {500return getReaderWriterInfo(ImageReaderSpi.class,501SpiInfo.MIME_TYPES);502}503504/**505* Returns an array of {@code String}s listing all of the506* file suffixes associated with the formats understood507* by the current set of registered readers.508*509* @return an array of {@code String}s.510* @since 1.6511*/512public static String[] getReaderFileSuffixes() {513return getReaderWriterInfo(ImageReaderSpi.class,514SpiInfo.FILE_SUFFIXES);515}516517static class ImageReaderIterator implements Iterator<ImageReader> {518// Contains ImageReaderSpis519private Iterator<ImageReaderSpi> iter;520521public ImageReaderIterator(Iterator<ImageReaderSpi> iter) {522this.iter = iter;523}524525public boolean hasNext() {526return iter.hasNext();527}528529public ImageReader next() {530ImageReaderSpi spi = null;531try {532spi = iter.next();533return spi.createReaderInstance();534} catch (IOException e) {535// Deregister the spi in this case, but only as536// an ImageReaderSpi537theRegistry.deregisterServiceProvider(spi, ImageReaderSpi.class);538}539return null;540}541542public void remove() {543throw new UnsupportedOperationException();544}545}546547static class CanDecodeInputFilter548implements ServiceRegistry.Filter {549550Object input;551552public CanDecodeInputFilter(Object input) {553this.input = input;554}555556public boolean filter(Object elt) {557try {558ImageReaderSpi spi = (ImageReaderSpi)elt;559ImageInputStream stream = null;560if (input instanceof ImageInputStream) {561stream = (ImageInputStream)input;562}563564// Perform mark/reset as a defensive measure565// even though plug-ins are supposed to take566// care of it.567boolean canDecode = false;568if (stream != null) {569stream.mark();570}571try {572canDecode = spi.canDecodeInput(input);573} finally {574if (stream != null) {575stream.reset();576}577}578579return canDecode;580} catch (IOException e) {581return false;582}583}584}585586static class CanEncodeImageAndFormatFilter587implements ServiceRegistry.Filter {588589ImageTypeSpecifier type;590String formatName;591592public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type,593String formatName) {594this.type = type;595this.formatName = formatName;596}597598public boolean filter(Object elt) {599ImageWriterSpi spi = (ImageWriterSpi)elt;600return Arrays.asList(spi.getFormatNames()).contains(formatName) &&601spi.canEncodeImage(type);602}603}604605static class ContainsFilter606implements ServiceRegistry.Filter {607608Method method;609String name;610611// method returns an array of Strings612public ContainsFilter(Method method,613String name) {614this.method = method;615this.name = name;616}617618public boolean filter(Object elt) {619try {620return contains((String[])method.invoke(elt), name);621} catch (Exception e) {622return false;623}624}625}626627/**628* Returns an {@code Iterator} containing all currently629* registered {@code ImageReader}s that claim to be able to630* decode the supplied {@code Object}, typically an631* {@code ImageInputStream}.632*633* <p> The stream position is left at its prior position upon634* exit from this method.635*636* @param input an {@code ImageInputStream} or other637* {@code Object} containing encoded image data.638*639* @return an {@code Iterator} containing {@code ImageReader}s.640*641* @exception IllegalArgumentException if {@code input} is642* {@code null}.643*644* @see javax.imageio.spi.ImageReaderSpi#canDecodeInput645*/646public static Iterator<ImageReader> getImageReaders(Object input) {647if (input == null) {648throw new IllegalArgumentException("input == null!");649}650Iterator<ImageReaderSpi> iter;651// Ensure category is present652try {653iter = theRegistry.getServiceProviders(ImageReaderSpi.class,654new CanDecodeInputFilter(input),655true);656} catch (IllegalArgumentException e) {657return Collections.emptyIterator();658}659660return new ImageReaderIterator(iter);661}662663private static Method readerFormatNamesMethod;664private static Method readerFileSuffixesMethod;665private static Method readerMIMETypesMethod;666private static Method writerFormatNamesMethod;667private static Method writerFileSuffixesMethod;668private static Method writerMIMETypesMethod;669670static {671try {672readerFormatNamesMethod =673ImageReaderSpi.class.getMethod("getFormatNames");674readerFileSuffixesMethod =675ImageReaderSpi.class.getMethod("getFileSuffixes");676readerMIMETypesMethod =677ImageReaderSpi.class.getMethod("getMIMETypes");678679writerFormatNamesMethod =680ImageWriterSpi.class.getMethod("getFormatNames");681writerFileSuffixesMethod =682ImageWriterSpi.class.getMethod("getFileSuffixes");683writerMIMETypesMethod =684ImageWriterSpi.class.getMethod("getMIMETypes");685} catch (NoSuchMethodException e) {686e.printStackTrace();687}688}689690/**691* Returns an {@code Iterator} containing all currently692* registered {@code ImageReader}s that claim to be able to693* decode the named format.694*695* @param formatName a {@code String} containing the informal696* name of a format (<i>e.g.</i>, "jpeg" or "tiff".697*698* @return an {@code Iterator} containing699* {@code ImageReader}s.700*701* @exception IllegalArgumentException if {@code formatName}702* is {@code null}.703*704* @see javax.imageio.spi.ImageReaderSpi#getFormatNames705*/706public static Iterator<ImageReader>707getImageReadersByFormatName(String formatName)708{709if (formatName == null) {710throw new IllegalArgumentException("formatName == null!");711}712Iterator<ImageReaderSpi> iter;713// Ensure category is present714try {715iter = theRegistry.getServiceProviders(ImageReaderSpi.class,716new ContainsFilter(readerFormatNamesMethod,717formatName),718true);719} catch (IllegalArgumentException e) {720return Collections.emptyIterator();721}722return new ImageReaderIterator(iter);723}724725/**726* Returns an {@code Iterator} containing all currently727* registered {@code ImageReader}s that claim to be able to728* decode files with the given suffix.729*730* @param fileSuffix a {@code String} containing a file731* suffix (<i>e.g.</i>, "jpg" or "tiff").732*733* @return an {@code Iterator} containing734* {@code ImageReader}s.735*736* @exception IllegalArgumentException if {@code fileSuffix}737* is {@code null}.738*739* @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes740*/741public static Iterator<ImageReader>742getImageReadersBySuffix(String fileSuffix)743{744if (fileSuffix == null) {745throw new IllegalArgumentException("fileSuffix == null!");746}747// Ensure category is present748Iterator<ImageReaderSpi> iter;749try {750iter = theRegistry.getServiceProviders(ImageReaderSpi.class,751new ContainsFilter(readerFileSuffixesMethod,752fileSuffix),753true);754} catch (IllegalArgumentException e) {755return Collections.emptyIterator();756}757return new ImageReaderIterator(iter);758}759760/**761* Returns an {@code Iterator} containing all currently762* registered {@code ImageReader}s that claim to be able to763* decode files with the given MIME type.764*765* @param MIMEType a {@code String} containing a file766* suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").767*768* @return an {@code Iterator} containing769* {@code ImageReader}s.770*771* @exception IllegalArgumentException if {@code MIMEType} is772* {@code null}.773*774* @see javax.imageio.spi.ImageReaderSpi#getMIMETypes775*/776public static Iterator<ImageReader>777getImageReadersByMIMEType(String MIMEType)778{779if (MIMEType == null) {780throw new IllegalArgumentException("MIMEType == null!");781}782// Ensure category is present783Iterator<ImageReaderSpi> iter;784try {785iter = theRegistry.getServiceProviders(ImageReaderSpi.class,786new ContainsFilter(readerMIMETypesMethod,787MIMEType),788true);789} catch (IllegalArgumentException e) {790return Collections.emptyIterator();791}792return new ImageReaderIterator(iter);793}794795// Writers796797/**798* Returns an array of {@code String}s listing all of the799* informal format names understood by the current set of registered800* writers.801*802* @return an array of {@code String}s.803*/804public static String[] getWriterFormatNames() {805return getReaderWriterInfo(ImageWriterSpi.class,806SpiInfo.FORMAT_NAMES);807}808809/**810* Returns an array of {@code String}s listing all of the811* MIME types understood by the current set of registered812* writers.813*814* @return an array of {@code String}s.815*/816public static String[] getWriterMIMETypes() {817return getReaderWriterInfo(ImageWriterSpi.class,818SpiInfo.MIME_TYPES);819}820821/**822* Returns an array of {@code String}s listing all of the823* file suffixes associated with the formats understood824* by the current set of registered writers.825*826* @return an array of {@code String}s.827* @since 1.6828*/829public static String[] getWriterFileSuffixes() {830return getReaderWriterInfo(ImageWriterSpi.class,831SpiInfo.FILE_SUFFIXES);832}833834static class ImageWriterIterator implements Iterator<ImageWriter> {835// Contains ImageWriterSpis836private Iterator<ImageWriterSpi> iter;837838public ImageWriterIterator(Iterator<ImageWriterSpi> iter) {839this.iter = iter;840}841842public boolean hasNext() {843return iter.hasNext();844}845846public ImageWriter next() {847ImageWriterSpi spi = null;848try {849spi = iter.next();850return spi.createWriterInstance();851} catch (IOException e) {852// Deregister the spi in this case, but only as a writerSpi853theRegistry.deregisterServiceProvider(spi, ImageWriterSpi.class);854}855return null;856}857858public void remove() {859throw new UnsupportedOperationException();860}861}862863private static boolean contains(String[] names, String name) {864for (int i = 0; i < names.length; i++) {865if (name.equalsIgnoreCase(names[i])) {866return true;867}868}869870return false;871}872873/**874* Returns an {@code Iterator} containing all currently875* registered {@code ImageWriter}s that claim to be able to876* encode the named format.877*878* @param formatName a {@code String} containing the informal879* name of a format (<i>e.g.</i>, "jpeg" or "tiff".880*881* @return an {@code Iterator} containing882* {@code ImageWriter}s.883*884* @exception IllegalArgumentException if {@code formatName} is885* {@code null}.886*887* @see javax.imageio.spi.ImageWriterSpi#getFormatNames888*/889public static Iterator<ImageWriter>890getImageWritersByFormatName(String formatName)891{892if (formatName == null) {893throw new IllegalArgumentException("formatName == null!");894}895Iterator<ImageWriterSpi> iter;896// Ensure category is present897try {898iter = theRegistry.getServiceProviders(ImageWriterSpi.class,899new ContainsFilter(writerFormatNamesMethod,900formatName),901true);902} catch (IllegalArgumentException e) {903return Collections.emptyIterator();904}905return new ImageWriterIterator(iter);906}907908/**909* Returns an {@code Iterator} containing all currently910* registered {@code ImageWriter}s that claim to be able to911* encode files with the given suffix.912*913* @param fileSuffix a {@code String} containing a file914* suffix (<i>e.g.</i>, "jpg" or "tiff").915*916* @return an {@code Iterator} containing {@code ImageWriter}s.917*918* @exception IllegalArgumentException if {@code fileSuffix} is919* {@code null}.920*921* @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes922*/923public static Iterator<ImageWriter>924getImageWritersBySuffix(String fileSuffix)925{926if (fileSuffix == null) {927throw new IllegalArgumentException("fileSuffix == null!");928}929Iterator<ImageWriterSpi> iter;930// Ensure category is present931try {932iter = theRegistry.getServiceProviders(ImageWriterSpi.class,933new ContainsFilter(writerFileSuffixesMethod,934fileSuffix),935true);936} catch (IllegalArgumentException e) {937return Collections.emptyIterator();938}939return new ImageWriterIterator(iter);940}941942/**943* Returns an {@code Iterator} containing all currently944* registered {@code ImageWriter}s that claim to be able to945* encode files with the given MIME type.946*947* @param MIMEType a {@code String} containing a file948* suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").949*950* @return an {@code Iterator} containing {@code ImageWriter}s.951*952* @exception IllegalArgumentException if {@code MIMEType} is953* {@code null}.954*955* @see javax.imageio.spi.ImageWriterSpi#getMIMETypes956*/957public static Iterator<ImageWriter>958getImageWritersByMIMEType(String MIMEType)959{960if (MIMEType == null) {961throw new IllegalArgumentException("MIMEType == null!");962}963Iterator<ImageWriterSpi> iter;964// Ensure category is present965try {966iter = theRegistry.getServiceProviders(ImageWriterSpi.class,967new ContainsFilter(writerMIMETypesMethod,968MIMEType),969true);970} catch (IllegalArgumentException e) {971return Collections.emptyIterator();972}973return new ImageWriterIterator(iter);974}975976/**977* Returns an {@code ImageWriter} corresponding to the given978* {@code ImageReader}, if there is one, or {@code null}979* if the plug-in for this {@code ImageReader} does not980* specify a corresponding {@code ImageWriter}, or if the981* given {@code ImageReader} is not registered. This982* mechanism may be used to obtain an {@code ImageWriter}983* that will understand the internal structure of non-pixel984* metadata (as encoded by {@code IIOMetadata} objects)985* generated by the {@code ImageReader}. By obtaining this986* data from the {@code ImageReader} and passing it on to the987* {@code ImageWriter} obtained with this method, a client988* program can read an image, modify it in some way, and write it989* back out preserving all metadata, without having to understand990* anything about the structure of the metadata, or even about991* the image format. Note that this method returns the992* "preferred" writer, which is the first in the list returned by993* {@code javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()}.994*995* @param reader an instance of a registered {@code ImageReader}.996*997* @return an {@code ImageWriter}, or null.998*999* @exception IllegalArgumentException if {@code reader} is1000* {@code null}.1001*1002* @see #getImageReader(ImageWriter)1003* @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()1004*/1005public static ImageWriter getImageWriter(ImageReader reader) {1006if (reader == null) {1007throw new IllegalArgumentException("reader == null!");1008}10091010ImageReaderSpi readerSpi = reader.getOriginatingProvider();1011if (readerSpi == null) {1012Iterator<ImageReaderSpi> readerSpiIter;1013// Ensure category is present1014try {1015readerSpiIter =1016theRegistry.getServiceProviders(ImageReaderSpi.class,1017false);1018} catch (IllegalArgumentException e) {1019return null;1020}10211022while (readerSpiIter.hasNext()) {1023ImageReaderSpi temp = readerSpiIter.next();1024if (temp.isOwnReader(reader)) {1025readerSpi = temp;1026break;1027}1028}1029if (readerSpi == null) {1030return null;1031}1032}10331034String[] writerNames = readerSpi.getImageWriterSpiNames();1035if (writerNames == null) {1036return null;1037}10381039Class<?> writerSpiClass = null;1040try {1041writerSpiClass = Class.forName(writerNames[0], true,1042ClassLoader.getSystemClassLoader());1043} catch (ClassNotFoundException e) {1044return null;1045}10461047ImageWriterSpi writerSpi = (ImageWriterSpi)1048theRegistry.getServiceProviderByClass(writerSpiClass);1049if (writerSpi == null) {1050return null;1051}10521053try {1054return writerSpi.createWriterInstance();1055} catch (IOException e) {1056// Deregister the spi in this case, but only as a writerSpi1057theRegistry.deregisterServiceProvider(writerSpi,1058ImageWriterSpi.class);1059return null;1060}1061}10621063/**1064* Returns an {@code ImageReader} corresponding to the given1065* {@code ImageWriter}, if there is one, or {@code null}1066* if the plug-in for this {@code ImageWriter} does not1067* specify a corresponding {@code ImageReader}, or if the1068* given {@code ImageWriter} is not registered. This method1069* is provided principally for symmetry with1070* {@code getImageWriter(ImageReader)}. Note that this1071* method returns the "preferred" reader, which is the first in1072* the list returned by1073* javax.imageio.spi.ImageWriterSpi.{@code getImageReaderSpiNames()}.1074*1075* @param writer an instance of a registered {@code ImageWriter}.1076*1077* @return an {@code ImageReader}, or null.1078*1079* @exception IllegalArgumentException if {@code writer} is1080* {@code null}.1081*1082* @see #getImageWriter(ImageReader)1083* @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()1084*/1085public static ImageReader getImageReader(ImageWriter writer) {1086if (writer == null) {1087throw new IllegalArgumentException("writer == null!");1088}10891090ImageWriterSpi writerSpi = writer.getOriginatingProvider();1091if (writerSpi == null) {1092Iterator<ImageWriterSpi> writerSpiIter;1093// Ensure category is present1094try {1095writerSpiIter =1096theRegistry.getServiceProviders(ImageWriterSpi.class,1097false);1098} catch (IllegalArgumentException e) {1099return null;1100}11011102while (writerSpiIter.hasNext()) {1103ImageWriterSpi temp = writerSpiIter.next();1104if (temp.isOwnWriter(writer)) {1105writerSpi = temp;1106break;1107}1108}1109if (writerSpi == null) {1110return null;1111}1112}11131114String[] readerNames = writerSpi.getImageReaderSpiNames();1115if (readerNames == null) {1116return null;1117}11181119Class<?> readerSpiClass = null;1120try {1121readerSpiClass = Class.forName(readerNames[0], true,1122ClassLoader.getSystemClassLoader());1123} catch (ClassNotFoundException e) {1124return null;1125}11261127ImageReaderSpi readerSpi = (ImageReaderSpi)1128theRegistry.getServiceProviderByClass(readerSpiClass);1129if (readerSpi == null) {1130return null;1131}11321133try {1134return readerSpi.createReaderInstance();1135} catch (IOException e) {1136// Deregister the spi in this case, but only as a readerSpi1137theRegistry.deregisterServiceProvider(readerSpi,1138ImageReaderSpi.class);1139return null;1140}1141}11421143/**1144* Returns an {@code Iterator} containing all currently1145* registered {@code ImageWriter}s that claim to be able to1146* encode images of the given layout (specified using an1147* {@code ImageTypeSpecifier}) in the given format.1148*1149* @param type an {@code ImageTypeSpecifier} indicating the1150* layout of the image to be written.1151* @param formatName the informal name of the {@code format}.1152*1153* @return an {@code Iterator} containing {@code ImageWriter}s.1154*1155* @exception IllegalArgumentException if any parameter is1156* {@code null}.1157*1158* @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)1159*/1160public static Iterator<ImageWriter>1161getImageWriters(ImageTypeSpecifier type, String formatName)1162{1163if (type == null) {1164throw new IllegalArgumentException("type == null!");1165}1166if (formatName == null) {1167throw new IllegalArgumentException("formatName == null!");1168}11691170Iterator<ImageWriterSpi> iter;1171// Ensure category is present1172try {1173iter = theRegistry.getServiceProviders(ImageWriterSpi.class,1174new CanEncodeImageAndFormatFilter(type,1175formatName),1176true);1177} catch (IllegalArgumentException e) {1178return Collections.emptyIterator();1179}11801181return new ImageWriterIterator(iter);1182}11831184static class ImageTranscoderIterator1185implements Iterator<ImageTranscoder>1186{1187// Contains ImageTranscoderSpis1188public Iterator<ImageTranscoderSpi> iter;11891190public ImageTranscoderIterator(Iterator<ImageTranscoderSpi> iter) {1191this.iter = iter;1192}11931194public boolean hasNext() {1195return iter.hasNext();1196}11971198public ImageTranscoder next() {1199ImageTranscoderSpi spi = null;1200spi = iter.next();1201return spi.createTranscoderInstance();1202}12031204public void remove() {1205throw new UnsupportedOperationException();1206}1207}12081209static class TranscoderFilter1210implements ServiceRegistry.Filter {12111212String readerSpiName;1213String writerSpiName;12141215public TranscoderFilter(ImageReaderSpi readerSpi,1216ImageWriterSpi writerSpi) {1217this.readerSpiName = readerSpi.getClass().getName();1218this.writerSpiName = writerSpi.getClass().getName();1219}12201221public boolean filter(Object elt) {1222ImageTranscoderSpi spi = (ImageTranscoderSpi)elt;1223String readerName = spi.getReaderServiceProviderName();1224String writerName = spi.getWriterServiceProviderName();1225return (readerName.equals(readerSpiName) &&1226writerName.equals(writerSpiName));1227}1228}12291230/**1231* Returns an {@code Iterator} containing all currently1232* registered {@code ImageTranscoder}s that claim to be1233* able to transcode between the metadata of the given1234* {@code ImageReader} and {@code ImageWriter}.1235*1236* @param reader an {@code ImageReader}.1237* @param writer an {@code ImageWriter}.1238*1239* @return an {@code Iterator} containing1240* {@code ImageTranscoder}s.1241*1242* @exception IllegalArgumentException if {@code reader} or1243* {@code writer} is {@code null}.1244*/1245public static Iterator<ImageTranscoder>1246getImageTranscoders(ImageReader reader, ImageWriter writer)1247{1248if (reader == null) {1249throw new IllegalArgumentException("reader == null!");1250}1251if (writer == null) {1252throw new IllegalArgumentException("writer == null!");1253}1254ImageReaderSpi readerSpi = reader.getOriginatingProvider();1255ImageWriterSpi writerSpi = writer.getOriginatingProvider();1256ServiceRegistry.Filter filter =1257new TranscoderFilter(readerSpi, writerSpi);12581259Iterator<ImageTranscoderSpi> iter;1260// Ensure category is present1261try {1262iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class,1263filter, true);1264} catch (IllegalArgumentException e) {1265return Collections.emptyIterator();1266}1267return new ImageTranscoderIterator(iter);1268}12691270// All-in-one methods12711272/**1273* Returns a {@code BufferedImage} as the result of decoding1274* a supplied {@code File} with an {@code ImageReader}1275* chosen automatically from among those currently registered.1276* The {@code File} is wrapped in an1277* {@code ImageInputStream}. If no registered1278* {@code ImageReader} claims to be able to read the1279* resulting stream, {@code null} is returned.1280*1281* <p> The current cache settings from {@code getUseCache} and1282* {@code getCacheDirectory} will be used to control caching in the1283* {@code ImageInputStream} that is created.1284*1285* <p> Note that there is no {@code read} method that takes a1286* filename as a {@code String}; use this method instead after1287* creating a {@code File} from the filename.1288*1289* <p> This method does not attempt to locate1290* {@code ImageReader}s that can read directly from a1291* {@code File}; that may be accomplished using1292* {@code IIORegistry} and {@code ImageReaderSpi}.1293*1294* @param input a {@code File} to read from.1295*1296* @return a {@code BufferedImage} containing the decoded1297* contents of the input, or {@code null}.1298*1299* @exception IllegalArgumentException if {@code input} is1300* {@code null}.1301* @exception IOException if an error occurs during reading or when not1302* able to create required ImageInputStream.1303*/1304public static BufferedImage read(File input) throws IOException {1305if (input == null) {1306throw new IllegalArgumentException("input == null!");1307}1308if (!input.canRead()) {1309throw new IIOException("Can't read input file!");1310}13111312ImageInputStream stream = createImageInputStream(input);1313if (stream == null) {1314throw new IIOException("Can't create an ImageInputStream!");1315}1316BufferedImage bi = read(stream);1317if (bi == null) {1318stream.close();1319}1320return bi;1321}13221323/**1324* Returns a {@code BufferedImage} as the result of decoding1325* a supplied {@code InputStream} with an {@code ImageReader}1326* chosen automatically from among those currently registered.1327* The {@code InputStream} is wrapped in an1328* {@code ImageInputStream}. If no registered1329* {@code ImageReader} claims to be able to read the1330* resulting stream, {@code null} is returned.1331*1332* <p> The current cache settings from {@code getUseCache} and1333* {@code getCacheDirectory} will be used to control caching in the1334* {@code ImageInputStream} that is created.1335*1336* <p> This method does not attempt to locate1337* {@code ImageReader}s that can read directly from an1338* {@code InputStream}; that may be accomplished using1339* {@code IIORegistry} and {@code ImageReaderSpi}.1340*1341* <p> This method <em>does not</em> close the provided1342* {@code InputStream} after the read operation has completed;1343* it is the responsibility of the caller to close the stream, if desired.1344*1345* @param input an {@code InputStream} to read from.1346*1347* @return a {@code BufferedImage} containing the decoded1348* contents of the input, or {@code null}.1349*1350* @exception IllegalArgumentException if {@code input} is1351* {@code null}.1352* @exception IOException if an error occurs during reading or when not1353* able to create required ImageInputStream.1354*/1355public static BufferedImage read(InputStream input) throws IOException {1356if (input == null) {1357throw new IllegalArgumentException("input == null!");1358}13591360ImageInputStream stream = createImageInputStream(input);1361if (stream == null) {1362throw new IIOException("Can't create an ImageInputStream!");1363}1364BufferedImage bi = read(stream);1365if (bi == null) {1366stream.close();1367}1368return bi;1369}13701371/**1372* Returns a {@code BufferedImage} as the result of decoding1373* a supplied {@code URL} with an {@code ImageReader}1374* chosen automatically from among those currently registered. An1375* {@code InputStream} is obtained from the {@code URL},1376* which is wrapped in an {@code ImageInputStream}. If no1377* registered {@code ImageReader} claims to be able to read1378* the resulting stream, {@code null} is returned.1379*1380* <p> The current cache settings from {@code getUseCache} and1381* {@code getCacheDirectory} will be used to control caching in the1382* {@code ImageInputStream} that is created.1383*1384* <p> This method does not attempt to locate1385* {@code ImageReader}s that can read directly from a1386* {@code URL}; that may be accomplished using1387* {@code IIORegistry} and {@code ImageReaderSpi}.1388*1389* @param input a {@code URL} to read from.1390*1391* @return a {@code BufferedImage} containing the decoded1392* contents of the input, or {@code null}.1393*1394* @exception IllegalArgumentException if {@code input} is1395* {@code null}.1396* @exception IOException if an error occurs during reading or when not1397* able to create required ImageInputStream.1398*/1399public static BufferedImage read(URL input) throws IOException {1400if (input == null) {1401throw new IllegalArgumentException("input == null!");1402}14031404InputStream istream = null;1405try {1406istream = input.openStream();1407} catch (IOException e) {1408throw new IIOException("Can't get input stream from URL!", e);1409}1410ImageInputStream stream = createImageInputStream(istream);1411if (stream == null) {1412/* close the istream when stream is null so that if user has1413* given filepath as URL he can delete it, otherwise stream will1414* be open to that file and he will not be able to delete it.1415*/1416istream.close();1417throw new IIOException("Can't create an ImageInputStream!");1418}1419BufferedImage bi;1420try {1421bi = read(stream);1422if (bi == null) {1423stream.close();1424}1425} finally {1426istream.close();1427}1428return bi;1429}14301431/**1432* Returns a {@code BufferedImage} as the result of decoding1433* a supplied {@code ImageInputStream} with an1434* {@code ImageReader} chosen automatically from among those1435* currently registered. If no registered1436* {@code ImageReader} claims to be able to read the stream,1437* {@code null} is returned.1438*1439* <p> Unlike most other methods in this class, this method <em>does</em>1440* close the provided {@code ImageInputStream} after the read1441* operation has completed, unless {@code null} is returned,1442* in which case this method <em>does not</em> close the stream.1443*1444* @param stream an {@code ImageInputStream} to read from.1445*1446* @return a {@code BufferedImage} containing the decoded1447* contents of the input, or {@code null}.1448*1449* @exception IllegalArgumentException if {@code stream} is1450* {@code null}.1451* @exception IOException if an error occurs during reading.1452*/1453public static BufferedImage read(ImageInputStream stream)1454throws IOException {1455if (stream == null) {1456throw new IllegalArgumentException("stream == null!");1457}14581459Iterator<ImageReader> iter = getImageReaders(stream);1460if (!iter.hasNext()) {1461return null;1462}14631464ImageReader reader = iter.next();1465ImageReadParam param = reader.getDefaultReadParam();1466reader.setInput(stream, true, true);1467BufferedImage bi;1468try {1469bi = reader.read(0, param);1470} finally {1471reader.dispose();1472stream.close();1473}1474return bi;1475}14761477/**1478* Writes an image using the an arbitrary {@code ImageWriter}1479* that supports the given format to an1480* {@code ImageOutputStream}. The image is written to the1481* {@code ImageOutputStream} starting at the current stream1482* pointer, overwriting existing stream data from that point1483* forward, if present.1484*1485* <p> This method <em>does not</em> close the provided1486* {@code ImageOutputStream} after the write operation has completed;1487* it is the responsibility of the caller to close the stream, if desired.1488*1489* @param im a {@code RenderedImage} to be written.1490* @param formatName a {@code String} containing the informal1491* name of the format.1492* @param output an {@code ImageOutputStream} to be written to.1493*1494* @return {@code false} if no appropriate writer is found.1495*1496* @exception IllegalArgumentException if any parameter is1497* {@code null}.1498* @exception IOException if an error occurs during writing.1499*/1500public static boolean write(RenderedImage im,1501String formatName,1502ImageOutputStream output) throws IOException {1503if (im == null) {1504throw new IllegalArgumentException("im == null!");1505}1506if (formatName == null) {1507throw new IllegalArgumentException("formatName == null!");1508}1509if (output == null) {1510throw new IllegalArgumentException("output == null!");1511}15121513return doWrite(im, getWriter(im, formatName), output);1514}15151516/**1517* Writes an image using an arbitrary {@code ImageWriter}1518* that supports the given format to a {@code File}. If1519* there is already a {@code File} present, its contents are1520* discarded.1521*1522* @param im a {@code RenderedImage} to be written.1523* @param formatName a {@code String} containing the informal1524* name of the format.1525* @param output a {@code File} to be written to.1526*1527* @return {@code false} if no appropriate writer is found.1528*1529* @exception IllegalArgumentException if any parameter is1530* {@code null}.1531* @exception IOException if an error occurs during writing or when not1532* able to create required ImageOutputStream.1533*/1534public static boolean write(RenderedImage im,1535String formatName,1536File output) throws IOException {1537if (output == null) {1538throw new IllegalArgumentException("output == null!");1539}15401541ImageWriter writer = getWriter(im, formatName);1542if (writer == null) {1543/* Do not make changes in the file system if we have1544* no appropriate writer.1545*/1546return false;1547}15481549output.delete();1550ImageOutputStream stream = createImageOutputStream(output);1551if (stream == null) {1552throw new IIOException("Can't create an ImageOutputStream!");1553}1554try {1555return doWrite(im, writer, stream);1556} finally {1557stream.close();1558}1559}15601561/**1562* Writes an image using an arbitrary {@code ImageWriter}1563* that supports the given format to an {@code OutputStream}.1564*1565* <p> This method <em>does not</em> close the provided1566* {@code OutputStream} after the write operation has completed;1567* it is the responsibility of the caller to close the stream, if desired.1568*1569* <p> The current cache settings from {@code getUseCache} and1570* {@code getCacheDirectory} will be used to control caching.1571*1572* @param im a {@code RenderedImage} to be written.1573* @param formatName a {@code String} containing the informal1574* name of the format.1575* @param output an {@code OutputStream} to be written to.1576*1577* @return {@code false} if no appropriate writer is found.1578*1579* @exception IllegalArgumentException if any parameter is1580* {@code null}.1581* @exception IOException if an error occurs during writing or when not1582* able to create required ImageOutputStream.1583*/1584public static boolean write(RenderedImage im,1585String formatName,1586OutputStream output) throws IOException {1587if (output == null) {1588throw new IllegalArgumentException("output == null!");1589}1590ImageOutputStream stream = createImageOutputStream(output);1591if (stream == null) {1592throw new IIOException("Can't create an ImageOutputStream!");1593}1594try {1595return doWrite(im, getWriter(im, formatName), stream);1596} finally {1597stream.close();1598}1599}16001601/**1602* Returns {@code ImageWriter} instance according to given1603* rendered image and image format or {@code null} if there1604* is no appropriate writer.1605*/1606private static ImageWriter getWriter(RenderedImage im,1607String formatName) {1608ImageTypeSpecifier type =1609ImageTypeSpecifier.createFromRenderedImage(im);1610Iterator<ImageWriter> iter = getImageWriters(type, formatName);16111612if (iter.hasNext()) {1613return iter.next();1614} else {1615return null;1616}1617}16181619/**1620* Writes image to output stream using given image writer.1621*/1622private static boolean doWrite(RenderedImage im, ImageWriter writer,1623ImageOutputStream output) throws IOException {1624if (writer == null) {1625return false;1626}1627writer.setOutput(output);1628try {1629writer.write(im);1630} finally {1631writer.dispose();1632output.flush();1633}1634return true;1635}1636}163716381639