diff --git a/render-wasm/src/wasm/fills.rs b/render-wasm/src/wasm/fills.rs index 9d74a8579..71d9a4286 100644 --- a/render-wasm/src/wasm/fills.rs +++ b/render-wasm/src/wasm/fills.rs @@ -7,6 +7,67 @@ use crate::shapes; use crate::with_current_shape; use crate::STATE; +#[repr(C)] +#[repr(align(4))] +#[repr(u8)] +#[derive(Debug, PartialEq, Clone, Copy)] +enum RawFillData { + Solid(solid::RawSolidData) = 0x00, + Linear(gradient::RawGradientData) = 0x01, + Radial(gradient::RawGradientData) = 0x02, + Image(image::RawImageFillData) = 0x03, +} + +impl From for shapes::Fill { + fn from(fill_data: RawFillData) -> Self { + match fill_data { + RawFillData::Solid(solid_fill_data) => shapes::Fill::Solid(solid_fill_data.into()), + RawFillData::Linear(linear_fill_data) => { + shapes::Fill::LinearGradient(linear_fill_data.into()) + } + RawFillData::Radial(radial_fill_data) => { + shapes::Fill::RadialGradient(radial_fill_data.into()) + } + RawFillData::Image(image_fill_data) => shapes::Fill::Image(image_fill_data.into()), + } + } +} + +impl TryFrom<&[u8]> for RawFillData { + type Error = String; + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() < std::mem::size_of::() { + return Err("Invalid fill data".to_string()); + } + + let fill_type = bytes[0]; + match fill_type { + 0x00 => Ok(RawFillData::Solid(solid::RawSolidData::try_from( + &bytes[1..], + )?)), + 0x01 => Ok(RawFillData::Linear(gradient::RawGradientData::try_from( + &bytes[1..], + )?)), + 0x02 => Ok(RawFillData::Radial(gradient::RawGradientData::try_from( + &bytes[1..], + )?)), + 0x03 => Ok(RawFillData::Image(image::RawImageFillData::try_from( + &bytes[1..], + )?)), + _ => Err("Invalid fill type".to_string()), + } + } +} + +#[no_mangle] +pub extern "C" fn add_shape_fill() { + with_current_shape!(state, |shape: &mut Shape| { + let bytes = mem::bytes(); + let raw_fill = RawFillData::try_from(&bytes[..]).expect("Invalid fill data"); + shape.add_fill(raw_fill.into()); + }); +} + #[no_mangle] pub extern "C" fn add_shape_solid_fill() { with_current_shape!(state, |shape: &mut Shape| { @@ -52,3 +113,32 @@ pub extern "C" fn clear_shape_fills() { shape.clear_fills(); }); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_raw_fill_data_layout() { + assert_eq!( + std::mem::size_of::(), + 4 + std::mem::size_of::() + ); + assert_eq!(std::mem::align_of::(), 4); + } + + #[test] + fn test_raw_fill_data_from_bytes_to_solid_fill() { + let mut bytes = vec![0x00; std::mem::size_of::()]; + bytes[0] = 0x00; + bytes[1..=4].copy_from_slice(&0xfffabada_u32.to_le_bytes()); + + let raw_fill = RawFillData::try_from(&bytes[..]); + + assert!(raw_fill.is_ok()); + assert_eq!( + raw_fill.unwrap(), + RawFillData::Solid(solid::RawSolidData { color: 0xfffabada }) + ); + } +} diff --git a/render-wasm/src/wasm/fills/gradient.rs b/render-wasm/src/wasm/fills/gradient.rs index a2f593879..cd16c0c85 100644 --- a/render-wasm/src/wasm/fills/gradient.rs +++ b/render-wasm/src/wasm/fills/gradient.rs @@ -5,9 +5,9 @@ const BASE_GRADIENT_DATA_SIZE: usize = 28; const RAW_GRADIENT_DATA_SIZE: usize = BASE_GRADIENT_DATA_SIZE + RAW_STOP_DATA_SIZE * MAX_GRADIENT_STOPS; -#[derive(Debug)] +#[derive(Debug, PartialEq, Clone, Copy)] #[repr(C)] -struct RawGradientData { +pub struct RawGradientData { start_x: f32, start_y: f32, end_x: f32, @@ -43,6 +43,17 @@ impl From<[u8; RAW_GRADIENT_DATA_SIZE]> for RawGradientData { } } +impl TryFrom<&[u8]> for RawGradientData { + type Error = String; + + fn try_from(bytes: &[u8]) -> Result { + let data: [u8; RAW_GRADIENT_DATA_SIZE] = bytes + .try_into() + .map_err(|_| "Invalid gradient data".to_string())?; + Ok(RawGradientData::from(data)) + } +} + impl RawGradientData { pub fn start(&self) -> (f32, f32) { (self.start_x, self.start_y) @@ -55,7 +66,7 @@ impl RawGradientData { pub const RAW_STOP_DATA_SIZE: usize = 8; -#[derive(Debug)] +#[derive(Debug, PartialEq, Clone, Copy)] #[repr(C)] struct RawStopData { color: u32, diff --git a/render-wasm/src/wasm/fills/solid.rs b/render-wasm/src/wasm/fills/solid.rs index f91f46640..124262b73 100644 --- a/render-wasm/src/wasm/fills/solid.rs +++ b/render-wasm/src/wasm/fills/solid.rs @@ -1,8 +1,9 @@ use crate::shapes::{Color, SolidColor}; #[repr(C)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct RawSolidData { - color: u32, + pub color: u32, } impl From<[u8; 4]> for RawSolidData { @@ -13,6 +14,18 @@ impl From<[u8; 4]> for RawSolidData { } } +impl TryFrom<&[u8]> for RawSolidData { + type Error = String; + + fn try_from(bytes: &[u8]) -> Result { + let data: [u8; 4] = bytes + .get(0..4) + .and_then(|slice| slice.try_into().ok()) + .ok_or("Invalid solid fill data".to_string())?; + Ok(RawSolidData::from(data)) + } +} + impl From for SolidColor { fn from(value: RawSolidData) -> Self { Self(Color::new(value.color))