diff --git a/common/src/app/common/types/shape/impl.cljc b/common/src/app/common/types/shape/impl.cljc index 45a0f9e13..ed798d1d5 100644 --- a/common/src/app/common/types/shape/impl.cljc +++ b/common/src/app/common/types/shape/impl.cljc @@ -19,6 +19,7 @@ (defonce wasm-set-shape-selrect (constantly nil)) (defonce wasm-set-shape-transform (constantly nil)) (defonce wasm-set-shape-rotation (constantly nil)) +(defonce wasm-set-shape-fills (constantly nil)) (defonce wasm-set-shapes (constantly nil)) (cr/defrecord Shape [id name type x y width height rotation selrect points @@ -118,6 +119,7 @@ :rotation (wasm-set-shape-rotation v) :transform (wasm-set-shape-transform v) :shapes (wasm-set-shapes v) + :fills (wasm-set-shape-fills 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 f954140ff..27231af09 100644 --- a/frontend/src/app/render_wasm.cljs +++ b/frontend/src/app/render_wasm.cljs @@ -7,6 +7,7 @@ (ns app.render-wasm "A WASM based render API" (:require + [app.common.colors :as cc] [app.common.data.macros :as dm] [app.common.types.shape.impl :as ctsi] [app.common.uuid :as uuid] @@ -65,6 +66,14 @@ (let [buffer (uuid/uuid->u32 id)] (._add_child_shape ^js internal-module (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))))) +(defn set-shape-fills + [fills] + (._clear_shape_fills ^js internal-module) + (doseq [fill (filter #(contains? % :fill-color) fills)] + (let [a (:fill-opacity fill) + [r g b] (cc/hex->rgb (:fill-color fill))] + (._add_shape_solid_fill ^js internal-module r g b a)))) + (defn set-objects [objects] (let [shapes (into [] xform (vals objects)) @@ -76,11 +85,13 @@ 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)] (use-shape id) (set-shape-selrect selrect) (set-shape-rotation rotation) (set-shape-transform transform) + (set-shape-fills fills) (set-shapes children) (recur (inc index))))))) @@ -143,4 +154,5 @@ (set! app.common.types.shape.impl/wasm-set-shape-selrect set-shape-selrect) (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-shapes set-shapes) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index c54074e9a..2d6271aa4 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -32,15 +32,6 @@ pub unsafe extern "C" fn resize_surface(width: i32, height: i32) { state.set_surface(surface); } -/// Draws a rect at the specified coordinates with the give ncolor -/// # Safety -#[no_mangle] -pub unsafe extern "C" fn draw_rect(x1: f32, y1: f32, x2: f32, y2: f32) { - let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); - let r = skia::Rect::new(x1, y1, x2, y2); - render::render_rect(&mut state.render_state.surface, r, skia::Color::RED); -} - #[no_mangle] pub unsafe extern "C" fn draw_all_shapes(zoom: f32, pan_x: f32, pan_y: f32) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); @@ -155,6 +146,24 @@ pub extern "C" fn clear_child_shapes() { } } +#[no_mangle] +pub extern "C" fn add_shape_solid_fill(r: u8, g: u8, b: u8, a: f32) { + let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); + if let Some(shape) = state.current_shape.as_deref_mut() { + let alpha: u8 = (a * 0xff as f32).floor() as u8; + let color = skia::Color::from_argb(alpha, r, g, b); + shape.add_fill(shapes::Fill::from(color)); + } +} + +#[no_mangle] +pub extern "C" fn clear_shape_fills() { + let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); + if let Some(shape) = state.current_shape.as_deref_mut() { + shape.clear_fills(); + } +} + fn main() { render::init_gl(); } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 68a51d41b..db98861c2 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -75,14 +75,6 @@ pub(crate) fn create_surface(gpu_state: &mut GpuState, width: i32, height: i32) .unwrap() } -pub(crate) fn render_rect(surface: &mut skia::Surface, rect: skia::Rect, color: skia::Color) { - let mut paint = skia::Paint::default(); - paint.set_style(skia::PaintStyle::Fill); - paint.set_color(color); - paint.set_anti_alias(true); - surface.canvas().draw_rect(rect, &paint); -} - pub(crate) fn render_shape_tree(state: &mut State, id: Uuid) { let shape = state.shapes.get(&id).unwrap(); @@ -133,9 +125,7 @@ fn render_single_shape(surface: &mut skia::Surface, shape: &Shape) { surface.canvas().concat(&matrix); - let mut color = skia::Color::RED; - if skew_x != 0. { - color = skia::Color::BLACK; + for fill in shape.fills() { + surface.canvas().draw_rect(r, &fill.to_paint()); } - render_rect(surface, r, color); } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 022ac1fff..0e6f49e19 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -1,7 +1,6 @@ +use skia_safe as skia; use uuid::Uuid; -use crate::state::State; - #[derive(Debug, Clone, Copy)] pub enum Kind { None, @@ -29,6 +28,8 @@ pub struct Rect { pub y2: f32, } +type Color = skia::Color; + #[derive(Debug, Clone, Copy)] pub struct Matrix { pub a: f32, @@ -52,6 +53,33 @@ impl Matrix { } } +#[derive(Debug, Clone, PartialEq)] +pub enum Fill { + Solid(Color), // TODO: add more fills here +} + +impl From for Fill { + fn from(value: Color) -> Self { + Self::Solid(value) + } +} + +impl Fill { + pub fn to_paint(&self) -> skia::Paint { + match self { + Self::Solid(color) => { + let mut p = skia::Paint::default(); + 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::DstOver); + p + } + } + } +} + #[derive(Debug, Clone)] pub struct Shape { pub id: Uuid, @@ -60,17 +88,19 @@ pub struct Shape { pub selrect: Rect, pub transform: Matrix, pub rotation: f32, + fills: Vec, } impl Shape { pub fn new(id: Uuid) -> Self { Self { id, - shapes: Vec::::new(), + shapes: vec![], kind: Kind::Rect, selrect: Rect::default(), transform: Matrix::identity(), rotation: 0., + fills: vec![], } } @@ -85,4 +115,16 @@ impl Shape { pub fn skew(&self) -> (f32, f32) { (self.transform.c, self.transform.b) } + + pub fn fills(&self) -> std::slice::Iter { + self.fills.iter() + } + + pub fn add_fill(&mut self, f: Fill) { + self.fills.push(f) + } + + pub fn clear_fills(&mut self) { + self.fills.clear(); + } }