/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.imageio;
import java.awt.image.
BufferedImage;
import java.awt.image.
RenderedImage;
import java.io.
File;
import java.io.
FilePermission;
import java.io.
InputStream;
import java.io.
IOException;
import java.io.
OutputStream;
import java.lang.reflect.
Method;
import java.net.
URL;
import java.security.
AccessController;
import java.util.
Arrays;
import java.util.
Collections;
import java.util.
HashSet;
import java.util.
Iterator;
import java.util.
NoSuchElementException;
import java.util.
Set;
import javax.imageio.spi.
IIORegistry;
import javax.imageio.spi.
ImageReaderSpi;
import javax.imageio.spi.
ImageReaderWriterSpi;
import javax.imageio.spi.
ImageWriterSpi;
import javax.imageio.spi.
ImageInputStreamSpi;
import javax.imageio.spi.
ImageOutputStreamSpi;
import javax.imageio.spi.
ImageTranscoderSpi;
import javax.imageio.spi.
ServiceRegistry;
import javax.imageio.stream.
ImageInputStream;
import javax.imageio.stream.
ImageOutputStream;
import sun.awt.
AppContext;
import sun.security.action.
GetPropertyAction;
/**
* A class containing static convenience methods for locating
* <code>ImageReader</code>s and <code>ImageWriter</code>s, and
* performing simple encoding and decoding.
*
*/
public final class
ImageIO {
private static final
IIORegistry theRegistry =
IIORegistry.
getDefaultInstance();
/**
* Constructor is private to prevent instantiation.
*/
private
ImageIO() {}
/**
* Scans for plug-ins on the application class path,
* loads their service provider classes, and registers a service
* provider instance for each one found with the
* <code>IIORegistry</code>.
*
* <p>This method is needed because the application class path can
* theoretically change, or additional plug-ins may become available.
* Rather than re-scanning the classpath on every invocation of the
* API, the class path is scanned automatically only on the first
* invocation. Clients can call this method to prompt a re-scan.
* Thus this method need only be invoked by sophisticated applications
* which dynamically make new plug-ins available at runtime.
*
* <p> The <code>getResources</code> method of the context
* <code>ClassLoader</code> is used locate JAR files containing
* files named
* <code>META-INF/services/javax.imageio.spi.</code><i>classname</i>,
* where <i>classname</i> is one of <code>ImageReaderSpi</code>,
* <code>ImageWriterSpi</code>, <code>ImageTranscoderSpi</code>,
* <code>ImageInputStreamSpi</code>, or
* <code>ImageOutputStreamSpi</code>, along the application class
* path.
*
* <p> The contents of the located files indicate the names of
* actual implementation classes which implement the
* aforementioned service provider interfaces; the default class
* loader is then used to load each of these classes and to
* instantiate an instance of each class, which is then placed
* into the registry for later retrieval.
*
* <p> The exact set of locations searched depends on the
* implementation of the Java runtime environment.
*
* @see ClassLoader#getResources
*/
public static void
scanForPlugins() {
theRegistry.
registerApplicationClasspathSpis();
}
// ImageInputStreams
/**
* A class to hold information about caching. Each
* <code>ThreadGroup</code> will have its own copy
* via the <code>AppContext</code> mechanism.
*/
static class
CacheInfo {
boolean
useCache = true;
File cacheDirectory = null;
Boolean hasPermission = null;
public
CacheInfo() {}
public boolean
getUseCache() {
return
useCache;
}
public void
setUseCache(boolean
useCache) {
this.
useCache =
useCache;
}
public
File getCacheDirectory() {
return
cacheDirectory;
}
public void
setCacheDirectory(
File cacheDirectory) {
this.
cacheDirectory =
cacheDirectory;
}
public
Boolean getHasPermission() {
return
hasPermission;
}
public void
setHasPermission(
Boolean hasPermission) {
this.
hasPermission =
hasPermission;
}
}
/**
* Returns the <code>CacheInfo</code> object associated with this
* <code>ThreadGroup</code>.
*/
private static synchronized
CacheInfo getCacheInfo() {
AppContext context =
AppContext.
getAppContext();
CacheInfo info = (
CacheInfo)
context.
get(
CacheInfo.class);
if (
info == null) {
info = new
CacheInfo();
context.
put(
CacheInfo.class,
info);
}
return
info;
}
/**
* Returns the default temporary (cache) directory as defined by the
* java.io.tmpdir system property.
*/
private static
String getTempDir() {
GetPropertyAction a = new
GetPropertyAction("java.io.tmpdir");
return (
String)
AccessController.
doPrivileged(
a);
}
/**
* Determines whether the caller has write access to the cache
* directory, stores the result in the <code>CacheInfo</code> object,
* and returns the decision. This method helps to prevent mysterious
* SecurityExceptions to be thrown when this convenience class is used
* in an applet, for example.
*/
private static boolean
hasCachePermission() {
Boolean hasPermission =
getCacheInfo().
getHasPermission();
if (
hasPermission != null) {
return
hasPermission.
booleanValue();
} else {
try {
SecurityManager security =
System.
getSecurityManager();
if (
security != null) {
File cachedir =
getCacheDirectory();
String cachepath;
if (
cachedir != null) {
cachepath =
cachedir.
getPath();
} else {
cachepath =
getTempDir();
if (
cachepath == null ||
cachepath.
isEmpty()) {
getCacheInfo().
setHasPermission(
Boolean.
FALSE);
return false;
}
}
// we have to check whether we can read, write,
// and delete cache files.
// So, compose cache file path and check it.
String filepath =
cachepath;
if (!
filepath.
endsWith(
File.
separator)) {
filepath +=
File.
separator;
}
filepath += "*";
security.
checkPermission(new
FilePermission(
filepath, "read, write, delete"));
}
} catch (
SecurityException e) {
getCacheInfo().
setHasPermission(
Boolean.
FALSE);
return false;
}
getCacheInfo().
setHasPermission(
Boolean.
TRUE);
return true;
}
}
/**
* Sets a flag indicating whether a disk-based cache file should
* be used when creating <code>ImageInputStream</code>s and
* <code>ImageOutputStream</code>s.
*
* <p> When reading from a standard <code>InputStream</code>, it
* may be necessary to save previously read information in a cache
* since the underlying stream does not allow data to be re-read.
* Similarly, when writing to a standard
* <code>OutputStream</code>, a cache may be used to allow a
* previously written value to be changed before flushing it to
* the final destination.
*
* <p> The cache may reside in main memory or on disk. Setting
* this flag to <code>false</code> disallows the use of disk for
* future streams, which may be advantageous when working with
* small images, as the overhead of creating and destroying files
* is removed.
*
* <p> On startup, the value is set to <code>true</code>.
*
* @param useCache a <code>boolean</code> indicating whether a
* cache file should be used, in cases where it is optional.
*
* @see #getUseCache
*/
public static void
setUseCache(boolean
useCache) {
getCacheInfo().
setUseCache(
useCache);
}
/**
* Returns the current value set by <code>setUseCache</code>, or
* <code>true</code> if no explicit setting has been made.
*
* @return true if a disk-based cache may be used for
* <code>ImageInputStream</code>s and
* <code>ImageOutputStream</code>s.
*
* @see #setUseCache
*/
public static boolean
getUseCache() {
return
getCacheInfo().
getUseCache();
}
/**
* Sets the directory where cache files are to be created. A
* value of <code>null</code> indicates that the system-dependent
* default temporary-file directory is to be used. If
* <code>getUseCache</code> returns false, this value is ignored.
*
* @param cacheDirectory a <code>File</code> specifying a directory.
*
* @see File#createTempFile(String, String, File)
*
* @exception SecurityException if the security manager denies
* access to the directory.
* @exception IllegalArgumentException if <code>cacheDir</code> is
* non-<code>null</code> but is not a directory.
*
* @see #getCacheDirectory
*/
public static void
setCacheDirectory(
File cacheDirectory) {
if ((
cacheDirectory != null) && !(
cacheDirectory.
isDirectory())) {
throw new
IllegalArgumentException("Not a directory!");
}
getCacheInfo().
setCacheDirectory(
cacheDirectory);
getCacheInfo().
setHasPermission(null);
}
/**
* Returns the current value set by
* <code>setCacheDirectory</code>, or <code>null</code> if no
* explicit setting has been made.
*
* @return a <code>File</code> indicating the directory where
* cache files will be created, or <code>null</code> to indicate
* the system-dependent default temporary-file directory.
*
* @see #setCacheDirectory
*/
public static
File getCacheDirectory() {
return
getCacheInfo().
getCacheDirectory();
}
/**
* Returns an <code>ImageInputStream</code> that will take its
* input from the given <code>Object</code>. The set of
* <code>ImageInputStreamSpi</code>s registered with the
* <code>IIORegistry</code> class is queried and the first one
* that is able to take input from the supplied object is used to
* create the returned <code>ImageInputStream</code>. If no
* suitable <code>ImageInputStreamSpi</code> exists,
* <code>null</code> is returned.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching.
*
* @param input an <code>Object</code> to be used as an input
* source, such as a <code>File</code>, readable
* <code>RandomAccessFile</code>, or <code>InputStream</code>.
*
* @return an <code>ImageInputStream</code>, or <code>null</code>.
*
* @exception IllegalArgumentException if <code>input</code>
* is <code>null</code>.
* @exception IOException if a cache file is needed but cannot be
* created.
*
* @see javax.imageio.spi.ImageInputStreamSpi
*/
public static
ImageInputStream createImageInputStream(
Object input)
throws
IOException {
if (
input == null) {
throw new
IllegalArgumentException("input == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageInputStreamSpi.class,
true);
} catch (
IllegalArgumentException e) {
return null;
}
boolean
usecache =
getUseCache() &&
hasCachePermission();
while (
iter.
hasNext()) {
ImageInputStreamSpi spi = (
ImageInputStreamSpi)
iter.
next();
if (
spi.
getInputClass().
isInstance(
input)) {
try {
return
spi.
createInputStreamInstance(
input,
usecache,
getCacheDirectory());
} catch (
IOException e) {
throw new
IIOException("Can't create cache file!",
e);
}
}
}
return null;
}
// ImageOutputStreams
/**
* Returns an <code>ImageOutputStream</code> that will send its
* output to the given <code>Object</code>. The set of
* <code>ImageOutputStreamSpi</code>s registered with the
* <code>IIORegistry</code> class is queried and the first one
* that is able to send output from the supplied object is used to
* create the returned <code>ImageOutputStream</code>. If no
* suitable <code>ImageOutputStreamSpi</code> exists,
* <code>null</code> is returned.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching.
*
* @param output an <code>Object</code> to be used as an output
* destination, such as a <code>File</code>, writable
* <code>RandomAccessFile</code>, or <code>OutputStream</code>.
*
* @return an <code>ImageOutputStream</code>, or
* <code>null</code>.
*
* @exception IllegalArgumentException if <code>output</code> is
* <code>null</code>.
* @exception IOException if a cache file is needed but cannot be
* created.
*
* @see javax.imageio.spi.ImageOutputStreamSpi
*/
public static
ImageOutputStream createImageOutputStream(
Object output)
throws
IOException {
if (
output == null) {
throw new
IllegalArgumentException("output == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageOutputStreamSpi.class,
true);
} catch (
IllegalArgumentException e) {
return null;
}
boolean
usecache =
getUseCache() &&
hasCachePermission();
while (
iter.
hasNext()) {
ImageOutputStreamSpi spi = (
ImageOutputStreamSpi)
iter.
next();
if (
spi.
getOutputClass().
isInstance(
output)) {
try {
return
spi.
createOutputStreamInstance(
output,
usecache,
getCacheDirectory());
} catch (
IOException e) {
throw new
IIOException("Can't create cache file!",
e);
}
}
}
return null;
}
private static enum
SpiInfo {
FORMAT_NAMES {
@
Override
String[]
info(
ImageReaderWriterSpi spi) {
return
spi.
getFormatNames();
}
},
MIME_TYPES {
@
Override
String[]
info(
ImageReaderWriterSpi spi) {
return
spi.
getMIMETypes();
}
},
FILE_SUFFIXES {
@
Override
String[]
info(
ImageReaderWriterSpi spi) {
return
spi.
getFileSuffixes();
}
};
abstract
String[]
info(
ImageReaderWriterSpi spi);
}
private static <S extends
ImageReaderWriterSpi>
String[]
getReaderWriterInfo(
Class<S>
spiClass,
SpiInfo spiInfo)
{
// Ensure category is present
Iterator<S>
iter;
try {
iter =
theRegistry.
getServiceProviders(
spiClass, true);
} catch (
IllegalArgumentException e) {
return new
String[0];
}
HashSet<
String>
s = new
HashSet<
String>();
while (
iter.
hasNext()) {
ImageReaderWriterSpi spi =
iter.
next();
Collections.
addAll(
s,
spiInfo.
info(
spi));
}
return
s.
toArray(new
String[
s.
size()]);
}
// Readers
/**
* Returns an array of <code>String</code>s listing all of the
* informal format names understood by the current set of registered
* readers.
*
* @return an array of <code>String</code>s.
*/
public static
String[]
getReaderFormatNames() {
return
getReaderWriterInfo(
ImageReaderSpi.class,
SpiInfo.
FORMAT_NAMES);
}
/**
* Returns an array of <code>String</code>s listing all of the
* MIME types understood by the current set of registered
* readers.
*
* @return an array of <code>String</code>s.
*/
public static
String[]
getReaderMIMETypes() {
return
getReaderWriterInfo(
ImageReaderSpi.class,
SpiInfo.
MIME_TYPES);
}
/**
* Returns an array of <code>String</code>s listing all of the
* file suffixes associated with the formats understood
* by the current set of registered readers.
*
* @return an array of <code>String</code>s.
* @since 1.6
*/
public static
String[]
getReaderFileSuffixes() {
return
getReaderWriterInfo(
ImageReaderSpi.class,
SpiInfo.
FILE_SUFFIXES);
}
static class
ImageReaderIterator implements
Iterator<
ImageReader> {
// Contains ImageReaderSpis
public
Iterator iter;
public
ImageReaderIterator(
Iterator iter) {
this.
iter =
iter;
}
public boolean
hasNext() {
return
iter.
hasNext();
}
public
ImageReader next() {
ImageReaderSpi spi = null;
try {
spi = (
ImageReaderSpi)
iter.
next();
return
spi.
createReaderInstance();
} catch (
IOException e) {
// Deregister the spi in this case, but only as
// an ImageReaderSpi
theRegistry.
deregisterServiceProvider(
spi,
ImageReaderSpi.class);
}
return null;
}
public void
remove() {
throw new
UnsupportedOperationException();
}
}
static class
CanDecodeInputFilter
implements
ServiceRegistry.
Filter {
Object input;
public
CanDecodeInputFilter(
Object input) {
this.
input =
input;
}
public boolean
filter(
Object elt) {
try {
ImageReaderSpi spi = (
ImageReaderSpi)
elt;
ImageInputStream stream = null;
if (
input instanceof
ImageInputStream) {
stream = (
ImageInputStream)
input;
}
// Perform mark/reset as a defensive measure
// even though plug-ins are supposed to take
// care of it.
boolean
canDecode = false;
if (
stream != null) {
stream.
mark();
}
canDecode =
spi.
canDecodeInput(
input);
if (
stream != null) {
stream.
reset();
}
return
canDecode;
} catch (
IOException e) {
return false;
}
}
}
static class
CanEncodeImageAndFormatFilter
implements
ServiceRegistry.
Filter {
ImageTypeSpecifier type;
String formatName;
public
CanEncodeImageAndFormatFilter(
ImageTypeSpecifier type,
String formatName) {
this.
type =
type;
this.
formatName =
formatName;
}
public boolean
filter(
Object elt) {
ImageWriterSpi spi = (
ImageWriterSpi)
elt;
return
Arrays.
asList(
spi.
getFormatNames()).
contains(
formatName) &&
spi.
canEncodeImage(
type);
}
}
static class
ContainsFilter
implements
ServiceRegistry.
Filter {
Method method;
String name;
// method returns an array of Strings
public
ContainsFilter(
Method method,
String name) {
this.
method =
method;
this.
name =
name;
}
public boolean
filter(
Object elt) {
try {
return
contains((
String[])
method.
invoke(
elt),
name);
} catch (
Exception e) {
return false;
}
}
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageReader</code>s that claim to be able to
* decode the supplied <code>Object</code>, typically an
* <code>ImageInputStream</code>.
*
* <p> The stream position is left at its prior position upon
* exit from this method.
*
* @param input an <code>ImageInputStream</code> or other
* <code>Object</code> containing encoded image data.
*
* @return an <code>Iterator</code> containing <code>ImageReader</code>s.
*
* @exception IllegalArgumentException if <code>input</code> is
* <code>null</code>.
*
* @see javax.imageio.spi.ImageReaderSpi#canDecodeInput
*/
public static
Iterator<
ImageReader>
getImageReaders(
Object input) {
if (
input == null) {
throw new
IllegalArgumentException("input == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageReaderSpi.class,
new
CanDecodeInputFilter(
input),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageReaderIterator(
iter);
}
private static
Method readerFormatNamesMethod;
private static
Method readerFileSuffixesMethod;
private static
Method readerMIMETypesMethod;
private static
Method writerFormatNamesMethod;
private static
Method writerFileSuffixesMethod;
private static
Method writerMIMETypesMethod;
static {
try {
readerFormatNamesMethod =
ImageReaderSpi.class.
getMethod("getFormatNames");
readerFileSuffixesMethod =
ImageReaderSpi.class.
getMethod("getFileSuffixes");
readerMIMETypesMethod =
ImageReaderSpi.class.
getMethod("getMIMETypes");
writerFormatNamesMethod =
ImageWriterSpi.class.
getMethod("getFormatNames");
writerFileSuffixesMethod =
ImageWriterSpi.class.
getMethod("getFileSuffixes");
writerMIMETypesMethod =
ImageWriterSpi.class.
getMethod("getMIMETypes");
} catch (
NoSuchMethodException e) {
e.
printStackTrace();
}
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageReader</code>s that claim to be able to
* decode the named format.
*
* @param formatName a <code>String</code> containing the informal
* name of a format (<i>e.g.</i>, "jpeg" or "tiff".
*
* @return an <code>Iterator</code> containing
* <code>ImageReader</code>s.
*
* @exception IllegalArgumentException if <code>formatName</code>
* is <code>null</code>.
*
* @see javax.imageio.spi.ImageReaderSpi#getFormatNames
*/
public static
Iterator<
ImageReader>
getImageReadersByFormatName(
String formatName)
{
if (
formatName == null) {
throw new
IllegalArgumentException("formatName == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageReaderSpi.class,
new
ContainsFilter(
readerFormatNamesMethod,
formatName),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageReaderIterator(
iter);
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageReader</code>s that claim to be able to
* decode files with the given suffix.
*
* @param fileSuffix a <code>String</code> containing a file
* suffix (<i>e.g.</i>, "jpg" or "tiff").
*
* @return an <code>Iterator</code> containing
* <code>ImageReader</code>s.
*
* @exception IllegalArgumentException if <code>fileSuffix</code>
* is <code>null</code>.
*
* @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes
*/
public static
Iterator<
ImageReader>
getImageReadersBySuffix(
String fileSuffix)
{
if (
fileSuffix == null) {
throw new
IllegalArgumentException("fileSuffix == null!");
}
// Ensure category is present
Iterator iter;
try {
iter =
theRegistry.
getServiceProviders(
ImageReaderSpi.class,
new
ContainsFilter(
readerFileSuffixesMethod,
fileSuffix),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageReaderIterator(
iter);
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageReader</code>s that claim to be able to
* decode files with the given MIME type.
*
* @param MIMEType a <code>String</code> containing a file
* suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
*
* @return an <code>Iterator</code> containing
* <code>ImageReader</code>s.
*
* @exception IllegalArgumentException if <code>MIMEType</code> is
* <code>null</code>.
*
* @see javax.imageio.spi.ImageReaderSpi#getMIMETypes
*/
public static
Iterator<
ImageReader>
getImageReadersByMIMEType(
String MIMEType)
{
if (
MIMEType == null) {
throw new
IllegalArgumentException("MIMEType == null!");
}
// Ensure category is present
Iterator iter;
try {
iter =
theRegistry.
getServiceProviders(
ImageReaderSpi.class,
new
ContainsFilter(
readerMIMETypesMethod,
MIMEType),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageReaderIterator(
iter);
}
// Writers
/**
* Returns an array of <code>String</code>s listing all of the
* informal format names understood by the current set of registered
* writers.
*
* @return an array of <code>String</code>s.
*/
public static
String[]
getWriterFormatNames() {
return
getReaderWriterInfo(
ImageWriterSpi.class,
SpiInfo.
FORMAT_NAMES);
}
/**
* Returns an array of <code>String</code>s listing all of the
* MIME types understood by the current set of registered
* writers.
*
* @return an array of <code>String</code>s.
*/
public static
String[]
getWriterMIMETypes() {
return
getReaderWriterInfo(
ImageWriterSpi.class,
SpiInfo.
MIME_TYPES);
}
/**
* Returns an array of <code>String</code>s listing all of the
* file suffixes associated with the formats understood
* by the current set of registered writers.
*
* @return an array of <code>String</code>s.
* @since 1.6
*/
public static
String[]
getWriterFileSuffixes() {
return
getReaderWriterInfo(
ImageWriterSpi.class,
SpiInfo.
FILE_SUFFIXES);
}
static class
ImageWriterIterator implements
Iterator<
ImageWriter> {
// Contains ImageWriterSpis
public
Iterator iter;
public
ImageWriterIterator(
Iterator iter) {
this.
iter =
iter;
}
public boolean
hasNext() {
return
iter.
hasNext();
}
public
ImageWriter next() {
ImageWriterSpi spi = null;
try {
spi = (
ImageWriterSpi)
iter.
next();
return
spi.
createWriterInstance();
} catch (
IOException e) {
// Deregister the spi in this case, but only as a writerSpi
theRegistry.
deregisterServiceProvider(
spi,
ImageWriterSpi.class);
}
return null;
}
public void
remove() {
throw new
UnsupportedOperationException();
}
}
private static boolean
contains(
String[]
names,
String name) {
for (int
i = 0;
i <
names.length;
i++) {
if (
name.
equalsIgnoreCase(
names[
i])) {
return true;
}
}
return false;
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageWriter</code>s that claim to be able to
* encode the named format.
*
* @param formatName a <code>String</code> containing the informal
* name of a format (<i>e.g.</i>, "jpeg" or "tiff".
*
* @return an <code>Iterator</code> containing
* <code>ImageWriter</code>s.
*
* @exception IllegalArgumentException if <code>formatName</code> is
* <code>null</code>.
*
* @see javax.imageio.spi.ImageWriterSpi#getFormatNames
*/
public static
Iterator<
ImageWriter>
getImageWritersByFormatName(
String formatName)
{
if (
formatName == null) {
throw new
IllegalArgumentException("formatName == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageWriterSpi.class,
new
ContainsFilter(
writerFormatNamesMethod,
formatName),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageWriterIterator(
iter);
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageWriter</code>s that claim to be able to
* encode files with the given suffix.
*
* @param fileSuffix a <code>String</code> containing a file
* suffix (<i>e.g.</i>, "jpg" or "tiff").
*
* @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
*
* @exception IllegalArgumentException if <code>fileSuffix</code> is
* <code>null</code>.
*
* @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes
*/
public static
Iterator<
ImageWriter>
getImageWritersBySuffix(
String fileSuffix)
{
if (
fileSuffix == null) {
throw new
IllegalArgumentException("fileSuffix == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageWriterSpi.class,
new
ContainsFilter(
writerFileSuffixesMethod,
fileSuffix),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageWriterIterator(
iter);
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageWriter</code>s that claim to be able to
* encode files with the given MIME type.
*
* @param MIMEType a <code>String</code> containing a file
* suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp").
*
* @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
*
* @exception IllegalArgumentException if <code>MIMEType</code> is
* <code>null</code>.
*
* @see javax.imageio.spi.ImageWriterSpi#getMIMETypes
*/
public static
Iterator<
ImageWriter>
getImageWritersByMIMEType(
String MIMEType)
{
if (
MIMEType == null) {
throw new
IllegalArgumentException("MIMEType == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageWriterSpi.class,
new
ContainsFilter(
writerMIMETypesMethod,
MIMEType),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageWriterIterator(
iter);
}
/**
* Returns an <code>ImageWriter</code>corresponding to the given
* <code>ImageReader</code>, if there is one, or <code>null</code>
* if the plug-in for this <code>ImageReader</code> does not
* specify a corresponding <code>ImageWriter</code>, or if the
* given <code>ImageReader</code> is not registered. This
* mechanism may be used to obtain an <code>ImageWriter</code>
* that will understand the internal structure of non-pixel
* metadata (as encoded by <code>IIOMetadata</code> objects)
* generated by the <code>ImageReader</code>. By obtaining this
* data from the <code>ImageReader</code> and passing it on to the
* <code>ImageWriter</code> obtained with this method, a client
* program can read an image, modify it in some way, and write it
* back out preserving all metadata, without having to understand
* anything about the structure of the metadata, or even about
* the image format. Note that this method returns the
* "preferred" writer, which is the first in the list returned by
* <code>javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()</code>.
*
* @param reader an instance of a registered <code>ImageReader</code>.
*
* @return an <code>ImageWriter</code>, or null.
*
* @exception IllegalArgumentException if <code>reader</code> is
* <code>null</code>.
*
* @see #getImageReader(ImageWriter)
* @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames()
*/
public static
ImageWriter getImageWriter(
ImageReader reader) {
if (
reader == null) {
throw new
IllegalArgumentException("reader == null!");
}
ImageReaderSpi readerSpi =
reader.
getOriginatingProvider();
if (
readerSpi == null) {
Iterator readerSpiIter;
// Ensure category is present
try {
readerSpiIter =
theRegistry.
getServiceProviders(
ImageReaderSpi.class,
false);
} catch (
IllegalArgumentException e) {
return null;
}
while (
readerSpiIter.
hasNext()) {
ImageReaderSpi temp = (
ImageReaderSpi)
readerSpiIter.
next();
if (
temp.
isOwnReader(
reader)) {
readerSpi =
temp;
break;
}
}
if (
readerSpi == null) {
return null;
}
}
String[]
writerNames =
readerSpi.
getImageWriterSpiNames();
if (
writerNames == null) {
return null;
}
Class writerSpiClass = null;
try {
writerSpiClass =
Class.
forName(
writerNames[0], true,
ClassLoader.
getSystemClassLoader());
} catch (
ClassNotFoundException e) {
return null;
}
ImageWriterSpi writerSpi = (
ImageWriterSpi)
theRegistry.
getServiceProviderByClass(
writerSpiClass);
if (
writerSpi == null) {
return null;
}
try {
return
writerSpi.
createWriterInstance();
} catch (
IOException e) {
// Deregister the spi in this case, but only as a writerSpi
theRegistry.
deregisterServiceProvider(
writerSpi,
ImageWriterSpi.class);
return null;
}
}
/**
* Returns an <code>ImageReader</code>corresponding to the given
* <code>ImageWriter</code>, if there is one, or <code>null</code>
* if the plug-in for this <code>ImageWriter</code> does not
* specify a corresponding <code>ImageReader</code>, or if the
* given <code>ImageWriter</code> is not registered. This method
* is provided principally for symmetry with
* <code>getImageWriter(ImageReader)</code>. Note that this
* method returns the "preferred" reader, which is the first in
* the list returned by
* javax.imageio.spi.ImageWriterSpi.<code>getImageReaderSpiNames()</code>.
*
* @param writer an instance of a registered <code>ImageWriter</code>.
*
* @return an <code>ImageReader</code>, or null.
*
* @exception IllegalArgumentException if <code>writer</code> is
* <code>null</code>.
*
* @see #getImageWriter(ImageReader)
* @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames()
*/
public static
ImageReader getImageReader(
ImageWriter writer) {
if (
writer == null) {
throw new
IllegalArgumentException("writer == null!");
}
ImageWriterSpi writerSpi =
writer.
getOriginatingProvider();
if (
writerSpi == null) {
Iterator writerSpiIter;
// Ensure category is present
try {
writerSpiIter =
theRegistry.
getServiceProviders(
ImageWriterSpi.class,
false);
} catch (
IllegalArgumentException e) {
return null;
}
while (
writerSpiIter.
hasNext()) {
ImageWriterSpi temp = (
ImageWriterSpi)
writerSpiIter.
next();
if (
temp.
isOwnWriter(
writer)) {
writerSpi =
temp;
break;
}
}
if (
writerSpi == null) {
return null;
}
}
String[]
readerNames =
writerSpi.
getImageReaderSpiNames();
if (
readerNames == null) {
return null;
}
Class readerSpiClass = null;
try {
readerSpiClass =
Class.
forName(
readerNames[0], true,
ClassLoader.
getSystemClassLoader());
} catch (
ClassNotFoundException e) {
return null;
}
ImageReaderSpi readerSpi = (
ImageReaderSpi)
theRegistry.
getServiceProviderByClass(
readerSpiClass);
if (
readerSpi == null) {
return null;
}
try {
return
readerSpi.
createReaderInstance();
} catch (
IOException e) {
// Deregister the spi in this case, but only as a readerSpi
theRegistry.
deregisterServiceProvider(
readerSpi,
ImageReaderSpi.class);
return null;
}
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageWriter</code>s that claim to be able to
* encode images of the given layout (specified using an
* <code>ImageTypeSpecifier</code>) in the given format.
*
* @param type an <code>ImageTypeSpecifier</code> indicating the
* layout of the image to be written.
* @param formatName the informal name of the <code>format</code>.
*
* @return an <code>Iterator</code> containing <code>ImageWriter</code>s.
*
* @exception IllegalArgumentException if any parameter is
* <code>null</code>.
*
* @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier)
*/
public static
Iterator<
ImageWriter>
getImageWriters(
ImageTypeSpecifier type,
String formatName)
{
if (
type == null) {
throw new
IllegalArgumentException("type == null!");
}
if (
formatName == null) {
throw new
IllegalArgumentException("formatName == null!");
}
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageWriterSpi.class,
new
CanEncodeImageAndFormatFilter(
type,
formatName),
true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageWriterIterator(
iter);
}
static class
ImageTranscoderIterator
implements
Iterator<
ImageTranscoder>
{
// Contains ImageTranscoderSpis
public
Iterator iter;
public
ImageTranscoderIterator(
Iterator iter) {
this.
iter =
iter;
}
public boolean
hasNext() {
return
iter.
hasNext();
}
public
ImageTranscoder next() {
ImageTranscoderSpi spi = null;
spi = (
ImageTranscoderSpi)
iter.
next();
return
spi.
createTranscoderInstance();
}
public void
remove() {
throw new
UnsupportedOperationException();
}
}
static class
TranscoderFilter
implements
ServiceRegistry.
Filter {
String readerSpiName;
String writerSpiName;
public
TranscoderFilter(
ImageReaderSpi readerSpi,
ImageWriterSpi writerSpi) {
this.
readerSpiName =
readerSpi.
getClass().
getName();
this.
writerSpiName =
writerSpi.
getClass().
getName();
}
public boolean
filter(
Object elt) {
ImageTranscoderSpi spi = (
ImageTranscoderSpi)
elt;
String readerName =
spi.
getReaderServiceProviderName();
String writerName =
spi.
getWriterServiceProviderName();
return (
readerName.
equals(
readerSpiName) &&
writerName.
equals(
writerSpiName));
}
}
/**
* Returns an <code>Iterator</code> containing all currently
* registered <code>ImageTranscoder</code>s that claim to be
* able to transcode between the metadata of the given
* <code>ImageReader</code> and <code>ImageWriter</code>.
*
* @param reader an <code>ImageReader</code>.
* @param writer an <code>ImageWriter</code>.
*
* @return an <code>Iterator</code> containing
* <code>ImageTranscoder</code>s.
*
* @exception IllegalArgumentException if <code>reader</code> or
* <code>writer</code> is <code>null</code>.
*/
public static
Iterator<
ImageTranscoder>
getImageTranscoders(
ImageReader reader,
ImageWriter writer)
{
if (
reader == null) {
throw new
IllegalArgumentException("reader == null!");
}
if (
writer == null) {
throw new
IllegalArgumentException("writer == null!");
}
ImageReaderSpi readerSpi =
reader.
getOriginatingProvider();
ImageWriterSpi writerSpi =
writer.
getOriginatingProvider();
ServiceRegistry.
Filter filter =
new
TranscoderFilter(
readerSpi,
writerSpi);
Iterator iter;
// Ensure category is present
try {
iter =
theRegistry.
getServiceProviders(
ImageTranscoderSpi.class,
filter, true);
} catch (
IllegalArgumentException e) {
return
Collections.
emptyIterator();
}
return new
ImageTranscoderIterator(
iter);
}
// All-in-one methods
/**
* Returns a <code>BufferedImage</code> as the result of decoding
* a supplied <code>File</code> with an <code>ImageReader</code>
* chosen automatically from among those currently registered.
* The <code>File</code> is wrapped in an
* <code>ImageInputStream</code>. If no registered
* <code>ImageReader</code> claims to be able to read the
* resulting stream, <code>null</code> is returned.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching in the
* <code>ImageInputStream</code> that is created.
*
* <p> Note that there is no <code>read</code> method that takes a
* filename as a <code>String</code>; use this method instead after
* creating a <code>File</code> from the filename.
*
* <p> This method does not attempt to locate
* <code>ImageReader</code>s that can read directly from a
* <code>File</code>; that may be accomplished using
* <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
*
* @param input a <code>File</code> to read from.
*
* @return a <code>BufferedImage</code> containing the decoded
* contents of the input, or <code>null</code>.
*
* @exception IllegalArgumentException if <code>input</code> is
* <code>null</code>.
* @exception IOException if an error occurs during reading.
*/
public static
BufferedImage read(
File input) throws
IOException {
if (
input == null) {
throw new
IllegalArgumentException("input == null!");
}
if (!
input.
canRead()) {
throw new
IIOException("Can't read input file!");
}
ImageInputStream stream =
createImageInputStream(
input);
if (
stream == null) {
throw new
IIOException("Can't create an ImageInputStream!");
}
BufferedImage bi =
read(
stream);
if (
bi == null) {
stream.
close();
}
return
bi;
}
/**
* Returns a <code>BufferedImage</code> as the result of decoding
* a supplied <code>InputStream</code> with an <code>ImageReader</code>
* chosen automatically from among those currently registered.
* The <code>InputStream</code> is wrapped in an
* <code>ImageInputStream</code>. If no registered
* <code>ImageReader</code> claims to be able to read the
* resulting stream, <code>null</code> is returned.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching in the
* <code>ImageInputStream</code> that is created.
*
* <p> This method does not attempt to locate
* <code>ImageReader</code>s that can read directly from an
* <code>InputStream</code>; that may be accomplished using
* <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
*
* <p> This method <em>does not</em> close the provided
* <code>InputStream</code> after the read operation has completed;
* it is the responsibility of the caller to close the stream, if desired.
*
* @param input an <code>InputStream</code> to read from.
*
* @return a <code>BufferedImage</code> containing the decoded
* contents of the input, or <code>null</code>.
*
* @exception IllegalArgumentException if <code>input</code> is
* <code>null</code>.
* @exception IOException if an error occurs during reading.
*/
public static
BufferedImage read(
InputStream input) throws
IOException {
if (
input == null) {
throw new
IllegalArgumentException("input == null!");
}
ImageInputStream stream =
createImageInputStream(
input);
BufferedImage bi =
read(
stream);
if (
bi == null) {
stream.
close();
}
return
bi;
}
/**
* Returns a <code>BufferedImage</code> as the result of decoding
* a supplied <code>URL</code> with an <code>ImageReader</code>
* chosen automatically from among those currently registered. An
* <code>InputStream</code> is obtained from the <code>URL</code>,
* which is wrapped in an <code>ImageInputStream</code>. If no
* registered <code>ImageReader</code> claims to be able to read
* the resulting stream, <code>null</code> is returned.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching in the
* <code>ImageInputStream</code> that is created.
*
* <p> This method does not attempt to locate
* <code>ImageReader</code>s that can read directly from a
* <code>URL</code>; that may be accomplished using
* <code>IIORegistry</code> and <code>ImageReaderSpi</code>.
*
* @param input a <code>URL</code> to read from.
*
* @return a <code>BufferedImage</code> containing the decoded
* contents of the input, or <code>null</code>.
*
* @exception IllegalArgumentException if <code>input</code> is
* <code>null</code>.
* @exception IOException if an error occurs during reading.
*/
public static
BufferedImage read(
URL input) throws
IOException {
if (
input == null) {
throw new
IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
istream =
input.
openStream();
} catch (
IOException e) {
throw new
IIOException("Can't get input stream from URL!",
e);
}
ImageInputStream stream =
createImageInputStream(
istream);
BufferedImage bi;
try {
bi =
read(
stream);
if (
bi == null) {
stream.
close();
}
} finally {
istream.
close();
}
return
bi;
}
/**
* Returns a <code>BufferedImage</code> as the result of decoding
* a supplied <code>ImageInputStream</code> with an
* <code>ImageReader</code> chosen automatically from among those
* currently registered. If no registered
* <code>ImageReader</code> claims to be able to read the stream,
* <code>null</code> is returned.
*
* <p> Unlike most other methods in this class, this method <em>does</em>
* close the provided <code>ImageInputStream</code> after the read
* operation has completed, unless <code>null</code> is returned,
* in which case this method <em>does not</em> close the stream.
*
* @param stream an <code>ImageInputStream</code> to read from.
*
* @return a <code>BufferedImage</code> containing the decoded
* contents of the input, or <code>null</code>.
*
* @exception IllegalArgumentException if <code>stream</code> is
* <code>null</code>.
* @exception IOException if an error occurs during reading.
*/
public static
BufferedImage read(
ImageInputStream stream)
throws
IOException {
if (
stream == null) {
throw new
IllegalArgumentException("stream == null!");
}
Iterator iter =
getImageReaders(
stream);
if (!
iter.
hasNext()) {
return null;
}
ImageReader reader = (
ImageReader)
iter.
next();
ImageReadParam param =
reader.
getDefaultReadParam();
reader.
setInput(
stream, true, true);
BufferedImage bi;
try {
bi =
reader.
read(0,
param);
} finally {
reader.
dispose();
stream.
close();
}
return
bi;
}
/**
* Writes an image using the an arbitrary <code>ImageWriter</code>
* that supports the given format to an
* <code>ImageOutputStream</code>. The image is written to the
* <code>ImageOutputStream</code> starting at the current stream
* pointer, overwriting existing stream data from that point
* forward, if present.
*
* <p> This method <em>does not</em> close the provided
* <code>ImageOutputStream</code> after the write operation has completed;
* it is the responsibility of the caller to close the stream, if desired.
*
* @param im a <code>RenderedImage</code> to be written.
* @param formatName a <code>String</code> containing the informal
* name of the format.
* @param output an <code>ImageOutputStream</code> to be written to.
*
* @return <code>false</code> if no appropriate writer is found.
*
* @exception IllegalArgumentException if any parameter is
* <code>null</code>.
* @exception IOException if an error occurs during writing.
*/
public static boolean
write(
RenderedImage im,
String formatName,
ImageOutputStream output) throws
IOException {
if (
im == null) {
throw new
IllegalArgumentException("im == null!");
}
if (
formatName == null) {
throw new
IllegalArgumentException("formatName == null!");
}
if (
output == null) {
throw new
IllegalArgumentException("output == null!");
}
return
doWrite(
im,
getWriter(
im,
formatName),
output);
}
/**
* Writes an image using an arbitrary <code>ImageWriter</code>
* that supports the given format to a <code>File</code>. If
* there is already a <code>File</code> present, its contents are
* discarded.
*
* @param im a <code>RenderedImage</code> to be written.
* @param formatName a <code>String</code> containing the informal
* name of the format.
* @param output a <code>File</code> to be written to.
*
* @return <code>false</code> if no appropriate writer is found.
*
* @exception IllegalArgumentException if any parameter is
* <code>null</code>.
* @exception IOException if an error occurs during writing.
*/
public static boolean
write(
RenderedImage im,
String formatName,
File output) throws
IOException {
if (
output == null) {
throw new
IllegalArgumentException("output == null!");
}
ImageOutputStream stream = null;
ImageWriter writer =
getWriter(
im,
formatName);
if (
writer == null) {
/* Do not make changes in the file system if we have
* no appropriate writer.
*/
return false;
}
try {
output.
delete();
stream =
createImageOutputStream(
output);
} catch (
IOException e) {
throw new
IIOException("Can't create output stream!",
e);
}
try {
return
doWrite(
im,
writer,
stream);
} finally {
stream.
close();
}
}
/**
* Writes an image using an arbitrary <code>ImageWriter</code>
* that supports the given format to an <code>OutputStream</code>.
*
* <p> This method <em>does not</em> close the provided
* <code>OutputStream</code> after the write operation has completed;
* it is the responsibility of the caller to close the stream, if desired.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching.
*
* @param im a <code>RenderedImage</code> to be written.
* @param formatName a <code>String</code> containing the informal
* name of the format.
* @param output an <code>OutputStream</code> to be written to.
*
* @return <code>false</code> if no appropriate writer is found.
*
* @exception IllegalArgumentException if any parameter is
* <code>null</code>.
* @exception IOException if an error occurs during writing.
*/
public static boolean
write(
RenderedImage im,
String formatName,
OutputStream output) throws
IOException {
if (
output == null) {
throw new
IllegalArgumentException("output == null!");
}
ImageOutputStream stream = null;
try {
stream =
createImageOutputStream(
output);
} catch (
IOException e) {
throw new
IIOException("Can't create output stream!",
e);
}
try {
return
doWrite(
im,
getWriter(
im,
formatName),
stream);
} finally {
stream.
close();
}
}
/**
* Returns <code>ImageWriter</code> instance according to given
* rendered image and image format or <code>null</code> if there
* is no appropriate writer.
*/
private static
ImageWriter getWriter(
RenderedImage im,
String formatName) {
ImageTypeSpecifier type =
ImageTypeSpecifier.
createFromRenderedImage(
im);
Iterator<
ImageWriter>
iter =
getImageWriters(
type,
formatName);
if (
iter.
hasNext()) {
return
iter.
next();
} else {
return null;
}
}
/**
* Writes image to output stream using given image writer.
*/
private static boolean
doWrite(
RenderedImage im,
ImageWriter writer,
ImageOutputStream output) throws
IOException {
if (
writer == null) {
return false;
}
writer.
setOutput(
output);
try {
writer.
write(
im);
} finally {
writer.
dispose();
output.
flush();
}
return true;
}
}