use crate::error::Result;
use crate::types::{ImageOutputFormat, ProtectionContext, ProtectionLevel, DEFAULT_OUTPUT_FORMAT};
use image::DynamicImage;
use image::ImageEncoder;
use std::borrow::Cow;
pub trait Protector: Send + Sync {
fn apply<'a>(
&self,
img: &'a DynamicImage,
ctx: &ProtectionContext,
) -> Result<Cow<'a, DynamicImage>>;
fn apply_bytes(&self, img_bytes: &[u8], ctx: &ProtectionContext) -> Result<Vec<u8>> {
if !self.modifies_pixels() {
return Ok(img_bytes.to_vec());
}
let format = ctx.input_format().unwrap_or_else(|| {
ImageOutputFormat::from_magic_bytes(img_bytes).unwrap_or(DEFAULT_OUTPUT_FORMAT)
});
let img = image::load_from_memory(img_bytes)?;
let processed = self.apply(&img, ctx)?;
let quality = ctx.jpeg_quality();
let mut bytes = Vec::new();
match format {
ImageOutputFormat::Png => {
let encoder = image::codecs::png::PngEncoder::new(&mut bytes);
encoder.write_image(
&processed.to_rgba8(),
processed.width(),
processed.height(),
image::ExtendedColorType::Rgba8,
)?;
}
ImageOutputFormat::Jpeg => {
let encoder =
image::codecs::jpeg::JpegEncoder::new_with_quality(&mut bytes, quality);
encoder.write_image(
&processed.to_rgb8(),
processed.width(),
processed.height(),
image::ExtendedColorType::Rgb8,
)?;
}
ImageOutputFormat::WebP => {
let encoder = image::codecs::webp::WebPEncoder::new_lossless(&mut bytes);
encoder.write_image(
&processed.to_rgba8(),
processed.width(),
processed.height(),
image::ExtendedColorType::Rgba8,
)?;
}
}
Ok(bytes)
}
fn name(&self) -> &'static str;
fn protection_level(&self) -> ProtectionLevel;
fn estimated_latency_ms(&self) -> u32;
fn modifies_pixels(&self) -> bool {
true
}
fn requires_bytes_level(&self) -> bool {
false
}
}