make error diffusers use indexedimage
bendn 2025-01-31
parent 3a89624 · commit 4e04c2e
-rw-r--r--Cargo.lock33
-rw-r--r--Cargo.toml3
-rw-r--r--src/diffusion.rs180
-rw-r--r--src/diffusion/sierra.rs185
-rw-r--r--src/dumb.rs19
-rw-r--r--src/lib.rs29
-rw-r--r--src/main.rs21
-rw-r--r--src/ordered.rs39
-rw-r--r--tests/test.rs12
9 files changed, 272 insertions, 249 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fd3eadd..7a2b540 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -82,7 +82,7 @@ checksum = "6a02dba6a60cd31533cf16561ced53239686d18f1464bff49579dd320fcea081"
[[package]]
name = "fimg"
version = "0.4.43"
-source = "git+https://github.com/bend-n/fimg#f8b7d64f22414e510a6ea622a5d8aa80dd58997c"
+source = "git+https://github.com/bend-n/fimg#e88f04d286a94a154865372b115cca6f842cadaf"
dependencies = [
"atools",
"clipline",
@@ -118,9 +118,9 @@ dependencies = [
[[package]]
name = "hinted"
-version = "0.0.1"
+version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9965bbdc3ee9917c5bc2b59da94906fce9e3693fc7609c8b178f15bf5077ba69"
+checksum = "c488b6122f67ca2749a801d562c8c952e1778c42910c43ef537a6f5a46b524f2"
[[package]]
name = "libc"
@@ -129,6 +129,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
+name = "lower"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b2b73ad1a9f69f5e3b6ede21f28b2a5aead191ccb9e8b8156f766c73696c9"
+dependencies = [
+ "lower-macros",
+]
+
+[[package]]
+name = "lower-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fff64bae8cfa89c8871e4b07d27629aa000978b341b98ba5fc1a0a55e2a79a53"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "mattr"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -228,6 +248,9 @@ dependencies = [
"car",
"exoquant",
"fimg",
+ "hinted",
+ "lower",
+ "lower-macros",
"mattr 0.0.3",
"rand",
]
@@ -257,9 +280,9 @@ checksum = "42f74eb7957e3a63fa27bfa53c3d361e7ce3871e66f2518292a011eb8e2c00cc"
[[package]]
name = "unicode-ident"
-version = "1.0.14"
+version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "vecto"
diff --git a/Cargo.toml b/Cargo.toml
index cfd33dc..91d6c4e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,9 @@ fimg = { version = "0.4.43", git = "https://github.com/bend-n/fimg", default-fea
"save",
"scale",
] }
+hinted = "0.0.2"
+lower = "0.1.3"
+lower-macros = "0.1.0"
mattr = "0.0.3"
rand = "0.8.5"
diff --git a/src/diffusion.rs b/src/diffusion.rs
index 257cfce..1243cc7 100644
--- a/src/diffusion.rs
+++ b/src/diffusion.rs
@@ -5,102 +5,98 @@ use super::*;
mod riemerasma;
pub mod sierra;
pub use riemerasma::*;
-pub fn atkinson<const N: usize>(
- image: Image<&[f32], N>,
- palette: &[[f32; N]],
-) -> Image<Box<[f32]>, N> {
- let mut image =
- Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice());
+pub fn atkinson<'a, const N: usize>(
+ mut image: Image<Box<[f32]>, N>,
+ palette: pal<'a, N>,
+) -> out<'a, pal<'a, N>> {
let eighth = [1. / 8.; N];
- for (x, y) in image.serpent() {
- unsafe {
- /*
- * 1 1
- 1 1 1
- 1
- */
- let p = image.pixel(x, y);
- let new = palette.best(p);
- *image.pixel_mut(x, y) = new;
- let error = p.asub(new);
- let f = |x: [f32; N]| x.aadd(error.amul(eighth));
- image.replace(x + 1, y, f);
- image.replace(x + 2, y, f);
+ let out = out::build(image.width() as _, image.height() as _).pal(palette);
+ let i = image.serpent().map(|(x, y)| unsafe {
+ /*
+ * 1 1
+ 1 1 1
+ 1
+ */
+ let p = image.pixel(x, y);
+ let (_, new, index) = palette.closest(p);
+ *image.pixel_mut(x, y) = new;
+ let error = p.asub(new);
+ let f = |x: [f32; N]| 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.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
+ image.replace(x, y + 2, f);
+ ((x, y), index as u32)
+ });
+ unsafe { out.from_iter(i) }
}
-pub fn jarvis<const FAC: u8>(
- image: Image<&[f32], 4>,
- palette: &[[f32; 4]],
-) -> Image<Box<[f32]>, 4> {
- let mut image =
- Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice());
- for (x, y) in image.serpent() {
- #[rustfmt::skip]
- unsafe {
- let p = image.pixel(x, y);
- let new = palette.best(p);
- *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 jarvis<'a, const N: usize, const FAC: u8>(
+ mut image: Image<Box<[f32]>, N>,
+ palette: pal<'a, N>,
+) -> out<'a, pal<'a, N>> {
+ let out = out::build(image.width() as _, image.height() as _).pal(palette);
+ #[rustfmt::skip]
+ let i = image.serpent().map(|(x, y)| unsafe {
+ let p = image.pixel(x, y);
+ let (_, new, r) = palette.closest(p);
+ *image.pixel_mut(x, y) = new;
+
+ let error = p.asub(new);
+ let f = |f| {
+ move |x: [f32; N]| {
+ x.aadd(error.amul([(f as f32 / 48.) * const { FAC as f32 * (1.0 / 255.) }; N]))
+ }
+ };
+ /* * 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));
+ ((x, y), r as u32)
+ });
+ unsafe { out.from_iter(i) }
}
-pub fn floyd_steinberg<const FAC: u8>(
- image: Image<&[f32], 4>,
- palette: &[[f32; 4]],
-) -> Image<Box<[f32]>, 4> {
- let mut image =
- Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice());
- for (x, y) in image.serpent() {
- unsafe {
- let p = image.pixel(x, y);
- let new = palette.best(p);
- *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
+
+pub fn floyd_steinberg<'p, const FAC: u8, const N: usize>(
+ mut image: Image<Box<[f32]>, N>,
+ palette: pal<'p, N>,
+) -> out<'p, pal<'p, N>> {
+ let out = out::build(image.width() as _, image.height() as _).pal(palette);
+ let i = image.serpent().map(|(x, y)| unsafe {
+ let p = image.pixel(x, y);
+ let (_, new, i) = palette.closest(p);
+ let error = p.asub(new);
+ let f = |f| {
+ move |x: [f32; N]| {
+ x.aadd(error.amul([(f as f32 / 48.) * const { FAC as f32 * (1.0 / 255.) }; N]))
+ }
+ };
+ /*
+ * 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));
+
+ ((x, y), i as u32)
+ });
+ unsafe { out.from_iter(i) }
}
diff --git a/src/diffusion/sierra.rs b/src/diffusion/sierra.rs
index 4ba814e..8de511a 100644
--- a/src/diffusion/sierra.rs
+++ b/src/diffusion/sierra.rs
@@ -1,101 +1,96 @@
use super::*;
-pub fn sierra<const FAC: u8>(
- image: Image<&[f32], 4>,
- palette: &[[f32; 4]],
-) -> Image<Box<[f32]>, 4> {
- let mut image =
- Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice());
- for (x, y) in image.serpent() {
- #[rustfmt::skip]
- unsafe {
- let p = image.pixel(x, y);
- let new = palette.closest(p).1;
- *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 / 32.) * const { FAC as f32 * (1.0 / 255.) }; 4]))
- }
- };
- /* * 5 3
- 2 4 5 4 2
- 2 3 2 */
- 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(2));
- image.replace(x.wrapping_sub(1), y, f(4));
- image.replace(x , y, f(5));
- image.replace(x + 1, y, f(4));
- image.replace(x + 2, y, f(2));
- let y = y + 1;
- image.replace(x.wrapping_sub(1), y, f(2));
- image.replace(x , y, f(3));
- image.replace(x + 1, y, f(2));
- }
- }
- image
+pub fn sierra<'p, const FAC: u8, const N: usize>(
+ mut image: Image<Box<[f32]>, N>,
+ palette: pal<'p, N>,
+) -> out<'p, pal<'p, N>> {
+ let out = out::build(image.width(), image.height()).pal(palette);
+ #[rustfmt::skip]
+ let i = image.serpent().map(|c @ (x, y)| unsafe {
+ let p = image.pixel(x, y);
+ let (_, new, i) = palette.closest(p);
+ *image.pixel_mut(x, y) = new;
+ let error = p.asub(new);
+ let f = |f| {
+ move |x: [f32; N]| {
+ x.aadd(error.amul([(f as f32 / 32.) * const { FAC as f32 * (1.0 / 255.) }; N]))
+ }
+ };
+ /* * 5 3
+ 2 4 5 4 2
+ 2 3 2 */
+ 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(2));
+ image.replace(x.wrapping_sub(1), y, f(4));
+ image.replace(x , y, f(5));
+ image.replace(x + 1, y, f(4));
+ image.replace(x + 2, y, f(2));
+ let y = y + 1;
+ image.replace(x.wrapping_sub(1), y, f(2));
+ image.replace(x , y, f(3));
+ image.replace(x + 1, y, f(2));
+ (c, i)
+ });
+ unsafe { out.from_iter(i) }
}
-pub fn sierra_two<const FAC: u8>(
- image: Image<&[f32], 4>,
- palette: &[[f32; 4]],
-) -> Image<Box<[f32]>, 4> {
- let mut image =
- Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice());
- for (x, y) in image.serpent() {
- #[rustfmt::skip]
- unsafe {
- let p = image.pixel(x, y);
- let new = palette.best(p);
- *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 / 16.) * const { FAC as f32 * (1.0 / 255.) }; 4]))
- }
- };
- /* * 4 3
- 1 2 3 2 1 */
- image.replace(x + 1, y, f(4));
- 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(2));
- image.replace(x , y, f(3));
- image.replace(x + 1, y, f(2));
- image.replace(x + 2, y, f(1));
- }
- }
- image
+pub fn sierra_two<'p, const FAC: u8, const N: usize>(
+ mut image: Image<Box<[f32]>, N>,
+ palette: pal<'p, N>,
+) -> out<'p, pal<'p, N>> {
+ let out = out::build(image.width(), image.height()).pal(palette);
+ #[rustfmt::skip]
+ let i = image.serpent().map(|c@(x, y)| unsafe {
+ let p = image.pixel(x, y);
+ let (_, new, i) = palette.closest(p);
+ *image.pixel_mut(x, y) = new;
+ let error = p.asub(new);
+ let f = |f| {
+ move |x: [f32; N]| {
+ x.aadd(error.amul([(f as f32 / 16.) * const { FAC as f32 * (1.0 / 255.) }; N]))
+ }
+ };
+ /* * 4 3
+ 1 2 3 2 1 */
+ image.replace(x + 1, y, f(4));
+ 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(2));
+ image.replace(x , y, f(3));
+ image.replace(x + 1, y, f(2));
+ image.replace(x + 2, y, f(1));
+ (c, i)
+ });
+ unsafe { out.from_iter(i) }
}
-pub fn sierra_lite<const FAC: u8>(
- image: Image<&[f32], 4>,
- palette: &[[f32; 4]],
-) -> Image<Box<[f32]>, 4> {
- let mut image =
- Image::build(image.width(), image.height()).buf(image.buffer().to_vec().into_boxed_slice());
- for (x, y) in image.serpent() {
- #[rustfmt::skip]
- unsafe {
- let p = image.pixel(x, y);
- let new = palette.best(p);
- *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 / 4.) * const { FAC as f32 * (1.0 / 255.) }; 4]))
- }
- };
- #[allow(warnings)]
- /** 2
- 1 1 */
- image.replace(x + 1, y, f(2));
- let y = y + 1;
- image.replace(x.wrapping_sub(1), y, f(1));
- image.replace(x , y, f(1));
- }
- }
- image
+pub fn sierra_lite<'p, const FAC: u8, const N: usize>(
+ mut image: Image<Box<[f32]>, N>,
+ palette: pal<'p, N>,
+) -> out<'p, pal<'p, N>> {
+ let out = out::build(image.width(), image.height()).pal(palette);
+ #[rustfmt::skip]
+ let i = image.serpent().map(|c@(x, y)| unsafe {
+ let p = image.pixel(x, y);
+ let (_, new, i) = palette.closest(p);
+ *image.pixel_mut(x, y) = new;
+ let error = p.asub(new);
+ let f = |f| {
+ move |x: [f32; N]| {
+ x.aadd(error.amul([(f as f32 / 4.) * const { FAC as f32 * (1.0 / 255.) }; N]))
+ }
+ };
+
+ #[allow(warnings)]
+ /** 2
+ 1 1 */
+ image.replace(x + 1, y, f(2));
+ let y = y + 1;
+ image.replace(x.wrapping_sub(1), y, f(1));
+ image.replace(x , y, f(1));
+ (c, i)
+ });
+ unsafe { out.from_iter(i) }
}
diff --git a/src/dumb.rs b/src/dumb.rs
index 6f1a800..da5e64d 100644
--- a/src/dumb.rs
+++ b/src/dumb.rs
@@ -1,10 +1,10 @@
use atools::prelude::*;
pub trait Closest<const N: usize> {
- fn closest(&self, color: [f32; N]) -> (f32, [f32; N], usize);
+ fn closest(&self, color: [f32; N]) -> (f32, [f32; N], u32);
fn best(&self, color: [f32; N]) -> [f32; N] {
self.closest(color).1
}
- fn nearest(&self, color: [f32; N]) -> usize {
+ fn nearest(&self, color: [f32; N]) -> u32 {
self.closest(color).2
}
fn space(&self) -> f32;
@@ -16,13 +16,18 @@ fn euclidean_distance<const N: usize>(f: [f32; N], with: [f32; N]) -> f32 {
.sum()
}
+#[no_mangle]
+fn closeer(x: [f32; 4], p: &[[f32; 4]]) -> [f32; 4] {
+ p.best(x)
+}
+
impl<const N: usize> Closest<N> for &[[f32; N]] {
/// o(nn)
- fn closest(&self, color: [f32; N]) -> (f32, [f32; N], usize) {
- self.iter()
- .copied()
- .enumerate()
- .map(|(i, x)| (euclidean_distance(x, color), x, i))
+ #[inline]
+ fn closest(&self, color: [f32; N]) -> (f32, [f32; N], u32) {
+ (0..)
+ .zip(*self)
+ .map(|(i, &x)| (euclidean_distance(x, color), x, i))
.min_by(|x, y| x.0.total_cmp(&y.0))
.unwrap()
}
diff --git a/src/lib.rs b/src/lib.rs
index 73d4bcc..afda5d9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -20,6 +20,9 @@
array_windows,
iter_map_windows
)]
+#![allow(non_camel_case_types)]
+type pal<'palette, const N: usize> = &'palette [[f32; N]];
+type out<'palette, P> = IndexedImage<Box<[u32]>, P>;
pub mod diffusion;
pub mod ordered;
@@ -32,16 +35,18 @@ use fimg::{indexed::IndexedImage, Image};
fn dither<'a, const C: usize>(
image: Image<&[f32], C>,
f: impl FnMut(((usize, usize), &[f32; C])) -> u32,
- pal: &'a [[f32; C]],
-) -> IndexedImage<Box<[u32]>, &'a [[f32; C]]> {
- IndexedImage::build(image.width(), image.height())
- .pal(pal)
- .buf(
- image
- .chunked()
- .zip(image.ordered())
- .map(|(p, xy)| (xy.array().map(|x| x as usize).tuple(), p))
- .map(f)
- .collect(),
- )
+ pal: pal<'a, C>,
+) -> out<'a, pal<'a, C>> {
+ unsafe {
+ IndexedImage::build(image.width(), image.height())
+ .pal(pal)
+ .buf_unchecked(
+ image
+ .chunked()
+ .zip(image.ordered())
+ .map(|(p, xy)| (xy.array().map(|x| x as usize).tuple(), p))
+ .map(f)
+ .collect(),
+ )
+ }
}
diff --git a/src/main.rs b/src/main.rs
index 47fe539..73387d8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,9 +1,6 @@
-#![feature(slice_as_chunks, generic_const_exprs)]
-use std::time::Instant;
-
-use atools::prelude::*;
-use exoquant::SimpleColorSpace;
+#![feature(slice_as_chunks, generic_arg_infer, iter_chain)]
use fimg::{DynImage, Image};
+use std::time::Instant;
fn main() {
reemap();
@@ -53,6 +50,7 @@ fn reemap() {
// println!("{pal:?}");
// dbg!(pal.space());
let i = DynImage::open("../fimg/tdata/cat.png").to_rgba();
+ // let pal = [[0.], [1.]];
// let mut pal = exoquant::generate_palette(
// &i.chunked()
// .map(|&[r, g, b, a]| exoquant::Color::new(r, g, b, a))
@@ -86,19 +84,10 @@ fn reemap() {
// decode(2.2, encode(2.4, i.as_ref()))
// .to_u8()
// .save("gamma/2_4.png");
- let x = remapper::ordered::remap(
- // fimg::Image::<&[u8], 4>::make::<256, 256>().as_ref(),
- i.as_ref(),
- &pal,
- )
- .to()
- .to_u8()
- .show();
let now = Instant::now();
- let x = remapper::ordered::blue(
+ let x = remapper::diffusion::sierra::sierra::<255, 4>(
// fimg::Image::<&[u8], 4>::make::<256, 256>().as_ref(),
- i.as_ref(),
- &pal,
+ i, &pal,
)
.to()
.to_u8();
diff --git a/src/ordered.rs b/src/ordered.rs
index 489cbde..2aae8de 100644
--- a/src/ordered.rs
+++ b/src/ordered.rs
@@ -52,8 +52,8 @@ const BAYER_64X64: [f32; 64 * 64] = threshold(BAYER5);
fn dither_with<'a, const N: usize, const C: usize>(
image: Image<&[f32], C>,
mut f: impl FnMut(((usize, usize), &[f32; C])) -> u32,
- palette: &'a [[f32; C]],
-) -> IndexedImage<Box<[u32]>, &'a [[f32; C]]> {
+ palette: pal<'a, C>,
+) -> out<'a, pal<'a, C>> {
dither(image, |((x, y), p)| f(((x % N, y % N), p)), palette)
}
@@ -64,8 +64,8 @@ macro_rules! bayer {
/// Dont expect too much difference from each of them.
pub fn $i<'a, const C: usize>(
image: Image<&[f32], C>,
- palette: &'a [[f32; C]],
- ) -> IndexedImage<Box<[u32]>, &'a [[f32; C]]> {
+ palette: pal<'a, C>,
+ ) -> out<'a, pal<'a, C>> {
let r = palette.space();
dither_with::<$j, C>(
image.into(),
@@ -88,17 +88,18 @@ bayer!(bayer64x64, BAYER_64X64, 64);
pub fn remap<'a, const C: usize>(
image: Image<&[f32], C>,
- palette: &'a [[f32; C]],
-) -> IndexedImage<Box<[u32]>, &'a [[f32; C]]> {
- // todo!();
- IndexedImage::build(image.width(), image.height())
- .pal(palette)
- .buf(
- image
- .chunked()
- .map(|x| palette.nearest(*x) as u32)
- .collect(),
- )
+ palette: pal<'a, C>,
+) -> out<'a, pal<'a, C>> {
+ unsafe {
+ IndexedImage::build(image.width(), image.height())
+ .pal(palette)
+ .buf_unchecked(
+ image
+ .chunked()
+ .map(|x| palette.nearest(*x) as u32)
+ .collect(),
+ )
+ }
}
const BLUE: Image<[f32; 1024 * 1024], 1> = unsafe {
@@ -162,8 +163,8 @@ pub fn decode<const C: usize, T: AsRef<[f32]>>(
pub fn blue<'a, const C: usize>(
image: Image<&[f32], C>,
- palette: &'a [[f32; C]],
-) -> IndexedImage<Box<[u32]>, &'a [[f32; C]]> {
+ palette: pal<'a, C>,
+) -> out<'a, pal<'a, C>> {
dither_with::<1024, C>(
image,
|((x, y), p)| unsafe {
@@ -176,8 +177,8 @@ pub fn blue<'a, const C: usize>(
pub fn triangular<'a, const C: usize>(
image: Image<&[f32], C>,
- palette: &'a [[f32; C]],
-) -> IndexedImage<Box<[u32]>, &'a [[f32; C]]>
+ palette: pal<'a, C>,
+) -> out<'a, pal<'a, C>>
where
{
// https://computergraphics.stackexchange.com/questions/5904/whats-a-proper-way-to-clamp-dither-noise/5952#5952
diff --git a/tests/test.rs b/tests/test.rs
index 2d9b67b..049fe05 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -29,6 +29,12 @@ macro_rules! test {
test(stringify!($x), $call);
}
};
+ (boxed $x:ident, $call:path) => {
+ #[test]
+ fn $x() {
+ test(stringify!($x), |x, p| $call(x.boxed(), p))
+ }
+ };
}
test!(o2x2, remapper::ordered::bayer2x2);
@@ -38,6 +44,6 @@ test!(o16x16, remapper::ordered::bayer16x16);
test!(o32x32, remapper::ordered::bayer32x32);
test!(o64x64, remapper::ordered::bayer64x64);
-// test!(s1, remapper::diffusion::sierra::sierra::<241>);
-// test!(s2, remapper::diffusion::sierra::sierra_two::<241>);
-// test!(s3, remapper::diffusion::sierra::sierra_lite::<241>);
+test!(boxed s1, remapper::diffusion::sierra::sierra::<255, 4>);
+test!(boxed s2, remapper::diffusion::sierra::sierra_two::<255, 4>);
+test!(boxed s3, remapper::diffusion::sierra::sierra_lite::<255, 4>);