Crates.io | dithereens |
lib.rs | dithereens |
version | 0.4.0 |
created_at | 2024-06-17 17:49:12.199893+00 |
updated_at | 2025-09-11 15:14:07.923346+00 |
description | Functions & traits for quantizing values with error-diffusion |
homepage | |
repository | https://github.com/virtualritz/dithereens/ |
max_upload_size | |
id | 1274678 |
size | 8,610,504 |
dithereens
Before (top) and after (bottom) dithering a gradient (uses
simple_dither()
, i.e. defaults).
Functions and traits for quantizing values with deterministic hash-based error-diffusion.
Quantizing from f64
/f32
/f16
to u32
/u16
/u8
without dithering
creates banding. This crate provides deterministic hash-based dithering to
reduce quantization artifacts.
dither()
, simple_dither()
.dither_iter()
, simple_dither_iter()
.dither_slice()
, simple_dither_slice()
.*_with_method()
functions.no_std
support: Works in embedded environments.f32
, f64
, f16
(with nightly_f16
feature), or
any type implementing DitherFloat
.blue_noise
feature).use dithereens::simple_dither;
let value: f32 = 0.5;
// Dither `value` to `127u8` or `128u8` deterministically.
// The same index and seed will always produce the same result.
let dithered_value: u8 =
simple_dither(value, 255.0, 0, 42).clamp(0.0, 255.0) as u8;
assert!(dithered_value == 127 || 128 == dithered_value);
1D methods have been used successfully for image dithering for years by processing images as flat arrays. They work well when you don't need spatial correlation between pixels.
blue_noise
feature): True blue noise from
precomputed tables.2D methods use pixel coordinates to create spatially-aware dithering patterns, which can produce more visually pleasing results for images.
use dithereens::{GoldenRatio, Hash, R2, simple_dither_with_linear_rng};
let value = 0.5f32;
let seed = 42;
// Use different dithering methods.
let hash_method = Hash::new(seed);
let r2_method = R2::new(seed);
let golden_method = GoldenRatio::new(seed);
let dithered_hash =
simple_dither_with_linear_rng(value, 255.0, 0, &hash_method);
let dithered_r2 =
simple_dither_with_linear_rng(value, 255.0, 0, &r2_method);
let dithered_golden =
simple_dither_with_linear_rng(value, 255.0, 0, &golden_method);
1D methods work great for images when processed as flat arrays:
use dithereens::{Hash, simple_dither_slice};
// Example: dither a grayscale image.
let width = 256;
let height = 256;
let mut pixels: Vec<f32> = vec![0.5; width * height];
// Process entire image as flat array with 1D dithering.
simple_dither_slice(&mut pixels, 255.0, 42);
// pixels now contains dithered values.
2D methods use spatial coordinates for better visual results:
use dithereens::{InterleavedGradientNoise, simple_dither_slice_2d};
// Example: dither a grayscale image.
let width = 256;
let height = 256;
let mut pixels: Vec<f32> = vec![0.5; width * height];
// Use IGN for 2D dithering.
let method = InterleavedGradientNoise::new(42);
simple_dither_slice_2d(&mut pixels, width, 255.0, &method);
// pixels now contains dithered values.
Benchmarks with 10,000 values:
dither()
, simple_dither()
.dither_slice()
,
[simple_dither_slice()
] (~5.6x faster than iterator methods).dither_iter()
, simple_dither_iter()
, or
DitherIteratorExt
adapters (allocation overhead).Via rayon
(enabled by default). With rayon
enabled, _iter
and
_slice
functions use parallel processing automatically for better
performance on large datasets.
no_std
SupportThis crate supports no_std
environments. The libm
crate provides a
native round()
implementation. Without libm
, a manual implementation is
used.
[dependencies]
# `no_std`
dithereens = { version = "0.3", default-features = false }
[dependencies]
# Optional: uses `libm`'s `round()` function instead of a manual
# implementation for `no_std`.
dithereens = {
version = "0.3",
default-features = false,
features = ["libm"]
}
f16
SupportEnable the nightly_f16
feature to use native f16
types (requires nightly
Rust):
[dependencies]
dithereens = { version = "0.3", features = ["nightly_f16"] }
Enable the blue_noise
feature for high-quality blue noise dithering:
[dependencies]
dithereens = { version = "0.3", features = ["blue_noise"] }
This adds the BlueNoise
struct which provides true blue noise dithering
using a precomputed 256×256×4 table. Note: This increases binary size by
~5MB.
#[cfg(feature = "blue_noise")]
use dithereens::{BlueNoise, simple_dither_slice_2d};
let width = 256;
let mut pixels: Vec<f32> = vec![0.5; width * width];
let blue_noise = BlueNoise::new(42);
simple_dither_slice_2d(&mut pixels, width, 255.0, &blue_noise);
Apache-2.0 OR BSD-3-Clause OR MIT OR Zlib at your discretion.