diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index b2a82844f0..bd0d074e41 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -216,6 +216,9 @@ impl RenderState { self.surfaces .flush_and_submit(&mut self.gpu_state, SurfaceId::DropShadows); + self.surfaces + .flush_and_submit(&mut self.gpu_state, SurfaceId::InnerShadows); + self.surfaces.draw_into( SurfaceId::DropShadows, SurfaceId::Current, @@ -228,6 +231,12 @@ impl RenderState { Some(&skia::Paint::default()), ); + self.surfaces.draw_into( + SurfaceId::InnerShadows, + SurfaceId::Current, + Some(&skia::Paint::default()), + ); + let mut render_overlay_below_strokes = false; if let Some(shape) = shape { render_overlay_below_strokes = shape.fills().len() > 0; @@ -269,6 +278,7 @@ impl RenderState { self.surfaces.apply_mut( &[ SurfaceId::Shadow, + SurfaceId::InnerShadows, SurfaceId::DropShadows, SurfaceId::Overlay, SurfaceId::Fills, @@ -286,7 +296,12 @@ impl RenderState { modifiers: Option<&Matrix>, clip_bounds: Option<(Rect, Option, Matrix)>, ) { - let surface_ids = &[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows]; + let surface_ids = &[ + SurfaceId::Fills, + SurfaceId::Strokes, + SurfaceId::DropShadows, + SurfaceId::InnerShadows, + ]; self.surfaces.apply_mut(surface_ids, |s| { s.canvas().save(); }); @@ -365,7 +380,12 @@ impl RenderState { } _ => { self.surfaces.apply_mut( - &[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows], + &[ + SurfaceId::Fills, + SurfaceId::Strokes, + SurfaceId::DropShadows, + SurfaceId::InnerShadows, + ], |s| { s.canvas().concat(&matrix); }, @@ -379,22 +399,19 @@ impl RenderState { strokes::render(self, &shape, stroke); } - for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) { - shadows::render_inner_shadow( - self, - shadow, - self.viewbox.zoom * self.options.dpr(), - shape.fills().len() > 0, - ); - } - + shadows::render_inner_shadows(self, &shape); shadows::render_drop_shadows(self, &shape); } }; self.apply_drawing_to_render_canvas(Some(&shape)); self.surfaces.apply_mut( - &[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows], + &[ + SurfaceId::Fills, + SurfaceId::Strokes, + SurfaceId::DropShadows, + SurfaceId::InnerShadows, + ], |s| { s.canvas().restore(); }, @@ -419,14 +436,17 @@ impl RenderState { self.cancel_animation_frame(frame_id); } } + let scale = self.get_scale(); self.reset_canvas(); self.surfaces.apply_mut( - &[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows], + &[ + SurfaceId::Fills, + SurfaceId::Strokes, + SurfaceId::DropShadows, + SurfaceId::InnerShadows, + ], |s| { - s.canvas().scale(( - self.viewbox.zoom * self.options.dpr(), - self.viewbox.zoom * self.options.dpr(), - )); + s.canvas().scale((scale, scale)); }, ); @@ -517,7 +537,7 @@ impl RenderState { .save_layer(&mask_rec); } - if let Some(image_filter) = element.image_filter(self.viewbox.zoom * self.options.dpr()) { + if let Some(image_filter) = element.image_filter(self.get_scale()) { paint.set_image_filter(image_filter); } @@ -546,9 +566,9 @@ impl RenderState { pub fn get_current_tile_bounds(&mut self) -> Rect { let (tile_x, tile_y) = self.current_tile.unwrap(); - let zoom = self.viewbox.zoom * self.options.dpr(); - let offset_x = self.viewbox.area.left * zoom; - let offset_y = self.viewbox.area.top * zoom; + let scale = self.get_scale(); + let offset_x = self.viewbox.area.left * scale; + let offset_y = self.viewbox.area.top * scale; Rect::from_xywh( (tile_x as f32 * tiles::TILE_SIZE) - offset_x, (tile_y as f32 * tiles::TILE_SIZE) - offset_y, @@ -782,4 +802,8 @@ impl RenderState { } } } + + pub fn get_scale(&self) -> f32 { + self.viewbox.zoom() * self.options.dpr() + } } diff --git a/render-wasm/src/render/shadows.rs b/render-wasm/src/render/shadows.rs index 17fca69d2d..d64a9779a7 100644 --- a/render-wasm/src/render/shadows.rs +++ b/render-wasm/src/render/shadows.rs @@ -1,15 +1,15 @@ -use skia_safe::{self as skia}; - use super::{RenderState, SurfaceId}; use crate::shapes::{Shadow, Shape, Type}; +use skia_safe::{self as skia, Paint}; +// Drop Shadows pub fn render_drop_shadows(render_state: &mut RenderState, shape: &Shape) { - if shape.fills().len() > 0 { + if shape.has_fills() { for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) { render_fill_drop_shadow(render_state, &shape, &shadow); } } else { - let scale = render_state.viewbox.zoom * render_state.options.dpr(); + let scale = render_state.get_scale(); for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) { render_stroke_drop_shadow(render_state, &shadow, scale); } @@ -18,27 +18,10 @@ pub fn render_drop_shadows(render_state: &mut RenderState, shape: &Shape) { fn render_fill_drop_shadow(render_state: &mut RenderState, shape: &Shape, shadow: &Shadow) { let paint = &shadow.get_drop_shadow_paint(); - - match &shape.shape_type { - Type::Rect(_) | Type::Frame(_) => { - render_state - .surfaces - .draw_rect_to(SurfaceId::DropShadows, shape, paint); - } - Type::Circle => { - render_state - .surfaces - .draw_circle_to(SurfaceId::DropShadows, shape, paint); - } - Type::Path(_) | Type::Bool(_) => { - render_state - .surfaces - .draw_path_to(SurfaceId::DropShadows, shape, paint); - } - _ => {} - } + render_shadow_paint(render_state, shape, paint, SurfaceId::DropShadows); } +// TODO: Stroke shadows fn render_stroke_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) { let shadow_paint = &shadow.to_paint(scale); @@ -58,30 +41,63 @@ fn render_stroke_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, sc .clear(skia::Color::TRANSPARENT); } -pub fn render_inner_shadow( - render_state: &mut RenderState, - shadow: &Shadow, - scale: f32, - render_over_fills: bool, -) { - let shadow_paint = shadow.to_paint(scale); - - if render_over_fills { - render_state - .surfaces - .draw_into(SurfaceId::Fills, SurfaceId::Shadow, Some(&shadow_paint)); +// Inner Shadows +pub fn render_inner_shadows(render_state: &mut RenderState, shape: &Shape) { + if shape.has_fills() { + for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) { + render_fill_inner_shadow(render_state, &shape, &shadow); + } } else { - render_state - .surfaces - .draw_into(SurfaceId::Strokes, SurfaceId::Shadow, Some(&shadow_paint)); + let scale = render_state.get_scale(); + for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) { + render_stroke_inner_shadow(render_state, &shadow, scale); + } } +} + +fn render_fill_inner_shadow(render_state: &mut RenderState, shape: &Shape, shadow: &Shadow) { + let paint = &shadow.get_inner_shadow_paint(); + render_shadow_paint(render_state, shape, paint, SurfaceId::InnerShadows); +} + +// TODO: Stroke shadows +fn render_stroke_inner_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) { + let shadow_paint = &shadow.to_paint(scale); render_state .surfaces - .draw_into(SurfaceId::Shadow, SurfaceId::Overlay, None); + .draw_into(SurfaceId::Strokes, SurfaceId::Shadow, Some(shadow_paint)); + + render_state.surfaces.draw_into( + SurfaceId::Shadow, + SurfaceId::Overlay, + Some(&skia::Paint::default()), + ); render_state .surfaces .canvas(SurfaceId::Shadow) .clear(skia::Color::TRANSPARENT); } + +fn render_shadow_paint( + render_state: &mut RenderState, + shape: &Shape, + paint: &Paint, + surface_id: SurfaceId, +) { + match &shape.shape_type { + Type::Rect(_) | Type::Frame(_) => { + render_state.surfaces.draw_rect_to(surface_id, shape, paint); + } + Type::Circle => { + render_state + .surfaces + .draw_circle_to(surface_id, shape, paint); + } + Type::Path(_) | Type::Bool(_) => { + render_state.surfaces.draw_path_to(surface_id, shape, paint); + } + _ => {} + } +} diff --git a/render-wasm/src/render/strokes.rs b/render-wasm/src/render/strokes.rs index b7c9ca064d..5084e25063 100644 --- a/render-wasm/src/render/strokes.rs +++ b/render-wasm/src/render/strokes.rs @@ -154,7 +154,7 @@ fn handle_stroke_caps( canvas: &skia::Canvas, is_open: bool, svg_attrs: &HashMap, - dpr_scale: f32, + scale: f32, ) { let points_count = path.count_points(); let mut points = vec![Point::default(); points_count]; @@ -165,7 +165,7 @@ fn handle_stroke_caps( let first_point = points.first().unwrap(); let last_point = points.last().unwrap(); - let mut paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, dpr_scale); + let mut paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale); handle_stroke_cap( canvas, @@ -332,11 +332,11 @@ fn draw_image_stroke_in_container( } let size = image_fill.size(); + let scale = render_state.get_scale(); let canvas = render_state.surfaces.canvas(SurfaceId::Fills); let container = &shape.selrect; let path_transform = shape.to_path_transform(); let svg_attrs = &shape.svg_attrs; - let dpr_scale = render_state.viewbox.zoom * render_state.options.dpr(); // Save canvas and layer state let mut pb = skia::Paint::default(); @@ -358,11 +358,11 @@ fn draw_image_stroke_in_container( &outer_rect, &shape_type.corners(), svg_attrs, - dpr_scale, + scale, ); } Type::Circle => { - draw_stroke_on_circle(canvas, stroke, container, &outer_rect, svg_attrs, dpr_scale) + draw_stroke_on_circle(canvas, stroke, container, &outer_rect, svg_attrs, scale) } shape_type @ (Type::Path(_) | Type::Bool(_)) => { @@ -381,12 +381,12 @@ fn draw_image_stroke_in_container( } } let is_open = p.is_open(); - let mut paint = stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, dpr_scale); + let mut paint = stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, scale); canvas.draw_path(&path, &paint); if stroke.render_kind(is_open) == StrokeKind::OuterStroke { // Small extra inner stroke to overlap with the fill // and avoid unnecesary artifacts. - paint.set_stroke_width(1. / dpr_scale); + paint.set_stroke_width(1. / scale); canvas.draw_path(&path, &paint); } handle_stroke_caps( @@ -396,7 +396,7 @@ fn draw_image_stroke_in_container( canvas, is_open, svg_attrs, - dpr_scale, + scale, ); canvas.restore(); } @@ -437,8 +437,8 @@ fn draw_image_stroke_in_container( * This SHOULD be the only public function in this module. */ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) { + let scale = render_state.get_scale(); let canvas = render_state.surfaces.canvas(SurfaceId::Strokes); - let dpr_scale = render_state.viewbox.zoom * render_state.options.dpr(); let selrect = shape.selrect; let path_transform = shape.to_path_transform(); let svg_attrs = &shape.svg_attrs; @@ -455,15 +455,14 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) { &selrect, &shape_type.corners(), svg_attrs, - dpr_scale, + scale, ); } Type::Circle => { - draw_stroke_on_circle(canvas, stroke, &selrect, &selrect, &svg_attrs, dpr_scale) + draw_stroke_on_circle(canvas, stroke, &selrect, &selrect, svg_attrs, scale) } shape_type @ (Type::Path(_) | Type::Bool(_)) => { if let Some(path) = shape_type.path() { - let svg_attrs = &shape.svg_attrs; draw_stroke_on_path( canvas, stroke, @@ -471,7 +470,7 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) { &selrect, path_transform.as_ref(), svg_attrs, - dpr_scale, + scale, ); } } diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index 353cbc3e81..c1653bd54d 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -15,6 +15,7 @@ pub enum SurfaceId { Strokes, Shadow, DropShadows, + InnerShadows, Overlay, Debug, } @@ -32,6 +33,7 @@ pub struct Surfaces { shadow: skia::Surface, // used for new shadow rendering drop_shadows: skia::Surface, + inner_shadows: skia::Surface, // for drawing the things that are over shadows. overlay: skia::Surface, // for drawing debug info. @@ -63,6 +65,7 @@ impl Surfaces { let current = target.new_surface_with_dimensions(extra_tile_dims).unwrap(); let shadow = target.new_surface_with_dimensions(extra_tile_dims).unwrap(); let drop_shadows = target.new_surface_with_dimensions(extra_tile_dims).unwrap(); + let inner_shadows = target.new_surface_with_dimensions(extra_tile_dims).unwrap(); let overlay = target.new_surface_with_dimensions(extra_tile_dims).unwrap(); let shape_fills = target.new_surface_with_dimensions(extra_tile_dims).unwrap(); let shape_strokes = target.new_surface_with_dimensions(extra_tile_dims).unwrap(); @@ -78,6 +81,7 @@ impl Surfaces { current, shadow, drop_shadows, + inner_shadows, overlay, shape_fills, shape_strokes, @@ -154,7 +158,12 @@ impl Surfaces { -render_area.top() + self.margins.height as f32 / viewbox.zoom, ); self.apply_mut( - &[SurfaceId::Fills, SurfaceId::Strokes, SurfaceId::DropShadows], + &[ + SurfaceId::Fills, + SurfaceId::Strokes, + SurfaceId::DropShadows, + SurfaceId::InnerShadows, + ], |s| { s.canvas().restore(); s.canvas().save(); @@ -169,6 +178,7 @@ impl Surfaces { SurfaceId::Current => &mut self.current, SurfaceId::Shadow => &mut self.shadow, SurfaceId::DropShadows => &mut self.drop_shadows, + SurfaceId::InnerShadows => &mut self.inner_shadows, SurfaceId::Overlay => &mut self.overlay, SurfaceId::Fills => &mut self.shape_fills, SurfaceId::Strokes => &mut self.shape_strokes, @@ -205,6 +215,7 @@ impl Surfaces { pub fn reset(&mut self, color: skia::Color) { self.canvas(SurfaceId::Fills).restore_to_count(1); self.canvas(SurfaceId::DropShadows).restore_to_count(1); + self.canvas(SurfaceId::InnerShadows).restore_to_count(1); self.canvas(SurfaceId::Strokes).restore_to_count(1); self.canvas(SurfaceId::Current).restore_to_count(1); self.apply_mut( @@ -213,6 +224,7 @@ impl Surfaces { SurfaceId::Strokes, SurfaceId::Current, SurfaceId::DropShadows, + SurfaceId::InnerShadows, SurfaceId::Shadow, SurfaceId::Overlay, ], diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index fc71c1a362..6cdfadd612 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -749,6 +749,10 @@ impl Shape { _ => false, } } + + pub fn has_fills(&self) -> bool { + !self.fills.is_empty() + } } #[cfg(test)] diff --git a/render-wasm/src/shapes/shadows.rs b/render-wasm/src/shapes/shadows.rs index 755d8e063c..56bc6409c6 100644 --- a/render-wasm/src/shapes/shadows.rs +++ b/render-wasm/src/shapes/shadows.rs @@ -76,6 +76,8 @@ impl Shadow { paint } + // To be removed: Old methods for Drop Shadows and Inner Shadows (now used opnly for stroke shadows) + fn drop_shadow_filters(&self, scale: f32) -> Option { let mut filter = image_filters::drop_shadow_only( (self.offset.0 * scale, self.offset.1 * scale), @@ -124,7 +126,8 @@ impl Shadow { filter } - // New methods for DropShadow + // New methods for Drop Shadows + pub fn get_drop_shadow_paint(&self) -> skia::Paint { let mut paint = skia::Paint::default(); @@ -152,4 +155,43 @@ impl Shadow { filter } + + // New methods for Inner Shadows + + pub fn get_inner_shadow_paint(&self) -> skia::Paint { + let mut paint = skia::Paint::default(); + + let image_filter = self.get_inner_shadow_filter(); + + paint.set_image_filter(image_filter); + paint.set_anti_alias(true); + + paint + } + + fn get_inner_shadow_filter(&self) -> Option { + let sigma = self.blur * 0.5; + let mut filter = skia::image_filters::drop_shadow_only( + (self.offset.0, self.offset.1), // DPR? + (sigma, sigma), + skia::Color::BLACK, + None, + None, + None, + ); + + filter = skia::image_filters::color_filter( + skia::color_filters::blend(self.color, skia::BlendMode::SrcOut).unwrap(), + filter, + None, + ); + + if self.spread > 0. { + filter = skia::image_filters::dilate((self.spread, self.spread), filter, None); + } + + filter = skia::image_filters::blend(skia::BlendMode::SrcIn, None, filter, None); + + filter + } } diff --git a/render-wasm/src/view.rs b/render-wasm/src/view.rs index 76585c078d..eeb7aa858b 100644 --- a/render-wasm/src/view.rs +++ b/render-wasm/src/view.rs @@ -51,4 +51,8 @@ impl Viewbox { self.area .set_wh(self.width / self.zoom, self.height / self.zoom); } + + pub fn zoom(&self) -> f32 { + self.zoom + } }