pbkx/dithr

Buffer-first rust dithering and halftoning library.

Rust

54

171 commits

updated Apr 16, 2026

See the code

README

dithr

crates.io

OriginalDithered
Before ditheringAfter dithering

Before (left) and after (right) using yliluoma_2_in_place.

Buffer-first rust dithering and halftoning library.

Quantizing grayscale/RGB/RGBA buffers without dithering creates visible banding and contouring. dithr provides deterministic ordered dithering, diffusion, stochastic binary methods, palette-constrained workflows, and advanced halftoning methods over typed mutable slices.

Overview

  • Buffer-first API: Works directly on mutable pixel slices with explicit width, height, and stride.
  • Typed formats: Supports u8, u16, and f32 sample types across Gray, Rgb, and Rgba layouts.
  • Quantization control: Uses QuantizeMode for grayscale levels, RGB levels, palette mapping, or single-color workflows.
  • Broad algorithm coverage: Includes stochastic, ordered, palette-oriented ordered, diffusion, variable diffusion, and advanced halftoning families.
  • Palette workflow: Includes Palette<S> and IndexedImage<S> for constrained output and indexed results.
  • Optional integrations: image adapters for DynamicImage workflows and rayon parallel wrappers for selected families.

Installation

cargo add dithr
[dependencies]
dithr = "0.3.0"
cargo add dithr --features image
cargo add dithr --features rayon

Quick Start

use dithr::{gray_u8, QuantizeMode, Result};
use dithr::ordered::bayer_8x8_in_place;

fn main() -> Result<()> {
    let width = 64_usize;
    let height = 64_usize;
    let mut data = Vec::with_capacity(width * height);

    for y in 0..height {
        for x in 0..width {
            let value = ((x + y * width) * 255 / (width * height - 1)) as u8;
            data.push(value);
        }
    }

    let mut buffer = gray_u8(&mut data, width, height, width)?;
    bayer_8x8_in_place(&mut buffer, QuantizeMode::gray_bits(1)?)?;

    assert!(data.iter().all(|&v| v == 0 || v == 255));
    Ok(())
}

Core Data Model

dithr is organized around a small set of types that are shared across algorithm families.

  • Buffer<'a, S, L>: Mutable typed view of image data (S = sample type, L = layout).
  • BufferKind / PixelFormat: Runtime format metadata (PixelFormat is an alias of BufferKind).
  • Palette<S>: Palette storage for fixed-color workflows (1 to 256 entries).
  • IndexedImage<S>: Indexed output (Vec<u8> indices) paired with a typed palette.
  • QuantizeMode<'a, S>: Common quantization target model used by ordered/diffusion/stochastic/dot/Riemersma families.
  • Error / Result<T>: Crate-level error and result surface.

Typed Buffers, Sample Types, and Layouts

Supported runtime kinds:

  • Gray8, Rgb8, Rgba8
  • Gray16, Rgb16, Rgba16
  • Gray32F, Rgb32F, Rgba32F

Typed buffer aliases:

  • GrayBuffer8, RgbBuffer8, RgbaBuffer8
  • GrayBuffer16, RgbBuffer16, RgbaBuffer16
  • GrayBuffer32F, RgbBuffer32F, RgbaBuffer32F

Constructor helpers:

  • Gray: gray_u8, gray_u16, gray_32f
  • Rgb: rgb_u8, rgb_u16, rgb_32f
  • Rgba: rgba_u8, rgba_u16, rgba_32f
  • Packed variants: gray_u8_packed, rgb_u16_packed, rgba_32f_packed, etc.

Generic constructors remain available on Buffer:

  • Buffer::new_typed(...)
  • Buffer::new_packed_typed(...)
  • Compatibility constructors with runtime kind checking:
    • Buffer::new(...)
    • Buffer::new_packed(...)

Choosing a Quantization Mode

QuantizeMode<'a, S> is the canonical quantization model:

  • GrayLevels(u16)
  • RgbLevels(u16)
  • Palette(&Palette<S>)
  • SingleColor { fg: [S; 3], levels: u16 }

