use std::fmt::Display; use image::{DynamicImage, imageops::FilterType}; use crate::{pixeldim::PixelDim, transformers::ImageTransformer}; #[derive(Debug, Clone, PartialEq)] pub struct MaxDimTransformer { w: PixelDim, h: PixelDim, } impl MaxDimTransformer { pub fn new(w: PixelDim, h: PixelDim) -> Self { Self { w, h } } fn target_dim(&self, img_width: u32, img_height: u32) -> (u32, u32) { let max_width = match self.w { PixelDim::Pixels(w) => Some(w), PixelDim::WidthPercent(pct) => Some(((img_width as f32) * pct / 100.0) as u32), PixelDim::HeightPercent(_) => None, }; let max_height = match self.h { PixelDim::Pixels(h) => Some(h), PixelDim::HeightPercent(pct) => Some(((img_height as f32) * pct / 100.0) as u32), PixelDim::WidthPercent(_) => None, }; if max_width.map(|x| img_width <= x).unwrap_or(true) && max_height.map(|x| img_height <= x).unwrap_or(true) { return (img_width, img_height); } let width_ratio = max_width .map(|x| x as f32 / img_width as f32) .unwrap_or(1.0); let height_ratio = max_height .map(|x| x as f32 / img_height as f32) .unwrap_or(1.0); let ratio = width_ratio.min(height_ratio); ( (img_width as f32 * ratio) as u32, (img_height as f32 * ratio) as u32, ) } } impl Display for MaxDimTransformer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "maxdim({},{})", self.w, self.h) } } impl ImageTransformer for MaxDimTransformer { fn parse_args(args: &str) -> Result { let args: Vec<&str> = args.split(",").collect(); if args.len() != 2 { return Err(format!("expected 2 args, got {}", args.len())); } let w = args[0].parse::()?; let h = args[1].parse::()?; Ok(Self { w, h }) } fn transform(&self, input: &mut DynamicImage) { let (img_width, img_height) = (input.width(), input.height()); let (target_width, target_height) = self.target_dim(img_width, img_height); // Only resize if needed if target_width != img_width || target_height != img_height { *input = input.resize(target_width, target_height, FilterType::Lanczos3); } } }