use zune_core::bit_depth::BitType;
use zune_core::colorspace::ColorSpace;
use zune_image::errors::ImageErrors;
use zune_image::image::Image;
use zune_image::traits::OperationsTrait;
#[derive(Default)]
pub struct Contrast {
contrast: f32
}
impl Contrast {
#[must_use]
pub fn new(contrast: f32) -> Contrast {
Contrast { contrast }
}
}
impl OperationsTrait for Contrast {
fn name(&self) -> &'static str {
"contrast"
}
fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
let depth = image.depth();
for channel in image.channels_mut(true) {
match depth.bit_type() {
BitType::U8 => contrast_u8(channel.reinterpret_as_mut::<u8>()?, self.contrast),
d => return Err(ImageErrors::ImageOperationNotImplemented(self.name(), d))
}
}
Ok(())
}
fn supported_colorspaces(&self) -> &'static [ColorSpace] {
&[
ColorSpace::RGBA,
ColorSpace::RGB,
ColorSpace::LumaA,
ColorSpace::Luma
]
}
fn supported_types(&self) -> &'static [BitType] {
&[BitType::U8]
}
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub fn contrast_u8(channel: &mut [u8], contrast: f32) {
let factor = (259.0 * (contrast + 255.0)) / (255.0 * (259.0 - contrast));
for pix in channel {
let float_pix = f32::from(*pix);
let new_val = ((factor * (float_pix - 128.0)) + 128.0).clamp(0.0, 255.0);
*pix = new_val as u8;
}
}