From f1e112cdecc0ee78b1644274d92d44bb00a1ab7d Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Tue, 21 May 2024 13:13:49 +0200 Subject: [PATCH] get color matrix stuff working --- Cargo.lock | 152 ++++++++++++++++++++++- Cargo.toml | 4 +- src/lib.rs | 356 +++++++++++++++++++++++++++-------------------------- 3 files changed, 336 insertions(+), 176 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15b7ef8..be25fb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1192,6 +1192,7 @@ checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593" dependencies = [ "egui", "enum-map", + "image 0.24.9", "log", "mime_guess2", "serde", @@ -1566,6 +1567,19 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows 0.48.0", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2013,6 +2027,12 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "lebe" version = "0.5.2" @@ -2111,6 +2131,20 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "pin-utils", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "loop9" version = "0.1.5" @@ -2129,6 +2163,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "mathe-img-processor" version = "0.1.0" @@ -2140,6 +2183,8 @@ dependencies = [ "env_logger", "image 0.25.1", "nalgebra", + "oneshot", + "rayon", "threadpool", ] @@ -2370,6 +2415,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.5" @@ -2582,6 +2637,15 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oneshot" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4" +dependencies = [ + "loom", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -2607,6 +2671,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owned_ttf_parser" version = "0.21.0" @@ -2992,8 +3062,17 @@ checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -3004,9 +3083,15 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.3", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.3" @@ -3061,6 +3146,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "safe_arch" version = "0.7.1" @@ -3155,6 +3246,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -3397,6 +3497,16 @@ dependencies = [ "syn 2.0.64", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -3542,6 +3652,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -3652,6 +3792,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version-compare" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 9b6d88c..27bd0c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,12 @@ edition = "2021" bytemuck = "1.16.0" eframe = {version = "0.27", features = ["wgpu"]} egui-file-dialog = "0.5.0" -egui_extras = "0.27.2" +egui_extras = { version = "0.27.2", features = ["image"] } env_logger = "0.11.3" image = "0.25.1" nalgebra = "0.32.5" +oneshot = "0.1.6" +rayon = "1.10.0" threadpool = "1.8.1" # winit = { version = "0.30", features = ["rwh_05" ] } # log = "0.4" diff --git a/src/lib.rs b/src/lib.rs index 8ab1c57..12e6c5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,182 +1,49 @@ -use std::{num::NonZeroU64, path::PathBuf}; +use std::{num::NonZeroU64, path::PathBuf, sync::Arc, thread}; use cb::TriangleRenderResources; use eframe::{ - egui, egui_wgpu, + egui::{self, ImageSource}, + egui_wgpu, wgpu::{self, util::DeviceExt}, }; use egui_file_dialog::FileDialog; +use image::Rgb32FImage; +use img_processor::Processor; use nalgebra::{Matrix3, SMatrix}; +use oneshot::TryRecvError; use crate::cb::CustomTriangleCallback; pub struct App { - color_matrix: SMatrix, - pos_matrix: SMatrix, + color_matrix: SMatrix, + pos_matrix: SMatrix, file_dialog: FileDialog, new_file: Option, - cur_img_size: Option, - image_loaded: bool, + cur_img: Option>, + proc: Processor, + cur_rx: Option>, + cur_res: Option, + cur_size: Option<(f32, f32)>, } impl App { pub fn new<'a>(cc: &'a eframe::CreationContext<'a>) -> Option { - // Get the WGPU render state from the eframe creation context. This can also be retrieved - // from `eframe::Frame` when you don't have a `CreationContext` available. - let wgpu_render_state = cc.wgpu_render_state.as_ref()?; - - let device = &wgpu_render_state.device; - - // let cat = include_bytes!("./cat.jpg"); - // let cat_img = image::load_from_memory(cat).unwrap(); - // let cat_rgba = cat_img.to_rgba(); - - // use image::GenericImageView; - // let dimensions = cat_img.dimensions(); - - // let tex_size = wgpu::Extent3d { - // width: dimensions.0, - // height: dimensions.1, - // depth_or_array_layers: 1, - // }; - // let cat_tex = device.create_texture(&wgpu::TextureDescriptor { - // size: tex_size, - // mip_level_count: 1, - // sample_count: 1, - // dimension: wgpu::TextureDimension::D2, - // format: wgpu::TextureFormat::Rgba8UnormSrgb, - // usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - // label: Some("cat"), - // view_formats: &[], - // }); - - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("./shader.wgsl").into()), - }); - - let tex_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("tex_bind_group_layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - // This should match the filterable field of the - // corresponding Texture entry above. - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - let matrices_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("matrices_bind_group_layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: Some(NonZeroU64::new(64)?), - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: Some(NonZeroU64::new(36)?), - }, - count: None, - }, - ], - }); - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("pipeline_layout"), - bind_group_layouts: &[&tex_bind_group_layout, &matrices_bind_group_layout], - push_constant_ranges: &[], - }); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("custom3d"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[], - // compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu_render_state.target_format.into())], - // compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - - // let color_matrix_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - // label: Some("color_matrix_buffer"), - // contents: bytemuck::cast_slice(&[0.0_f32; 4]), // 16 bytes aligned! - // // Mapping at creation (as done by the create_buffer_init utility) doesn't require us to to add the MAP_WRITE usage - // // (this *happens* to workaround this bug ) - // usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, - // }); - - // let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - // label: Some("custom3d"), - // layout: &bind_group_layout, - // entries: &[wgpu::BindGroupEntry { - // binding: 0, - // resource: uniform_buffer.as_entire_binding(), - // }], - // }); - - // Because the graphics pipeline must have the same lifetime as the egui render pass, - // instead of storing the pipeline in our `Custom3D` struct, we insert it into the - // `paint_callback_resources` type map, which is stored alongside the render pass. - - // wgpu_render_state - // .renderer - // .write() - // .callback_resources - // .insert(TriangleRenderResources { - // pipeline, - // bind_group, - // uniform_buffer, - // }); - Some(Self { color_matrix: SMatrix::identity(), pos_matrix: SMatrix::identity(), file_dialog: FileDialog::new(), new_file: None, - image_loaded: false, - cur_img_size: None, + cur_img: None, + proc: Processor::init(), + cur_rx: None, + cur_res: None, + cur_size: None, }) } } impl eframe::App for App { fn update(&mut self, ctx: &eframe::egui::Context, frame: &mut eframe::Frame) { - let mut img = None; + let mut mats_changed = false; egui::SidePanel::right("sidebar").show(ctx, |ui| { if ui.button("Select Image").clicked() { self.file_dialog.select_file() @@ -184,43 +51,188 @@ impl eframe::App for App { if self.file_dialog.update(ctx).selected().is_some() { if let Some(path) = self.file_dialog.take_selected() { println!("path: {path:?}"); - if path.ends_with(".jpg") || path.ends_with(".jpeg") || path.ends_with(".png") { - img = Some(image::open(path).unwrap().to_rgba8()); + let Some(ext) = path.extension() else { + panic!("path should have an extension") + }; + if ext == "jpg" || ext == "jpeg" || ext == "png" { + println!("img!"); + self.cur_img = Some(Arc::new(image::open(path).unwrap().into_rgb32f())); + + self.cur_rx = Some(self.proc.exec( + self.cur_img.clone().unwrap(), + self.color_matrix, + self.pos_matrix, + ctx.clone(), + self.cur_res.clone(), + )); } } } ui.add_space(8.); + let old_color_mat = self.color_matrix; + let old_pos_mat = self.pos_matrix; ui.label("Color Matrix Editor"); - components::mat_editor(ui, &mut self.color_matrix, "color_matrix"); + let color_mat_edited = + components::mat_editor(ui, &mut self.color_matrix, "color_matrix").dragged(); ui.add_space(8.); ui.label("Position Matrix Editor"); - components::mat_editor(ui, &mut self.pos_matrix, "pos_matrix"); + let pos_mat_edited = + components::mat_editor(ui, &mut self.pos_matrix, "pos_matrix").dragged(); + mats_changed |= old_color_mat == self.color_matrix || old_pos_mat == self.pos_matrix; + + if mats_changed + && self.cur_img.is_some() + && self.cur_res.is_some() + && !color_mat_edited + && !pos_mat_edited + && self.cur_rx.is_none() + { + self.cur_rx = Some(self.proc.exec( + self.cur_img.clone().unwrap(), + self.color_matrix, + self.pos_matrix, + ctx.clone(), + self.cur_res.clone(), + )); + } }); egui::CentralPanel::default().show(ctx, |ui| { ui.heading("My egui Application"); - if let Some(dimensions) = self.cur_img_size { - egui::Frame::canvas(ui.style()).show(ui, |ui| { - let (rect, response) = ui.allocate_exact_size(dimensions, egui::Sense::drag()); - - // let callback = egui_wgpu::Callback::new_paint_callback( - // rect, - // CustomTriangleCallback { angle: 0.5 }, - // ); - // ui.painter().add(callback); - }); + if let Some(r) = &self.cur_rx { + match r.try_recv() { + Ok((handle, size)) => { + println!("mew"); + self.cur_res = Some(handle); + self.cur_size = Some(size); + self.cur_rx = None; + } + Err(TryRecvError::Empty) => {} + Err(TryRecvError::Disconnected) => panic!("oneshot channel disconnected"), + } } + + if let Some(r) = &self.cur_res { + let sized_img = + egui::load::SizedTexture::new(r.id(), egui::Vec2::from(self.cur_size.unwrap())); + let img = egui::Image::from_texture(sized_img); + ui.add(img); + } + // if let Some(dimensions) = self.cur_img_size { + // egui::Frame::canvas(ui.style()).show(ui, |ui| { + // let (rect, response) = ui.allocate_exact_size(dimensions, egui::Sense::drag()); + + // let callback = egui_wgpu::Callback::new_paint_callback( + // rect, + // CustomTriangleCallback { angle: 0.5 }, + // ); + // ui.painter().add(callback); + // }); + // } }); } } -mod gpu_proc { - use eframe::wgpu; +mod img_processor { + use std::{ + sync::{mpsc, Arc}, + thread, + }; - pub struct GpuResources { - pipeline: wgpu::RenderPipeline, - // img_buffer: Option, + color_matrix: SMatrix, + pos_matrix: SMatrix, + ret: oneshot::Sender<(egui::TextureHandle, (f32, f32))>, + ctx: egui::Context, + handle: Option, + } + + pub struct Processor { + channel: mpsc::Sender, + } + + 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(); + println!("a"); + let mut r = (*img).clone(); + 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(), + ) + }); + } + + println!("b"); + 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()) + }; + println!("c"); + + 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, + ) -> 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 + } } } @@ -240,7 +252,7 @@ mod components { ui.add( egui::DragValue::new(item) .speed(0.01) - .clamp_range(-1.0..=1.0), + .clamp_range(-10.0..=10.0), ); }); ui.end_row();