♻️ Decouple shapes serialization from model (rust) (#7328)

* ♻️ Move shape type serialization to wasm module

* ♻️ Refactor serialization of constraints and vertical alignment into wasm module

* ♻️ Refactor serialization and model of shape blur

* ♻️ Refactor bool serialization to the wasm module

* ♻️ Split wasm::layout into submodules

* ♻️ Refactor serialization of AlignItems, AlignContent, JustifyItems and JustifyContent

* ♻️ Refactor serialization of WrapType and FlexDirection

* ♻️ Refactor serialization of JustifySelf

* ♻️ Refactor serialization of GridCell

* ♻️ Refactor serialization of AlignSelf

* 🐛 Fix AlignSelf not being serialized

* ♻️ Refactor handling of None variants in Raw* enums

* ♻️ Refactor serialization of grid direction

* ♻️ Refactor serialization of GridTrack and GridTrackType

* ♻️ Refactor serialization of Sizing

* ♻️ Refactor serialization of ShadowStyle

* ♻️ Refactor serialization of StrokeCap and StrokeStyle

* ♻️ Refactor serialization of BlendMode

* ♻️ Refactor serialization of FontStyle

* ♻️ Refactor serialization of GrowType
This commit is contained in:
Belén Albeza
2025-09-22 13:47:54 +02:00
committed by GitHub
parent 5c23a678cc
commit e4d610d503
33 changed files with 1489 additions and 1178 deletions

View File

@@ -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)

View File

@@ -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]

View File

@@ -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

View File

@@ -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, {

View File

@@ -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<Vec<Fill>>,
pub nested_blurs: Vec<Option<Blur>>,
pub nested_blurs: Vec<Option<Blur>>, // FIXME: why is this an option?
pub show_grid: Option<Uuid>,
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));
}
}
_ => {}
}

View File

@@ -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<i32> for BlendMode {
fn from(value: i32) -> Self {
if value <= skia::BlendMode::Luminosity as i32 {
unsafe { Self(std::mem::transmute::<i32, skia_safe::BlendMode>(value)) }
} else {
Self::default()
}
}
}
impl From<BlendMode> for skia::BlendMode {
fn from(val: BlendMode) -> Self {
match val {
BlendMode(skia_blend) => skia_blend,
}
}
}

View File

@@ -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],
);
}
}
}

View File

@@ -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<Corners> {
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<Self> {
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<Self> {
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<Stroke>,
pub blend_mode: BlendMode,
pub vertical_align: VerticalAlign,
pub blur: Blur,
pub blur: Option<Blur>,
pub opacity: f32,
pub hidden: bool,
pub svg: Option<skia::svg::Dom>,
@@ -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<RawGridTrack>) {
pub fn set_grid_columns(&mut self, tracks: Vec<GridTrack>) {
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<RawGridTrack>) {
pub fn set_grid_rows(&mut self, tracks: Vec<GridTrack>) {
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<RawGridCell>) {
pub fn set_grid_cells(&mut self, cells: Vec<GridCell>) {
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<Blur>) {
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<skia::ImageFilter> {
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<skia::MaskFilter> {
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 {

View File

@@ -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<BlendMode> for skia::BlendMode {
fn from(val: BlendMode) -> Self {
match val {
BlendMode(skia_blend) => skia_blend,
}
}
}

View File

@@ -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<u8> 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,
}

View File

@@ -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<u8> 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

View File

@@ -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<u8> 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 {

View File

@@ -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<Uuid>,
}
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<AlignSelf> {
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<JustifySelf> {
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,

View File

@@ -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

View File

@@ -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<u8> 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,

View File

@@ -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<u8> 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<u8> 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<StrokeCap>,
pub cap_start: Option<StrokeCap>,
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<StrokeCap>,
cap_end: Option<StrokeCap>,
) -> 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<StrokeCap>,
cap_end: Option<StrokeCap>,
) -> 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<StrokeCap>,
cap_end: Option<StrokeCap>,
) -> 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,
}
}

View File

@@ -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<u8>> for RawTextData {
fn from(bytes: &Vec<u8>) -> Self {
let paragraph = RawParagraphData::try_from(&bytes[..RAW_PARAGRAPH_DATA_SIZE]).unwrap();
@@ -596,12 +582,10 @@ impl From<&Vec<u8>> 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,

View File

@@ -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;

View File

@@ -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<u8> for RawBlendMode {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawBlendMode> 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());
});
}

View File

@@ -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<u8> for RawBlurType {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawBlurType> 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);
});
}

View File

@@ -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<u8> for RawFontStyle {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawFontStyle> 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

View File

@@ -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::<shapes::RawGridTrack>())
.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::<shapes::RawGridTrack>())
.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::<shapes::RawGridCell>())
.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)
}

