From 001aa3f31909c2cd948f0ea38102ef775f069b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 10 Dec 2024 15:03:15 +0100 Subject: [PATCH 1/6] :recycle: Refactor into render submodules --- render-wasm/src/render.rs | 74 ++--------------------------- render-wasm/src/render/gpu_state.rs | 45 ++++++++++++++++++ render-wasm/src/render/options.rs | 26 ++++++++++ 3 files changed, 76 insertions(+), 69 deletions(-) create mode 100644 render-wasm/src/render/gpu_state.rs create mode 100644 render-wasm/src/render/options.rs diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 4b89e06597..063ab4b7a6 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1,56 +1,17 @@ -use skia::gpu::{self, gl::FramebufferInfo, DirectContext}; +mod gpu_state; +mod options; + use skia::Contains; use skia_safe as skia; use std::collections::HashMap; use uuid::Uuid; -use crate::debug; use crate::math::Rect; use crate::shapes::{draw_image_in_container, Fill, Image, Kind, Shape}; use crate::view::Viewbox; -struct GpuState { - pub context: DirectContext, - framebuffer_info: FramebufferInfo, -} - -impl GpuState { - fn new() -> Self { - let interface = skia::gpu::gl::Interface::new_native().unwrap(); - let context = skia::gpu::direct_contexts::make_gl(interface, None).unwrap(); - let framebuffer_info = { - let mut fboid: gl::types::GLint = 0; - unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; - - FramebufferInfo { - fboid: fboid.try_into().unwrap(), - format: skia::gpu::gl::Format::RGBA8.into(), - protected: skia::gpu::Protected::No, - } - }; - - GpuState { - context, - framebuffer_info, - } - } - - /// Create a Skia surface that will be used for rendering. - fn create_target_surface(&mut self, width: i32, height: i32) -> skia::Surface { - let backend_render_target = - gpu::backend_render_targets::make_gl((width, height), 1, 8, self.framebuffer_info); - - gpu::surfaces::wrap_backend_render_target( - &mut self.context, - &backend_render_target, - skia::gpu::SurfaceOrigin::BottomLeft, - skia::ColorType::RGBA8888, - None, - None, - ) - .unwrap() - } -} +use gpu_state::GpuState; +use options::RenderOptions; pub(crate) struct CachedSurfaceImage { pub image: Image, @@ -64,31 +25,6 @@ impl CachedSurfaceImage { } } -#[derive(Debug, Copy, Clone, PartialEq)] -struct RenderOptions { - debug_flags: u32, - dpr: Option, -} - -impl Default for RenderOptions { - fn default() -> Self { - Self { - debug_flags: 0x00, - dpr: None, - } - } -} - -impl RenderOptions { - pub fn is_debug_visible(&self) -> bool { - self.debug_flags & debug::DEBUG_VISIBLE == debug::DEBUG_VISIBLE - } - - pub fn dpr(&self) -> f32 { - self.dpr.unwrap_or(1.0) - } -} - pub(crate) struct RenderState { gpu_state: GpuState, pub final_surface: skia::Surface, diff --git a/render-wasm/src/render/gpu_state.rs b/render-wasm/src/render/gpu_state.rs new file mode 100644 index 0000000000..9c1b87f282 --- /dev/null +++ b/render-wasm/src/render/gpu_state.rs @@ -0,0 +1,45 @@ +use skia_safe as skia; +use skia_safe::gpu::{self, gl::FramebufferInfo, DirectContext}; + +pub struct GpuState { + pub context: DirectContext, + framebuffer_info: FramebufferInfo, +} + +impl GpuState { + pub fn new() -> Self { + let interface = gpu::gl::Interface::new_native().unwrap(); + let context = gpu::direct_contexts::make_gl(interface, None).unwrap(); + let framebuffer_info = { + let mut fboid: gl::types::GLint = 0; + unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; + + FramebufferInfo { + fboid: fboid.try_into().unwrap(), + format: gpu::gl::Format::RGBA8.into(), + protected: gpu::Protected::No, + } + }; + + GpuState { + context, + framebuffer_info, + } + } + + /// Create a Skia surface that will be used for rendering. + pub fn create_target_surface(&mut self, width: i32, height: i32) -> skia::Surface { + let backend_render_target = + gpu::backend_render_targets::make_gl((width, height), 1, 8, self.framebuffer_info); + + gpu::surfaces::wrap_backend_render_target( + &mut self.context, + &backend_render_target, + gpu::SurfaceOrigin::BottomLeft, + skia::ColorType::RGBA8888, + None, + None, + ) + .unwrap() + } +} diff --git a/render-wasm/src/render/options.rs b/render-wasm/src/render/options.rs new file mode 100644 index 0000000000..5e27f674b8 --- /dev/null +++ b/render-wasm/src/render/options.rs @@ -0,0 +1,26 @@ +use crate::debug; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct RenderOptions { + pub debug_flags: u32, + pub dpr: Option, +} + +impl Default for RenderOptions { + fn default() -> Self { + Self { + debug_flags: 0x00, + dpr: None, + } + } +} + +impl RenderOptions { + pub fn is_debug_visible(&self) -> bool { + self.debug_flags & debug::DEBUG_VISIBLE == debug::DEBUG_VISIBLE + } + + pub fn dpr(&self) -> f32 { + self.dpr.unwrap_or(1.0) + } +} From 967bc75a1c38a929461500e5f40c3e59c9d38203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 10 Dec 2024 15:29:49 +0100 Subject: [PATCH 2/6] :recycle: Move rendering a single shape to the shape itself --- render-wasm/src/main.rs | 2 +- render-wasm/src/render.rs | 94 ++++++--------------- render-wasm/src/{shapes => render}/blend.rs | 0 render-wasm/src/shapes.rs | 5 +- render-wasm/src/shapes/render.rs | 92 ++++++++++++++++++++ 5 files changed, 121 insertions(+), 72 deletions(-) rename render-wasm/src/{shapes => render}/blend.rs (100%) create mode 100644 render-wasm/src/shapes/render.rs diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index f15d3232f0..fb751aa07e 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -307,7 +307,7 @@ pub extern "C" fn clear_shape_fills() { pub extern "C" fn set_shape_blend_mode(mode: i32) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.set_blend_mode(shapes::BlendMode::from(mode)); + shape.set_blend_mode(render::BlendMode::from(mode)); } } diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 063ab4b7a6..9c8c2ebad2 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1,18 +1,30 @@ -mod gpu_state; -mod options; - use skia::Contains; use skia_safe as skia; use std::collections::HashMap; use uuid::Uuid; -use crate::math::Rect; -use crate::shapes::{draw_image_in_container, Fill, Image, Kind, Shape}; +use crate::shapes::{Image, Shape}; use crate::view::Viewbox; +mod blend; +mod gpu_state; +mod options; + use gpu_state::GpuState; use options::RenderOptions; +pub use blend::BlendMode; + +pub trait Renderable { + fn render( + &self, + surface: &mut skia::Surface, + images: &HashMap, + ) -> Result<(), String>; + fn blend_mode(&self) -> BlendMode; + fn opacity(&self) -> f32; +} + pub(crate) struct CachedSurfaceImage { pub image: Image, pub viewbox: Viewbox, @@ -133,39 +145,15 @@ impl RenderState { .reset_matrix(); } - pub fn render_single_shape(&mut self, shape: &Shape) { - let mut transform = skia::Matrix::new_identity(); - let (translate_x, translate_y) = shape.translation(); - let (scale_x, scale_y) = shape.scale(); - let (skew_x, skew_y) = shape.skew(); - transform.set_all( - scale_x, - skew_x, - translate_x, - skew_y, - scale_y, - translate_y, - 0., - 0., - 1., - ); - - // Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc - let center = shape.selrect.center(); - let mut matrix = skia::Matrix::new_identity(); - matrix.pre_translate(center); - matrix.pre_concat(&transform); - matrix.pre_translate(-center); - - self.drawing_surface.canvas().concat(&matrix); - - for fill in shape.fills().rev() { - self.render_fill(fill, shape.selrect, &shape.kind); - } + pub fn render_single_element(&mut self, element: &impl Renderable) { + element + .render(&mut self.drawing_surface, &self.images) + .unwrap(); let mut paint = skia::Paint::default(); - paint.set_blend_mode(shape.blend_mode.into()); - paint.set_alpha_f(shape.opacity); + paint.set_blend_mode(element.blend_mode().into()); + paint.set_alpha_f(element.opacity()); + self.drawing_surface.draw( &mut self.final_surface.canvas(), (0.0, 0.0), @@ -217,38 +205,6 @@ impl RenderState { self.flush(); } - fn render_fill(&mut self, fill: &Fill, selrect: Rect, kind: &Kind) { - match (fill, kind) { - (Fill::Image(image_fill), kind) => { - 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(), - kind, - &fill.to_paint(&selrect), - ); - } - } - (_, Kind::Rect(rect)) => { - self.drawing_surface - .canvas() - .draw_rect(rect, &fill.to_paint(&selrect)); - } - (_, Kind::Circle(rect)) => { - self.drawing_surface - .canvas() - .draw_oval(rect, &fill.to_paint(&selrect)); - } - (_, Kind::Path(path)) => { - self.drawing_surface - .canvas() - .draw_path(&path.to_skia_path(), &fill.to_paint(&selrect)); - } - } - } - fn render_all_from_cache(&mut self) -> Result<(), String> { self.reset_canvas(); @@ -353,7 +309,7 @@ impl RenderState { self.drawing_surface.canvas().save(); if !id.is_nil() { - self.render_single_shape(shape); + self.render_single_element(shape); if shape.clip_content { self.drawing_surface.canvas().clip_rect( shape.selrect, diff --git a/render-wasm/src/shapes/blend.rs b/render-wasm/src/render/blend.rs similarity index 100% rename from render-wasm/src/shapes/blend.rs rename to render-wasm/src/render/blend.rs diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 5362c6d4f5..71b573d9ff 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -2,11 +2,12 @@ use crate::math; use skia_safe as skia; use uuid::Uuid; -mod blend; +use crate::render::BlendMode; + mod fills; mod images; mod paths; -pub use blend::*; +mod render; pub use fills::*; pub use images::*; pub use paths::*; diff --git a/render-wasm/src/shapes/render.rs b/render-wasm/src/shapes/render.rs new file mode 100644 index 0000000000..1a4fa74db5 --- /dev/null +++ b/render-wasm/src/shapes/render.rs @@ -0,0 +1,92 @@ +use skia_safe as skia; +use std::collections::HashMap; +use uuid::Uuid; + +use super::{draw_image_in_container, Fill, Image, Kind, Shape}; +use crate::math::Rect; +use crate::render::Renderable; + +impl Renderable for Shape { + fn blend_mode(&self) -> crate::render::BlendMode { + self.blend_mode + } + + fn opacity(&self) -> f32 { + self.opacity + } + + fn render( + &self, + surface: &mut skia_safe::Surface, + images: &HashMap, + ) -> Result<(), String> { + let mut transform = skia::Matrix::new_identity(); + let (translate_x, translate_y) = self.translation(); + let (scale_x, scale_y) = self.scale(); + let (skew_x, skew_y) = self.skew(); + transform.set_all( + scale_x, + skew_x, + translate_x, + skew_y, + scale_y, + translate_y, + 0., + 0., + 1., + ); + + // Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc + let center = self.selrect.center(); + let mut matrix = skia::Matrix::new_identity(); + matrix.pre_translate(center); + matrix.pre_concat(&transform); + matrix.pre_translate(-center); + + surface.canvas().concat(&matrix); + + for fill in self.fills().rev() { + render_fill(surface, images, fill, self.selrect, &self.kind); + } + + let mut paint = skia::Paint::default(); + paint.set_blend_mode(self.blend_mode.into()); + paint.set_alpha_f(self.opacity); + + Ok(()) + } +} + +fn render_fill( + surface: &mut skia::Surface, + images: &HashMap, + fill: &Fill, + selrect: Rect, + kind: &Kind, +) { + match (fill, kind) { + (Fill::Image(image_fill), kind) => { + let image = images.get(&image_fill.id()); + if let Some(image) = image { + draw_image_in_container( + surface.canvas(), + &image, + image_fill.size(), + kind, + &fill.to_paint(&selrect), + ); + } + } + (_, Kind::Rect(rect)) => { + surface.canvas().draw_rect(rect, &fill.to_paint(&selrect)); + } + (_, Kind::Circle(rect)) => { + surface.canvas().draw_oval(rect, &fill.to_paint(&selrect)); + } + (_, Kind::Path(path)) => { + surface + .canvas() + .draw_path(&path.to_skia_path(), &fill.to_paint(&selrect)); + } + } +} From 7b1934dcb6e731b91ad0cec40d6f96fe9b2fb90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 10 Dec 2024 15:41:50 +0100 Subject: [PATCH 3/6] :recycle: Create an ImageStore type --- render-wasm/src/render.rs | 25 ++++++-------- render-wasm/src/render/images.rs | 33 +++++++++++++++++++ render-wasm/src/shapes.rs | 2 +- .../src/shapes/{render.rs => renderable.rs} | 14 +++----- 4 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 render-wasm/src/render/images.rs rename render-wasm/src/shapes/{render.rs => renderable.rs} (87%) diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 9c8c2ebad2..e3a34b9830 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -1,26 +1,25 @@ +use std::collections::HashMap; + use skia::Contains; use skia_safe as skia; -use std::collections::HashMap; use uuid::Uuid; -use crate::shapes::{Image, Shape}; +use crate::shapes::Shape; use crate::view::Viewbox; mod blend; mod gpu_state; +mod images; mod options; use gpu_state::GpuState; use options::RenderOptions; pub use blend::BlendMode; +pub use images::*; pub trait Renderable { - fn render( - &self, - surface: &mut skia::Surface, - images: &HashMap, - ) -> Result<(), String>; + fn render(&self, surface: &mut skia::Surface, images: &ImageStore) -> Result<(), String>; fn blend_mode(&self) -> BlendMode; fn opacity(&self) -> f32; } @@ -45,7 +44,7 @@ pub(crate) struct RenderState { pub cached_surface_image: Option, options: RenderOptions, pub viewbox: Viewbox, - images: HashMap, + images: ImageStore, } impl RenderState { @@ -68,20 +67,16 @@ impl RenderState { cached_surface_image: None, options: RenderOptions::default(), viewbox: Viewbox::new(width as f32, height as f32), - images: HashMap::with_capacity(2048), + images: ImageStore::new(), } } 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(()) + self.images.add(id, image_data) } pub fn has_image(&mut self, id: &Uuid) -> bool { - self.images.contains_key(id) + self.images.contains(id) } pub fn set_debug_flags(&mut self, debug: u32) { diff --git a/render-wasm/src/render/images.rs b/render-wasm/src/render/images.rs new file mode 100644 index 0000000000..a1d9f5fe76 --- /dev/null +++ b/render-wasm/src/render/images.rs @@ -0,0 +1,33 @@ +use skia_safe as skia; +use std::collections::HashMap; +use uuid::Uuid; + +pub type Image = skia::Image; + +pub struct ImageStore { + images: HashMap, +} + +impl ImageStore { + pub fn new() -> Self { + Self { + images: HashMap::with_capacity(2048), + } + } + + pub fn add(&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 contains(&mut self, id: &Uuid) -> bool { + self.images.contains_key(id) + } + + pub fn get(&self, id: &Uuid) -> Option<&Image> { + self.images.get(id) + } +} diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 71b573d9ff..82120ac92e 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -7,7 +7,7 @@ use crate::render::BlendMode; mod fills; mod images; mod paths; -mod render; +mod renderable; pub use fills::*; pub use images::*; pub use paths::*; diff --git a/render-wasm/src/shapes/render.rs b/render-wasm/src/shapes/renderable.rs similarity index 87% rename from render-wasm/src/shapes/render.rs rename to render-wasm/src/shapes/renderable.rs index 1a4fa74db5..718dc2e636 100644 --- a/render-wasm/src/shapes/render.rs +++ b/render-wasm/src/shapes/renderable.rs @@ -1,10 +1,8 @@ use skia_safe as skia; -use std::collections::HashMap; -use uuid::Uuid; -use super::{draw_image_in_container, Fill, Image, Kind, Shape}; +use super::{draw_image_in_container, Fill, Kind, Shape}; use crate::math::Rect; -use crate::render::Renderable; +use crate::render::{ImageStore, Renderable}; impl Renderable for Shape { fn blend_mode(&self) -> crate::render::BlendMode { @@ -15,11 +13,7 @@ impl Renderable for Shape { self.opacity } - fn render( - &self, - surface: &mut skia_safe::Surface, - images: &HashMap, - ) -> Result<(), String> { + fn render(&self, surface: &mut skia_safe::Surface, images: &ImageStore) -> Result<(), String> { let mut transform = skia::Matrix::new_identity(); let (translate_x, translate_y) = self.translation(); let (scale_x, scale_y) = self.scale(); @@ -59,7 +53,7 @@ impl Renderable for Shape { fn render_fill( surface: &mut skia::Surface, - images: &HashMap, + images: &ImageStore, fill: &Fill, selrect: Rect, kind: &Kind, From c7c43d6313a6f42d2d980a1563c8161f2bbe8c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 10 Dec 2024 15:54:51 +0100 Subject: [PATCH 4/6] :recycle: Decouple render and shapes --- render-wasm/src/render.rs | 46 +++++++++++++++------------- render-wasm/src/shapes/renderable.rs | 33 +++++++++++++++----- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index e3a34b9830..46723a5640 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -4,7 +4,7 @@ use skia::Contains; use skia_safe as skia; use uuid::Uuid; -use crate::shapes::Shape; +use crate::math; use crate::view::Viewbox; mod blend; @@ -22,6 +22,10 @@ pub trait Renderable { fn render(&self, surface: &mut skia::Surface, images: &ImageStore) -> Result<(), String>; fn blend_mode(&self) -> BlendMode; fn opacity(&self) -> f32; + fn bounds(&self) -> math::Rect; + fn hidden(&self) -> bool; + fn clip(&self) -> bool; + fn children_ids(&self) -> Vec; } pub(crate) struct CachedSurfaceImage { @@ -160,10 +164,10 @@ impl RenderState { .clear(skia::Color::TRANSPARENT); } - pub fn navigate(&mut self, shapes: &HashMap) -> Result<(), String> { + pub fn navigate(&mut self, tree: &HashMap) -> Result<(), String> { if let Some(cached_surface_image) = self.cached_surface_image.as_ref() { if cached_surface_image.is_dirty(&self.viewbox) { - self.render_all(shapes, true); + self.render_all(tree, true); } else { self.render_all_from_cache()?; } @@ -174,7 +178,7 @@ impl RenderState { pub fn render_all( &mut self, - shapes: &HashMap, + tree: &HashMap, generate_cached_surface_image: bool, ) { self.reset_canvas(); @@ -184,7 +188,7 @@ impl RenderState { ); self.translate(self.viewbox.pan_x, self.viewbox.pan_y); - let is_complete = self.render_shape_tree(&Uuid::nil(), shapes); + let is_complete = self.render_shape_tree(&Uuid::nil(), tree); if generate_cached_surface_image || self.cached_surface_image.is_none() { self.cached_surface_image = Some(CachedSurfaceImage { image: self.final_surface.image_snapshot(), @@ -252,7 +256,7 @@ impl RenderState { self.debug_surface.canvas().draw_rect(scaled_rect, &paint); } - fn render_debug_shape(&mut self, shape: &Shape, intersected: bool) { + fn render_debug_element(&mut self, element: &impl Renderable, intersected: bool) { let mut paint = skia::Paint::default(); paint.set_style(skia::PaintStyle::Stroke); paint.set_color(if intersected { @@ -262,7 +266,7 @@ impl RenderState { }); paint.set_stroke_width(1.); - let mut scaled_rect = shape.selrect.clone(); + let mut scaled_rect = element.bounds(); let x = 100. + scaled_rect.x() * 0.2; let y = 100. + scaled_rect.y() * 0.2; let width = scaled_rect.width() * 0.2; @@ -284,18 +288,18 @@ impl RenderState { } // Returns a boolean indicating if the viewbox contains the rendered shapes - fn render_shape_tree(&mut self, id: &Uuid, shapes: &HashMap) -> bool { - let shape = shapes.get(&id).unwrap(); - let mut is_complete = self.viewbox.area.contains(shape.selrect); + fn render_shape_tree(&mut self, root_id: &Uuid, tree: &HashMap) -> bool { + let element = tree.get(&root_id).unwrap(); + let mut is_complete = self.viewbox.area.contains(element.bounds()); - if !id.is_nil() { - if !shape.selrect.intersects(self.viewbox.area) || shape.hidden { - self.render_debug_shape(shape, false); + if !root_id.is_nil() { + if !element.bounds().intersects(self.viewbox.area) || element.hidden() { + self.render_debug_element(element, false); // TODO: This means that not all the shapes are renderer so we // need to call a render_all on the zoom out. return is_complete; // TODO return is_complete or return false?? } else { - self.render_debug_shape(shape, true); + self.render_debug_element(element, true); } } @@ -303,11 +307,11 @@ impl RenderState { self.final_surface.canvas().save(); self.drawing_surface.canvas().save(); - if !id.is_nil() { - self.render_single_element(shape); - if shape.clip_content { + if !root_id.is_nil() { + self.render_single_element(element); + if element.clip() { self.drawing_surface.canvas().clip_rect( - shape.selrect, + element.bounds(), skia::ClipOp::Intersect, true, ); @@ -315,13 +319,13 @@ impl RenderState { } // draw all the children shapes - let shape_ids = shape.children.iter(); - for shape_id in shape_ids { - is_complete = self.render_shape_tree(shape_id, shapes) && is_complete; + for id in element.children_ids() { + is_complete = self.render_shape_tree(&id, tree) && is_complete; } self.final_surface.canvas().restore(); self.drawing_surface.canvas().restore(); + return is_complete; } } diff --git a/render-wasm/src/shapes/renderable.rs b/render-wasm/src/shapes/renderable.rs index 718dc2e636..fa11447799 100644 --- a/render-wasm/src/shapes/renderable.rs +++ b/render-wasm/src/shapes/renderable.rs @@ -1,18 +1,11 @@ use skia_safe as skia; +use uuid::Uuid; use super::{draw_image_in_container, Fill, Kind, Shape}; use crate::math::Rect; use crate::render::{ImageStore, Renderable}; impl Renderable for Shape { - fn blend_mode(&self) -> crate::render::BlendMode { - self.blend_mode - } - - fn opacity(&self) -> f32 { - self.opacity - } - fn render(&self, surface: &mut skia_safe::Surface, images: &ImageStore) -> Result<(), String> { let mut transform = skia::Matrix::new_identity(); let (translate_x, translate_y) = self.translation(); @@ -49,6 +42,30 @@ impl Renderable for Shape { Ok(()) } + + fn blend_mode(&self) -> crate::render::BlendMode { + self.blend_mode + } + + fn opacity(&self) -> f32 { + self.opacity + } + + fn hidden(&self) -> bool { + self.hidden + } + + fn bounds(&self) -> Rect { + self.selrect + } + + fn clip(&self) -> bool { + self.clip_content + } + + fn children_ids(&self) -> Vec { + self.children.clone() + } } fn render_fill( From 9f7428d44a21436ed20859643435662810c6d973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 10 Dec 2024 16:07:33 +0100 Subject: [PATCH 5/6] :recycle: Fix visibility of Shape properties --- render-wasm/src/main.rs | 25 +++++++---------- render-wasm/src/shapes.rs | 57 ++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index fb751aa07e..343f26e5b9 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -109,7 +109,7 @@ pub unsafe extern "C" fn set_shape_kind_circle() { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.kind = Kind::Circle(math::Rect::new_empty()); + shape.set_kind(Kind::Circle(math::Rect::new_empty())); } } @@ -118,7 +118,7 @@ pub unsafe extern "C" fn set_shape_kind_rect() { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.kind = Kind::Rect(math::Rect::new_empty()); + shape.set_kind(Kind::Rect(math::Rect::new_empty())); } } @@ -127,7 +127,7 @@ pub unsafe extern "C" fn set_shape_kind_path() { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { let p = Path::try_from(Vec::new()).unwrap(); - shape.kind = Kind::Path(p); + shape.set_kind(Kind::Path(p)); } } @@ -143,7 +143,7 @@ pub extern "C" fn set_shape_selrect(left: f32, top: f32, right: f32, bottom: f32 pub unsafe extern "C" fn set_shape_clip_content(clip_content: bool) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.clip_content = clip_content; + shape.set_clip(clip_content); } } @@ -151,7 +151,7 @@ pub unsafe extern "C" fn set_shape_clip_content(clip_content: bool) { pub unsafe extern "C" fn set_shape_rotation(rotation: f32) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.rotation = rotation; + shape.set_rotation(rotation); } } @@ -159,12 +159,7 @@ pub unsafe extern "C" fn set_shape_rotation(rotation: f32) { pub extern "C" fn set_shape_transform(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.transform.a = a; - shape.transform.b = b; - shape.transform.c = c; - shape.transform.d = d; - shape.transform.e = e; - shape.transform.f = f; + shape.set_transform(a, b, c, d, e, f); } } @@ -173,7 +168,7 @@ pub extern "C" fn add_shape_child(a: u32, b: u32, c: u32, d: u32) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); let id = uuid_from_u32_quartet(a, b, c, d); if let Some(shape) = state.current_shape() { - shape.children.push(id); + shape.add_child(id); } } @@ -181,7 +176,7 @@ pub extern "C" fn add_shape_child(a: u32, b: u32, c: u32, d: u32) { pub extern "C" fn clear_shape_children() { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.children.clear(); + shape.clear_children(); } } @@ -315,7 +310,7 @@ pub extern "C" fn set_shape_blend_mode(mode: i32) { pub extern "C" fn set_shape_opacity(opacity: f32) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.opacity = opacity; + shape.set_opacity(opacity); } } @@ -323,7 +318,7 @@ pub extern "C" fn set_shape_opacity(opacity: f32) { pub extern "C" fn set_shape_hidden(hidden: bool) { let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer"); if let Some(shape) = state.current_shape() { - shape.hidden = hidden; + shape.set_hidden(hidden); } } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 82120ac92e..743cfde6ff 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -47,17 +47,17 @@ impl Matrix { #[derive(Debug, Clone)] #[allow(dead_code)] pub struct Shape { - pub id: Uuid, - pub children: Vec, - pub kind: Kind, - pub selrect: math::Rect, - pub transform: Matrix, - pub rotation: f32, - pub clip_content: bool, + id: Uuid, + children: Vec, + kind: Kind, + selrect: math::Rect, + transform: Matrix, + rotation: f32, + clip_content: bool, fills: Vec, - pub blend_mode: BlendMode, - pub opacity: f32, - pub hidden: bool, + blend_mode: BlendMode, + opacity: f32, + hidden: bool, } impl Shape { @@ -90,6 +90,43 @@ impl Shape { }; } + pub fn set_kind(&mut self, kind: Kind) { + self.kind = kind; + } + + pub fn set_clip(&mut self, value: bool) { + self.clip_content = value; + } + + pub fn set_rotation(&mut self, angle: f32) { + self.rotation = angle; + } + + pub fn set_transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) { + self.transform.a = a; + self.transform.b = b; + self.transform.c = c; + self.transform.d = d; + self.transform.e = e; + self.transform.f = f; + } + + pub fn set_opacity(&mut self, opacity: f32) { + self.opacity = opacity; + } + + pub fn set_hidden(&mut self, value: bool) { + self.hidden = value; + } + + pub fn add_child(&mut self, id: Uuid) { + self.children.push(id); + } + + pub fn clear_children(&mut self) { + self.children.clear(); + } + pub fn translation(&self) -> (f32, f32) { (self.transform.e, self.transform.f) } From c9355a257a0d294a624aa33305a96739bb0a5b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 10 Dec 2024 16:15:07 +0100 Subject: [PATCH 6/6] :recycle: Refactor custom matrix code into submodule --- render-wasm/src/shapes.rs | 45 ++------------------ render-wasm/src/shapes/matrix.rs | 61 ++++++++++++++++++++++++++++ render-wasm/src/shapes/renderable.rs | 16 +------- 3 files changed, 66 insertions(+), 56 deletions(-) create mode 100644 render-wasm/src/shapes/matrix.rs diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 743cfde6ff..78ff1f4138 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -6,10 +6,13 @@ use crate::render::BlendMode; mod fills; mod images; +mod matrix; mod paths; mod renderable; + pub use fills::*; pub use images::*; +use matrix::*; pub use paths::*; #[derive(Debug, Clone, PartialEq)] @@ -21,29 +24,6 @@ pub enum Kind { pub type Color = skia::Color; -#[derive(Debug, Clone, Copy)] -pub struct Matrix { - pub a: f32, - pub b: f32, - pub c: f32, - pub d: f32, - pub e: f32, - pub f: f32, -} - -impl Matrix { - pub fn identity() -> Self { - Self { - a: 1., - b: 0., - c: 0., - d: 1., - e: 0., - f: 0., - } - } -} - #[derive(Debug, Clone)] #[allow(dead_code)] pub struct Shape { @@ -103,12 +83,7 @@ impl Shape { } pub fn set_transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) { - self.transform.a = a; - self.transform.b = b; - self.transform.c = c; - self.transform.d = d; - self.transform.e = e; - self.transform.f = f; + self.transform = Matrix::new(a, b, c, d, e, f); } pub fn set_opacity(&mut self, opacity: f32) { @@ -127,18 +102,6 @@ impl Shape { self.children.clear(); } - pub fn translation(&self) -> (f32, f32) { - (self.transform.e, self.transform.f) - } - - pub fn scale(&self) -> (f32, f32) { - (self.transform.a, self.transform.d) - } - - pub fn skew(&self) -> (f32, f32) { - (self.transform.c, self.transform.b) - } - pub fn fills(&self) -> std::slice::Iter { self.fills.iter() } diff --git a/render-wasm/src/shapes/matrix.rs b/render-wasm/src/shapes/matrix.rs new file mode 100644 index 0000000000..bd353b910d --- /dev/null +++ b/render-wasm/src/shapes/matrix.rs @@ -0,0 +1,61 @@ +use skia_safe as skia; + +#[derive(Debug, Clone, Copy)] +pub struct Matrix { + pub a: f32, + pub b: f32, + pub c: f32, + pub d: f32, + pub e: f32, + pub f: f32, +} + +impl Matrix { + pub fn new(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self { + Self { a, b, c, d, e, f } + } + + pub fn identity() -> Self { + Self { + a: 1., + b: 0., + c: 0., + d: 1., + e: 0., + f: 0., + } + } + + pub fn to_skia_matrix(&self) -> skia::Matrix { + let mut res = skia::Matrix::new_identity(); + + let (translate_x, translate_y) = self.translation(); + let (scale_x, scale_y) = self.scale(); + let (skew_x, skew_y) = self.skew(); + res.set_all( + scale_x, + skew_x, + translate_x, + skew_y, + scale_y, + translate_y, + 0., + 0., + 1., + ); + + res + } + + fn translation(&self) -> (f32, f32) { + (self.e, self.f) + } + + fn scale(&self) -> (f32, f32) { + (self.a, self.d) + } + + fn skew(&self) -> (f32, f32) { + (self.c, self.b) + } +} diff --git a/render-wasm/src/shapes/renderable.rs b/render-wasm/src/shapes/renderable.rs index fa11447799..ef124c272c 100644 --- a/render-wasm/src/shapes/renderable.rs +++ b/render-wasm/src/shapes/renderable.rs @@ -7,21 +7,7 @@ use crate::render::{ImageStore, Renderable}; impl Renderable for Shape { fn render(&self, surface: &mut skia_safe::Surface, images: &ImageStore) -> Result<(), String> { - let mut transform = skia::Matrix::new_identity(); - let (translate_x, translate_y) = self.translation(); - let (scale_x, scale_y) = self.scale(); - let (skew_x, skew_y) = self.skew(); - transform.set_all( - scale_x, - skew_x, - translate_x, - skew_y, - scale_y, - translate_y, - 0., - 0., - 1., - ); + let transform = self.transform.to_skia_matrix(); // Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc let center = self.selrect.center();