diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index f1c2eed21..84dd6dc87 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -55,8 +55,13 @@ impl GpuState { pub(crate) struct CachedSurfaceImage { pub image: Image, pub viewbox: Viewbox, - // is_complete indicates if stored image renders the complete shape tree - pub is_complete: bool, + has_all_shapes: bool, +} + +impl CachedSurfaceImage { + fn is_dirty(&self, viewbox: &Viewbox) -> bool { + !self.has_all_shapes && !self.viewbox.area.contains(viewbox.area) + } } #[derive(Debug, Default, Copy, Clone, PartialEq)] @@ -198,49 +203,20 @@ impl RenderState { .clear(skia::Color::TRANSPARENT); } - pub fn navigate(&mut self, viewbox: &Viewbox, shapes: &HashMap) { - self.reset_canvas(); - if let Some(cached_surface_image) = &self.cached_surface_image { - // If we are drawing something bigger than the visible let's do a redraw - if !cached_surface_image.is_complete - && ((viewbox.x > cached_surface_image.viewbox.x) - || (-viewbox.x + viewbox.area.width() - > -cached_surface_image.viewbox.x - + cached_surface_image.viewbox.area.width()) - || (viewbox.y > cached_surface_image.viewbox.y) - || (-viewbox.y + viewbox.area.height() - > -cached_surface_image.viewbox.y - + cached_surface_image.viewbox.area.height())) - { + pub fn navigate( + &mut self, + viewbox: &Viewbox, + shapes: &HashMap, + ) -> Result<(), String> { + if let Some(cached_surface_image) = self.cached_surface_image.as_ref() { + if cached_surface_image.is_dirty(viewbox) { self.render_all(viewbox, shapes, true); } else { - let image = &cached_surface_image.image; - let paint = skia::Paint::default(); - self.final_surface.canvas().save(); - self.drawing_surface.canvas().save(); - - let navigate_zoom = viewbox.zoom / cached_surface_image.viewbox.zoom; - let navigate_x = cached_surface_image.viewbox.zoom - * (viewbox.x - cached_surface_image.viewbox.x); - let navigate_y = cached_surface_image.viewbox.zoom - * (viewbox.y - cached_surface_image.viewbox.y); - - self.final_surface - .canvas() - .scale((navigate_zoom, navigate_zoom)); - self.final_surface - .canvas() - .translate((navigate_x, navigate_y)); - self.final_surface - .canvas() - .draw_image(image.clone(), (0, 0), Some(&paint)); - - self.final_surface.canvas().restore(); - self.drawing_surface.canvas().restore(); + self.render_all_from_cache(viewbox)?; } } - self.flush(); + Ok(()) } pub fn render_all( @@ -251,14 +227,14 @@ impl RenderState { ) { self.reset_canvas(); self.scale(viewbox.zoom, viewbox.zoom); - self.translate(viewbox.x, viewbox.y); + self.translate(viewbox.pan_x, viewbox.pan_y); let is_complete = self.render_shape_tree(&Uuid::nil(), viewbox, shapes); if generate_cached_surface_image || self.cached_surface_image.is_none() { self.cached_surface_image = Some(CachedSurfaceImage { image: self.final_surface.image_snapshot(), viewbox: viewbox.clone(), - is_complete, + has_all_shapes: is_complete, }); } @@ -269,6 +245,41 @@ impl RenderState { self.flush(); } + fn render_all_from_cache(&mut self, viewbox: &Viewbox) -> Result<(), String> { + self.reset_canvas(); + + let cached = self + .cached_surface_image + .as_ref() + .ok_or("Uninitialized cached surface image")?; + + let image = &cached.image; + let paint = skia::Paint::default(); + self.final_surface.canvas().save(); + self.drawing_surface.canvas().save(); + + let navigate_zoom = viewbox.zoom / cached.viewbox.zoom; + let navigate_x = cached.viewbox.zoom * (viewbox.pan_x - cached.viewbox.pan_x); + let navigate_y = cached.viewbox.zoom * (viewbox.pan_y - cached.viewbox.pan_y); + + self.final_surface + .canvas() + .scale((navigate_zoom, navigate_zoom)); + self.final_surface + .canvas() + .translate((navigate_x, navigate_y)); + self.final_surface + .canvas() + .draw_image(image.clone(), (0, 0), Some(&paint)); + + self.final_surface.canvas().restore(); + self.drawing_surface.canvas().restore(); + + self.flush(); + + Ok(()) + } + fn render_debug_view(&mut self, viewbox: &Viewbox) { let mut paint = skia::Paint::default(); paint.set_style(skia::PaintStyle::Stroke); diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index 916a6ff85..363e3e867 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -27,8 +27,8 @@ impl<'a> State<'a> { current_shape: None, shapes: HashMap::with_capacity(capacity), viewbox: Viewbox { - x: 0., - y: 0., + pan_x: 0., + pan_y: 0., zoom: 1., width: width as f32, height: height as f32, @@ -47,7 +47,11 @@ impl<'a> State<'a> { } pub fn navigate(&mut self) { - self.render_state.navigate(&self.viewbox, &self.shapes); + // TODO: propagate error to main fn + let _ = self + .render_state + .navigate(&self.viewbox, &self.shapes) + .unwrap(); } pub fn render_all(&mut self, generate_cached_surface_image: bool) { diff --git a/render-wasm/src/view.rs b/render-wasm/src/view.rs index 0f1ab1193..175af41b4 100644 --- a/render-wasm/src/view.rs +++ b/render-wasm/src/view.rs @@ -2,8 +2,8 @@ use skia_safe as skia; #[derive(Debug, Copy, Clone)] pub(crate) struct Viewbox { - pub x: f32, - pub y: f32, + pub pan_x: f32, + pub pan_y: f32, pub width: f32, pub height: f32, pub zoom: f32, @@ -11,13 +11,13 @@ pub(crate) struct Viewbox { } impl Viewbox { - pub fn set_all(&mut self, zoom: f32, x: f32, y: f32) -> &Self { - self.x = x; - self.y = y; + pub fn set_all(&mut self, zoom: f32, x: f32, y: f32) -> &mut Self { + self.pan_x = x; + self.pan_y = y; self.zoom = zoom; self.area.set_xywh( - -self.x, - -self.y, + -self.pan_x, + -self.pan_y, self.width / self.zoom, self.height / self.zoom, ); @@ -31,9 +31,9 @@ impl Viewbox { self } - pub fn set_xy(&mut self, x: f32, y: f32) -> &Self { - self.x = x; - self.y = y; + pub fn set_xy(&mut self, x: f32, y: f32) -> &mut Self { + self.pan_x = x; + self.pan_y = y; self.area.left = -x; self.area.top = -y; self