use std::{ sync::{mpsc, Arc}, thread, }; use eframe::egui::{self, TextureOptions}; use image::{DynamicImage, Pixel, Rgb, Rgb32FImage}; use nalgebra::{ComplexField, Const, Matrix, OMatrix, SMatrix, Vector3, Vector4}; use rayon::prelude::*; use image::EncodableLayout; struct ProcessorInstruction { img: Arc, color_matrix: SMatrix, pos_matrix: SMatrix, ret: oneshot::Sender<(egui::TextureHandle, (f32, f32))>, ctx: egui::Context, handle: Option, realloc: bool, } pub struct Processor { channel: mpsc::Sender, } impl Processor { pub fn init() -> Self { let (tx, rx) = mpsc::channel(); thread::spawn(|| { let mut working_img = DynamicImage::new_rgb32f(1, 1); for ProcessorInstruction { img, color_matrix, pos_matrix, ret, ctx, handle, realloc, } in rx { let (width, height) = img.dimensions(); if realloc { working_img = DynamicImage::new_rgb32f(width, height); } let do_pos_trans = !pos_matrix.is_identity(f32::EPSILON) && pos_matrix.is_invertible(); let do_color_trans = !color_matrix.is_identity(f32::EPSILON); let mut inv_pos_mat = OMatrix::, Const<3>>::identity(); if do_pos_trans { inv_pos_mat = pos_matrix.try_inverse().unwrap(); let mut cols_mut = inv_pos_mat.column_part_mut(2, 2); *(cols_mut.get_mut(0).unwrap()) *= width as f32; *(cols_mut.get_mut(1).unwrap()) *= height as f32; } { let working_img = working_img.as_mut_rgb32f().unwrap(); match (do_pos_trans, do_color_trans) { (true, true) => { working_img .par_enumerate_pixels_mut() .for_each(|(x, y, px)| { let org_pos = inv_pos_mat * Vector3::new(x as f32, y as f32, 1.0); let Rgb([r, g, b]) = *img .get_pixel_checked( org_pos.x.abs() as u32 % width, org_pos.y.abs() as u32 % height, ) .unwrap_or(&Rgb([0., 0., 0.])); *px = Rgb::from(Into::<[f32; 3]>::into( color_matrix * Vector4::new(r, g, b, 1.0), )); }); } (true, false) => { working_img .par_enumerate_pixels_mut() .for_each(|(x, y, px)| { let r = inv_pos_mat * Vector3::new(x as f32, y as f32, 1.0); *px = *img .get_pixel_checked( r.x.abs() as u32 % width, r.y.abs() as u32 % height, ) .unwrap_or(&Rgb([0., 0., 0.])) }); } (false, true) => { working_img .par_enumerate_pixels_mut() .for_each(|(x, y, px)| { let Rgb([r, g, b]) = *img.get_pixel(x, y); *px = Rgb::from(Into::<[f32; 3]>::into( color_matrix * Vector4::new(r, g, b, 1.0), )); }); } (false, false) => { *working_img = (*img).clone(); } } } let color_image = egui::ColorImage::from_rgb( [working_img.width() as usize, working_img.height() as usize], working_img.to_rgb8().as_bytes(), ); let handle = if let Some(mut handle) = handle { handle.set(color_image, TextureOptions::default()); handle } else { ctx.load_texture("res_tex", color_image, TextureOptions::default()) }; ret.send((handle, (width as f32, height as f32))).unwrap(); // (color_matrix.is_identity(f32::EPSILON), pos_matrix.is_identity(f32::EPSILON)) } }); Self { channel: tx } } pub fn exec( &self, img: Arc, color_matrix: SMatrix, pos_matrix: SMatrix, ctx: egui::Context, handle: Option, realloc: bool, ) -> oneshot::Receiver<(egui::TextureHandle, (f32, f32))> { let (tx, rx) = oneshot::channel(); let r = self.channel.send(ProcessorInstruction { img, color_matrix, pos_matrix, ret: tx, ctx, handle, realloc, }); dbg!(r).unwrap(); rx } }