How TwelveMonkeys plugins are discovered
masterImageIO.read() and ImageIO.write() to utilize the extended format support.repository·master·Indexed 24 days ago
https://github.com/haraldk/twelvemonkeysA collection of plugins for the Java ImageIO API that extends support for image file formats not natively covered by the JDK, including SVG, PSD, TIFF, and WebP. It provides extended support for BMP and JPEG, as well as utilities for high-quality image resampling via ResampleOp, Floyd-Steinberg dithering with DiffusionDither, and Adobe Clipping Path support.
ImageIO.read() and ImageIO.write() to utilize the extended format support.TwelveMonkeys plugins rely on the Java Service Provider Interface (SPI) mechanism. Each JAR contains files in META-INF/services/ (e.g., javax.imageio.spi.ImageReaderSpi).
If you are creating a 'fat' JAR by unpacking and repacking, you must merge these SPI files rather than overwriting them. If you simply overwrite them, only one plugin will be installed.
If using the Maven Shade Plugin, use the ServicesResourceTransformer to properly merge these files, and consider ManifestResourceTransformer to preserve vendor and version information.
TwelveMonkeys utilizes the standard Java ImageIO service provider mechanism. When the TwelveMonkeys JARs are present on the classpath, ImageIO discovers them at runtime.
To ensure better compatibility and features, the TwelveMonkeys service providers for JPEG, BMP, and TIFF override the onRegistration method. They use the pairwise partial ordering mechanism of the IIOServiceRegistry to ensure they are installed before the default implementations provided by:
JPEGImageReader, BMPImageReader, TIFFImageReader)TIFFImageReader)This prioritization ensures that in most cases, your application will use the TwelveMonkeys implementation instead of the default JDK implementation without removing any existing functionality.
TwelveMonkeys is designed to work as a drop-in replacement for standard Java ImageIO. You do not need to change your existing code to benefit from the improved format support.
The recommended way to use TwelveMonkeys is to add the specific plugin dependencies to your project using a dependency management tool like Maven or Gradle.
If you are not using a build tool, ensure that all necessary TwelveMonkeys ImageIO JAR files are included in your application's classpath. ImageIO uses a service lookup mechanism to discover these plugins at runtime.
For basic usage, you can continue using standard javax.imageio APIs. The plugins are automatically discovered and used by methods such as:
ImageIO.read(...)ImageIO.getImageReaders(...)While most functionality is accessed through standard APIs, some formats offer advanced features that may require using specific APIs. Examples include:
For fine-grained control over reading parameters (like sub-sampling, source regions, or destination buffers), use the ImageReader API instead of the high-level ImageIO.read() method.
Key steps:
ImageInputStream.ImageReader via ImageIO.getImageReaders(input).ImageReadParam to configure settings like setSourceSubsampling, setSourceRegion, or setDestination.reader.read(index, param) to perform the read.reader.dispose() in a finally block to prevent memory leaks.You can also query dimensions using reader.getWidth(n) and reader.getHeight(n) without loading the full image into memory, or loop through multiple images in a single file using reader.getNumImages().
// Create input stream (in try-with-resource block to avoid leaks)
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
// Get the reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) {
throw new IllegalArgumentException("No reader for: " + file);
}
ImageReader reader = readers.next();
try {
reader.setInput(input);
// Optionally, listen for read warnings, progress, etc.
reader.addIIOReadWarningListener(...);
reader.addIIOReadProgressListener(...);
ImageReadParam param = reader.getDefaultReadParam();
// Optionally, control read settings like sub sampling, source region or destination etc.
param.setSourceSubsampling(...);
param.setSourceRegion(...);
param.setDestination(...);
// ...
// Finally read the image, using settings from param
BufferedImage image = reader.read(0, param);
// Optionally, read thumbnails, meta data, etc...
int numThumbs = reader.getNumThumbnails(0);
// ...
}
finally {
// Dispose reader in finally block to avoid memory leaks
reader.dispose();
}
}To build the project locally, follow these steps:
git clone git@github.com:haraldk/TwelveMonkeys.gitcd TwelveMonkeysmvn packageNotes:
MAVEN_OPTS environment variable, for example: export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m".mvn install$ git clone git@github.com:haraldk/TwelveMonkeys.git
$ cd TwelveMonkeys
$ mvn packageTo use the plugins without a build tool like Maven, manually add the following JARs to your application's classpath. Note that specific plugins (like JPEG or TIFF) require the common and core libraries to function.
twelvemonkeys-common-lang-3.13.1.jar
twelvemonkeys-common-io-3.13.1.jar
twelvemonkeys-common-image-3.13.1.jar
twelvemonkeys-imageio-core-3.13.1.jar
twelvemonkeys-imageio-metadata-3.13.1.jar
twelvemonkeys-imageio-jpeg-3.13.1.jar
twelvemonkeys-imageio-tiff-3.13.1.jarTwelveMonkeys plugins are discovered automatically at runtime via the standard javax.imageio service provider mechanism. To use them, simply include the relevant plugin JARs in your project classpath.
To load the first image of a file entirely into memory:
BufferedImage image = ImageIO.read(file);To write an entire image into a single file using the default settings for the specified format:
if (!ImageIO.write(image, format, file)) {
// Handle image not written case
}Because the ImageIO registry (IIORegistry) is VM-global, loading plugins from WEB-INF/lib or classes in a servlet context can lead to discovery issues or memory leaks during application restarts.
It is strongly recommended to use the IIOProviderContextListener to handle dynamic loading and unloading of plugins. This ensures plugins are correctly discovered within the servlet context and prevents resource leaks.
Alternatively, you can place the JAR files in the application server's shared or common lib folder.
<web-app ...>
<listener>
<display-name>ImageIO service provider loader/unloader</display-name>
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
</listener>
</web-app>Standard ImageIO.read(file) calls return null and throw an IOException if an image is damaged. To attempt to recover usable data from a damaged file, you must manually manage the ImageReader and provide a destination buffer via ImageReadParam.
reader.getWidth(0) and reader.getRawImageType(0).BufferedImage using the ImageTypeSpecifier.ImageReadParam.setDestination(image).reader.read(0, param) inside a try-catch block.Note: Results are plugin-specific; you may still receive a blank or empty image.
int width = reader.getWidth(0);
int height = reader.getHeight(0);
ImageTypeSpecifier imageType = reader.getRawImageType(0);
BufferedImage image = imageType.createBufferedImage(width, height);
ImageReadParam param = reader.getDefaultReadParam();
param.setDestination(image);
try {
reader.read(0, param);
}
catch (IOException e) {
// Handle, log a warning/error etc
}To control write parameters and the writing process, use the ImageWriter API.
Key steps:
ImageWriter via ImageIO.getImageWritersByFormatName(format).ImageOutputStream.ImageWriteParam to configure format-specific settings (often requiring casting) or generic settings like sub-sampling and source regions.writer.write(...) providing an IIOImage (which contains metadata, the image, and other data) and the ImageWriteParam.writer.dispose() in a finally block to prevent memory leaks.// Get the writer
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
if (!writers.hasNext()) {
throw new IllegalArgumentException("No writer for: " + format);
}
ImageWriter writer = writers.next();
try {
// Create output stream (in try-with-resource block to avoid leaks)
try (ImageOutputStream output = ImageIO.createImageOutputStream(file)) {
writer.setOutput(output);
// Optionally, listen to progress, warnings, etc.
ImageWriteParam param = writer.getDefaultWriteParam();
// Optionally, control format specific settings of param (requires casting), or
// control generic write settings like sub sampling, source region, output type etc.
// Optionally, provide thumbnails and image/stream metadata
writer.write(..., new IIOImage(..., image, ...), param);
}
}
finally {
// Dispose writer in finally block to avoid memory leaks
writer.dispose();
}To use TwelveMonkeys plugins in a Maven project, add the specific plugin dependencies to your pom.xml. For example, to include JPEG and TIFF support, add imageio-jpeg and imageio-tiff from the com.twelvemonkeys.imageio group.
If you are deploying as part of a web application, you should also include the com.twelvemonkeys.servlet:servlet dependency. For applications using Jakarta EE (Servlet API 5.0+), use the version with the jakarta classifier.
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.13.1</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.13.1</version>
</dependency>
<!-- Optional: For web apps (javax.servlet) -->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.13.1</version>
</dependency>
<!-- Optional: For web apps (jakarta.servlet) -->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.13.1</version>
<classifier>jakarta</classifier>
</dependency>
</dependencies>