diff --git a/pixstrip-core/src/operations/mod.rs b/pixstrip-core/src/operations/mod.rs index afdd189..8243176 100644 --- a/pixstrip-core/src/operations/mod.rs +++ b/pixstrip-core/src/operations/mod.rs @@ -1,3 +1,5 @@ +pub mod resize; + use serde::{Deserialize, Serialize}; use crate::types::{Dimensions, ImageFormat, QualityPreset}; diff --git a/pixstrip-core/src/operations/resize.rs b/pixstrip-core/src/operations/resize.rs new file mode 100644 index 0000000..5423445 --- /dev/null +++ b/pixstrip-core/src/operations/resize.rs @@ -0,0 +1,60 @@ +use fast_image_resize::{images::Image, Resizer, ResizeOptions, ResizeAlg, FilterType}; + +use crate::error::{PixstripError, Result}; +use crate::types::Dimensions; + +use super::ResizeConfig; + +pub fn resize_image( + src: &image::DynamicImage, + config: &ResizeConfig, +) -> Result { + let original = Dimensions { + width: src.width(), + height: src.height(), + }; + let target = config.target_for(original); + + if target.width == original.width && target.height == original.height { + return Ok(src.clone()); + } + + let src_rgba = src.to_rgba8(); + let (src_w, src_h) = (src_rgba.width(), src_rgba.height()); + + let src_image = Image::from_vec_u8( + src_w, + src_h, + src_rgba.into_raw(), + fast_image_resize::PixelType::U8x4, + ) + .map_err(|e| PixstripError::Processing { + operation: "resize".into(), + reason: format!("Failed to create source image: {}", e), + })?; + + let mut dst_image = Image::new( + target.width, + target.height, + fast_image_resize::PixelType::U8x4, + ); + + let mut resizer = Resizer::new(); + let options = ResizeOptions::new().resize_alg(ResizeAlg::Convolution(FilterType::Lanczos3)); + + resizer + .resize(&src_image, &mut dst_image, &options) + .map_err(|e| PixstripError::Processing { + operation: "resize".into(), + reason: format!("Resize failed: {}", e), + })?; + + let result_buf: image::RgbaImage = + image::ImageBuffer::from_raw(target.width, target.height, dst_image.into_vec()) + .ok_or_else(|| PixstripError::Processing { + operation: "resize".into(), + reason: "Failed to create output image buffer".into(), + })?; + + Ok(image::DynamicImage::ImageRgba8(result_buf)) +} diff --git a/pixstrip-core/tests/resize_tests.rs b/pixstrip-core/tests/resize_tests.rs new file mode 100644 index 0000000..11f4035 --- /dev/null +++ b/pixstrip-core/tests/resize_tests.rs @@ -0,0 +1,61 @@ +use pixstrip_core::operations::resize::resize_image; +use pixstrip_core::operations::ResizeConfig; +use pixstrip_core::types::Dimensions; + +fn create_test_image(width: u32, height: u32) -> image::DynamicImage { + let img = image::RgbImage::from_fn(width, height, |x, y| { + image::Rgb([(x % 256) as u8, (y % 256) as u8, 128]) + }); + image::DynamicImage::ImageRgb8(img) +} + +#[test] +fn resize_by_width() { + let img = create_test_image(4000, 3000); + let config = ResizeConfig::ByWidth(1200); + let result = resize_image(&img, &config).unwrap(); + assert_eq!(result.width(), 1200); + assert_eq!(result.height(), 900); +} + +#[test] +fn resize_by_height() { + let img = create_test_image(4000, 3000); + let config = ResizeConfig::ByHeight(600); + let result = resize_image(&img, &config).unwrap(); + assert_eq!(result.width(), 800); + assert_eq!(result.height(), 600); +} + +#[test] +fn resize_fit_in_box() { + let img = create_test_image(4000, 3000); + let config = ResizeConfig::FitInBox { + max: Dimensions { width: 1920, height: 1080 }, + allow_upscale: false, + }; + let result = resize_image(&img, &config).unwrap(); + assert_eq!(result.width(), 1440); + assert_eq!(result.height(), 1080); +} + +#[test] +fn resize_no_upscale() { + let img = create_test_image(800, 600); + let config = ResizeConfig::FitInBox { + max: Dimensions { width: 1920, height: 1080 }, + allow_upscale: false, + }; + let result = resize_image(&img, &config).unwrap(); + assert_eq!(result.width(), 800); + assert_eq!(result.height(), 600); +} + +#[test] +fn resize_exact() { + let img = create_test_image(4000, 3000); + let config = ResizeConfig::Exact(Dimensions { width: 1080, height: 1080 }); + let result = resize_image(&img, &config).unwrap(); + assert_eq!(result.width(), 1080); + assert_eq!(result.height(), 1080); +}