diff --git a/gdk/gdkrgba.c b/gdk/gdkrgba.c index 1519ea726c..ecaab93644 100644 --- a/gdk/gdkrgba.c +++ b/gdk/gdkrgba.c @@ -396,10 +396,18 @@ gboolean */ char * gdk_rgba_to_string (const GdkRGBA *rgba) +{ + return g_string_free (gdk_rgba_print (rgba, g_string_new ("")), FALSE); +} + +GString * +gdk_rgba_print (const GdkRGBA *rgba, + GString *string) { if (rgba->alpha > 0.999) { - return g_strdup_printf ("rgb(%d,%d,%d)", + g_string_append_printf (string, + "rgb(%d,%d,%d)", (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.), (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.), (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.)); @@ -410,12 +418,15 @@ gdk_rgba_to_string (const GdkRGBA *rgba) g_ascii_formatd (alpha, G_ASCII_DTOSTR_BUF_SIZE, "%g", CLAMP (rgba->alpha, 0, 1)); - return g_strdup_printf ("rgba(%d,%d,%d,%s)", + g_string_append_printf (string, + "rgba(%d,%d,%d,%s)", (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.), (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.), (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.), alpha); } + + return string; } static gboolean diff --git a/gdk/gdkrgbaprivate.h b/gdk/gdkrgbaprivate.h index 7c6bf4ec20..3866680ab9 100644 --- a/gdk/gdkrgbaprivate.h +++ b/gdk/gdkrgbaprivate.h @@ -72,5 +72,8 @@ _gdk_rgba_equal (gconstpointer p1, rgba1->alpha == rgba2->alpha; } +GString * gdk_rgba_print (const GdkRGBA *rgba, + GString *string); + G_END_DECLS diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index b0f4159017..f3b403f154 100644 --- a/gsk/gskrendernodeparser.c +++ b/gsk/gskrendernodeparser.c @@ -3232,17 +3232,6 @@ append_rounded_rect (GString *str, } } -static void -append_rgba (GString *str, - const GdkRGBA *rgba) -{ - char *rgba_str = gdk_rgba_to_string (rgba); - - g_string_append (str, rgba_str); - - g_free (rgba_str); -} - static void append_point (GString *str, const graphene_point_t *p) @@ -3288,7 +3277,7 @@ append_rgba_param (Printer *p, { _indent (p); g_string_append_printf (p->str, "%s: ", param_name); - append_rgba (p->str, value); + gdk_rgba_print (value, p->str); g_string_append_c (p->str, ';'); g_string_append_c (p->str, '\n'); } @@ -3442,7 +3431,7 @@ append_stops_param (Printer *p, string_append_double (p->str, stops[i].offset); g_string_append_c (p->str, ' '); - append_rgba (p->str, &stops[i].color); + gdk_rgba_print (&stops[i].color, p->str); } g_string_append (p->str, ";\n"); } @@ -4068,7 +4057,7 @@ render_node_print (Printer *p, { if (i > 0) g_string_append_c (p->str, ' '); - append_rgba (p->str, &colors[i]); + gdk_rgba_print (&colors[i], p->str); } g_string_append (p->str, ";\n"); } diff --git a/gtk/gtkcolorutils.c b/gtk/gtkcolorutils.c index 4abcddbdc0..f165022225 100644 --- a/gtk/gtkcolorutils.c +++ b/gtk/gtkcolorutils.c @@ -301,13 +301,29 @@ gtk_hwb_to_rgb (float hue, float white, float black, #define DEG_TO_RAD(x) ((x) * G_PI / 180) #define RAD_TO_DEG(x) ((x) * 180 / G_PI) +static inline void +_sincosf (float angle, + float *out_s, + float *out_c) +{ +#ifdef HAVE_SINCOSF + sincosf (angle, out_s, out_c); +#else + *out_s = sinf (angle); + *out_c = cosf (angle); +#endif +} + void gtk_oklab_to_oklch (float L, float a, float b, float *L2, float *C, float *H) { *L2 = L; - *C = sqrtf (a * a + b * b); - *H = atan2 (b, a); + *C = hypotf (a, b); + *H = RAD_TO_DEG (atan2 (b, a)); + *H = fmod (*H, 360); + if (*H < 0) + *H += 360; } void @@ -315,16 +331,9 @@ gtk_oklch_to_oklab (float L, float C, float H, float *L2, float *a, float *b) { *L2 = L; - - if (H == 0) - { - *a = *b = 0; - } - else - { - *a = C * cosf (DEG_TO_RAD (H)); - *b = C * sinf (DEG_TO_RAD (H)); - } + _sincosf (DEG_TO_RAD (H), b, a); + *a *= C; + *b *= C; } static float diff --git a/gtk/gtkcsscolor.c b/gtk/gtkcsscolor.c index 98256254cc..053d5a0296 100644 --- a/gtk/gtkcsscolor.c +++ b/gtk/gtkcsscolor.c @@ -59,6 +59,104 @@ gtk_css_color_init (GtkCssColor *color, gtk_css_color_init_with_missing (color, color_space, values, missing); } +/* }}} */ +/* {{{ Utilities */ + +static inline void +append_color_component (GString *string, + const GtkCssColor *color, + guint idx) +{ + if (gtk_css_color_component_missing (color, idx)) + g_string_append (string, "none"); + else + g_string_append_printf (string, "%g", gtk_css_color_get_component (color, idx)); +} + +GString * +gtk_css_color_print (const GtkCssColor *color, + gboolean serialize_as_rgb, + GString *string) +{ + switch (color->color_space) + { + case GTK_CSS_COLOR_SPACE_HSL: + case GTK_CSS_COLOR_SPACE_HWB: +print_rgb: + { + GtkCssColor tmp; + + gtk_css_color_convert (color, GTK_CSS_COLOR_SPACE_SRGB, &tmp); + if (tmp.values[3] > 0.999) + { + g_string_append_printf (string, "rgb(%d,%d,%d)", + (int)(0.5 + CLAMP (tmp.values[0], 0., 1.) * 255.), + (int)(0.5 + CLAMP (tmp.values[1], 0., 1.) * 255.), + (int)(0.5 + CLAMP (tmp.values[2], 0., 1.) * 255.)); + } + else + { + char alpha[G_ASCII_DTOSTR_BUF_SIZE]; + + g_ascii_formatd (alpha, G_ASCII_DTOSTR_BUF_SIZE, "%g", CLAMP (tmp.values[3], 0, 1)); + + g_string_append_printf (string, "rgba(%d,%d,%d,%s)", + (int)(0.5 + CLAMP (tmp.values[0], 0., 1.) * 255.), + (int)(0.5 + CLAMP (tmp.values[1], 0., 1.) * 255.), + (int)(0.5 + CLAMP (tmp.values[2], 0., 1.) * 255.), + alpha); + } + } + return string; + + case GTK_CSS_COLOR_SPACE_SRGB: + if (serialize_as_rgb) + goto print_rgb; + + g_string_append (string, "color(srgb "); + break; + + case GTK_CSS_COLOR_SPACE_SRGB_LINEAR: + g_string_append (string, "color(srgb-linear "); + break; + + case GTK_CSS_COLOR_SPACE_OKLAB: + g_string_append (string, "oklab("); + break; + + case GTK_CSS_COLOR_SPACE_OKLCH: + g_string_append (string, "oklch("); + break; + + default: + g_assert_not_reached (); + } + + for (guint i = 0; i < 3; i++) + { + if (i > 0) + g_string_append_c (string, ' '); + append_color_component (string, color, i); + } + + if (gtk_css_color_component_missing (color, 3) || + color->values[3] < 0.999) + { + g_string_append (string, " / "); + append_color_component (string, color, 3); + } + + g_string_append_c (string, ')'); + + return string; +} + +char * +gtk_css_color_to_string (const GtkCssColor *color) +{ + return g_string_free (gtk_css_color_print (color, FALSE, g_string_new ("")), FALSE); +} + /* }}} */ /* {{{ Color conversion */ @@ -66,6 +164,7 @@ static void convert_to_rectangular (GtkCssColor *output) { float v[4]; + gboolean no_missing[4] = { 0, }; switch (output->color_space) { @@ -80,7 +179,7 @@ convert_to_rectangular (GtkCssColor *output) output->values[2] / 100, &v[0], &v[1], &v[2]); v[3] = output->values[3]; - gtk_css_color_init (output, GTK_CSS_COLOR_SPACE_SRGB, v); + gtk_css_color_init_with_missing (output, GTK_CSS_COLOR_SPACE_SRGB, v, no_missing); break; case GTK_CSS_COLOR_SPACE_HWB: @@ -89,16 +188,16 @@ convert_to_rectangular (GtkCssColor *output) output->values[2] / 100, &v[0], &v[1], &v[2]); v[3] = output->values[3]; - gtk_css_color_init (output, GTK_CSS_COLOR_SPACE_SRGB, v); + gtk_css_color_init_with_missing (output, GTK_CSS_COLOR_SPACE_SRGB, v, no_missing); break; case GTK_CSS_COLOR_SPACE_OKLCH: gtk_oklch_to_oklab (output->values[0], - output->values[1] / 100, - output->values[2] / 100, + output->values[1], + output->values[2], &v[0], &v[1], &v[2]); v[3] = output->values[3]; - gtk_css_color_init (output, GTK_CSS_COLOR_SPACE_OKLAB, v); + gtk_css_color_init_with_missing (output, GTK_CSS_COLOR_SPACE_OKLAB, v, no_missing); break; default: @@ -238,7 +337,7 @@ convert_linear_to_linear (GtkCssColor *output, gtk_css_color_init (output, dest_linear, v); } else if (dest_linear == GTK_CSS_COLOR_SPACE_OKLAB && - output->color_space == GTK_CSS_COLOR_SPACE_SRGB_LINEAR) + output->color_space == GTK_CSS_COLOR_SPACE_SRGB_LINEAR) { gtk_linear_srgb_to_oklab (output->values[0], output->values[1], @@ -472,15 +571,15 @@ collect_analogous_missing (const GtkCssColor *color, gboolean missing[4]) { /* Coords for red, green, blue, lightness, colorfulness, hue, - * opposite a, opposite b, for each of our colorspaces + * opposite a, opposite b, alpha, for each of our colorspaces, */ - static int analogous[][8] = { - { 0, 1, 2, -1, -1, -1, -1, -1 }, /* srgb */ - { 0, 1, 2, -1, -1, -1, -1, -1 }, /* srgb-linear */ - { -1, -1, -1, 2, 1, 0, -1, -1 }, /* hsl */ - { -1, -1, -1, -1, -1, 0, -1, -1 }, /* hwb */ - { -1, -1, -1, 0, -1, -1, 1, 2 }, /* oklab */ - { -1, -1, -1, 0, 1, 2, -1, -1 }, /* oklch */ + static int analogous[][9] = { + { 0, 1, 2, -1, -1, -1, -1, -1, 3 }, /* srgb */ + { 0, 1, 2, -1, -1, -1, -1, -1, 3 }, /* srgb-linear */ + { -1, -1, -1, 2, 1, 0, -1, -1, 3 }, /* hsl */ + { -1, -1, -1, -1, -1, 0, -1, -1, 3 }, /* hwb */ + { -1, -1, -1, 0, -1, -1, 1, 2, 3 }, /* oklab */ + { -1, -1, -1, 0, 1, 2, -1, -1, 3 }, /* oklch */ }; @@ -495,7 +594,7 @@ collect_analogous_missing (const GtkCssColor *color, if ((color->missing & (1 << i)) == 0) continue; - for (guint j = 0; j < 8; j++) + for (guint j = 0; j < 9; j++) { if (src[j] == i) { @@ -537,13 +636,16 @@ gtk_css_color_interpolate (const GtkCssColor *from, gboolean m2 = to_missing[i]; if (m1 && !m2) - from1.values[i] = to1.values[1]; + from1.values[i] = to1.values[i]; else if (!m1 && m2) - to1.values[i] = from1.values[1]; + to1.values[i] = from1.values[i]; missing[i] = from_missing[i] && to_missing[i]; } + from1.missing = 0; + to1.missing = 0; + apply_hue_interpolation (&from1, &to1, in, interp); premultiply (&from1); diff --git a/gtk/gtkcsscolorprivate.h b/gtk/gtkcsscolorprivate.h index f7bd66b370..5ed457d224 100644 --- a/gtk/gtkcsscolorprivate.h +++ b/gtk/gtkcsscolorprivate.h @@ -84,6 +84,12 @@ void gtk_css_color_init (GtkCssColor *color, GtkCssColorSpace color_space, const float values[4]); +GString * gtk_css_color_print (const GtkCssColor *color, + gboolean serialize_as_rgb, + GString *string); + +char * gtk_css_color_to_string (const GtkCssColor *color); + void gtk_css_color_convert (const GtkCssColor *input, GtkCssColorSpace dest, GtkCssColor *output); diff --git a/gtk/gtkcsscolorvalue.c b/gtk/gtkcsscolorvalue.c index 92fbe25629..aaa6d8a1bb 100644 --- a/gtk/gtkcsscolorvalue.c +++ b/gtk/gtkcsscolorvalue.c @@ -267,83 +267,11 @@ gtk_css_value_color_print (const GtkCssValue *value, switch (value->type) { case COLOR_TYPE_LITERAL: - { - char *s = gdk_rgba_to_string (&value->rgba); - g_string_append (string, s); - g_free (s); - } + gdk_rgba_print (&value->rgba, string); break; case COLOR_TYPE_COLOR: - switch (value->color.color_space) - { - case GTK_CSS_COLOR_SPACE_HSL: - case GTK_CSS_COLOR_SPACE_HWB: -print_rgb: - { - GtkCssColor tmp; - - gtk_css_color_convert (&value->color, GTK_CSS_COLOR_SPACE_SRGB, &tmp); - if (tmp.values[3] > 0.999) - { - g_string_append_printf (string, "rgb(%d,%d,%d)", - (int)(0.5 + CLAMP (tmp.values[0], 0., 1.) * 255.), - (int)(0.5 + CLAMP (tmp.values[1], 0., 1.) * 255.), - (int)(0.5 + CLAMP (tmp.values[2], 0., 1.) * 255.)); - } - else - { - char alpha[G_ASCII_DTOSTR_BUF_SIZE]; - - g_ascii_formatd (alpha, G_ASCII_DTOSTR_BUF_SIZE, "%g", CLAMP (tmp.values[3], 0, 1)); - - g_string_append_printf (string, "rgba(%d,%d,%d,%s)", - (int)(0.5 + CLAMP (tmp.values[0], 0., 1.) * 255.), - (int)(0.5 + CLAMP (tmp.values[1], 0., 1.) * 255.), - (int)(0.5 + CLAMP (tmp.values[2], 0., 1.) * 255.), - alpha); - } - } - return; - - case GTK_CSS_COLOR_SPACE_SRGB: - if (value->serialize_as_rgb) - goto print_rgb; - - g_string_append (string, "color(srgb "); - break; - - case GTK_CSS_COLOR_SPACE_SRGB_LINEAR: - g_string_append (string, "color(srgb-linear "); - break; - - case GTK_CSS_COLOR_SPACE_OKLAB: - g_string_append (string, "oklab("); - break; - - case GTK_CSS_COLOR_SPACE_OKLCH: - g_string_append (string, "oklch("); - break; - - default: - g_assert_not_reached (); - } - - for (guint i = 0; i < 3; i++) - { - if (i > 0) - g_string_append_c (string, ' '); - append_color_component (string, &value->color, i); - } - - if (gtk_css_color_component_missing (&value->color, 3) || - gtk_css_color_get_component (&value->color, 3) < 0.999) - { - g_string_append (string, " / "); - append_color_component (string, &value->color, 3); - } - - g_string_append_c (string, ')'); + gtk_css_color_print (&value->color, value->serialize_as_rgb, string); break; case COLOR_TYPE_NAME: @@ -608,10 +536,10 @@ gtk_css_color_value_new_literal (const GdkRGBA *color) return value; } -static GtkCssValue * +GtkCssValue * gtk_css_color_value_new_color (GtkCssColorSpace color_space, gboolean serialize_as_rgb, - float values[4], + const float values[4], gboolean missing[4]) { GtkCssValue *value; @@ -1085,7 +1013,7 @@ parse_ok_C_value (GtkCssParser *parser, if (val == NULL) return FALSE; - *value = gtk_css_number_value_get_canonical (val, 1); + *value = gtk_css_number_value_get_canonical (val, 0.4); *value = MAX (*value, 0.0); gtk_css_value_unref (val); @@ -1633,3 +1561,12 @@ gtk_css_color_value_get_rgba (const GtkCssValue *color) return &color->rgba; } + +const GtkCssColor * +gtk_css_color_value_get_color (const GtkCssValue *color) +{ + g_assert (color->class == >K_CSS_VALUE_COLOR); + g_assert (color->type == COLOR_TYPE_COLOR); + + return &color->color; +} diff --git a/gtk/gtkcsscolorvalueprivate.h b/gtk/gtkcsscolorvalueprivate.h index 83681a4a3b..f8d4f9c3b9 100644 --- a/gtk/gtkcsscolorvalueprivate.h +++ b/gtk/gtkcsscolorvalueprivate.h @@ -21,6 +21,7 @@ #include "gtk/css/gtkcsstokenizerprivate.h" #include "gtk/css/gtkcssparserprivate.h" #include "gtkcssvalueprivate.h" +#include "gtkcsscolorprivate.h" G_BEGIN_DECLS @@ -37,6 +38,14 @@ GtkCssValue * gtk_css_color_value_resolve (GtkCssValue *color GtkCssValue *current); const GdkRGBA * gtk_css_color_value_get_rgba (const GtkCssValue *color) G_GNUC_CONST; +GtkCssValue * gtk_css_color_value_new_color (GtkCssColorSpace color_space, + gboolean serialize_as_rgb, + const float values[4], + gboolean missing[4]) G_GNUC_PURE; + +const GtkCssColor * + gtk_css_color_value_get_color (const GtkCssValue *color) G_GNUC_CONST; + G_END_DECLS diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 9ced2fb246..0f31e52322 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -55,6 +55,7 @@ #include #include #include +#include #include "gtk/gtkdebug.h" #include "gtk/gtkbuiltiniconprivate.h" #include "gtk/gtkrendernodepaintableprivate.h" @@ -1083,9 +1084,9 @@ populate_render_node_properties (GListStore *store, s = g_string_new (""); for (i = 0; i < n_stops; i++) { - char *tmp = gdk_rgba_to_string (&stops[i].color); - g_string_append_printf (s, "%.2f, %s\n", stops[i].offset, tmp); - g_free (tmp); + g_string_append_printf (s, "%.2f, ", stops[i].offset); + gdk_rgba_print (&stops[i].color, s); + g_string_append_c (s, '\n'); } texture = get_linear_gradient_texture (n_stops, stops); @@ -1117,9 +1118,9 @@ populate_render_node_properties (GListStore *store, s = g_string_new (""); for (i = 0; i < n_stops; i++) { - char *tmp = gdk_rgba_to_string (&stops[i].color); - g_string_append_printf (s, "%.2f, %s\n", stops[i].offset, tmp); - g_free (tmp); + g_string_append_printf (s, "%.2f, ", stops[i].offset); + gdk_rgba_print (&stops[i].color, s); + g_string_append_c (s, '\n'); } texture = get_linear_gradient_texture (n_stops, stops); @@ -1146,9 +1147,9 @@ populate_render_node_properties (GListStore *store, s = g_string_new (""); for (i = 0; i < n_stops; i++) { - char *tmp = gdk_rgba_to_string (&stops[i].color); - g_string_append_printf (s, "%.2f, %s\n", stops[i].offset, tmp); - g_free (tmp); + g_string_append_printf (s, "%.2f, ", stops[i].offset); + gdk_rgba_print (&stops[i].color, s); + g_string_append_c (s, '\n'); } texture = get_linear_gradient_texture (n_stops, stops); diff --git a/testsuite/css/color.c b/testsuite/css/color.c new file mode 100644 index 0000000000..daef958c49 --- /dev/null +++ b/testsuite/css/color.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2024 Red Hat Inc. + * + * Author: + * Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include +#include "gtk/gtkcssvalueprivate.h" +#include "gtk/gtkcsscolorprivate.h" +#include "gtk/gtkcsscolorvalueprivate.h" +#include "gtk/gtkcssstylepropertyprivate.h" +#include "gtk/gtkcssstaticstyleprivate.h" + +#define EPSILON 0.001 + +static gboolean +color_is_near (const GtkCssColor *color1, + const GtkCssColor *color2) +{ + return color1->color_space == color2->color_space && + color1->missing == color2->missing && + fabs (color1->values[0] - color2->values[0]) <= EPSILON && + fabs (color1->values[1] - color2->values[1]) <= EPSILON && + fabs (color1->values[2] - color2->values[2]) <= EPSILON && + fabs (color1->values[3] - color2->values[3]) <= EPSILON; +} + +static void +error_cb (GtkCssParser *parser, + const GtkCssLocation *start, + const GtkCssLocation *end, + const GError *error, + gpointer user_data) +{ + *(GError **)user_data = g_error_copy (error); +} + +static void +color_from_string (const char *str, + GtkCssColor *res) +{ + GtkStyleProperty *prop; + GBytes *bytes; + GtkCssParser *parser; + GtkCssValue *value; + GError *error = NULL; + + bytes = g_bytes_new_static (str, strlen (str)); + parser = gtk_css_parser_new_for_bytes (bytes, NULL, error_cb, &error, NULL); + prop = _gtk_style_property_lookup ("color"); + value = _gtk_style_property_parse_value (prop, parser); + g_assert_nonnull (value); + g_assert_no_error (error); + gtk_css_parser_unref (parser); + g_bytes_unref (bytes); + + gtk_css_color_init_from_color (res, gtk_css_color_value_get_color (value)); + + gtk_css_value_unref (value); +} + +static void +print_css_color (const char *prefix, + const GtkCssColor *c) +{ + GtkCssValue *v; + char *s; + + v = gtk_css_color_value_new_color (c->color_space, + FALSE, + c->values, + (gboolean[4]) { + gtk_css_color_component_missing (c, 0), + gtk_css_color_component_missing (c, 1), + gtk_css_color_component_missing (c, 2), + gtk_css_color_component_missing (c, 3), + }); + s = gtk_css_value_to_string (v); + g_print ("%s: %s\n", prefix, s); + g_free (s); + gtk_css_value_unref (v); +} + +/* Tests for css color conversions */ + +typedef struct { + const char *input; + GtkCssColorSpace dest; + const char *expected; +} ColorConversionTest; + +static ColorConversionTest conversion_tests[] = { + { "rgb(255,0,0)", GTK_CSS_COLOR_SPACE_SRGB_LINEAR, "color(srgb-linear 1 0 0)" }, + { "color(srgb 0.5 none 1 / 0.7)", GTK_CSS_COLOR_SPACE_SRGB_LINEAR, "color(srgb-linear 0.214041 0 1 / 0.7)" }, + { "rgb(100,100,100)", GTK_CSS_COLOR_SPACE_HSL, "hsla(0deg 0 39.215687 / 1)" }, + /* the following are from color-4, Example 26 */ + { "oklch(40.101% 0.12332 21.555)", GTK_CSS_COLOR_SPACE_SRGB, "rgb(49.06% 13.87% 15.9%)" }, + { "oklch(59.686% 0.15619 49.7694)", GTK_CSS_COLOR_SPACE_SRGB, "rgb(77.61% 36.34% 2.45%)" }, + { "oklch(0.65125 0.13138 104.097)", GTK_CSS_COLOR_SPACE_SRGB, "rgb(61.65% 57.51% 9.28%)" }, + { "oklch(0.66016 0.15546 134.231)", GTK_CSS_COLOR_SPACE_SRGB, "rgb(40.73% 65.12% 22.35%)" }, + { "oklch(72.322% 0.12403 247.996)", GTK_CSS_COLOR_SPACE_SRGB, "rgb(38.29% 67.27% 93.85%)" }, + { "oklch(42.1% 48.25% 328.4)", GTK_CSS_COLOR_SPACE_SRGB, "color(srgb 0.501808 0.00257216 0.501403)" }, + /* more random tests */ + { "oklch(0.392 0.4 none)", GTK_CSS_COLOR_SPACE_OKLCH, "oklch(0.392 0.4 0)" }, +}; + +static void +test_conversion (gconstpointer data) +{ + ColorConversionTest *test = &conversion_tests[GPOINTER_TO_INT (data)]; + GtkCssColor input; + GtkCssColor expected; + GtkCssColor result; + + color_from_string (test->input, &input); + color_from_string (test->expected, &expected); + + gtk_css_color_convert (&input, test->dest, &result); + + if (g_test_verbose ()) + { + print_css_color ("expected", &expected); + print_css_color ("converted", &result); + } + + g_assert_true (color_is_near (&result, &expected)); +} + +/* Tests for css color interpolation */ + +typedef struct { + const char *input1; + const char *input2; + float progress; + GtkCssColorSpace in; + GtkCssHueInterpolation interp; + const char *expected; +} ColorInterpolationTest; + +static ColorInterpolationTest interpolation_tests[] = { + /* color-4, example 33 */ + { "oklch(78.3% 0.108 326.5)", "oklch(39.2% 0.4 none)", 0.5, GTK_CSS_COLOR_SPACE_OKLCH, GTK_CSS_HUE_INTERPOLATION_SHORTER, "oklch(58.75% 0.254 326.5)" }, + /* color-4, example 34 */ + { "oklch(0.783 0.108 326.5 / 0.5)", "oklch(0.392 0.4 0 / none)", 0.5, GTK_CSS_COLOR_SPACE_OKLCH, GTK_CSS_HUE_INTERPOLATION_SHORTER, "oklch(0.5875 0.254 343.25 / 0.5)" }, + /* color-4, example 35 */ + { "rgb(24% 12% 98% / 0.4)", "rgb(62% 26% 64% / 0.6)", 0.5, GTK_CSS_COLOR_SPACE_SRGB, GTK_CSS_HUE_INTERPOLATION_SHORTER, "rgb(46.8% 20.4% 77.6% / 0.5)" }, + /* color-4, example 38 */ + { "oklch(0.6 0.24 30)", "oklch(0.8 0.15 90)", 0.5, GTK_CSS_COLOR_SPACE_OKLCH, GTK_CSS_HUE_INTERPOLATION_SHORTER, "oklch(0.7 0.195 60)" }, + /* color-4, example 39 */ + { "oklch(0.6 0.24 30)", "oklch(0.8 0.15 90)", 0.5, GTK_CSS_COLOR_SPACE_OKLCH, GTK_CSS_HUE_INTERPOLATION_LONGER, "oklch(0.7 0.195 240)" }, +}; + +static void +test_interpolation (gconstpointer data) +{ + ColorInterpolationTest *test = &interpolation_tests[GPOINTER_TO_INT (data)]; + GtkCssColor input1; + GtkCssColor input2; + GtkCssColor expected; + GtkCssColor result; + + color_from_string (test->input1, &input1); + color_from_string (test->input2, &input2); + color_from_string (test->expected, &expected); + + gtk_css_color_interpolate (&input1, + &input2, + test->progress, + test->in, + test->interp, + &result); + + if (g_test_verbose ()) + { + print_css_color ("expected", &expected); + print_css_color ("interpolated", &result); + } + + g_assert_true (color_is_near (&result, &expected)); +} + +int +main (int argc, char **argv) +{ + gtk_test_init (&argc, &argv); + + for (int i = 0; i < G_N_ELEMENTS (conversion_tests); i++) + { + char *path; + + path = g_strdup_printf ("/css/color/conversion/%d", i); + g_test_add_data_func (path, GINT_TO_POINTER (i), test_conversion); + g_free (path); + } + + for (int i = 0; i < G_N_ELEMENTS (interpolation_tests); i++) + { + char *path; + + path = g_strdup_printf ("/css/color/interpolation/%d", i); + g_test_add_data_func (path, GINT_TO_POINTER (i), test_interpolation); + g_free (path); + } + + return g_test_run (); +} diff --git a/testsuite/css/meson.build b/testsuite/css/meson.build index f9d8f9fc99..0b07714dd2 100644 --- a/testsuite/css/meson.build +++ b/testsuite/css/meson.build @@ -52,3 +52,15 @@ test('transition', transition, suite: 'css' ) +color = executable('color', + sources: ['color.c'], + c_args: common_cflags + ['-DGTK_COMPILATION'], + dependencies: libgtk_static_dep, +) + +test('color', color, + args: [ '--tap', '-k'], + protocol: 'tap', + env: csstest_env, + suite: 'css' +)