0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-12 15:51:37 -05:00

Merge pull request #5403 from penpot/ladybenko-wasm-save-buffer-pointer

Wasm save buffer pointer
This commit is contained in:
Alejandro 2024-12-05 08:58:35 +01:00 committed by GitHub
commit 7c7ede9d0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 118 additions and 89 deletions

View file

@ -132,7 +132,6 @@
(aget buffer 1) (aget buffer 1)
(aget buffer 2) (aget buffer 2)
(aget buffer 3) (aget buffer 3)
image-ptr
image-size) image-size)
true)))))) true))))))

View file

@ -1,6 +1,6 @@
mod debug; mod debug;
mod math; mod math;
pub mod mem; mod mem;
mod render; mod render;
mod shapes; mod shapes;
mod state; mod state;
@ -9,7 +9,6 @@ mod view;
use skia_safe as skia; use skia_safe as skia;
use crate::shapes::Image;
use crate::state::State; use crate::state::State;
use crate::utils::uuid_from_u32_quartet; use crate::utils::uuid_from_u32_quartet;
@ -177,66 +176,46 @@ pub extern "C" fn add_shape_linear_fill(
} }
} }
#[derive(Debug)]
pub struct RawStopData {
color: [u8; 4],
offset: u8,
}
#[no_mangle] #[no_mangle]
pub extern "C" fn add_shape_fill_stops(ptr: *mut RawStopData, n_stops: i32) { pub extern "C" fn add_shape_fill_stops(ptr: *mut shapes::RawStopData, n_stops: u32) {
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_stops as usize;
unsafe { unsafe {
let buf = Vec::<RawStopData>::from_raw_parts(ptr, n_stops as usize, n_stops as usize); let buffer = Vec::<shapes::RawStopData>::from_raw_parts(ptr, len, len);
for raw_stop in buf.iter() { shape
let color = skia::Color::from_argb( .add_gradient_stops(buffer)
raw_stop.color[3], .expect("could not add gradient stops");
raw_stop.color[0], mem::free_bytes();
raw_stop.color[1],
raw_stop.color[2],
);
shape
.add_gradient_stop(color, (raw_stop.offset as f32) / 100.)
.expect("got no fill or an invalid one");
}
mem::free(
ptr as *mut u8,
n_stops as usize * std::mem::size_of::<RawStopData>(),
);
} }
} }
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn store_image(a: u32, b: u32, c: u32, d: u32, ptr: *mut u8, size: u32) { pub extern "C" fn store_image(a: u32, b: u32, c: u32, d: u32, size: u32) {
if ptr.is_null() || size == 0 {
panic!("Invalid data, null pointer or zero size");
}
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
let render_state = state.render_state();
let id = uuid_from_u32_quartet(a, b, c, d); let id = uuid_from_u32_quartet(a, b, c, d);
unsafe { unsafe {
let image_bytes = Vec::<u8>::from_raw_parts(ptr, size as usize, size as usize); let image_bytes =
let image_data = skia::Data::new_copy(&*image_bytes); Vec::<u8>::from_raw_parts(mem::buffer_ptr(), size as usize, size as usize);
match Image::from_encoded(image_data) { match state.render_state().add_image(id, &image_bytes) {
Some(image) => { Err(msg) => {
render_state.images.insert(id.to_string(), image); eprintln!("{}", msg);
}
None => {
eprintln!("Error on image decoding");
} }
_ => {}
} }
mem::free(ptr as *mut u8, size as usize * std::mem::size_of::<u8>()); mem::free_bytes();
} }
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn is_image_cached(a: u32, b: u32, c: u32, d: u32) -> bool { pub extern "C" fn is_image_cached(a: u32, b: u32, c: u32, d: u32) -> bool {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
let render_state = state.render_state();
let id = uuid_from_u32_quartet(a, b, c, d); let id = uuid_from_u32_quartet(a, b, c, d);
render_state.images.contains_key(&id.to_string()) state.render_state().has_image(&id)
} }
#[no_mangle] #[no_mangle]
@ -246,8 +225,8 @@ pub extern "C" fn add_shape_image_fill(
c: u32, c: u32,
d: u32, d: u32,
alpha: f32, alpha: f32,
width: f32, width: i32,
height: f32, height: i32,
) { ) {
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
let id = uuid_from_u32_quartet(a, b, c, d); let id = uuid_from_u32_quartet(a, b, c, d);
@ -255,8 +234,7 @@ pub extern "C" fn add_shape_image_fill(
shape.add_fill(shapes::Fill::new_image_fill( shape.add_fill(shapes::Fill::new_image_fill(
id, id,
(alpha * 0xff as f32).floor() as u8, (alpha * 0xff as f32).floor() as u8,
height, (width, height),
width,
)); ));
} }
} }

View file

@ -1,17 +1,25 @@
static mut BUFFERU8: Option<Box<Vec<u8>>> = None;
#[no_mangle] #[no_mangle]
pub extern "C" fn alloc_bytes(len: usize) -> *mut u8 { pub extern "C" fn alloc_bytes(len: usize) -> *mut u8 {
// create a new mutable buffer with capacity `len` // TODO: Figure out how to deal with Result<T> from Emscripten
let mut buf: Vec<u8> = Vec::with_capacity(len); if unsafe { BUFFERU8.is_some() } {
let ptr = buf.as_mut_ptr(); panic!("Bytes already allocated");
// take ownership of the memory block and ensure the its destructor is not }
// called when the object goes out of scope at the end of the function
std::mem::forget(buf); let mut buffer = Box::new(Vec::<u8>::with_capacity(len));
let ptr = buffer.as_mut_ptr();
unsafe { BUFFERU8 = Some(buffer) };
return ptr; return ptr;
} }
pub fn free(ptr: *mut u8, len: usize) { pub fn free_bytes() {
unsafe { let buffer = unsafe { BUFFERU8.take() }.expect("uninitialized buffer");
let buf = Vec::<u8>::from_raw_parts(ptr, len, len); std::mem::drop(buffer);
std::mem::forget(buf); }
}
pub fn buffer_ptr() -> *mut u8 {
let buffer = unsafe { BUFFERU8.as_mut() }.expect("uninitializied buffer");
buffer.as_mut_ptr()
} }

View file

@ -5,9 +5,8 @@ use std::collections::HashMap;
use uuid::Uuid; use uuid::Uuid;
use crate::debug; use crate::debug;
use crate::shapes::Fill; use crate::math::Rect;
use crate::shapes::Shape; use crate::shapes::{draw_image_in_container, Fill, Image, Shape};
use crate::shapes::{draw_image_in_container, Image};
use crate::view::Viewbox; use crate::view::Viewbox;
struct GpuState { struct GpuState {
@ -98,7 +97,7 @@ pub(crate) struct RenderState {
pub cached_surface_image: Option<CachedSurfaceImage>, pub cached_surface_image: Option<CachedSurfaceImage>,
options: RenderOptions, options: RenderOptions,
pub viewbox: Viewbox, pub viewbox: Viewbox,
pub images: HashMap<String, Image>, images: HashMap<Uuid, Image>,
} }
impl RenderState { impl RenderState {
@ -125,6 +124,18 @@ impl RenderState {
} }
} }
pub fn add_image(&mut self, id: Uuid, image_data: &[u8]) -> Result<(), String> {
let image_data = skia::Data::new_copy(image_data);
let image = Image::from_encoded(image_data).ok_or("Error decoding image data")?;
self.images.insert(id, image);
Ok(())
}
pub fn has_image(&mut self, id: &Uuid) -> bool {
self.images.contains_key(id)
}
pub fn set_debug_flags(&mut self, debug: u32) { pub fn set_debug_flags(&mut self, debug: u32) {
self.options.debug_flags = debug; self.options.debug_flags = debug;
} }
@ -213,22 +224,7 @@ impl RenderState {
self.drawing_surface.canvas().concat(&matrix); self.drawing_surface.canvas().concat(&matrix);
for fill in shape.fills().rev() { for fill in shape.fills().rev() {
if let Fill::Image(image_fill) = fill { self.render_fill(fill, shape.selrect);
let image = self.images.get(&image_fill.id.to_string());
if let Some(image) = image {
draw_image_in_container(
&self.drawing_surface.canvas(),
&image,
(image_fill.width, image_fill.height),
shape.selrect,
&fill.to_paint(&shape.selrect),
);
}
} else {
self.drawing_surface
.canvas()
.draw_rect(shape.selrect, &fill.to_paint(&shape.selrect));
}
} }
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
@ -285,6 +281,25 @@ impl RenderState {
self.flush(); self.flush();
} }
fn render_fill(&mut self, fill: &Fill, selrect: Rect) {
if let Fill::Image(image_fill) = fill {
let image = self.images.get(&image_fill.id());
if let Some(image) = image {
draw_image_in_container(
&self.drawing_surface.canvas(),
&image,
image_fill.size(),
selrect,
&fill.to_paint(&selrect),
);
}
} else {
self.drawing_surface
.canvas()
.draw_rect(selrect, &fill.to_paint(&selrect));
}
}
fn render_all_from_cache(&mut self) -> Result<(), String> { fn render_all_from_cache(&mut self) -> Result<(), String> {
self.reset_canvas(); self.reset_canvas();

View file

@ -92,14 +92,16 @@ impl Shape {
self.fills.clear(); self.fills.clear();
} }
pub fn add_gradient_stop(&mut self, color: skia::Color, offset: f32) -> Result<(), String> { pub fn add_gradient_stops(&mut self, buffer: Vec<RawStopData>) -> Result<(), String> {
let fill = self.fills.last_mut().ok_or("Shape has no fills")?; let fill = self.fills.last_mut().ok_or("Shape has no fills")?;
let gradient = match fill { let gradient = match fill {
Fill::LinearGradient(g) => Ok(g), Fill::LinearGradient(g) => Ok(g),
_ => Err("Active fill is not a gradient"), _ => Err("Active fill is not a gradient"),
}?; }?;
gradient.add_stop(color, offset); for stop in buffer.into_iter() {
gradient.add_stop(stop.color(), stop.offset());
}
Ok(()) Ok(())
} }

View file

@ -4,6 +4,23 @@ use super::Color;
use crate::math; use crate::math;
use uuid::Uuid; use uuid::Uuid;
#[derive(Debug)]
#[repr(C)]
pub struct RawStopData {
color: [u8; 4],
offset: u8,
}
impl RawStopData {
pub fn color(&self) -> skia::Color {
skia::Color::from_argb(self.color[3], self.color[0], self.color[1], self.color[2])
}
pub fn offset(&self) -> f32 {
self.offset as f32 / 100.0
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Gradient { pub struct Gradient {
colors: Vec<Color>, colors: Vec<Color>,
@ -43,10 +60,20 @@ impl Gradient {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ImageFill { pub struct ImageFill {
pub id: Uuid, id: Uuid,
pub alpha: u8, opacity: u8,
pub height: f32, height: i32,
pub width: f32, width: i32,
}
impl ImageFill {
pub fn size(&self) -> (i32, i32) {
(self.width, self.height)
}
pub fn id(&self) -> Uuid {
self.id
}
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -67,10 +94,10 @@ impl Fill {
}) })
} }
pub fn new_image_fill(id: Uuid, alpha: u8, height: f32, width: f32) -> Self { pub fn new_image_fill(id: Uuid, opacity: u8, (width, height): (i32, i32)) -> Self {
Self::Image(ImageFill { Self::Image(ImageFill {
id, id,
alpha, opacity,
height, height,
width, width,
}) })
@ -99,7 +126,7 @@ impl Fill {
p.set_style(skia::PaintStyle::Fill); p.set_style(skia::PaintStyle::Fill);
p.set_anti_alias(true); p.set_anti_alias(true);
p.set_blend_mode(skia::BlendMode::SrcOver); p.set_blend_mode(skia::BlendMode::SrcOver);
p.set_alpha(image_fill.alpha); p.set_alpha(image_fill.opacity);
p p
} }
} }

View file

@ -6,12 +6,12 @@ pub type Image = skia::Image;
pub fn draw_image_in_container( pub fn draw_image_in_container(
canvas: &skia::Canvas, canvas: &skia::Canvas,
image: &Image, image: &Image,
size: (f32, f32), size: (i32, i32),
container: skia::Rect, container: skia::Rect,
paint: &skia::Paint, paint: &skia::Paint,
) { ) {
let width = size.0; let width = size.0 as f32;
let height = size.1; let height = size.1 as f32;
let image_aspect_ratio = width / height; let image_aspect_ratio = width / height;
// Container size // Container size