Amy Pearson Amy is the product lead for JDeli with expertise in image code, Java, web development, and cloud computing. She focuses on JDeli and has also contributed to JPedal, cloud services, and support. Outside work, she enjoys gaming, F1, and music.

JDeli vs Java ImageIO: Benchmarks and Migration

7 min read

Java’s built-in javax.imageio.ImageIO has been the default image library since J2SE 1.4. It reads and writes JPEG, PNG, BMP, GIF, and TIFF (from JDK 9 onward). For prototypes and internal tools, that coverage works. But production systems that process user-uploaded images, medical imaging data, or Apple device photos will eventually need AVIF, HEIC, JPEG 2000, WebP support, and that’s where ImageIO stops.

This post compares JDeli and Java ImageIO across speed, output quality, memory behaviour, format coverage, stability, and API design. The benchmark data comes from JMH (Java Microbenchmark Harness) tests published on the JDeli performance comparison docs (last updated May 2026).

The test code is on GitHub, runs with the trial jar, and you can reproduce every number on your own hardware.

We built JDeli because ImageIO and JAI were breaking our PDF rendering pipeline in ways we couldn’t patch from the outside. This is a review of where each library wins and where it falls short.

What Is Java ImageIO?

javax.imageio.ImageIO is the standard Java API for reading and writing raster images. It ships with every JDK, so there’s nothing to install. You call ImageIO.read(file) to decode an image into a BufferedImage, and ImageIO.write(image, format, file) to encode it.

ImageIO uses a plugin-based service provider architecture. Third-party plugins can register additional format readers and writers. The most common plugin historically was JAI (Java Advanced Imaging), which added JPEG 2000 and TIFF support. It ships with known bugs, runs slowly, and consumes excessive memory on large files.

What Is JDeli?

JDeli is a commercial pure-Java image library built by IDRsolutions. It writes formats including AVIF, BMP, GIF, HEIC, JPEG, JPEG 2000, PNG, TIFF, WebP and can read DICOM, EMF, JPEG XL, PSD, SGI, WMF. It has no native dependencies, no third-party dependencies, and makes no network calls during operation.

JDeli’s API mirrors ImageIO’s simplicity (JDeli.read(), JDeli.write()) but adds type-safe format enums, custom encoder options, chained image processing, and one-step format conversion.

It also includes an ImageIO plugin that routes existing ImageIO.read() and ImageIO.write() calls through JDeli’s decoders, which means you can add JDeli to a legacy codebase without changing a single line of application code.

JMH Speed Benchmarks

All figures below use JMH in throughput mode (operations per second), where the higher the result, the better it performs. A standard set of test images was used across all libraries, with 25 measurement iterations per benchmark. Apache Commons Imaging is included where it supports the format.

Reading (Decoding) Throughput

FormatImageIO (ops/s)ImageIO + JAI (ops/s)JDeli (ops/s)Fastest
BMP56.38171.24JDeli (3.0x)
GIF1.655.36JDeli (3.2x)
JPEG0.1430.162JDeli (1.1x)
JPEG 200048.97123.79JDeli (2.5x)
PNG296.922160.50JDeli (7.3x)
TIFF7.9811.30JDeli (1.4x)

JDeli wins across every read benchmark. The PNG read improvement is the most dramatic at 7.3x faster than ImageIO.

JPEG reads are close between ImageIO and JDeli (0.143 vs 0.162 ops/s). For JPEG-heavy workloads where read speed is the bottleneck, the difference is small enough that other factors (format support, stability, memory behaviour) should drive the decision.

Writing (Encoding) Throughput

Writing benchmarks are more nuanced because each library offers different compression modes. The table below compares equivalent default settings:

FormatImageIO (ops/s)ImageIO + JAI (ops/s)JDeli (ops/s)Fastest
BMP5.2917.22JDeli (3.3x)
GIF4.710.54ImageIO (8.7x)
JPEG13.3528.07JDeli (2.1x)
JPEG 20002.316.23JDeli (2.7x)
PNG4.825.38JDeli (1.1x)
TIFF (Deflate)4.536.36JDeli (1.4x)
TIFF (LZW)4.279.47JDeli (2.2x)