Convenience constructors:

  • Generic:
    • QuantizeMode::gray_levels(levels)
    • QuantizeMode::rgb_levels(levels)
    • QuantizeMode::palette(&palette)
    • QuantizeMode::single_color(fg, levels)
  • u8 compatibility helpers:
    • QuantizeMode::gray_bits(bits)
    • QuantizeMode::rgb_bits(bits)
  • Shared conversion helper:
    • levels_from_bits(bits)

Use GrayLevels/gray_bits when output should be grayscale levels, RgbLevels/rgb_bits for uniform per-channel color quantization, Palette for strict membership in a fixed color set, and SingleColor for foreground-scaled tonal output.

Dithering and Halftoning Method Families

Binary stochastic

Fast binary dithering with fixed or randomized threshold behavior.

  • threshold_binary_in_place
  • random_binary_in_place

Parallel variants (rayon feature):

  • threshold_binary_in_place_par
  • random_binary_in_place_par

Ordered methods

Deterministic threshold-map methods with predictable structure and straightforward benchmarking.

Bayer:

  • bayer_2x2_in_place
  • bayer_4x4_in_place
  • bayer_8x8_in_place
  • bayer_16x16_in_place

Cluster-dot:

  • cluster_dot_4x4_in_place
  • cluster_dot_8x8_in_place
  • void_and_cluster_in_place

Custom map:

  • custom_ordered_in_place
  • adaptive_ordered_dither_in_place
  • space_filling_curve_ordered_dither_in_place
  • ranked_dither_in_place
  • image_based_dither_screen_in_place
  • polyomino_ordered_dither_in_place
  • stochastic_clustered_dot_in_place
  • am_fm_hybrid_halftoning_in_place
  • clustered_am_fm_halftoning_in_place
  • blue_noise_multitone_dither_in_place

Parallel variants (rayon feature):

  • bayer_2x2_in_place_par
  • bayer_4x4_in_place_par
  • bayer_8x8_in_place_par
  • bayer_16x16_in_place_par
  • cluster_dot_4x4_in_place_par
  • cluster_dot_8x8_in_place_par
  • custom_ordered_in_place_par

Palette-oriented ordered (Yliluoma)

Ordered methods designed for fixed-palette workflows.

  • yliluoma_1_in_place
  • yliluoma_2_in_place
  • yliluoma_3_in_place

Classic diffusion

Scanline error diffusion kernels for higher local tonal quality.

  • floyd_steinberg_in_place
  • false_floyd_steinberg_in_place
  • jarvis_judice_ninke_in_place
  • stucki_in_place
  • burkes_in_place
  • sierra_in_place
  • two_row_sierra_in_place
  • sierra_lite_in_place
  • stevenson_arce_in_place
  • atkinson_in_place

Extended diffusion

Additional diffusion kernels with different spread patterns.

  • fan_in_place
  • shiau_fan_in_place
  • shiau_fan_2_in_place
  • block_error_diffusion_in_place

Scope note: block_error_diffusion_in_place is grayscale-only.

Variable diffusion

Tone-dependent coefficient families.

  • ostromoukhov_in_place
  • zhou_fang_in_place
  • hvs_optimized_error_diffusion_in_place
  • gradient_based_error_diffusion_in_place
  • multiscale_error_diffusion_in_place
  • feature_preserving_msed_in_place
  • green_noise_msed_in_place
  • linear_pixel_shuffling_in_place
  • tone_dependent_error_diffusion_in_place
  • structure_aware_error_diffusion_in_place
  • adaptive_vector_error_diffusion_in_place
  • vector_error_diffusion_in_place
  • semivector_error_diffusion_in_place
  • hierarchical_error_diffusion_in_place
  • mbvq_color_error_diffusion_in_place
  • neugebauer_color_error_diffusion_in_place
  • multichannel_green_noise_error_diffusion_in_place

Scope note: variable diffusion methods are grayscale-only except adaptive_vector_error_diffusion_in_place, vector_error_diffusion_in_place, semivector_error_diffusion_in_place, and hierarchical_error_diffusion_in_place, mbvq_color_error_diffusion_in_place, and neugebauer_color_error_diffusion_in_place, and multichannel_green_noise_error_diffusion_in_place, which support Rgb/Rgba with alpha preservation on Rgba.

Advanced halftoning

