From 99bb3ee962099d2f27a2241d5fed461d0409cc1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Mon, 9 Dec 2024 15:26:51 +0100 Subject: [PATCH] :tada: Render path fills --- frontend/src/app/render_wasm/api.cljs | 6 +- frontend/src/app/render_wasm/path.cljs | 128 ++++++++++++------------- render-wasm/src/main.rs | 3 +- render-wasm/src/render.rs | 42 ++++---- render-wasm/src/shapes.rs | 11 ++- render-wasm/src/shapes/images.rs | 18 +++- 6 files changed, 118 insertions(+), 90 deletions(-) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 39fd8a00a..24f5481cd 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -296,9 +296,9 @@ (defn- debug-flags [] - (cond-> 0 - (dbg/enabled? :wasm-viewbox) - (bit-or 2r00000000000000000000000000000001))) + (cond-> 0 + (dbg/enabled? :wasm-viewbox) + (bit-or 2r00000000000000000000000000000001))) (defn assign-canvas [canvas] diff --git a/frontend/src/app/render_wasm/path.cljs b/frontend/src/app/render_wasm/path.cljs index 19a4d93c3..e62eadb66 100644 --- a/frontend/src/app/render_wasm/path.cljs +++ b/frontend/src/app/render_wasm/path.cljs @@ -3,70 +3,70 @@ (def command-size 28) #_(defn content->buffer - "Converts the path content into binary format." - [content] - (let [total (count content) - buffer (new js/ArrayBuffer (* total command-size)) - dview (new js/DataView buffer)] - (loop [index 0] - (when (< index total) - (let [segment (nth content index) - offset (* index command-size)] - (case (:command segment) - :move-to - (let [{:keys [x y]} (:params segment)] - (.setUint16 dview (+ offset 0) 1) - (.setFloat32 dview (+ offset 20) x) - (.setFloat32 dview (+ offset 24) y)) - :line-to - (let [{:keys [x y]} (:params segment)] - (.setUint16 dview (+ offset 0) 2) - (.setFloat32 dview (+ offset 20) x) - (.setFloat32 dview (+ offset 24) y)) - :curve-to - (let [{:keys [c1x c1y c2x c2y x y]} (:params segment)] - (.setUint16 dview (+ offset 0) 3) - (.setFloat32 dview (+ offset 4) c1x) - (.setFloat32 dview (+ offset 8) c1y) - (.setFloat32 dview (+ offset 12) c2x) - (.setFloat32 dview (+ offset 16) c2y) - (.setFloat32 dview (+ offset 20) x) - (.setFloat32 dview (+ offset 24) y)) + "Converts the path content into binary format." + [content] + (let [total (count content) + buffer (new js/ArrayBuffer (* total command-size)) + dview (new js/DataView buffer)] + (loop [index 0] + (when (< index total) + (let [segment (nth content index) + offset (* index command-size)] + (case (:command segment) + :move-to + (let [{:keys [x y]} (:params segment)] + (.setUint16 dview (+ offset 0) 1) + (.setFloat32 dview (+ offset 20) x) + (.setFloat32 dview (+ offset 24) y)) + :line-to + (let [{:keys [x y]} (:params segment)] + (.setUint16 dview (+ offset 0) 2) + (.setFloat32 dview (+ offset 20) x) + (.setFloat32 dview (+ offset 24) y)) + :curve-to + (let [{:keys [c1x c1y c2x c2y x y]} (:params segment)] + (.setUint16 dview (+ offset 0) 3) + (.setFloat32 dview (+ offset 4) c1x) + (.setFloat32 dview (+ offset 8) c1y) + (.setFloat32 dview (+ offset 12) c2x) + (.setFloat32 dview (+ offset 16) c2y) + (.setFloat32 dview (+ offset 20) x) + (.setFloat32 dview (+ offset 24) y)) - :close-path - (.setUint16 dview (+ offset 0) 4)) - (recur (inc index))))) - buffer)) + :close-path + (.setUint16 dview (+ offset 0) 4)) + (recur (inc index))))) + buffer)) #_(defn buffer->content - "Converts the a buffer to a path content vector" - [buffer] - (assert (instance? js/ArrayBuffer buffer) "expected ArrayBuffer instance") - (let [total (/ (.-byteLength buffer) command-size) - dview (new js/DataView buffer)] - (loop [index 0 - result []] - (if (< index total) - (let [offset (* index command-size) - type (.getUint16 dview (+ offset 0)) - command (case type - 1 :move-to - 2 :line-to - 3 :curve-to - 4 :close-path) - params (case type - 1 {:x (.getFloat32 dview (+ offset 20)) - :y (.getFloat32 dview (+ offset 24))} - 2 {:x (.getFloat32 dview (+ offset 20)) - :y (.getFloat32 dview (+ offset 24))} - 3 {:c1x (.getFloat32 dview (+ offset 4)) - :c1y (.getFloat32 dview (+ offset 8)) - :c2x (.getFloat32 dview (+ offset 12)) - :c2y (.getFloat32 dview (+ offset 16)) - :x (.getFloat32 dview (+ offset 20)) - :y (.getFloat32 dview (+ offset 24))} - 4 {})] - (recur (inc index) - (conj result {:command command - :params params}))) - result)))) + "Converts the a buffer to a path content vector" + [buffer] + (assert (instance? js/ArrayBuffer buffer) "expected ArrayBuffer instance") + (let [total (/ (.-byteLength buffer) command-size) + dview (new js/DataView buffer)] + (loop [index 0 + result []] + (if (< index total) + (let [offset (* index command-size) + type (.getUint16 dview (+ offset 0)) + command (case type + 1 :move-to + 2 :line-to + 3 :curve-to + 4 :close-path) + params (case type + 1 {:x (.getFloat32 dview (+ offset 20)) + :y (.getFloat32 dview (+ offset 24))} + 2 {:x (.getFloat32 dview (+ offset 20)) + :y (.getFloat32 dview (+ offset 24))} + 3 {:c1x (.getFloat32 dview (+ offset 4)) + :c1y (.getFloat32 dview (+ offset 8)) + :c2x (.getFloat32 dview (+ offset 12)) + :c2y (.getFloat32 dview (+ offset 16)) + :x (.getFloat32 dview (+ offset 20)) + :y (.getFloat32 dview (+ offset 24))} + 4 {})] + (recur (inc index) + (conj result {:command command + :params params}))) + result)))) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 759997460..c316961a3 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -7,7 +7,6 @@ mod state; mod utils; mod view; -use shapes::RawPathData; use skia_safe as skia; use crate::state::State; @@ -107,7 +106,7 @@ pub extern "C" fn use_shape(a: u32, b: u32, c: u32, d: u32) { pub extern "C" fn set_shape_selrect(left: f32, top: f32, right: f32, bottom: f32) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.selrect.set_ltrb(left, top, right, bottom); + shape.set_selrect(left, top, right, bottom); } } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index d07cb96eb..74639741f 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -6,7 +6,7 @@ use uuid::Uuid; use crate::debug; use crate::math::Rect; -use crate::shapes::{draw_image_in_container, Fill, Image, Shape}; +use crate::shapes::{draw_image_in_container, Fill, Image, Kind, Shape}; use crate::view::Viewbox; struct GpuState { @@ -224,7 +224,7 @@ impl RenderState { self.drawing_surface.canvas().concat(&matrix); for fill in shape.fills().rev() { - self.render_fill(fill, shape.selrect); + self.render_fill(fill, shape.selrect, &shape.kind); } let mut paint = skia::Paint::default(); @@ -281,22 +281,30 @@ impl RenderState { self.flush(); } - fn render_fill(&mut self, fill: &Fill, selrect: Rect) { - if let Fill::Image(image_fill) = fill { - let image = self.images.get(&image_fill.id()); - if let Some(image) = image { - draw_image_in_container( - &self.drawing_surface.canvas(), - &image, - image_fill.size(), - selrect, - &fill.to_paint(&selrect), - ); + fn render_fill(&mut self, fill: &Fill, selrect: Rect, kind: &Kind) { + match (fill, kind) { + (Fill::Image(image_fill), kind) => { + let image = self.images.get(&image_fill.id()); + if let Some(image) = image { + draw_image_in_container( + &self.drawing_surface.canvas(), + &image, + image_fill.size(), + kind, + &fill.to_paint(&selrect), + ); + } + } + (_, Kind::Rect(rect)) => { + self.drawing_surface + .canvas() + .draw_rect(rect, &fill.to_paint(&selrect)); + } + (_, Kind::Path(path)) => { + self.drawing_surface + .canvas() + .draw_path(&path.to_skia_path(), &fill.to_paint(&selrect)); } - } else { - self.drawing_surface - .canvas() - .draw_rect(selrect, &fill.to_paint(&selrect)); } } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index b6f679891..273908511 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -13,7 +13,7 @@ pub use paths::*; #[derive(Debug, Clone, PartialEq)] pub enum Kind { - Rect, + Rect(math::Rect), Path(Path), } @@ -62,7 +62,7 @@ impl Shape { Self { id, children: Vec::::new(), - kind: Kind::Rect, + kind: Kind::Rect(math::Rect::new_empty()), selrect: math::Rect::new_empty(), transform: Matrix::identity(), rotation: 0., @@ -73,6 +73,13 @@ impl Shape { } } + pub fn set_selrect(&mut self, left: f32, top: f32, right: f32, bottom: f32) { + self.selrect.set_ltrb(left, top, right, bottom); + if let Kind::Rect(_) = self.kind { + self.kind = Kind::Rect(self.selrect.to_owned()); + } + } + pub fn translation(&self) -> (f32, f32) { (self.transform.e, self.transform.f) } diff --git a/render-wasm/src/shapes/images.rs b/render-wasm/src/shapes/images.rs index d073d4b7e..85b96d1e2 100644 --- a/render-wasm/src/shapes/images.rs +++ b/render-wasm/src/shapes/images.rs @@ -3,17 +3,24 @@ use skia_safe as skia; pub type Image = skia::Image; +use crate::shapes::Kind; + pub fn draw_image_in_container( canvas: &skia::Canvas, image: &Image, size: (i32, i32), - container: skia::Rect, + kind: &Kind, paint: &skia::Paint, ) { let width = size.0 as f32; let height = size.1 as f32; let image_aspect_ratio = width / height; + let container = match kind { + Kind::Rect(r) => r.to_owned(), + Kind::Path(p) => p.to_skia_path().bounds().to_owned(), + }; + // Container size let container_width = container.width(); let container_height = container.height(); @@ -42,7 +49,14 @@ pub fn draw_image_in_container( canvas.save(); // Set the clipping rectangle to the container bounds - canvas.clip_rect(container, skia::ClipOp::Intersect, true); + match kind { + Kind::Rect(_) => { + canvas.clip_rect(container, skia::ClipOp::Intersect, true); + } + Kind::Path(p) => { + canvas.clip_path(&p.to_skia_path(), skia::ClipOp::Intersect, true); + } + } // Draw the image with the calculated destination rectangle canvas.draw_image_rect(image, None, dest_rect, &paint);