imageslapper 0.2.0

A tool for processing and manipulating images with parallelism and advanced rendering.
Documentation
use std::time::{Duration, Instant};
use image::{DynamicImage, ImageFormat};
use std::path::Path;

/// A trait that defines a drawable object.
pub trait Drawable {
    fn draw(&self, image: &mut DynamicImage);
}

/// A struct that wraps around an image and provides drawing functionality.
pub struct ImageWrapper {
    image: DynamicImage,
}

/// A wrapper around the image to provide drawing functionality.
impl ImageWrapper {
    /// Creates a new ImageWrapper with the given image.
    pub fn new(image: DynamicImage) -> Self {
        ImageWrapper { image }
    }

    /// Draws the given drawable on the image.
    pub fn draw<T: Drawable>(&mut self, drawable: &T) {
        drawable.draw(&mut self.image);
    }

    /// Returns a reference to the image.
    pub fn get_image(&self) -> &DynamicImage {
        &self.image
    }

    /// Saves the image to the specified output path and returns the elapsed time.
    /// Automatically handles format conversion for JPEG files.
    pub fn save_image(&self, output_path: &str) -> Result<Duration, Box<dyn std::error::Error>> {
        let start = Instant::now();
        
        // Determine the format from the file extension
        let path = Path::new(output_path);
        let extension = path.extension()
            .and_then(|ext| ext.to_str())
            .map(|ext| ext.to_lowercase());

        match extension.as_deref() {
            Some("jpg") | Some("jpeg") => {
                // Convert RGBA to RGB for JPEG format
                let rgb_image = match &self.image {
                    DynamicImage::ImageRgba8(rgba_img) => {
                        // Convert RGBA to RGB by removing alpha channel
                        DynamicImage::ImageRgb8(
                            image::ImageBuffer::from_fn(
                                rgba_img.width(),
                                rgba_img.height(),
                                |x, y| {
                                    let rgba_pixel = rgba_img.get_pixel(x, y);
                                    image::Rgb([rgba_pixel[0], rgba_pixel[1], rgba_pixel[2]])
                                }
                            )
                        )
                    }
                    _ => self.image.clone(), // For other formats, use as-is
                };
                rgb_image.save_with_format(output_path, ImageFormat::Jpeg)?;
            }
            Some("png") => {
                // PNG supports RGBA, so save directly
                self.image.save_with_format(output_path, ImageFormat::Png)?;
            }
            Some("bmp") => {
                self.image.save_with_format(output_path, ImageFormat::Bmp)?;
            }
            Some("gif") => {
                self.image.save_with_format(output_path, ImageFormat::Gif)?;
            }
            Some("tiff") | Some("tif") => {
                self.image.save_with_format(output_path, ImageFormat::Tiff)?;
            }
            _ => {
                // Default behavior: let the image crate infer the format
                self.image.save(output_path)?;
            }
        }
        
        Ok(start.elapsed())
    }

    /// Alternative method: Save with explicit format conversion
    pub fn save_image_as_rgb(&self, output_path: &str) -> Result<Duration, Box<dyn std::error::Error>> {
        let start = Instant::now();
        
        // Always convert to RGB before saving
        let rgb_image = self.image.to_rgb8();
        let dynamic_rgb = DynamicImage::ImageRgb8(rgb_image);
        dynamic_rgb.save(output_path)?;
        
        Ok(start.elapsed())
    }

    /// Save with a specific format, handling RGBA->RGB conversion when needed
    pub fn save_with_format(&self, output_path: &str, format: ImageFormat) -> Result<Duration, Box<dyn std::error::Error>> {
        let start = Instant::now();
        
        match format {
            ImageFormat::Jpeg => {
                // Convert to RGB for JPEG
                let rgb_image = self.image.to_rgb8();
                let dynamic_rgb = DynamicImage::ImageRgb8(rgb_image);
                dynamic_rgb.save_with_format(output_path, format)?;
            }
            _ => {
                // For other formats, save directly
                self.image.save_with_format(output_path, format)?;
            }
        }
        
        Ok(start.elapsed())
    }
}