diff options
| author | ache <ache@ache.one> | 2025-04-03 02:18:38 +0200 |
|---|---|---|
| committer | ache <ache@ache.one> | 2025-04-03 02:18:38 +0200 |
| commit | 1abfb4faffd7f4e43166cbadbccbafbd570630f3 (patch) | |
| tree | 93963510e620dcde7530dc12bfbacb51d26fecfd | |
Init commit
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | david.png | bin | 0 -> 27218 bytes | |||
| -rw-r--r-- | src/main.rs | 80 |
3 files changed, 87 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cb0a9de --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dither" +version = "0.1.0" +edition = "2024" + +[dependencies] +image = "0.25.6" diff --git a/david.png b/david.png Binary files differnew file mode 100644 index 0000000..6cfa884 --- /dev/null +++ b/david.png diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..46e2063 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,80 @@ +// Implement a simple dithering algorithm +use image::{DynamicImage, ImageReader, Rgb, RgbImage}; +use std::env; +use std::io::Cursor; + +fn dither_atkinson(image: &mut RgbImage) { + let (width, height) = image.dimensions(); + let mut error: Vec<f32> = vec![0.; (3 * width) as usize]; + + for y in 0..height { + for x in 0..width { + let Rgb([r, g, b]) = image.get_pixel(x, y); + let pixel = (*r as u32 + *g as u32 + *b as u32) as f32 / 3.; + let err_pos: u32 = (y * width + x) % (3 * width); + + let (pixel, err) = if (pixel + error[err_pos as usize]) > 127. { + (255, (pixel + error[err_pos as usize] - 255.) / 8.) + } else { + (0, (pixel + error[err_pos as usize]) / 8.) + }; + + error[err_pos as usize] = 0.; + error[((err_pos + 1) % (3 * width)) as usize] += err; + error[((err_pos + 2) % (3 * width)) as usize] += err; + error[((err_pos + width + 1) % (3 * width)) as usize] += err; + error[((err_pos + width - 1) % (3 * width)) as usize] += err; + error[((err_pos + width) % (3 * width)) as usize] += err; + error[((err_pos + 2 * width) % (3 * width)) as usize] += err; + + image.put_pixel(x, y, image::Rgb([pixel, pixel, pixel])); + } + } +} + +fn dither_atkinson_1_level(image: &mut RgbImage) { + let (width, height) = image.dimensions(); + let mut error: Vec<f32> = vec![0.; (3 * width) as usize]; + + for y in 0..height { + for x in 0..width { + let Rgb([r, g, b]) = image.get_pixel(x, y); + let pixel = (*r as u32 + *g as u32 + *b as u32) as f32 / 3.; + let err_pos: u32 = (y * width + x) % (3 * width); + + let (pixel, err) = if (pixel + error[err_pos as usize]) > 127. { + (255, (pixel - 255.) / 8.) + } else { + (0, pixel / 8.) + }; + + error[err_pos as usize] = 0.; + error[((err_pos + 1) % (3 * width)) as usize] += err; + error[((err_pos + 2) % (3 * width)) as usize] += err; + error[((err_pos + width + 1) % (3 * width)) as usize] += err; + error[((err_pos + width - 1) % (3 * width)) as usize] += err; + error[((err_pos + width) % (3 * width)) as usize] += err; + error[((err_pos + 2 * width) % (3 * width)) as usize] += err; + + image.put_pixel(x, y, image::Rgb([pixel, pixel, pixel])); + } + } +} + +fn main() -> Result<(), image::ImageError> { + let args: Vec<String> = env::args().collect(); + + let image_path = format!("{}.jpg", &args[1]); + let image_out = format!("{}_dithered.jpg", &args[1]); + let image_out_1l = format!("{}_dithered_1l.jpg", &args[1]); + + let mut img = ImageReader::open(&image_path)?.decode()?.into_rgb8(); + dither_atkinson(&mut img); + img.save(image_out)?; + + let mut img = ImageReader::open(&image_path)?.decode()?.into_rgb8(); + dither_atkinson_1_level(&mut img); + img.save(image_out_1l)?; + + Ok(()) +} |