BlazeDiff vs spng
blazediff-png decodes the same pixels spng decodes and is ~31% faster
at it. Compressed encode is ~3.8x faster,
and the files come out smaller. Both are single-threaded codecs. spng is C and
blazediff-png is Rust.
libspng is a security-focused C PNG library with a simpler API than libpng. BlazeDiff
used it through FFI before it had its own codec. blazediff-png replaces it on its
own terms. With the default backend, it returns the same accept or reject
decision and the same pixels as spng, for valid files and for malformed chunks
and deflate streams. The test suite checks this on a 395-file corpus that
includes PngSuite.
At a glance
| spng (libspng) | BlazeDiff (blazediff-png) | |
|---|---|---|
| License | BSD-2-Clause | MIT |
| Language | C | Rust |
| Delivery | Source files, built with CMake or Meson | crates.io; Node and wasm packages @blazediff/codec-png-* |
| Decode formats | Every PNG format, to many SPNG_FMT_* outputs | Every PNG format, to RGBA8 or any SPNG_FMT_* output (decode_with) |
| Encode | zlib levels | libdeflate levels 1 to 12, plus stored |
| Streaming / progressive | Yes | Streaming encode (encode_to); whole-buffer decode |
| Parallel encode | No | Optional parallel feature |
| Deflate backend | zlib or miniz | System zlib + libdeflate, or pure Rust (rust-backend) |
Benchmarks
The compressed encode compares the default levels: libdeflate 6 in blazediff-png
and zlib 4 in spng. Decode is to RGBA8.
Decode (PNG to RGBA8)
TOTAL TIME, BEST-OF Β· 36 FIXTURES Β· IO EXCLUDED
Apple M1 Max Β· darwin/arm64 Β· Node v22.14.0 Β· rustc 1.97.1 (8bab26f4f 2026-07-14) Β· best-of, iterations scaled by image size, single-threaded Β· 2026-10-22 Β· 234b300-dirty Β· pnpm benchmark png
Per-fixture results (36)
| Fixture | MPx | blazediff | spng | image-rs | zune | blazediff vs spng | image-rs vs spng | zune vs spng |
|---|---|---|---|---|---|---|---|---|
| 4k/1a.png | 17.9 | 138ms | 179ms | 152ms | 209ms | 1.30x | 1.18x | 0.86x |
| 4k/1b.png | 17.9 | 125ms | 187ms | 145ms | 196ms | 1.50x | 1.29x | 0.95x |
| 4k/2a.png | 20.0 | 158ms | 279ms | 194ms | 234ms | 1.76x | 1.44x | 1.19x |
| 4k/2b.png | 20.0 | 160ms | 221ms | 196ms | 235ms | 1.39x | 1.13x | 0.94x |
| 4k/3a.png | 24.0 | 160ms | 220ms | 194ms | 265ms | 1.38x | 1.14x | 0.83x |
| 4k/3b.png | 24.0 | 160ms | 220ms | 200ms | 266ms | 1.37x | 1.10x | 0.83x |
| alpha/1a.png | 0.0 | 0.08ms | 0.20ms | 0.07ms | 0.11ms | 2.37x | 2.64x | 1.76x |
| alpha/1b.png | 0.0 | 0.09ms | 0.20ms | 0.08ms | 0.11ms | 2.31x | 2.57x | 1.73x |
| blazediff/1a.png | 0.4 | 0.60ms | 0.69ms | 0.71ms | 1.01ms | 1.14x | 0.97x | 0.68x |
| blazediff/1b.png | 0.4 | 0.60ms | 0.70ms | 0.71ms | 1.01ms | 1.15x | 0.98x | 0.69x |
| blazediff/2a.png | 0.4 | 0.62ms | 0.83ms | 0.69ms | 0.93ms | 1.36x | 1.21x | 0.90x |
| blazediff/2b.png | 0.4 | 0.66ms | 0.85ms | 0.73ms | 1.02ms | 1.30x | 1.17x | 0.84x |
| blazediff/3a.png | 1.6 | 10.86ms | 13.38ms | 12.10ms | 17.55ms | 1.23x | 1.11x | 0.76x |
| blazediff/3b.png | 1.6 | 10.83ms | 13.25ms | 12.12ms | 17.45ms | 1.22x | 1.09x | 0.76x |
| blazediff/4a.png | 3.8 | 2.80ms | 3.41ms | 3.38ms | 4.43ms | 1.22x | 1.01x | 0.77x |
| blazediff/4b.png | 3.8 | 2.93ms | 3.48ms | 3.42ms | 4.49ms | 1.19x | 1.02x | 0.78x |
| page/1a.png | 58.9 | 156ms | 236ms | 184ms | 255ms | 1.52x | 1.28x | 0.93x |
| page/1b.png | 58.9 | 156ms | 234ms | 184ms | 246ms | 1.50x | 1.28x | 0.95x |
| page/2a.png | 41.7 | 26.12ms | 36.51ms | 20.21ms | 47.56ms | 1.40x | 1.81x | 0.77x |
| page/2b.png | 41.7 | 26.46ms | 35.71ms | 20.65ms | 58.02ms | 1.35x | 1.73x | 0.62x |
| pixelmatch/1a.png | 0.1 | 0.74ms | 0.95ms | 0.67ms | 1.07ms | 1.28x | 1.42x | 0.89x |
| pixelmatch/1b.png | 0.1 | 0.55ms | 0.71ms | 0.53ms | 0.89ms | 1.30x | 1.35x | 0.80x |
| pixelmatch/2a.png | 0.1 | 0.11ms | 0.37ms | 0.12ms | 0.15ms | 3.32x | 3.03x | 2.41x |
| pixelmatch/2b.png | 0.1 | 0.11ms | 0.38ms | 0.12ms | 0.15ms | 3.34x | 3.18x | 2.43x |
| pixelmatch/3a.png | 0.1 | 0.50ms | 0.73ms | 0.47ms | 0.74ms | 1.46x | 1.57x | 0.98x |
| pixelmatch/3b.png | 0.1 | 0.50ms | 0.72ms | 0.46ms | 0.74ms | 1.45x | 1.56x | 0.97x |
| pixelmatch/4a.png | 0.2 | 1.08ms | 1.83ms | 1.36ms | 1.74ms | 1.69x | 1.35x | 1.06x |
| pixelmatch/4b.png | 0.2 | 1.31ms | 1.75ms | 1.36ms | 2.20ms | 1.34x | 1.29x | 0.80x |
| pixelmatch/5a.png | 0.1 | 0.29ms | 0.43ms | 0.28ms | 0.37ms | 1.48x | 1.53x | 1.17x |
| pixelmatch/5b.png | 0.1 | 0.29ms | 0.43ms | 0.27ms | 0.36ms | 1.47x | 1.60x | 1.19x |
| pixelmatch/6a.png | 0.1 | 0.35ms | 0.66ms | 0.38ms | 0.46ms | 1.87x | 1.71x | 1.42x |
| pixelmatch/6b.png | 0.1 | 0.60ms | 0.94ms | 0.51ms | 0.84ms | 1.56x | 1.85x | 1.12x |
| pixelmatch/7a.png | 0.3 | 0.19ms | 0.47ms | 0.18ms | 0.31ms | 2.50x | 2.58x | 1.49x |
| pixelmatch/7b.png | 0.3 | 0.18ms | 0.46ms | 0.19ms | 0.31ms | 2.58x | 2.49x | 1.48x |
| same/1a.png | 1.7 | 2.09ms | 2.53ms | 2.63ms | 9.23ms | 1.21x | 0.96x | 0.27x |
| same/1b.png | 1.7 | 2.09ms | 2.53ms | 2.64ms | 9.28ms | 1.21x | 0.96x | 0.27x |
| TOTAL | 1.31s | 1.90s | 1.53s | 2.09s | 1.46x | 1.24x | 0.91x |
Encode, no compression
TOTAL TIME, BEST-OF Β· 36 FIXTURES Β· IO EXCLUDED
Levels: blazediff stored Β· spng stored Β· image-rs stored Β· zune stored.
Apple M1 Max Β· darwin/arm64 Β· Node v22.14.0 Β· rustc 1.97.1 (8bab26f4f 2026-07-14) Β· best-of, iterations scaled by image size, single-threaded Β· 2026-10-22 Β· 234b300-dirty Β· pnpm benchmark png
Per-fixture results (36)
| Fixture | MPx | blazediff | spng | image-rs | zune | blazediff vs spng | image-rs vs spng | zune vs spng |
|---|---|---|---|---|---|---|---|---|
| 4k/1a.png | 17.9 | 4.35ms | 30.16ms | 27.64ms | 56.75ms | 6.93x | 1.09x | 0.53x |
| 4k/1b.png | 17.9 | 4.83ms | 36.47ms | 25.84ms | 68.77ms | 7.56x | 1.41x | 0.53x |
| 4k/2a.png | 20.0 | 4.64ms | 32.07ms | 25.19ms | 71.57ms | 6.91x | 1.27x | 0.45x |
| 4k/2b.png | 20.0 | 4.63ms | 45.57ms | 31.10ms | 69.71ms | 9.84x | 1.47x | 0.65x |
| 4k/3a.png | 24.0 | 5.63ms | 61.56ms | 60.05ms | 82.34ms | 10.94x | 1.03x | 0.75x |
| 4k/3b.png | 24.0 | 5.63ms | 61.71ms | 29.50ms | 72.25ms | 10.97x | 2.09x | 0.85x |
| alpha/1a.png | 0.0 | 0.01ms | 0.04ms | 0.04ms | 0.10ms | 4.66x | 1.09x | 0.43x |
| alpha/1b.png | 0.0 | 0.01ms | 0.04ms | 0.04ms | 0.10ms | 4.69x | 1.08x | 0.43x |
| blazediff/1a.png | 0.4 | 0.10ms | 0.48ms | 0.39ms | 1.18ms | 4.88x | 1.23x | 0.40x |
| blazediff/1b.png | 0.4 | 0.10ms | 0.42ms | 0.39ms | 1.14ms | 4.22x | 1.09x | 0.37x |
| blazediff/2a.png | 0.4 | 0.09ms | 0.36ms | 0.31ms | 0.94ms | 4.26x | 1.18x | 0.39x |
| blazediff/2b.png | 0.4 | 0.08ms | 0.37ms | 0.31ms | 0.94ms | 4.41x | 1.18x | 0.39x |
| blazediff/3a.png | 1.6 | 0.36ms | 2.00ms | 3.51ms | 4.86ms | 5.50x | 0.57x | 0.41x |
| blazediff/3b.png | 1.6 | 0.36ms | 1.98ms | 2.28ms | 4.92ms | 5.50x | 0.87x | 0.40x |
| blazediff/4a.png | 3.8 | 0.87ms | 5.35ms | 5.73ms | 11.38ms | 6.17x | 0.93x | 0.47x |
| blazediff/4b.png | 3.8 | 0.87ms | 6.12ms | 7.87ms | 13.12ms | 7.05x | 0.78x | 0.47x |
| page/1a.png | 58.9 | 16.84ms | 114ms | 117ms | 192ms | 6.76x | 0.97x | 0.59x |
| page/1b.png | 58.9 | 22.59ms | 108ms | 92.34ms | 196ms | 4.77x | 1.17x | 0.55x |
| page/2a.png | 41.7 | 12.32ms | 88.23ms | 109ms | 122ms | 7.16x | 0.81x | 0.72x |
| page/2b.png | 41.7 | 12.08ms | 78.17ms | 53.95ms | 127ms | 6.47x | 1.45x | 0.62x |
| pixelmatch/1a.png | 0.1 | 0.03ms | 0.16ms | 0.10ms | 0.34ms | 5.05x | 1.56x | 0.47x |
| pixelmatch/1b.png | 0.1 | 0.03ms | 0.13ms | 0.10ms | 0.34ms | 4.51x | 1.33x | 0.39x |
| pixelmatch/2a.png | 0.1 | 0.01ms | 0.08ms | 0.06ms | 0.17ms | 5.25x | 1.42x | 0.45x |
| pixelmatch/2b.png | 0.1 | 0.01ms | 0.08ms | 0.05ms | 0.17ms | 5.39x | 1.44x | 0.47x |
| pixelmatch/3a.png | 0.1 | 0.03ms | 0.16ms | 0.10ms | 0.34ms | 5.27x | 1.55x | 0.46x |
| pixelmatch/3b.png | 0.1 | 0.03ms | 0.16ms | 0.10ms | 0.34ms | 5.27x | 1.56x | 0.47x |
| pixelmatch/4a.png | 0.2 | 0.04ms | 0.18ms | 0.15ms | 0.47ms | 4.06x | 1.24x | 0.38x |
| pixelmatch/4b.png | 0.2 | 0.05ms | 0.19ms | 0.16ms | 0.49ms | 4.20x | 1.16x | 0.39x |
| pixelmatch/5a.png | 0.1 | 0.01ms | 0.07ms | 0.05ms | 0.17ms | 4.60x | 1.29x | 0.40x |
| pixelmatch/5b.png | 0.1 | 0.01ms | 0.08ms | 0.05ms | 0.17ms | 5.38x | 1.52x | 0.47x |
| pixelmatch/6a.png | 0.1 | 0.01ms | 0.07ms | 0.05ms | 0.18ms | 4.53x | 1.25x | 0.38x |
| pixelmatch/6b.png | 0.1 | 0.02ms | 0.07ms | 0.05ms | 0.17ms | 4.56x | 1.31x | 0.41x |
| pixelmatch/7a.png | 0.3 | 0.06ms | 0.24ms | 0.20ms | 0.65ms | 4.07x | 1.22x | 0.37x |
| pixelmatch/7b.png | 0.3 | 0.06ms | 0.27ms | 0.20ms | 0.65ms | 4.62x | 1.38x | 0.42x |
| same/1a.png | 1.7 | 0.40ms | 2.07ms | 3.45ms | 4.98ms | 5.17x | 0.60x | 0.42x |
| same/1b.png | 1.7 | 0.40ms | 2.57ms | 3.96ms | 5.88ms | 6.49x | 0.65x | 0.44x |
| TOTAL | 97.60ms | 679ms | 602ms | 1.11s | 6.96x | 1.13x | 0.61x |
Encode, half compression
TOTAL TIME, BEST-OF Β· 36 FIXTURES Β· IO EXCLUDED
Levels: blazediff libdeflate 6 Β· spng zlib 4 Β· image-rs flate2 4 Β· zune stored.
Apple M1 Max Β· darwin/arm64 Β· Node v22.14.0 Β· rustc 1.97.1 (8bab26f4f 2026-07-14) Β· best-of, iterations scaled by image size, single-threaded Β· 2026-10-22 Β· 234b300-dirty Β· pnpm benchmark png
Per-fixture results (36)
| Fixture | MPx | blazediff | spng | image-rs | zune | blazediff vs spng | image-rs vs spng | zune vs spng |
|---|---|---|---|---|---|---|---|---|
| 4k/1a.png | 17.9 | 1.15s | 2.80s | 1.08s | 55.06ms | 2.43x | 2.59x | 50.89x |
| 4k/1b.png | 17.9 | 959ms | 2.51s | 900ms | 62.97ms | 2.61x | 2.78x | 39.80x |
| 4k/2a.png | 20.0 | 963ms | 3.15s | 1.10s | 71.73ms | 3.27x | 2.85x | 43.91x |
| 4k/2b.png | 20.0 | 1.01s | 3.05s | 1.12s | 60.27ms | 3.02x | 2.73x | 50.64x |
| 4k/3a.png | 24.0 | 1.41s | 3.82s | 1.46s | 83.12ms | 2.70x | 2.62x | 45.99x |
| 4k/3b.png | 24.0 | 1.41s | 3.88s | 1.47s | 72.03ms | 2.75x | 2.64x | 53.80x |
| alpha/1a.png | 0.0 | 0.36ms | 2.88ms | 0.37ms | 0.10ms | 8.01x | 7.84x | 30.06x |
| alpha/1b.png | 0.0 | 0.36ms | 2.88ms | 0.38ms | 0.10ms | 7.99x | 7.68x | 30.03x |
| blazediff/1a.png | 0.4 | 3.80ms | 33.99ms | 2.96ms | 1.14ms | 8.95x | 11.48x | 29.83x |
| blazediff/1b.png | 0.4 | 3.83ms | 34.20ms | 2.96ms | 1.15ms | 8.92x | 11.55x | 29.76x |
| blazediff/2a.png | 0.4 | 3.31ms | 27.83ms | 2.59ms | 0.93ms | 8.41x | 10.73x | 29.78x |
| blazediff/2b.png | 0.4 | 3.34ms | 28.06ms | 2.68ms | 0.93ms | 8.40x | 10.49x | 30.25x |
| blazediff/3a.png | 1.6 | 37.88ms | 157ms | 37.58ms | 4.88ms | 4.15x | 4.18x | 32.20x |
| blazediff/3b.png | 1.6 | 36.72ms | 158ms | 37.02ms | 4.98ms | 4.31x | 4.28x | 31.80x |
| blazediff/4a.png | 3.8 | 29.16ms | 292ms | 20.48ms | 11.31ms | 10.02x | 14.26x | 25.83x |
| blazediff/4b.png | 3.8 | 29.18ms | 294ms | 20.45ms | 12.70ms | 10.07x | 14.37x | 23.14x |
| page/1a.png | 58.9 | 822ms | 5.19s | 706ms | 192ms | 6.32x | 7.36x | 27.00x |
| page/1b.png | 58.9 | 829ms | 5.13s | 695ms | 194ms | 6.19x | 7.38x | 26.42x |
| page/2a.png | 41.7 | 629ms | 3.67s | 595ms | 133ms | 5.84x | 6.17x | 27.55x |
| page/2b.png | 41.7 | 619ms | 3.69s | 598ms | 131ms | 5.96x | 6.17x | 28.23x |
| pixelmatch/1a.png | 0.1 | 1.65ms | 11.24ms | 1.72ms | 0.34ms | 6.82x | 6.52x | 33.36x |
| pixelmatch/1b.png | 0.1 | 1.65ms | 11.21ms | 1.73ms | 0.34ms | 6.80x | 6.47x | 32.73x |
| pixelmatch/2a.png | 0.1 | 2.45ms | 8.91ms | 3.20ms | 0.17ms | 3.63x | 2.78x | 51.41x |
| pixelmatch/2b.png | 0.1 | 2.49ms | 9.20ms | 3.31ms | 0.18ms | 3.70x | 2.78x | 50.33x |
| pixelmatch/3a.png | 0.1 | 1.34ms | 10.42ms | 1.16ms | 0.34ms | 7.77x | 8.99x | 30.45x |
| pixelmatch/3b.png | 0.1 | 1.32ms | 10.43ms | 1.16ms | 0.34ms | 7.93x | 9.01x | 30.87x |
| pixelmatch/4a.png | 0.2 | 6.97ms | 24.69ms | 8.84ms | 0.47ms | 3.54x | 2.79x | 52.02x |
| pixelmatch/4b.png | 0.2 | 7.05ms | 25.16ms | 9.30ms | 0.47ms | 3.57x | 2.71x | 53.97x |
| pixelmatch/5a.png | 0.1 | 0.61ms | 5.07ms | 0.58ms | 0.17ms | 8.37x | 8.69x | 29.47x |
| pixelmatch/5b.png | 0.1 | 0.61ms | 5.06ms | 0.58ms | 0.17ms | 8.36x | 8.78x | 29.70x |
| pixelmatch/6a.png | 0.1 | 2.11ms | 7.23ms | 2.44ms | 0.18ms | 3.43x | 2.96x | 40.61x |
| pixelmatch/6b.png | 0.1 | 2.03ms | 6.88ms | 2.14ms | 0.17ms | 3.39x | 3.22x | 40.20x |
| pixelmatch/7a.png | 0.3 | 3.77ms | 22.39ms | 4.05ms | 0.64ms | 5.95x | 5.53x | 34.74x |
| pixelmatch/7b.png | 0.3 | 3.77ms | 22.07ms | 4.03ms | 0.66ms | 5.85x | 5.47x | 33.58x |
| same/1a.png | 1.7 | 15.40ms | 137ms | 12.25ms | 5.18ms | 8.90x | 11.19x | 26.47x |
| same/1b.png | 1.7 | 15.70ms | 140ms | 12.38ms | 5.86ms | 8.92x | 11.30x | 23.91x |
| TOTAL | 10.02s | 38.37s | 9.92s | 1.11s | 3.83x | 3.87x | 34.59x |
Encode size, no compression
TOTAL OUTPUT SIZE Β· 36 FIXTURES
Levels: blazediff stored Β· spng stored Β· image-rs stored Β· zune stored.
Apple M1 Max Β· darwin/arm64 Β· Node v22.14.0 Β· rustc 1.97.1 (8bab26f4f 2026-07-14) Β· best-of, iterations scaled by image size, single-threaded Β· 2026-10-22 Β· 234b300-dirty Β· pnpm benchmark png
Per-fixture results (36)
| Fixture | MPx | blazediff | spng | image-rs | zune | blazediff vs spng | image-rs vs spng | zune vs spng |
|---|---|---|---|---|---|---|---|---|
| 4k/1a.png | 17.9 | 68.4 MB | 68.5 MB | 68.4 MB | 68.5 MB | 99.8% | 99.8% | 100.0% |
| 4k/1b.png | 17.9 | 68.4 MB | 68.5 MB | 68.4 MB | 68.5 MB | 99.8% | 99.8% | 100.0% |
| 4k/2a.png | 20.0 | 76.2 MB | 76.3 MB | 76.2 MB | 76.3 MB | 99.8% | 99.8% | 100.0% |
| 4k/2b.png | 20.0 | 76.2 MB | 76.3 MB | 76.2 MB | 76.3 MB | 99.8% | 99.8% | 100.0% |
| 4k/3a.png | 24.0 | 91.6 MB | 91.7 MB | 91.6 MB | 91.7 MB | 99.9% | 99.9% | 100.0% |
| 4k/3b.png | 24.0 | 91.6 MB | 91.7 MB | 91.6 MB | 91.7 MB | 99.9% | 99.9% | 100.0% |
| alpha/1a.png | 0.0 | 144.3 KB | 144.5 KB | 144.3 KB | 144.5 KB | 99.8% | 99.8% | 100.0% |
| alpha/1b.png | 0.0 | 144.3 KB | 144.5 KB | 144.3 KB | 144.5 KB | 99.8% | 99.8% | 100.0% |
| blazediff/1a.png | 0.4 | 1.6 MB | 1.6 MB | 1.6 MB | 1.6 MB | 99.8% | 99.8% | 100.0% |
| blazediff/1b.png | 0.4 | 1.6 MB | 1.6 MB | 1.6 MB | 1.6 MB | 99.8% | 99.8% | 100.0% |
| blazediff/2a.png | 0.4 | 1.3 MB | 1.3 MB | 1.3 MB | 1.3 MB | 99.8% | 99.8% | 100.0% |
| blazediff/2b.png | 0.4 | 1.3 MB | 1.3 MB | 1.3 MB | 1.3 MB | 99.8% | 99.8% | 100.0% |
| blazediff/3a.png | 1.6 | 6.2 MB | 6.2 MB | 6.2 MB | 6.2 MB | 99.8% | 99.8% | 100.0% |
| blazediff/3b.png | 1.6 | 6.2 MB | 6.2 MB | 6.2 MB | 6.2 MB | 99.8% | 99.8% | 100.0% |
| blazediff/4a.png | 3.8 | 14.4 MB | 14.5 MB | 14.4 MB | 14.5 MB | 99.8% | 99.8% | 100.0% |
| blazediff/4b.png | 3.8 | 14.4 MB | 14.5 MB | 14.4 MB | 14.5 MB | 99.8% | 99.8% | 100.0% |
| page/1a.png | 58.9 | 224.9 MB | 225.2 MB | 224.9 MB | 225.2 MB | 99.8% | 99.8% | 100.0% |
| page/1b.png | 58.9 | 224.9 MB | 225.2 MB | 224.9 MB | 225.2 MB | 99.8% | 99.8% | 100.0% |
| page/2a.png | 41.7 | 159.1 MB | 159.4 MB | 159.1 MB | 159.4 MB | 99.8% | 99.8% | 100.0% |
| page/2b.png | 41.7 | 159.1 MB | 159.4 MB | 159.1 MB | 159.4 MB | 99.8% | 99.8% | 100.0% |
| pixelmatch/1a.png | 0.1 | 512.4 KB | 513.1 KB | 512.4 KB | 513.1 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/1b.png | 0.1 | 512.4 KB | 513.1 KB | 512.4 KB | 513.1 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/2a.png | 0.1 | 256.3 KB | 256.7 KB | 256.3 KB | 256.7 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/2b.png | 0.1 | 256.3 KB | 256.7 KB | 256.3 KB | 256.7 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/3a.png | 0.1 | 512.4 KB | 513.1 KB | 512.4 KB | 513.1 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/3b.png | 0.1 | 512.4 KB | 513.1 KB | 512.4 KB | 513.1 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/4a.png | 0.2 | 705.4 KB | 706.5 KB | 705.4 KB | 706.5 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/4b.png | 0.2 | 705.4 KB | 706.5 KB | 705.4 KB | 706.5 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/5a.png | 0.1 | 256.3 KB | 256.7 KB | 256.3 KB | 256.7 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/5b.png | 0.1 | 256.3 KB | 256.7 KB | 256.3 KB | 256.7 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/6a.png | 0.1 | 256.3 KB | 256.7 KB | 256.3 KB | 256.7 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/6b.png | 0.1 | 256.3 KB | 256.7 KB | 256.3 KB | 256.7 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/7a.png | 0.3 | 977.2 KB | 978.7 KB | 977.2 KB | 978.6 KB | 99.8% | 99.8% | 100.0% |
| pixelmatch/7b.png | 0.3 | 977.2 KB | 978.7 KB | 977.2 KB | 978.6 KB | 99.8% | 99.8% | 100.0% |
| same/1a.png | 1.7 | 6.6 MB | 6.6 MB | 6.6 MB | 6.6 MB | 99.8% | 99.8% | 100.0% |
| same/1b.png | 1.7 | 6.6 MB | 6.6 MB | 6.6 MB | 6.6 MB | 99.8% | 99.8% | 100.0% |
| TOTAL | 1.28 GB | 1.28 GB | 1.28 GB | 1.28 GB | 99.8% | 99.8% | 100.0% |
Encode size, half compression
TOTAL OUTPUT SIZE Β· 36 FIXTURES
Levels: blazediff libdeflate 6 Β· spng zlib 4 Β· image-rs flate2 4 Β· zune stored.
Apple M1 Max Β· darwin/arm64 Β· Node v22.14.0 Β· rustc 1.97.1 (8bab26f4f 2026-07-14) Β· best-of, iterations scaled by image size, single-threaded Β· 2026-10-22 Β· 234b300-dirty Β· pnpm benchmark png
Per-fixture results (36)
| Fixture | MPx | blazediff | spng | image-rs | zune | blazediff vs spng | image-rs vs spng | zune vs spng |
|---|---|---|---|---|---|---|---|---|
| 4k/1a.png | 17.9 | 20.6 MB | 21.3 MB | 21.5 MB | 68.5 MB | 96.6% | 100.9% | 320.8% |
| 4k/1b.png | 17.9 | 17.2 MB | 18.2 MB | 17.9 MB | 68.5 MB | 94.5% | 98.2% | 375.4% |
| 4k/2a.png | 20.0 | 26.1 MB | 27.1 MB | 26.6 MB | 76.3 MB | 96.2% | 98.3% | 281.7% |
| 4k/2b.png | 20.0 | 26.6 MB | 27.6 MB | 27.2 MB | 76.3 MB | 96.6% | 98.6% | 276.8% |
| 4k/3a.png | 24.0 | 32.1 MB | 35.0 MB | 33.1 MB | 91.7 MB | 91.7% | 94.7% | 262.2% |
| 4k/3b.png | 24.0 | 31.6 MB | 34.5 MB | 32.6 MB | 91.7 MB | 91.5% | 94.4% | 265.5% |
| alpha/1a.png | 0.0 | 2.8 KB | 2.9 KB | 3.0 KB | 144.5 KB | 95.3% | 100.3% | 4908.5% |
| alpha/1b.png | 0.0 | 2.8 KB | 3.0 KB | 3.0 KB | 144.5 KB | 95.2% | 99.9% | 4826.8% |
| blazediff/1a.png | 0.4 | 30.8 KB | 35.4 KB | 30.2 KB | 1.6 MB | 86.9% | 85.4% | 4770.7% |
| blazediff/1b.png | 0.4 | 30.9 KB | 35.4 KB | 30.3 KB | 1.6 MB | 87.1% | 85.4% | 4765.8% |
| blazediff/2a.png | 0.4 | 31.5 KB | 38.1 KB | 30.1 KB | 1.3 MB | 82.6% | 79.2% | 3610.7% |
| blazediff/2b.png | 0.4 | 32.8 KB | 39.8 KB | 31.5 KB | 1.3 MB | 82.3% | 79.2% | 3449.9% |
| blazediff/3a.png | 1.6 | 815.5 KB | 877.0 KB | 835.0 KB | 6.2 MB | 93.0% | 95.2% | 727.6% |
| blazediff/3b.png | 1.6 | 792.2 KB | 853.3 KB | 812.1 KB | 6.2 MB | 92.8% | 95.2% | 747.8% |
| blazediff/4a.png | 3.8 | 98.7 KB | 114.0 KB | 100.5 KB | 14.5 MB | 86.6% | 88.2% | 12998.3% |
| blazediff/4b.png | 3.8 | 98.3 KB | 114.1 KB | 100.4 KB | 14.5 MB | 86.1% | 88.0% | 12985.2% |
| page/1a.png | 58.9 | 11.1 MB | 12.0 MB | 11.1 MB | 225.2 MB | 92.5% | 92.5% | 1884.8% |
| page/1b.png | 58.9 | 11.0 MB | 11.9 MB | 11.0 MB | 225.2 MB | 92.5% | 92.5% | 1886.3% |
| page/2a.png | 41.7 | 10.3 MB | 11.0 MB | 10.4 MB | 159.4 MB | 93.9% | 94.6% | 1446.2% |
| page/2b.png | 41.7 | 10.4 MB | 11.0 MB | 10.5 MB | 159.4 MB | 94.0% | 94.8% | 1445.4% |
| pixelmatch/1a.png | 0.1 | 31.8 KB | 32.2 KB | 33.4 KB | 513.1 KB | 98.9% | 103.6% | 1593.4% |
| pixelmatch/1b.png | 0.1 | 31.9 KB | 32.2 KB | 33.4 KB | 513.1 KB | 98.9% | 103.5% | 1591.6% |
| pixelmatch/2a.png | 0.1 | 90.8 KB | 96.9 KB | 91.0 KB | 256.7 KB | 93.8% | 93.9% | 265.0% |
| pixelmatch/2b.png | 0.1 | 94.4 KB | 99.9 KB | 94.3 KB | 256.7 KB | 94.4% | 94.3% | 257.0% |
| pixelmatch/3a.png | 0.1 | 13.9 KB | 14.7 KB | 13.8 KB | 513.1 KB | 94.6% | 94.2% | 3497.0% |
| pixelmatch/3b.png | 0.1 | 13.7 KB | 14.3 KB | 13.5 KB | 513.1 KB | 95.7% | 94.5% | 3593.6% |
| pixelmatch/4a.png | 0.2 | 240.1 KB | 254.1 KB | 241.3 KB | 706.5 KB | 94.5% | 95.0% | 278.0% |
| pixelmatch/4b.png | 0.2 | 251.0 KB | 268.1 KB | 251.9 KB | 706.5 KB | 93.6% | 94.0% | 263.5% |
| pixelmatch/5a.png | 0.1 | 3.0 KB | 3.3 KB | 3.1 KB | 256.7 KB | 91.4% | 92.3% | 7693.1% |
| pixelmatch/5b.png | 0.1 | 3.2 KB | 3.3 KB | 3.2 KB | 256.7 KB | 98.5% | 98.4% | 7793.4% |
| pixelmatch/6a.png | 0.1 | 44.4 KB | 50.7 KB | 48.5 KB | 256.7 KB | 87.6% | 95.6% | 506.4% |
| pixelmatch/6b.png | 0.1 | 32.0 KB | 37.9 KB | 36.5 KB | 256.7 KB | 84.6% | 96.4% | 678.0% |
| pixelmatch/7a.png | 0.3 | 88.2 KB | 96.1 KB | 87.4 KB | 978.6 KB | 91.8% | 91.0% | 1018.8% |
| pixelmatch/7b.png | 0.3 | 87.3 KB | 94.8 KB | 86.5 KB | 978.6 KB | 92.1% | 91.2% | 1032.4% |
| same/1a.png | 1.7 | 124.9 KB | 140.3 KB | 126.7 KB | 6.6 MB | 89.1% | 90.3% | 4847.0% |
| same/1b.png | 1.7 | 124.9 KB | 140.3 KB | 126.7 KB | 6.6 MB | 89.1% | 90.3% | 4847.0% |
| TOTAL | 200.2 MB | 213.1 MB | 205.1 MB | 1.28 GB | 93.9% | 96.3% | 614.8% |
Switching
# Cargo.toml
[dependencies]
blazediff-png = "6"use blazediff_png::{decode, encode, EncodeOptions};
let bytes = std::fs::read("image.png")?;
let image = decode(&bytes)?; // RGBA8: Image { data, width, height }
let png = encode(&image, &EncodeOptions::default())?;There is one deliberate difference. The PNG specification says a decoder must
mask a gray tRNS key to the image bit depth. spng does not, and
blazediff-png does, as image-rs does. Only files from encoders that leave
the unused key bits set decode differently: those pixels are transparent in
blazediff-png and opaque in spng.
When to keep spng
- Your project is C or C++ and you do not want a Rust toolchain.
- You need progressive or streaming decode.
- You rely on spngβs security record: CERT C rules, OSS-Fuzz, Coverity and PVS-Studio.