blazediff-shared
The primitives every BlazeDiff crate sits on: the RGBA8 Image buffer, YIQ color math, and
decode/encode for PNG, JPEG and QOI.
Why it exists
The crates above it form a chain — blazediff depends on blazediff-ssim, which depends on
blazediff-interpret — so anything two of them share has to live below all of them. Two things
qualify:
- Image I/O. Everyone needs to turn a path or an encoded buffer into pixels, and nobody wants to be a codec. Keeping it here also means one copy of the format dispatch; it used to be pasted into the CLI, the N-API binding and the Python extension separately, which is exactly the kind of thing that drifts.
- YIQ color math. Both the pixel diff and the region classifier measure perceptual distance between two pixels, and they now live in different crates.
Usage
use ;
let = load_image_pair?;
println!;
// `Image` is RGBA8, 4 bytes per pixel, row-major.
let out = new;
save_image?;
# Ok::
Format is taken from the file extension for paths and from the magic bytes for buffers:
use ;
assert_eq!;
assert_eq!;
let image = decode_image?;
# Ok::
API
| Item | Purpose |
|---|---|
Image |
RGBA8 buffer plus dimensions, with as_u32 / get_pixel / set_pixel helpers |
ImageError |
Io, Png, Jpeg, Qoi, UnsupportedFormat |
ImageFormat |
from_path, from_bytes, as_str |
load_image, load_image_pair |
path in, format auto-detected; the pair loads in parallel |
decode_image, decode_image_pair |
encoded bytes in, format sniffed from magic bytes |
save_image |
format from the output extension |
load_png … save_qoi |
the per-codec entry points, when you already know the format |
yiq::color_delta |
squared YIQ distance between two packed pixels, the perceptual metric behind both the diff and the classifier |
yiq::{unpack_pixel, pack_pixel, is_opaque} |
packed-u32 pixel helpers |
Per-codec modules (png_io, jpeg_io, qoi_io) are public too, for callers that want to skip
detection.
Codecs
- PNG — vendored libspng, compiled with its SIMD paths.
Setting
BLAZEDIFF_PNG_ENABLEDto a truthy value (1/true/yes/on) routes decode and level-0 encode through the in-houseblazediff-pngcodec instead, with spng staying as a defensive fallback. The toggle lives here rather than in any one front-end, so everything built on this crate inherits it — theblazediffCLI, its N-API and Python bindings, and@blazediff/ssim-native. Read once per process; query it withblazediff_shared::blazediff_png_enabled(). - JPEG — vendored libjpeg-turbo via the TurboJPEG API.
- QOI —
qoi-rust, pure Rust.
Adler-32 verification stays on for PNG decode: these entry points read arbitrary, possibly untrusted files, so a corrupt zlib stream must error rather than hand back wrong pixels.
Features
codecs(default) — everything above. Requires a C toolchain and cmake.- Without it the crate is pure Rust and compiles to
wasm32, leaving onlyImage,ImageErrorandImageFormat. That is what the wasm build ofblazedifflinks. fuzzing— internal only; exposes the spng reference decoder forblazediff-png's differential tests.
Error messages are contract
ImageError's Display strings are surfaced verbatim by the CLI, the N-API binding, the Python
extension and the JS wrappers, and @blazediff/core-native pattern-matches on them to tell a
missing file from a malformed one. Changing their wording is a breaking change.
License
MIT