epaper-dithering-core
High-quality dithering for e-paper / e-ink displays.
- Weighted Cartesian OKLab color matching — perceptually accurate, hue-preserving, no achromatic-attractor bug
- 7 error diffusion kernels — Floyd-Steinberg, Atkinson, Burkes, Stucki, Sierra, Sierra Lite, Jarvis-Judice-Ninke
- Ordered (Bayer 4×4) dithering — perceptually-correct sRGB-space thresholding, parallelized via rayon
- Measured palettes — calibrated colors for real displays (Spectra 7.3", BWRY 3.97", and more)
- Pre-dither knobs — exposure, saturation, shadows, highlights, dynamic-range compression, gamut compression
- Serpentine scanning — reduces directional artifacts in error diffusion
Examples

Usage
use ;
// pixels: flat RGB bytes, row-major (width × height × 3)
let img = new;
let indices = dither;
With a measured palette and pre-dither adjustments:
use ;
let img = new;
let indices = dither;
DitherConfig defaults: Burkes, serpentine: true, exposure: 1.0, saturation: 1.0,
shadows: 0.0, highlights: 0.0, tone: Fixed(0.0), gamut: None.
Pipeline order: exposure → saturation → shadows/highlights → tone → gamut → dither.
DitherMode::None performs direct nearest-color mapping without error diffusion or ordered dithering. dither_with_canonical lets measured palettes use calibrated RGB values for matching while preserving the canonical display palette for exact-color bypass and firmware indices.
With dither_with_canonical, exact canonical display colors are also protected in ordered and error-diffusion modes when pre-processing is off: an image made entirely of display colors is returned as a direct palette-index map, and exact display-color pixels inside a mixed image keep their canonical index instead of being rematched to the measured RGB palette. Pre-processing runs before that exact-pixel check, so explicit tone/gamut compression or other adjustments may intentionally alter those pixels first.
Related packages
- Python:
epaper-dithering - JavaScript:
@opendisplay/epaper-dithering