diff --git a/Cargo.lock b/Cargo.lock index f59356b..7e4037b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -913,9 +913,9 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -2697,9 +2697,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.9" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a" dependencies = [ "bitflags 1.3.2", "crc32fast", diff --git a/src/components/control_panel/control_panel.rs b/src/components/control_panel/control_panel.rs index a69e931..a0ff9b7 100644 --- a/src/components/control_panel/control_panel.rs +++ b/src/components/control_panel/control_panel.rs @@ -1,8 +1,14 @@ +use std::path::PathBuf; + use adw::prelude::WidgetExt; use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt}; use relm4::*; +use relm4_components::open_button::{OpenButton, OpenButtonSettings}; +use relm4_components::open_dialog::OpenDialogSettings; -pub struct ControlPanelModel; +pub struct ControlPanelModel { + open_button: Controller, +} #[derive(Debug)] pub enum HeaderOutput { @@ -11,11 +17,16 @@ pub enum HeaderOutput { Export, } +#[derive(Debug)] +pub enum AppMsg { + Open(PathBuf), +} + #[relm4::component(pub)] impl SimpleComponent for ControlPanelModel { type Init = i8; type Output = HeaderOutput; - type Input = (); + type Input = AppMsg; view! { #[root] @@ -23,9 +34,7 @@ impl SimpleComponent for ControlPanelModel { set_orientation: gtk::Orientation::Horizontal, set_spacing: 10, set_size_request: (100, 150), - gtk::Button { - set_label: "View", - }, + model.open_button.widget(), gtk::Button { set_label: "Edit", }, @@ -40,11 +49,23 @@ impl SimpleComponent for ControlPanelModel { root: &Self::Root, sender: relm4::ComponentSender, ) -> relm4::ComponentParts { - let model = ControlPanelModel {}; - // Insert the macro code generation here + let open_button = OpenButton::builder() + .launch(OpenButtonSettings { + dialog_settings: OpenDialogSettings::default(), + text: "Open file", + recently_opened_files: Some(".recent_files"), + max_recent_files: 10, + }) + .forward(sender.input_sender(), AppMsg::Open); + + let model = ControlPanelModel { open_button }; let widgets = view_output!(); ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: ComponentSender) {} + fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { + match msg { + AppMsg::Open(p) => println!("Open file: {:?}", p), + } + } } diff --git a/src/components/render_panel/monitor/monitor.rs b/src/components/render_panel/monitor/monitor.rs index 08db61e..48fa731 100644 --- a/src/components/render_panel/monitor/monitor.rs +++ b/src/components/render_panel/monitor/monitor.rs @@ -100,9 +100,7 @@ impl Component for MonitorModel { let cms = CMS::new(map, (3000.0, 3000.0)); let canvas = Arc::new(Mutex::new(canvas)); let c = f(imp, canvas, cms).await; - Some(c) - // None } else { None }; diff --git a/src/main.rs b/src/main.rs index 120cf99..d17d5f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ mod utils; +use gtk::prelude::*; use gtk::{gio, glib, Application, ApplicationWindow}; use pipeline::offscreen_renderer::OffscreenRenderer; +use relm4::menu; +use relm4::RelmApp; use std::ptr; use tokio::runtime::Runtime; +use utils::creator; mod chart; mod components; mod coords; @@ -15,16 +19,13 @@ mod render; mod window; use components::app::{AppMode, AppModel}; use once_cell::sync::Lazy; -use std::sync::Mutex; const APP_ID: &str = "org.gtk_rs.HelloWorld2"; static RUNTIME: Lazy = Lazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed.")); -// static OFFSCREEN: Lazy> = Lazy::new(|| { -// Mutex::new(OffscreenRenderer::new(3000, 3000).expect("Can't create offscreen renderer.")) -// }); +// static OFFSCREEN: Lazy = Lazy::new(|| OffscreenRenderer::new().expect("Can't create offscreen renderer.")); fn main() { // Load GL pointers from epoxy (GL context management library used by GTK). diff --git a/src/pipeline/offscreen_renderer.rs b/src/pipeline/offscreen_renderer.rs index d5fe06e..286d336 100644 --- a/src/pipeline/offscreen_renderer.rs +++ b/src/pipeline/offscreen_renderer.rs @@ -52,8 +52,8 @@ impl OffscreenRenderer { gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name)); unsafe { - debug_assert_eq!(gl::GetError(), gl::NO_ERROR); gl::BindFramebuffer(gl::FRAMEBUFFER, surface_info.framebuffer_object); + gl::Viewport(0, 0, width, height); debug_assert_eq!(gl::GetError(), gl::NO_ERROR); } @@ -68,9 +68,7 @@ impl OffscreenRenderer { pub fn create_canvas(&mut self) -> CanvasWrapper { let (w, h) = self.size; - let mut pixels: Vec = vec![0; w as usize * h as usize * 4]; - - let (mut renderer, fbo, ctx) = unsafe { + let (mut renderer, fbo) = unsafe { let renderer = OpenGl::new_from_function(|s| { self.device .get_proc_address(&self.context.read().unwrap(), s) as *const _ @@ -82,66 +80,45 @@ impl OffscreenRenderer { .get_proc_address(&self.context.read().unwrap(), s) as *const _ }); - let fbo = glow::NativeFramebuffer(self.fbo); + let surface_info = self + .device + .context_surface_info(&self.context.read().unwrap()) + .unwrap() + .unwrap(); + + let fbo = + glow::NativeFramebuffer(NonZeroU32::new(surface_info.framebuffer_object).unwrap()); ctx.bind_framebuffer(glow::FRAMEBUFFER, None); - (renderer, fbo, ctx) + (renderer, fbo) }; renderer.set_screen_target(Some(fbo)); let mut canvas = Canvas::new(renderer).expect("Cannot create canvas"); - canvas.set_size(3000, 3000, 1.0); - - let img = canvas - .create_image_empty( - 3000, - 3000, - femtovg::PixelFormat::Rgba8, - femtovg::ImageFlags::empty(), - ) - .unwrap(); - - canvas.set_render_target(femtovg::RenderTarget::Image(img)); - let mut path = femtovg::Path::new(); - path.rect(0.0, 0.0, 300.0, 300.0); - canvas.fill_path( - &mut path, - &femtovg::Paint::color(femtovg::Color::rgb(255, 0, 0)), - ); - - unsafe { - ctx.get_tex_image( - glow::TEXTURE_2D, - 0, - glow::RGBA, - glow::UNSIGNED_BYTE, - glow::PixelPackData::Slice(&mut pixels), - ); - } - - // unsafe { - // let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER); - // println!("status: {}", status); - // gl::ReadPixels( - // 0, - // 0, - // w, - // h, - // gl::RGBA, - // gl::UNSIGNED_BYTE, - // pixels.as_mut_ptr() as *mut GLvoid, - // ); - - // debug_assert_eq!(gl::GetError(), gl::NO_ERROR); - // } - - if let Some(max) = pixels.iter().max() { - println!("The maximum value is {}", max); - } else { - println!("The vector is empty"); - } + canvas.set_size(w as u32, h as u32, 1.0); CanvasWrapper::new(canvas) } + + pub fn get_mem_img(&self) -> Vec { + let (w, h) = self.size; + let mut pixels: Vec = vec![0; w as usize * h as usize * 4]; + + unsafe { + gl::ReadPixels( + 0, + 0, + w, + h, + gl::RGBA, + gl::UNSIGNED_BYTE, + pixels.as_mut_ptr() as *mut GLvoid, + ); + + debug_assert_eq!(gl::GetError(), gl::NO_ERROR); + } + + pixels + } } impl Drop for OffscreenRenderer { diff --git a/src/render/interior/layers.rs b/src/render/interior/layers.rs index a33dcb0..2713091 100644 --- a/src/render/interior/layers.rs +++ b/src/render/interior/layers.rs @@ -112,8 +112,8 @@ impl Layer { self.target.lock().unwrap().replace(target); } - pub fn render_target(&self) -> Option { - self.target.lock().unwrap().clone() + pub fn render_target(&self) -> Arc>> { + self.target.clone() } pub fn get_imp(&self) -> Option>>> { @@ -122,16 +122,22 @@ impl Layer { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub struct Target { - pub target: ImageId, - width: f32, - height: f32, - bounds: (Range, Range), + pub target: TargetType, + pub width: f32, + pub height: f32, + pub bounds: (Range, Range), +} + +#[derive(Debug, Clone)] +pub enum TargetType { + ImageId(ImageId), + Mem(Vec), } impl Target { - pub fn new(target: ImageId, width: f32, height: f32, bounds: (Range, Range)) -> Self { + pub fn new(target: TargetType, width: f32, height: f32, bounds: (Range, Range)) -> Self { Self { target, width, @@ -157,4 +163,8 @@ impl Target { let p1 = (x.0, y.1); render.map(p1).unwrap() } + + pub fn set_target(&mut self, target: TargetType) { + self.target = target; + } } diff --git a/src/render/interior/mod.rs b/src/render/interior/mod.rs index 32f4347..6c19896 100644 --- a/src/render/interior/mod.rs +++ b/src/render/interior/mod.rs @@ -2,7 +2,7 @@ mod imp; mod layers; use crate::render::Render; use femtovg::{renderer::OpenGl, Canvas}; -pub use layers::{Layer, LayerImpl, LayerImplSync, Target}; +pub use layers::{Layer, LayerImpl, LayerImplSync, Target, TargetType}; use std::cell::Ref; use crate::render::imp::{RenderConfig, RenderStatus}; diff --git a/src/render/mod.rs b/src/render/mod.rs index 7b1e664..080cccb 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -14,7 +14,7 @@ use glib::clone; pub use glib::subclass::prelude::*; use gtk::traits::WidgetExt; use gtk::{EventControllerScrollFlags, Inhibit}; -pub use interior::{Layer, LayerImpl, LayerImplSync, Target}; +pub use interior::*; use relm4::once_cell::sync::Lazy; use std::cell::{Ref, RefCell, RefMut}; use std::sync::{Arc, Mutex}; diff --git a/src/render/predefined/grid_field_renderer.rs b/src/render/predefined/grid_field_renderer.rs index 30746b2..c7ca998 100644 --- a/src/render/predefined/grid_field_renderer.rs +++ b/src/render/predefined/grid_field_renderer.rs @@ -3,14 +3,16 @@ use femtovg::{ renderer::OpenGl, Canvas, ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget, }; use geo_types::LineString; +use gl::types::GLvoid; +use image::{ImageBuffer, Rgba}; use ndarray::ArrayView2; use num_traits::{Num, NumOps}; -use std::{fmt::Debug, marker::PhantomData}; +use std::{fmt::Debug, io::Cursor, marker::PhantomData}; use super::super::renders::DataRenderer; use crate::{ data::Radar2d, - render::{cms::CMS, LayerImpl, Render, Target}, + render::{cms::CMS, LayerImpl, Render, Target, TargetType}, utils::meshgrid, }; @@ -133,7 +135,31 @@ where data.fill_value, ); - canvas.set_render_target(RenderTarget::Screen); + canvas.flush(); + + let mut pixels: Vec = vec![0; w as usize * h as usize * 4]; + + unsafe { + gl::ReadPixels( + 0, + 0, + w as i32, + h as i32, + gl::RGBA, + gl::UNSIGNED_BYTE, + pixels.as_mut_ptr() as *mut GLvoid, + ); + debug_assert_eq!(gl::GetError(), gl::NO_ERROR); + } + + let img: ImageBuffer, Vec> = ImageBuffer::from_raw(w as u32, h as u32, pixels) + .expect("Failed to create ImageBuffer"); + // 将 ImageBuffer 编码为 PNG + let mut png_buffer = Cursor::new(Vec::new()); + img.write_to(&mut png_buffer, image::ImageOutputFormat::Png) + .expect("Failed to write PNG buffer"); + + let png_data = png_buffer.into_inner(); let d1_start = (data.dim1.view()).first().unwrap().clone(); let d1_end = (data.dim1.view()).last().unwrap().clone(); @@ -142,16 +168,12 @@ where let d2_end = data.dim2.view().last().unwrap().clone(); Target::new( - new_img, + TargetType::Mem(png_data), w, h, ((d1_start, d1_end).into(), (d2_start, d2_end).into()), ) } - - // fn render(&self, canvas: &mut Canvas, cms: &CMS, data: &Self::Data) -> Target { - - // } } #[derive(Debug)] @@ -186,12 +208,9 @@ where canvas: &mut femtovg::Canvas, cms: crate::render::cms::CMS, ) -> Option { - canvas.save(); - canvas.reset(); let result = self .renderer .render(canvas, cms, &self.data, (3000.0, 3000.0)); - canvas.restore(); return Some(result); } } diff --git a/src/render/predefined/layers.rs b/src/render/predefined/layers.rs index 673b65b..0bd5e4a 100644 --- a/src/render/predefined/layers.rs +++ b/src/render/predefined/layers.rs @@ -8,8 +8,8 @@ use std::sync::Mutex; use crate::pipeline::offscreen_renderer::CanvasWrapper; use crate::render::cms::CMS; -use crate::render::LayerImplSync; use crate::render::{LayerImpl, Target}; +use crate::render::{LayerImplSync, TargetType}; use crate::{ data::{AsyncDataLoader, DataLoader, Radar2d}, render::{Layer, Render}, @@ -29,13 +29,33 @@ impl Layer { Self::new( true, |s, render, _| { - if let Some(target) = s.render_target() { - let mut c = render.get_canvas(); - let c = c.as_mut().unwrap(); - if let Ok(_) = c.image_size(target.target) { - let (x, y) = target.size(&render); - let (ox, oy) = target.origin(&render); - let painter = Paint::image(target.target, ox, oy, x, y, 0.0, 1.0); + let mut c = render.get_canvas(); + let c = c.as_mut().unwrap(); + + let render_target = s.render_target(); + let mut unlock_target = render_target.lock().unwrap(); + + let image_id = if let Some(target) = unlock_target.as_mut() { + let result_id = match target.target { + TargetType::ImageId(id) => id, + TargetType::Mem(ref mem) => { + let converted = c + .load_image_mem(&mem, femtovg::ImageFlags::empty()) + .unwrap(); + target.set_target(TargetType::ImageId(converted)); + converted + } + }; + let (x, y) = target.size(&render); + let (ox, oy) = target.origin(&render); + Some((result_id, (x, y), (ox, oy))) + } else { + None + }; + + if let Some((id, (x, y), (ox, oy))) = image_id { + if let Ok(_) = c.image_size(id) { + let painter = Paint::image(id, ox, oy, x, y, 0.0, 1.0); let mut path = femtovg::Path::new(); path.rect(ox, oy, x, y); c.fill_path(&path, &painter);