From da41271dd516dc6f4a5720f500dedb921b7ed347 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 16 Jun 2024 12:45:54 -0400 Subject: [PATCH] css: Parse interpolation for conic gradients Parse things like "in hsl hue longer". For details, see the CSS Images Module Level 4, https://www.w3.org/TR/css-images-4. This commit fixes preexisting brokenness in conic-gradient parsing and printing as well, and includes the relevant test changes. Tests included. Gradient interpolation color spaces aren't supported for rendering yet. --- gtk/gtkcssimageconic.c | 101 ++++++++++++++++++++--------- gtk/gtkcssimageconicprivate.h | 3 + testsuite/css/parser/conic.css | 43 ++++++++++++ testsuite/css/parser/conic.ref.css | 43 ++++++++++++ testsuite/css/style/gradient.nodes | 2 +- 5 files changed, 161 insertions(+), 31 deletions(-) create mode 100644 testsuite/css/parser/conic.css create mode 100644 testsuite/css/parser/conic.ref.css diff --git a/gtk/gtkcssimageconic.c b/gtk/gtkcssimageconic.c index ceef29e59e..d48bfcc30a 100644 --- a/gtk/gtkcssimageconic.c +++ b/gtk/gtkcssimageconic.c @@ -81,7 +81,10 @@ gtk_css_image_conic_snapshot (GtkCssImage *image, last = i; } - gtk_snapshot_append_conic_gradient ( + if (self->color_space != GTK_CSS_COLOR_SPACE_SRGB) + g_warning_once ("Gradient interpolation color spaces are not supported yet"); + + gtk_snapshot_append_conic_gradient ( snapshot, &GRAPHENE_RECT_INIT (0, 0, width, height), &GRAPHENE_POINT_INIT (_gtk_css_position_value_get_x (self->center, width), @@ -172,37 +175,57 @@ gtk_css_image_conic_parse_first_arg (GtkCssImageConic *self, GtkCssParser *parser, GArray *stop_array) { - gboolean nothing_parsed = TRUE; + gboolean has_rotation = FALSE; + gboolean has_center = FALSE; + gboolean has_colorspace = FALSE; + int retval = 1; - if (gtk_css_parser_try_ident (parser, "from")) + do { - self->rotation = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE); - if (self->rotation == NULL) - return 0; - nothing_parsed = FALSE; - } - else - { - self->rotation = gtk_css_number_value_new (0, GTK_CSS_DEG); + if (!has_colorspace && gtk_css_color_interpolation_method_can_parse (parser)) + { + if (!gtk_css_color_interpolation_method_parse (parser, &self->color_space, &self->hue_interp)) + return 0; + has_colorspace = TRUE; + } + else if (!has_rotation && gtk_css_parser_try_ident (parser, "from")) + { + self->rotation = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_ANGLE); + if (self->rotation == NULL) + return 0; + has_rotation = TRUE; + } + else if (!has_center && gtk_css_parser_try_ident (parser, "at")) + { + self->center = _gtk_css_position_value_parse (parser); + if (self->center == NULL) + return 0; + has_center = TRUE; + } + else if (gtk_css_token_is (gtk_css_parser_get_token (parser), GTK_CSS_TOKEN_COMMA)) + { + retval = 1; + break; + } + else + { + if (gtk_css_image_conic_parse_color_stop (self, parser, stop_array)) + { + retval = 2; + break; + } + } } + while (!(has_colorspace && has_rotation && has_center)); - if (gtk_css_parser_try_ident (parser, "at")) - { - self->center = _gtk_css_position_value_parse (parser); - if (self->center == NULL) - return 0; - nothing_parsed = FALSE; - } - else - { - self->center = _gtk_css_position_value_new (gtk_css_number_value_new (50, GTK_CSS_PERCENT), - gtk_css_number_value_new (50, GTK_CSS_PERCENT)); - } + if (!has_rotation) + self->rotation = gtk_css_number_value_new (0, GTK_CSS_DEG); - if (!nothing_parsed) - return 1; + if (!has_center) + self->center = _gtk_css_position_value_new (gtk_css_number_value_new (50, GTK_CSS_PERCENT), + gtk_css_number_value_new (50, GTK_CSS_PERCENT)); - return 1 + gtk_css_image_conic_parse_color_stop (self, parser, stop_array); + return retval; } typedef struct @@ -265,7 +288,7 @@ gtk_css_image_conic_print (GtkCssImage *image, gboolean written = FALSE; guint i; - g_string_append (string, "self-gradient("); + g_string_append (string, "conic-gradient("); if (self->center) { @@ -274,7 +297,7 @@ gtk_css_image_conic_print (GtkCssImage *image, if (!gtk_css_value_equal (self->center, compare)) { - g_string_append (string, "from "); + g_string_append (string, "at "); gtk_css_value_print (self->center, string); written = TRUE; } @@ -286,8 +309,19 @@ gtk_css_image_conic_print (GtkCssImage *image, { if (written) g_string_append_c (string, ' '); - g_string_append (string, "at "); + g_string_append (string, "from "); gtk_css_value_print (self->rotation, string); + written = TRUE; + } + + if (self->color_space != GTK_CSS_COLOR_SPACE_SRGB) + { + if (written) + g_string_append_c (string, ' '); + gtk_css_color_interpolation_method_print (self->color_space, + self->hue_interp, + string); + written = TRUE; } if (written) @@ -325,6 +359,8 @@ gtk_css_image_conic_compute (GtkCssImage *image, copy->center = gtk_css_value_compute (self->center, property_id, context); copy->rotation = gtk_css_value_compute (self->rotation, property_id, context); + copy->color_space = self->color_space; + copy->hue_interp = self->hue_interp; copy->n_stops = self->n_stops; copy->color_stops = g_malloc (sizeof (GtkCssImageConicColorStop) * copy->n_stops); @@ -380,6 +416,9 @@ gtk_css_image_conic_transition (GtkCssImage *start_image, if (result->rotation == NULL) goto fail; + result->color_space = start->color_space; + result->hue_interp = start->hue_interp; + result->color_stops = g_malloc (sizeof (GtkCssImageConicColorStop) * start->n_stops); result->n_stops = 0; for (i = 0; i < start->n_stops; i++) @@ -435,7 +474,9 @@ gtk_css_image_conic_equal (GtkCssImage *image1, guint i; if (!gtk_css_value_equal (conic1->center, conic2->center) || - !gtk_css_value_equal (conic1->rotation, conic2->rotation)) + !gtk_css_value_equal (conic1->rotation, conic2->rotation) || + conic1->color_space != conic2->color_space || + conic1->hue_interp != conic2->hue_interp) return FALSE; for (i = 0; i < conic1->n_stops; i++) diff --git a/gtk/gtkcssimageconicprivate.h b/gtk/gtkcssimageconicprivate.h index b0d33bf23b..521557734d 100644 --- a/gtk/gtkcssimageconicprivate.h +++ b/gtk/gtkcssimageconicprivate.h @@ -47,6 +47,9 @@ struct _GtkCssImageConic GtkCssValue *center; GtkCssValue *rotation; + GtkCssColorSpace color_space; + GtkCssHueInterpolation hue_interp; + guint n_stops; GtkCssImageConicColorStop *color_stops; }; diff --git a/testsuite/css/parser/conic.css b/testsuite/css/parser/conic.css new file mode 100644 index 0000000000..52b639f8f7 --- /dev/null +++ b/testsuite/css/parser/conic.css @@ -0,0 +1,43 @@ +a { + background-image: conic-gradient(yellow, green); +} + +b { + background-image: conic-gradient(at center, yellow 0%, green 100%); +} + +c { + background-image: conic-gradient(at 50% 50%, yellow, green); +} + +d { + background-image: conic-gradient(at left top, yellow, green); +} + +e { + background-image: conic-gradient(at 25% bottom, red, yellow, green); +} + +f { + background-image: conic-gradient(from 20deg, red, yellow 50%, green); +} + +g { + background-image: conic-gradient(from 0.25turn at 20px 20px, red, yellow, green); +} + +h { + background-image: conic-gradient(from 20deg in oklab, red, yellow, green); +} + +i { + background-image: conic-gradient(in rec2020 at center, red, yellow, green); +} + +j { + background-image: conic-gradient(in hsl longer hue from 10deg, red, yellow, green); +} + +k { + background-image: conic-gradient(in srgb from -10deg at 20px 20px, red, yellow); +} diff --git a/testsuite/css/parser/conic.ref.css b/testsuite/css/parser/conic.ref.css new file mode 100644 index 0000000000..ef3fe63264 --- /dev/null +++ b/testsuite/css/parser/conic.ref.css @@ -0,0 +1,43 @@ +a { + background-image: conic-gradient(rgb(255,255,0), rgb(0,128,0)); +} + +b { + background-image: conic-gradient(rgb(255,255,0) 0, rgb(0,128,0) 100%); +} + +c { + background-image: conic-gradient(rgb(255,255,0), rgb(0,128,0)); +} + +d { + background-image: conic-gradient(at left top, rgb(255,255,0), rgb(0,128,0)); +} + +e { + background-image: conic-gradient(at 25% bottom, rgb(255,0,0), rgb(255,255,0), rgb(0,128,0)); +} + +f { + background-image: conic-gradient(from 20deg, rgb(255,0,0), rgb(255,255,0) 50%, rgb(0,128,0)); +} + +g { + background-image: conic-gradient(at 20px 20px from 0.25turn, rgb(255,0,0), rgb(255,255,0), rgb(0,128,0)); +} + +h { + background-image: conic-gradient(from 20deg in oklab, rgb(255,0,0), rgb(255,255,0), rgb(0,128,0)); +} + +i { + background-image: conic-gradient(in rec2020, rgb(255,0,0), rgb(255,255,0), rgb(0,128,0)); +} + +j { + background-image: conic-gradient(from 10deg in hsl longer hue, rgb(255,0,0), rgb(255,255,0), rgb(0,128,0)); +} + +k { + background-image: conic-gradient(at 20px 20px from -10deg, rgb(255,0,0), rgb(255,255,0)); +} diff --git a/testsuite/css/style/gradient.nodes b/testsuite/css/style/gradient.nodes index b5755cea54..cc34f9ff8c 100644 --- a/testsuite/css/style/gradient.nodes +++ b/testsuite/css/style/gradient.nodes @@ -7,4 +7,4 @@ window.background:dir(ltr) label:dir(ltr) background-image: radial-gradient(ellipse farthest-corner at center 30px, rgb(255,255,0), rgb(255,0,0) 30%, rgb(0,0,255)); /* gradient.css:10:3-89 */ image:dir(ltr) - background-image: self-gradient(from center 30px at 25deg, rgb(255,255,0), rgb(255,0,0) 30%, rgb(0,0,255)); /* gradient.css:14:3-83 */ + background-image: conic-gradient(at center 30px from 25deg, rgb(255,255,0), rgb(255,0,0) 30%, rgb(0,0,255)); /* gradient.css:14:3-83 */