diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index a54c7c0dc6..8297738804 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -47,9 +47,9 @@ (def ^:const MODIFIER-U32-SIZE (/ MODIFIER-U8-SIZE 4)) (def ^:const MODIFIER-TRANSFORM-U8-OFFSET-SIZE 16) -(def ^:const GRID-LAYOUT-ROW-U8-SIZE 5) -(def ^:const GRID-LAYOUT-COLUMN-U8-SIZE 5) -(def ^:const GRID-LAYOUT-CELL-U8-SIZE 37) +(def ^:const GRID-LAYOUT-ROW-U8-SIZE 8) +(def ^:const GRID-LAYOUT-COLUMN-U8-SIZE 8) +(def ^:const GRID-LAYOUT-CELL-U8-SIZE 36) (def dpr (if use-dpr? (if (exists? js/window) js/window.devicePixelRatio 1.0) 1.0)) @@ -364,18 +364,14 @@ [bool-type] (h/call wasm/internal-module "_set_shape_bool_type" (sr/translate-bool-type bool-type))) -(defn- translate-blur-type - [blur-type] - (case blur-type - :layer-blur 1 - 0)) - (defn set-shape-blur [blur] - (let [type (-> blur :type sr/translate-blur-type) - hidden (:hidden blur) - value (:value blur)] - (h/call wasm/internal-module "_set_shape_blur" type hidden value))) + (if (some? blur) + (let [type (-> blur :type sr/translate-blur-type) + hidden (:hidden blur) + value (:value blur)] + (h/call wasm/internal-module "_set_shape_blur" type hidden value)) + (h/call wasm/internal-module "_clear_shape_blur"))) (defn set-shape-corners [corners] @@ -457,13 +453,9 @@ dview (mem/get-data-view)] (reduce (fn [offset {:keys [type value]}] - ;; NOTE: because of the nature of the grid row data - ;; structure memory layout we can't use fully 32 bits - ;; alligned writes, so for heteregeneus writes we use - ;; the buffer abstraction (DataView) for perform - ;; surgical writes. (-> offset (mem/write-u8 dview (sr/translate-grid-track-type type)) + (+ 3) ;; padding (mem/write-f32 dview value) (mem/assert-written offset GRID-LAYOUT-ROW-U8-SIZE))) @@ -479,17 +471,11 @@ dview (mem/get-data-view)] (reduce (fn [offset {:keys [type value]}] - ;; NOTE: because of the nature of the grid column data - ;; structure memory layout we can't use fully 32 bits - ;; alligned writes, so for heteregeneus writes we use - ;; the buffer abstraction (DataView) for perform - ;; surgical writes. (-> offset (mem/write-u8 dview (sr/translate-grid-track-type type)) + (+ 3) ;; padding (mem/write-f32 dview value) (mem/assert-written offset GRID-LAYOUT-COLUMN-U8-SIZE))) - - offset entries) @@ -504,38 +490,17 @@ (reduce-kv (fn [offset _ cell] (let [shape-id (-> (get cell :shapes) first)] (-> offset - ;; row: [u8; 4], (mem/write-i32 dview (get cell :row)) - - ;; row_span: [u8; 4], (mem/write-i32 dview (get cell :row-span)) - - ;; column: [u8; 4], (mem/write-i32 dview (get cell :column)) - - ;; column_span: [u8; 4], (mem/write-i32 dview (get cell :column-span)) - ;; has_align_self: u8, - (mem/write-bool dview (some? (get cell :align-self))) - - ;; align_self: u8, - (mem/write-u8 dview (get cell :align-self)) - - ;; has_justify_self: u8, - (mem/write-bool dview (get cell :justify-self)) - - ;; justify_self: u8, + (mem/write-u8 dview (sr/translate-align-self (get cell :align-self))) (mem/write-u8 dview (sr/translate-justify-self (get cell :justify-self))) - ;; has_shape_id: u8, - ;; (.set heap (sr/bool->u8 (d/not-empty? (:shapes cell))) (+ current-offset 20)) - (mem/write-u8 dview (some? shape-id)) + ;; padding + (+ 2) - ;; shape_id_a: [u8; 4], - ;; shape_id_b: [u8; 4], - ;; shape_id_c: [u8; 4], - ;; shape_id_d: [u8; 4], (mem/write-uuid dview (d/nilv shape-id uuid/zero)) (mem/assert-written offset GRID-LAYOUT-CELL-U8-SIZE)))) @@ -589,7 +554,7 @@ (d/nilv max-w 0) has-min-w (d/nilv min-w 0) - (some? align-self) + (d/nilv align-self 0) is-absolute (d/nilv z-index)))) @@ -756,10 +721,9 @@ (set-shape-hidden hidden) (set-shape-children children) (set-shape-corners corners) + (set-shape-blur blur) (when (and (= type :group) masked) (set-masked masked)) - (when (some? blur) - (set-shape-blur blur)) (when (= type :bool) (set-shape-bool-type bool-type)) (when (and (some? content) @@ -1073,28 +1037,29 @@ (->> (js/dynamicImport (str uri)) (p/mcat (fn [module] (let [default (unchecked-get module "default") - serializers #js{:blur-type (unchecked-get module "BlurType") - :bool-type (unchecked-get module "BoolType") - :font-style (unchecked-get module "FontStyle") - :flex-direction (unchecked-get module "FlexDirection") - :grid-direction (unchecked-get module "GridDirection") - :grow-type (unchecked-get module "GrowType") - :align-items (unchecked-get module "AlignItems") - :align-self (unchecked-get module "AlignSelf") - :align-content (unchecked-get module "AlignContent") - :justify-items (unchecked-get module "JustifyItems") - :justify-content (unchecked-get module "JustifyContent") - :justify-self (unchecked-get module "JustifySelf") - :wrap-type (unchecked-get module "WrapType") - :grid-track-type (unchecked-get module "GridTrackType") - :shadow-style (unchecked-get module "ShadowStyle") - :stroke-style (unchecked-get module "StrokeStyle") - :stroke-cap (unchecked-get module "StrokeCap") - :shape-type (unchecked-get module "Type") - :constraint-h (unchecked-get module "ConstraintH") - :constraint-v (unchecked-get module "ConstraintV") - :sizing (unchecked-get module "Sizing") - :vertical-align (unchecked-get module "VerticalAlign") + serializers #js{:blur-type (unchecked-get module "RawBlurType") + :blend-mode (unchecked-get module "RawBlendMode") + :bool-type (unchecked-get module "RawBoolType") + :font-style (unchecked-get module "RawFontStyle") + :flex-direction (unchecked-get module "RawFlexDirection") + :grid-direction (unchecked-get module "RawGridDirection") + :grow-type (unchecked-get module "RawGrowType") + :align-items (unchecked-get module "RawAlignItems") + :align-self (unchecked-get module "RawAlignSelf") + :align-content (unchecked-get module "RawAlignContent") + :justify-items (unchecked-get module "RawJustifyItems") + :justify-content (unchecked-get module "RawJustifyContent") + :justify-self (unchecked-get module "RawJustifySelf") + :wrap-type (unchecked-get module "RawWrapType") + :grid-track-type (unchecked-get module "RawGridTrackType") + :shadow-style (unchecked-get module "RawShadowStyle") + :stroke-style (unchecked-get module "RawStrokeStyle") + :stroke-cap (unchecked-get module "RawStrokeCap") + :shape-type (unchecked-get module "RawShapeType") + :constraint-h (unchecked-get module "RawConstraintH") + :constraint-v (unchecked-get module "RawConstraintV") + :sizing (unchecked-get module "RawSizing") + :vertical-align (unchecked-get module "RawVerticalAlign") :fill-data (unchecked-get module "RawFillData") :segment-data (unchecked-get module "RawSegmentData")}] (set! wasm/serializers serializers) diff --git a/frontend/src/app/render_wasm/serializers.cljs b/frontend/src/app/render_wasm/serializers.cljs index e6f1729c8a..4c718c83fa 100644 --- a/frontend/src/app/render_wasm/serializers.cljs +++ b/frontend/src/app/render_wasm/serializers.cljs @@ -85,24 +85,9 @@ (defn translate-blend-mode [blend-mode] - (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)) + (let [values (unchecked-get wasm/serializers "blend-mode") + default (unchecked-get values "normal")] + (d/nilv (unchecked-get values (d/name blend-mode)) default))) (defn translate-constraint-h [type] @@ -126,7 +111,7 @@ (defn translate-blur-type [blur-type] (let [values (unchecked-get wasm/serializers "blur-type") - default (unchecked-get values "none")] + default (unchecked-get values "layer-blur")] (d/nilv (unchecked-get values (d/name blur-type)) default))) (defn translate-layout-flex-dir @@ -184,13 +169,15 @@ (defn translate-align-self [align-self] - (let [values (unchecked-get wasm/serializers "align-self")] - (unchecked-get values (d/name align-self)))) + (let [values (unchecked-get wasm/serializers "align-self") + default (unchecked-get values "none")] + (d/nilv (unchecked-get values (d/name align-self)) default))) (defn translate-justify-self [justify-self] - (let [values (unchecked-get wasm/serializers "justify-self")] - (unchecked-get values (d/name justify-self)))) + (let [values (unchecked-get wasm/serializers "justify-self") + default (unchecked-get values "none")] + (d/nilv (unchecked-get values (d/name justify-self)) default))) (defn translate-shadow-style [style] diff --git a/render-wasm/docs/serialization.md b/render-wasm/docs/serialization.md index 0b41bdb561..41c4741ba7 100644 --- a/render-wasm/docs/serialization.md +++ b/render-wasm/docs/serialization.md @@ -221,16 +221,6 @@ Shadow styles are serialized as `u8`: | 3 | Stretch | | \_ | error | -### Align self - -| Value | Field | -| ----- | ------- | -| 0 | Start | -| 1 | End | -| 2 | Center | -| 3 | Stretch | -| \_ | error | - ### Align Content | Value | Field | @@ -269,25 +259,27 @@ Shadow styles are serialized as `u8`: ### Align Self -| Value | Field | -| ----- | ------- | -| 0 | Auto | -| 1 | Start | -| 2 | End | -| 3 | Center | -| 4 | Stretch | -| \_ | error | +| Value | Field | +| ----- | ------------- | +| 0 | (none) | +| 1 | Auto | +| 2 | Start | +| 3 | End | +| 4 | Center | +| 5 | Stretch | +| \_ | (unsupported) | ### Justify Self -| Value | Field | -| ----- | ------- | -| 0 | Auto | -| 1 | Start | -| 2 | End | -| 3 | Center | -| 4 | Stretch | -| \_ | error | +| Value | Field | +| ----- | ------------- | +| 0 | (none) | +| 1 | Auto | +| 2 | Start | +| 3 | End | +| 4 | Center | +| 5 | Stretch | +| \_ | (unsupported) | ### Wrap type diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 8a9a5fab30..aa061da8af 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -18,7 +18,7 @@ mod wasm; use indexmap::IndexSet; use math::{Bounds, Matrix}; use mem::SerializableResult; -use shapes::{BoolType, StructureEntry, StructureEntryType, TransformEntry, Type}; +use shapes::{StructureEntry, StructureEntryType, TransformEntry}; use skia_safe as skia; use state::State; use utils::uuid_from_u32_quartet; @@ -238,20 +238,6 @@ pub extern "C" fn set_shape_masked_group(masked: bool) { }); } -#[no_mangle] -pub extern "C" fn set_shape_bool_type(raw_bool_type: u8) { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_bool_type(BoolType::from(raw_bool_type)); - }); -} - -#[no_mangle] -pub extern "C" fn set_shape_type(shape_type: u8) { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_shape_type(Type::from(shape_type)); - }); -} - #[no_mangle] pub extern "C" fn set_shape_selrect(left: f32, top: f32, right: f32, bottom: f32) { with_state_mut!(state, { @@ -365,13 +351,6 @@ pub extern "C" fn set_shape_svg_raw_content() { }); } -#[no_mangle] -pub extern "C" fn set_shape_blend_mode(mode: i32) { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_blend_mode(render::BlendMode::from(mode)); - }); -} - #[no_mangle] pub extern "C" fn set_shape_opacity(opacity: f32) { with_current_shape_mut!(state, |shape: &mut Shape| { @@ -386,13 +365,6 @@ pub extern "C" fn set_shape_hidden(hidden: bool) { }); } -#[no_mangle] -pub extern "C" fn set_shape_blur(blur_type: u8, hidden: bool, value: f32) { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_blur(blur_type, hidden, value); - }); -} - #[no_mangle] pub extern "C" fn set_shape_corners(r1: f32, r2: f32, r3: f32, r4: f32) { with_current_shape_mut!(state, |shape: &mut Shape| { @@ -528,31 +500,6 @@ pub extern "C" fn set_modifiers() { }); } -#[no_mangle] -pub extern "C" fn add_shape_shadow( - raw_color: u32, - blur: f32, - spread: f32, - x: f32, - y: f32, - raw_style: u8, - hidden: bool, -) { - with_current_shape_mut!(state, |shape: &mut Shape| { - let color = skia::Color::new(raw_color); - let style = shapes::ShadowStyle::from(raw_style); - let shadow = shapes::Shadow::new(color, blur, spread, (x, y), style, hidden); - shape.add_shadow(shadow); - }); -} - -#[no_mangle] -pub extern "C" fn clear_shape_shadows() { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.clear_shadows(); - }); -} - #[no_mangle] pub extern "C" fn update_shape_tiles() { with_state_mut!(state, { diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 35feb470a0..f45bb3ef2f 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1,4 +1,3 @@ -mod blend; mod debug; mod fills; pub mod filters; @@ -37,7 +36,6 @@ use crate::wapi; use crate::math; use crate::math::bools; -pub use blend::BlendMode; pub use fonts::*; pub use images::*; @@ -253,7 +251,7 @@ pub(crate) struct RenderState { // can affect its child elements if they don't specify one themselves. If the planned // migration to remove group-level fills is completed, this code should be removed. pub nested_fills: Vec>, - pub nested_blurs: Vec>, + pub nested_blurs: Vec>, // FIXME: why is this an option? pub show_grid: Option, pub focus_mode: FocusMode, } @@ -568,14 +566,15 @@ impl RenderState { } } - if !shape.blur.hidden && shape.blur.blur_type == BlurType::LayerBlur { - nested_blur_value += shape.blur.value.powf(2.); + if let Some(blur) = shape.blur { + if !blur.hidden { + nested_blur_value += blur.value.powf(2.); + } } if nested_blur_value > 0. { - shape - .to_mut() - .set_blur(BlurType::LayerBlur as u8, false, nested_blur_value.sqrt()); + let blur = Blur::new(BlurType::LayerBlur, false, nested_blur_value.sqrt()); + shape.to_mut().set_blur(Some(blur)); } let center = shape.center(); @@ -1170,7 +1169,7 @@ impl RenderState { plain_shape.to_mut().add_stroke(Stroke { fill: Fill::Solid(SolidColor(skia::Color::BLACK)), width: stroke.width, - style: stroke.style.clone(), + style: stroke.style, cap_end: stroke.cap_end, cap_start: stroke.cap_start, kind: stroke.kind, @@ -1449,7 +1448,9 @@ impl RenderState { match element.shape_type { Type::Frame(_) | Type::Group(_) => { - self.nested_blurs.push(Some(element.blur)); + if let Some(blur) = element.blur { + self.nested_blurs.push(Some(blur)); + } } _ => {} } diff --git a/render-wasm/src/render/blend.rs b/render-wasm/src/render/blend.rs deleted file mode 100644 index b232867e6d..0000000000 --- a/render-wasm/src/render/blend.rs +++ /dev/null @@ -1,30 +0,0 @@ -use skia_safe as skia; - -// TODO: maybe move this to the wasm module? -// TODO: find a way to use the ToJS derive macro for this -#[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 From for skia::BlendMode { - fn from(val: BlendMode) -> Self { - match val { - BlendMode(skia_blend) => skia_blend, - } - } -} diff --git a/render-wasm/src/render/strokes.rs b/render-wasm/src/render/strokes.rs index 784705bffb..8346d9af7e 100644 --- a/render-wasm/src/render/strokes.rs +++ b/render-wasm/src/render/strokes.rs @@ -182,7 +182,6 @@ fn handle_stroke_cap( ) { paint.set_style(skia::PaintStyle::Fill); match cap { - StrokeCap::None => {} StrokeCap::LineArrow => { // We also draw this square cap to fill the gap between the path and the arrow draw_square_cap(canvas, paint, p1, p2, width, 0.); @@ -241,23 +240,27 @@ fn handle_stroke_caps( paint_stroke.set_image_filter(filter.clone()); } - handle_stroke_cap( - canvas, - stroke.cap_start, - stroke.width, - &mut paint_stroke, - first_point, - &points[1], - ); + if let Some(cap) = stroke.cap_start { + handle_stroke_cap( + canvas, + cap, + stroke.width, + &mut paint_stroke, + first_point, + &points[1], + ); + } - handle_stroke_cap( - canvas, - stroke.cap_end, - stroke.width, - &mut paint_stroke, - last_point, - &points[c_points - 2], - ); + if let Some(cap) = stroke.cap_end { + handle_stroke_cap( + canvas, + cap, + stroke.width, + &mut paint_stroke, + last_point, + &points[c_points - 2], + ); + } } } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index b785c4c786..6da311714d 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -1,13 +1,12 @@ -use macros::ToJs; use skia_safe::{self as skia}; -use crate::render::BlendMode; use crate::uuid::Uuid; use std::borrow::Cow; use std::cell::OnceCell; use std::collections::{HashMap, HashSet}; use std::iter::once; +mod blend; mod blurs; mod bools; mod corners; @@ -27,6 +26,7 @@ mod text; pub mod text_paths; mod transform; +pub use blend::*; pub use blurs::*; pub use bools::*; pub use corners::*; @@ -55,36 +55,19 @@ const MIN_VISIBLE_SIZE: f32 = 2.0; const ANTIALIAS_THRESHOLD: f32 = 15.0; const MIN_STROKE_WIDTH: f32 = 0.001; -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq)] pub enum Type { - Frame(Frame) = 0, - Group(Group) = 1, - Bool(Bool) = 2, - Rect(Rect) = 3, - Path(Path) = 4, - Text(TextContent) = 5, - Circle = 6, - SVGRaw(SVGRaw) = 7, + Frame(Frame), + Group(Group), + Bool(Bool), + Rect(Rect), + Path(Path), + Text(TextContent), + Circle, // FIXME: shouldn't this have a rect inside, like the Rect variant? + SVGRaw(SVGRaw), } impl Type { - // TODO: move this to the wasm module, use transmute - pub fn from(value: u8) -> Self { - match value { - 0 => Type::Frame(Frame::default()), - 1 => Type::Group(Group::default()), - 2 => Type::Bool(Bool::default()), - 3 => Type::Rect(Rect::default()), - 4 => Type::Path(Path::default()), - 5 => Type::Text(TextContent::default()), - 6 => Type::Circle, - 7 => Type::SVGRaw(SVGRaw::default()), - _ => Type::Rect(Rect::default()), - } - } - pub fn corners(&self) -> Option { match self { Type::Rect(Rect { corners, .. }) => *corners, @@ -145,77 +128,29 @@ impl Type { } } -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum ConstraintH { - Left = 0, - Right = 1, - Leftright = 2, - Center = 3, - Scale = 4, + Left, + Right, + LeftRight, + Center, + Scale, } -impl ConstraintH { - // TODO: we should implement a proper From trait for this - // TODO: use transmute - pub fn from(value: u8) -> Option { - match value { - 0 => Some(Self::Left), - 1 => Some(Self::Right), - 2 => Some(Self::Leftright), - 3 => Some(Self::Center), - 4 => Some(Self::Scale), - _ => None, - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum VerticalAlign { - Top = 0, - Center = 1, - Bottom = 2, + Top, + Center, + Bottom, } -impl VerticalAlign { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from(value: u8) -> Self { - match value { - 0 => Self::Top, - 1 => Self::Center, - 2 => Self::Bottom, - _ => Self::Top, - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum ConstraintV { - Top = 0, - Bottom = 1, - Topbottom = 2, - Center = 3, - Scale = 4, -} - -impl ConstraintV { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from(value: u8) -> Option { - match value { - 0 => Some(Self::Top), - 1 => Some(Self::Bottom), - 2 => Some(Self::Topbottom), - 3 => Some(Self::Center), - 4 => Some(Self::Scale), - _ => None, - } - } + Top, + Bottom, + TopBottom, + Center, + Scale, } pub type Color = skia::Color; @@ -236,7 +171,7 @@ pub struct Shape { pub strokes: Vec, pub blend_mode: BlendMode, pub vertical_align: VerticalAlign, - pub blur: Blur, + pub blur: Option, pub opacity: f32, pub hidden: bool, pub svg: Option, @@ -265,7 +200,7 @@ impl Shape { vertical_align: VerticalAlign::Top, opacity: 1., hidden: false, - blur: Blur::default(), + blur: None, svg: None, svg_attrs: HashMap::new(), shadows: Vec::with_capacity(1), @@ -285,7 +220,11 @@ impl Shape { .shadows .iter_mut() .for_each(|s| s.scale_content(value)); - result.blur.scale_content(value); + + if let Some(blur) = result.blur.as_mut() { + blur.scale_content(value); + } + result .layout_item .iter_mut() @@ -518,39 +457,39 @@ impl Shape { } } - pub fn set_grid_columns(&mut self, tracks: Vec) { + pub fn set_grid_columns(&mut self, tracks: Vec) { let Type::Frame(frame_data) = &mut self.shape_type else { return; }; let Some(Layout::GridLayout(_, grid_data)) = &mut frame_data.layout else { return; }; - grid_data.columns = tracks.iter().map(GridTrack::from_raw).collect(); + grid_data.columns = tracks; } - pub fn set_grid_rows(&mut self, tracks: Vec) { + pub fn set_grid_rows(&mut self, tracks: Vec) { let Type::Frame(frame_data) = &mut self.shape_type else { return; }; let Some(Layout::GridLayout(_, grid_data)) = &mut frame_data.layout else { return; }; - grid_data.rows = tracks.iter().map(GridTrack::from_raw).collect(); + grid_data.rows = tracks; } - pub fn set_grid_cells(&mut self, cells: Vec) { + pub fn set_grid_cells(&mut self, cells: Vec) { let Type::Frame(frame_data) = &mut self.shape_type else { return; }; let Some(Layout::GridLayout(_, grid_data)) = &mut frame_data.layout else { return; }; - grid_data.cells = cells.iter().map(GridCell::from_raw).collect(); + grid_data.cells = cells; } - pub fn set_blur(&mut self, blur_type: u8, hidden: bool, value: f32) { + pub fn set_blur(&mut self, blur: Option) { self.invalidate_extrect(); - self.blur = Blur::new(blur_type, hidden, value); + self.blur = blur; } pub fn add_child(&mut self, id: Uuid) { @@ -672,7 +611,7 @@ impl Shape { self.svg_attrs.insert(name, value); } - pub fn blend_mode(&self) -> crate::render::BlendMode { + pub fn blend_mode(&self) -> BlendMode { self.blend_mode } @@ -854,11 +793,13 @@ impl Shape { } } - if self.blur.blur_type != blurs::BlurType::None && !self.blur.hidden { - rect.left -= self.blur.value; - rect.top -= self.blur.value; - rect.right += self.blur.value; - rect.bottom += self.blur.value; + if let Some(blur) = self.blur { + if !blur.hidden { + rect.left -= blur.value; + rect.top -= blur.value; + rect.right += blur.value; + rect.bottom += blur.value; + } } // For groups and frames without clipping, extend the bounding rectangle to include all nested shapes @@ -985,35 +926,27 @@ impl Shape { } pub fn image_filter(&self, scale: f32) -> Option { - if !self.blur.hidden { - match self.blur.blur_type { - BlurType::None => None, + self.blur + .filter(|blur| !blur.hidden) + .and_then(|blur| match blur.blur_type { BlurType::LayerBlur => skia::image_filters::blur( - (self.blur.value * scale, self.blur.value * scale), + (blur.value * scale, blur.value * scale), None, None, None, ), - } - } else { - None - } + }) } #[allow(dead_code)] pub fn mask_filter(&self, scale: f32) -> Option { - if !self.blur.hidden { - match self.blur.blur_type { - BlurType::None => None, - BlurType::LayerBlur => skia::MaskFilter::blur( - skia::BlurStyle::Normal, - self.blur.value * scale, - Some(true), - ), - } - } else { - None - } + self.blur + .filter(|blur| !blur.hidden) + .and_then(|blur| match blur.blur_type { + BlurType::LayerBlur => { + skia::MaskFilter::blur(skia::BlurStyle::Normal, blur.value * scale, Some(true)) + } + }) } pub fn is_recursive(&self) -> bool { diff --git a/render-wasm/src/shapes/blend.rs b/render-wasm/src/shapes/blend.rs new file mode 100644 index 0000000000..a225b58592 --- /dev/null +++ b/render-wasm/src/shapes/blend.rs @@ -0,0 +1,18 @@ +use skia_safe as skia; + +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct BlendMode(pub skia::BlendMode); + +impl Default for BlendMode { + fn default() -> Self { + BlendMode(skia::BlendMode::SrcOver) + } +} + +impl From for skia::BlendMode { + fn from(val: BlendMode) -> Self { + match val { + BlendMode(skia_blend) => skia_blend, + } + } +} diff --git a/render-wasm/src/shapes/blurs.rs b/render-wasm/src/shapes/blurs.rs index 101e125d5f..4232f0ee1c 100644 --- a/render-wasm/src/shapes/blurs.rs +++ b/render-wasm/src/shapes/blurs.rs @@ -1,10 +1,6 @@ -use macros::ToJs; - -#[derive(Debug, Clone, Copy, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum BlurType { - None = 0, - LayerBlur = 1, + LayerBlur, } #[derive(Debug, Clone, Copy, PartialEq)] @@ -14,28 +10,10 @@ pub struct Blur { pub value: f32, } -// TODO: maybe move this to the wasm module? -impl From for BlurType { - // TODO: use transmute - fn from(value: u8) -> Self { - match value { - 1 => BlurType::LayerBlur, - _ => BlurType::None, - } - } -} - impl Blur { - pub fn default() -> Self { + pub fn new(blur_type: BlurType, hidden: bool, value: f32) -> Self { Blur { - blur_type: BlurType::None, - hidden: true, - value: 0., - } - } - pub fn new(blur_type: u8, hidden: bool, value: f32) -> Self { - Blur { - blur_type: BlurType::from(blur_type), + blur_type, hidden, value, } diff --git a/render-wasm/src/shapes/bools.rs b/render-wasm/src/shapes/bools.rs index 381eba3e65..9fa6610887 100644 --- a/render-wasm/src/shapes/bools.rs +++ b/render-wasm/src/shapes/bools.rs @@ -1,5 +1,3 @@ -use macros::ToJs; - use super::Path; #[derive(Debug, Clone, PartialEq)] @@ -8,14 +6,12 @@ pub struct Bool { pub path: Path, } -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, Copy, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum BoolType { - Union = 0, - Difference = 1, - Intersection = 2, - Exclusion = 3, + Union, + Difference, + Intersection, + Exclusion, } impl Default for Bool { @@ -27,20 +23,6 @@ impl Default for Bool { } } -// TODO: maybe move this to the wasm module? -impl From for BoolType { - // TODO: use transmute - fn from(value: u8) -> Self { - match value { - 0 => Self::Union, - 1 => Self::Difference, - 2 => Self::Intersection, - 3 => Self::Exclusion, - _ => Self::default(), - } - } -} - impl Default for BoolType { fn default() -> Self { Self::Union diff --git a/render-wasm/src/shapes/fonts.rs b/render-wasm/src/shapes/fonts.rs index 296e08570e..86ab5d3897 100644 --- a/render-wasm/src/shapes/fonts.rs +++ b/render-wasm/src/shapes/fonts.rs @@ -1,26 +1,11 @@ use std::fmt; use crate::uuid::Uuid; -use macros::ToJs; -// TODO: maybe move this to the wasm module? -#[derive(Debug, PartialEq, Clone, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum FontStyle { - Normal = 0, - Italic = 1, -} - -// TODO: maybe move this to the wasm module? -impl From for FontStyle { - // TODO: use transmute - fn from(value: u8) -> Self { - match value { - 0 => Self::Normal, - 1 => Self::Italic, - _ => Self::Normal, - } - } + Normal, + Italic, } impl fmt::Display for FontStyle { diff --git a/render-wasm/src/shapes/layouts.rs b/render-wasm/src/shapes/layouts.rs index 9fefa58224..9bbb2dee08 100644 --- a/render-wasm/src/shapes/layouts.rs +++ b/render-wasm/src/shapes/layouts.rs @@ -1,6 +1,3 @@ -use macros::ToJs; - -use crate::utils::uuid_from_u32_quartet; use crate::uuid::Uuid; #[derive(Debug, Clone, PartialEq)] @@ -24,208 +21,70 @@ impl Layout { } } -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum FlexDirection { - Row = 0, - RowReverse = 1, - Column = 2, - ColumnReverse = 3, + Row, + RowReverse, + Column, + ColumnReverse, } -// TODO: maybe move this to the wasm module? -impl FlexDirection { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Row, - 1 => Self::RowReverse, - 2 => Self::Column, - 3 => Self::ColumnReverse, - _ => unreachable!(), - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum GridDirection { - Row = 0, - Column = 1, + Row, + Column, } -// TODO: maybe move this to the wasm module? -impl GridDirection { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Row, - 1 => Self::Column, - _ => unreachable!(), - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum AlignItems { - Start = 0, - End = 1, - Center = 2, - Stretch = 3, + Start, + End, + Center, + Stretch, } -// TODO: maybe move this to the wasm module? -impl AlignItems { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Start, - 1 => Self::End, - 2 => Self::Center, - 3 => Self::Stretch, - _ => unreachable!(), - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum AlignContent { - Start = 0, - End = 1, - Center = 2, - SpaceBetween = 3, - SpaceAround = 4, - SpaceEvenly = 5, - Stretch = 6, + Start, + End, + Center, + SpaceBetween, + SpaceAround, + SpaceEvenly, + Stretch, } -// TODO: maybe move this to the wasm module? -impl AlignContent { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Start, - 1 => Self::End, - 2 => Self::Center, - 3 => Self::SpaceBetween, - 4 => Self::SpaceAround, - 5 => Self::SpaceEvenly, - 6 => Self::Stretch, - _ => unreachable!(), - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum JustifyItems { - Start = 0, - End = 1, - Center = 2, - Stretch = 3, + Start, + End, + Center, + Stretch, } -// TODO: maybe move this to the wasm module? -impl JustifyItems { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Start, - 1 => Self::End, - 2 => Self::Center, - 3 => Self::Stretch, - _ => unreachable!(), - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum JustifyContent { - Start = 0, - End = 1, - Center = 2, - SpaceBetween = 3, - SpaceAround = 4, - SpaceEvenly = 5, - Stretch = 6, + Start, + End, + Center, + SpaceBetween, + SpaceAround, + SpaceEvenly, + Stretch, } -// TODO: maybe move this to the wasm module? -impl JustifyContent { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Start, - 1 => Self::End, - 2 => Self::Center, - 3 => Self::SpaceBetween, - 4 => Self::SpaceAround, - 5 => Self::SpaceEvenly, - 6 => Self::Stretch, - _ => unreachable!(), - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq)] pub enum WrapType { - Wrap = 0, - Nowrap = 1, + Wrap, + NoWrap, } -// TODO: maybe move this to the wasm module? -impl WrapType { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Wrap, - 1 => Self::Nowrap, - _ => unreachable!(), - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Copy, Clone, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum GridTrackType { - Percent = 0, - Flex = 1, - Auto = 2, - Fixed = 3, -} - -// TODO: maybe move this to the wasm module? -impl GridTrackType { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Percent, - 1 => Self::Flex, - 2 => Self::Auto, - 3 => Self::Fixed, - _ => unreachable!(), - } - } + Percent, + Flex, + Auto, + Fixed, } #[derive(Debug, Clone, PartialEq)] @@ -235,13 +94,6 @@ pub struct GridTrack { } impl GridTrack { - pub fn from_raw(raw: &RawGridTrack) -> Self { - Self { - track_type: GridTrackType::from_u8(raw.track_type), - value: f32::from_le_bytes(raw.value), - } - } - pub fn scale_content(&mut self, value: f32) { if self.track_type == GridTrackType::Fixed { self.value *= value; @@ -249,7 +101,7 @@ impl GridTrack { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Copy)] pub struct GridCell { pub row: i32, pub row_span: i32, @@ -260,58 +112,11 @@ pub struct GridCell { pub shape: Option, } -impl GridCell { - pub fn from_raw(raw: &RawGridCell) -> Self { - Self { - row: i32::from_le_bytes(raw.row), - row_span: i32::from_le_bytes(raw.row_span), - column: i32::from_le_bytes(raw.column), - column_span: i32::from_le_bytes(raw.column_span), - align_self: if raw.has_align_self == 1 { - AlignSelf::from_u8(raw.align_self) - } else { - None - }, - justify_self: if raw.has_justify_self == 1 { - JustifySelf::from_u8(raw.justify_self) - } else { - None - }, - shape: if raw.has_shape_id == 1 { - Some(uuid_from_u32_quartet( - u32::from_le_bytes(raw.shape_id_a), - u32::from_le_bytes(raw.shape_id_b), - u32::from_le_bytes(raw.shape_id_c), - u32::from_le_bytes(raw.shape_id_d), - )) - } else { - None - }, - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum Sizing { - Fill = 0, - Fix = 1, - Auto = 2, -} - -// TODO: maybe move this to the wasm module? -impl Sizing { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Self { - match value { - 0 => Self::Fill, - 1 => Self::Fix, - 2 => Self::Auto, - _ => unreachable!(), - } - } + Fill, + Fix, + Auto, } #[derive(Debug, Clone, PartialEq)] @@ -339,36 +144,17 @@ impl LayoutData { } } -// TODO: maybe move this to the wasm module? -#[derive(Debug, Copy, Clone, PartialEq, ToJs)] +#[derive(Debug, Copy, Clone, PartialEq)] #[repr(u8)] pub enum AlignSelf { - Auto = 0, - Start = 1, - End = 2, - Center = 3, - Stretch = 4, + Auto, + Start, + End, + Center, + Stretch, } -// TODO: maybe move this to the wasm module? -impl AlignSelf { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Option { - match value { - 0 => Some(Self::Auto), - 1 => Some(Self::Start), - 2 => Some(Self::End), - 3 => Some(Self::Center), - 4 => Some(Self::Stretch), - _ => None, - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Copy, Clone, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum JustifySelf { Auto = 0, Start = 1, @@ -377,22 +163,6 @@ pub enum JustifySelf { Stretch = 4, } -// TODO: maybe move this to the wasm module? -impl JustifySelf { - // TODO: implement a proper From trait for this - // TODO: use transmute - pub fn from_u8(value: u8) -> Option { - match value { - 0 => Some(Self::Auto), - 1 => Some(Self::Start), - 2 => Some(Self::End), - 3 => Some(Self::Center), - 4 => Some(Self::Stretch), - _ => None, - } - } -} - #[derive(Debug, Clone, PartialEq)] pub struct FlexData { pub direction: FlexDirection, @@ -443,65 +213,6 @@ impl GridData { } } -// TODO: move this to the wasm module -// FIXME: use transmute -#[derive(Debug)] -#[repr(C)] -pub struct RawGridTrack { - track_type: u8, - value: [u8; 4], -} - -impl RawGridTrack { - pub fn from_bytes(bytes: [u8; 5]) -> Self { - Self { - track_type: bytes[0], - value: [bytes[1], bytes[2], bytes[3], bytes[4]], - } - } -} - -#[derive(Debug)] -#[repr(C)] -pub struct RawGridCell { - row: [u8; 4], - row_span: [u8; 4], - column: [u8; 4], - column_span: [u8; 4], - has_align_self: u8, - align_self: u8, - has_justify_self: u8, - justify_self: u8, - has_shape_id: u8, - shape_id_a: [u8; 4], - shape_id_b: [u8; 4], - shape_id_c: [u8; 4], - shape_id_d: [u8; 4], -} - -impl RawGridCell { - pub fn from_bytes(bytes: [u8; 37]) -> Self { - Self { - row: [bytes[0], bytes[1], bytes[2], bytes[3]], - row_span: [bytes[4], bytes[5], bytes[6], bytes[7]], - column: [bytes[8], bytes[9], bytes[10], bytes[11]], - column_span: [bytes[12], bytes[13], bytes[14], bytes[15]], - - has_align_self: bytes[16], - align_self: bytes[17], - - has_justify_self: bytes[18], - justify_self: bytes[19], - - has_shape_id: bytes[20], - shape_id_a: [bytes[21], bytes[22], bytes[23], bytes[24]], - shape_id_b: [bytes[25], bytes[26], bytes[27], bytes[28]], - shape_id_c: [bytes[29], bytes[30], bytes[31], bytes[32]], - shape_id_d: [bytes[33], bytes[34], bytes[35], bytes[36]], - } - } -} - #[derive(Debug, Clone, PartialEq, Copy)] pub struct LayoutItem { pub margin_top: f32, diff --git a/render-wasm/src/shapes/modifiers/constraints.rs b/render-wasm/src/shapes/modifiers/constraints.rs index 1dd051480e..1a838c0a9a 100644 --- a/render-wasm/src/shapes/modifiers/constraints.rs +++ b/render-wasm/src/shapes/modifiers/constraints.rs @@ -13,7 +13,7 @@ pub fn calculate_resize( ConstraintH::Left | ConstraintH::Right | ConstraintH::Center => { parent_before.width() / f32::max(0.01, parent_after.width()) } - ConstraintH::Leftright => { + ConstraintH::LeftRight => { let left = parent_before.left(child_before.nw); let right = parent_before.right(child_before.ne); let target_width = parent_after.width() - left - right; @@ -26,7 +26,7 @@ pub fn calculate_resize( ConstraintV::Top | ConstraintV::Bottom | ConstraintV::Center => { parent_before.height() / f32::max(0.01, parent_after.height()) } - ConstraintV::Topbottom => { + ConstraintV::TopBottom => { let top = parent_before.top(child_before.nw); let bottom = parent_before.bottom(child_before.sw); let target_height = parent_after.height() - top - bottom; @@ -51,7 +51,7 @@ pub fn calculate_displacement( child_after: &Bounds, ) -> Option<(f32, f32)> { let delta_x = match constraint_h { - ConstraintH::Left | ConstraintH::Leftright => { + ConstraintH::Left | ConstraintH::LeftRight => { let target_left = parent_before.left(child_before.nw); let current_left = parent_after.left(child_after.nw); target_left - current_left @@ -71,7 +71,7 @@ pub fn calculate_displacement( }; let delta_y = match constraint_v { - ConstraintV::Top | ConstraintV::Topbottom => { + ConstraintV::Top | ConstraintV::TopBottom => { let target_top = parent_before.top(child_before.nw); let current_top = parent_after.top(child_after.nw); target_top - current_top diff --git a/render-wasm/src/shapes/shadows.rs b/render-wasm/src/shapes/shadows.rs index 65d0a51f31..28d940eddd 100644 --- a/render-wasm/src/shapes/shadows.rs +++ b/render-wasm/src/shapes/shadows.rs @@ -1,27 +1,12 @@ -use macros::ToJs; use skia_safe::{self as skia, image_filters, ImageFilter, Paint}; use super::Color; use crate::render::filters::compose_filters; -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, Copy, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum ShadowStyle { - Drop = 0, - Inner = 1, -} - -// TODO: maybe move this to the wasm module? -impl From for ShadowStyle { - // TODO: use transmute - fn from(value: u8) -> Self { - match value { - 0 => Self::Drop, - 1 => Self::Inner, - _ => Self::default(), - } - } + Drop, + Inner, } impl Default for ShadowStyle { @@ -40,7 +25,6 @@ pub struct Shadow { hidden: bool, } -// TODO: create shadows out of a chunk of bytes impl Shadow { pub fn new( color: Color, diff --git a/render-wasm/src/shapes/strokes.rs b/render-wasm/src/shapes/strokes.rs index b5a1a84a9c..108b03bcd3 100644 --- a/render-wasm/src/shapes/strokes.rs +++ b/render-wasm/src/shapes/strokes.rs @@ -1,61 +1,26 @@ use crate::shapes::fills::{Fill, SolidColor}; -use macros::ToJs; use skia_safe::{self as skia, Rect}; use std::collections::HashMap; use super::Corners; -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, PartialEq, ToJs)] -#[repr(u8)] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum StrokeStyle { - Solid = 0, - Dotted = 1, - Dashed = 2, - Mixed = 3, + Solid, + Dotted, + Dashed, + Mixed, } -// TODO: maybe move this to the wasm module? -impl From for StrokeStyle { - // TODO: use transmute - fn from(value: u8) -> Self { - match value { - 1 => StrokeStyle::Dotted, - 2 => StrokeStyle::Dashed, - 3 => StrokeStyle::Mixed, - _ => StrokeStyle::Solid, - } - } -} - -// TODO: maybe move this to the wasm module? -#[derive(Debug, Clone, Copy, PartialEq, ToJs)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum StrokeCap { - None = 0, - LineArrow = 1, - TriangleArrow = 2, - SquareMarker = 3, - CircleMarker = 4, - DiamondMarker = 5, - Round = 6, - Square = 7, -} - -// TODO: maybe move this to the wasm module? -impl From for StrokeCap { - // TODO: use transmute - fn from(value: u8) -> Self { - match value { - 1 => StrokeCap::LineArrow, - 2 => StrokeCap::TriangleArrow, - 3 => StrokeCap::SquareMarker, - 4 => StrokeCap::CircleMarker, - 5 => StrokeCap::DiamondMarker, - 6 => StrokeCap::Round, - 7 => StrokeCap::Square, - _ => StrokeCap::None, - } - } + LineArrow, + TriangleArrow, + SquareMarker, + CircleMarker, + DiamondMarker, + Round, + Square, } #[derive(Debug, Clone, Copy, PartialEq)] @@ -70,8 +35,8 @@ pub struct Stroke { pub fill: Fill, pub width: f32, pub style: StrokeStyle, - pub cap_end: StrokeCap, - pub cap_start: StrokeCap, + pub cap_end: Option, + pub cap_start: Option, pub kind: StrokeKind, } @@ -85,35 +50,50 @@ impl Stroke { } } - pub fn new_center_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) -> Self { + pub fn new_center_stroke( + width: f32, + style: StrokeStyle, + cap_start: Option, + cap_end: Option, + ) -> Self { Stroke { fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)), width, - style: StrokeStyle::from(style), - cap_end: StrokeCap::from(cap_end), - cap_start: StrokeCap::from(cap_start), + style, + cap_end, + cap_start, kind: StrokeKind::Center, } } - pub fn new_inner_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) -> Self { + pub fn new_inner_stroke( + width: f32, + style: StrokeStyle, + cap_start: Option, + cap_end: Option, + ) -> Self { Stroke { fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)), width, - style: StrokeStyle::from(style), - cap_end: StrokeCap::from(cap_end), - cap_start: StrokeCap::from(cap_start), + style, + cap_end, + cap_start, kind: StrokeKind::Inner, } } - pub fn new_outer_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) -> Self { + pub fn new_outer_stroke( + width: f32, + style: StrokeStyle, + cap_start: Option, + cap_end: Option, + ) -> Self { Stroke { fill: Fill::Solid(SolidColor(skia::Color::TRANSPARENT)), width, - style: StrokeStyle::from(style), - cap_end: StrokeCap::from(cap_end), - cap_start: StrokeCap::from(cap_start), + style, + cap_end, + cap_start, kind: StrokeKind::Outer, } } diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index 2309096c1f..0e4a11b127 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -4,7 +4,6 @@ use crate::{ textlayout::paragraph_builder_group_from_text, }; -use macros::ToJs; use skia_safe::{ self as skia, paint::{self, Paint}, @@ -19,26 +18,11 @@ use crate::utils::uuid_from_u32; use crate::wasm::fills::parse_fills_from_bytes; use crate::Uuid; -// TODO: maybe move this to the wasm module? -#[derive(Debug, PartialEq, Clone, Copy, ToJs)] -#[repr(u8)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum GrowType { - Fixed = 0, - AutoWidth = 1, - AutoHeight = 2, -} - -// TODO: maybe move this to the wasm module? -impl GrowType { - // TODO: use transmute - pub fn from(grow_type: u8) -> Self { - match grow_type { - 0 => Self::Fixed, - 1 => Self::AutoWidth, - 2 => Self::AutoHeight, - _ => unreachable!(), - } - } + Fixed, + AutoWidth, + AutoHeight, } #[derive(Debug, PartialEq, Clone)] @@ -572,10 +556,12 @@ impl RawTextData { } } +// TODO: maybe move this to the wasm module? pub struct RawTextData { pub paragraph: Paragraph, } +// TODO: maybe move this to the wasm module? impl From<&Vec> for RawTextData { fn from(bytes: &Vec) -> Self { let paragraph = RawParagraphData::try_from(&bytes[..RAW_PARAGRAPH_DATA_SIZE]).unwrap(); @@ -596,12 +582,10 @@ impl From<&Vec> for RawTextData { let font_id = uuid_from_u32(text_leaf.font_id); let font_variant_id = uuid_from_u32(text_leaf.font_variant_id); + let font_style = crate::wasm::fonts::RawFontStyle::from(text_leaf.font_style); - let font_family = FontFamily::new( - font_id, - text_leaf.font_weight as u32, - text_leaf.font_style.into(), - ); + let font_family = + FontFamily::new(font_id, text_leaf.font_weight as u32, font_style.into()); let new_text_leaf = TextLeaf::new( text, diff --git a/render-wasm/src/wasm.rs b/render-wasm/src/wasm.rs index b905da4687..8002e462c4 100644 --- a/render-wasm/src/wasm.rs +++ b/render-wasm/src/wasm.rs @@ -1,6 +1,10 @@ +pub mod blend; +pub mod blurs; pub mod fills; pub mod fonts; -pub mod layout; +pub mod layouts; pub mod paths; +pub mod shadows; +pub mod shapes; pub mod strokes; pub mod text; diff --git a/render-wasm/src/wasm/blend.rs b/render-wasm/src/wasm/blend.rs new file mode 100644 index 0000000000..d0dcbad118 --- /dev/null +++ b/render-wasm/src/wasm/blend.rs @@ -0,0 +1,65 @@ +use macros::ToJs; +use skia_safe as skia; + +use crate::shapes::BlendMode; +use crate::{with_current_shape_mut, STATE}; + +#[derive(Debug, PartialEq, Clone, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawBlendMode { + Normal = 3, + Screen = 14, + Overlay = 15, + Darken = 16, + Lighten = 17, + ColorDodge = 18, + ColorBurn = 19, + HardLight = 20, + SoftLight = 21, + Difference = 22, + Exclusion = 23, + Multiply = 24, + Hue = 25, + Saturation = 26, + Color = 27, + Luminosity = 28, +} + +impl From for RawBlendMode { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for BlendMode { + fn from(value: RawBlendMode) -> Self { + match value { + RawBlendMode::Normal => BlendMode(skia::BlendMode::SrcOver), + RawBlendMode::Screen => BlendMode(skia::BlendMode::Screen), + RawBlendMode::Overlay => BlendMode(skia::BlendMode::Overlay), + RawBlendMode::Darken => BlendMode(skia::BlendMode::Darken), + RawBlendMode::Lighten => BlendMode(skia::BlendMode::Lighten), + RawBlendMode::ColorDodge => BlendMode(skia::BlendMode::ColorDodge), + RawBlendMode::ColorBurn => BlendMode(skia::BlendMode::ColorBurn), + RawBlendMode::HardLight => BlendMode(skia::BlendMode::HardLight), + RawBlendMode::SoftLight => BlendMode(skia::BlendMode::SoftLight), + RawBlendMode::Difference => BlendMode(skia::BlendMode::Difference), + RawBlendMode::Exclusion => BlendMode(skia::BlendMode::Exclusion), + RawBlendMode::Multiply => BlendMode(skia::BlendMode::Multiply), + RawBlendMode::Hue => BlendMode(skia::BlendMode::Hue), + RawBlendMode::Saturation => BlendMode(skia::BlendMode::Saturation), + RawBlendMode::Color => BlendMode(skia::BlendMode::Color), + RawBlendMode::Luminosity => BlendMode(skia::BlendMode::Luminosity), + } + } +} + +#[no_mangle] +pub extern "C" fn set_shape_blend_mode(mode: u8) { + let mode = RawBlendMode::from(mode); + + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_blend_mode(mode.into()); + }); +} diff --git a/render-wasm/src/wasm/blurs.rs b/render-wasm/src/wasm/blurs.rs new file mode 100644 index 0000000000..700ac053bc --- /dev/null +++ b/render-wasm/src/wasm/blurs.rs @@ -0,0 +1,40 @@ +use macros::ToJs; + +use crate::shapes::{Blur, BlurType}; +use crate::{with_current_shape_mut, STATE}; + +#[derive(Debug, Clone, Copy, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawBlurType { + LayerBlur = 0, // odd naming to comply with cljs value +} + +impl From for RawBlurType { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for BlurType { + fn from(value: RawBlurType) -> Self { + match value { + RawBlurType::LayerBlur => BlurType::LayerBlur, + } + } +} + +#[no_mangle] +pub extern "C" fn set_shape_blur(blur_type: u8, hidden: bool, value: f32) { + with_current_shape_mut!(state, |shape: &mut Shape| { + let blur_type = RawBlurType::from(blur_type); + shape.set_blur(Some(Blur::new(blur_type.into(), hidden, value))); + }); +} + +#[no_mangle] +pub extern "C" fn clear_shape_blur() { + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_blur(None); + }); +} diff --git a/render-wasm/src/wasm/fonts.rs b/render-wasm/src/wasm/fonts.rs index b4295e63d6..bdc85ac124 100644 --- a/render-wasm/src/wasm/fonts.rs +++ b/render-wasm/src/wasm/fonts.rs @@ -1,9 +1,33 @@ +use macros::ToJs; + use crate::mem; +use crate::shapes::{FontFamily, FontStyle}; use crate::utils::uuid_from_u32_quartet; use crate::with_state_mut; use crate::STATE; -use crate::shapes::FontFamily; +#[derive(Debug, PartialEq, Clone, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawFontStyle { + Normal = 0, + Italic = 1, +} + +impl From for RawFontStyle { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for FontStyle { + fn from(value: RawFontStyle) -> Self { + match value { + RawFontStyle::Normal => FontStyle::Normal, + RawFontStyle::Italic => FontStyle::Italic, + } + } +} #[no_mangle] pub extern "C" fn store_font( @@ -23,8 +47,9 @@ pub extern "C" fn store_font( with_state_mut!(state, { let id = uuid_from_u32_quartet(a2, b2, c2, d2); let font_bytes = mem::bytes(); + let font_style = RawFontStyle::from(style); - let family = FontFamily::new(id, weight, style.into()); + let family = FontFamily::new(id, weight, font_style.into()); let _ = state .render_state_mut() @@ -52,7 +77,8 @@ pub extern "C" fn is_font_uploaded( ) -> bool { with_state_mut!(state, { let id = uuid_from_u32_quartet(a, b, c, d); - let family = FontFamily::new(id, weight, style.into()); + let font_style = RawFontStyle::from(style); + let family = FontFamily::new(id, weight, font_style.into()); let res = state.render_state().fonts().has_family(&family, is_emoji); res diff --git a/render-wasm/src/wasm/layout.rs b/render-wasm/src/wasm/layout.rs deleted file mode 100644 index 2eb7139433..0000000000 --- a/render-wasm/src/wasm/layout.rs +++ /dev/null @@ -1,249 +0,0 @@ -use crate::mem; -use crate::shapes::{self, ConstraintH, ConstraintV, VerticalAlign}; -use crate::{uuid_from_u32_quartet, with_current_shape_mut, with_state, with_state_mut, STATE}; - -#[no_mangle] -pub extern "C" fn set_shape_constraint_h(constraint: u8) { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_constraint_h(ConstraintH::from(constraint)); - }); -} - -#[no_mangle] -pub extern "C" fn set_shape_constraint_v(constraint: u8) { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_constraint_v(ConstraintV::from(constraint)); - }); -} - -#[no_mangle] -pub extern "C" fn clear_shape_constraints() { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.clear_constraints(); - }); -} - -#[no_mangle] -pub extern "C" fn set_shape_vertical_align(align: u8) { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_vertical_align(VerticalAlign::from(align)); - }); -} - -#[no_mangle] -pub extern "C" fn clear_shape_layout() { - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.clear_layout(); - }); -} - -#[no_mangle] -pub extern "C" fn set_flex_layout_data( - dir: u8, - row_gap: f32, - column_gap: f32, - align_items: u8, - align_content: u8, - justify_items: u8, - justify_content: u8, - wrap_type: u8, - padding_top: f32, - padding_right: f32, - padding_bottom: f32, - padding_left: f32, -) { - let dir = shapes::FlexDirection::from_u8(dir); - let align_items = shapes::AlignItems::from_u8(align_items); - let align_content = shapes::AlignContent::from_u8(align_content); - let justify_items = shapes::JustifyItems::from_u8(justify_items); - let justify_content = shapes::JustifyContent::from_u8(justify_content); - let wrap_type = shapes::WrapType::from_u8(wrap_type); - - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_flex_layout_data( - dir, - row_gap, - column_gap, - align_items, - align_content, - justify_items, - justify_content, - wrap_type, - padding_top, - padding_right, - padding_bottom, - padding_left, - ); - }); -} - -#[no_mangle] -pub extern "C" fn set_layout_child_data( - margin_top: f32, - margin_right: f32, - margin_bottom: f32, - margin_left: f32, - h_sizing: u8, - v_sizing: u8, - has_max_h: bool, - max_h: f32, - has_min_h: bool, - min_h: f32, - has_max_w: bool, - max_w: f32, - has_min_w: bool, - min_w: f32, - has_align_self: bool, - align_self: u8, - is_absolute: bool, - z_index: i32, -) { - let h_sizing = shapes::Sizing::from_u8(h_sizing); - let v_sizing = shapes::Sizing::from_u8(v_sizing); - let max_h = if has_max_h { Some(max_h) } else { None }; - let min_h = if has_min_h { Some(min_h) } else { None }; - let max_w = if has_max_w { Some(max_w) } else { None }; - let min_w = if has_min_w { Some(min_w) } else { None }; - let align_self = if has_align_self { - shapes::AlignSelf::from_u8(align_self) - } else { - None - }; - - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_flex_layout_child_data( - margin_top, - margin_right, - margin_bottom, - margin_left, - h_sizing, - v_sizing, - max_h, - min_h, - max_w, - min_w, - align_self, - is_absolute, - z_index, - ); - }); -} - -#[no_mangle] -pub extern "C" fn set_grid_layout_data( - dir: u8, - row_gap: f32, - column_gap: f32, - align_items: u8, - align_content: u8, - justify_items: u8, - justify_content: u8, - padding_top: f32, - padding_right: f32, - padding_bottom: f32, - padding_left: f32, -) { - let dir = shapes::GridDirection::from_u8(dir); - let align_items = shapes::AlignItems::from_u8(align_items); - let align_content = shapes::AlignContent::from_u8(align_content); - let justify_items = shapes::JustifyItems::from_u8(justify_items); - let justify_content = shapes::JustifyContent::from_u8(justify_content); - - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_grid_layout_data( - dir, - row_gap, - column_gap, - align_items, - align_content, - justify_items, - justify_content, - padding_top, - padding_right, - padding_bottom, - padding_left, - ); - }); -} - -#[no_mangle] -pub extern "C" fn set_grid_columns() { - let bytes = mem::bytes(); - - let entries: Vec<_> = bytes - .chunks(size_of::()) - .map(|data| shapes::RawGridTrack::from_bytes(data.try_into().unwrap())) - .collect(); - - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_grid_columns(entries); - }); - - mem::free_bytes(); -} - -#[no_mangle] -pub extern "C" fn set_grid_rows() { - let bytes = mem::bytes(); - - let entries: Vec<_> = bytes - .chunks(size_of::()) - .map(|data| shapes::RawGridTrack::from_bytes(data.try_into().unwrap())) - .collect(); - - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_grid_rows(entries); - }); - - mem::free_bytes(); -} - -#[no_mangle] -pub extern "C" fn set_grid_cells() { - let bytes = mem::bytes(); - - let entries: Vec<_> = bytes - .chunks(size_of::()) - .map(|data| shapes::RawGridCell::from_bytes(data.try_into().unwrap())) - .collect(); - - with_current_shape_mut!(state, |shape: &mut Shape| { - shape.set_grid_cells(entries); - }); - - mem::free_bytes(); -} - -#[no_mangle] -pub extern "C" fn show_grid(a: u32, b: u32, c: u32, d: u32) { - with_state_mut!(state, { - let id = uuid_from_u32_quartet(a, b, c, d); - state.render_state.show_grid = Some(id); - }); -} - -#[no_mangle] -pub extern "C" fn hide_grid() { - with_state_mut!(state, { - state.render_state.show_grid = None; - }); -} - -#[no_mangle] -pub extern "C" fn get_grid_coords(pos_x: f32, pos_y: f32) -> *mut u8 { - let row: i32; - let col: i32; - with_state!(state, { - if let Some((r, c)) = state.get_grid_coords(pos_x, pos_y) { - row = r; - col = c; - } else { - row = -1; - col = -1; - }; - }); - let mut bytes = vec![0; 8]; - bytes[0..4].clone_from_slice(&row.to_le_bytes()); - bytes[4..8].clone_from_slice(&col.to_le_bytes()); - mem::write_bytes(bytes) -} diff --git a/render-wasm/src/wasm/layouts.rs b/render-wasm/src/wasm/layouts.rs new file mode 100644 index 0000000000..a97812b519 --- /dev/null +++ b/render-wasm/src/wasm/layouts.rs @@ -0,0 +1,90 @@ +use crate::shapes::Sizing; +use crate::{with_current_shape_mut, STATE}; +use macros::ToJs; + +mod align; +mod constraints; +mod flex; +mod grid; + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawSizing { + Fill = 0, + Fix = 1, + Auto = 2, +} + +impl From for RawSizing { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for Sizing { + fn from(value: RawSizing) -> Self { + match value { + RawSizing::Fill => Sizing::Fill, + RawSizing::Fix => Sizing::Fix, + RawSizing::Auto => Sizing::Auto, + } + } +} + +#[no_mangle] +pub extern "C" fn clear_shape_layout() { + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.clear_layout(); + }); +} + +#[no_mangle] +pub extern "C" fn set_layout_child_data( + margin_top: f32, + margin_right: f32, + margin_bottom: f32, + margin_left: f32, + h_sizing: u8, + v_sizing: u8, + has_max_h: bool, + max_h: f32, + has_min_h: bool, + min_h: f32, + has_max_w: bool, + max_w: f32, + has_min_w: bool, + min_w: f32, + align_self: u8, + is_absolute: bool, + z_index: i32, +) { + let h_sizing = RawSizing::from(h_sizing); + let v_sizing = RawSizing::from(v_sizing); + let max_h = if has_max_h { Some(max_h) } else { None }; + let min_h = if has_min_h { Some(min_h) } else { None }; + let max_w = if has_max_w { Some(max_w) } else { None }; + let min_w = if has_min_w { Some(min_w) } else { None }; + + let raw_align_self = align::RawAlignSelf::from(align_self); + + let align_self = raw_align_self.try_into().ok(); + + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_flex_layout_child_data( + margin_top, + margin_right, + margin_bottom, + margin_left, + h_sizing.into(), + v_sizing.into(), + max_h, + min_h, + max_w, + min_w, + align_self, + is_absolute, + z_index, + ); + }); +} diff --git a/render-wasm/src/wasm/layouts/align.rs b/render-wasm/src/wasm/layouts/align.rs new file mode 100644 index 0000000000..42810588d9 --- /dev/null +++ b/render-wasm/src/wasm/layouts/align.rs @@ -0,0 +1,223 @@ +use macros::ToJs; + +use crate::shapes::{ + AlignContent, AlignItems, AlignSelf, JustifyContent, JustifyItems, JustifySelf, VerticalAlign, +}; +use crate::{with_current_shape_mut, STATE}; + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawAlignItems { + Start = 0, + End = 1, + Center = 2, + Stretch = 3, +} + +impl From for RawAlignItems { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for AlignItems { + fn from(value: RawAlignItems) -> Self { + match value { + RawAlignItems::Start => AlignItems::Start, + RawAlignItems::End => AlignItems::End, + RawAlignItems::Center => AlignItems::Center, + RawAlignItems::Stretch => AlignItems::Stretch, + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawAlignContent { + Start = 0, + End = 1, + Center = 2, + SpaceBetween = 3, + SpaceAround = 4, + SpaceEvenly = 5, + Stretch = 6, +} + +impl From for RawAlignContent { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for AlignContent { + fn from(value: RawAlignContent) -> Self { + match value { + RawAlignContent::Start => AlignContent::Start, + RawAlignContent::End => AlignContent::End, + RawAlignContent::Center => AlignContent::Center, + RawAlignContent::SpaceBetween => AlignContent::SpaceBetween, + RawAlignContent::SpaceAround => AlignContent::SpaceAround, + RawAlignContent::SpaceEvenly => AlignContent::SpaceEvenly, + RawAlignContent::Stretch => AlignContent::Stretch, + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawJustifyItems { + Start = 0, + End = 1, + Center = 2, + Stretch = 3, +} + +impl From for RawJustifyItems { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for JustifyItems { + fn from(value: RawJustifyItems) -> Self { + match value { + RawJustifyItems::Start => JustifyItems::Start, + RawJustifyItems::End => JustifyItems::End, + RawJustifyItems::Center => JustifyItems::Center, + RawJustifyItems::Stretch => JustifyItems::Stretch, + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawJustifyContent { + Start = 0, + End = 1, + Center = 2, + SpaceBetween = 3, + SpaceAround = 4, + SpaceEvenly = 5, + Stretch = 6, +} + +impl From for RawJustifyContent { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for JustifyContent { + fn from(value: RawJustifyContent) -> Self { + match value { + RawJustifyContent::Start => JustifyContent::Start, + RawJustifyContent::End => JustifyContent::End, + RawJustifyContent::Center => JustifyContent::Center, + RawJustifyContent::SpaceBetween => JustifyContent::SpaceBetween, + RawJustifyContent::SpaceAround => JustifyContent::SpaceAround, + RawJustifyContent::SpaceEvenly => JustifyContent::SpaceEvenly, + RawJustifyContent::Stretch => JustifyContent::Stretch, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawJustifySelf { + None = 0, + Auto = 1, + Start = 2, + End = 3, + Center = 4, + Stretch = 5, +} + +impl From for RawJustifySelf { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl TryFrom for JustifySelf { + type Error = (); + fn try_from(value: RawJustifySelf) -> Result { + match value { + RawJustifySelf::None => Err(()), + RawJustifySelf::Auto => Ok(JustifySelf::Auto), + RawJustifySelf::Start => Ok(JustifySelf::Start), + RawJustifySelf::End => Ok(JustifySelf::End), + RawJustifySelf::Center => Ok(JustifySelf::Center), + RawJustifySelf::Stretch => Ok(JustifySelf::Stretch), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawAlignSelf { + None = 0, + Auto = 1, + Start = 2, + End = 3, + Center = 4, + Stretch = 5, +} + +impl From for RawAlignSelf { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl TryFrom for AlignSelf { + type Error = (); + fn try_from(value: RawAlignSelf) -> Result { + match value { + RawAlignSelf::None => Err(()), + RawAlignSelf::Auto => Ok(AlignSelf::Auto), + RawAlignSelf::Start => Ok(AlignSelf::Start), + RawAlignSelf::End => Ok(AlignSelf::End), + RawAlignSelf::Center => Ok(AlignSelf::Center), + RawAlignSelf::Stretch => Ok(AlignSelf::Stretch), + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawVerticalAlign { + Top = 0, + Center = 1, + Bottom = 2, +} + +impl From for RawVerticalAlign { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for VerticalAlign { + fn from(value: RawVerticalAlign) -> Self { + match value { + RawVerticalAlign::Top => VerticalAlign::Top, + RawVerticalAlign::Center => VerticalAlign::Center, + RawVerticalAlign::Bottom => VerticalAlign::Bottom, + } + } +} + +#[no_mangle] +pub extern "C" fn set_shape_vertical_align(align: u8) { + with_current_shape_mut!(state, |shape: &mut Shape| { + let align = RawVerticalAlign::from(align); + shape.set_vertical_align(align.into()); + }); +} diff --git a/render-wasm/src/wasm/layouts/constraints.rs b/render-wasm/src/wasm/layouts/constraints.rs new file mode 100644 index 0000000000..057e739241 --- /dev/null +++ b/render-wasm/src/wasm/layouts/constraints.rs @@ -0,0 +1,85 @@ +use macros::ToJs; + +use crate::shapes::{ConstraintH, ConstraintV}; +use crate::{with_current_shape_mut, STATE}; + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawConstraintH { + Left = 0, + Right = 1, + Leftright = 2, // odd casing to comply with cljs value + Center = 3, + Scale = 4, +} + +impl From for RawConstraintH { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for ConstraintH { + fn from(value: RawConstraintH) -> Self { + match value { + RawConstraintH::Left => ConstraintH::Left, + RawConstraintH::Right => ConstraintH::Right, + RawConstraintH::Leftright => ConstraintH::LeftRight, + RawConstraintH::Center => ConstraintH::Center, + RawConstraintH::Scale => ConstraintH::Scale, + } + } +} + +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawConstraintV { + Top = 0, + Bottom = 1, + Topbottom = 2, // odd casing to comply with cljs value + Center = 3, + Scale = 4, +} + +impl From for RawConstraintV { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for ConstraintV { + fn from(value: RawConstraintV) -> Self { + match value { + RawConstraintV::Top => ConstraintV::Top, + RawConstraintV::Bottom => ConstraintV::Bottom, + RawConstraintV::Topbottom => ConstraintV::TopBottom, + RawConstraintV::Center => ConstraintV::Center, + RawConstraintV::Scale => ConstraintV::Scale, + } + } +} + +#[no_mangle] +pub extern "C" fn set_shape_constraint_h(constraint: u8) { + with_current_shape_mut!(state, |shape: &mut Shape| { + let constraint = RawConstraintH::from(constraint); + shape.set_constraint_h(Some(constraint.into())); + }); +} + +#[no_mangle] +pub extern "C" fn set_shape_constraint_v(constraint: u8) { + with_current_shape_mut!(state, |shape: &mut Shape| { + let constraint = RawConstraintV::from(constraint); + shape.set_constraint_v(Some(constraint.into())); + }); +} + +#[no_mangle] +pub extern "C" fn clear_shape_constraints() { + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.clear_constraints(); + }); +} diff --git a/render-wasm/src/wasm/layouts/flex.rs b/render-wasm/src/wasm/layouts/flex.rs new file mode 100644 index 0000000000..10d1f991e8 --- /dev/null +++ b/render-wasm/src/wasm/layouts/flex.rs @@ -0,0 +1,95 @@ +use crate::shapes::{FlexDirection, WrapType}; +use crate::{with_current_shape_mut, STATE}; +use macros::ToJs; + +use super::align; + +#[derive(Debug, Clone, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawFlexDirection { + Row = 0, + RowReverse = 1, + Column = 2, + ColumnReverse = 3, +} + +impl From for RawFlexDirection { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for FlexDirection { + fn from(value: RawFlexDirection) -> Self { + match value { + RawFlexDirection::Row => FlexDirection::Row, + RawFlexDirection::RowReverse => FlexDirection::RowReverse, + RawFlexDirection::Column => FlexDirection::Column, + RawFlexDirection::ColumnReverse => FlexDirection::ColumnReverse, + } + } +} + +#[derive(Debug, Clone, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawWrapType { + Wrap = 0, + Nowrap = 1, // odd casing to comply with cljs value +} + +impl From for RawWrapType { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for WrapType { + fn from(value: RawWrapType) -> Self { + match value { + RawWrapType::Wrap => WrapType::Wrap, + RawWrapType::Nowrap => WrapType::NoWrap, + } + } +} + +#[no_mangle] +pub extern "C" fn set_flex_layout_data( + dir: u8, + row_gap: f32, + column_gap: f32, + align_items: u8, + align_content: u8, + justify_items: u8, + justify_content: u8, + wrap_type: u8, + padding_top: f32, + padding_right: f32, + padding_bottom: f32, + padding_left: f32, +) { + let dir = RawFlexDirection::from(dir); + let align_items = align::RawAlignItems::from(align_items); + let align_content = align::RawAlignContent::from(align_content); + let justify_items = align::RawJustifyItems::from(justify_items); + let justify_content = align::RawJustifyContent::from(justify_content); + let wrap_type = RawWrapType::from(wrap_type); + + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_flex_layout_data( + dir.into(), + row_gap, + column_gap, + align_items.into(), + align_content.into(), + justify_items.into(), + justify_content.into(), + wrap_type.into(), + padding_top, + padding_right, + padding_bottom, + padding_left, + ); + }); +} diff --git a/render-wasm/src/wasm/layouts/grid.rs b/render-wasm/src/wasm/layouts/grid.rs new file mode 100644 index 0000000000..096468e514 --- /dev/null +++ b/render-wasm/src/wasm/layouts/grid.rs @@ -0,0 +1,253 @@ +use macros::ToJs; + +use crate::mem; +use crate::shapes::{GridCell, GridDirection, GridTrack, GridTrackType}; +use crate::uuid::Uuid; +use crate::{uuid_from_u32_quartet, with_current_shape_mut, with_state, with_state_mut, STATE}; + +use super::align; + +#[derive(Debug)] +#[repr(C, align(1))] +struct RawGridCell { + row: i32, + row_span: i32, + column: i32, + column_span: i32, + align_self: u8, + justify_self: u8, + _padding: u16, + shape_id_a: u32, + shape_id_b: u32, + shape_id_c: u32, + shape_id_d: u32, +} + +impl From<[u8; size_of::()]> for RawGridCell { + fn from(bytes: [u8; size_of::()]) -> Self { + unsafe { std::mem::transmute(bytes) } + } +} + +impl From for GridCell { + fn from(raw: RawGridCell) -> Self { + let raw_justify_self = super::align::RawJustifySelf::from(raw.justify_self); + let raw_align_self = super::align::RawAlignSelf::from(raw.align_self); + let shape_id = uuid_from_u32_quartet( + raw.shape_id_a, + raw.shape_id_b, + raw.shape_id_c, + raw.shape_id_d, + ); + + Self { + row: raw.row, + row_span: raw.row_span, + column: raw.column, + column_span: raw.column_span, + align_self: raw_align_self.try_into().ok(), + justify_self: raw_justify_self.try_into().ok(), + shape: if shape_id != Uuid::nil() { + Some(shape_id) + } else { + None + }, + } + } +} + +#[derive(Debug, Clone, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawGridDirection { + Row = 0, + Column = 1, +} + +impl From for RawGridDirection { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for GridDirection { + fn from(value: RawGridDirection) -> Self { + match value { + RawGridDirection::Row => Self::Row, + RawGridDirection::Column => Self::Column, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawGridTrackType { + Percent = 0, + Flex = 1, + Auto = 2, + Fixed = 3, +} + +impl From for RawGridTrackType { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for GridTrackType { + fn from(value: RawGridTrackType) -> Self { + match value { + RawGridTrackType::Percent => Self::Percent, + RawGridTrackType::Flex => Self::Flex, + RawGridTrackType::Auto => Self::Auto, + RawGridTrackType::Fixed => Self::Fixed, + } + } +} + +#[derive(Debug)] +#[repr(C, align(4))] +#[allow(dead_code)] +pub struct RawGridTrack { + track_type: RawGridTrackType, + _padding: [u8; 3], + value: f32, +} + +impl From<[u8; size_of::()]> for RawGridTrack { + fn from(bytes: [u8; size_of::()]) -> Self { + unsafe { std::mem::transmute(bytes) } + } +} + +impl From for GridTrack { + fn from(raw: RawGridTrack) -> Self { + Self { + track_type: raw.track_type.into(), + value: raw.value, + } + } +} + +#[no_mangle] +pub extern "C" fn set_grid_layout_data( + dir: u8, + row_gap: f32, + column_gap: f32, + align_items: u8, + align_content: u8, + justify_items: u8, + justify_content: u8, + padding_top: f32, + padding_right: f32, + padding_bottom: f32, + padding_left: f32, +) { + let dir = RawGridDirection::from(dir); + let align_items = align::RawAlignItems::from(align_items); + let align_content = align::RawAlignContent::from(align_content); + let justify_items = align::RawJustifyItems::from(justify_items); + let justify_content = align::RawJustifyContent::from(justify_content); + + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_grid_layout_data( + dir.into(), + row_gap, + column_gap, + align_items.into(), + align_content.into(), + justify_items.into(), + justify_content.into(), + padding_top, + padding_right, + padding_bottom, + padding_left, + ); + }); +} + +#[no_mangle] +pub extern "C" fn set_grid_columns() { + let bytes = mem::bytes(); + + let entries: Vec = bytes + .chunks(size_of::()) + .map(|data| data.try_into().unwrap()) + .map(|data: [u8; size_of::()]| RawGridTrack::from(data).into()) + .collect(); + + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_grid_columns(entries); + }); + + mem::free_bytes(); +} + +#[no_mangle] +pub extern "C" fn set_grid_rows() { + let bytes = mem::bytes(); + + let entries: Vec = bytes + .chunks(size_of::()) + .map(|data| data.try_into().unwrap()) + .map(|data: [u8; size_of::()]| RawGridTrack::from(data).into()) + .collect(); + + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_grid_rows(entries); + }); + + mem::free_bytes(); +} + +#[no_mangle] +pub extern "C" fn set_grid_cells() { + let bytes = mem::bytes(); + + let cells: Vec = bytes + .chunks(size_of::()) + .map(|data| data.try_into().expect("Invalid grid cell data")) + .map(|data: [u8; size_of::()]| RawGridCell::from(data)) + .collect(); + + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_grid_cells(cells.into_iter().map(|raw| raw.into()).collect()); + }); + + mem::free_bytes(); +} + +#[no_mangle] +pub extern "C" fn show_grid(a: u32, b: u32, c: u32, d: u32) { + with_state_mut!(state, { + let id = uuid_from_u32_quartet(a, b, c, d); + state.render_state.show_grid = Some(id); + }); +} + +#[no_mangle] +pub extern "C" fn hide_grid() { + with_state_mut!(state, { + state.render_state.show_grid = None; + }); +} + +#[no_mangle] +pub extern "C" fn get_grid_coords(pos_x: f32, pos_y: f32) -> *mut u8 { + let row: i32; + let col: i32; + with_state!(state, { + if let Some((r, c)) = state.get_grid_coords(pos_x, pos_y) { + row = r; + col = c; + } else { + row = -1; + col = -1; + }; + }); + let mut bytes = vec![0; 8]; + bytes[0..4].clone_from_slice(&row.to_le_bytes()); + bytes[4..8].clone_from_slice(&col.to_le_bytes()); + mem::write_bytes(bytes) +} diff --git a/render-wasm/src/wasm/paths.rs b/render-wasm/src/wasm/paths.rs index c31160a3c4..c08c3c076a 100644 --- a/render-wasm/src/wasm/paths.rs +++ b/render-wasm/src/wasm/paths.rs @@ -1,16 +1,15 @@ #![allow(unused_mut, unused_variables)] -use indexmap::IndexSet; use macros::ToJs; use mem::SerializableResult; -use uuid::Uuid; +use std::mem::size_of; -use crate::math::bools; -use crate::shapes::{BoolType, Path, Segment, ToPath}; -use crate::uuid; -use crate::{mem, with_current_shape, with_current_shape_mut, with_state, STATE}; +use crate::shapes::{Path, Segment, ToPath}; +use crate::{mem, with_current_shape, with_current_shape_mut, STATE}; const RAW_SEGMENT_DATA_SIZE: usize = size_of::(); +pub mod bools; + #[repr(C, u16, align(4))] #[derive(Debug, PartialEq, Clone, Copy, ToJs)] #[allow(dead_code)] @@ -183,37 +182,6 @@ pub extern "C" fn current_to_path() -> *mut u8 { mem::write_vec(result) } -#[no_mangle] -pub extern "C" fn calculate_bool(raw_bool_type: u8) -> *mut u8 { - let bytes = mem::bytes_or_empty(); - - let entries: IndexSet = bytes - .chunks(size_of::<::BytesType>()) - .map(|data| Uuid::from_bytes(data.try_into().unwrap())) - .collect(); - - mem::free_bytes(); - - let bool_type = BoolType::from(raw_bool_type); - let result; - with_state!(state, { - let path = bools::bool_from_shapes( - bool_type, - &entries, - &state.shapes, - &state.modifiers, - &state.structure, - ); - result = path - .segments() - .iter() - .copied() - .map(RawSegmentData::from_segment) - .collect(); - }); - mem::write_vec(result) -} - // Extracts a string from the bytes slice until the next null byte (0) and returns the result as a `String`. // Updates the `start` index to the end of the extracted string. fn extract_string(start: &mut usize, bytes: &[u8]) -> String { diff --git a/render-wasm/src/wasm/paths/bools.rs b/render-wasm/src/wasm/paths/bools.rs new file mode 100644 index 0000000000..cc72543d63 --- /dev/null +++ b/render-wasm/src/wasm/paths/bools.rs @@ -0,0 +1,75 @@ +use macros::ToJs; + +use indexmap::IndexSet; + +use super::RawSegmentData; +use crate::math; +use crate::shapes::BoolType; +use crate::uuid::Uuid; +use crate::{mem, SerializableResult}; +use crate::{with_current_shape_mut, with_state, STATE}; + +#[derive(Debug, Clone, Copy, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawBoolType { + Union = 0, + Difference = 1, + Intersection = 2, + Exclusion = 3, +} + +impl From for RawBoolType { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for BoolType { + fn from(value: RawBoolType) -> Self { + match value { + RawBoolType::Union => BoolType::Union, + RawBoolType::Difference => BoolType::Difference, + RawBoolType::Intersection => BoolType::Intersection, + RawBoolType::Exclusion => BoolType::Exclusion, + } + } +} + +#[no_mangle] +pub extern "C" fn set_shape_bool_type(raw_bool_type: u8) { + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.set_bool_type(RawBoolType::from(raw_bool_type).into()); + }); +} + +#[no_mangle] +pub extern "C" fn calculate_bool(raw_bool_type: u8) -> *mut u8 { + let bytes = mem::bytes_or_empty(); + + let entries: IndexSet = bytes + .chunks(size_of::<::BytesType>()) + .map(|data| Uuid::from_bytes(data.try_into().unwrap())) + .collect(); + + mem::free_bytes(); + + let bool_type = RawBoolType::from(raw_bool_type).into(); + let result; + with_state!(state, { + let path = math::bools::bool_from_shapes( + bool_type, + &entries, + &state.shapes, + &state.modifiers, + &state.structure, + ); + result = path + .segments() + .iter() + .copied() + .map(RawSegmentData::from_segment) + .collect(); + }); + mem::write_vec(result) +} diff --git a/render-wasm/src/wasm/shadows.rs b/render-wasm/src/wasm/shadows.rs new file mode 100644 index 0000000000..c7ce0b4fab --- /dev/null +++ b/render-wasm/src/wasm/shadows.rs @@ -0,0 +1,53 @@ +use macros::ToJs; +use skia_safe as skia; + +use crate::shapes::{Shadow, ShadowStyle}; +use crate::{with_current_shape_mut, STATE}; + +#[derive(Debug, Clone, Copy, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawShadowStyle { + Drop = 0, + Inner = 1, +} + +impl From for RawShadowStyle { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for ShadowStyle { + fn from(value: RawShadowStyle) -> Self { + match value { + RawShadowStyle::Drop => Self::Drop, + RawShadowStyle::Inner => Self::Inner, + } + } +} + +#[no_mangle] +pub extern "C" fn add_shape_shadow( + raw_color: u32, + blur: f32, + spread: f32, + x: f32, + y: f32, + raw_style: u8, + hidden: bool, +) { + with_current_shape_mut!(state, |shape: &mut Shape| { + let color = skia::Color::new(raw_color); + let style = RawShadowStyle::from(raw_style).into(); + let shadow = Shadow::new(color, blur, spread, (x, y), style, hidden); + shape.add_shadow(shadow); + }); +} + +#[no_mangle] +pub extern "C" fn clear_shape_shadows() { + with_current_shape_mut!(state, |shape: &mut Shape| { + shape.clear_shadows(); + }); +} diff --git a/render-wasm/src/wasm/shapes.rs b/render-wasm/src/wasm/shapes.rs new file mode 100644 index 0000000000..c2ff3b4931 --- /dev/null +++ b/render-wasm/src/wasm/shapes.rs @@ -0,0 +1,47 @@ +use macros::ToJs; + +use crate::shapes::{Bool, Frame, Group, Path, Rect, SVGRaw, TextContent, Type}; +use crate::{with_current_shape_mut, STATE}; + +#[derive(Debug, Clone, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawShapeType { + Frame = 0, + Group = 1, + Bool = 2, + Rect = 3, + Path = 4, + Text = 5, + Circle = 6, + SVGRaw = 7, +} + +impl From for RawShapeType { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for Type { + fn from(value: RawShapeType) -> Self { + match value { + RawShapeType::Frame => Type::Frame(Frame::default()), + RawShapeType::Group => Type::Group(Group::default()), + RawShapeType::Bool => Type::Bool(Bool::default()), + RawShapeType::Rect => Type::Rect(Rect::default()), + RawShapeType::Path => Type::Path(Path::default()), + RawShapeType::Text => Type::Text(TextContent::default()), + RawShapeType::Circle => Type::Circle, + RawShapeType::SVGRaw => Type::SVGRaw(SVGRaw::default()), + } + } +} + +#[no_mangle] +pub extern "C" fn set_shape_type(shape_type: u8) { + with_current_shape_mut!(state, |shape: &mut Shape| { + let shape_type = RawShapeType::from(shape_type); + shape.set_shape_type(shape_type.into()); + }); +} diff --git a/render-wasm/src/wasm/strokes.rs b/render-wasm/src/wasm/strokes.rs index 4e64fedad6..21a7e321ff 100644 --- a/render-wasm/src/wasm/strokes.rs +++ b/render-wasm/src/wasm/strokes.rs @@ -1,31 +1,118 @@ +use macros::ToJs; + use crate::mem; -use crate::shapes; +use crate::shapes::{self, StrokeCap, StrokeStyle}; use crate::with_current_shape_mut; use crate::STATE; +#[derive(Debug, Clone, PartialEq, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawStrokeStyle { + Solid = 0, + Dotted = 1, + Dashed = 2, + Mixed = 3, +} + +impl From for RawStrokeStyle { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for StrokeStyle { + fn from(value: RawStrokeStyle) -> Self { + match value { + RawStrokeStyle::Solid => StrokeStyle::Solid, + RawStrokeStyle::Dotted => StrokeStyle::Dotted, + RawStrokeStyle::Dashed => StrokeStyle::Dashed, + RawStrokeStyle::Mixed => StrokeStyle::Mixed, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawStrokeCap { + None = 0, + LineArrow = 1, + TriangleArrow = 2, + SquareMarker = 3, + CircleMarker = 4, + DiamondMarker = 5, + Round = 6, + Square = 7, +} + +impl From for RawStrokeCap { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl TryFrom for StrokeCap { + type Error = (); + + fn try_from(value: RawStrokeCap) -> Result { + match value { + RawStrokeCap::None => Err(()), + RawStrokeCap::LineArrow => Ok(StrokeCap::LineArrow), + RawStrokeCap::TriangleArrow => Ok(StrokeCap::TriangleArrow), + RawStrokeCap::SquareMarker => Ok(StrokeCap::SquareMarker), + RawStrokeCap::CircleMarker => Ok(StrokeCap::CircleMarker), + RawStrokeCap::DiamondMarker => Ok(StrokeCap::DiamondMarker), + RawStrokeCap::Round => Ok(StrokeCap::Round), + RawStrokeCap::Square => Ok(StrokeCap::Square), + } + } +} + #[no_mangle] pub extern "C" fn add_shape_center_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) { + let stroke_style = RawStrokeStyle::from(style); + let cap_start = RawStrokeCap::from(cap_start); + let cap_end = RawStrokeCap::from(cap_end); + with_current_shape_mut!(state, |shape: &mut Shape| { shape.add_stroke(shapes::Stroke::new_center_stroke( - width, style, cap_start, cap_end, + width, + stroke_style.into(), + cap_start.try_into().ok(), + cap_end.try_into().ok(), )); }); } #[no_mangle] pub extern "C" fn add_shape_inner_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) { + let stroke_style = RawStrokeStyle::from(style); + let cap_start = RawStrokeCap::from(cap_start); + let cap_end = RawStrokeCap::from(cap_end); + with_current_shape_mut!(state, |shape: &mut Shape| { shape.add_stroke(shapes::Stroke::new_inner_stroke( - width, style, cap_start, cap_end, + width, + stroke_style.into(), + cap_start.try_into().ok(), + cap_end.try_into().ok(), )); }); } #[no_mangle] pub extern "C" fn add_shape_outer_stroke(width: f32, style: u8, cap_start: u8, cap_end: u8) { + let stroke_style = RawStrokeStyle::from(style); + let cap_start = RawStrokeCap::from(cap_start); + let cap_end = RawStrokeCap::from(cap_end); + with_current_shape_mut!(state, |shape: &mut Shape| { shape.add_stroke(shapes::Stroke::new_outer_stroke( - width, style, cap_start, cap_end, + width, + stroke_style.into(), + cap_start.try_into().ok(), + cap_end.try_into().ok(), )); }); } diff --git a/render-wasm/src/wasm/text.rs b/render-wasm/src/wasm/text.rs index 2c791e4350..ea8b6cb737 100644 --- a/render-wasm/src/wasm/text.rs +++ b/render-wasm/src/wasm/text.rs @@ -1,3 +1,5 @@ +use macros::ToJs; + use crate::mem; use crate::shapes::{GrowType, RawTextData, Type}; use crate::textlayout::{ @@ -5,6 +7,31 @@ use crate::textlayout::{ }; use crate::{with_current_shape, with_current_shape_mut, STATE}; +#[derive(Debug, PartialEq, Clone, Copy, ToJs)] +#[repr(u8)] +#[allow(dead_code)] +pub enum RawGrowType { + Fixed = 0, + AutoWidth = 1, + AutoHeight = 2, +} + +impl From for RawGrowType { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for GrowType { + fn from(value: RawGrowType) -> Self { + match value { + RawGrowType::Fixed => GrowType::Fixed, + RawGrowType::AutoWidth => GrowType::AutoWidth, + RawGrowType::AutoHeight => GrowType::AutoHeight, + } + } +} + #[no_mangle] pub extern "C" fn clear_shape_text() { with_current_shape_mut!(state, |shape: &mut Shape| { @@ -26,9 +53,11 @@ pub extern "C" fn set_shape_text_content() { #[no_mangle] pub extern "C" fn set_shape_grow_type(grow_type: u8) { + let grow_type = RawGrowType::from(grow_type); + with_current_shape_mut!(state, |shape: &mut Shape| { if let Type::Text(text_content) = &mut shape.shape_type { - text_content.set_grow_type(GrowType::from(grow_type)); + text_content.set_grow_type(grow_type.into()); } }); }