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.
This commit is contained in:
Benjamin Otte 2023-09-07 16:02:09 +02:00
parent 9ffd7840ba
commit a05a021fd1
6 changed files with 54 additions and 6 deletions

View File

@ -248,6 +248,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,
@ -257,6 +264,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);
@ -562,13 +576,29 @@ gsk_radial_gradient_node_draw (GskRenderNode *node,
else
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
for (i = 0; i < self->n_stops; i++)
if (self->stops[0].offset > 0.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);
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,
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);

View File

@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

View File

@ -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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

View File

@ -80,6 +80,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',