#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly_f16", feature(f16))]
#[cfg(feature = "blue-noise")]
mod blue_noise;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(not(feature = "nightly_f16"))]
use common_traits::{CastableFrom, Number};
#[cfg(feature = "nightly_f16")]
use common_traits_f16::{CastableFrom, Number};
use core::{
cmp::PartialOrd,
ops::{Add, Mul, Neg, Sub},
};
use enum_dispatch::enum_dispatch;
#[enum_dispatch]
pub trait LinearRng: Sized + Send + Sync {
fn compute(&self, index: u32) -> f32;
#[inline]
fn dither<T>(
&self,
value: T,
min: T,
one: T,
dither_amplitude: T,
index: u32,
) -> T
where
T: DitherFloat,
Self: Sized,
{
dither_with(value, min, one, dither_amplitude, index, self)
}
#[inline]
fn simple_dither<T>(&self, value: T, one: T, index: u32) -> T
where
T: DitherFloat + Number + CastableFrom<f32>,
Self: Sized,
{
simple_dither_with(value, one, index, self)
}
#[cfg(not(feature = "rayon"))]
fn dither_slice<T>(
&self,
values: &mut [T],
min: T,
one: T,
dither_amplitude: T,
) where
T: DitherFloat,
Self: Sized,
{
dither_slice_with(values, min, one, dither_amplitude, self)
}
#[cfg(feature = "rayon")]
fn dither_slice<T>(
&self,
values: &mut [T],
min: T,
one: T,
dither_amplitude: T,
) where
T: DitherFloat + Send + Sync,
Self: Sized,
{
dither_slice_with(values, min, one, dither_amplitude, self)
}
#[cfg(not(feature = "rayon"))]
fn simple_dither_slice<T>(&self, values: &mut [T], one: T)
where
T: DitherFloat + Number + CastableFrom<f32>,
Self: Sized,
{
simple_dither_slice_with(values, one, self)
}
#[cfg(feature = "rayon")]
fn simple_dither_slice<T>(&self, values: &mut [T], one: T)
where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
Self: Sized,
{
simple_dither_slice_with(values, one, self)
}
#[cfg(not(feature = "rayon"))]
fn dither_iter<T, I>(
&self,
values: I,
min: T,
one: T,
dither_amplitude: T,
) -> Vec<T>
where
T: DitherFloat,
I: IntoIterator<Item = T>,
Self: Sized,
{
dither_iter_with(values, min, one, dither_amplitude, self)
}
#[cfg(feature = "rayon")]
fn dither_iter<T, I>(
&self,
values: I,
min: T,
one: T,
dither_amplitude: T,
) -> Vec<T>
where
T: DitherFloat + Send + Sync,
I: IntoIterator<Item = T>,
I::IntoIter: Send,
Self: Sized,
{
dither_iter_with(values, min, one, dither_amplitude, self)
}
#[cfg(not(feature = "rayon"))]
fn simple_dither_iter<T, I>(&self, values: I, one: T) -> Vec<T>
where
T: DitherFloat + Number + CastableFrom<f32>,
I: IntoIterator<Item = T>,
Self: Sized,
{
simple_dither_iter_with(values, one, self)
}
#[cfg(feature = "rayon")]
fn simple_dither_iter<T, I>(&self, values: I, one: T) -> Vec<T>
where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
I: IntoIterator<Item = T>,
I::IntoIter: Send,
Self: Sized,
{
simple_dither_iter_with(values, one, self)
}
#[inline]
fn dither_float<Src, Dest>(&self, value: Src, index: u32) -> Dest
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
Self: Sized,
{
dither_float_with(value, index, self)
}
#[cfg(not(feature = "rayon"))]
fn dither_float_slice<Src, Dest>(&self, values: &[Src]) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Copy,
Self: Sized,
{
dither_float_slice_with(values, self)
}
#[cfg(feature = "rayon")]
fn dither_float_slice<Src, Dest>(&self, values: &[Src]) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Copy
+ Send
+ Sync,
Dest: Send,
Self: Sized,
{
dither_float_slice_with(values, self)
}
#[cfg(not(feature = "rayon"))]
fn dither_float_iter<Src, Dest, I>(&self, values: I) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
I: IntoIterator<Item = Src>,
Self: Sized,
{
dither_float_iter_with(values, self)
}
#[cfg(feature = "rayon")]
fn dither_float_iter<Src, Dest, I>(&self, values: I) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Send
+ Sync,
Dest: Send,
I: IntoIterator<Item = Src>,
I::IntoIter: Send,
Self: Sized,
{
dither_float_iter_with(values, self)
}
}
#[enum_dispatch]
pub trait SpatialRng: Sized + Send + Sync {
fn compute(&self, x: u32, y: u32) -> f32;
#[inline]
fn dither_2d<T>(
&self,
value: T,
min: T,
one: T,
dither_amplitude: T,
x: u32,
y: u32,
) -> T
where
T: DitherFloat,
Self: Sized,
{
dither_2d(value, min, one, dither_amplitude, x, y, self)
}
#[inline]
fn simple_dither_2d<T>(&self, value: T, one: T, x: u32, y: u32) -> T
where
T: DitherFloat + Number + CastableFrom<f32>,
Self: Sized,
{
simple_dither_2d(value, one, x, y, self)
}
#[cfg(not(feature = "rayon"))]
fn dither_slice_2d<const CHANNELS: usize, const SEED_OFFSET: u32, T>(
&self,
values: &mut [T],
width: usize,
min: T,
one: T,
dither_amplitude: T,
) where
T: DitherFloat,
Self: Sized,
{
dither_slice_2d::<CHANNELS, SEED_OFFSET, T, Self>(
values,
width,
min,
one,
dither_amplitude,
self,
)
}
#[cfg(feature = "rayon")]
fn dither_slice_2d<const CHANNELS: usize, const SEED_OFFSET: u32, T>(
&self,
values: &mut [T],
width: usize,
min: T,
one: T,
dither_amplitude: T,
) where
T: DitherFloat + Send + Sync,
Self: Sized,
{
dither_slice_2d::<CHANNELS, SEED_OFFSET, T, Self>(
values,
width,
min,
one,
dither_amplitude,
self,
)
}
#[cfg(not(feature = "rayon"))]
fn simple_dither_slice_2d<
const CHANNELS: usize,
const SEED_OFFSET: u32,
T,
>(
&self,
values: &mut [T],
width: usize,
one: T,
) where
T: DitherFloat + Number + CastableFrom<f32>,
Self: Sized,
{
simple_dither_slice_2d::<CHANNELS, SEED_OFFSET, T, Self>(
values, width, one, self,
)
}
#[cfg(feature = "rayon")]
fn simple_dither_slice_2d<
const CHANNELS: usize,
const SEED_OFFSET: u32,
T,
>(
&self,
values: &mut [T],
width: usize,
one: T,
) where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
Self: Sized,
{
simple_dither_slice_2d::<CHANNELS, SEED_OFFSET, T, Self>(
values, width, one, self,
)
}
#[inline]
fn dither_float_2d<Src, Dest>(&self, value: Src, x: u32, y: u32) -> Dest
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
Self: Sized,
{
dither_float_2d(value, x, y, self)
}
#[cfg(not(feature = "rayon"))]
fn dither_float_slice_2d<Src, Dest>(
&self,
values: &[Src],
width: usize,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Copy,
Self: Sized,
{
dither_float_slice_2d(values, width, self)
}
#[cfg(feature = "rayon")]
fn dither_float_slice_2d<Src, Dest>(
&self,
values: &[Src],
width: usize,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Copy
+ Send
+ Sync,
Dest: Send,
Self: Sized,
{
dither_float_slice_2d(values, width, self)
}
}
mod linear;
mod spatial;
pub use linear::*;
pub use spatial::*;
pub trait DitherFloat:
Copy
+ PartialEq
+ PartialOrd
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Neg<Output = Self>
+ Number
+ CastableFrom<f64>
{
fn round(self) -> Self;
}
impl DitherFloat for f32 {
#[cfg(feature = "std")]
fn round(self) -> Self {
self.round()
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
fn round(self) -> Self {
libm::roundf(self)
}
#[cfg(all(not(feature = "std"), not(feature = "libm")))]
fn round(self) -> Self {
if self >= 0.0 {
(self + 0.5) as i32 as f32
} else {
(self - 0.5) as i32 as f32
}
}
}
impl DitherFloat for f64 {
#[cfg(feature = "std")]
fn round(self) -> Self {
self.round()
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
fn round(self) -> Self {
libm::round(self)
}
#[cfg(all(not(feature = "std"), not(feature = "libm")))]
fn round(self) -> Self {
if self >= 0.0 {
(self + 0.5) as i64 as f64
} else {
(self - 0.5) as i64 as f64
}
}
}
#[cfg(feature = "nightly_f16")]
impl DitherFloat for f16 {
#[cfg(feature = "std")]
fn round(self) -> Self {
(self as f32).round() as f16
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
fn round(self) -> Self {
libm::roundf(self as f32) as f16
}
#[cfg(all(not(feature = "std"), not(feature = "libm")))]
fn round(self) -> Self {
let f32_val = self as f32;
let rounded = if f32_val >= 0.0 {
(f32_val + 0.5) as i32 as f32
} else {
(f32_val - 0.5) as i32 as f32
};
rounded as f16
}
}
pub trait DitherFloatConversion<Dest>: Sized {
fn compute_target_ulp(self) -> Self;
fn cast_to_dest(self) -> Dest;
}
#[cfg(feature = "nightly_f16")]
impl DitherFloatConversion<f16> for f32 {
fn compute_target_ulp(self) -> Self {
if !self.is_finite() {
return 1.0; }
let abs_val = self.abs();
const F16_MAX: f32 = 65504.0;
if abs_val >= F16_MAX {
return 1.0; }
const F16_MIN_NORMAL: f32 = 6.103_515_6e-5; if abs_val < F16_MIN_NORMAL {
return 5.960_464_5e-8; }
let bits = self.to_bits();
let exponent = ((bits >> 23) & 0xFF) as i32 - 127;
2.0_f32.powi(exponent - 10)
}
#[inline(always)]
fn cast_to_dest(self) -> f16 {
self as f16
}
}
#[cfg(feature = "nightly_f16")]
impl DitherFloatConversion<f16> for f64 {
fn compute_target_ulp(self) -> Self {
if !self.is_finite() {
return 1.0;
}
let abs_val = self.abs();
const F16_MAX: f64 = 65504.0;
if abs_val >= F16_MAX {
return 1.0;
}
const F16_MIN_NORMAL: f64 = 6.103515625e-5; if abs_val < F16_MIN_NORMAL {
return 5.960464477539063e-8; }
let bits = self.to_bits();
let exponent = ((bits >> 52) & 0x7FF) as i32 - 1023;
2.0_f64.powi(exponent - 10)
}
#[inline(always)]
fn cast_to_dest(self) -> f16 {
self as f16
}
}
impl DitherFloatConversion<f32> for f64 {
fn compute_target_ulp(self) -> Self {
if !self.is_finite() {
return 1.0;
}
let abs_val = self.abs();
const F32_MAX: f64 = 3.4028234663852886e38;
if abs_val >= F32_MAX {
return 1.0;
}
const F32_MIN_NORMAL: f64 = 1.1754943508222875e-38; if abs_val < F32_MIN_NORMAL {
return 1.401298464324817e-45; }
let bits = self.to_bits();
let exponent = ((bits >> 52) & 0x7FF) as i32 - 1023;
2.0_f64.powi(exponent - 23)
}
#[inline(always)]
fn cast_to_dest(self) -> f32 {
self as f32
}
}
#[inline]
pub fn dither<T>(
value: T,
min: T,
one: T,
dither_amplitude: T,
index: u32,
seed: u32,
) -> T
where
T: DitherFloat,
{
let method = Hash::new(seed);
dither_with(value, min, one, dither_amplitude, index, &method)
}
#[inline]
pub fn dither_with<T, M: LinearRng>(
value: T,
min: T,
one: T,
dither_amplitude: T,
index: u32,
method: &M,
) -> T
where
T: DitherFloat,
{
let dither = if dither_amplitude == T::ZERO {
T::ZERO
} else {
T::cast_from(method.compute(index) as f64) * dither_amplitude
};
(min + value * (one - min) + dither).round()
}
#[inline]
pub fn simple_dither<T>(value: T, one: T, index: u32, seed: u32) -> T
where
T: DitherFloat + Number + CastableFrom<f32>,
{
let method = Hash::new(seed);
simple_dither_with(value, one, index, &method)
}
#[inline]
pub fn simple_dither_with<T, M: LinearRng>(
value: T,
one: T,
index: u32,
method: &M,
) -> T
where
T: DitherFloat + Number + CastableFrom<f32>,
{
dither_with(value, T::ZERO, one, T::cast_from(0.5_f32), index, method)
.clamp(T::ZERO, one)
}
#[inline]
pub fn dither_2d<T, M: SpatialRng>(
value: T,
min: T,
one: T,
dither_amplitude: T,
x: u32,
y: u32,
method: &M,
) -> T
where
T: DitherFloat,
{
let dither = if dither_amplitude == T::ZERO {
T::ZERO
} else {
T::cast_from(method.compute(x, y) as f64) * dither_amplitude
};
(min + value * (one - min) + dither).round()
}
#[inline]
pub fn simple_dither_2d<T, M: SpatialRng>(
value: T,
one: T,
x: u32,
y: u32,
method: &M,
) -> T
where
T: DitherFloat + Number + CastableFrom<f32>,
{
dither_2d(value, T::ZERO, one, T::cast_from(0.5_f32), x, y, method)
.clamp(T::ZERO, one)
}
#[inline]
pub fn dither_float<Src, Dest>(value: Src, index: u32, seed: u32) -> Dest
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
{
let method = Hash::new(seed);
dither_float_with(value, index, &method)
}
#[inline]
pub fn dither_float_with<Src, Dest, M: LinearRng>(
value: Src,
index: u32,
method: &M,
) -> Dest
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
{
let ulp = value.compute_target_ulp();
let dither_noise = Src::cast_from(method.compute(index) as f64);
let dithered = value + dither_noise * ulp * Src::cast_from(0.5);
dithered.cast_to_dest()
}
#[inline]
pub fn dither_float_2d<Src, Dest, M: SpatialRng>(
value: Src,
x: u32,
y: u32,
method: &M,
) -> Dest
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
{
let ulp = value.compute_target_ulp();
let dither_noise = Src::cast_from(method.compute(x, y) as f64);
let dithered = value + dither_noise * ulp * Src::cast_from(0.5);
dithered.cast_to_dest()
}
#[cfg(not(feature = "rayon"))]
pub fn dither_slice<T>(
values: &mut [T],
min: T,
one: T,
dither_amplitude: T,
seed: u32,
) where
T: DitherFloat,
{
let method = Hash::new(seed);
dither_slice_with(values, min, one, dither_amplitude, &method)
}
#[cfg(feature = "rayon")]
pub fn dither_slice<T>(
values: &mut [T],
min: T,
one: T,
dither_amplitude: T,
seed: u32,
) where
T: DitherFloat + Send + Sync,
{
let method = Hash::new(seed);
dither_slice_with(values, min, one, dither_amplitude, &method)
}
#[cfg(not(feature = "rayon"))]
pub fn dither_slice_with<T, M: LinearRng>(
values: &mut [T],
min: T,
one: T,
dither_amplitude: T,
method: &M,
) where
T: DitherFloat,
{
for (index, value) in values.iter_mut().enumerate() {
*value = dither_with(
*value,
min,
one,
dither_amplitude,
index as u32,
method,
);
}
}
#[cfg(feature = "rayon")]
pub fn dither_slice_with<T, M: LinearRng>(
values: &mut [T],
min: T,
one: T,
dither_amplitude: T,
method: &M,
) where
T: DitherFloat + Send + Sync,
{
use rayon::prelude::*;
values
.par_iter_mut()
.enumerate()
.for_each(|(index, value)| {
*value = dither_with(
*value,
min,
one,
dither_amplitude,
index as u32,
method,
);
});
}
#[cfg(not(feature = "rayon"))]
pub fn dither_slice_2d<const CHANNELS: usize, const SEED_OFFSET: u32, T, M>(
values: &mut [T],
width: usize,
min: T,
one: T,
dither_amplitude: T,
method: &M,
) where
T: DitherFloat,
M: SpatialRng,
{
let height = values.len() / (width * CHANNELS);
if SEED_OFFSET == 0 {
for y in 0..height {
for x in 0..width {
let noise = method.compute(x as u32, y as u32);
let dither = if dither_amplitude == T::ZERO {
T::ZERO
} else {
T::cast_from(noise as f64) * dither_amplitude
};
let pixel_start = (y * width + x) * CHANNELS;
for c in 0..CHANNELS {
let idx = pixel_start + c;
values[idx] =
(min + values[idx] * (one - min) + dither).round();
}
}
}
} else {
for y in 0..height {
for x in 0..width {
let pixel_start = (y * width + x) * CHANNELS;
for c in 0..CHANNELS {
let channel_offset = (c as u32) * SEED_OFFSET;
let noise = method.compute(
(x as u32).wrapping_add(channel_offset),
(y as u32).wrapping_add(channel_offset),
);
let dither = if dither_amplitude == T::ZERO {
T::ZERO
} else {
T::cast_from(noise as f64) * dither_amplitude
};
let idx = pixel_start + c;
values[idx] =
(min + values[idx] * (one - min) + dither).round();
}
}
}
}
}
#[cfg(feature = "rayon")]
pub fn dither_slice_2d<const CHANNELS: usize, const SEED_OFFSET: u32, T, M>(
values: &mut [T],
width: usize,
min: T,
one: T,
dither_amplitude: T,
method: &M,
) where
T: DitherFloat + Send + Sync,
M: SpatialRng,
{
use rayon::prelude::*;
let row_size = width * CHANNELS;
if SEED_OFFSET == 0 {
values
.par_chunks_mut(row_size)
.enumerate()
.for_each(|(y, row)| {
for x in 0..width {
let noise = method.compute(x as u32, y as u32);
let dither = if dither_amplitude == T::ZERO {
T::ZERO
} else {
T::cast_from(noise as f64) * dither_amplitude
};
let pixel_start = x * CHANNELS;
for c in 0..CHANNELS {
let idx = pixel_start + c;
row[idx] =
(min + row[idx] * (one - min) + dither).round();
}
}
});
} else {
values
.par_chunks_mut(row_size)
.enumerate()
.for_each(|(y, row)| {
for x in 0..width {
let pixel_start = x * CHANNELS;
for c in 0..CHANNELS {
let channel_offset = (c as u32) * SEED_OFFSET;
let noise = method.compute(
(x as u32).wrapping_add(channel_offset),
(y as u32).wrapping_add(channel_offset),
);
let dither = if dither_amplitude == T::ZERO {
T::ZERO
} else {
T::cast_from(noise as f64) * dither_amplitude
};
let idx = pixel_start + c;
row[idx] =
(min + row[idx] * (one - min) + dither).round();
}
}
});
}
}
#[cfg(not(feature = "rayon"))]
pub fn simple_dither_slice<T>(values: &mut [T], one: T, seed: u32)
where
T: DitherFloat + Number + CastableFrom<f32>,
{
let method = Hash::new(seed);
simple_dither_slice_with(values, one, &method)
}
#[cfg(feature = "rayon")]
pub fn simple_dither_slice<T>(values: &mut [T], one: T, seed: u32)
where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
{
let method = Hash::new(seed);
simple_dither_slice_with(values, one, &method)
}
#[cfg(not(feature = "rayon"))]
pub fn simple_dither_slice_with<T, M: LinearRng>(
values: &mut [T],
one: T,
method: &M,
) where
T: DitherFloat + Number + CastableFrom<f32>,
{
for (index, value) in values.iter_mut().enumerate() {
*value = simple_dither_with(*value, one, index as u32, method);
}
}
#[cfg(feature = "rayon")]
pub fn simple_dither_slice_with<T, M: LinearRng>(
values: &mut [T],
one: T,
method: &M,
) where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
{
use rayon::prelude::*;
values
.par_iter_mut()
.enumerate()
.for_each(|(index, value)| {
*value = simple_dither_with(*value, one, index as u32, method);
});
}
#[cfg(not(feature = "rayon"))]
pub fn simple_dither_slice_2d<
const CHANNELS: usize,
const SEED_OFFSET: u32,
T,
M,
>(
values: &mut [T],
width: usize,
one: T,
method: &M,
) where
T: DitherFloat + Number + CastableFrom<f32>,
M: SpatialRng,
{
let height = values.len() / (width * CHANNELS);
if SEED_OFFSET == 0 {
for y in 0..height {
for x in 0..width {
let noise = method.compute(x as u32, y as u32);
let dither_offset =
T::cast_from(noise as f64) * T::cast_from(0.5_f32);
let pixel_start = (y * width + x) * CHANNELS;
for c in 0..CHANNELS {
let idx = pixel_start + c;
let scaled = values[idx] * one;
values[idx] =
(scaled + dither_offset).round().clamp(T::ZERO, one);
}
}
}
} else {
for y in 0..height {
for x in 0..width {
let pixel_start = (y * width + x) * CHANNELS;
for c in 0..CHANNELS {
let channel_offset = (c as u32) * SEED_OFFSET;
let noise = method.compute(
(x as u32).wrapping_add(channel_offset),
(y as u32).wrapping_add(channel_offset),
);
let dither_offset =
T::cast_from(noise as f64) * T::cast_from(0.5_f32);
let idx = pixel_start + c;
let scaled = values[idx] * one;
values[idx] =
(scaled + dither_offset).round().clamp(T::ZERO, one);
}
}
}
}
}
#[cfg(feature = "rayon")]
pub fn simple_dither_slice_2d<
const CHANNELS: usize,
const SEED_OFFSET: u32,
T,
M,
>(
values: &mut [T],
width: usize,
one: T,
method: &M,
) where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
M: SpatialRng,
{
use rayon::prelude::*;
let row_size = width * CHANNELS;
if SEED_OFFSET == 0 {
values
.par_chunks_mut(row_size)
.enumerate()
.for_each(|(y, row)| {
for x in 0..width {
let noise = method.compute(x as u32, y as u32);
let dither_offset =
T::cast_from(noise as f64) * T::cast_from(0.5_f32);
let pixel_start = x * CHANNELS;
for c in 0..CHANNELS {
let idx = pixel_start + c;
let scaled = row[idx] * one;
row[idx] = (scaled + dither_offset)
.round()
.clamp(T::ZERO, one);
}
}
});
} else {
values
.par_chunks_mut(row_size)
.enumerate()
.for_each(|(y, row)| {
for x in 0..width {
let pixel_start = x * CHANNELS;
for c in 0..CHANNELS {
let channel_offset = (c as u32) * SEED_OFFSET;
let noise = method.compute(
(x as u32).wrapping_add(channel_offset),
(y as u32).wrapping_add(channel_offset),
);
let dither_offset =
T::cast_from(noise as f64) * T::cast_from(0.5_f32);
let idx = pixel_start + c;
let scaled = row[idx] * one;
row[idx] = (scaled + dither_offset)
.round()
.clamp(T::ZERO, one);
}
}
});
}
}
#[cfg(not(feature = "rayon"))]
pub fn dither_iter<T, I>(
values: I,
min: T,
one: T,
dither_amplitude: T,
seed: u32,
) -> Vec<T>
where
T: DitherFloat,
I: IntoIterator<Item = T>,
{
let method = Hash::new(seed);
dither_iter_with(values, min, one, dither_amplitude, &method)
}
#[cfg(feature = "rayon")]
pub fn dither_iter<T, I>(
values: I,
min: T,
one: T,
dither_amplitude: T,
seed: u32,
) -> Vec<T>
where
T: DitherFloat + Send + Sync,
I: IntoIterator<Item = T>,
I::IntoIter: Send,
{
let method = Hash::new(seed);
dither_iter_with(values, min, one, dither_amplitude, &method)
}
#[cfg(not(feature = "rayon"))]
pub fn dither_iter_with<T, I, M: LinearRng>(
values: I,
min: T,
one: T,
dither_amplitude: T,
method: &M,
) -> Vec<T>
where
T: DitherFloat,
I: IntoIterator<Item = T>,
{
values
.into_iter()
.enumerate()
.map(|(index, value)| {
dither_with(value, min, one, dither_amplitude, index as u32, method)
})
.collect()
}
#[cfg(feature = "rayon")]
pub fn dither_iter_with<T, I, M: LinearRng>(
values: I,
min: T,
one: T,
dither_amplitude: T,
method: &M,
) -> Vec<T>
where
T: DitherFloat + Send + Sync,
I: IntoIterator<Item = T>,
I::IntoIter: Send,
{
use rayon::prelude::*;
let values_vec: Vec<_> = values.into_iter().collect();
values_vec
.into_par_iter()
.enumerate()
.map(|(index, value)| {
dither_with(value, min, one, dither_amplitude, index as u32, method)
})
.collect()
}
#[cfg(not(feature = "rayon"))]
pub fn simple_dither_iter<T, I>(values: I, one: T, seed: u32) -> Vec<T>
where
T: DitherFloat + Number + CastableFrom<f32>,
I: IntoIterator<Item = T>,
{
let method = Hash::new(seed);
simple_dither_iter_with(values, one, &method)
}
#[cfg(feature = "rayon")]
pub fn simple_dither_iter<T, I>(values: I, one: T, seed: u32) -> Vec<T>
where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
I: IntoIterator<Item = T>,
I::IntoIter: Send,
{
let method = Hash::new(seed);
simple_dither_iter_with(values, one, &method)
}
#[cfg(not(feature = "rayon"))]
pub fn simple_dither_iter_with<T, I, M: LinearRng>(
values: I,
one: T,
method: &M,
) -> Vec<T>
where
T: DitherFloat + Number + CastableFrom<f32>,
I: IntoIterator<Item = T>,
{
values
.into_iter()
.enumerate()
.map(|(index, value)| {
simple_dither_with(value, one, index as u32, method)
})
.collect()
}
#[cfg(feature = "rayon")]
pub fn simple_dither_iter_with<T, I, M: LinearRng>(
values: I,
one: T,
method: &M,
) -> Vec<T>
where
T: DitherFloat + Number + CastableFrom<f32> + Send + Sync,
I: IntoIterator<Item = T>,
I::IntoIter: Send,
{
use rayon::prelude::*;
let values_vec: Vec<_> = values.into_iter().collect();
values_vec
.into_par_iter()
.enumerate()
.map(|(index, value)| {
simple_dither_with(value, one, index as u32, method)
})
.collect()
}
#[cfg(not(feature = "rayon"))]
pub fn dither_float_slice<Src, Dest>(values: &[Src], seed: u32) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64> + Copy,
{
let method = Hash::new(seed);
dither_float_slice_with(values, &method)
}
#[cfg(feature = "rayon")]
pub fn dither_float_slice<Src, Dest>(values: &[Src], seed: u32) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Copy
+ Send
+ Sync,
Dest: Send,
{
let method = Hash::new(seed);
dither_float_slice_with(values, &method)
}
#[cfg(not(feature = "rayon"))]
pub fn dither_float_slice_with<Src, Dest, M: LinearRng>(
values: &[Src],
method: &M,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64> + Copy,
{
values
.iter()
.enumerate()
.map(|(index, &value)| dither_float_with(value, index as u32, method))
.collect()
}
#[cfg(feature = "rayon")]
pub fn dither_float_slice_with<Src, Dest, M: LinearRng>(
values: &[Src],
method: &M,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Copy
+ Send
+ Sync,
Dest: Send,
{
use rayon::prelude::*;
values
.par_iter()
.enumerate()
.map(|(index, &value)| dither_float_with(value, index as u32, method))
.collect()
}
#[cfg(not(feature = "rayon"))]
pub fn dither_float_slice_2d<Src, Dest, M: SpatialRng>(
values: &[Src],
width: usize,
method: &M,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64> + Copy,
{
values
.iter()
.enumerate()
.map(|(index, &value)| {
let x = (index % width) as u32;
let y = (index / width) as u32;
dither_float_2d(value, x, y, method)
})
.collect()
}
#[cfg(feature = "rayon")]
pub fn dither_float_slice_2d<Src, Dest, M: SpatialRng>(
values: &[Src],
width: usize,
method: &M,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Copy
+ Send
+ Sync,
Dest: Send,
{
use rayon::prelude::*;
values
.par_iter()
.enumerate()
.map(|(index, &value)| {
let x = (index % width) as u32;
let y = (index / width) as u32;
dither_float_2d(value, x, y, method)
})
.collect()
}
#[cfg(not(feature = "rayon"))]
pub fn dither_float_iter<Src, Dest, I>(values: I, seed: u32) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
I: IntoIterator<Item = Src>,
{
let method = Hash::new(seed);
dither_float_iter_with(values, &method)
}
#[cfg(feature = "rayon")]
pub fn dither_float_iter<Src, Dest, I>(values: I, seed: u32) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Send
+ Sync,
Dest: Send,
I: IntoIterator<Item = Src>,
I::IntoIter: Send,
{
let method = Hash::new(seed);
dither_float_iter_with(values, &method)
}
#[cfg(not(feature = "rayon"))]
pub fn dither_float_iter_with<Src, Dest, I, M: LinearRng>(
values: I,
method: &M,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest> + DitherFloat + CastableFrom<f64>,
I: IntoIterator<Item = Src>,
{
values
.into_iter()
.enumerate()
.map(|(index, value)| dither_float_with(value, index as u32, method))
.collect()
}
#[cfg(feature = "rayon")]
pub fn dither_float_iter_with<Src, Dest, I, M: LinearRng>(
values: I,
method: &M,
) -> Vec<Dest>
where
Src: DitherFloatConversion<Dest>
+ DitherFloat
+ CastableFrom<f64>
+ Send
+ Sync,
Dest: Send,
I: IntoIterator<Item = Src>,
I::IntoIter: Send,
{
use rayon::prelude::*;
let values_vec: Vec<_> = values.into_iter().collect();
values_vec
.into_par_iter()
.enumerate()
.map(|(index, value)| dither_float_with(value, index as u32, method))
.collect()
}
pub trait DitherIteratorExt<T>: Iterator<Item = T> + Sized
where
T: DitherFloat,
{
#[cfg(not(feature = "rayon"))]
fn dither(self, min: T, one: T, dither_amplitude: T, seed: u32) -> Vec<T> {
self.enumerate()
.map(|(index, value)| {
dither(value, min, one, dither_amplitude, index as u32, seed)
})
.collect()
}
#[cfg(feature = "rayon")]
fn dither(self, min: T, one: T, dither_amplitude: T, seed: u32) -> Vec<T>
where
T: Send + Sync,
Self: Send,
Self::Item: Send,
{
dither_iter(self, min, one, dither_amplitude, seed)
}
#[cfg(not(feature = "rayon"))]
fn dither_with<M: LinearRng>(
self,
min: T,
one: T,
dither_amplitude: T,
method: &M,
) -> Vec<T> {
self.enumerate()
.map(|(index, value)| {
dither_with(
value,
min,
one,
dither_amplitude,
index as u32,
method,
)
})
.collect()
}
#[cfg(feature = "rayon")]
fn dither_with<M: LinearRng>(
self,
min: T,
one: T,
dither_amplitude: T,
method: &M,
) -> Vec<T>
where
T: Send + Sync,
Self: Send,
Self::Item: Send,
{
dither_iter_with(self, min, one, dither_amplitude, method)
}
#[cfg(not(feature = "rayon"))]
fn simple_dither(self, one: T, seed: u32) -> Vec<T>
where
T: Number + CastableFrom<f32>,
{
self.enumerate()
.map(|(index, value)| simple_dither(value, one, index as u32, seed))
.collect()
}
#[cfg(feature = "rayon")]
fn simple_dither(self, one: T, seed: u32) -> Vec<T>
where
T: Number + CastableFrom<f32> + Send + Sync,
Self: Send,
Self::Item: Send,
{
simple_dither_iter(self, one, seed)
}
#[cfg(not(feature = "rayon"))]
fn simple_dither_with<M: LinearRng>(self, one: T, method: &M) -> Vec<T>
where
T: Number + CastableFrom<f32>,
{
self.enumerate()
.map(|(index, value)| {
simple_dither_with(value, one, index as u32, method)
})
.collect()
}
#[cfg(feature = "rayon")]
fn simple_dither_with<M: LinearRng>(self, one: T, method: &M) -> Vec<T>
where
T: Number + CastableFrom<f32> + Send + Sync,
Self: Send,
Self::Item: Send,
{
simple_dither_iter_with(self, one, method)
}
}
impl<I, T> DitherIteratorExt<T> for I
where
I: Iterator<Item = T>,
T: DitherFloat,
{
}
#[cfg(feature = "rayon")]
pub trait DitherParallelIteratorExt<T>:
rayon::iter::IndexedParallelIterator<Item = T> + Sized
where
T: DitherFloat,
{
fn dither(self, min: T, one: T, dither_amplitude: T, seed: u32) -> Vec<T>
where
T: Send + Sync,
{
use rayon::prelude::*;
self.enumerate()
.map(|(index, value)| {
dither(value, min, one, dither_amplitude, index as u32, seed)
})
.collect()
}
fn dither_with<M>(
self,
min: T,
one: T,
dither_amplitude: T,
method: &M,
) -> Vec<T>
where
T: Send + Sync,
M: LinearRng + Sync,
{
use rayon::prelude::*;
self.enumerate()
.map(|(index, value)| {
dither_with(
value,
min,
one,
dither_amplitude,
index as u32,
method,
)
})
.collect()
}
fn simple_dither(self, one: T, seed: u32) -> Vec<T>
where
T: Number + CastableFrom<f32> + Send + Sync,
{
use rayon::prelude::*;
self.enumerate()
.map(|(index, value)| simple_dither(value, one, index as u32, seed))
.collect()
}
fn simple_dither_with<M>(self, one: T, method: &M) -> Vec<T>
where
T: Number + CastableFrom<f32> + Send + Sync,
M: LinearRng + Sync,
{
use rayon::prelude::*;
self.enumerate()
.map(|(index, value)| {
simple_dither_with(value, one, index as u32, method)
})
.collect()
}
}
#[cfg(feature = "rayon")]
impl<I, T> DitherParallelIteratorExt<T> for I
where
I: rayon::iter::IndexedParallelIterator<Item = T>,
T: DitherFloat,
{
}
pub mod rng {
use crate::SpatialRng;
#[inline(always)]
pub fn spatial_rng<T: SpatialRng>(method: &T, x: u32, y: u32) -> f32 {
(method.compute(x, y) + 1.0) * 0.5
}
#[inline(always)]
pub fn spatial_rng_range<T: SpatialRng>(
method: &T,
x: u32,
y: u32,
max: f32,
) -> f32 {
spatial_rng(method, x, y) * max
}
#[inline(always)]
pub fn spatial_rng_int<T: SpatialRng>(
method: &T,
x: u32,
y: u32,
max: u32,
) -> u32 {
(spatial_rng(method, x, y) * max as f32) as u32
}
}