Skip to content

Instantly share code, notes, and snippets.

@randrews
Last active June 15, 2025 04:58
Show Gist options
  • Save randrews/6fedcf0968e40b87aec7bba9ce09aca1 to your computer and use it in GitHub Desktop.
Save randrews/6fedcf0968e40b87aec7bba9ce09aca1 to your computer and use it in GitHub Desktop.
Floyd-Steinberg dithering in Rust
[package]
name = "dither"
version = "0.1.0"
edition = "2024"
[dependencies]
image = "0.25.6"
fn main() {
let input = image::load_from_memory(include_bytes!("lena.png")).unwrap();
let (width, height) = (input.width(), input.height());
let pixels = input.into_luma16();
let mut out_image = image::GrayImage::new(width, height);
let mut curr_row_errors = vec![0.0; width as usize];
let mut next_row_errors = vec![0.0; width as usize];
for (x, y, p) in pixels.enumerate_pixels(){
let xu = x as usize;
// We're at the start of a new row, so we need to swap the error buffers around:
if x == 0 && y > 0 {
(curr_row_errors, next_row_errors) = (next_row_errors, curr_row_errors);
next_row_errors.fill(0.0);
}
let val = (p.0[0] as f32 / 65535.0) + curr_row_errors[xu];
// Get the 16-bit value for calculating everything else:
let new_val = if val < 0.5 {
0.0
} else {
1.0
};
// Write the 8-bit value to output
out_image.put_pixel(x, y, [new_val as u8 * 255].into());
let error = val - new_val;
if x < width - 1 {
curr_row_errors[xu + 1] += error * (7.0 / 16.0);
next_row_errors[xu + 1] += error * (1.0 / 16.0);
}
if x > 0 {
next_row_errors[xu - 1] += error * (3.0 / 16.0);
}
next_row_errors[xu] += error * (5.0 / 16.0);
}
out_image.save_with_format("lena-dithered.png", image::ImageFormat::Png).unwrap()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment