From cba65972ddfd13154c1161e5773cf9e6d4618405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 6 May 2025 12:33:14 +0200 Subject: [PATCH] :sparkles: Use same wasm function to add all types of fills --- frontend/src/app/render_wasm/api.cljs | 54 +++++------- .../app/render_wasm/serializers/fills.cljs | 86 +++++++++++-------- render-wasm/src/wasm/fills.rs | 49 ++--------- render-wasm/src/wasm/fills/gradient.rs | 5 +- render-wasm/src/wasm/fills/image.rs | 5 +- render-wasm/src/wasm/fills/solid.rs | 8 +- render-wasm/src/wasm/strokes.rs | 45 +--------- 7 files changed, 89 insertions(+), 163 deletions(-) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index caf29baf3e..56a38d7b6f 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -214,35 +214,30 @@ (let [opacity (or (:fill-opacity fill) 1.0) color (:fill-color fill) gradient (:fill-color-gradient fill) - image (:fill-image fill)] + image (:fill-image fill) + offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE) + heap (mem/get-heap-u32)] (cond (some? color) - (let [size sr-fills/SOLID-BYTE-SIZE - offset (mem/alloc-bytes size) - heap (mem/get-heap-u32) - argb (sr-clr/hex->u32argb color opacity)] + (let [argb (sr-clr/hex->u32argb color opacity)] (sr-fills/write-solid-fill! offset heap argb) - (h/call wasm/internal-module "_add_shape_solid_fill")) + (h/call wasm/internal-module "_add_shape_fill")) (some? gradient) - (let [size sr-fills/GRADIENT-BYTE-SIZE - offset (mem/alloc-bytes size) - heap (mem/get-heap-u32)] + (let [_ nil] (sr-fills/write-gradient-fill! offset heap gradient opacity) (case (:type gradient) :linear - (h/call wasm/internal-module "_add_shape_linear_fill") + (h/call wasm/internal-module "_add_shape_fill") :radial - (h/call wasm/internal-module "_add_shape_radial_fill"))) + (h/call wasm/internal-module "_add_shape_fill"))) (some? image) - (let [heap (mem/get-heap-u32) - offset (mem/alloc-bytes sr-fills/IMAGE-BYTE-SIZE) - id (dm/get-prop image :id) + (let [id (dm/get-prop image :id) buffer (uuid/get-u32 id) cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))] (sr-fills/write-image-fill! offset heap id opacity (dm/get-prop image :width) (dm/get-prop image :height)) - (h/call wasm/internal-module "_add_shape_image_fill") + (h/call wasm/internal-module "_add_shape_fill") (when (== cached-image? 0) (store-image id)))))) fills)) @@ -259,7 +254,9 @@ align (:stroke-alignment stroke) style (-> stroke :stroke-style sr/translate-stroke-style) cap-start (-> stroke :stroke-cap-start sr/translate-stroke-cap) - cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap)] + cap-end (-> stroke :stroke-cap-end sr/translate-stroke-cap) + offset (mem/alloc-bytes sr-fills/FILL-BYTE-SIZE) + heap (mem/get-heap-u32)] (case align :inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end) :outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end) @@ -267,38 +264,27 @@ (cond (some? gradient) - (let [size sr-fills/GRADIENT-BYTE-SIZE - offset (mem/alloc-bytes size) - heap (mem/get-heap-u32)] + (let [_ nil] (sr-fills/write-gradient-fill! offset heap gradient opacity) (case (:type gradient) :linear - (h/call wasm/internal-module "_add_shape_stroke_linear_fill") + (h/call wasm/internal-module "_add_shape_stroke_fill") :radial - (h/call wasm/internal-module "_add_shape_stroke_radial_fill"))) + (h/call wasm/internal-module "_add_shape_stroke_fill"))) (some? image) (let [id (dm/get-prop image :id) buffer (uuid/get-u32 id) cached-image? (h/call wasm/internal-module "_is_image_cached" (aget buffer 0) (aget buffer 1) (aget buffer 2) (aget buffer 3))] - (h/call wasm/internal-module "_add_shape_image_stroke" - (aget buffer 0) - (aget buffer 1) - (aget buffer 2) - (aget buffer 3) - opacity - (dm/get-prop image :width) - (dm/get-prop image :height)) + (sr-fills/write-image-fill! offset heap id opacity (dm/get-prop image :width) (dm/get-prop image :height)) + (h/call wasm/internal-module "_add_shape_stroke_fill") (when (== cached-image? 0) (store-image id))) (some? color) - (let [size sr-fills/SOLID-BYTE-SIZE - offset (mem/alloc-bytes size) - heap (mem/get-heap-u32) - argb (sr-clr/hex->u32argb color opacity)] + (let [argb (sr-clr/hex->u32argb color opacity)] (sr-fills/write-solid-fill! offset heap argb) - (h/call wasm/internal-module "_add_shape_stroke_solid_fill"))))) + (h/call wasm/internal-module "_add_shape_stroke_fill"))))) strokes)) (defn set-shape-path-attrs diff --git a/frontend/src/app/render_wasm/serializers/fills.cljs b/frontend/src/app/render_wasm/serializers/fills.cljs index 32ee70887a..c557bca2d8 100644 --- a/frontend/src/app/render_wasm/serializers/fills.cljs +++ b/frontend/src/app/render_wasm/serializers/fills.cljs @@ -3,30 +3,6 @@ [app.common.uuid :as uuid] [app.render-wasm.serializers.color :as clr])) -(def SOLID-BYTE-SIZE 4) - -(defn write-solid-fill! - [offset heap-u32 argb] - (let [dview (js/DataView. (.-buffer heap-u32))] - (.setUint32 dview offset argb true) - (+ offset 4))) - -(def IMAGE-BYTE-SIZE 28) - -(defn write-image-fill! - [offset heap-u32 id opacity width height] - (js/console.log "write-image-fill!" (str id) opacity width height) - (let [dview (js/DataView. (.-buffer heap-u32)) - uuid-buffer (uuid/get-u32 id)] - (.setUint32 dview offset (aget uuid-buffer 0) true) - (.setUint32 dview (+ offset 4) (aget uuid-buffer 1) true) - (.setUint32 dview (+ offset 8) (aget uuid-buffer 2) true) - (.setUint32 dview (+ offset 12) (aget uuid-buffer 3) true) - (.setFloat32 dview (+ offset 16) opacity true) - (.setInt32 dview (+ offset 20) width true) - (.setInt32 dview (+ offset 24) height true) - (+ offset 28))) - (def ^:private GRADIENT-STOP-SIZE 8) (def ^:private GRADIENT-BASE-SIZE 28) ;; TODO: Define in shape model @@ -35,6 +11,40 @@ (def GRADIENT-BYTE-SIZE (+ GRADIENT-BASE-SIZE (* MAX-GRADIENT-STOPS GRADIENT-STOP-SIZE))) +(def SOLID-BYTE-SIZE 4) +(def IMAGE-BYTE-SIZE 28) + +;; FIXME: get it from the wasm module +(def FILL-BYTE-SIZE (+ 4 (max GRADIENT-BYTE-SIZE IMAGE-BYTE-SIZE SOLID-BYTE-SIZE))) + +;; (defn write-fill! [offset heap-u32 fill] +;; (let [dview (js/DataView. (.-buffer heap-u32))] +;; offset)) + + +(defn write-solid-fill! + [offset heap-u32 argb] + (let [dview (js/DataView. (.-buffer heap-u32))] + (.setUint8 dview offset 0x00 true) + (.setUint32 dview (+ offset 4) argb true) + (+ offset FILL-BYTE-SIZE))) + +(defn write-image-fill! + [offset heap-u32 id opacity width height] + (let [dview (js/DataView. (.-buffer heap-u32)) + uuid-buffer (uuid/get-u32 id)] + (.setUint8 dview offset 0x03 true) + (.setUint32 dview (+ offset 4) (aget uuid-buffer 0) true) + (.setUint32 dview (+ offset 8) (aget uuid-buffer 1) true) + (.setUint32 dview (+ offset 12) (aget uuid-buffer 2) true) + (.setUint32 dview (+ offset 16) (aget uuid-buffer 3) true) + (.setFloat32 dview (+ offset 20) opacity true) + (.setInt32 dview (+ offset 24) width true) + (.setInt32 dview (+ offset 28) height true) + (+ offset FILL-BYTE-SIZE))) + + + (defn write-gradient-fill! [offset heap-u32 gradient opacity] (let [dview (js/DataView. (.-buffer heap-u32)) @@ -43,22 +53,24 @@ end-x (:end-x gradient) end-y (:end-y gradient) width (or (:width gradient) 0) - stops (take MAX-GRADIENT-STOPS (:stops gradient))] - (.setFloat32 dview offset start-x true) - (.setFloat32 dview (+ offset 4) start-y true) - (.setFloat32 dview (+ offset 8) end-x true) - (.setFloat32 dview (+ offset 12) end-y true) - (.setFloat32 dview (+ offset 16) opacity true) - (.setFloat32 dview (+ offset 20) width true) - (.setUint8 dview (+ offset 24) (count stops) true) - (loop [stops (seq stops) offset (+ offset GRADIENT-BASE-SIZE)] + stops (take MAX-GRADIENT-STOPS (:stops gradient)) + type (if (= (:type gradient) :linear) 0x01 0x02)] + (.setUint8 dview offset type true) + (.setFloat32 dview (+ offset 4) start-x true) + (.setFloat32 dview (+ offset 8) start-y true) + (.setFloat32 dview (+ offset 12) end-x true) + (.setFloat32 dview (+ offset 16) end-y true) + (.setFloat32 dview (+ offset 20) opacity true) + (.setFloat32 dview (+ offset 24) width true) + (.setUint8 dview (+ offset 28) (count stops) true) + (loop [stops (seq stops) loop-offset (+ offset 32)] (if (empty? stops) - offset + (+ offset FILL-BYTE-SIZE) (let [stop (first stops) hex-color (:color stop) opacity (:opacity stop) argb (clr/hex->u32argb hex-color opacity) stop-offset (:offset stop)] - (.setUint32 dview offset argb true) - (.setFloat32 dview (+ offset 4) stop-offset true) - (recur (rest stops) (+ offset GRADIENT-STOP-SIZE))))))) \ No newline at end of file + (.setUint32 dview loop-offset argb true) + (.setFloat32 dview (+ loop-offset 4) stop-offset true) + (recur (rest stops) (+ loop-offset GRADIENT-STOP-SIZE))))))) \ No newline at end of file diff --git a/render-wasm/src/wasm/fills.rs b/render-wasm/src/wasm/fills.rs index 71d9a4286f..4c54fb187b 100644 --- a/render-wasm/src/wasm/fills.rs +++ b/render-wasm/src/wasm/fills.rs @@ -11,7 +11,7 @@ use crate::STATE; #[repr(align(4))] #[repr(u8)] #[derive(Debug, PartialEq, Clone, Copy)] -enum RawFillData { +pub enum RawFillData { Solid(solid::RawSolidData) = 0x00, Linear(gradient::RawGradientData) = 0x01, Radial(gradient::RawGradientData) = 0x02, @@ -43,16 +43,16 @@ impl TryFrom<&[u8]> for RawFillData { let fill_type = bytes[0]; match fill_type { 0x00 => Ok(RawFillData::Solid(solid::RawSolidData::try_from( - &bytes[1..], + &bytes[4..], )?)), 0x01 => Ok(RawFillData::Linear(gradient::RawGradientData::try_from( - &bytes[1..], + &bytes[4..], )?)), 0x02 => Ok(RawFillData::Radial(gradient::RawGradientData::try_from( - &bytes[1..], + &bytes[4..], )?)), 0x03 => Ok(RawFillData::Image(image::RawImageFillData::try_from( - &bytes[1..], + &bytes[4..], )?)), _ => Err("Invalid fill type".to_string()), } @@ -68,45 +68,6 @@ pub extern "C" fn add_shape_fill() { }); } -#[no_mangle] -pub extern "C" fn add_shape_solid_fill() { - with_current_shape!(state, |shape: &mut Shape| { - let bytes = mem::bytes(); - let solid_color = - shapes::SolidColor::try_from(&bytes[..]).expect("Invalid solid color data"); - - shape.add_fill(shapes::Fill::Solid(solid_color)); - }); -} - -#[no_mangle] -pub extern "C" fn add_shape_linear_fill() { - with_current_shape!(state, |shape: &mut Shape| { - let bytes = mem::bytes(); - let gradient = shapes::Gradient::try_from(&bytes[..]).expect("Invalid gradient data"); - shape.add_fill(shapes::Fill::LinearGradient(gradient)); - }); -} - -#[no_mangle] -pub extern "C" fn add_shape_radial_fill() { - with_current_shape!(state, |shape: &mut Shape| { - let bytes = mem::bytes(); - let gradient = shapes::Gradient::try_from(&bytes[..]).expect("Invalid gradient data"); - shape.add_fill(shapes::Fill::RadialGradient(gradient)); - }); -} - -#[no_mangle] -pub extern "C" fn add_shape_image_fill() { - with_current_shape!(state, |shape: &mut Shape| { - let bytes = mem::bytes(); - let image_fill = shapes::ImageFill::try_from(&bytes[..]).expect("Invalid image fill data"); - - shape.add_fill(shapes::Fill::Image(image_fill)); - }); -} - #[no_mangle] pub extern "C" fn clear_shape_fills() { with_current_shape!(state, |shape: &mut Shape| { diff --git a/render-wasm/src/wasm/fills/gradient.rs b/render-wasm/src/wasm/fills/gradient.rs index cd16c0c857..060ca010a3 100644 --- a/render-wasm/src/wasm/fills/gradient.rs +++ b/render-wasm/src/wasm/fills/gradient.rs @@ -48,8 +48,9 @@ impl TryFrom<&[u8]> for RawGradientData { fn try_from(bytes: &[u8]) -> Result { let data: [u8; RAW_GRADIENT_DATA_SIZE] = bytes - .try_into() - .map_err(|_| "Invalid gradient data".to_string())?; + .get(0..RAW_GRADIENT_DATA_SIZE) + .and_then(|slice| slice.try_into().ok()) + .ok_or("Invalid gradient fill data".to_string())?; Ok(RawGradientData::from(data)) } } diff --git a/render-wasm/src/wasm/fills/image.rs b/render-wasm/src/wasm/fills/image.rs index a233b817d4..e8714b06b6 100644 --- a/render-wasm/src/wasm/fills/image.rs +++ b/render-wasm/src/wasm/fills/image.rs @@ -50,8 +50,9 @@ impl TryFrom<&[u8]> for RawImageFillData { fn try_from(value: &[u8]) -> Result { let data: [u8; RAW_IMAGE_DATA_SIZE] = value - .try_into() - .map_err(|_| "Invalid image fill data".to_string())?; + .get(0..RAW_IMAGE_DATA_SIZE) + .and_then(|slice| slice.try_into().ok()) + .ok_or("Invalid image fill data".to_string())?; Ok(Self::from(data)) } } diff --git a/render-wasm/src/wasm/fills/solid.rs b/render-wasm/src/wasm/fills/solid.rs index 124262b736..bd4219b4b1 100644 --- a/render-wasm/src/wasm/fills/solid.rs +++ b/render-wasm/src/wasm/fills/solid.rs @@ -1,5 +1,7 @@ use crate::shapes::{Color, SolidColor}; +const RAW_SOLID_DATA_SIZE: usize = 4; + #[repr(C)] #[derive(Debug, PartialEq, Clone, Copy)] pub struct RawSolidData { @@ -7,7 +9,7 @@ pub struct RawSolidData { } impl From<[u8; 4]> for RawSolidData { - fn from(value: [u8; 4]) -> Self { + fn from(value: [u8; RAW_SOLID_DATA_SIZE]) -> Self { Self { color: u32::from_le_bytes(value), } @@ -18,8 +20,8 @@ impl TryFrom<&[u8]> for RawSolidData { type Error = String; fn try_from(bytes: &[u8]) -> Result { - let data: [u8; 4] = bytes - .get(0..4) + let data: [u8; RAW_SOLID_DATA_SIZE] = bytes + .get(0..RAW_SOLID_DATA_SIZE) .and_then(|slice| slice.try_into().ok()) .ok_or("Invalid solid fill data".to_string())?; Ok(RawSolidData::from(data)) diff --git a/render-wasm/src/wasm/strokes.rs b/render-wasm/src/wasm/strokes.rs index 72b6ea43d5..4f9905daac 100644 --- a/render-wasm/src/wasm/strokes.rs +++ b/render-wasm/src/wasm/strokes.rs @@ -31,50 +31,13 @@ pub extern "C" fn add_shape_outer_stroke(width: f32, style: u8, cap_start: u8, c } #[no_mangle] -pub extern "C" fn add_shape_stroke_solid_fill() { +pub extern "C" fn add_shape_stroke_fill() { with_current_shape!(state, |shape: &mut Shape| { let bytes = mem::bytes(); - let solid_color = - shapes::SolidColor::try_from(&bytes[..]).expect("Invalid solid color data"); + let raw_fill = super::fills::RawFillData::try_from(&bytes[..]).expect("Invalid fill data"); shape - .set_stroke_fill(shapes::Fill::Solid(solid_color)) - .expect("could not add stroke solid fill"); - }); -} - -#[no_mangle] -pub extern "C" fn add_shape_stroke_linear_fill() { - with_current_shape!(state, |shape: &mut Shape| { - let bytes = mem::bytes(); - let gradient = shapes::Gradient::try_from(&bytes[..]).expect("Invalid gradient data"); - - shape - .set_stroke_fill(shapes::Fill::LinearGradient(gradient)) - .expect("could not add stroke linear gradient fill"); - }); -} - -#[no_mangle] -pub extern "C" fn add_shape_stroke_radial_fill() { - with_current_shape!(state, |shape: &mut Shape| { - let bytes = mem::bytes(); - let gradient = shapes::Gradient::try_from(&bytes[..]).expect("Invalid gradient data"); - - shape - .set_stroke_fill(shapes::Fill::RadialGradient(gradient)) - .expect("could not add stroke radial gradient fill"); - }); -} - -#[no_mangle] -pub extern "C" fn add_shape_image_stroke() { - with_current_shape!(state, |shape: &mut Shape| { - let bytes = mem::bytes(); - let image_fill = shapes::ImageFill::try_from(&bytes[..]).expect("Invalid image fill data"); - - shape - .set_stroke_fill(shapes::Fill::Image(image_fill)) - .expect("could not add stroke image fill"); + .set_stroke_fill(raw_fill.into()) + .expect("could not add stroke fill"); }); }