mirror of
https://github.com/penpot/penpot.git
synced 2026-06-09 08:52:05 +00:00
🐛 Fix masked group applied blur and bounds (#10028)
This commit is contained in:
parent
dfa88a28fd
commit
47ce68eed0
@ -2392,7 +2392,52 @@ impl RenderState {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn render_shape_enter(&mut self, element: &Shape, mask: bool, target_surface: SurfaceId) {
|
||||
fn clip_target_surface_to_stack(
|
||||
&mut self,
|
||||
clips: &ClipStack,
|
||||
target_surface: SurfaceId,
|
||||
scale: f32,
|
||||
antialias: bool,
|
||||
) {
|
||||
let translation = self
|
||||
.surfaces
|
||||
.get_render_context_translation(self.render_area, scale);
|
||||
|
||||
for (bounds, corners, transform) in clips.iter() {
|
||||
let mut total_matrix = Matrix::new_identity();
|
||||
if target_surface == SurfaceId::Export {
|
||||
let Some((export_rect, export_scale)) = self.export_context else {
|
||||
continue;
|
||||
};
|
||||
total_matrix.pre_scale((export_scale, export_scale), None);
|
||||
total_matrix.pre_translate((-export_rect.x(), -export_rect.y()));
|
||||
} else {
|
||||
total_matrix.pre_scale((scale, scale), None);
|
||||
total_matrix.pre_translate((translation.0, translation.1));
|
||||
}
|
||||
total_matrix.pre_concat(transform);
|
||||
|
||||
let canvas = self.surfaces.canvas(target_surface);
|
||||
canvas.concat(&total_matrix);
|
||||
if let Some(corners) = corners {
|
||||
let rrect = RRect::new_rect_radii(*bounds, corners);
|
||||
canvas.clip_rrect(rrect, skia::ClipOp::Intersect, antialias);
|
||||
} else {
|
||||
canvas.clip_rect(*bounds, skia::ClipOp::Intersect, antialias);
|
||||
}
|
||||
self.surfaces
|
||||
.canvas(target_surface)
|
||||
.concat(&total_matrix.invert().unwrap_or_default());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_shape_enter(
|
||||
&mut self,
|
||||
element: &Shape,
|
||||
mask: bool,
|
||||
clip_bounds: Option<&ClipStack>,
|
||||
target_surface: SurfaceId,
|
||||
) {
|
||||
// Masked groups needs two rendering passes, the first one rendering
|
||||
// the content and the second one rendering the mask so we need to do
|
||||
// an extra save_layer to keep all the masked group separate from
|
||||
@ -2404,7 +2449,33 @@ impl RenderState {
|
||||
self.nested_shadows.push(shadows.to_vec());
|
||||
|
||||
if group.masked {
|
||||
let paint = skia::Paint::default();
|
||||
// A masked group's blur is applied as a single layer blur over
|
||||
// the whole masked result.
|
||||
let mask_group_blur = element.masked_group_layer_blur().is_some();
|
||||
if mask_group_blur {
|
||||
self.surfaces.canvas(target_surface).save();
|
||||
if let Some(clips) = clip_bounds {
|
||||
let scale = self.get_scale();
|
||||
let antialias = !self.options.is_fast_mode()
|
||||
&& element
|
||||
.should_use_antialias(scale, self.options.antialias_threshold);
|
||||
self.clip_target_surface_to_stack(clips, target_surface, scale, antialias);
|
||||
}
|
||||
}
|
||||
|
||||
let mut paint = skia::Paint::default();
|
||||
if !self.options.is_fast_mode() {
|
||||
if let Some(blur) = element.masked_group_layer_blur() {
|
||||
let scale = self.get_scale();
|
||||
let sigma = radius_to_sigma(blur.value * scale);
|
||||
if let Some(filter) =
|
||||
skia::image_filters::blur((sigma, sigma), None, None, None)
|
||||
{
|
||||
paint.set_image_filter(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
|
||||
self.surfaces.canvas(target_surface).save_layer(&layer_rec);
|
||||
}
|
||||
@ -2558,6 +2629,10 @@ impl RenderState {
|
||||
self.surfaces.canvas(target_surface).restore();
|
||||
}
|
||||
|
||||
if visited_mask && element.masked_group_layer_blur().is_some() {
|
||||
self.surfaces.canvas(target_surface).restore();
|
||||
}
|
||||
|
||||
self.focus_mode.exit(&element.id);
|
||||
Ok(())
|
||||
}
|
||||
@ -2864,7 +2939,6 @@ impl RenderState {
|
||||
extrect: &mut Option<Rect>,
|
||||
clip_bounds: Option<ClipStack>,
|
||||
scale: f32,
|
||||
translation: (f32, f32),
|
||||
node_render_state: &NodeRenderState,
|
||||
target_surface: SurfaceId,
|
||||
) -> Result<()> {
|
||||
@ -2970,60 +3044,7 @@ impl RenderState {
|
||||
let antialias = !self.options.is_fast_mode()
|
||||
&& element.should_use_antialias(scale, self.options.antialias_threshold);
|
||||
self.surfaces.canvas(target_surface).save();
|
||||
for (bounds, corners, transform) in clips.iter() {
|
||||
if target_surface == SurfaceId::Export {
|
||||
let Some((export_rect, export_scale)) = self.export_context else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut total_matrix = Matrix::new_identity();
|
||||
|
||||
total_matrix.pre_scale((export_scale, export_scale), None);
|
||||
total_matrix.pre_translate((-export_rect.x(), -export_rect.y()));
|
||||
|
||||
total_matrix.pre_concat(transform);
|
||||
|
||||
let canvas = self.surfaces.canvas(target_surface);
|
||||
canvas.concat(&total_matrix);
|
||||
|
||||
let bounds = *bounds;
|
||||
if let Some(corners) = corners {
|
||||
let rrect = RRect::new_rect_radii(bounds, corners);
|
||||
canvas.clip_rrect(rrect, skia::ClipOp::Intersect, antialias);
|
||||
} else {
|
||||
canvas.clip_rect(bounds, skia::ClipOp::Intersect, antialias);
|
||||
}
|
||||
self.surfaces
|
||||
.canvas(target_surface)
|
||||
.concat(&total_matrix.invert().unwrap_or_default());
|
||||
} else {
|
||||
let mut total_matrix = Matrix::new_identity();
|
||||
total_matrix.pre_scale((scale, scale), None);
|
||||
total_matrix.pre_translate((translation.0, translation.1));
|
||||
total_matrix.pre_concat(transform);
|
||||
|
||||
self.surfaces.canvas(target_surface).concat(&total_matrix);
|
||||
|
||||
if let Some(corners) = corners {
|
||||
let rrect = RRect::new_rect_radii(*bounds, corners);
|
||||
self.surfaces.canvas(target_surface).clip_rrect(
|
||||
rrect,
|
||||
skia::ClipOp::Intersect,
|
||||
antialias,
|
||||
);
|
||||
} else {
|
||||
self.surfaces.canvas(target_surface).clip_rect(
|
||||
*bounds,
|
||||
skia::ClipOp::Intersect,
|
||||
antialias,
|
||||
);
|
||||
}
|
||||
|
||||
self.surfaces
|
||||
.canvas(target_surface)
|
||||
.concat(&total_matrix.invert().unwrap_or_default());
|
||||
}
|
||||
}
|
||||
self.clip_target_surface_to_stack(clips, target_surface, scale, antialias);
|
||||
self.surfaces
|
||||
.draw_into(SurfaceId::DropShadows, target_surface, None);
|
||||
self.surfaces.canvas(target_surface).restore();
|
||||
@ -3249,17 +3270,12 @@ impl RenderState {
|
||||
&& element.drop_shadows_visible().next().is_some();
|
||||
|
||||
if shadow_before_layer {
|
||||
let translation = self
|
||||
.surfaces
|
||||
.get_render_context_translation(self.render_area, scale);
|
||||
|
||||
self.render_element_drop_shadows_and_composite(
|
||||
element,
|
||||
tree,
|
||||
&mut extrect,
|
||||
clip_bounds.clone(),
|
||||
scale,
|
||||
translation,
|
||||
&node_render_state,
|
||||
target_surface,
|
||||
)?;
|
||||
@ -3271,14 +3287,10 @@ impl RenderState {
|
||||
self.render_background_blur(element, target_surface);
|
||||
}
|
||||
|
||||
self.render_shape_enter(element, mask, target_surface);
|
||||
self.render_shape_enter(element, mask, clip_bounds.as_ref(), target_surface);
|
||||
}
|
||||
|
||||
if !node_render_state.is_root() && self.focus_mode.is_active() {
|
||||
let translation = self
|
||||
.surfaces
|
||||
.get_render_context_translation(self.render_area, scale);
|
||||
|
||||
// Skip expensive drop shadow rendering in fast mode (during pan/zoom).
|
||||
let skip_shadows = self.options.is_fast_mode();
|
||||
|
||||
@ -3296,7 +3308,6 @@ impl RenderState {
|
||||
&mut extrect,
|
||||
clip_bounds.clone(),
|
||||
scale,
|
||||
translation,
|
||||
&node_render_state,
|
||||
target_surface,
|
||||
)?;
|
||||
@ -3350,6 +3361,9 @@ impl RenderState {
|
||||
Type::Frame(_) if Self::frame_clip_layer_blur(element).is_some() => {
|
||||
self.nested_blurs.push(None);
|
||||
}
|
||||
Type::Group(_) if element.masked_group_layer_blur().is_some() => {
|
||||
self.nested_blurs.push(None);
|
||||
}
|
||||
Type::Frame(_) | Type::Group(_) => {
|
||||
self.nested_blurs.push(element.blur);
|
||||
}
|
||||
|
||||
@ -1711,6 +1711,16 @@ impl Shape {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn masked_group_layer_blur(&self) -> Option<Blur> {
|
||||
use crate::shapes::BlurType;
|
||||
match self.shape_type {
|
||||
Type::Group(Group { masked: true }) => self.blur.filter(|blur| {
|
||||
!blur.hidden && blur.blur_type == BlurType::LayerBlur && blur.value > 0.0
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if this shape has visual effects that might extend its bounds beyond selrect
|
||||
/// Shapes with these effects require expensive extrect calculation for accurate visibility checks
|
||||
pub fn has_effects_that_extend_bounds(&self) -> bool {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user