use crate::{BitPixel, Image, Pixel};
use num_traits::{bounds::UpperBounded, AsPrimitive, FromPrimitive, Zero};
use std::marker::PhantomData;
pub trait Filter {
type Input: Pixel;
type Output: Pixel;
fn apply_pixel(
&self,
image: &Image<Self::Input>,
x: u32,
y: u32,
pixel: Self::Input,
) -> Self::Output;
fn apply_image(&self, image: Image<Self::Input>) -> Image<Self::Output> {
image.map_image_with_coords(|image, x, y, pixel| self.apply_pixel(image, x, y, pixel))
}
}
pub struct Brightness<P: Pixel> {
pub factor: f64,
_marker: PhantomData<P>,
}
impl<P: Pixel> Brightness<P>
where
P::Subpixel: AsPrimitive<f64> + FromPrimitive + Ord + UpperBounded + Zero,
{
#[must_use]
pub const fn new(factor: f64) -> Self {
Self {
factor,
_marker: PhantomData,
}
}
}
impl<P: Pixel> Filter for Brightness<P>
where
P::Subpixel: AsPrimitive<f64> + FromPrimitive + Ord + UpperBounded + Zero,
{
type Input = P;
type Output = P;
fn apply_pixel(
&self,
_image: &Image<Self::Input>,
_x: u32,
_y: u32,
pixel: Self::Input,
) -> Self::Output {
pixel.map_subpixels(
|c| {
let max = P::Subpixel::max_value();
let c = self.factor.mul_add(max.as_(), c.as_());
P::Subpixel::from_f64(c)
.expect("out of bounds")
.clamp(P::Subpixel::zero(), max)
},
|alpha| alpha,
)
}
}
pub struct Mask<F: Filter> {
pub filter: F,
pub mask: Image<BitPixel>,
}