bendn 2025-01-19
parent 49d182f · commit f369c09
-rw-r--r--Cargo.lock42
-rw-r--r--Cargo.toml1
-rw-r--r--src/diffusion.rs12
-rw-r--r--src/diffusion/riemerasma.rs2
-rw-r--r--src/diffusion/sierra.rs12
-rw-r--r--src/dumb.rs4
-rw-r--r--src/lib.rs45
-rw-r--r--src/main.rs16
-rw-r--r--src/ordered.rs37
9 files changed, 88 insertions, 83 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fb60c62..d25289a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
diff --git a/Cargo.toml b/Cargo.toml
index 6e38bb3..1df7b6b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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..) {
diff --git a/src/lib.rs b/src/lib.rs
index acf4e8b..fa301fc 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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);