ImageIO wins GIF writes. JDeli wins everything else. The JPEG write performance gap is notable: JDeli encodes JPEG at 28.07 ops/s vs ImageIO’s 13.35 ops/s at default quality, a 2.1x improvement.

JDeli also offers specialized compression modes not available in ImageIO. For PNG, JDeli’s fast mode hits 13.06 ops/s (2.7x faster than its own default and nearly 3x faster than ImageIO). For TIFF, JDeli’s speed-optimized mode reaches 13.12 ops/s compared to ImageIO’s best of 9.21 ops/s (uncompressed).

Output Quality (SSIM)

Speed means nothing if the output looks wrong. The benchmark suite measures structural similarity (SSIM) between the original image and the encoded output. SSIM scores range from 0 (completely different) to 1.0 (identical).

FormatImageIO SSIMJDeli SSIMImageIO Unreadable FilesJDeli Unreadable Files
BMP0.4430.96830 / 1660 / 166
JPEG (default)0.9840.96436 / 1660 / 166
JPEG (high quality)0.9960.98236 / 1660 / 166
PNG1.0001.0000 / 1660 / 166
TIFF (Deflate)1.0001.0001 / 1661 / 166

The SSIM scores are comparable for lossless formats (PNG, TIFF deflate both produce identical output). For JPEG, ImageIO has a slight SSIM advantage at both default and high quality settings, but ImageIO also failed to produce readable output for 36 out of 166 test files.

JDeli produced readable output for all 166. A library that scores 0.984 on the files it can handle but fails on 22% of them is less useful than one that scores 0.964 on every file in the set.

The BMP result is particularly revealing: ImageIO’s BMP writer produced unreadable output for 30 out of 166 test files, and the files it did write had an average SSIM of just 0.443 (very different from the originals). JDeli wrote all 166 files with an average SSIM of 0.968.

Memory and JVM Stability

ImageIO’s JPEG and TIFF decoders rely on native (C/C++) code. That native code allocates memory outside the Java heap, invisible to the garbage collector. In practice this creates two failure modes:

The first is OutOfMemoryError exceptions that don’t respond to -Xmx tuning, because the memory pressure is on the native heap, not the Java heap. Increasing -Xmx won’t help if the native allocator is the one running out of space.

The second is full JVM crashes. When native code corrupts memory or exceeds its allocation, the process dies without a catchable exception. Your application just stops. In a containerized environment, that means the pod restarts and any in-flight requests are lost.

JDeli is 100% pure Java. Every byte of memory it uses lives on the Java heap where the GC can manage it and where standard JVM flags (-Xmx, -Xms, GC tuning) control its behaviour.

For server-side and cloud deployments, this difference matters more than any speed benchmark. A library that’s 20% slower but never crashes your JVM is a better production choice than one that’s faster but occasionally kills your container.

Format Support Comparison

AVIFNoNoRead / Write
BMPRead / WriteRead / WriteRead / Write
DICOMNoNoRead
EMF / WMFNoNoRead
GIFRead / WriteRead / WriteRead / Write
HEICNoNoRead / Write
JPEGRead / WriteRead / WriteRead / Write
JPEG 2000NoRead (buggy, slow)Read / Write
JPEG XLNoNoRead
PDFNoNoWrite
PNGRead / WriteRead / WriteRead / Write
PSDNoNoRead
SGINoNoRead
TIFFRead (JDK 9+)Read / WriteRead / Write
WebPNoNoRead / Write

ImageIO covers the classic web formats. If your project only processes JPEG, PNG, and GIF, it works. The gaps show up in specific scenarios: mobile applications (Apple devices shoot HEIC by default), next-gen web delivery (WebP and AVIF produce smaller files than JPEG at equivalent quality), medical imaging (DICOM), print and publishing workflows (JPEG 2000 with full write support), and vector graphics embedded in enterprise documents (EMF/WMF).

JAI partially fills the JPEG 2000 gap, but Oracle abandoned JAI years ago.

Image Processing

ImageIO is a read/write library. If you need to resize, rotate, crop, blur, or watermark an image, you’re on your own with Graphics2D and AffineTransform.

JDeli includes a built-in processing API. You define operations on an ImageProcessingOperations instance, chain them in any order, and apply them in a single pass. Operations execute in the order they’re added.

