snek
| -rw-r--r-- | Cargo.lock | 42 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/diffusion.rs | 12 | ||||
| -rw-r--r-- | src/diffusion/riemerasma.rs | 2 | ||||
| -rw-r--r-- | src/diffusion/sierra.rs | 12 | ||||
| -rw-r--r-- | src/dumb.rs | 4 | ||||
| -rw-r--r-- | src/lib.rs | 45 | ||||
| -rw-r--r-- | src/main.rs | 16 | ||||
| -rw-r--r-- | src/ordered.rs | 37 |
9 files changed, 88 insertions, 83 deletions
@@ -82,13 +82,14 @@ checksum = "6a02dba6a60cd31533cf16561ced53239686d18f1464bff49579dd320fcea081" [[package]] name = "fimg" version = "0.4.43" -source = "git+https://github.com/bend-n/fimg#3e3ca7b2ee24e1a62b8b49d54bf903248371f010" +source = "git+https://github.com/bend-n/fimg#193a7b4ec395a7e4e102da3cbce11bbebb7bdb85" dependencies = [ "atools", "clipline", "fer", + "hinted", "libc", - "mattr", + "mattr 0.0.2", "png", "umath", "vecto", @@ -122,10 +123,16 @@ dependencies = [ ] [[package]] +name = "hinted" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9965bbdc3ee9917c5bc2b59da94906fce9e3693fc7609c8b178f15bf5077ba69" + +[[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "mattr" @@ -134,10 +141,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f63dc7ec862e5d146c89d104d437548fef5216a6a653f4afc4b87c581970677" [[package]] +name = "mattr" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602c380ae9aba3adfad5f01dd23a956f881472b7c9b6b917e57abaa944569de2" + +[[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", "simd-adler32", @@ -145,9 +158,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.15" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67582bd5b65bdff614270e2ea89a1cf15bef71245cc1e5f7ea126977144211d" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags", "crc32fast", @@ -167,18 +180,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -222,6 +235,7 @@ dependencies = [ "exoquant", "fimg", "fux_kdtree", + "mattr 0.0.3", "rand", ] @@ -233,9 +247,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -12,6 +12,7 @@ fimg = { version = "0.4.43", git = "https://github.com/bend-n/fimg", default-fea "scale", ] } fux_kdtree = "0.2.0" +mattr = "0.0.3" rand = "0.8.5" diff --git a/src/diffusion.rs b/src/diffusion.rs index ea58adc..494fa7e 100644 --- a/src/diffusion.rs +++ b/src/diffusion.rs @@ -9,10 +9,8 @@ pub fn atkinson(image: Image<&[f32], 4>, palette: &[[f32; 4]]) -> Image<Box<[f32 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))) { + for (x, y) in image.serpent() { unsafe { /* * 1 1 @@ -44,9 +42,7 @@ pub fn jarvis<const FAC: u8>( 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))) { + for (x, y) in image.serpent() { #[rustfmt::skip] unsafe { let p = image.pixel(x, y); @@ -86,9 +82,7 @@ pub fn floyd_steinberg<const FAC: u8>( 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))) { + for (x, y) in image.serpent() { unsafe { let p = image.pixel(x, y); let new = palette[kd.find_nearest(p) as usize]; diff --git a/src/diffusion/riemerasma.rs b/src/diffusion/riemerasma.rs index 09b18bc..1d640c3 100644 --- a/src/diffusion/riemerasma.rs +++ b/src/diffusion/riemerasma.rs @@ -1,6 +1,4 @@ //! https://www.compuphase.com/riemer.htm -use std::{collections::VecDeque, mem::MaybeUninit}; - use super::*; #[test] diff --git a/src/diffusion/sierra.rs b/src/diffusion/sierra.rs index 4177f1b..c3f3a3a 100644 --- a/src/diffusion/sierra.rs +++ b/src/diffusion/sierra.rs @@ -7,9 +7,7 @@ pub fn sierra<const FAC: u8>( 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))) { + for (x, y) in image.serpent() { #[rustfmt::skip] unsafe { let p = image.pixel(x, y); @@ -48,9 +46,7 @@ pub fn sierra_two<const FAC: u8>( 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))) { + for (x, y) in image.serpent() { #[rustfmt::skip] unsafe { let p = image.pixel(x, y); @@ -84,9 +80,7 @@ pub fn sierra_lite<const FAC: u8>( 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))) { + for (x, y) in image.serpent() { #[rustfmt::skip] unsafe { let p = image.pixel(x, y); diff --git a/src/dumb.rs b/src/dumb.rs index 12608a7..c406fac 100644 --- a/src/dumb.rs +++ b/src/dumb.rs @@ -10,10 +10,10 @@ fn euclidean_distance(f: [f32; 4], with: [f32; 4]) -> f32 { impl Closest for &[[f32; 4]] { fn closest(&self, color: [f32; 4]) -> (f32, [f32; 4], usize) { self.iter() + .copied() .enumerate() - .map(|(i, x)| (euclidean_distance(*x, color), x, i)) + .map(|(i, x)| (euclidean_distance(x, color), x, i)) .min_by(|x, y| x.0.total_cmp(&y.0)) - .map(|(d, x, i)| (d, *x, i)) .unwrap() // let mut best = (euclidean_distance(self[0], color), self[0], 0); // for (&c, i) in self[1..].iter().zip(1..) { @@ -1,6 +1,8 @@ #![allow(incomplete_features, internal_features)] #![feature( + const_fn_floating_point_arithmetic, inline_const_pat, + iter_chain, const_option, adt_const_params, stmt_expr_attributes, @@ -30,53 +32,16 @@ fn map(colors: &[[f32; 4]]) -> KD { KD::new(colors) } -static BAYER_2X2: [f32; 4] = { - let map = [ - 0, 2, // - 3, 1, - ]; - car::map!(map, |x| x as f32 * (1. / 4.) - 0.5 * (3. * (1. / 4.))) -}; -static BAYER_4X4: [f32; 4 * 4] = { - let map = [ - 0, 8, 2, 10, // - 12, 4, 14, 6, // - 3, 11, 1, 9, // - 15, 7, 13, 5, - ]; - car::map!(map, |x| x as f32 * (1. / 16.) - 0.5 * (15. * (1. / 16.))) -}; -static BAYER_8X8: [f32; 8 * 8] = { - let map = [ - 0, 32, 8, 40, 2, 34, 10, 42, // - 48, 16, 56, 24, 50, 18, 58, 26, // - 12, 44, 4, 36, 14, 46, 6, 38, // - 60, 28, 52, 20, 62, 30, 54, 22, // - 3, 35, 11, 43, 1, 33, 9, 41, // - 51, 19, 59, 27, 49, 17, 57, 25, // - 15, 47, 7, 39, 13, 45, 5, 37, // - 63, 31, 55, 23, 61, 29, 53, 21, // - ]; - car::map!(map, |x| x as f32 * (1. / 64.) - 0.5 * (63. * (1. / 64.))) -}; - fn dither( image: Image<&[f32], 4>, f: impl FnMut(((usize, usize), &[f32; 4])) -> [f32; 4], ) -> Image<Box<[f32]>, 4> { Image::build(image.width(), image.height()).buf( image - .rows() - .enumerate() - .flat_map(|(x, p)| p.iter().enumerate().map(move |(y, p)| ((x, y), p))) + .chunked() + .zip(image.ordered()) + .map(|(p, xy)| (xy.array().map(|x| x as usize).tuple(), p)) .flat_map(f) .collect(), ) } - -fn dither_with<const N: usize>( - image: Image<&[f32], 4>, - mut f: impl FnMut(((usize, usize), &[f32; 4])) -> [f32; 4], -) -> Image<Box<[f32]>, 4> { - dither(image, |((x, y), p)| f(((x % N, y % N), p))) -} diff --git a/src/main.rs b/src/main.rs index a7f2ce4..361f33f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![feature(slice_as_chunks, generic_const_exprs)] use atools::prelude::*; use exoquant::SimpleColorSpace; +use remapper::ordered; fn main() { reemap(); @@ -15,6 +16,7 @@ fn main() { } fn reemap() { + // println!("{:?}", ordered::BAYER_8x8Q); // let mut rng = rand::thread_rng(); // let pal = std::iter::repeat_with(|| { // let a: [f32; 3] = std::array::from_fn(|_| rng.next_u64() as f32 / u64::MAX as f32); @@ -40,14 +42,14 @@ fn reemap() { // println!("{pal:?}"); fimg::Image::<Box<[u8]>, 4>::from( - remapper::diffusion::riemerasma( + remapper::ordered::remap_bayer_8x8( fimg::Image::<Box<[f32]>, 4>::from( - fimg::Image::<&[u8], 4>::make::<256, 256>().as_ref(), - // fimg::Image::<Vec<u8>, 4>::open("../fimg/tdata/cat.png") - // .show() - // .scale::<fimg::scale::Nearest>(800, 480) - // .show() - // .as_ref(), + // fimg::Image::<&[u8], 4>::make::<256, 256>().as_ref(), + fimg::Image::<Vec<u8>, 4>::open("../fimg/tdata/cat.png") + // .show() + // .scale::<fimg::scale::Nearest>(800, 480) + // .show() + .as_ref(), ) .as_ref(), &pal, diff --git a/src/ordered.rs b/src/ordered.rs index d36adbc..84b7220 100644 --- a/src/ordered.rs +++ b/src/ordered.rs @@ -1,6 +1,43 @@ //! # Ordered dithering. //! The way this works is by adding a constant texture to the image, and then quantizing that. use super::*; + +const fn threshold<const N: usize>(x: [u32; N]) -> [f32; N] { + car::map!(x, |x| x as f32 * (1. / N as f32) + - 0.5 * ((N - 1) as f32 * (1. / N as f32))) +} + +static BAYER_2X2: [f32; 4] = { + threshold([ + 0, 2, // + 3, 1, + ]) +}; +static BAYER_4X4: [f32; 4 * 4] = { + threshold([ + 0, 8, 2, 10, // + 12, 4, 14, 6, // + 3, 11, 1, 9, // + 15, 7, 13, 5, + ]) +}; + +pub const BAYER_8X8: [f32; 8 * 8] = threshold(mattr::transposed::<_, 8, 8>(car::from_fn!(|p| { + let q = p ^ (p >> 3); + // https://bisqwit.iki.fi/story/howto/dither/jy/ + #[rustfmt::skip] + (((p & 4) >> 2) | ((q & 4) >> 1) + | ((p & 2) << 1) | ((q & 2) << 2) + | ((p & 1) << 4) | ((q & 1) << 5)) as u32 +}))); + +fn dither_with<const N: usize>( + image: Image<&[f32], 4>, + mut f: impl FnMut(((usize, usize), &[f32; 4])) -> [f32; 4], +) -> Image<Box<[f32]>, 4> { + dither(image, |((x, y), p)| f(((x % N, y % N), p))) +} + pub fn remap_bayer_2x2(image: Image<&[f32], 4>, palette: &[[f32; 4]]) -> Image<Box<[f32]>, 4> { let kd = map(palette); let r = kd.space(palette); |