FPNG Java¶
FPNG-Java provides Java wrappers around three native, SIMD-optimised PNG encoders — FPNG (SSE2), FPNGE (AVX2), and ZPNG (zlib-ng). All produce fully compliant PNG files at significantly higher throughput than javax.imageio.
The three encoders solve different problems:
FPNG and FPNGE are tuned for photographic content and raw throughput. They use minimal-effort deflate via lookup tables, producing large files very fast.
ZPNG uses zlib-ng’s full deflate implementation and is tuned for screen content (UI screenshots, dashboards, remote-desktop tiles, anything with sharp edges and large flat-colour regions). It produces files about half the size of FPNG/FPNGE on this kind of input, at roughly 2-3× the encoding cost. For bandwidth-constrained delivery this is the right trade.
The JDK22+ version of the encoders uses Foreign Function & Memory API and is the fastest. Otherwise JNI is used via JNA, which comes with some overhead.
Latest stable release: fpng-java-2.1.jar
GitHub Repository: https://github.com/manticore-projects/fpng-java
Features¶
- Three encoders, each tuned for a different workload
FPNG — SSE2/SSE4.1/PCLMUL, works on any x86-64 CPU; raw throughput
FPNGE — AVX2, used automatically on capable CPUs; faster than FPNG
ZPNG — zlib-ng backed, NEON/AVX2 accelerated; best file size for screen content
Runtime
hasAVX2()CPUID probe — no AVX2 library is loaded on unsupported hardwareFFM for JDK22+, JNA for older JDKs
Encodes
BufferedImagedirectly — 3-channel (RGB) and 4-channel (RGBA)Handles Java’s native
TYPE_3BYTE_BGR/TYPE_4BYTE_ABGRchannel order in C via SIMD byte shuffles (NEON on ARM64, SSE/AVX2 on x86)Interleaved Adler32 checksum — no redundant full-image pass (FPNG/FPNGE)
PCLMUL-accelerated CRC32
Supports
Java 8,Java 11,Java 17,Java 21, andJava 23Platforms: Linux x86-64 / ARM64, macOS x86-64 / ARM64 (Apple Silicon), Windows x86 / x86-64
All binaries built and tested natively on each target platform via GitHub Runners
Benchmarks¶
Run the JMH benchmarks with:
./gradlew jmh
Results are written to benchmark/build/results/jmh/results.json.
Speed¶
PNG Encoder Benchmark Report
3 ch text_test.png
| Encoder | Score (ms/op) | Error | vs Winner |
|---|---|---|---|
| FPNGE (Java 23 FFM) | 2.291 | ±NaN | baseline |
| FPNGE (Java 23 FFM) | 2.591 | ±NaN | +13.1% |
| FPNGE (JNI) | 2.657 | ±NaN | +16.0% |
| FPNGE (JNI) | 2.972 | ±NaN | +29.8% |
| FPNGE (Java 23 FFM) | 3.350 | ±NaN | +46.2% |
| FPNGE (JNI) | 3.763 | ±NaN | +64.3% |
| FPNGE (Java 23 FFM) | 4.625 | ±NaN | +101.9% |
| FPNG Encoder (Java 23) | 4.695 | ±NaN | +104.9% |
| FPNGE (JNI) | 5.025 | ±NaN | +119.4% |
| FPNGE (Java 23 FFM) | 5.104 | ±NaN | +122.8% |
| ZPNG23Benchmark | 5.517 | ±NaN | +140.8% |
| FPNGE (JNI) | 5.531 | ±NaN | +141.4% |
| FPNG Encoder (Java 23) | 5.871 | ±NaN | +156.3% |
| FPNG Encoder (Java 23) | 5.902 | ±NaN | +157.7% |
| PNGEncoder (fastest) | 6.984 | ±NaN | +204.9% |
| ZPNG23Benchmark | 7.307 | ±NaN | +219.0% |
| FPNG Encoder (JNI) | 7.383 | ±NaN | +222.3% |
| FPNG Encoder (Java 23) | 7.792 | ±NaN | +240.1% |
| FPNG Encoder (Java 23) | 7.914 | ±NaN | +245.5% |
| PNGEncoder (fastest) | 8.323 | ±NaN | +263.3% |
| FPNG Encoder (JNI) | 8.579 | ±NaN | +274.5% |
| FPNG Encoder (JNI) | 8.591 | ±NaN | +275.0% |
| FPNG Encoder (JNI) | 9.835 | ±NaN | +329.4% |
| FPNG Encoder (JNI) | 10.120 | ±NaN | +341.8% |
| PNGEncoder (fastest) | 10.698 | ±NaN | +367.0% |
| PNGEncoder (fastest) | 12.615 | ±NaN | +450.7% |
| PNGEncoder (fastest) | 15.023 | ±NaN | +555.8% |
| ZPNG23Benchmark | 22.746 | ±NaN | +892.9% |
| ZPNG23Benchmark | 23.545 | ±NaN | +927.8% |
| ZPNG23Benchmark | 25.026 | ±NaN | +992.5% |
| ImageIO | 36.859 | ±NaN | +1509.0% |
| ImageIO | 36.895 | ±NaN | +1510.6% |
| ImageIO | 36.928 | ±NaN | +1512.1% |
| ImageIO | 38.221 | ±NaN | +1568.5% |
| ImageIO | 38.263 | ±NaN | +1570.4% |
| ObjectPlanet PNG | 53.660 | ±NaN | +2242.5% |
| ObjectPlanet PNG | 55.038 | ±NaN | +2302.6% |
| ObjectPlanet PNG | 57.271 | ±NaN | +2400.1% |
| PNGEncoder | 57.828 | ±NaN | +2424.4% |
| ObjectPlanet PNG | 59.245 | ±NaN | +2486.3% |
| ObjectPlanet PNG | 61.444 | ±NaN | +2582.3% |
| PNGEncoder | 61.974 | ±NaN | +2605.4% |
| PNGEncoder | 62.756 | ±NaN | +2639.6% |
| PNGEncoder | 66.641 | ±NaN | +2809.2% |
| PNGEncoder | 69.236 | ±NaN | +2922.4% |
Efficiency¶
PNG Encoder Pareto Report
3 ch text_test.png
FPNG Encoder (JNI)
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | dominated | 10.12 ±NaN | 339.3 | 4.2% |
| 2 | dominated | 8.58 ±NaN | 8155.5 | 100.0% |
| 3 | dominated | 8.59 ±NaN | 8155.5 | 100.0% |
| 4 | PARETO | 7.38 ±NaN | 668.2 | 8.2% |
| 5 | PARETO | 9.84 ±NaN | 339.3 | 4.2% |
FPNG Encoder (Java 23)
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | PARETO | 7.79 ±NaN | 339.3 | 4.2% |
| 2 | dominated | 5.90 ±NaN | 8155.5 | 100.0% |
| 3 | dominated | 5.87 ±NaN | 8155.5 | 100.0% |
| 4 | PARETO | 4.69 ±NaN | 668.2 | 8.2% |
| 5 | dominated | 7.91 ±NaN | 339.3 | 4.2% |
FPNGE (JNI)
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | PARETO | 2.66 ±NaN | 741.6 | 94.7% |
| 2 | dominated | 2.97 ±NaN | 783.3 | 100.0% |
| 3 | PARETO | 3.76 ±NaN | 685.4 | 87.5% |
| 4 | KNEE | 5.02 ±NaN | 684.7 | 87.4% |
| 5 | PARETO | 5.53 ±NaN | 525.9 | 67.1% |
FPNGE (Java 23 FFM)
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | PARETO | 2.29 ±NaN | 736.1 | 94.0% |
| 2 | dominated | 2.59 ±NaN | 783.3 | 100.0% |
| 3 | KNEE | 3.35 ±NaN | 679.9 | 86.8% |
| 4 | dominated | 4.63 ±NaN | 684.7 | 87.4% |
| 5 | PARETO | 5.10 ±NaN | 525.9 | 67.1% |
ImageIO
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | dominated | 38.26 ±NaN | 152.1 | 100.0% |
| 2 | PARETO | 36.86 ±NaN | 152.1 | 100.0% |
| 3 | dominated | 38.22 ±NaN | 152.1 | 100.0% |
| 4 | dominated | 36.89 ±NaN | 152.1 | 100.0% |
| 5 | dominated | 36.93 ±NaN | 152.1 | 100.0% |
ObjectPlanet PNG
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | PARETO | 53.66 ±NaN | 336.4 | 100.0% |
| 2 | KNEE | 55.04 ±NaN | 187.0 | 55.6% |
| 3 | PARETO | 57.27 ±NaN | 169.0 | 50.2% |
| 4 | PARETO | 59.25 ±NaN | 152.0 | 45.2% |
| 5 | PARETO | 61.44 ±NaN | 144.6 | 43.0% |
PNGEncoder
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | dominated | 61.97 ±NaN | 336.5 | 100.0% |
| 1 | PARETO | 6.98 ±NaN | 336.5 | 100.0% |
| 2 | dominated | 62.76 ±NaN | 187.1 | 55.6% |
| 2 | KNEE | 8.32 ±NaN | 187.1 | 55.6% |
| 3 | dominated | 57.83 ±NaN | 169.1 | 50.2% |
| 3 | PARETO | 10.70 ±NaN | 169.1 | 50.2% |
| 4 | dominated | 66.64 ±NaN | 152.1 | 45.2% |
| 4 | PARETO | 12.62 ±NaN | 152.1 | 45.2% |
| 5 | dominated | 69.24 ±NaN | 144.6 | 43.0% |
| 5 | PARETO | 15.02 ±NaN | 144.6 | 43.0% |
ZPNG23Benchmark
| Lvl | Status | Time (ms) | Size (KB) | Ratio |
|---|---|---|---|---|
| 1 | PARETO | 5.52 ±NaN | 338.8 | 100.0% |
| 2 | WINNER | 7.31 ±NaN | 187.0 | 55.2% |
| 3 | PARETO | 22.75 ±NaN | 168.2 | 49.6% |
| 4 | PARETO | 23.55 ±NaN | 151.7 | 44.8% |
| 5 | PARETO | 25.03 ±NaN | 144.2 | 42.6% |