From 966e942a7ffe0e4913637bf42f6992d81b06e75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Wed, 13 Nov 2024 16:39:26 +0100 Subject: [PATCH] :tada: Implement drawing with blend mode (single fill) --- common/src/app/common/types/shape/impl.cljc | 12 +++--- frontend/src/app/render_wasm.cljs | 41 +++++++++++++++++---- render-wasm/src/main.rs | 8 ++++ render-wasm/src/render.rs | 5 ++- render-wasm/src/shapes.rs | 36 +++++++++++++++++- 5 files changed, 87 insertions(+), 15 deletions(-) diff --git a/common/src/app/common/types/shape/impl.cljc b/common/src/app/common/types/shape/impl.cljc index a6ac5555f..16893e7f0 100644 --- a/common/src/app/common/types/shape/impl.cljc +++ b/common/src/app/common/types/shape/impl.cljc @@ -20,6 +20,7 @@ (defonce wasm-set-shape-transform (constantly nil)) (defonce wasm-set-shape-rotation (constantly nil)) (defonce wasm-set-shape-fills (constantly nil)) +(defonce wasm-set-shape-blend-mode (constantly nil)) (defonce wasm-set-shape-children (constantly nil)) (cr/defrecord Shape [id name type x y width height rotation selrect points @@ -115,11 +116,12 @@ (when *wasm-sync* (wasm-use-shape (:id coll)) (case k - :selrect (wasm-set-shape-selrect v) - :rotation (wasm-set-shape-rotation v) - :transform (wasm-set-shape-transform v) - :fills (wasm-set-shape-fills v) - :shapes (wasm-set-shape-children v) + :selrect (wasm-set-shape-selrect v) + :rotation (wasm-set-shape-rotation v) + :transform (wasm-set-shape-transform v) + :fills (wasm-set-shape-fills v) + :blend-mode (wasm-set-shape-blend-mode v) + :shapes (wasm-set-shape-children v) nil)) (let [delegate (.-delegate ^ShapeProxy coll) delegate' (assoc delegate k v)] diff --git a/frontend/src/app/render_wasm.cljs b/frontend/src/app/render_wasm.cljs index 3c662b50c..4560ecdb6 100644 --- a/frontend/src/app/render_wasm.cljs +++ b/frontend/src/app/render_wasm.cljs @@ -74,24 +74,50 @@ [r g b] (cc/hex->rgb (:fill-color fill))] (._add_shape_solid_fill ^js internal-module r g b a)))) +(defn set-shape-blend-mode + [blend-mode] + ;; These values correspond to skia::BlendMode representation + ;; https://rust-skia.github.io/doc/skia_safe/enum.BlendMode.html + (let [encoded-blend (case blend-mode + :normal 3 + :darken 16 + :multiply 24 + :color-burn 19 + :lighten 17 + :screen 14 + :color-dodge 18 + :overlay 15 + :soft-light 21 + :hard-light 20 + :difference 22 + :exclusion 23 + :hue 25 + :saturation 26 + :color 27 + :luminosity 28 + 3)] + (._set_shape_blend_mode ^js internal-module encoded-blend))) + (defn set-objects [objects] (let [shapes (into [] xform (vals objects)) total-shapes (count shapes)] (loop [index 0] (when (< index total-shapes) - (let [shape (nth shapes index) - id (dm/get-prop shape :id) - selrect (dm/get-prop shape :selrect) - rotation (dm/get-prop shape :rotation) - transform (dm/get-prop shape :transform) - fills (dm/get-prop shape :fills) - children (dm/get-prop shape :shapes)] + (let [shape (nth shapes index) + id (dm/get-prop shape :id) + selrect (dm/get-prop shape :selrect) + rotation (dm/get-prop shape :rotation) + transform (dm/get-prop shape :transform) + fills (dm/get-prop shape :fills) + children (dm/get-prop shape :shapes) + blend-mode (dm/get-prop shape :blend-mode)] (use-shape id) (set-shape-selrect selrect) (set-shape-rotation rotation) (set-shape-transform transform) (set-shape-fills fills) + (set-shape-blend-mode blend-mode) (set-shape-children children) (recur (inc index))))))) @@ -155,4 +181,5 @@ (set! app.common.types.shape.impl/wasm-set-shape-transform set-shape-transform) (set! app.common.types.shape.impl/wasm-set-shape-rotation set-shape-rotation) (set! app.common.types.shape.impl/wasm-set-shape-fills set-shape-fills) +(set! app.common.types.shape.impl/wasm-set-shape-blend-mode set-shape-blend-mode) (set! app.common.types.shape.impl/wasm-set-shape-children set-shape-children) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 52e97c0e2..ce202d1ea 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -128,6 +128,14 @@ pub extern "C" fn clear_shape_fills() { } } +#[no_mangle] +pub extern "C" fn set_shape_blend_mode(mode: i32) { + let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); + if let Some(shape) = state.current_shape() { + shape.set_blend_mode(shapes::BlendMode::from(mode)); + } +} + fn main() { render::init_gl(); } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index ce79fda6a..9dc0b6bcf 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -141,7 +141,10 @@ fn render_single_shape(surface: &mut skia::Surface, shape: &Shape) { surface.canvas().concat(&matrix); + // TODO: use blend mode for the shape as a whole, not in each fill for fill in shape.fills().rev() { - surface.canvas().draw_rect(r, &fill.to_paint()); + let mut p = fill.to_paint(); + p.set_blend_mode(shape.blend_mode.into()); + surface.canvas().draw_rect(r, &p); } } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index eb44322ce..d37ed8c19 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -72,7 +72,6 @@ impl Fill { p.set_color(*color); p.set_style(skia::PaintStyle::Fill); p.set_anti_alias(true); - // TODO: get proper blend mode. See https://tree.taiga.io/project/penpot/task/9275 p.set_blend_mode(skia::BlendMode::SrcOver); p } @@ -80,15 +79,43 @@ impl Fill { } } +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct BlendMode(skia::BlendMode); + +impl Default for BlendMode { + fn default() -> Self { + BlendMode(skia::BlendMode::SrcOver) + } +} + +impl From for BlendMode { + fn from(value: i32) -> Self { + if value <= skia::BlendMode::Luminosity as i32 { + unsafe { Self(std::mem::transmute(value)) } + } else { + Self::default() + } + } +} + +impl Into for BlendMode { + fn into(self) -> skia::BlendMode { + match self { + Self(skia_blend) => skia_blend, + } + } +} + #[derive(Debug, Clone)] pub struct Shape { pub id: Uuid, - pub children: Vec::, + pub children: Vec, pub kind: Kind, pub selrect: Rect, pub transform: Matrix, pub rotation: f32, fills: Vec, + pub blend_mode: BlendMode, } impl Shape { @@ -101,6 +128,7 @@ impl Shape { transform: Matrix::identity(), rotation: 0., fills: vec![], + blend_mode: BlendMode::default(), } } @@ -127,4 +155,8 @@ impl Shape { pub fn clear_fills(&mut self) { self.fills.clear(); } + + pub fn set_blend_mode(&mut self, mode: BlendMode) { + self.blend_mode = mode; + } }