Specialized methods with narrower scope than the ordered/diffusion baseline.

  • riemersma_in_place
  • knuth_dot_diffusion_in_place
  • optimized_dot_diffusion_in_place
  • direct_binary_search_in_place
  • clustered_dot_direct_multibit_search_in_place
  • direct_pattern_control_in_place
  • hierarchical_colorant_dbs_in_place
  • lattice_boltzmann_in_place
  • electrostatic_halftoning_in_place
  • model_based_med_in_place
  • least_squares_model_based_in_place

Scope notes:

  • direct_binary_search_in_place, clustered_dot_direct_multibit_search_in_place, lattice_boltzmann_in_place, electrostatic_halftoning_in_place, model_based_med_in_place, and least_squares_model_based_in_place require integer grayscale buffers.
  • direct_pattern_control_in_place supports integer Rgb/Rgba buffers; alpha is preserved for Rgba.
  • hierarchical_colorant_dbs_in_place supports integer Rgb buffers.
  • riemersma_in_place, knuth_dot_diffusion_in_place, and optimized_dot_diffusion_in_place support Gray/Rgb/Rgba layouts, with alpha preserved for Rgba paths.

Palette Workflow

dithr keeps palette workflows explicit: define a palette, dither into it, and optionally build indexed output.

use dithr::{rgb_u8, IndexedImage, Palette, Result};
use dithr::ordered::yliluoma_1_in_place;

fn main() -> Result<()> {
    let width = 32_usize;
    let height = 32_usize;
    let mut data = vec![0_u8; width * height * 3];

    for y in 0..height {
        for x in 0..width {
            let i = (y * width + x) * 3;
            data[i] = (x * 255 / (width - 1)) as u8;
            data[i + 1] = (y * 255 / (height - 1)) as u8;
            data[i + 2] = ((x + y) * 255 / (width + height - 2)) as u8;
        }
    }

    let palette = Palette::new(vec![
        [0, 0, 0],
        [255, 255, 255],
        [255, 0, 0],
        [0, 0, 255],
    ])?;

    let mut buffer = rgb_u8(&mut data, width, height, width * 3)?;
    yliluoma_1_in_place(&mut buffer, &palette)?;

    let mut indices = Vec::with_capacity(width * height);
    for px in data.chunks_exact(3) {
        indices.push(palette.nearest_rgb_index([px[0], px[1], px[2]]) as u8);
    }

    let indexed = IndexedImage::new(indices, width, height, palette)?;
    assert_eq!(indexed.len(), width * height);

    Ok(())
}

Built-in palette helpers are also exported:

  • cga_palette()
  • grayscale_2()
  • grayscale_4()
  • grayscale_16()

Optional image Integration

Enable image to adapt image crate buffers into dithr buffers.

Typed adapters:

  • gray8_image_as_buffer
  • rgb8_image_as_buffer
  • rgba8_image_as_buffer
  • gray16_image_as_buffer
  • rgb16_image_as_buffer
  • rgba16_image_as_buffer
  • rgb32f_image_as_buffer
  • rgba32f_image_as_buffer

Dynamic adapter:

  • dynamic_image_as_buffer(&mut image::DynamicImage) -> Result<DynamicImageBuffer<'_>>

Dynamic variants:

  • DynamicImageBuffer::Gray8
  • DynamicImageBuffer::Rgb8
  • DynamicImageBuffer::Rgba8
  • DynamicImageBuffer::Gray16
  • DynamicImageBuffer::Rgb16
  • DynamicImageBuffer::Rgba16
  • DynamicImageBuffer::Rgb32F
  • DynamicImageBuffer::Rgba32F

DynamicImage::ImageLumaA8 and DynamicImage::ImageLumaA16 are promoted to DynamicImageBuffer::Rgba8 and DynamicImageBuffer::Rgba16 during adaptation.

Current manifest configuration enables PNG and JPEG codecs for the optional image dependency.

Optional rayon Integration

Enable rayon for parallel wrappers where available.

Parallelized families:

  • Ordered: all *_in_place_par ordered wrappers
  • Yliluoma: yliluoma_1_in_place_par, yliluoma_2_in_place_par, yliluoma_3_in_place_par
  • Binary stochastic: threshold_binary_in_place_par, random_binary_in_place_par

