moggu 0.1.1

A lightning-fast TUI image processing tool with 21+ professional filters in under 200ms
Documentation
use image::{DynamicImage, ImageBuffer, Rgb};
use rayon::{iter::{IndexedParallelIterator, ParallelIterator}, slice::ParallelSliceMut};
use crate::{ProgressSender, send_progress};

pub fn rotate90(img: &DynamicImage, progress_tx: Option<ProgressSender>) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
    let rgb_img = img.to_rgb8();
    let (width, height) = rgb_img.dimensions();
    let mut out_buffer = ImageBuffer::new(height, width);
    
    send_progress(&progress_tx, 0.0);

    let in_pixels = rgb_img.as_raw();

    out_buffer.as_mut()
        .par_chunks_exact_mut((height * 3) as usize)
        .enumerate()
        .for_each(|(new_y, out_row)| {
            for new_x in 0..height {
                let old_x = new_y as u32;
                let old_y = height -1 - new_x;

                let in_idx = ((old_y * width + old_x) * 3) as usize;
                let out_idx = (new_x * 3) as usize;

                out_row[out_idx] = in_pixels[in_idx];
                out_row[out_idx + 1] = in_pixels[in_idx + 1];
                out_row[out_idx + 2] = in_pixels[in_idx + 2];
            }
        });

    send_progress(&progress_tx, 1.0);
    out_buffer
}



pub fn rotate180(img: &DynamicImage, progress_tx: Option<ProgressSender>) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
    let rgb_img = img.to_rgb8();
    let (width, height) = rgb_img.dimensions();
    let mut out_buffer = ImageBuffer::new(width, height);
    
    send_progress(&progress_tx, 0.0);

    let in_pixels = rgb_img.as_raw();

    out_buffer.as_mut()
        .par_chunks_exact_mut((width * 3) as usize)
        .enumerate()
        .for_each(|(y, out_row)| {

            let old_y = height - 1 - y as u32;

            for x in 0..width {
                let old_x = width - 1 - x;

                let in_idx = ((old_y * width + old_x) * 3) as usize;
                let out_idx = (x * 3) as usize;

                out_row[out_idx] = in_pixels[in_idx];
                out_row[out_idx + 1] = in_pixels[in_idx + 1];
                out_row[out_idx + 2] = in_pixels[in_idx + 2];
            }

        });

    send_progress(&progress_tx, 1.0);
    out_buffer
}



pub fn rotate270(img: &DynamicImage, progress_tx: Option<ProgressSender>) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
    let rgb_img = img.to_rgb8();
    let (width, height) = rgb_img.dimensions();
    let mut out_buffer = ImageBuffer::new(height, width);
    send_progress(&progress_tx, 0.0);
    
    let in_pixels = rgb_img.as_raw();

    out_buffer.as_mut().par_chunks_exact_mut((height * 3) as usize)
        .enumerate()
        .for_each(|(new_y, out_row)| {
            
            for new_x in 0..height {

                let old_x = height - 1 - new_y as u32;
                let old_y = new_x;
                
                if old_x < height && old_y < width {
                    let in_idx = ((old_y * width + old_x) * 3) as usize;
                    let out_idx = (new_x * 3) as usize;
                    
                    out_row[out_idx] = in_pixels[in_idx];
                    out_row[out_idx + 1] = in_pixels[in_idx + 1];
                    out_row[out_idx + 2] = in_pixels[in_idx + 2];
                }
            }
        });

    send_progress(&progress_tx, 1.0);
    out_buffer
}



pub fn flip_horizontal(img: &DynamicImage, progress_tx: Option<ProgressSender>) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
    let rgb_img = img.to_rgb8();
    let (width, height) = rgb_img.dimensions();
    let mut output = ImageBuffer::new(width, height);
    
    send_progress(&progress_tx, 0.0);
    
    let in_pixels = rgb_img.as_raw();

    output.as_mut().par_chunks_exact_mut((width * 3) as usize)
        .enumerate()
        .for_each(|(y, out_row)| {
            
            for x in 0..width {
                let old_x = width - 1 - x;
                
                let in_idx = ((y as u32 * width + old_x) * 3) as usize;
                let out_idx = (x * 3) as usize;
                
                out_row[out_idx] = in_pixels[in_idx];
                out_row[out_idx + 1] = in_pixels[in_idx + 1];
                out_row[out_idx + 2] = in_pixels[in_idx + 2];
            }
        });

    send_progress(&progress_tx, 1.0);
    output
}



pub fn flip_vertical(img: &DynamicImage, progress_tx: Option<ProgressSender>) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
    let rgb_img = img.to_rgb8();
    let (width, height) = rgb_img.dimensions();
    let mut output = ImageBuffer::new(width, height);
    
    send_progress(&progress_tx, 0.0);
    
    let in_pixels = rgb_img.as_raw();

    output.as_mut().par_chunks_exact_mut((width * 3) as usize)
        .enumerate()
        .for_each(|(y, out_row)| {
            let old_y = height - 1 - y as u32;
            let src_start = (old_y * width * 3) as usize;
            out_row.copy_from_slice(&in_pixels[src_start..src_start + (width * 3) as usize]);
        });

    send_progress(&progress_tx, 1.0);
    output
}