From: Timm Bäder Date: Sun, 24 May 2020 10:06:07 +0000 (+0200) Subject: gl renderer: Improve clipping code X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~16^2~112 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=d5bf3c2cd13b711487ce1bfcd7ef57222583ee54;p=gtk4.git 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 --- diff --git a/gsk/gl/glutilsprivate.h b/gsk/gl/glutilsprivate.h index c2a67796b0..f035bda26e 100644 --- a/gsk/gl/glutilsprivate.h +++ b/gsk/gl/glutilsprivate.h @@ -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, diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 8212ca30fd..a451338d59 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -1159,59 +1159,112 @@ 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) - { - /* Simple case: */ - graphene_rect_intersection (&transformed_clip, - &builder->current_clip->bounds, - &intersection); + goto trivial; - gsk_rounded_rect_init_from_rect (&child_clip, &intersection, 0.0f); + { + 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, + &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); + + if (n_corners == 0) + goto trivial; + + 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; + } - ops_push_clip (builder, &child_clip); + /* 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; } + return; + } - /* Intersection might end up having rounded corners again */ - if (!rounded_inner_rect_contains_rect (builder->current_clip, - &transformed_clip)) - { - /* well fuck */ - const float scale = ops_get_scale (builder); - gboolean is_offscreen; - TextureRegion region; - GskRoundedRect scaled_clip; - - memset (&scaled_clip, 0, sizeof (GskRoundedRect)); - - scaled_clip.bounds.origin.x = clip->origin.x * scale; - scaled_clip.bounds.origin.y = clip->origin.y * scale; - scaled_clip.bounds.size.width = clip->size.width * scale; - scaled_clip.bounds.size.height = clip->size.height * scale; +rtt: + { + /* well fuck */ + const float scale = ops_get_scale (builder); + gboolean is_offscreen; + TextureRegion region; + GskRoundedRect scaled_clip; + + memset (&scaled_clip, 0, sizeof (GskRoundedRect)); + + scaled_clip.bounds.origin.x = clip->origin.x * scale; + scaled_clip.bounds.origin.y = clip->origin.y * scale; + scaled_clip.bounds.size.width = clip->size.width * scale; + scaled_clip.bounds.size.height = clip->size.height * scale; + + ops_push_clip (builder, &scaled_clip); + if (!add_offscreen_ops (self, builder, &child->bounds, + child, + ®ion, &is_offscreen, + RESET_OPACITY | FORCE_OFFSCREEN)) + g_assert_not_reached (); + ops_pop_clip (builder); - ops_push_clip (builder, &scaled_clip); - if (!add_offscreen_ops (self, builder, &child->bounds, - child, - ®ion, &is_offscreen, - RESET_OPACITY | FORCE_OFFSCREEN)) - g_assert_not_reached (); - ops_pop_clip (builder); + ops_set_program (builder, &self->programs->blit_program); + ops_set_texture (builder, region.texture_id); + load_offscreen_vertex_data (ops_draw (builder, NULL), child, builder); + return; + } - ops_set_program (builder, &self->programs->blit_program); - ops_set_texture (builder, region.texture_id); - load_offscreen_vertex_data (ops_draw (builder, NULL), child, builder); - 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 diff --git a/testsuite/gsk/compare/clip-in-rounded-clip1.node b/testsuite/gsk/compare/clip-in-rounded-clip1.node new file mode 100644 index 0000000000..b5f041bae9 --- /dev/null +++ b/testsuite/gsk/compare/clip-in-rounded-clip1.node @@ -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; + } + } +} diff --git a/testsuite/gsk/compare/clip-in-rounded-clip1.png b/testsuite/gsk/compare/clip-in-rounded-clip1.png new file mode 100644 index 0000000000..0041bf54de Binary files /dev/null and b/testsuite/gsk/compare/clip-in-rounded-clip1.png differ diff --git a/testsuite/gsk/compare/clip-in-rounded-clip2.node b/testsuite/gsk/compare/clip-in-rounded-clip2.node new file mode 100644 index 0000000000..ab346520e5 --- /dev/null +++ b/testsuite/gsk/compare/clip-in-rounded-clip2.node @@ -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; +} diff --git a/testsuite/gsk/compare/clip-in-rounded-clip2.png b/testsuite/gsk/compare/clip-in-rounded-clip2.png new file mode 100644 index 0000000000..d7f0e06d9e Binary files /dev/null and b/testsuite/gsk/compare/clip-in-rounded-clip2.png differ diff --git a/testsuite/gsk/compare/clip-in-rounded-clip3.node b/testsuite/gsk/compare/clip-in-rounded-clip3.node new file mode 100644 index 0000000000..7af2cdf8cf --- /dev/null +++ b/testsuite/gsk/compare/clip-in-rounded-clip3.node @@ -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; +} diff --git a/testsuite/gsk/compare/clip-in-rounded-clip3.png b/testsuite/gsk/compare/clip-in-rounded-clip3.png new file mode 100644 index 0000000000..4442750417 Binary files /dev/null and b/testsuite/gsk/compare/clip-in-rounded-clip3.png differ diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build index 6240145569..892f1d0fbc 100644 --- a/testsuite/gsk/meson.build +++ b/testsuite/gsk/meson.build @@ -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