Skip to Content
New: ssim-native brings SSIM, MS-SSIM and Hitchhiker's to Node, and interpret-native ships diff interpretation on its own. Read more β†’
APIsblazediff-shared

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

ItemPurpose
ImageRGBA8 buffer plus dimensions, with as_u32 / get_pixel / set_pixel helpers
ImageErrorIo, Png, Jpeg, Qoi, UnsupportedFormat
ImageFormatfrom_path, from_bytes, as_str
load_image, load_image_pairpath in, format auto-detected; the pair loads in parallel
decode_image, decode_image_pairencoded bytes in, format sniffed from magic bytes
save_imageformat from the output extension
load_png … save_qoithe per-codec entry points, when you already know the format
yiq::color_deltasquared 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_ENABLED to a truthy value routes decode and level-0 encode through the in-house blazediff-png codec 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 only Image, ImageError and ImageFormat. That is what the wasm build of blazediff links.
  • fuzzing β€” internal only; exposes the spng reference decoder for blazediff-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.

Last updated on