Current serial-only families:

  • Diffusion (classic/extended/variable)
  • Advanced halftoning

Example Programs

Raw buffer workflows:

cargo run --example gray_buffer
cargo run --example rgb_buffer
cargo run --example indexed_palette

Image workflows (image feature):

cargo run --example image_bayer_png --features image -- input.png output.png
cargo run --example image_palette_png --features image -- input.png output.png

Benchmarks and Development

Bench families (criterion):

  • stochastic
  • ordered
  • yliluoma
  • diffusion
  • advanced
cargo bench --no-run
cargo bench --bench stochastic
cargo bench --bench ordered
cargo bench --bench yliluoma
cargo bench --bench diffusion
cargo bench --bench advanced

Development checks:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo check --all-features
cargo test --workspace --all-features --lib --tests --examples
cargo test --doc --all-features

Limitations and Scope

  • Core processing is in-memory and buffer-first; it does not provide general image editing workflows.
  • Not every algorithm supports every format/layout/sample combination.
  • am_fm_hybrid_halftoning_in_place and clustered_am_fm_halftoning_in_place are grayscale-only.
  • blue_noise_multitone_dither_in_place is grayscale-only.
  • Variable diffusion methods are grayscale-only except adaptive_vector_error_diffusion_in_place, vector_error_diffusion_in_place, semivector_error_diffusion_in_place, mbvq_color_error_diffusion_in_place, and neugebauer_color_error_diffusion_in_place, and multichannel_green_noise_error_diffusion_in_place for Rgb/Rgba.
  • block_error_diffusion_in_place is grayscale-only.
  • direct_binary_search_in_place, clustered_dot_direct_multibit_search_in_place, lattice_boltzmann_in_place, and electrostatic_halftoning_in_place are integer grayscale-only.
  • Parallel wrappers are currently provided for ordered, Yliluoma, and binary stochastic families.

References

License

MIT. See LICENSE.

dithering
graphics
halftoning
image
quantization

Contributors

pbkx

171 commits

pbkx/dithr

Buffer-first rust dithering and halftoning library.

Rust

54

171 commits

updated Apr 16, 2026

See the code

README

dithr

crates.io

OriginalDithered
Before ditheringAfter dithering

Before (left) and after (right) using yliluoma_2_in_place.

Buffer-first rust dithering and halftoning library.

Quantizing grayscale/RGB/RGBA buffers without dithering creates visible banding and contouring. dithr provides deterministic ordered dithering, diffusion, stochastic binary methods, palette-constrained workflows, and advanced halftoning methods over typed mutable slices.

Overview

  • Buffer-first API: Works directly on mutable pixel slices with explicit width, height, and stride.
  • Typed formats: Supports u8, u16, and f32 sample types across Gray, Rgb, and Rgba layouts.
  • Quantization control: Uses QuantizeMode for grayscale levels, RGB levels, palette mapping, or single-color workflows.
  • Broad algorithm coverage: Includes stochastic, ordered, palette-oriented ordered, diffusion, variable diffusion, and advanced halftoning families.
  • Palette workflow: Includes Palette<S> and IndexedImage<S> for constrained output and indexed results.
  • Optional integrations: image adapters for DynamicImage workflows and rayon parallel wrappers for selected families.

Installation

cargo add dithr
[dependencies]
dithr = "0.3.0"
cargo add dithr --features image
cargo add dithr --features rayon

Quick Start

use dithr::{gray_u8, QuantizeMode, Result};
use dithr::ordered::bayer_8x8_in_place;

fn main() -> Result<()> {
    let width = 64_usize;
    let height = 64_usize;
    let mut data = Vec::with_capacity(width * height);

    for y in 0..height {
        for x in 0..width {
            let value = ((x + y * width) * 255 / (width * height - 1)) as u8;
            data.push(value);
        }
    }

    let mut buffer = gray_u8(&mut data, width, height, width)?;
    bayer_8x8_in_place(&mut buffer, QuantizeMode::gray_bits(1)?)?;

    assert!(data.iter().all(|&v| v == 0 || v == 255));
    Ok(())
}

Core Data Model

