🎉 Deserialize shape data in wasm

This commit is contained in:
Belén Albeza 2024-12-09 14:30:03 +01:00
parent 22b01c63b5
commit 0bfcc1f854
6 changed files with 140 additions and 18 deletions

View file

@ -194,10 +194,8 @@
ptr (h/call internal-module "_alloc_bytes" size) ptr (h/call internal-module "_alloc_bytes" size)
heap (gobj/get ^js internal-module "HEAPU8") heap (gobj/get ^js internal-module "HEAPU8")
mem (js/Uint8Array. (.-buffer heap) ptr size)] mem (js/Uint8Array. (.-buffer heap) ptr size)]
(.set mem (js/Uint8Array. buffer) (.set mem (js/Uint8Array. buffer))
(h/call internal-module "_set_shape_path_content" (count content)) (h/call internal-module "_set_shape_path_content")))
(js/console.log mem)
(js/console.log buffer))))
(defn- translate-blend-mode (defn- translate-blend-mode
[blend-mode] [blend-mode]

View file

@ -7,6 +7,7 @@ mod state;
mod utils; mod utils;
mod view; mod view;
use shapes::RawPathData;
use skia_safe as skia; use skia_safe as skia;
use crate::state::State; use crate::state::State;
@ -271,20 +272,18 @@ pub extern "C" fn set_shape_hidden(hidden: bool) {
} }
#[no_mangle] #[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"); let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
if let Some(shape) = state.current_shape() { if let Some(shape) = state.current_shape() {
let len = n_segments as usize; let bytes = mem::bytes();
let raw_segments = bytes
unsafe { .chunks(size_of::<shapes::RawPathData>())
let buffer = Vec::<shapes::RawPathData>::from_raw_parts( .map(|data| shapes::RawPathData {
mem::buffer_ptr() as *mut shapes::RawPathData, data: data.try_into().unwrap(),
len, })
len, .collect();
); shape.set_path_segments(raw_segments).unwrap();
mem::free_bytes();
}
} }
} }

View file

@ -1,3 +1,4 @@
use skia_safe as skia; use skia_safe as skia;
pub type Rect = skia::Rect; pub type Rect = skia::Rect;
pub type Point = (f32, f32);

View file

@ -7,7 +7,7 @@ pub extern "C" fn alloc_bytes(len: usize) -> *mut u8 {
panic!("Bytes already allocated"); panic!("Bytes already allocated");
} }
let mut buffer = Box::new(Vec::<u8>::with_capacity(len)); let mut buffer = Box::new(vec![0u8; len]);
let ptr = buffer.as_mut_ptr(); let ptr = buffer.as_mut_ptr();
unsafe { BUFFERU8 = Some(buffer) }; unsafe { BUFFERU8 = Some(buffer) };
@ -23,3 +23,8 @@ pub fn buffer_ptr() -> *mut u8 {
let buffer = unsafe { BUFFERU8.as_mut() }.expect("uninitializied buffer"); let buffer = unsafe { BUFFERU8.as_mut() }.expect("uninitializied buffer");
buffer.as_mut_ptr() buffer.as_mut_ptr()
} }
pub fn bytes() -> Vec<u8> {
let buffer = unsafe { BUFFERU8.take() }.expect("uninitialized buffer");
*buffer
}

View file

@ -11,9 +11,10 @@ pub use fills::*;
pub use images::*; pub use images::*;
pub use paths::*; pub use paths::*;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, PartialEq)]
pub enum Kind { pub enum Kind {
Rect, Rect,
Path(Path),
} }
pub type Color = skia::Color; pub type Color = skia::Color;
@ -110,6 +111,12 @@ impl Shape {
Ok(()) Ok(())
} }
pub fn set_path_segments(&mut self, buffer: Vec<RawPathData>) -> Result<(), String> {
let p = Path::try_from(buffer)?;
self.kind = Kind::Path(p);
Ok(())
}
pub fn set_blend_mode(&mut self, mode: BlendMode) { pub fn set_blend_mode(&mut self, mode: BlendMode) {
self.blend_mode = mode; self.blend_mode = mode;
} }

View file

@ -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)] #[derive(Debug)]
pub struct RawPathData { pub struct RawPathData {
data: [u8; 28], pub data: [u8; 28],
}
impl RawPathData {
fn command(&self) -> Result<u16, String> {
let cmd = u16::from_be_bytes(self.data[0..2].try_into().map_err(stringify_slice_err)?);
Ok(cmd)
}
fn xy(&self) -> Result<Point, String> {
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<Point, String> {
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<Point, String> {
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<RawPathData> for Segment {
type Error = String;
fn try_from(value: RawPathData) -> Result<Self, Self::Error> {
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<Segment>,
skia_path: skia::Path,
}
impl TryFrom<Vec<RawPathData>> for Path {
type Error = String;
fn try_from(value: Vec<RawPathData>) -> Result<Self, Self::Error> {
let segments = value
.into_iter()
.map(|raw| Segment::try_from(raw))
.collect::<Result<Vec<Segment>, 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()
}
} }