pxls 0.1.0

A library for pixelising images
Documentation
use pxls::{
    dither_palette, get_palette, DistanceAlgorithm, OutputSettings, PaletteSettings,
};
use image::{DynamicImage, ImageReader, Rgba};
use rfd::FileDialog;
use std::env::current_dir;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread::JoinHandle;

pub enum ThreadRequest {
    GetInputImage,
    GetOutputImage,
    RenderPalette {
        input: DynamicImage,
        palette_settings: PaletteSettings,
        distance_algorithm: DistanceAlgorithm,
    },
    RenderOutput {
        input: DynamicImage,
        palette: Vec<Rgba<u8>>,
        output_settings: OutputSettings,
        distance_algorithm: DistanceAlgorithm,
    },
}

pub enum ThreadResult {
    ReadInFile(DynamicImage),
    GotDestination(PathBuf),
    RenderedPalette(DynamicImage, Vec<Rgba<u8>>),
    RenderedImage {
        input: DynamicImage,
        palette: Vec<Rgba<u8>>,
        output: DynamicImage,
    },
}

#[allow(clippy::type_complexity)]
pub fn start_worker_thread() -> (
    JoinHandle<()>,
    Sender<ThreadRequest>,
    Receiver<ThreadResult>,
    Receiver<(u32, u32)>,
    Arc<AtomicBool>,
) {
    let (req_tx, req_rx) = channel();
    let (res_tx, res_rx) = channel();
    let (prog_tx, prog_rx) = channel();
    let should_stop = Arc::new(AtomicBool::new(false));
    let ret_should_stop = should_stop.clone();

    let handle = std::thread::spawn(move || loop {
        if should_stop.load(Ordering::Relaxed) {
            break;
        }

        for req in req_rx.try_iter() {
            match req {
                ThreadRequest::GetInputImage => {
                    if let Some(file) = FileDialog::new()
                        .set_directory(current_dir().unwrap_or_else(|_| "/".into()))
                        .pick_file()
                    {
                        match ImageReader::open(file) {
                            Ok(img) => match img.decode() {
                                Ok(img) => {
                                    res_tx.send(ThreadResult::ReadInFile(img)).unwrap();
                                }
                                Err(e) => {
                                    eprintln!("Error decoding image: {e:?}");
                                }
                            },
                            Err(e) => {
                                eprintln!("Error reading image file: {e:?}");
                            }
                        }
                    }
                }
                ThreadRequest::RenderPalette {
                    input,
                    palette_settings,
                    distance_algorithm,
                } => {
                    let palette =
                        get_palette(&input, palette_settings, distance_algorithm, &prog_tx, should_stop.clone());

                    res_tx
                        .send(ThreadResult::RenderedPalette(input, palette))
                        .unwrap();
                }
                ThreadRequest::RenderOutput {
                    input,
                    palette,
                    output_settings,
                    distance_algorithm,
                } => {
                    let output = dither_palette(
                        &input,
                        &palette,
                        distance_algorithm,
                        output_settings,
                        &prog_tx,
                        should_stop.clone()
                    );

                    res_tx
                        .send(ThreadResult::RenderedImage {
                            input,
                            palette,
                            output,
                        })
                        .unwrap();
                },
                ThreadRequest::GetOutputImage => {
                    if let Some(file) = FileDialog::new()
                        .add_filter("Image Files", &["png", "jpg"])
                        .set_directory(current_dir().unwrap_or_else(|_| "/".into()))
                        .save_file() {
                        res_tx.send(ThreadResult::GotDestination(file)).unwrap();
                    }
                }
            }
        }
    });

    (handle, req_tx, res_rx, prog_rx, ret_should_stop)
}