use super::background::BackgroundWidget; use super::exterior::ExteriorWidget; use super::foreground::ForegroundWidget; use super::WindowCoord; use crate::coords::proj::Mercator; use crate::coords::Mapper; use femtovg::{Color, FontId, Paint, Path, Transform2D}; use gtk::glib; use gtk::prelude::WidgetExtManual; use gtk::subclass::prelude::*; use gtk::traits::{GLAreaExt, WidgetExt}; use ndarray::Array2; use std::cell::RefCell; use std::num::NonZeroU32; #[derive(Debug, Default, Clone)] pub struct RenderConfig { pub dim1: Option>, pub dim2: Option>, pub scale: f32, pub pointer_location: WindowCoord, pub pointer_lon_lat: (f64, f64), pub translate: WindowCoord, pub updated_translate: WindowCoord, } struct Fonts { sans: FontId, bold: FontId, light: FontId, } pub struct Render { pub(super) background: RefCell, pub(super) foreground: RefCell, pub(super) exterior: RefCell, pub config: RefCell, pub mapper: RefCell, pub(super) canvas: RefCell>>, } impl Default for Render { fn default() -> Self { Self { background: RefCell::new(BackgroundWidget::default()), foreground: RefCell::new(ForegroundWidget::default()), exterior: RefCell::new(ExteriorWidget::default()), config: RefCell::new(RenderConfig::default()), mapper: RefCell::new(Mercator::new().into()), canvas: RefCell::new(None), } } } #[glib::object_subclass] impl ObjectSubclass for Render { const NAME: &'static str = "MyRender"; type Type = super::Render; type ParentType = gtk::GLArea; } // Trait shared by all GObjects impl ObjectImpl for Render { fn constructed(&self) { self.parent_constructed(); let area = self.obj(); area.set_has_stencil_buffer(true); // area.add_tick_callback(|area, _| { // area.queue_render(); // glib::Continue(true) // }); } } // Trait shared by all widgets impl WidgetImpl for Render {} impl GLAreaImpl for Render { fn resize(&self, width: i32, height: i32) { self.ensure_canvas(); let mut canvas = self.canvas.borrow_mut(); let canvas = canvas.as_mut().unwrap(); canvas.set_size( width as u32, height as u32, self.obj().scale_factor() as f32, ); } fn render(&self, context: >k::gdk::GLContext) -> bool { self.ensure_canvas(); let mut canvas = self.canvas.borrow_mut(); let canvas = canvas.as_mut().unwrap(); let dpi = self.obj().scale_factor(); let w = canvas.width(); let h = canvas.height(); let configs = self.config.borrow(); canvas.clear_rect( 0, 0, (w as i32 * dpi) as u32, (h as i32 * dpi) as u32, Color::rgba(0, 0, 0, 255), ); let mapper = self.mapper.borrow(); let (lon_range, lat_range) = mapper.range.clone(); // { // let background_widget = self.background.borrow_mut(); // background_widget.set_lat_lines(lat_range.key_points(9)); // } // { // let background_widget = self.background.borrow_mut(); // background_widget.set_lon_lines(lon_range.key_points(20)); // } let translate = self.translate(); self.exterior.borrow().draw(canvas,self.mapper.borrow()); // self.foreground // .borrow() // .draw(canvas, configs.scale, dpi, self.mapper.borrow()); // self.background.borrow().draw( // canvas, // configs.scale, // dpi, // translate, // self.mapper.borrow(), // (lon_range, lat_range), // ); canvas.flush(); true } } impl Render { fn ensure_canvas(&self) { use femtovg::{renderer, Canvas}; use glow::HasContext; if self.canvas.borrow().is_some() { return; } let widget = self.obj(); widget.attach_buffers(); static LOAD_FN: fn(&str) -> *const std::ffi::c_void = |s| epoxy::get_proc_addr(s) as *const _; // SAFETY: Need to get the framebuffer id that gtk expects us to draw into, so femtovg // knows which framebuffer to bind. This is safe as long as we call attach_buffers // beforehand. Also unbind it here just in case, since this can be called outside render. let (mut renderer, fbo) = unsafe { let renderer = renderer::OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer"); let ctx = glow::Context::from_loader_function(LOAD_FN); let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32) .expect("No GTK provided framebuffer binding"); ctx.bind_framebuffer(glow::FRAMEBUFFER, None); (renderer, glow::NativeFramebuffer(id)) }; renderer.set_screen_target(Some(fbo)); let mut canvas = Canvas::new(renderer).expect("Cannot create canvas"); canvas .add_font_dir("/Users/tsuki/projects/radar-g/src/assets") .unwrap(); self.canvas.replace(Some(canvas)); } pub fn translate(&self) -> WindowCoord { let cfg = self.config.borrow(); let (rx, ry) = cfg.translate; let (ux, uy) = cfg.updated_translate; return (rx + ux, ry + uy); } }