There are two ways to apply operations to a BufferedImage in memory. JDeli.process() keeps processing separate from file I/O:

You can also chain operations using the builder pattern and call apply() directly:

To apply processing during a file conversion (read, process, and write in one call), pass the operations into JDeli.convert(). This also handles format conversion at the same time:

JDeli.convert() also accepts InputStream/OutputStream and byte[], so the same pattern works in servlet pipelines or cloud functions where you don’t have file handles.

Available built-in operations include blur, brighten, crop, edge detection, emboss, gaussian blur, invert colors, mirror, resize (to fit, to height, to width), rotate, scale, sharpen, superscale2x, and watermark. You can also implement the ImageOperation interface to define your own:

This is relevant for batch pipelines where you’re converting uploaded images to web-friendly formats and generating thumbnails. With ImageIO, you’d read the file, manipulate the BufferedImage with separate Graphics2D calls, then write it out as a separate step. With JDeli, the read-process-write flow is a single JDeli.convert() call.

Security and Deployment

JDeli makes zero network calls. No telemetry, no license server phone-home, no cloud routing. Images are processed entirely in-process on whatever machine runs the JVM. The library ships as a single jar with no transitive dependencies.

This matters for three deployment scenarios:

  • Air-gapped environments (government, defense, financial services) need a definitive “no data leaves the environment” answer. JDeli provides that without caveats.
  • Container-based deployments benefit from having no native libraries to install or configure. JDeli runs in any JVM container image. No system library packages, no JNI configuration, no platform-specific binaries.
  • Security scans flag unmaintained dependencies. JAI and the older ImageIO native code modules trigger these warnings regularly. JDeli is actively maintained with regular stable releases, so it won’t show up as unsupported software in your vulnerability scanner.

How JDeli Improves on ImageIO’s API Behaviour

If you’re migrating code that was written around ImageIO’s quirks, you’ll notice a few places where JDeli does things differently, and in each case it’s a deliberate improvement:

Explicit Error Reporting (Instead of Silent Nulls)

ImageIO.read() returns null when it can’t find a reader for a format. This means a missing codec shows up as a NullPointerException somewhere downstream, far from the actual cause. JDeli throws an exception immediately with a message that tells you what went wrong.

If your ImageIO code checks for null returns, you can replace those null checks with try-catch blocks for cleaner control flow, or leave them as defensive checks alongside the exception handling.

Default Full Thread Safety

ImageIO’s native-backed readers have had historical thread-safety issues, particularly with JAI plugins. If you’re processing images in parallel and seeing intermittent, hard-to-reproduce failures with ImageIO, switching to JDeli may eliminate them without any changes to your threading logic.

Higher File Compatibility

During benchmark testing, ImageIO’s readers failed to produce readable output for 22-69% of test files in certain formats (36/166 JPEGs, 30/166 BMPs, 114/166 JPEG-compressed TIFFs). JDeli processed the same files successfully.

If you have a corpus of images that occasionally throws exceptions under ImageIO, test them against JDeli before assuming the files are corrupted.

Which Image Library To Choose?

Stick with ImageIO if:

  • You only need JPEG, PNG, BMP, and GIF
  • You’re building a quick prototype or internal tool where stability under load doesn’t matter
  • You can’t add commercial dependencies to your project

Switch to JDeli if:

  • You need HEIC, AVIF, WebP, JPEG XL, or JPEG 2000 support
  • You’re seeing OutOfMemoryErrors or JVM crashes from image processing
  • You need faster JPEG, PNG, or TIFF encoding in batch pipelines
  • You’re processing user-uploaded images and need to handle whatever formats arrive
  • You need reproducible builds with no native dependencies
  • You deploy to air-gapped or high-security environments
  • Security scans are flagging JAI or ImageIO native modules as unsupported

As experienced Java developers, we help you work with images in Java and bring over a decade of hands-on experience with many image file formats.



Are you a Java Developer working with Image files?

Amy Pearson Amy is the product lead for JDeli with expertise in image code, Java, web development, and cloud computing. She focuses on JDeli and has also contributed to JPedal, cloud services, and support. Outside work, she enjoys gaming, F1, and music.