dithr is organized around a small set of types that are shared across algorithm families.

  • Buffer<'a, S, L>: Mutable typed view of image data (S = sample type, L = layout).
  • BufferKind / PixelFormat: Runtime format metadata (PixelFormat is an alias of BufferKind).
  • Palette<S>: Palette storage for fixed-color workflows (1 to 256 entries).
  • IndexedImage<S>: Indexed output (Vec<u8> indices) paired with a typed palette.
  • QuantizeMode<'a, S>: Common quantization target model used by ordered/diffusion/stochastic/dot/Riemersma families.
  • Error / Result<T>: Crate-level error and result surface.

Typed Buffers, Sample Types, and Layouts

Supported runtime kinds:

  • Gray8, Rgb8, Rgba8
  • Gray16, Rgb16, Rgba16
  • Gray32F, Rgb32F, Rgba32F

Typed buffer aliases:

  • GrayBuffer8, RgbBuffer8, RgbaBuffer8
  • GrayBuffer16, RgbBuffer16, RgbaBuffer16
  • GrayBuffer32F, RgbBuffer32F, RgbaBuffer32F

Constructor helpers:

  • Gray: gray_u8, gray_u16, gray_32f
  • Rgb: rgb_u8, rgb_u16, rgb_32f
  • Rgba: rgba_u8, rgba_u16, rgba_32f
  • Packed variants: gray_u8_packed, rgb_u16_packed, rgba_32f_packed, etc.

Generic constructors remain available on Buffer:

  • Buffer::new_typed(...)
  • Buffer::new_packed_typed(...)
  • Compatibility constructors with runtime kind checking:
    • Buffer::new(...)
    • Buffer::new_packed(...)

Choosing a Quantization Mode

QuantizeMode<'a, S> is the canonical quantization model:

  • GrayLevels(u16)
  • RgbLevels(u16)
  • Palette(&Palette<S>)
  • SingleColor { fg: [S; 3], levels: u16 }

Convenience constructors:

  • Generic:
    • QuantizeMode::gray_levels(levels)
    • QuantizeMode::rgb_levels(levels)
    • QuantizeMode::palette(&palette)
    • QuantizeMode::single_color(fg, levels)
  • u8 compatibility helpers:
    • QuantizeMode::gray_bits(bits)
    • QuantizeMode::rgb_bits(bits)
  • Shared conversion helper:
    • levels_from_bits(bits)

Use GrayLevels/gray_bits when output should be grayscale levels, RgbLevels/rgb_bits for uniform per-channel color quantization, Palette for strict membership in a fixed color set, and SingleColor for foreground-scaled tonal output.

Dithering and Halftoning Method Families

Binary stochastic

Fast binary dithering with fixed or randomized threshold behavior.

  • threshold_binary_in_place
  • random_binary_in_place

Parallel variants (rayon feature):

  • threshold_binary_in_place_par
  • random_binary_in_place_par

Ordered methods

Deterministic threshold-map methods with predictable structure and straightforward benchmarking.

Bayer:

  • bayer_2x2_in_place
  • bayer_4x4_in_place
  • bayer_8x8_in_place
  • bayer_16x16_in_place

Cluster-dot:

  • cluster_dot_4x4_in_place
  • cluster_dot_8x8_in_place
  • void_and_cluster_in_place

Custom map:

  • custom_ordered_in_place
  • adaptive_ordered_dither_in_place
  • space_filling_curve_ordered_dither_in_place
  • ranked_dither_in_place
  • image_based_dither_screen_in_place
  • polyomino_ordered_dither_in_place
  • stochastic_clustered_dot_in_place
  • am_fm_hybrid_halftoning_in_place
  • clustered_am_fm_halftoning_in_place
  • blue_noise_multitone_dither_in_place

Parallel variants (rayon feature):

  • bayer_2x2_in_place_par
  • bayer_4x4_in_place_par
  • bayer_8x8_in_place_par
  • bayer_16x16_in_place_par
  • cluster_dot_4x4_in_place_par
  • cluster_dot_8x8_in_place_par
  • custom_ordered_in_place_par

Palette-oriented ordered (Yliluoma)

