Merge pull request #6077 from penpot/elenatorro-10516-fix-shadow-rendering

🐛 Fix drop shadows viewport clipping
This commit is contained in:
Alejandro 2025-03-19 08:48:03 +01:00 committed by GitHub
commit b727f2fe1f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 174 additions and 63 deletions

View file

@ -1,14 +1,14 @@
use skia_safe::{self as skia, RRect};
use skia_safe::{self as skia, Paint, RRect};
use super::{RenderState, SurfaceId};
use crate::math::Rect as MathRect;
use crate::shapes::{Fill, Frame, ImageFill, Rect, Shape, Type};
fn draw_image_fill_in_container(
fn draw_image_fill(
render_state: &mut RenderState,
shape: &Shape,
fill: &Fill,
image_fill: &ImageFill,
paint: &Paint,
) {
let image = render_state.images.get(&image_fill.id());
if image.is_none() {
@ -19,7 +19,6 @@ fn draw_image_fill_in_container(
let canvas = render_state.surfaces.canvas(SurfaceId::Fills);
let container = &shape.selrect;
let path_transform = shape.to_path_transform();
let paint = fill.to_paint(container);
let width = size.0 as f32;
let height = size.1 as f32;
@ -110,39 +109,29 @@ fn draw_image_fill_in_container(
* This SHOULD be the only public function in this module.
*/
pub fn render(render_state: &mut RenderState, shape: &Shape, fill: &Fill) {
let canvas = render_state.surfaces.canvas(SurfaceId::Fills);
let selrect = shape.selrect;
let path_transform = shape.to_path_transform();
let paint = &fill.to_paint(&shape.selrect);
match (fill, &shape.shape_type) {
(Fill::Image(image_fill), _) => {
draw_image_fill_in_container(render_state, shape, fill, image_fill);
draw_image_fill(render_state, shape, image_fill, paint);
}
(_, Type::Rect(_) | Type::Frame(_)) => {
if let Some(corners) = shape.shape_type.corners() {
let rrect = RRect::new_rect_radii(selrect, &corners);
canvas.draw_rrect(rrect, &fill.to_paint(&selrect));
} else {
canvas.draw_rect(selrect, &fill.to_paint(&selrect));
}
render_state
.surfaces
.draw_rect_to(SurfaceId::Fills, shape, paint);
}
(_, Type::Circle) => {
canvas.draw_oval(selrect, &fill.to_paint(&selrect));
render_state
.surfaces
.draw_circle_to(SurfaceId::Fills, shape, paint);
}
(_, Type::Path(_)) | (_, Type::Bool(_)) => {
if let Some(path) = &shape.shape_type.path() {
let svg_attrs = &shape.svg_attrs;
let mut skia_path = &mut path.to_skia_path();
if let Some(path_transform) = path_transform {
skia_path = skia_path.transform(&path_transform);
if let Some("evenodd") = svg_attrs.get("fill-rule").map(String::as_str) {
skia_path.set_fill_type(skia::PathFillType::EvenOdd);
}
canvas.draw_path(&skia_path, &fill.to_paint(&selrect));
}
}
render_state
.surfaces
.draw_path_to(SurfaceId::Fills, shape, paint);
}
(_, _) => {
unreachable!("This shape should not have fills")
}
(_, _) => unreachable!("This shape should not have fills"),
}
}

View file

@ -1,17 +1,50 @@
use skia_safe::{self as skia};
use super::{RenderState, SurfaceId};
use crate::shapes::Shadow;
use crate::shapes::{Shadow, Shape, Type};
pub fn render_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) {
let shadow_paint = shadow.to_paint(scale);
render_state
.surfaces
.draw_into(SurfaceId::Fills, SurfaceId::Shadow, Some(&shadow_paint));
pub fn render_drop_shadows(render_state: &mut RenderState, shape: &Shape) {
if shape.fills().len() > 0 {
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();
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
render_stroke_drop_shadow(render_state, &shadow, scale);
}
}
}
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);
}
_ => {}
}
}
fn render_stroke_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) {
let shadow_paint = &shadow.to_paint(scale);
render_state
.surfaces
.draw_into(SurfaceId::Strokes, SurfaceId::Shadow, Some(&shadow_paint));
.draw_into(SurfaceId::Strokes, SurfaceId::Shadow, Some(shadow_paint));
render_state.surfaces.draw_into(
SurfaceId::Shadow,

View file

@ -1,5 +1,6 @@
use super::gpu_state::GpuState;
use skia_safe as skia;
use crate::shapes::Shape;
use skia_safe::{self as skia, Paint, RRect};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SurfaceId {
@ -8,6 +9,7 @@ pub enum SurfaceId {
Fills,
Strokes,
Shadow,
DropShadows,
Overlay,
Debug,
}
@ -23,6 +25,8 @@ pub struct Surfaces {
shape_strokes: skia::Surface,
// used for rendering shadows
shadow: skia::Surface,
// used for new shadow rendering
drop_shadows: skia::Surface,
// for drawing the things that are over shadows.
overlay: skia::Surface,
// for drawing debug info.
@ -40,6 +44,7 @@ impl Surfaces {
let mut target = gpu_state.create_target_surface(width, height);
let current = target.new_surface_with_dimensions((width, height)).unwrap();
let shadow = target.new_surface_with_dimensions((width, height)).unwrap();
let drop_shadows = target.new_surface_with_dimensions((width, height)).unwrap();
let overlay = target.new_surface_with_dimensions((width, height)).unwrap();
let shape_fills = target.new_surface_with_dimensions((width, height)).unwrap();
let shape_strokes = target.new_surface_with_dimensions((width, height)).unwrap();
@ -49,6 +54,7 @@ impl Surfaces {
target,
current,
shadow,
drop_shadows,
overlay,
shape_fills,
shape_strokes,
@ -94,6 +100,7 @@ impl Surfaces {
SurfaceId::Target => &mut self.target,
SurfaceId::Current => &mut self.current,
SurfaceId::Shadow => &mut self.shadow,
SurfaceId::DropShadows => &mut self.drop_shadows,
SurfaceId::Overlay => &mut self.overlay,
SurfaceId::Fills => &mut self.shape_fills,
SurfaceId::Strokes => &mut self.shape_strokes,
@ -107,7 +114,27 @@ impl Surfaces {
self.current = self.target.new_surface_with_dimensions(dim).unwrap();
self.overlay = self.target.new_surface_with_dimensions(dim).unwrap();
self.shadow = self.target.new_surface_with_dimensions(dim).unwrap();
self.drop_shadows = self.target.new_surface_with_dimensions(dim).unwrap();
self.shape_fills = self.target.new_surface_with_dimensions(dim).unwrap();
self.debug = self.target.new_surface_with_dimensions(dim).unwrap();
}
pub fn draw_rect_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
if let Some(corners) = shape.shape_type.corners() {
let rrect = RRect::new_rect_radii(shape.selrect, &corners);
self.canvas(id).draw_rrect(rrect, paint);
} else {
self.canvas(id).draw_rect(shape.selrect, paint);
}
}
pub fn draw_circle_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
self.canvas(id).draw_oval(shape.selrect, paint);
}
pub fn draw_path_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
if let Some(path) = shape.get_skia_path() {
self.canvas(id).draw_path(&path, paint);
}
}
}