use image::GenericImageView; use image::{ImageFormat, imageops::FilterType}; use crate::image_errors::ImageError; /// Validate image data and ensure it's a valid image pub(crate) fn validate_image(data: &[u8], max_size: usize) -> Result<(), ImageError> { if data.len() > max_size { return Err(ImageError::SizeExceedsMaximum { size: data.len(), max: max_size, }); } // Try to load the image to ensure it's valid image::load_from_memory(data).map_err(|e| ImageError::InvalidImageData(e.to_string()))?; Ok(()) } /// Process avatar image: validate 1:1 aspect ratio, resize to 400x400, convert to PNG pub(crate) fn process_avatar(data: &[u8]) -> Result, ImageError> { // Load the image let img = image::load_from_memory(data).map_err(|e| ImageError::AvatarLoadFailed(e.to_string()))?; let (width, height) = img.dimensions(); // Validate 1:1 aspect ratio (allow 5% deviation) let aspect_ratio = width as f32 / height as f32; if (aspect_ratio - 1.0).abs() > 0.05 { return Err(ImageError::InvalidAvatarAspectRatio { width, height }); } // Resize to 400x400 if needed let resized = if width != 400 || height != 400 { img.resize_exact(400, 400, FilterType::Lanczos3) } else { img }; // Convert to PNG let mut png_buffer = std::io::Cursor::new(Vec::new()); resized .write_to(&mut png_buffer, ImageFormat::Png) .map_err(|e| ImageError::AvatarEncodeFailed(e.to_string()))?; Ok(png_buffer.into_inner()) } /// Process banner image: validate 16:9 aspect ratio, resize to 1600x900, convert to PNG pub(crate) fn process_banner(data: &[u8]) -> Result, ImageError> { // Load the image let img = image::load_from_memory(data).map_err(|e| ImageError::BannerLoadFailed(e.to_string()))?; let (width, height) = img.dimensions(); // Validate 16:9 aspect ratio (allow 10% deviation) let aspect_ratio = width as f32 / height as f32; let expected_ratio = 16.0 / 9.0; if (aspect_ratio - expected_ratio).abs() / expected_ratio > 0.10 { return Err(ImageError::InvalidBannerAspectRatio { width, height }); } // Resize to 1600x900 if needed let resized = if width != 1600 || height != 900 { img.resize_exact(1600, 900, FilterType::Lanczos3) } else { img }; // Convert to PNG let mut png_buffer = std::io::Cursor::new(Vec::new()); resized .write_to(&mut png_buffer, ImageFormat::Png) .map_err(|e| ImageError::BannerEncodeFailed(e.to_string()))?; Ok(png_buffer.into_inner()) } /// Process event header image: validate 3:1 aspect ratio, resize to 1500x500, convert to PNG pub(crate) fn process_event_header(data: &[u8]) -> Result, ImageError> { // Load the image let img = image::load_from_memory(data) .map_err(|e| ImageError::EventHeaderLoadFailed(e.to_string()))?; let (width, height) = img.dimensions(); // Validate 3:1 aspect ratio (allow 10% deviation) let aspect_ratio = width as f32 / height as f32; let expected_ratio = 3.0 / 1.0; if (aspect_ratio - expected_ratio).abs() / expected_ratio > 0.10 { return Err(ImageError::InvalidEventHeaderAspectRatio { width, height }); } // Resize to 1500x500 if needed let resized = if width != 1500 || height != 500 { img.resize_exact(1500, 500, FilterType::Lanczos3) } else { img }; // Convert to PNG let mut png_buffer = std::io::Cursor::new(Vec::new()); resized .write_to(&mut png_buffer, ImageFormat::Png) .map_err(|e| ImageError::EventHeaderEncodeFailed(e.to_string()))?; Ok(png_buffer.into_inner()) } /// Process event thumbnail image: validate 1:1 aspect ratio, resize within 512x512 to 1024x1024, convert to PNG pub(crate) fn process_event_thumbnail(data: &[u8]) -> Result, ImageError> { // Load the image let img = image::load_from_memory(data) .map_err(|e| ImageError::ThumbnailLoadFailed(e.to_string()))?; let (width, height) = img.dimensions(); // Validate 1:1 aspect ratio (allow 5% deviation) let aspect_ratio = width as f32 / height as f32; if (aspect_ratio - 1.0).abs() > 0.05 { return Err(ImageError::InvalidThumbnailAspectRatio { width, height }); } // Validate minimum size of 512x512 if width < 512 || height < 512 { return Err(ImageError::ThumbnailTooSmall { width, height }); } // Resize to maximum 1024x1024 if larger, otherwise keep original size let resized = if width > 1024 || height > 1024 { img.resize_exact(1024, 1024, FilterType::Lanczos3) } else { // Keep original size if within bounds (512-1024) img }; // Convert to PNG let mut png_buffer = std::io::Cursor::new(Vec::new()); resized .write_to(&mut png_buffer, ImageFormat::Png) .map_err(|e| ImageError::ThumbnailEncodeFailed(e.to_string()))?; Ok(png_buffer.into_inner()) }