Diffstat (limited to 'src/diffusion/riemerasma.rs')
| -rw-r--r-- | src/diffusion/riemerasma.rs | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/src/diffusion/riemerasma.rs b/src/diffusion/riemerasma.rs new file mode 100644 index 0000000..09b18bc --- /dev/null +++ b/src/diffusion/riemerasma.rs @@ -0,0 +1,164 @@ +//! https://www.compuphase.com/riemer.htm +use std::{collections::VecDeque, mem::MaybeUninit}; + +use super::*; + +#[test] +fn x() { + let mut q = Ring::new([0.0, 2.0, 5.0]); + dbg!(q.iter().collect::<Vec<_>>()); + assert_eq!(q.pop_front_push_back(6.0), 0.0); + dbg!(q.iter().collect::<Vec<_>>()); + assert_eq!(q.pop_front_push_back(3.0), 2.0); + dbg!(q.iter().collect::<Vec<_>>()); + assert_eq!(q.pop_front_push_back(4.0), 5.0); + dbg!(q.iter().collect::<Vec<_>>()); + assert_eq!(q.pop_front_push_back(7.0), 6.0); + dbg!(q.iter().collect::<Vec<_>>()); +} + +pub struct Ring<T, const N: usize> { + arr: [T; N], + front: u8, +} +impl<T, const N: usize> Ring<T, N> { + pub fn new(contents: [T; N]) -> Self { + Ring { + arr: contents, + front: 0, + } + } + pub fn pop_front_push_back(&mut self, push_back: T) -> T { + unsafe { + let e = std::mem::replace(self.arr.get_unchecked_mut(self.front as usize), push_back); + self.front += 1; + self.front %= N as u8; + e + } + } + + pub fn iter(&self) -> impl Iterator<Item = &T> { + self.arr.iter().cycle().skip(self.front as _).take(N as _) + } +} + +pub fn riemerasma(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()); + #[rustfmt::skip] + const WEIGH: [f32; 16] = [0.0625, 0.07518906, 0.09045432, 0.10881881, 0.13091174, 0.15749009, 0.18946451, 0.22793055, 0.27420613, 0.32987684, 0.39685008, 0.47742057, 0.57434887, 0.69095606, 0.8312374, 1.0]; + let mut errors = Ring::<[f32; 4], 16>::new([[0.; 4]; 16]); + let mut level = image.width().max(image.height()).ilog2(); + if (1 << level) < image.width().max(image.height()) { + level += 1; + } + // static mut visualization: Image<[u8; 256 * 256], 1> = fimg::make!(1 channels 256 x 256); + // static mut stage: u8 = 0; + enum Dir { + UP, + DOWN, + LEFT, + RIGHT, + } + use Dir::*; + hl( + level, + UP, + &mut (&mut (0, 0), &mut errors, &kd, palette, &mut image), + ); + fn hl( + level: u32, + dir: Dir, + p: &mut ( + &mut (i32, i32), + &mut Ring<[f32; 4], 16>, + &KD, + &[[f32; 4]], + &mut Image<Box<[f32]>, 4>, + ), + ) { + macro_rules! mv { + ($dir: expr) => {{ + unsafe { + if p.0 .0 >= 0 + && p.0 .0 < p.4.width() as i32 + && p.0 .1 >= 0 + && p.0 .1 < p.4.height() as i32 + { + let error = + p.1.iter() + .zip(WEIGH) + .map(|(&a, b)| a.mul(b)) + .fold([0.; 4], |acc, x| acc.aadd(x)) + .div(WEIGH.len() as f32); + let (x, y) = *p.0; + let (x, y) = (x as u32, y as u32); + // visualization.set_pixel(x, y, [stage]); + // stage += 1; + let px = p.4.pixel(x, y).aadd(error); + let np = p.3[p.2.find_nearest(px) as usize]; + p.1.pop_front_push_back(px.asub(np)); + *p.4.pixel_mut(x, y) = np; + } + match $dir { + LEFT => p.0 .0 -= 1, + RIGHT => p.0 .0 += 1, + UP => p.0 .1 -= 1, + DOWN => p.0 .1 += 1, + } + } + }}; + } + macro_rules! dir { + (^) => { + UP + }; + (>) => { + RIGHT + }; + (<) => { + LEFT + }; + (v) => { + DOWN + }; + } + macro_rules! pattern { + ($($x:tt)+) => {{ + $(mv!(dir!($x));)+ + }}; + } + macro_rules! hilbert { + ($a1:tt $b1:tt $a2:tt $b2:tt $a3:tt $b3:tt $b4:tt) => {{ + hl(level - 1, dir!($a1), p); + mv!(dir!($b1)); + + hl(level - 1, dir!($a2), p); + mv!(dir!($b2)); + + hl(level - 1, dir!($a3), p); + mv!(dir!($b3)); + + hl(level - 1, dir!($b4), p); + }}; + } + if level == 1 { + match dir { + LEFT => pattern!(>v<), + RIGHT => pattern!(<^>), + UP => pattern!(v>^), + DOWN => pattern!(^<v), + } + } else { + match dir { + LEFT => hilbert!(^> <v < < v), + RIGHT => hilbert!(v< >^ > > ^), + UP => hilbert!(<v ^> ^^ >), + DOWN => hilbert!(>^ v< v v <), + } + } + } + // unsafe { visualization.as_ref().show() }; + image +} |