Diffstat (limited to 'src/diffusion.rs')
| -rw-r--r-- | src/diffusion.rs | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/diffusion.rs b/src/diffusion.rs new file mode 100644 index 0000000..ea58adc --- /dev/null +++ b/src/diffusion.rs @@ -0,0 +1,112 @@ +//! # Error diffusion dithering. +//! The way this works is by finding the amount of error between the quantized color and the original color, and offseting the error to the (next) neighboring pixels. +//! Which neighboring pixels depend on the algorithm chosen. +use super::*; +mod riemerasma; +pub mod sierra; +pub use riemerasma::*; +pub fn atkinson(image: Image<&[f32], 4>, palette: &[[f32; 4]]) -> Image<Box<[f32]>, 4> { + let kd = map(palette); + let mut image = + Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice()); + let w = image.width(); + let h = image.height(); + let eighth = [1. / 8.; 4]; + for (x, y) in (0..h).flat_map(move |y| (0..w).map(move |x| (x, y))) { + unsafe { + /* + * 1 1 + 1 1 1 + 1 + */ + let p = image.pixel(x, y); + let new = palette[kd.find_nearest(p) as usize]; + *image.pixel_mut(x, y) = new; + let error = p.asub(new); + let f = |x: [f32; 4]| x.aadd(error.amul(eighth)); + image.replace(x + 1, y, f); + image.replace(x + 2, y, f); + + image.replace(x.wrapping_sub(1), y + 1, f); + image.replace(x, y + 1, f); + image.replace(x + 1, y + 1, f); + + image.replace(x, y + 2, f); + } + } + image +} + +pub fn jarvis<const FAC: u8>( + image: Image<&[f32], 4>, + palette: &[[f32; 4]], +) -> Image<Box<[f32]>, 4> { + let kd = map(palette); + let mut image = + Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice()); + let w = image.width(); + let h = image.height(); + for (x, y) in (0..h).flat_map(move |y| (0..w).map(move |x| (x, y))) { + #[rustfmt::skip] + unsafe { + let p = image.pixel(x, y); + let new = palette[kd.find_nearest(p) as usize]; + *image.pixel_mut(x, y) = new; + let error = p.asub(new); + let f = |f| { + move |x: [f32; 4]| { + x.aadd(error.amul([(f as f32 / 48.) * const { FAC as f32 * (1.0 / 255.) }; 4])) + } + }; + /* * 7 5 + 3 5 7 5 3 + 1 3 5 3 1*/ + image.replace(x + 1, y, f(7)); + image.replace(x + 2, y, f(5)); + let y = y + 1; + image.replace(x.wrapping_sub(2), y, f(3)); + image.replace(x.wrapping_sub(1), y, f(5)); + image.replace(x , y, f(7)); + image.replace(x + 1, y, f(5)); + image.replace(x + 2, y, f(3)); + let y = y + 1; + image.replace(x.wrapping_sub(2), y, f(1)); + image.replace(x.wrapping_sub(1), y, f(3)); + image.replace(x , y, f(5)); + image.replace(x + 1, y, f(3)); + image.replace(x + 2, y, f(1)); + } + } + image +} +pub fn floyd_steinberg<const FAC: u8>( + image: Image<&[f32], 4>, + palette: &[[f32; 4]], +) -> Image<Box<[f32]>, 4> { + let kd = map(palette); + let mut image = + Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice()); + let w = image.width(); + let h = image.height(); + for (x, y) in (0..h).flat_map(move |y| (0..w).map(move |x| (x, y))) { + unsafe { + let p = image.pixel(x, y); + let new = palette[kd.find_nearest(p) as usize]; + *image.pixel_mut(x, y) = new; + let error = p.asub(new); + let f = |f| { + move |x: [f32; 4]| { + x.aadd(error.amul([(f as f32 / 48.) * const { FAC as f32 * (1.0 / 255.) }; 4])) + } + }; + /* + * 7 + 3 5 1 */ + image.replace(x + 1, y, f(7)); + image.replace(x.wrapping_sub(1), y + 1, f(3)); + image.replace(x, y + 1, f(5)); + image.replace(x + 1, y + 1, f(1)); + } + } + image +} |