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
|
|
|
|
}
|
|
|
|
}
|