diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 717b2b03dd..7ff66e770c 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -571,11 +571,13 @@ nil (ctm/has-geometry? (:modifiers data)) - (d/vec2 id (ctm/modifiers->transform (:modifiers data))) + (let [parent (:geometry-parent (:modifiers data)) + kind (if (d/not-empty? parent) :parent :child)] + (d/vec2 id {:transform (ctm/modifiers->transform (:modifiers data)) :kind kind})) ;; Unit matrix is used for reflowing :else - (d/vec2 id default-transform)))))) + (d/vec2 id {:transform default-transform :kind :parent})))))) (defn- parse-geometry-modifiers [modif-tree] diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 82fcc00f27..f6b2ed8881 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -1247,7 +1247,6 @@ (some? new-modif) (assoc (:id frame) {:modifiers new-modif}))))) {}))] - (if (features/active-feature? state "render-wasm/v1") (rx/of (dwm/apply-wasm-modifiers modifiers {:undo-group undo-group})) (rx/of (dwm/apply-modifiers {:modifiers modifiers :undo-group undo-group}))))))) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 46a32ef16e..849be1089b 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -51,15 +51,18 @@ [cuerdas.core :as str] [promesa.core :as p] [rumext.v2 :as mf])) - (def use-dpr? (contains? cf/flags :render-wasm-dpr)) (def ^:const UUID-U8-SIZE 16) (def ^:const UUID-U32-SIZE (/ UUID-U8-SIZE 4)) +;; FIXME: Migrate this as we adjust the DTO structure in wasm (def ^:const MODIFIER-U8-SIZE 40) (def ^:const MODIFIER-U32-SIZE (/ MODIFIER-U8-SIZE 4)) (def ^:const MODIFIER-TRANSFORM-U8-OFFSET-SIZE 16) +(def ^:const INPUT-MODIFIER-U8-SIZE 44) +(def ^:const INPUT-MODIFIER-U32-SIZE (/ INPUT-MODIFIER-U8-SIZE 4)) + (def ^:const GRID-LAYOUT-ROW-U8-SIZE 8) (def ^:const GRID-LAYOUT-COLUMN-U8-SIZE 8) @@ -1278,13 +1281,16 @@ (when-not ^boolean (empty? entries) (let [heapf32 (mem/get-heap-f32) heapu32 (mem/get-heap-u32) - size (mem/get-alloc-size entries MODIFIER-U8-SIZE) + size (mem/get-alloc-size entries INPUT-MODIFIER-U8-SIZE) offset (mem/alloc->offset-32 size)] - (reduce (fn [offset [id transform]] - (-> offset - (mem.h32/write-uuid heapu32 id) - (mem.h32/write-matrix heapf32 transform))) + (reduce (fn [offset [id data]] + (let [transform (:transform data) + kind (:kind data)] + (-> offset + (mem.h32/write-uuid heapu32 id) + (mem.h32/write-matrix heapf32 transform) + (mem.h32/write-u32 heapu32 (sr/translate-transform-entry-kind kind))))) offset entries) diff --git a/frontend/src/app/render_wasm/api/shared.js b/frontend/src/app/render_wasm/api/shared.js index e5456d91d2..250cc7bf78 100644 --- a/frontend/src/app/render_wasm/api/shared.js +++ b/frontend/src/app/render_wasm/api/shared.js @@ -249,3 +249,8 @@ export const CursorDirection = { "line-end": 5, }; +export const RawTransformEntryKind = { + "parent": 0, + "child": 1, +}; + diff --git a/frontend/src/app/render_wasm/serializers.cljs b/frontend/src/app/render_wasm/serializers.cljs index d6e73aa3f5..1755f359da 100644 --- a/frontend/src/app/render_wasm/serializers.cljs +++ b/frontend/src/app/render_wasm/serializers.cljs @@ -274,3 +274,8 @@ :edge 3 :unknown 4 4)) + +(defn translate-transform-entry-kind [kind] + (let [values (unchecked-get wasm/serializers "transform-entry-kind") + default (unchecked-get values "parent")] + (d/nilv (unchecked-get values (d/name kind)) default))) diff --git a/frontend/src/app/render_wasm/wasm.cljs b/frontend/src/app/render_wasm/wasm.cljs index 6547d27b9a..dbe78d15f6 100644 --- a/frontend/src/app/render_wasm/wasm.cljs +++ b/frontend/src/app/render_wasm/wasm.cljs @@ -54,6 +54,7 @@ :text-direction shared/RawTextDirection :text-decoration shared/RawTextDecoration :text-transform shared/RawTextTransform + :transform-entry-kind shared/RawTransformEntryKind :segment-data shared/RawSegmentData :stroke-linecap shared/RawStrokeLineCap :stroke-linejoin shared/RawStrokeLineJoin diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index a1de07e8fe..005bf31781 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -697,21 +697,6 @@ pub extern "C" fn clean_modifiers() { }); } -#[no_mangle] -pub extern "C" fn propagate_modifiers(pixel_precision: bool) -> *mut u8 { - let bytes = mem::bytes(); - - let entries: Vec<_> = bytes - .chunks(size_of::<::BytesType>()) - .map(|data| TransformEntry::try_from(data).unwrap()) - .collect(); - - with_state!(state, { - let result = shapes::propagate_modifiers(state, &entries, pixel_precision); - mem::write_vec(result) - }) -} - #[no_mangle] pub extern "C" fn set_modifiers() { let bytes = mem::bytes(); diff --git a/render-wasm/src/shapes/transform.rs b/render-wasm/src/shapes/transform.rs index 61ed53e891..7e6200c0cb 100644 --- a/render-wasm/src/shapes/transform.rs +++ b/render-wasm/src/shapes/transform.rs @@ -39,6 +39,7 @@ pub struct TransformEntry { } impl TransformEntry { + // FIXME: We should be able to refactor code so we don't need these from_* methods pub fn from_input(id: Uuid, transform: Matrix) -> Self { TransformEntry { id, @@ -47,6 +48,8 @@ impl TransformEntry { propagate: true, } } + + // FIXME: We should be able to refactor code so we don't need these from_* methods pub fn from_propagate(id: Uuid, transform: Matrix) -> Self { TransformEntry { id, @@ -119,6 +122,7 @@ impl From for [u8; 40] { } } +// FIXME: Use a DTO for this impl SerializableResult for TransformEntry { type BytesType = [u8; 40]; diff --git a/render-wasm/src/wasm.rs b/render-wasm/src/wasm.rs index 3612a79984..47578e5b9c 100644 --- a/render-wasm/src/wasm.rs +++ b/render-wasm/src/wasm.rs @@ -10,3 +10,4 @@ pub mod strokes; pub mod svg_attrs; pub mod text; pub mod text_editor; +pub mod transforms; diff --git a/render-wasm/src/wasm/transforms.rs b/render-wasm/src/wasm/transforms.rs new file mode 100644 index 0000000000..88b888d1ef --- /dev/null +++ b/render-wasm/src/wasm/transforms.rs @@ -0,0 +1,88 @@ +use macros::ToJs; + +use skia_safe as skia; + +use crate::mem; +use crate::shapes::{self, TransformEntry, TransformEntrySource}; +use crate::utils::uuid_from_u32_quartet; +use crate::{with_state, STATE}; + +#[derive(Debug, PartialEq, Clone, Copy, ToJs)] +#[repr(u8)] +enum RawTransformEntryKind { + #[allow(dead_code)] + Parent = 0, + Child = 1, +} + +impl From for RawTransformEntryKind { + fn from(value: u8) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +#[repr(C)] +#[repr(align(4))] +pub struct RawTransformEntry { + id: [u32; 4], + transform: [f32; 6], + kind: RawTransformEntryKind, +} + +const RAW_TRANSFORM_ENTRY_SIZE: usize = size_of::(); + +impl From<[u8; RAW_TRANSFORM_ENTRY_SIZE]> for RawTransformEntry { + fn from(bytes: [u8; RAW_TRANSFORM_ENTRY_SIZE]) -> Self { + unsafe { std::mem::transmute(bytes) } + } +} + +impl TryFrom<&[u8]> for RawTransformEntry { + type Error = String; + fn try_from(bytes: &[u8]) -> Result { + let bytes: [u8; RAW_TRANSFORM_ENTRY_SIZE] = bytes + .try_into() + .map_err(|_| "Invalid transform entry bytes".to_string())?; + Ok(RawTransformEntry::from(bytes)) + } +} + +impl From for TransformEntry { + fn from(value: RawTransformEntry) -> Self { + let [a, b, c, d] = value.id; + let transform = skia::Matrix::new_all( + value.transform[0], + value.transform[2], + value.transform[4], + value.transform[1], + value.transform[3], + value.transform[5], + 0.0, + 0.0, + 1.0, + ); + + TransformEntry { + id: uuid_from_u32_quartet(a, b, c, d), + transform, + source: TransformEntrySource::Input, + propagate: value.kind == RawTransformEntryKind::Child, + } + } +} + +#[no_mangle] +pub extern "C" fn propagate_modifiers(pixel_precision: bool) -> *mut u8 { + let bytes = mem::bytes(); + + let entries: Vec = bytes + .chunks(RAW_TRANSFORM_ENTRY_SIZE) + .map(|data| RawTransformEntry::try_from(data).unwrap().into()) + .collect(); + + with_state!(state, { + let result = shapes::propagate_modifiers(state, &entries, pixel_precision); + mem::write_vec(result) + }) +}