cairo: Handle clipped blur content

This is the result of experimenting with corner cases when blurring.

The result is a test that tests when the child of a blur node is
clipped out but the blurred child is not, the blurred parts are still
visible.

This immediately broke the cairo renderer, so the fix is included.
This commit is contained in:
Benjamin Otte 2023-12-05 17:07:54 +01:00
parent 8fd02f6fa2
commit e3299e38df
4 changed files with 64 additions and 16 deletions

View File

@ -5901,35 +5901,42 @@ gsk_blur_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskBlurNode *self = (GskBlurNode *) node;
cairo_pattern_t *pattern;
cairo_surface_t *surface;
cairo_surface_t *image_surface;
cairo_t *cr2;
graphene_rect_t blur_bounds;
double clip_radius;
cairo_save (cr);
clip_radius = gsk_cairo_blur_compute_pixels (0.5 * self->radius);
/* clip so the push_group() creates a smaller surface */
gsk_cairo_rectangle (cr, &node->bounds);
cairo_clip (cr);
/* We need to extend the clip by the blur radius
* so we can blur pixels in that region */
_graphene_rect_init_from_clip_extents (&blur_bounds, cr);
graphene_rect_inset (&blur_bounds, - clip_radius, - clip_radius);
if (!gsk_rect_intersection (&blur_bounds, &node->bounds, &blur_bounds))
return;
cairo_push_group (cr);
surface = cairo_surface_create_similar_image (cairo_get_target (cr),
CAIRO_FORMAT_ARGB32,
ceil (blur_bounds.size.width),
ceil (blur_bounds.size.height));
cairo_surface_set_device_offset (surface,
- blur_bounds.origin.x,
- blur_bounds.origin.y);
gsk_render_node_draw (self->child, cr);
cr2 = cairo_create (surface);
gsk_render_node_draw (self->child, cr2);
cairo_destroy (cr2);
pattern = cairo_pop_group (cr);
cairo_pattern_get_surface (pattern, &surface);
image_surface = cairo_surface_map_to_image (surface, NULL);
blur_image_surface (image_surface, (int)self->radius, 3);
blur_image_surface (surface, (int) ceil (0.5 * self->radius), 3);
cairo_surface_mark_dirty (surface);
cairo_surface_unmap_image (surface, image_surface);
cairo_set_source (cr, pattern);
cairo_set_source_surface (cr, surface, 0, 0);
cairo_rectangle (cr,
node->bounds.origin.x, node->bounds.origin.y,
node->bounds.size.width, node->bounds.size.height);
cairo_fill (cr);
cairo_restore (cr);
cairo_pattern_destroy (pattern);
cairo_surface_destroy (surface);
}
static void

View File

@ -0,0 +1,40 @@
color-matrix {
matrix: matrix3d(256, 0, 0, 0, 0, 256, 0, 0, 0, 0, 256, 0, 0, 0, 0, 256);
child: clip {
clip: -37 59 50 50;
child: container {
blur {
blur: 17;
child: color {
bounds: -87 59 50 50;
color: rgb(255,0,0);
}
}
blur {
blur: 17;
child: color {
bounds: 13 59 50 50;
color: rgb(255,0,0);
}
}
blur {
blur: 17;
child: color {
bounds: -37 9 50 50;
color: rgb(255,0,0);
}
}
blur {
blur: 17;
child: color {
bounds: -37 109 50 50;
color: rgb(255,0,0);
}
}
color {
bounds: -22 74 20 20;
color: rgb(0,0,0);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

View File

@ -20,6 +20,7 @@ compare_render_tests = [
'blend-in-rotate',
'blend-normal',
'blurred-lines',
'blur-contents-outside-of-clip',
'border-bottom-right',
'border-one-rounded',
'borders-rotated',