From cf3b2d8f67b853158c37d38ab4a4cadb504ac659 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sun, 30 Jul 2023 21:26:35 +0200 Subject: [PATCH] Add Stucki dithering --- src/dither.rs | 114 +++++++++++++++++++++++++++++++++----------------- src/main.rs | 6 ++- 2 files changed, 79 insertions(+), 41 deletions(-) diff --git a/src/dither.rs b/src/dither.rs index 37fcfcf..c0be645 100644 --- a/src/dither.rs +++ b/src/dither.rs @@ -177,48 +177,29 @@ where } } -pub struct AlgoFloydSteinberg; - -impl AlgoFloydSteinberg { - fn update_pixel(image: &mut RgbaImage, palette: &Palette, x: u32, y: u32) - where - C: Copy, - C: IntoColor, - C: Sub, - D: Difference, - Srgb: IntoColor, - C: Add, - C: Mul, - { - let pixel = image.get_pixel(x, y); - let before: C = util::pixel_to_color(*pixel); - let after = palette.nearest::(before); - let error = after - before; - - util::update_pixel_with_color(image.get_pixel_mut(x, y), after); - Self::diffuse_error(image, x + 1, y, error, 7.0 / 16.0); - if x > 0 { - Self::diffuse_error(image, x - 1, y + 1, error, 3.0 / 16.0); - } - Self::diffuse_error(image, x, y + 1, error, 5.0 / 16.0); - Self::diffuse_error(image, x + 1, y + 1, error, 1.0 / 16.0); +fn diffuse_error(image: &mut RgbaImage, error: C, x: u32, y: u32, dx: i32, dy: i32, factor: f32) +where + C: Add, + C: IntoColor, + C: Mul, + Srgb: IntoColor, +{ + if x == 0 && dx < 0 { + return; } - - fn diffuse_error(image: &mut RgbaImage, x: u32, y: u32, error: C, factor: f32) - where - C: Add, - C: IntoColor, - C: Mul, - Srgb: IntoColor, - { - if let Some(pixel) = image.get_pixel_mut_checked(x, y) { - let color: C = util::pixel_to_color(*pixel); - let color = color + error * factor; - util::update_pixel_with_color(pixel, color); - } + if y == 0 && dy < 0 { + return; } + let x = (x as i32 + dx) as u32; + let y = (y as i32 + dy) as u32; + let Some(pixel) = image.get_pixel_mut_checked(x, y) else{ return; }; + let color: C = util::pixel_to_color(*pixel); + let color = color + error * factor; + util::update_pixel_with_color(pixel, color); } +pub struct AlgoFloydSteinberg; + impl Algorithm for AlgoFloydSteinberg where C: Add, @@ -232,9 +213,64 @@ where fn run(mut image: RgbaImage, palette: Palette) -> RgbaImage { for y in 0..image.height() { for x in 0..image.width() { - Self::update_pixel::(&mut image, &palette, x, y); + let pixel = image.get_pixel(x, y); + let before: C = util::pixel_to_color(*pixel); + let after = palette.nearest::(before); + let error = before - after; + + util::update_pixel_with_color(image.get_pixel_mut(x, y), after); + diffuse_error(&mut image, error, x, y, 1, 0, 7.0 / 16.0); + diffuse_error(&mut image, error, x, y, -1, 1, 3.0 / 16.0); + diffuse_error(&mut image, error, x, y, 0, 1, 5.0 / 16.0); + diffuse_error(&mut image, error, x, y, 1, 1, 1.0 / 16.0); } } + + image + } +} + +pub struct AlgoStucki; + +impl Algorithm for AlgoStucki +where + C: Add, + C: Copy, + C: IntoColor, + C: Mul, + C: Sub, + D: Difference, + Srgb: IntoColor, +{ + fn run(mut image: RgbaImage, palette: Palette) -> RgbaImage { + for y in 0..image.height() { + for x in 0..image.width() { + let pixel = image.get_pixel(x, y); + let before: C = util::pixel_to_color(*pixel); + let after = palette.nearest::(before); + let error = before - after; + + util::update_pixel_with_color(image.get_pixel_mut(x, y), after); + + let base = 42.; + + diffuse_error(&mut image, error, x, y, 1, 0, 8. / base); + diffuse_error(&mut image, error, x, y, 2, 0, 4. / base); + + diffuse_error(&mut image, error, x, y, -2, 1, 2. / base); + diffuse_error(&mut image, error, x, y, -1, 1, 4. / base); + diffuse_error(&mut image, error, x, y, 0, 1, 8. / base); + diffuse_error(&mut image, error, x, y, 1, 1, 4. / base); + diffuse_error(&mut image, error, x, y, 2, 1, 2. / base); + + diffuse_error(&mut image, error, x, y, -2, 2, 1. / base); + diffuse_error(&mut image, error, x, y, -1, 2, 2. / base); + diffuse_error(&mut image, error, x, y, 0, 2, 4. / base); + diffuse_error(&mut image, error, x, y, 1, 2, 2. / base); + diffuse_error(&mut image, error, x, y, 2, 2, 1. / base); + } + } + image } } diff --git a/src/main.rs b/src/main.rs index e9b618b..8868534 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,8 +24,8 @@ use image::{ImageFormat, RgbaImage}; use mark::{ bw, dither::{ - AlgoFloydSteinberg, AlgoRandom, AlgoThreshold, Algorithm, DiffCiede2000, DiffClamp, - DiffEuclid, DiffHyAb, DiffManhattan, DiffManhattanSquare, Difference, Palette, + AlgoFloydSteinberg, AlgoRandom, AlgoStucki, AlgoThreshold, Algorithm, DiffCiede2000, + DiffClamp, DiffEuclid, DiffHyAb, DiffManhattan, DiffManhattanSquare, Difference, Palette, }, }; use palette::{color_difference::EuclideanDistance, Clamp, IntoColor, Lab, LinSrgb, Oklab, Srgb}; @@ -72,6 +72,7 @@ enum DitherAlgorithm { Threshold, Random, FloydSteinberg, + Stucki, } #[derive(Debug, Clone, Copy, clap::ValueEnum)] @@ -205,6 +206,7 @@ impl DitherCmd { Threshold => self.run_acd::(image), Random => self.run_acd::(image), FloydSteinberg => self.run_acd::(image), + Stucki => self.run_acd::(image), } }