gsk: Fix clipping error when drawing shadows
authorBenjamin Otte <otte@redhat.com>
Mon, 18 Sep 2023 05:48:10 +0000 (07:48 +0200)
committerMatthias Clasen <mclasen@redhat.com>
Tue, 19 Sep 2023 20:32:27 +0000 (16:32 -0400)
When shadows were offset - in particular when offset so the original
source was out of bounds of the result - the drawing code would create a
pattern for it that didn't include enough of it to compose a shadow.

Fix that by not creating those patterns anymore, but instead drawing the
source (potentially multiple times) at the required offsets.

While that does more drawing, it simplifies the shadow node draw code,
and that's the primary goal of the Cairo rendering.

Test included.

gsk/gskrendernodeimpl.c
testsuite/gsk/compare/shadow-clip-contents.node [new file with mode: 0644]
testsuite/gsk/compare/shadow-clip-contents.png [new file with mode: 0644]
testsuite/gsk/meson.build

index 80bf406ae32ca0bf46e8e1adef27cad4c5618616..3790fcb4c35c592752afbe4c90f856163062a3e6 100644 (file)
@@ -4432,18 +4432,8 @@ gsk_shadow_node_draw (GskRenderNode *node,
                       cairo_t       *cr)
 {
   GskShadowNode *self = (GskShadowNode *) node;
-  cairo_pattern_t *pattern;
   gsize i;
 
-  cairo_save (cr);
-  /* clip so the push_group() creates a small surface */
-  gsk_cairo_rectangle (cr, &self->child->bounds);
-  cairo_clip (cr);
-  cairo_push_group (cr);
-  gsk_render_node_draw (self->child, cr);
-  pattern = cairo_pop_group (cr);
-  cairo_restore (cr);
-
   cairo_save (cr);
   /* clip so the blur area stays small */
   gsk_cairo_rectangle (cr, &node->bounds);
@@ -4452,27 +4442,31 @@ gsk_shadow_node_draw (GskRenderNode *node,
   for (i = 0; i < self->n_shadows; i++)
     {
       GskShadow *shadow = &self->shadows[i];
+      cairo_pattern_t *pattern;
 
       /* We don't need to draw invisible shadows */
       if (gdk_rgba_is_clear (&shadow->color))
         continue;
 
       cairo_save (cr);
-      gdk_cairo_set_source_rgba (cr, &shadow->color);
       cr = gsk_cairo_blur_start_drawing (cr, shadow->radius, GSK_BLUR_X | GSK_BLUR_Y);
 
+      cairo_save (cr);
       cairo_translate (cr, shadow->dx, shadow->dy);
+      cairo_push_group (cr);
+      gsk_render_node_draw (self->child, cr);
+      pattern = cairo_pop_group (cr);
+      gdk_cairo_set_source_rgba (cr, &shadow->color);
       cairo_mask (cr, pattern);
+      cairo_restore (cr);
 
       cr = gsk_cairo_blur_finish_drawing (cr, shadow->radius, &shadow->color, GSK_BLUR_X | GSK_BLUR_Y);
       cairo_restore (cr);
     }
 
-  cairo_set_source (cr, pattern);
-  cairo_paint (cr);
-  cairo_restore (cr);
+  gsk_render_node_draw (self->child, cr);
 
-  cairo_pattern_destroy (pattern);
+  cairo_restore (cr);
 }
 
 static void
diff --git a/testsuite/gsk/compare/shadow-clip-contents.node b/testsuite/gsk/compare/shadow-clip-contents.node
new file mode 100644 (file)
index 0000000..8906577
--- /dev/null
@@ -0,0 +1,10 @@
+clip {
+  clip: 0 0 50 50;
+  child: shadow {
+    shadows: rgb(0,0,0) -20 -20;
+    child: color {
+      bounds: 40 40 50 50;
+      color: rgb(255,0,204);
+    }
+  }
+}
diff --git a/testsuite/gsk/compare/shadow-clip-contents.png b/testsuite/gsk/compare/shadow-clip-contents.png
new file mode 100644 (file)
index 0000000..ffe7bff
Binary files /dev/null and b/testsuite/gsk/compare/shadow-clip-contents.png differ
index bbcefc920588e8177a2c01b87ea31074337de707..cd1a32c33106dd1277e8c60f8b7993a59056d663 100644 (file)
@@ -90,6 +90,7 @@ compare_render_tests = [
   'scaled-cairo',
   'scaled-texture',
   'shadow-behind',
+  'shadow-clip-contents',
   'shadow-in-opacity',
   'shadow-opacity',
   'shrink-rounded-border',