Most Java developers find TwelveMonkeys the same way: ImageIO won’t read some format, a quick search turns it up, a few JARs go on the classpath, and the problem disappears. It’s BSD licensed, still actively maintained by Harald Kuhr. We are well-known to each other and have a long-term ‘friendly’ rivalry as you will see from our blog post comments and his forums.
The wall you hit is writing modern formats, TwelveMonkeys can’t write HEIC, AVIF, JPEG 2000, or WebP.
This post compares what each library reads and writes, how the APIs feel, and which one fits which job. JDeli and TwelveMonkeys are often compared (and we have noticed we get mentioned on their support forums)
What Is TwelveMonkeys?
TwelveMonkeys is a set of plugins that bolt extra formats onto Java’s javax.imageio package. Drop the JARs on the classpath and the ImageIO registry picks them up at runtime. Open source, BSD licensed, Java 7+, with releases continuing into 2025 including fixes for recent JDKs.
It’s open source under the BSD license, runs on Java 7 and up, and is under active development, with regular releases through 2025 and fixes for recent JDK versions.
The BMP, JPEG, and TIFF plugins replace the JDK’s own readers with stronger ones. GIF and PNG are intentionally left alone because the built-in plugins are already adequate.
What Is JDeli?
JDeli is a commercial pure-Java image library from IDRsolutions. It’s a standalone API rather than an ImageIO plugin layer, although it does ship an ImageIO plugin if you want the drop-in route.
It supports 16+ formats, including AVIF, BMP, DICOM, EMF, GIF, HEIC, JPEG, JPEG 2000, JPEG XL, PNG, PSD, SGI, TIFF, WebP, and WMF, with no native code and no third-party dependencies. It targets Java 17 and above, and bundles an image processing API for the usual operations: crop, resize, rotate, watermark, and so on.
Format Coverage
The format tables are where the two libraries pull apart, where TwelveMonkeys leans read-heavy. It adds decoders for a long list of formats but encoders for only a handful, and the gap shows up exactly where you’d least want it. The WebP plugin is the clearest example: it reads WebP fine and can’t write a byte of it. JDeli reads and writes the modern formats outright.
Here are the formats most likely to settle a library choice. “Read” means there’s a decoder; “Write” means there’s an encoder.
| Format | TwelveMonkeys | JDeli |
|---|---|---|
| AVIF | No | Read / Write |
| BMP | Read / Write | Read / Write |
| DICOM | No | Read |
| GIF | No (uses JDK built-in) | Read / Write |
| HEIC | No | Read / Write |
| JPEG | Read / Write | Read / Write |
| JPEG 2000 | No | Read / Write |
| JPEG XL | No | Read |
| PNG | No (uses JDK built-in) | Read / Write |
| PSD | Read | Read |
| SGI | Read | Read |
| TIFF | Read / Write | Read / Write |
| WebP | Read | Read / Write |
TwelveMonkeys also reads DDS, HDR, ICNS, ICO, PCX, PICT, TGA, and SVG via a Batik wrapper. For old or unusual raster formats, its long-tail read coverage is wider than JDeli’s. On the write side, it’s stuck around 2010, anything newer isn’t there.
TwelveMonkeys will read almost anything you throw at it. The moment you need to produce AVIF, HEIC, JPEG 2000, WebP output, it can’t, and JDeli can. That single fact decides a lot of projects on its own.
Benchmarks
These numbers come from JMH, so the scores are operations per second and higher is better. The tests run over a shared corpus of real-world files, which is why the write tables also report failed files, average output size, and SSIM (structural similarity to the original, where 1.000000 is a perfect match). Where the two libraries land close, treat it as a tie; the interesting rows are the ones where they don’t.
TwelveMonkeys is faster at reading BMP and TIFF, and its BMP write throughput is competitive. JDeli’s edge is on JPEG throughput, on WebP, and on write quality, particularly the TIFF cases where TwelveMonkeys trades similarity for speed.
BMP
Reading:
| Benchmark | Score (ops/s) | Error |
|---|---|---|
| TwelveMonkeys | 146.289 | ± 3.832 |
| JDeli | 138.964 | ± 1.764 |
Writing:
| Benchmark | Score (ops/s) | Error | Avg file size | Failed files | SSIM |
|---|---|---|---|---|---|
| TwelveMonkeys | 13.679 | ± 1.460 | 1.09 MB | 3 | 1.000000 |
| JDeli | 16.880 | ± 0.815 | 0.625 MB | 0 | 1.000000 |
JPEG
Reading:
| Benchmark | Score (ops/s) | Error |
|---|---|---|
| TwelveMonkeys | 31.903 | ± 0.896 |
| JDeli | 55.762 | ± 9.188 |
Writing:
| Benchmark | Score (ops/s) | Error | Avg file size | Failed files | SSIM |
|---|---|---|---|---|---|
| TwelveMonkeys | 21.606 | ± 3.217 | 17.39 KB | 2 | 0.991111 |
| JDeli | 58.474 | ± 5.696 | 12.69 KB | 0 | 0.991671 |
TIFF
Reading:
| Benchmark | Score (ops/s) | Error |
|---|---|---|
| TwelveMonkeys | 153.075 | ± 18.910 |
| JDeli | 109.574 | ± 4.372 |
Writing:
| Benchmark | Score (ops/s) | Error | Avg file size | Failed files | SSIM |
|---|---|---|---|---|---|
| JDeli (Deflate) | 6.343 | ± 0.262 | 366.17 KB | 0 | 1.000000 |
| JDeli (JPEG) | 62.020 | ± 1.121 | 12.87 KB | 0 | 0.991671 |
| JDeli (LZW) | 10.482 | ± 0.327 | 532.83 KB | 0 | 1.000000 |
| TwelveMonkeys (Deflate) | 11.525 | ± 0.817 | 265.25 KB | 0 | 0.974967 |
| TwelveMonkeys (JPEG) | 15.360 | ± 1.053 | 103.40 KB | 3 | 0.141341 |
| TwelveMonkeys (LZW) | 9.703 | ± 0.366 | 235.56 KB | 0 | 0.974967 |
TwelveMonkeys posts higher throughput on Deflate and LZW, but at SSIM 0.974967 against JDeli’s lossless 1.000000. Its TIFF-JPEG mode is the outlier: 3 files failed to write and the ones that did came back at SSIM 0.141341, effectively unusable. JDeli’s TIFF-JPEG mode is faster (62.020) and holds quality at 0.991671.
WebP
Reading (TwelveMonkeys has no WebP writer, so only read is measured):
| Benchmark | Score (ops/s) | Error |
|---|---|---|
| TwelveMonkeys | 0.877 | ± 0.107 |
| JDeli | 0.950 | ± 0.073 |
Where TwelveMonkeys Is Strong
The JPEG and TIFF readers are excellent and worth pulling in on their own merits. The JPEG plugin handles CMYK JPEGs, files with broken or mismatched ICC profiles, and JFIF/Exif/Adobe marker combinations the JDK reader rejects outright. The TIFF plugin reads CCITT T.4/T.6, LZW, PackBits, and many other compression schemes.
There’s also Adobe Clipping Path support for JPEG, PSD, and TIFF, plus a Lanczos/Mitchell ResampleOp that produces better results than rolling your own with Graphics2D.
If your problem is making sense of messy JPEG and TIFF that arrives from who-knows-where, TwelveMonkeys is a genuinely good free answer, and worth reaching for before you pay for anything.
Architecture: Plugin vs Standalone Library
The bigger difference between the two is how you actually write code against them.
TwelveMonkeys keeps you inside ImageIO. You call ImageIO.read() and ImageIO.write(), you set encoding options through ImageWriteParam, and anything beyond read/write, resizing, cropping, rotating, you build yourself with Graphics2D and AffineTransform, exactly as you would with plain ImageIO.
You also inherit ImageIO’s quirks. ImageIO.read() returns null when no reader matches instead of throwing, and that null surfaces later as an NPE somewhere unrelated. It’s an ImageIO API decision rather than a TwelveMonkeys one, but the result is the same.
JDeli is its own API. Reads and writes go through JDeli.read() and JDeli.write(), encoder options are type-safe objects instead of the ImageWriteParam casting dance, and processing is a chain you build rather than Graphics2D you hand-write:
ImageProcessingOperations operations = new ImageProcessingOperations()
.scale(0.5f)
.rotate(90)
.blur();
BufferedImage result = operations.apply(image);
Read, process, and write, conversion included, collapses into a single call:
ImageProcessingOperations operations = new ImageProcessingOperations()
.scale(0.75f);
JDeli.convert(new File("input.tiff"), new File("output.webp"), operations);
If you’d rather not touch existing ImageIO calls at all, JDeli’s ImageIO plugin routes ImageIO.read() and ImageIO.write() through its own decoders, the same trick TwelveMonkeys plays.
Production Considerations
Both libraries are pure Java, so the native memory and stability issues associated with ImageIO’s native JPEG/TIFF code, or JAI, don’t apply to either. Heap, GC, normal JVM behavior. Library choice won’t save you from JVM crashes you were already going to have.
Support is also something to consider. JDeli is commercial, with an active development team behind it. TwelveMonkeys is a well-run open source project that is, largely one maintainer’s work. Whether that matters depends on whether your team needs a defined response path or is fine filing GitHub issues.
When to Use Which
TwelveMonkeys fits when the budget is zero, the workload is mostly reading, and the formats you need are in its supported set. Its CMYK/ICC JPEG handling and TIFF read coverage are reason enough on their own.
JDeli fits when you need to write AVIF, HEIC, JPEG 2000, or WebP, when DICOM or JPEG XL is in scope or when commercial support is a procurement requirement. Disclosure: JDeli is our product, we’d be happy for you to test against TwelveMonkey or other alternatives.
Worth noting that nothing stops you using both. TwelveMonkeys for the reading legacy formats, JDeli for the modern formats it can’t write. They register through the same ImageIO mechanism and coexist happily enough.
Getting Started
TwelveMonkeys is packaged per format. Here are the JPEG and WebP readers:
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>3.12.0</version>
</dependency>
On the classpath, that’s all it takes. No code change:
BufferedImage image = ImageIO.read(new File("photo.webp"));
JDeli is a single dependency:
<dependency>
<groupId>com.idrsolutions</groupId>
<artifactId>jdeli</artifactId>
<version>1.0</version>
</dependency>
BufferedImage image = JDeli.read(new File("photo.heic"));
JDeli.write(image, OutputFormat.WEBP, new File("output.webp"));
Frequently Asked Questions
Is there any pure-Java way to read HEIC or AVIF for free?
Not really. TwelveMonkeys has no plugin for either, and the free ecosystem doesn’t have a maintained pure-Java decoder for them. People usually end up shelling out to ImageMagick or libheif, or paying for a library. JDeli decodes both in pure Java, which is the part that matters once you’re in a container or an air-gapped box where installing native libraries is its own headache.
Can I move from TwelveMonkeys to JDeli without rewriting my image code?
Mostly, as long as your code goes through ImageIO.read() and ImageIO.write(). JDeli’s ImageIO plugin registers the same way, so it slots in at the classpath level. The exception is anything written against TwelveMonkeys-specific classes, like ResampleOp or its clipping-path support, which has no ImageIO-standard equivalent and would need rewriting.
Does TwelveMonkeys need any native libraries installed?
No. TwelveMonkeys is pure Java, JARs on the classpath and nothing else, no system packages, no JNI, no platform-specific binaries which is one reason its WebP and JPEG reading is easy to deploy. The catch isn’t native dependencies, it’s format coverage: pure Java is why it’s straightforward to ship, but it still doesn’t write the modern formats. JDeli is also pure Java, so on the deployment question specifically the two are even.
Are you a Java Developer working with Image files?
// Read an image
BufferedImage bufferedImage = JDeli.read(avifImageFile);
// Write an image
JDeli.write(bufferedImage, "avif", outputStreamOrFile);// Read an image
BufferedImage bufferedImage = JDeli.read(dicomImageFile);// Read an image
BufferedImage bufferedImage = JDeli.read(heicImageFile);
// Write an image
JDeli.write(bufferedImage, "heic", outputStreamOrFile);// Read an image
BufferedImage bufferedImage = JDeli.read(jpegImageFile);
// Write an image
JDeli.write(bufferedImage, "jpeg", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(jpeg2000ImageFile);
// Write an image
JDeli.write(bufferedImage, "jpx", outputStreamOrFile);
// Write an image
JDeli.write(bufferedImage, "pdf", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(pngImageFile);
// Write an image
JDeli.write(bufferedImage, "png", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(tiffImageFile);
// Write an image
JDeli.write(bufferedImage, "tiff", outputStreamOrFile);
// Read an image
BufferedImage bufferedImage = JDeli.read(webpImageFile);
// Write an image
JDeli.write(bufferedImage, "webp", outputStreamOrFile);
What is JDeli?
JDeli is a commercial Java Image library that is used to read, write, convert, manipulate and process many different image formats.
Why use JDeli?
To handle many well known formats such as JPEG, PNG, TIFF as well as newer formats like AVIF, HEIC and JPEG XL in java with no calls to any external system or third party library.
What licenses are available?
We have 3 licenses available:
Server for on premises and cloud servers, Distribution for use in a named end user applications, and Custom for more demanding requirements.
How does JDeli compare?
We work hard to make sure JDeli performance is better than or similar to other java image libraries. Check out our benchmarks to see just how well JDeli performs.