View File

@@ -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<u8> for RawSizing {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawSizing> 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,
);
});
}

View File

@@ -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<u8> for RawAlignItems {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawAlignItems> 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<u8> for RawAlignContent {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawAlignContent> 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<u8> for RawJustifyItems {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawJustifyItems> 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<u8> for RawJustifyContent {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawJustifyContent> 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<u8> for RawJustifySelf {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl TryFrom<RawJustifySelf> for JustifySelf {
type Error = ();
fn try_from(value: RawJustifySelf) -> Result<JustifySelf, Self::Error> {
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<u8> for RawAlignSelf {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl TryFrom<RawAlignSelf> for AlignSelf {
type Error = ();
fn try_from(value: RawAlignSelf) -> Result<AlignSelf, Self::Error> {
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<u8> for RawVerticalAlign {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawVerticalAlign> 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());
});
}

View File

@@ -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<u8> for RawConstraintH {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawConstraintH> 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<u8> for RawConstraintV {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawConstraintV> 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();
});
}

View File

@@ -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<u8> for RawFlexDirection {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawFlexDirection> 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<u8> for RawWrapType {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawWrapType> 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,
);
});
}

View File

@@ -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::<RawGridCell>()]> for RawGridCell {
fn from(bytes: [u8; size_of::<RawGridCell>()]) -> Self {
unsafe { std::mem::transmute(bytes) }
}
}
impl From<RawGridCell> 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<u8> for RawGridDirection {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawGridDirection> 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<u8> for RawGridTrackType {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawGridTrackType> 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::<RawGridTrack>()]> for RawGridTrack {
fn from(bytes: [u8; size_of::<RawGridTrack>()]) -> Self {
unsafe { std::mem::transmute(bytes) }
}
}
impl From<RawGridTrack> 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<GridTrack> = bytes
.chunks(size_of::<RawGridTrack>())
.map(|data| data.try_into().unwrap())
.map(|data: [u8; size_of::<RawGridTrack>()]| 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<GridTrack> = bytes
.chunks(size_of::<RawGridTrack>())
.map(|data| data.try_into().unwrap())
.map(|data: [u8; size_of::<RawGridTrack>()]| 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<RawGridCell> = bytes
.chunks(size_of::<RawGridCell>())
.map(|data| data.try_into().expect("Invalid grid cell data"))
.map(|data: [u8; size_of::<RawGridCell>()]| 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)
}

View File

@@ -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::<RawSegmentData>();
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<Uuid> = bytes
.chunks(size_of::<<Uuid as SerializableResult>::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 {

View File

@@ -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<u8> for RawBoolType {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawBoolType> 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<Uuid> = bytes
.chunks(size_of::<<Uuid as SerializableResult>::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)
}

View File

@@ -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<u8> for RawShadowStyle {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawShadowStyle> 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();
});
}

View File

@@ -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<u8> for RawShapeType {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawShapeType> 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());
});
}

View File

@@ -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<u8> for RawStrokeStyle {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawStrokeStyle> 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<u8> for RawStrokeCap {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl TryFrom<RawStrokeCap> for StrokeCap {
type Error = ();
fn try_from(value: RawStrokeCap) -> Result<Self, Self::Error> {
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(),
));
});
}

View File

@@ -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<u8> for RawGrowType {
fn from(value: u8) -> Self {
unsafe { std::mem::transmute(value) }
}
}
impl From<RawGrowType> 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());
}
});
}