TwelveMonkeys ImageIO

repository·master·Indexed 24 days ago

https://github.com/haraldk/twelvemonkeys

A 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.

Tokens
6.9K
Snippets
14
Records
30
Agent score
79%

What's inside TwelveMonkeys ImageIO

  1. How TwelveMonkeys plugins are discovered

    master
    The plugins are discovered automatically at runtime using the standard Java ImageIO service provider mechanism. You do not need to manually register them; simply adding the plugin dependencies to your classpath allows ImageIO.read() and ImageIO.write() to utilize the extended format support.
  2. Create a 'fat' JAR with TwelveMonkeys plugins

    master

    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.

  3. How TwelveMonkeys plugins are discovered and prioritized

    master

    TwelveMonkeys utilizes the standard Java ImageIO service provider mechanism. When the TwelveMonkeys JARs are present on the classpath, ImageIO discovers them at runtime.

    Plugin Prioritization

    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:

    • Sun/Oracle (JPEGImageReader, BMPImageReader, TIFFImageReader)
    • Apple (on OS X, 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.

  4. How to use TwelveMonkeys ImageIO

    master

    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.

    Integration via Build Tools

    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.

    Integration via Classpath

    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.

    Usage with Standard APIs

    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(...)

    Advanced Usage

    While most functionality is accessed through standard APIs, some formats offer advanced features that may require using specific APIs. Examples include:

    • Setting a base URL for an SVG image consisting of multiple files.
    • Controlling the output compression of a TIFF file.
  5. Advanced image reading with ImageReader

    master

    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:

    1. Create an ImageInputStream.
    2. Obtain an ImageReader via ImageIO.getImageReaders(input).
    3. Set the input on the reader.
    4. Use ImageReadParam to configure settings like setSourceSubsampling, setSourceRegion, or setDestination.
    5. Call reader.read(index, param) to perform the read.
    6. Crucial: Always call 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();
        }
    }
  6. Build TwelveMonkeys from source

    master

    To build the project locally, follow these steps:

    1. Clone the repository:
      git clone git@github.com:haraldk/TwelveMonkeys.git
    2. Navigate to the directory:
      cd TwelveMonkeys
    3. Build using Maven:
      mvn package

    Notes:

    • JDK: Oracle JDK 8.x is recommended. Using OpenJDK may cause some tests to fail due to color management differences.
    • Memory: Unit tests require significant memory. You may need to set the MAVEN_OPTS environment variable, for example: export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m".
    • Local Install: To install the project to your local Maven repository, run:
      mvn install
    $ git clone git@github.com:haraldk/TwelveMonkeys.git
    $ cd TwelveMonkeys
    $ mvn package
  7. Install TwelveMonkeys plugins manually

    master

    To 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.jar
  8. Basic usage of TwelveMonkeys ImageIO

    master

    TwelveMonkeys 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.

    Reading an image

    To load the first image of a file entirely into memory:

    BufferedImage image = ImageIO.read(file);

    Writing an image

    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
    }
  9. Deploy TwelveMonkeys in a Web Application

    master

    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>
  10. Recover data from damaged images

    master

    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.

    1. Get the image dimensions and raw image type using reader.getWidth(0) and reader.getRawImageType(0).
    2. Create a BufferedImage using the ImageTypeSpecifier.
    3. Set this buffer as the destination in ImageReadParam.setDestination(image).
    4. Call 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
    }
  11. Advanced image writing with ImageWriter

    master

    To control write parameters and the writing process, use the ImageWriter API.

    Key steps:

    1. Obtain an ImageWriter via ImageIO.getImageWritersByFormatName(format).
    2. Create an ImageOutputStream.
    3. Set the output on the writer.
    4. Use ImageWriteParam to configure format-specific settings (often requiring casting) or generic settings like sub-sampling and source regions.
    5. Call writer.write(...) providing an IIOImage (which contains metadata, the image, and other data) and the ImageWriteParam.
    6. Crucial: Always call 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();
    }
  12. Install TwelveMonkeys plugins via Maven

    master

    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>