0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-05 20:31:20 -05:00

♻️ Refactor panning/zoom rendering

This commit is contained in:
AzazelN28 2025-01-07 09:04:44 +01:00
parent a5b2f0e4c3
commit 10b32de447
7 changed files with 70 additions and 23 deletions

View file

@ -292,19 +292,23 @@
(when @canvas-init? (when @canvas-init?
(wasm.api/resize-viewbox (:width vport) (:height vport)))) (wasm.api/resize-viewbox (:width vport) (:height vport))))
(mf/with-effect [base-objects @canvas-init?] (mf/with-effect [@canvas-init? base-objects]
(when @canvas-init? (when @canvas-init?
(wasm.api/set-objects base-objects))) (wasm.api/set-objects base-objects)))
(mf/with-effect [preview-blend @canvas-init?] (mf/with-effect [@canvas-init? preview-blend]
(when (and @canvas-init? preview-blend) (when (and @canvas-init? preview-blend)
(wasm.api/request-render))) (wasm.api/request-render "with-effect")))
(mf/with-effect [vbox @canvas-init?] (mf/with-effect [@canvas-init? vbox]
(when @canvas-init? (when @canvas-init?
(wasm.api/set-view zoom vbox))) (wasm.api/set-view-zoom zoom vbox)))
(mf/with-effect [background] (mf/with-effect [@canvas-init? vbox]
(when @canvas-init?
(wasm.api/set-view-box zoom vbox)))
(mf/with-effect [@canvas-init? background]
(when @canvas-init? (when @canvas-init?
(wasm.api/set-canvas-background background))) (wasm.api/set-canvas-background background)))

View file

