gl renderer: Improve clipping code

don't render a clip to a texture if the new clip does not intersect with
any of the corners of the currently rounded clip.

Fixes #2770
This commit is contained in:
Timm Bäder 2020-05-24 12:06:07 +02:00
parent 094788f1a3
commit d5bf3c2cd1
9 changed files with 208 additions and 35 deletions

View File

@ -4,6 +4,36 @@
#define SANITY_CHECKS 0
#define rounded_rect_top_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
r->bounds.origin.y, \
r->corner[0].width, r->corner[0].height))
#define rounded_rect_top_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[1].width, \
r->bounds.origin.y, \
r->corner[1].width, r->corner[1].height))
#define rounded_rect_bottom_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[2].width, \
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
r->corner[2].width, r->corner[2].height))
#define rounded_rect_bottom_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
r->corner[3].width, r->corner[3].height))
#define rounded_rect_corner0(r) rounded_rect_top_left(r)
#define rounded_rect_corner1(r) rounded_rect_top_right(r)
#define rounded_rect_corner2(r) rounded_rect_bottom_right(r)
#define rounded_rect_corner3(r) rounded_rect_bottom_left(r)
#define rounded_rect_corner(r, i) (rounded_rect_corner ##i(r))
#define graphene_size_non_zero(s) (s->width > 0 && s->height > 0)
#define rounded_rect_has_corner(r, i) (r->corner[i].width > 0 && r->corner[i].height > 0)
#define rect_contains_point(r, _x, _y) (_x >= (r)->origin.x && _x <= (r)->origin.x + (r)->size.width && \
_y >= (r)->origin.y && _y <= (r)->origin.y + (r)->size.height)
enum {
NINE_SLICE_TOP_LEFT = 0,
NINE_SLICE_TOP_CENTER = 1,

View File

@ -1159,29 +1159,72 @@ render_clipped_child (GskGLRenderer *self,
GskRenderNode *child)
{
graphene_rect_t transformed_clip;
graphene_rect_t intersection;
GskRoundedRect child_clip;
ops_transform_bounds_modelview (builder, clip, &transformed_clip);
if (builder->clip_is_rectilinear)
goto trivial;
{
/* Simple case: */
const GskRoundedRect *cur_clip = builder->current_clip;
int n_corners = 0;
bool corners[4];
/* Intersects with top left corner? */
n_corners += corners[0] = rounded_rect_has_corner (cur_clip, 0) &&
graphene_rect_intersection (&transformed_clip,
&builder->current_clip->bounds,
&intersection);
&rounded_rect_corner (cur_clip, 0), NULL);
/* top right? */
n_corners += corners[1] = rounded_rect_has_corner (cur_clip, 1) &&
graphene_rect_intersection (&transformed_clip,
&rounded_rect_corner (cur_clip, 1), NULL);
/* bottom right? */
n_corners += corners[2] = rounded_rect_has_corner (cur_clip, 2) &&
graphene_rect_intersection (&transformed_clip,
&rounded_rect_corner (cur_clip, 2), NULL);
/* bottom left */
n_corners += corners[3] = rounded_rect_has_corner (cur_clip, 3) &&
graphene_rect_intersection (&transformed_clip,
&rounded_rect_corner (cur_clip, 3), NULL);
gsk_rounded_rect_init_from_rect (&child_clip, &intersection, 0.0f);
if (n_corners == 0)
goto trivial;
ops_push_clip (builder, &child_clip);
if (corners[0] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 0)))
goto rtt;
if (corners[1] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 1)))
goto rtt;
if (corners[2] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 2)))
goto rtt;
if (corners[3] && !graphene_rect_contains_rect (&transformed_clip, &rounded_rect_corner (cur_clip, 3)))
goto rtt;
/* We do intersect with at least one of the corners, but in such a way that the
* intersection between the two clips can still be represented by a single rounded
* rect in a trivial way. do that. */
{
GskRoundedRect real_intersection;
graphene_rect_intersection (&transformed_clip, &cur_clip->bounds, &real_intersection.bounds);
for (int i = 0; i < 4; i++)
{
if (corners[i])
real_intersection.corner[i] = cur_clip->corner[i];
else
real_intersection.corner[i].width = real_intersection.corner[i].height = 0;
}
/* Draw with that new clip */
ops_push_clip (builder, &real_intersection);
gsk_gl_renderer_add_render_ops (self, child, builder);
ops_pop_clip (builder);
}
return;
}
/* Intersection might end up having rounded corners again */
if (!rounded_inner_rect_contains_rect (builder->current_clip,
&transformed_clip))
rtt:
{
/* well fuck */
const float scale = ops_get_scale (builder);
@ -1204,7 +1247,6 @@ render_clipped_child (GskGLRenderer *self,
g_assert_not_reached ();
ops_pop_clip (builder);
ops_set_program (builder, &self->programs->blit_program);
ops_set_texture (builder, region.texture_id);
@ -1212,6 +1254,17 @@ render_clipped_child (GskGLRenderer *self,
return;
}
trivial:
memset (&child_clip, 0, sizeof (GskRoundedRect));
graphene_rect_intersection (&transformed_clip,
&builder->current_clip->bounds,
&child_clip.bounds);
ops_push_clip (builder, &child_clip);
gsk_gl_renderer_add_render_ops (self, child, builder);
ops_pop_clip (builder);
return;
}
static inline void

View File

@ -0,0 +1,11 @@
rounded-clip {
clip: 0 0 200 100 / 8 8 20 12;
child: clip {
clip: 40 30 20 50;
child: color {
bounds: 0 0 200 500;
color: teal;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

View File

@ -0,0 +1,36 @@
rounded-clip {
clip: 0 0 200 100 / 0 0 40 0;
child: clip {
clip: 40 0 200 300;
child: color {
bounds: 0 0 300 500;
color: teal;
}
}
}
color {
bounds: 160 95 20 5;
color: black;
}
color {
bounds: 178 90 9 5;
color: black;
}
color {
bounds: 183 85 9 5;
color: black;
}
color {
bounds: 188 76 9 9;
color: black;
}
color {
bounds: 195 50 5 30;
color: black;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

View File

@ -0,0 +1,40 @@
/* Force offscreen drawing by creating a clip that
cannot be expressed using a single rounded rect */
rounded-clip {
clip: 0 0 100 100 / 0 50 50 50;
child: clip {
clip: 0 0 80 50;
child: color {
bounds: 0 0 300 500;
color: teal;
}
}
}
color {
bounds: 75 6 5 5;
color: black;
}
color {
bounds: 70 4 5 5;
color: black;
}
color {
bounds: 65 2 5 5;
color: black;
}
color {
bounds: 55 0 10 5;
color: black;
}
color {
bounds: 40 0 15 2;
color: black;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

View File

@ -69,6 +69,9 @@ compare_render_tests = [
'transform-in-transform',
'transform-in-transform-in-transform',
'nested-rounded-clips',
'clip-in-rounded-clip1',
'clip-in-rounded-clip2',
'clip-in-rounded-clip3',
]
# these are too sensitive to differences in the renderers