diff --git a/frontend/resources/wasm-playground/js/lib.js b/frontend/resources/wasm-playground/js/lib.js index d653398a19..4aefe6701f 100644 --- a/frontend/resources/wasm-playground/js/lib.js +++ b/frontend/resources/wasm-playground/js/lib.js @@ -71,11 +71,11 @@ function ptr8ToPtr32(ptr8) { return ptr8 >>> 2; } -function allocBytes(size) { +export function allocBytes(size) { return Module._alloc_bytes(size); } -function getHeapU32() { +export function getHeapU32() { return Module.HEAPU32; } diff --git a/frontend/resources/wasm-playground/plus.html b/frontend/resources/wasm-playground/plus.html new file mode 100644 index 0000000000..9ed6734bff --- /dev/null +++ b/frontend/resources/wasm-playground/plus.html @@ -0,0 +1,109 @@ + + + + + WASM + WebGL2 Canvas + + + + + + + diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index c505cd8517..7db114109c 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -2,6 +2,7 @@ use skia_safe::{self as skia}; use crate::render::BlendMode; use crate::uuid::Uuid; +use std::cell::OnceCell; use std::collections::{HashMap, HashSet}; mod blurs; @@ -177,6 +178,7 @@ pub struct Shape { pub svg_attrs: HashMap, pub shadows: Vec, pub layout_item: Option, + pub extrect: OnceCell, } impl Shape { @@ -202,9 +204,14 @@ impl Shape { svg_attrs: HashMap::new(), shadows: vec![], layout_item: None, + extrect: OnceCell::new(), } } + fn invalidate_extrect(&mut self) { + self.extrect = OnceCell::new(); + } + pub fn set_parent(&mut self, id: Uuid) { self.parent_id = Some(id); } @@ -233,6 +240,7 @@ impl Shape { } pub fn set_selrect(&mut self, left: f32, top: f32, right: f32, bottom: f32) { + self.invalidate_extrect(); self.selrect.set_ltrb(left, top, right, bottom); if let Type::Text(ref mut text) = self.shape_type { text.set_xywh(left, top, right - left, bottom - top); @@ -423,6 +431,7 @@ impl Shape { } pub fn set_blur(&mut self, blur_type: u8, hidden: bool, value: f32) { + self.invalidate_extrect(); self.blur = Blur::new(blur_type, hidden, value); } @@ -456,6 +465,7 @@ impl Shape { } pub fn add_stroke(&mut self, s: Stroke) { + self.invalidate_extrect(); self.strokes.push(s) } @@ -466,12 +476,13 @@ impl Shape { } pub fn clear_strokes(&mut self) { + self.invalidate_extrect(); self.strokes.clear(); } pub fn set_path_segments(&mut self, segments: Vec) { + self.invalidate_extrect(); let path = Path::new(segments); - match &mut self.shape_type { Type::Bool(Bool { bool_type, .. }) => { self.shape_type = Type::Bool(Bool { @@ -549,8 +560,8 @@ impl Shape { } pub fn visually_insignificant(&self, scale: f32) -> bool { - self.selrect.width() * scale < MIN_VISIBLE_SIZE - || self.selrect.height() * scale < MIN_VISIBLE_SIZE + let extrect = self.extrect(); + extrect.width() * scale < MIN_VISIBLE_SIZE || extrect.height() * scale < MIN_VISIBLE_SIZE } pub fn should_use_antialias(&self, scale: f32) -> bool { @@ -585,7 +596,40 @@ impl Shape { } pub fn extrect(&self) -> math::Rect { - let mut rect = self.bounds().to_rect(); + *self.extrect.get_or_init(|| self.calculate_extrect()) + } + + pub fn calculate_extrect(&self) -> math::Rect { + let mut max_stroke: f32 = 0.; + let is_open = if let Type::Path(p) = &self.shape_type { + p.is_open() + } else { + false + }; + + for stroke in self.strokes.iter() { + let width = match stroke.render_kind(is_open) { + StrokeKind::Inner => 0., + StrokeKind::Center => stroke.width / 2., + StrokeKind::Outer => stroke.width, + }; + max_stroke = max_stroke.max(width); + } + let mut rect = if let Some(path) = self.get_skia_path() { + path.compute_tight_bounds() + .with_outset((max_stroke, max_stroke)) + } else { + let mut bounds_rect = self.bounds().to_rect(); + let mut stroke_rect = bounds_rect; + stroke_rect.left -= max_stroke; + stroke_rect.right += max_stroke; + stroke_rect.top -= max_stroke; + stroke_rect.bottom += max_stroke; + + bounds_rect.join(stroke_rect); + bounds_rect + }; + for shadow in self.shadows.iter() { let (x, y) = shadow.offset; let mut shadow_rect = rect; @@ -601,6 +645,7 @@ impl Shape { rect.join(shadow_rect); } + if self.blur.blur_type != blurs::BlurType::None { rect.left -= self.blur.value; rect.top -= self.blur.value; @@ -666,10 +711,12 @@ impl Shape { } pub fn add_shadow(&mut self, shadow: Shadow) { + self.invalidate_extrect(); self.shadows.push(shadow); } pub fn clear_shadows(&mut self) { + self.invalidate_extrect(); self.shadows.clear(); } @@ -751,6 +798,7 @@ impl Shape { } pub fn apply_transform(&mut self, transform: &Matrix) { + self.invalidate_extrect(); self.transform_selrect(transform); if let shape_type @ (Type::Path(_) | Type::Bool(_)) = &mut self.shape_type { if let Some(path) = shape_type.path_mut() {