Ordered methods designed for fixed-palette workflows.

  • yliluoma_1_in_place
  • yliluoma_2_in_place
  • yliluoma_3_in_place

Classic diffusion

Scanline error diffusion kernels for higher local tonal quality.

  • floyd_steinberg_in_place
  • false_floyd_steinberg_in_place
  • jarvis_judice_ninke_in_place
  • stucki_in_place
  • burkes_in_place
  • sierra_in_place
  • two_row_sierra_in_place
  • sierra_lite_in_place
  • stevenson_arce_in_place
  • atkinson_in_place

Extended diffusion

Additional diffusion kernels with different spread patterns.

  • fan_in_place
  • shiau_fan_in_place
  • shiau_fan_2_in_place
  • block_error_diffusion_in_place

Scope note: block_error_diffusion_in_place is grayscale-only.

Variable diffusion

Tone-dependent coefficient families.

  • ostromoukhov_in_place
  • zhou_fang_in_place
  • hvs_optimized_error_diffusion_in_place
  • gradient_based_error_diffusion_in_place
  • multiscale_error_diffusion_in_place
  • feature_preserving_msed_in_place
  • green_noise_msed_in_place
  • linear_pixel_shuffling_in_place
  • tone_dependent_error_diffusion_in_place
  • structure_aware_error_diffusion_in_place
  • adaptive_vector_error_diffusion_in_place
  • vector_error_diffusion_in_place
  • semivector_error_diffusion_in_place
  • hierarchical_error_diffusion_in_place
  • mbvq_color_error_diffusion_in_place
  • neugebauer_color_error_diffusion_in_place
  • multichannel_green_noise_error_diffusion_in_place

Scope note: variable diffusion methods are grayscale-only except adaptive_vector_error_diffusion_in_place, vector_error_diffusion_in_place, semivector_error_diffusion_in_place, and hierarchical_error_diffusion_in_place, mbvq_color_error_diffusion_in_place, and neugebauer_color_error_diffusion_in_place, and multichannel_green_noise_error_diffusion_in_place, which support Rgb/Rgba with alpha preservation on Rgba.

Advanced halftoning

Specialized methods with narrower scope than the ordered/diffusion baseline.

  • riemersma_in_place
  • knuth_dot_diffusion_in_place
  • optimized_dot_diffusion_in_place
  • direct_binary_search_in_place
  • clustered_dot_direct_multibit_search_in_place
  • direct_pattern_control_in_place
  • hierarchical_colorant_dbs_in_place
  • lattice_boltzmann_in_place
  • electrostatic_halftoning_in_place
  • model_based_med_in_place
  • least_squares_model_based_in_place

Scope notes:

  • direct_binary_search_in_place, clustered_dot_direct_multibit_search_in_place, lattice_boltzmann_in_place, electrostatic_halftoning_in_place, model_based_med_in_place, and least_squares_model_based_in_place require integer grayscale buffers.
  • direct_pattern_control_in_place supports integer Rgb/Rgba buffers; alpha is preserved for Rgba.
  • hierarchical_colorant_dbs_in_place supports integer Rgb buffers.
  • riemersma_in_place, knuth_dot_diffusion_in_place, and optimized_dot_diffusion_in_place support Gray/Rgb/Rgba layouts, with alpha preserved for Rgba paths.

Palette Workflow

dithr keeps palette workflows explicit: define a palette, dither into it, and optionally build indexed output.

use dithr::{rgb_u8, IndexedImage, Palette, Result};
use dithr::ordered::yliluoma_1_in_place;

fn main() -> Result<()> {
    let width = 32_usize;
    let height = 32_usize;
    let mut data = vec![0_u8; width * height * 3];

    for y in 0..height {
        for x in 0..width {
            let i = (y * width + x) * 3;
            data[i] = (x * 255 / (width - 1)) as u8;
            data[i + 1] = (y * 255 / (height - 1)) as u8;
            data[i + 2] = ((x + y) * 255 / (width + height - 2)) as u8;
        }
    }

    let palette = Palette::new(vec![
        [0, 0, 0],
        [255, 255, 255],
        [255, 0, 0],
        [0, 0, 255],
    ])?;

    let mut buffer = rgb_u8(&mut data, width, height, width * 3)?;
    yliluoma_1_in_place(&mut buffer, &palette)?;

    let mut indices = Vec::with_capacity(width * height);
    for px in data.chunks_exact(3) {
        indices.push(palette.nearest_rgb_index([px[0], px[1], px[2]]) as u8);
    }

    let indexed = IndexedImage::new(indices, width, height, palette)?;
    assert_eq!(indexed.len(), width * height);

    Ok(())
}

