matrix-image-editor/src/img_processor.rs

118 lines
3.8 KiB
Rust
Raw Normal View History

2024-05-22 09:08:55 +02:00
use std::{
sync::{mpsc, Arc},
thread,
};
use eframe::egui::{self, TextureOptions};
use image::{DynamicImage, Pixel, Rgb, Rgb32FImage};
2024-05-22 09:42:40 +02:00
use nalgebra::{SMatrix, Vector3, Vector4};
2024-05-22 09:08:55 +02:00
use rayon::prelude::*;
use image::EncodableLayout;
struct ProcessorInstruction {
img: Arc<Rgb32FImage>,
color_matrix: SMatrix<f32, 3, 4>,
2024-05-22 09:42:40 +02:00
pos_matrix: SMatrix<f32, 3, 3>,
2024-05-22 09:08:55 +02:00
ret: oneshot::Sender<(egui::TextureHandle, (f32, f32))>,
ctx: egui::Context,
handle: Option<egui::TextureHandle>,
}
pub struct Processor {
channel: mpsc::Sender<ProcessorInstruction>,
}
impl Processor {
pub fn init() -> Self {
let (tx, rx) = mpsc::channel();
thread::spawn(|| {
for ProcessorInstruction {
img,
color_matrix,
pos_matrix,
ret,
ctx,
handle,
} in rx
{
let (width, height) = img.dimensions();
2024-05-22 09:42:40 +02:00
// let mut r = (*img).clone();
let mut r = Rgb32FImage::new(width, height);
if !pos_matrix.is_identity(f32::EPSILON) && pos_matrix.is_invertible() {
let mut inv_mat = pos_matrix.try_inverse().unwrap();
let mut cols_mut = inv_mat.column_part_mut(2, 2);
*(cols_mut.get_mut(0).unwrap()) *= width as f32;
*(cols_mut.get_mut(1).unwrap()) *= height as f32;
r.par_enumerate_pixels_mut().for_each(|(x, y, px)| {
let r = inv_mat * Vector3::new(x as f32, y as f32, 1.0);
*px = if r.x.is_sign_positive() && r.y.is_sign_positive() {
*img.get_pixel_checked(r.x as u32, r.y as u32)
.unwrap_or(&Rgb([0., 0., 0.]))
} else {
Rgb([0., 0., 0.])
};
});
} else {
r = (*img).clone()
}
2024-05-22 09:08:55 +02:00
if !color_matrix.is_identity(f32::EPSILON) {
r.par_pixels_mut().for_each(|px| {
*px = Rgb::from(
TryInto::<[f32; 3]>::try_into(
(color_matrix * Vector4::new(px.0[0], px.0[1], px.0[2], 1.))
.as_slice(),
)
.unwrap(),
)
});
}
let color_image = {
let image = DynamicImage::from(r).to_rgb8();
egui::ColorImage::from_rgb(
[image.width() as usize, image.height() as usize],
image.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<Rgb32FImage>,
color_matrix: SMatrix<f32, 3, 4>,
2024-05-22 09:42:40 +02:00
pos_matrix: SMatrix<f32, 3, 3>,
2024-05-22 09:08:55 +02:00
ctx: egui::Context,
handle: Option<egui::TextureHandle>,
) -> 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,
});
dbg!(r).unwrap();
rx
}
}