diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 8419d5d2c..39fd8a00a 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -194,10 +194,8 @@ ptr (h/call internal-module "_alloc_bytes" size) heap (gobj/get ^js internal-module "HEAPU8") mem (js/Uint8Array. (.-buffer heap) ptr size)] - (.set mem (js/Uint8Array. buffer) - (h/call internal-module "_set_shape_path_content" (count content)) - (js/console.log mem) - (js/console.log buffer)))) + (.set mem (js/Uint8Array. buffer)) + (h/call internal-module "_set_shape_path_content"))) (defn- translate-blend-mode [blend-mode] diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index b32236593..759997460 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -7,6 +7,7 @@ mod state; mod utils; mod view; +use shapes::RawPathData; use skia_safe as skia; use crate::state::State; @@ -271,20 +272,18 @@ pub extern "C" fn set_shape_hidden(hidden: bool) { } #[no_mangle] -pub extern "C" fn set_shape_path_content(n_segments: u32) { +pub extern "C" fn set_shape_path_content() { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - let len = n_segments as usize; - - unsafe { - let buffer = Vec::::from_raw_parts( - mem::buffer_ptr() as *mut shapes::RawPathData, - len, - len, - ); - mem::free_bytes(); - } + let bytes = mem::bytes(); + let raw_segments = bytes + .chunks(size_of::()) + .map(|data| shapes::RawPathData { + data: data.try_into().unwrap(), + }) + .collect(); + shape.set_path_segments(raw_segments).unwrap(); } } diff --git a/render-wasm/src/math.rs b/render-wasm/src/math.rs index f58d04584..d402b23dc 100644 --- a/render-wasm/src/math.rs +++ b/render-wasm/src/math.rs @@ -1,3 +1,4 @@ use skia_safe as skia; pub type Rect = skia::Rect; +pub type Point = (f32, f32); diff --git a/render-wasm/src/mem.rs b/render-wasm/src/mem.rs index a23010df2..ea0df45fc 100644 --- a/render-wasm/src/mem.rs +++ b/render-wasm/src/mem.rs @@ -7,7 +7,7 @@ pub extern "C" fn alloc_bytes(len: usize) -> *mut u8 { panic!("Bytes already allocated"); } - let mut buffer = Box::new(Vec::::with_capacity(len)); + let mut buffer = Box::new(vec![0u8; len]); let ptr = buffer.as_mut_ptr(); unsafe { BUFFERU8 = Some(buffer) }; @@ -23,3 +23,8 @@ pub fn buffer_ptr() -> *mut u8 { let buffer = unsafe { BUFFERU8.as_mut() }.expect("uninitializied buffer"); buffer.as_mut_ptr() } + +pub fn bytes() -> Vec { + let buffer = unsafe { BUFFERU8.take() }.expect("uninitialized buffer"); + *buffer +} diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 1586d2f62..b6f679891 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -11,9 +11,10 @@ pub use fills::*; pub use images::*; pub use paths::*; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, PartialEq)] pub enum Kind { Rect, + Path(Path), } pub type Color = skia::Color; @@ -110,6 +111,12 @@ impl Shape { Ok(()) } + pub fn set_path_segments(&mut self, buffer: Vec) -> Result<(), String> { + let p = Path::try_from(buffer)?; + self.kind = Kind::Path(p); + Ok(()) + } + pub fn set_blend_mode(&mut self, mode: BlendMode) { self.blend_mode = mode; } diff --git a/render-wasm/src/shapes/paths.rs b/render-wasm/src/shapes/paths.rs index 0232aa93d..ae2367fb7 100644 --- a/render-wasm/src/shapes/paths.rs +++ b/render-wasm/src/shapes/paths.rs @@ -1,4 +1,116 @@ +use skia_safe as skia; +use std::array::TryFromSliceError; + +use crate::math::Point; + +fn stringify_slice_err(_: TryFromSliceError) -> String { + format!("Error deserializing path") +} + #[derive(Debug)] pub struct RawPathData { - data: [u8; 28], + pub data: [u8; 28], +} + +impl RawPathData { + fn command(&self) -> Result { + let cmd = u16::from_be_bytes(self.data[0..2].try_into().map_err(stringify_slice_err)?); + Ok(cmd) + } + + fn xy(&self) -> Result { + let x = f32::from_be_bytes(self.data[20..24].try_into().map_err(stringify_slice_err)?); + let y = f32::from_be_bytes(self.data[24..].try_into().map_err(stringify_slice_err)?); + Ok((x, y)) + } + + fn c1(&self) -> Result { + let c1_x = f32::from_be_bytes(self.data[4..8].try_into().map_err(stringify_slice_err)?); + let c1_y = f32::from_be_bytes(self.data[8..12].try_into().map_err(stringify_slice_err)?); + + Ok((c1_x, c1_y)) + } + + fn c2(&self) -> Result { + let c2_x = f32::from_be_bytes(self.data[12..16].try_into().map_err(stringify_slice_err)?); + let c2_y = f32::from_be_bytes(self.data[16..20].try_into().map_err(stringify_slice_err)?); + + Ok((c2_x, c2_y)) + } +} + +const MOVE_TO: u16 = 1; +const LINE_TO: u16 = 2; +const CURVE_TO: u16 = 3; +const CLOSE: u16 = 4; + +#[derive(Debug, PartialEq, Copy, Clone)] +enum Segment { + MoveTo(Point), + LineTo(Point), + CurveTo((Point, Point, Point)), + Close, +} + +impl TryFrom for Segment { + type Error = String; + fn try_from(value: RawPathData) -> Result { + let cmd = value.command()?; + match cmd { + MOVE_TO => Ok(Segment::MoveTo(value.xy()?)), + LINE_TO => Ok(Segment::LineTo(value.xy()?)), + CURVE_TO => Ok(Segment::CurveTo((value.c1()?, value.c2()?, value.xy()?))), + CLOSE => Ok(Segment::Close), + _ => Err(format!( + "Error deserializing path. Unknown command/flags: {:#010x}", + cmd + )), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Path { + segments: Vec, + skia_path: skia::Path, +} + +impl TryFrom> for Path { + type Error = String; + + fn try_from(value: Vec) -> Result { + let segments = value + .into_iter() + .map(|raw| Segment::try_from(raw)) + .collect::, String>>()?; + + let mut skia_path = skia::Path::new(); + for segment in segments.iter() { + match *segment { + Segment::MoveTo(xy) => { + skia_path.move_to(xy); + } + Segment::LineTo(xy) => { + skia_path.line_to(xy); + } + Segment::CurveTo((c1, c2, xy)) => { + skia_path.cubic_to(c1, c2, xy); + } + Segment::Close => { + skia_path.close(); + } + } + } + + Ok(Path { + segments, + skia_path, + }) + } +} + +impl Path { + pub fn to_skia_path(&self) -> skia::Path { + self.skia_path.snapshot() + } }