blazediff-shared
The primitives every BlazeDiff crate sits on: the RGBA8 Image buffer, YIQ color math, and PNG, JPEG and QOI decode and encode β normalized to one representation so the compute crates never see a codec.
Installation
# Cargo.toml
[dependencies]
blazediff-shared = "5.4.0"The crate name is blazediff-shared; the library imports as blazediff_shared.
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, because everyone needs pixels and nobody wants to be a codec, and YIQ color math, because both the pixel diff and the region classifier measure perceptual distance and now sit in different crates.
Keeping I/O here also collapses the format dispatch to one copy. It used to be pasted separately into the CLI, the N-API binding and the Python extension β three places to forget when adding a format.
Usage
use blazediff_shared::{load_image_pair, save_image, Image, ImageFormat};
let (a, b) = load_image_pair("expected.png", "actual.jpg")?;
println!("{}x{}", a.width, a.height);
let out = Image::new(a.width, a.height);
save_image(&out, "diff.png", /* compression */ 0, /* quality */ 90)?;Format comes from the file extension for paths and from the magic bytes for buffers:
use blazediff_shared::{decode_image, ImageFormat};
assert_eq!(ImageFormat::from_path("a.JPEG"), Some(ImageFormat::Jpeg));
assert_eq!(ImageFormat::from_bytes(b"qoif...."), Some(ImageFormat::Qoi));
let image = decode_image(&encoded_bytes)?;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 region classifier |
yiq::{unpack_pixel, pack_pixel, is_opaque} | packed-u32 pixel helpers |
The 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 routes decode and level-0 encode through the in-houseblazediff-pngcodec instead, with spng staying as a defensive fallback. - 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. Needs 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.