Built-in palette helpers are also exported:

  • cga_palette()
  • grayscale_2()
  • grayscale_4()
  • grayscale_16()

Optional image Integration

Enable image to adapt image crate buffers into dithr buffers.

Typed adapters:

  • gray8_image_as_buffer
  • rgb8_image_as_buffer
  • rgba8_image_as_buffer
  • gray16_image_as_buffer
  • rgb16_image_as_buffer
  • rgba16_image_as_buffer
  • rgb32f_image_as_buffer
  • rgba32f_image_as_buffer

Dynamic adapter:

  • dynamic_image_as_buffer(&mut image::DynamicImage) -> Result<DynamicImageBuffer<'_>>

Dynamic variants:

  • DynamicImageBuffer::Gray8
  • DynamicImageBuffer::Rgb8
  • DynamicImageBuffer::Rgba8
  • DynamicImageBuffer::Gray16
  • DynamicImageBuffer::Rgb16
  • DynamicImageBuffer::Rgba16
  • DynamicImageBuffer::Rgb32F
  • DynamicImageBuffer::Rgba32F

DynamicImage::ImageLumaA8 and DynamicImage::ImageLumaA16 are promoted to DynamicImageBuffer::Rgba8 and DynamicImageBuffer::Rgba16 during adaptation.

Current manifest configuration enables PNG and JPEG codecs for the optional image dependency.

Optional rayon Integration

Enable rayon for parallel wrappers where available.

Parallelized families:

  • Ordered: all *_in_place_par ordered wrappers
  • Yliluoma: yliluoma_1_in_place_par, yliluoma_2_in_place_par, yliluoma_3_in_place_par
  • Binary stochastic: threshold_binary_in_place_par, random_binary_in_place_par

Current serial-only families:

  • Diffusion (classic/extended/variable)
  • Advanced halftoning

Example Programs

Raw buffer workflows:

cargo run --example gray_buffer
cargo run --example rgb_buffer
cargo run --example indexed_palette

Image workflows (image feature):

cargo run --example image_bayer_png --features image -- input.png output.png
cargo run --example image_palette_png --features image -- input.png output.png

Benchmarks and Development

Bench families (criterion):

  • stochastic
  • ordered
  • yliluoma
  • diffusion
  • advanced
cargo bench --no-run
cargo bench --bench stochastic
cargo bench --bench ordered
cargo bench --bench yliluoma
cargo bench --bench diffusion
cargo bench --bench advanced

Development checks:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo check --all-features
cargo test --workspace --all-features --lib --tests --examples
cargo test --doc --all-features

Limitations and Scope

  • Core processing is in-memory and buffer-first; it does not provide general image editing workflows.
  • Not every algorithm supports every format/layout/sample combination.
  • am_fm_hybrid_halftoning_in_place and clustered_am_fm_halftoning_in_place are grayscale-only.
  • blue_noise_multitone_dither_in_place is grayscale-only.
  • Variable diffusion methods are grayscale-only except adaptive_vector_error_diffusion_in_place, vector_error_diffusion_in_place, semivector_error_diffusion_in_place, mbvq_color_error_diffusion_in_place, and neugebauer_color_error_diffusion_in_place, and multichannel_green_noise_error_diffusion_in_place for Rgb/Rgba.
  • block_error_diffusion_in_place is grayscale-only.
  • direct_binary_search_in_place, clustered_dot_direct_multibit_search_in_place, lattice_boltzmann_in_place, and electrostatic_halftoning_in_place are integer grayscale-only.
  • Parallel wrappers are currently provided for ordered, Yliluoma, and binary stochastic families.

References

License

MIT. See LICENSE.

dithering
graphics
halftoning
image
quantization

Contributors

pbkx

171 commits

Languages

Rust

100.0%