diff --git a/frontend/resources/wasm-playground/clips.html b/frontend/resources/wasm-playground/clips.html new file mode 100644 index 0000000000..0248e71b40 --- /dev/null +++ b/frontend/resources/wasm-playground/clips.html @@ -0,0 +1,90 @@ + + + + + WASM + WebGL2 Canvas + + + + + + + diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index fa544e60b8..e9b6226cd0 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -12,7 +12,7 @@ mod surfaces; mod text; mod ui; -use skia_safe::{self as skia, Matrix, RRect, Rect}; +use skia_safe::{self as skia, Matrix, Rect}; use std::borrow::Cow; use std::collections::{HashMap, HashSet}; @@ -21,7 +21,7 @@ use options::RenderOptions; use surfaces::{SurfaceId, Surfaces}; use crate::performance; -use crate::shapes::{Corners, Fill, Shape, StructureEntry, Type}; +use crate::shapes::{Corners, Fill, Shape, SolidColor, StructureEntry, Type}; use crate::tiles::{self, PendingTiles, TileRect}; use crate::uuid::Uuid; use crate::view::Viewbox; @@ -317,8 +317,11 @@ impl RenderState { &tile_rect, ); - self.surfaces - .draw_cached_tile_surface(self.current_tile.unwrap(), rect); + self.surfaces.draw_cached_tile_surface( + self.current_tile.unwrap(), + rect, + self.background_color, + ); if self.options.is_debug_visible() { debug::render_workspace_current_tile( @@ -394,7 +397,6 @@ impl RenderState { shape: &Shape, modifiers: Option<&Matrix>, scale_content: Option<&f32>, - clip_bounds: Option<(Rect, Option, Matrix)>, ) { let shape = if let Some(scale_content) = scale_content { &shape.scale_content(*scale_content) @@ -412,43 +414,6 @@ impl RenderState { let antialias = shape.should_use_antialias(self.get_scale()); - // set clipping - if let Some((bounds, corners, transform)) = clip_bounds { - self.surfaces.apply_mut(surface_ids, |s| { - s.canvas().concat(&transform); - }); - - if let Some(corners) = corners { - let rrect = RRect::new_rect_radii(bounds, &corners); - self.surfaces.apply_mut(surface_ids, |s| { - s.canvas() - .clip_rrect(rrect, skia::ClipOp::Intersect, antialias); - }); - } else { - self.surfaces.apply_mut(surface_ids, |s| { - s.canvas() - .clip_rect(bounds, skia::ClipOp::Intersect, antialias); - }); - } - - // This renders a red line around clipped - // shapes (frames). - if self.options.is_debug_visible() { - let mut paint = skia::Paint::default(); - paint.set_style(skia::PaintStyle::Stroke); - paint.set_color(skia::Color::from_argb(255, 255, 0, 0)); - paint.set_stroke_width(4.); - self.surfaces - .canvas(SurfaceId::Fills) - .draw_rect(bounds, &paint); - } - - self.surfaces.apply_mut(surface_ids, |s| { - s.canvas() - .concat(&transform.invert().unwrap_or(Matrix::default())); - }); - } - // We don't want to change the value in the global state let mut shape: Cow = Cow::Borrowed(shape); @@ -566,7 +531,6 @@ impl RenderState { shadows::render_fill_drop_shadows(self, &shape, antialias); } }; - self.apply_drawing_to_render_canvas(Some(&shape)); let surface_ids = SurfaceId::Strokes as u32 | SurfaceId::Fills as u32 @@ -765,7 +729,13 @@ impl RenderState { } #[inline] - pub fn render_shape_exit(&mut self, element: &Shape, visited_mask: bool) { + pub fn render_shape_exit( + &mut self, + element: &Shape, + visited_mask: bool, + modifiers: Option<&Matrix>, + scale_content: Option<&f32>, + ) { if visited_mask { // Because masked groups needs two rendering passes (first drawing // the content and then drawing the mask), we need to do an @@ -806,8 +776,39 @@ impl RenderState { if let Type::Group(_) = element.shape_type { self.nested_fills.pop(); } - self.surfaces.canvas(SurfaceId::Current).restore(); + // Detect clipping and apply it properly + if let Type::Frame(_) = &element.shape_type { + if element.clip() { + let mut layer_paint = skia::Paint::default(); + layer_paint.set_blend_mode(skia::BlendMode::DstIn); + let layer_rec = skia::canvas::SaveLayerRec::default().paint(&layer_paint); + self.surfaces + .canvas(SurfaceId::Current) + .save_layer(&layer_rec); + + // We clip only the fills content + let mut element_fills: Cow = Cow::Borrowed(element); + element_fills.to_mut().clear_strokes(); + element_fills.to_mut().clear_shadows(); + // We use the white color as the mask selection one + element_fills + .to_mut() + .set_fills([Fill::Solid(SolidColor(skia::Color::WHITE))].to_vec()); + self.render_shape(&element_fills, modifiers, scale_content); + + self.surfaces.canvas(SurfaceId::Current).restore(); + + // Now we paint the strokes - in clipped content strokes are drawn over the contained elements + let mut element_strokes: Cow = Cow::Borrowed(element); + element_strokes.to_mut().clear_fills(); + element_strokes.to_mut().clear_shadows(); + self.render_shape(&element_strokes, modifiers, scale_content); + + // TODO: drop shadows. With thos approach actually drop shadows for frames with clipped content are lost. + } + } + self.surfaces.canvas(SurfaceId::Current).restore(); self.focus_mode.exit(&element.id); } @@ -861,7 +862,7 @@ impl RenderState { let NodeRenderState { id: node_id, visited_children, - clip_bounds, + clip_bounds: _, visited_mask, mask, } = node_render_state; @@ -879,7 +880,12 @@ impl RenderState { } if visited_children { - self.render_shape_exit(element, visited_mask); + self.render_shape_exit( + element, + visited_mask, + modifiers.get(&node_id), + scale_content.get(&element.id), + ); continue; } @@ -909,7 +915,6 @@ impl RenderState { element, modifiers.get(&element.id), scale_content.get(&element.id), - clip_bounds, ); } else if visited_children { self.apply_drawing_to_render_canvas(Some(element)); @@ -974,8 +979,11 @@ impl RenderState { if self.surfaces.has_cached_tile_surface(current_tile) { performance::begin_measure!("render_shape_tree::cached"); let tile_rect = self.get_current_tile_bounds(); - self.surfaces - .draw_cached_tile_surface(current_tile, tile_rect); + self.surfaces.draw_cached_tile_surface( + current_tile, + tile_rect, + self.background_color, + ); performance::end_measure!("render_shape_tree::cached"); if self.options.is_debug_visible() { @@ -1012,6 +1020,7 @@ impl RenderState { } } + // println!("clear current {:?}", self.current_tile); self.surfaces .canvas(SurfaceId::Current) .clear(self.background_color); diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index a3188f4860..792e5d8727 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -307,8 +307,14 @@ impl Surfaces { self.tiles.remove(tile) } - pub fn draw_cached_tile_surface(&mut self, tile: Tile, rect: skia::Rect) { + pub fn draw_cached_tile_surface(&mut self, tile: Tile, rect: skia::Rect, color: skia::Color) { let image = self.tiles.get(tile).unwrap(); + + let mut paint = skia::Paint::default(); + paint.set_color(color); + + self.target.canvas().draw_rect(rect, &paint); + self.target .canvas() .draw_image_rect(&image, None, rect, &skia::Paint::default()); diff --git a/render-wasm/src/tiles.rs b/render-wasm/src/tiles.rs index 6a53632c5f..ec530ab23d 100644 --- a/render-wasm/src/tiles.rs +++ b/render-wasm/src/tiles.rs @@ -4,7 +4,7 @@ use indexmap::IndexSet; use skia_safe as skia; use std::collections::{HashMap, HashSet}; -#[derive(PartialEq, Eq, Hash, Clone, Copy)] +#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] pub struct Tile(pub i32, pub i32); #[derive(PartialEq, Eq, Hash, Clone, Copy)]