use crate::error::Result; use image::DynamicImage; #[derive(Debug, Clone, Copy)] pub enum ColorGrade { CinematicDark, CoolTones, WarmTones, Desaturated, HighContrast, None, } pub struct PostProcessor { pub color_grade: ColorGrade, pub contrast: f32, pub brightness: f32, pub saturation: f32, } impl Default for PostProcessor { fn default() -> Self { Self { color_grade: ColorGrade::CinematicDark, contrast: 1.1, brightness: 0.95, saturation: 1.1, } } } impl PostProcessor { pub fn new(color_grade: ColorGrade) -> Self { Self { color_grade, ..Default::default() } } /// Appliquer tous les traitements de post-processing pub fn process(&self, img: DynamicImage) -> Result { let mut result = img; // 1. Appliquer le grade de couleur result = self.apply_color_grade(result)?; // 2. Ajuster contraste/luminosité result = self.adjust_contrast_brightness(result)?; // 3. Ajuster saturation result = self.adjust_saturation(result)?; Ok(result) } fn apply_color_grade(&self, img: DynamicImage) -> Result { match self.color_grade { ColorGrade::CinematicDark => self.apply_cinematic_dark(img), ColorGrade::CoolTones => self.apply_cool_tones(img), ColorGrade::WarmTones => self.apply_warm_tones(img), ColorGrade::Desaturated => self.apply_desaturated(img), ColorGrade::HighContrast => self.apply_high_contrast(img), ColorGrade::None => Ok(img), } } fn apply_cinematic_dark(&self, img: DynamicImage) -> Result { let rgba = img.to_rgba8(); let mut output = rgba.clone(); for pixel in output.pixels_mut() { let [r, g, b, a] = pixel.0; // Ajouter une teinte bleuée et assombrir pixel.0 = [ (r as f32 * 0.95) as u8, (g as f32 * 0.98) as u8, (b as f32 * 1.05).min(255.0) as u8, a, ]; } Ok(DynamicImage::ImageRgba8(output)) } fn apply_cool_tones(&self, img: DynamicImage) -> Result { let rgba = img.to_rgba8(); let mut output = rgba.clone(); for pixel in output.pixels_mut() { let [r, g, b, a] = pixel.0; pixel.0 = [ (r as f32 * 0.9) as u8, (g as f32 * 0.98) as u8, (b as f32 * 1.1).min(255.0) as u8, a, ]; } Ok(DynamicImage::ImageRgba8(output)) } fn apply_warm_tones(&self, img: DynamicImage) -> Result { let rgba = img.to_rgba8(); let mut output = rgba.clone(); for pixel in output.pixels_mut() { let [r, g, b, a] = pixel.0; pixel.0 = [ (r as f32 * 1.1).min(255.0) as u8, (g as f32 * 1.05).min(255.0) as u8, (b as f32 * 0.9) as u8, a, ]; } Ok(DynamicImage::ImageRgba8(output)) } fn apply_desaturated(&self, img: DynamicImage) -> Result { let rgba = img.to_rgba8(); let mut output = rgba.clone(); for pixel in output.pixels_mut() { let [r, g, b, a] = pixel.0; let gray = ((r as f32 + g as f32 + b as f32) / 3.0) as u8; // 70% saturé = 30% gris let sat = 0.7; pixel.0 = [ ((r as f32 * sat + gray as f32 * (1.0 - sat)) as u8), ((g as f32 * sat + gray as f32 * (1.0 - sat)) as u8), ((b as f32 * sat + gray as f32 * (1.0 - sat)) as u8), a, ]; } Ok(DynamicImage::ImageRgba8(output)) } fn apply_high_contrast(&self, img: DynamicImage) -> Result { let rgba = img.to_rgba8(); let mut output = rgba.clone(); for pixel in output.pixels_mut() { let [r, g, b, a] = pixel.0; // Formule: (val - 128) * 1.5 + 128 let contrast = 1.5; let offset = 128.0; pixel.0 = [ ((r as f32 - offset) * contrast + offset).clamp(0.0, 255.0) as u8, ((g as f32 - offset) * contrast + offset).clamp(0.0, 255.0) as u8, ((b as f32 - offset) * contrast + offset).clamp(0.0, 255.0) as u8, a, ]; } Ok(DynamicImage::ImageRgba8(output)) } fn adjust_contrast_brightness(&self, img: DynamicImage) -> Result { let rgba = img.to_rgba8(); let mut output = rgba.clone(); for pixel in output.pixels_mut() { let [r, g, b, a] = pixel.0; pixel.0 = [ ((r as f32 - 128.0) * self.contrast + 128.0 + self.brightness * 255.0) .clamp(0.0, 255.0) as u8, ((g as f32 - 128.0) * self.contrast + 128.0 + self.brightness * 255.0) .clamp(0.0, 255.0) as u8, ((b as f32 - 128.0) * self.contrast + 128.0 + self.brightness * 255.0) .clamp(0.0, 255.0) as u8, a, ]; } Ok(DynamicImage::ImageRgba8(output)) } fn adjust_saturation(&self, img: DynamicImage) -> Result { let rgba = img.to_rgba8(); let mut output = rgba.clone(); for pixel in output.pixels_mut() { let [r, g, b, a] = pixel.0; let gray = (r as f32 + g as f32 + b as f32) / 3.0; pixel.0 = [ (r as f32 + (gray - r as f32) * (1.0 - self.saturation)).clamp(0.0, 255.0) as u8, (g as f32 + (gray - g as f32) * (1.0 - self.saturation)).clamp(0.0, 255.0) as u8, (b as f32 + (gray - b as f32) * (1.0 - self.saturation)).clamp(0.0, 255.0) as u8, a, ]; } Ok(DynamicImage::ImageRgba8(output)) } }