@ -61,15 +61,16 @@
[r g b a])) [r g b a]))
(defn cancel-render (defn cancel-render
[] [_]
(when internal-frame-id (when internal-frame-id
(js/cancelAnimationFrame internal-frame-id) (js/cancelAnimationFrame internal-frame-id)
(set! internal-frame-id nil))) (set! internal-frame-id nil)))
(defn request-render (defn request-render
[] [requester]
(when internal-frame-id (cancel-render)) (when internal-frame-id (cancel-render requester))
(set! internal-frame-id (js/requestAnimationFrame render))) (let [frame-id (js/requestAnimationFrame render)]
(set! internal-frame-id frame-id)))
(defn use-shape (defn use-shape
[id] [id]
@ -378,10 +379,15 @@
(def debounce-render-without-cache (fns/debounce render-without-cache 100)) (def debounce-render-without-cache (fns/debounce render-without-cache 100))
(defn set-view (defn set-view-box
[zoom vbox] [zoom vbox]
(h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox))) (h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
(h/call internal-module "_navigate") (h/call internal-module "_pan"))
(defn set-view-zoom
[zoom vbox]
(h/call internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
(h/call internal-module "_zoom")
(debounce-render-without-cache)) (debounce-render-without-cache))
(defn set-objects (defn set-objects
@ -427,7 +433,7 @@
(let [pending' (concat (set-shape-fills fills) (set-shape-strokes strokes))] (let [pending' (concat (set-shape-fills fills) (set-shape-strokes strokes))]
(recur (inc index) (into pending pending')))) (recur (inc index) (into pending pending'))))
pending))] pending))]
(request-render) (request-render "set-objects")
(when-let [pending (seq pending)] (when-let [pending (seq pending)]
(->> (rx/from pending) (->> (rx/from pending)
(rx/mapcat identity) (rx/mapcat identity)

View file

@ -130,7 +130,7 @@
;; when something synced with wasm ;; when something synced with wasm
;; is modified, we need to request ;; is modified, we need to request
;; a new render. ;; a new render.
(api/request-render))) (api/request-render "set-wasm-attrs")))
(defn- impl-assoc (defn- impl-assoc
[self k v] [self k v]

View file

@ -69,9 +69,16 @@ pub unsafe extern "C" fn render_without_cache() {
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn navigate() { pub unsafe extern "C" fn zoom() {
let state: &mut Box<State<'_>> =
unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
state.zoom();
}
#[no_mangle]
pub unsafe extern "C" fn pan() {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
state.navigate(); state.pan();
} }
#[no_mangle] #[no_mangle]

View file

@ -36,9 +36,13 @@ pub(crate) struct CachedSurfaceImage {
} }
impl CachedSurfaceImage { impl CachedSurfaceImage {
fn is_dirty(&self, viewbox: &Viewbox) -> bool { fn is_dirty_for_zooming(&mut self, viewbox: &Viewbox) -> bool {
!self.has_all_shapes && !self.viewbox.area.contains(viewbox.area) !self.has_all_shapes && !self.viewbox.area.contains(viewbox.area)
} }
fn is_dirty_for_panning(&mut self, _viewbox: &Viewbox) -> bool {
!self.has_all_shapes
}
} }
pub(crate) struct RenderState { pub(crate) struct RenderState {
@ -168,9 +172,23 @@ impl RenderState {
.clear(skia::Color::TRANSPARENT); .clear(skia::Color::TRANSPARENT);
} }
pub fn navigate(&mut self, tree: &HashMap<Uuid, impl Renderable>) -> Result<(), String> { pub fn zoom(&mut self, tree: &HashMap<Uuid, impl Renderable>) -> Result<(), String> {
if let Some(cached_surface_image) = self.cached_surface_image.as_ref() { if let Some(cached_surface_image) = self.cached_surface_image.as_mut() {
if cached_surface_image.is_dirty(&self.viewbox) { let is_dirty = cached_surface_image.is_dirty_for_zooming(&self.viewbox);
if is_dirty {
self.render_all(tree, true);
} else {
self.render_all_from_cache()?;
}
}
Ok(())
}
pub fn pan(&mut self, tree: &HashMap<Uuid, impl Renderable>) -> Result<(), String> {
if let Some(cached_surface_image) = self.cached_surface_image.as_mut() {
let is_dirty = cached_surface_image.is_dirty_for_panning(&self.viewbox);
if is_dirty {
self.render_all(tree, true); self.render_all(tree, true);
} else { } else {
self.render_all_from_cache()?; self.render_all_from_cache()?;
@ -192,6 +210,7 @@ impl RenderState {
); );
self.translate(self.viewbox.pan_x, self.viewbox.pan_y); self.translate(self.viewbox.pan_x, self.viewbox.pan_y);
// Reset shape tree
let is_complete = self.render_shape_tree(&Uuid::nil(), tree); let is_complete = self.render_shape_tree(&Uuid::nil(), tree);
if generate_cached_surface_image || self.cached_surface_image.is_none() { if generate_cached_surface_image || self.cached_surface_image.is_none() {
self.cached_surface_image = Some(CachedSurfaceImage { self.cached_surface_image = Some(CachedSurfaceImage {
@ -299,7 +318,7 @@ impl RenderState {
if !root_id.is_nil() { if !root_id.is_nil() {
if !element.bounds().intersects(self.viewbox.area) || element.hidden() { if !element.bounds().intersects(self.viewbox.area) || element.hidden() {
self.render_debug_element(element, false); self.render_debug_element(element, false);
// TODO: This means that not all the shapes are renderer so we // TODO: This means that not all the shapes are rendered so we
// need to call a render_all on the zoom out. // need to call a render_all on the zoom out.
return is_complete; // TODO return is_complete or return false?? return is_complete; // TODO return is_complete or return false??
} else { } else {

View file

@ -0,0 +1,6 @@
use crate::math::Point;
#[derive(Debug, Clone, PartialEq)]
pub struct Rect {
}

View file

@ -36,9 +36,14 @@ impl<'a> State<'a> {
&mut self.render_state &mut self.render_state
} }
pub fn navigate(&mut self) { pub fn pan(&mut self) {
// TODO: propagate error to main fn // TODO: propagate error to main fn
let _ = self.render_state.navigate(&self.shapes).unwrap(); let _ = self.render_state.pan(&self.shapes).unwrap();
}
pub fn zoom(&mut self) {
// TODO: propagate error to main fn
let _ = self.render_state.zoom(&self.shapes).unwrap();
} }
pub fn render_all(&mut self, generate_cached_surface_image: bool) { pub fn render_all(&mut self, generate_cached_surface_image: bool) {