[gtk/wip/baedert/for-master] gl renderer: Improve clipping code



commit c03270bdc6b52f8b25985258c7bb3ec42d705303
Author: Timm Bäder <mail baedert org>
Date:   Sun May 24 12:06:07 2020 +0200

    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

 gsk/gl/glutilsprivate.h                          |  30 ++++++
 gsk/gl/gskglrenderer.c                           | 127 ++++++++++++++++-------
 testsuite/gsk/compare/clip-in-rounded-clip1.node |  11 ++
 testsuite/gsk/compare/clip-in-rounded-clip1.png  | Bin 0 -> 115 bytes
 testsuite/gsk/compare/clip-in-rounded-clip2.node |  36 +++++++
 testsuite/gsk/compare/clip-in-rounded-clip2.png  | Bin 0 -> 407 bytes
 testsuite/gsk/compare/clip-in-rounded-clip3.node |  40 +++++++
 testsuite/gsk/compare/clip-in-rounded-clip3.png  | Bin 0 -> 259 bytes
 testsuite/gsk/meson.build                        |   3 +
 9 files changed, 210 insertions(+), 37 deletions(-)
---
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,
+                            &region, &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,
-                              &region, &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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]