From 552a7dc3080acdf1ad3b6eed96f2af984bd9715f Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 7 Sep 2023 16:02:09 +0200 Subject: [PATCH] rendernode: Fix Cairo rendering of repeating gradients Cairo and the GL renderer have a different idea of how to handle transitioning of colors outside the defined range. Consider these stops: black 50%, white 50% What color is at 0%? Cairo would transition between the last and first stop, ie it'd do a white-to-black transition and end up at rgb(0.5,0.5,0.5) at 0%. GL would behave as it would for non-repeating gradients and use black for the range [0%..50%] and white for [50%..100%]. The web would rescale the range so the first stop would be at 0% and the last stop would be at 100%, so this gradient would be illegal. Considering that it's possible for code to transition between the different behaviors by adding explicit stops at 0%/100%, I could choose any method. So I chose the simplest one, which is what the GL renderer does and which treats repeating and non-repeating gradients the same. Tests attached. --- gsk/gskrendernodeimpl.c | 40 +++++++++++++++--- ...repeating-linear-gradient-edge-colors.node | 6 +++ .../repeating-linear-gradient-edge-colors.png | Bin 0 -> 151 bytes ...repeating-radial-gradient-edge-colors.node | 10 +++++ .../repeating-radial-gradient-edge-colors.png | Bin 0 -> 144 bytes testsuite/gsk/meson.build | 2 + 6 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 testsuite/gsk/compare/repeating-linear-gradient-edge-colors.node create mode 100644 testsuite/gsk/compare/repeating-linear-gradient-edge-colors.png create mode 100644 testsuite/gsk/compare/repeating-radial-gradient-edge-colors.node create mode 100644 testsuite/gsk/compare/repeating-radial-gradient-edge-colors.png diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 2758dff2a6..08bdc58e47 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -245,6 +245,13 @@ gsk_linear_gradient_node_draw (GskRenderNode *node, if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE) cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + if (self->stops[0].offset > 0.0) + cairo_pattern_add_color_stop_rgba (pattern, + 0.0, + self->stops[0].color.red, + self->stops[0].color.green, + self->stops[0].color.blue, + self->stops[0].color.alpha); for (i = 0; i < self->n_stops; i++) { cairo_pattern_add_color_stop_rgba (pattern, @@ -254,6 +261,13 @@ gsk_linear_gradient_node_draw (GskRenderNode *node, self->stops[i].color.blue, self->stops[i].color.alpha); } + if (self->stops[self->n_stops-1].offset < 1.0) + cairo_pattern_add_color_stop_rgba (pattern, + 1.0, + self->stops[self->n_stops-1].color.red, + self->stops[self->n_stops-1].color.green, + self->stops[self->n_stops-1].color.blue, + self->stops[self->n_stops-1].color.alpha); cairo_set_source (cr, pattern); cairo_pattern_destroy (pattern); @@ -559,13 +573,29 @@ gsk_radial_gradient_node_draw (GskRenderNode *node, else cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); + if (self->stops[0].offset > 0.0) + cairo_pattern_add_color_stop_rgba (pattern, + 0.0, + self->stops[0].color.red, + self->stops[0].color.green, + self->stops[0].color.blue, + self->stops[0].color.alpha); for (i = 0; i < self->n_stops; i++) + { + cairo_pattern_add_color_stop_rgba (pattern, + self->stops[i].offset, + self->stops[i].color.red, + self->stops[i].color.green, + self->stops[i].color.blue, + self->stops[i].color.alpha); + } + if (self->stops[self->n_stops-1].offset < 1.0) cairo_pattern_add_color_stop_rgba (pattern, - self->stops[i].offset, - self->stops[i].color.red, - self->stops[i].color.green, - self->stops[i].color.blue, - self->stops[i].color.alpha); + 1.0, + self->stops[self->n_stops-1].color.red, + self->stops[self->n_stops-1].color.green, + self->stops[self->n_stops-1].color.blue, + self->stops[self->n_stops-1].color.alpha); gsk_cairo_rectangle (cr, &node->bounds); cairo_translate (cr, self->center.x, self->center.y); diff --git a/testsuite/gsk/compare/repeating-linear-gradient-edge-colors.node b/testsuite/gsk/compare/repeating-linear-gradient-edge-colors.node new file mode 100644 index 0000000000..06ffe1bcef --- /dev/null +++ b/testsuite/gsk/compare/repeating-linear-gradient-edge-colors.node @@ -0,0 +1,6 @@ +repeating-linear-gradient { + bounds: 0 0 50 50; + start: 0 0; + end: 0 50; + stops: 0.5 rgb(255,0,0), 0.5 rgb(0,255,0); +} diff --git a/testsuite/gsk/compare/repeating-linear-gradient-edge-colors.png b/testsuite/gsk/compare/repeating-linear-gradient-edge-colors.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac3fd1e445f3072f12721b8f6e819369ec3281f GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETI8PVHkc`H+HxBY1FyLV}T>kt0 z8@<2ww|8{w28sf)z4*}Q$iB}mr*h> literal 0 HcmV?d00001 diff --git a/testsuite/gsk/compare/repeating-radial-gradient-edge-colors.node b/testsuite/gsk/compare/repeating-radial-gradient-edge-colors.node new file mode 100644 index 0000000000..10db7dc2ad --- /dev/null +++ b/testsuite/gsk/compare/repeating-radial-gradient-edge-colors.node @@ -0,0 +1,10 @@ +clip { + clip: -25 -25 50 50; + child: repeating-radial-gradient { + bounds: -100 -100 200 200; + center: 0 0; + hradius: 100; + vradius: 100; + stops: 0.5 rgb(255,0,0), 0.5 rgb(0,255,0); + } +} diff --git a/testsuite/gsk/compare/repeating-radial-gradient-edge-colors.png b/testsuite/gsk/compare/repeating-radial-gradient-edge-colors.png new file mode 100644 index 0000000000000000000000000000000000000000..9a5764a9b2e6bb2f1829015252759d489970f477 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETa8DPxi4@=GhTEXDy L>gTe~DWM4f*sU)x literal 0 HcmV?d00001 diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build index e48e9a3a45..c0facb5a12 100644 --- a/testsuite/gsk/meson.build +++ b/testsuite/gsk/meson.build @@ -78,6 +78,8 @@ compare_render_tests = [ 'outset_shadow_rounded_top', 'outset_shadow_simple', 'repeat', + 'repeating-linear-gradient-edge-colors', + 'repeating-radial-gradient-edge-colors', 'repeat-no-repeat', 'repeat-empty-child-bounds', 'repeat-negative-coords', -- 2.30.2