mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-09-19 21:40:22 +00:00
csscolorvalue: Implement color-mix()
This commit is contained in:
parent
f5783f1637
commit
743329a3af
@ -41,6 +41,7 @@ typedef enum {
|
||||
COLOR_TYPE_LITERAL,
|
||||
COLOR_TYPE_COLOR,
|
||||
COLOR_TYPE_NAME,
|
||||
COLOR_TYPE_COLOR_MIX,
|
||||
COLOR_TYPE_SHADE,
|
||||
COLOR_TYPE_ALPHA,
|
||||
COLOR_TYPE_MIX,
|
||||
@ -59,6 +60,16 @@ struct _GtkCssValue
|
||||
char *name;
|
||||
GtkCssColor color;
|
||||
|
||||
struct
|
||||
{
|
||||
GtkCssColorSpace color_space;
|
||||
GtkCssHueInterpolation hue_interpolation;
|
||||
GtkCssValue *color1;
|
||||
GtkCssValue *color2;
|
||||
float percentage1;
|
||||
float percentage2;
|
||||
} color_mix;
|
||||
|
||||
struct
|
||||
{
|
||||
GtkCssValue *color;
|
||||
@ -85,6 +96,11 @@ gtk_css_value_color_free (GtkCssValue *color)
|
||||
g_free (color->name);
|
||||
break;
|
||||
|
||||
case COLOR_TYPE_COLOR_MIX:
|
||||
gtk_css_value_unref (color->color_mix.color1);
|
||||
gtk_css_value_unref (color->color_mix.color2);
|
||||
break;
|
||||
|
||||
case COLOR_TYPE_SHADE:
|
||||
gtk_css_value_unref (color->shade.color);
|
||||
break;
|
||||
@ -214,6 +230,16 @@ gtk_css_value_color_equal (const GtkCssValue *value1,
|
||||
case COLOR_TYPE_NAME:
|
||||
return g_str_equal (value1->name, value2->name);
|
||||
|
||||
case COLOR_TYPE_COLOR_MIX:
|
||||
return value1->color_mix.color_space == value2->color_mix.color_space &&
|
||||
value1->color_mix.hue_interpolation == value2->color_mix.hue_interpolation &&
|
||||
value1->color_mix.percentage1 == value2->color_mix.percentage1 &&
|
||||
value1->color_mix.percentage2 == value2->color_mix.percentage2 &&
|
||||
gtk_css_value_equal (value1->color_mix.color1,
|
||||
value2->color_mix.color1) &&
|
||||
gtk_css_value_equal (value1->color_mix.color2,
|
||||
value2->color_mix.color2);
|
||||
|
||||
case COLOR_TYPE_SHADE:
|
||||
return value1->shade.factor == value2->shade.factor &&
|
||||
gtk_css_value_equal (value1->shade.color,
|
||||
@ -240,6 +266,50 @@ gtk_css_value_color_equal (const GtkCssValue *value1,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
normalize_color_mix_percentages (float p1,
|
||||
float p2,
|
||||
float *out_p1,
|
||||
float *out_p2,
|
||||
float *alpha_multiplier)
|
||||
{
|
||||
*alpha_multiplier = 1;
|
||||
|
||||
if (p1 < -0.5 && p2 < -0.5)
|
||||
{
|
||||
if (out_p1)
|
||||
*out_p1 = 50;
|
||||
if (out_p2)
|
||||
*out_p2 = 50;
|
||||
return;
|
||||
}
|
||||
|
||||
if (p1 < -0.5)
|
||||
{
|
||||
if (out_p1)
|
||||
*out_p1 = 100 - p2;
|
||||
if (out_p2)
|
||||
*out_p2 = p2;
|
||||
return;
|
||||
}
|
||||
|
||||
if (p2 < -0.5)
|
||||
{
|
||||
if (out_p1)
|
||||
*out_p1 = p1;
|
||||
if (out_p2)
|
||||
*out_p2 = 100 - p1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (out_p1)
|
||||
*out_p1 = p1 / (p1 + p2) * 100;
|
||||
if (out_p2)
|
||||
*out_p2 = p2 / (p1 + p2) * 100;
|
||||
if (alpha_multiplier)
|
||||
*alpha_multiplier = MIN (1, (p1 + p2) * 0.01);
|
||||
}
|
||||
|
||||
static GtkCssValue *
|
||||
gtk_css_value_color_transition (GtkCssValue *start,
|
||||
GtkCssValue *end,
|
||||
@ -270,6 +340,56 @@ gtk_css_value_color_print (const GtkCssValue *value,
|
||||
g_string_append (string, value->name);
|
||||
break;
|
||||
|
||||
case COLOR_TYPE_COLOR_MIX:
|
||||
{
|
||||
char percent[G_ASCII_DTOSTR_BUF_SIZE];
|
||||
float p1 = value->color_mix.percentage1;
|
||||
float p2 = value->color_mix.percentage2;
|
||||
gboolean p1_present = p1 > -0.5;
|
||||
gboolean p2_present = p2 > -0.5;
|
||||
gboolean defaults = (!p1_present || G_APPROX_VALUE (p1, 50, FLT_EPSILON)) &&
|
||||
(!p2_present || G_APPROX_VALUE (p2, 50, FLT_EPSILON));
|
||||
|
||||
if (!p1_present && p2_present && !defaults)
|
||||
{
|
||||
p1 = 100 - p2;
|
||||
p1_present = TRUE;
|
||||
p2_present = FALSE;
|
||||
}
|
||||
|
||||
g_string_append (string, "color-mix(");
|
||||
gtk_css_color_interpolation_method_print (value->color_mix.color_space,
|
||||
value->color_mix.hue_interpolation,
|
||||
string);
|
||||
|
||||
g_string_append (string, ", ");
|
||||
|
||||
gtk_css_value_print (value->color_mix.color1, string);
|
||||
|
||||
if (p1_present && !defaults)
|
||||
{
|
||||
g_ascii_dtostr (percent, sizeof (percent), p1);
|
||||
g_string_append_c (string, ' ');
|
||||
g_string_append (string, percent);
|
||||
g_string_append_c (string, '%');
|
||||
}
|
||||
|
||||
g_string_append (string, ", ");
|
||||
|
||||
gtk_css_value_print (value->color_mix.color2, string);
|
||||
|
||||
if (p2_present && !defaults)
|
||||
{
|
||||
g_ascii_dtostr (percent, sizeof (percent), p2);
|
||||
g_string_append_c (string, ' ');
|
||||
g_string_append (string, percent);
|
||||
g_string_append_c (string, '%');
|
||||
}
|
||||
|
||||
g_string_append (string, ")");
|
||||
}
|
||||
break;
|
||||
|
||||
case COLOR_TYPE_SHADE:
|
||||
g_string_append (string, "shade(");
|
||||
gtk_css_value_print (value->shade.color, string);
|
||||
@ -373,6 +493,51 @@ apply_mix (const GdkRGBA *in1,
|
||||
}
|
||||
}
|
||||
|
||||
static GtkCssValue *
|
||||
apply_color_mix (GtkCssColorSpace in,
|
||||
GtkCssHueInterpolation interp,
|
||||
const GtkCssValue *value1,
|
||||
const GtkCssValue *value2,
|
||||
float percentage1,
|
||||
float percentage2)
|
||||
{
|
||||
float p2, alpha_multiplier;
|
||||
GtkCssColor color1, color2, output;
|
||||
gboolean missing[4];
|
||||
int i;
|
||||
|
||||
if (value1->type == COLOR_TYPE_LITERAL)
|
||||
{
|
||||
gtk_css_color_init (&color1, GTK_CSS_COLOR_SPACE_SRGB,
|
||||
(float[4]) { value1->rgba.red, value1->rgba.green, value1->rgba.blue, value1->rgba.alpha });
|
||||
}
|
||||
else
|
||||
color1 = value1->color;
|
||||
|
||||
if (value2->type == COLOR_TYPE_LITERAL)
|
||||
{
|
||||
gtk_css_color_init (&color2, GTK_CSS_COLOR_SPACE_SRGB,
|
||||
(float[4]) { value2->rgba.red, value2->rgba.green, value2->rgba.blue, value2->rgba.alpha });
|
||||
}
|
||||
else
|
||||
color2 = value2->color;
|
||||
|
||||
normalize_color_mix_percentages (percentage1, percentage2,
|
||||
NULL, &p2, &alpha_multiplier);
|
||||
|
||||
gtk_css_color_interpolate (&color1, &color2, p2 * 0.01, in, interp, &output);
|
||||
|
||||
output.values[3] *= alpha_multiplier;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
missing[i] = gtk_css_color_component_missing (&output, i);
|
||||
|
||||
return gtk_css_color_value_new_color (output.color_space,
|
||||
FALSE,
|
||||
output.values,
|
||||
missing);
|
||||
}
|
||||
|
||||
static GtkCssValue *
|
||||
gtk_css_color_value_do_resolve (GtkCssValue *color,
|
||||
GtkStyleProvider *provider,
|
||||
@ -408,6 +573,24 @@ gtk_css_color_value_do_resolve (GtkCssValue *color,
|
||||
}
|
||||
break;
|
||||
|
||||
case COLOR_TYPE_COLOR_MIX:
|
||||
{
|
||||
GtkCssValue *val1, *val2;
|
||||
|
||||
val1 = gtk_css_color_value_do_resolve (color->color_mix.color1, provider, current, cycle_list);
|
||||
val2 = gtk_css_color_value_do_resolve (color->color_mix.color2, provider, current, cycle_list);
|
||||
|
||||
value = apply_color_mix (color->color_mix.color_space,
|
||||
color->color_mix.hue_interpolation,
|
||||
val1, val2,
|
||||
color->color_mix.percentage1,
|
||||
color->color_mix.percentage2);
|
||||
|
||||
gtk_css_value_unref (val1);
|
||||
gtk_css_value_unref (val2);
|
||||
}
|
||||
break;
|
||||
|
||||
case COLOR_TYPE_SHADE:
|
||||
{
|
||||
const GdkRGBA *c;
|
||||
@ -623,6 +806,41 @@ gtk_css_color_value_new_alpha (GtkCssValue *color,
|
||||
return value;
|
||||
}
|
||||
|
||||
static GtkCssValue *
|
||||
gtk_css_color_value_new_color_mix (GtkCssColorSpace color_space,
|
||||
GtkCssHueInterpolation hue_interpolation,
|
||||
GtkCssValue *color1,
|
||||
GtkCssValue *color2,
|
||||
float percentage1,
|
||||
float percentage2)
|
||||
{
|
||||
GtkCssValue *value;
|
||||
|
||||
gtk_internal_return_val_if_fail (color1->class == >K_CSS_VALUE_COLOR, NULL);
|
||||
gtk_internal_return_val_if_fail (color2->class == >K_CSS_VALUE_COLOR, NULL);
|
||||
|
||||
if (percentage1 > -0.5 && percentage2 > -0.5 && fabs (percentage1 + percentage2) < 0.001)
|
||||
return NULL;
|
||||
|
||||
if (color1->is_computed && color2->is_computed)
|
||||
{
|
||||
return apply_color_mix (color_space, hue_interpolation,
|
||||
color1, color2,
|
||||
percentage1, percentage2);
|
||||
}
|
||||
|
||||
value = gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_COLOR);
|
||||
value->type = COLOR_TYPE_COLOR_MIX;
|
||||
value->color_mix.color_space = color_space;
|
||||
value->color_mix.hue_interpolation = hue_interpolation;
|
||||
value->color_mix.color1 = gtk_css_value_ref (color1);
|
||||
value->color_mix.color2 = gtk_css_value_ref (color2);
|
||||
value->color_mix.percentage1 = percentage1;
|
||||
value->color_mix.percentage2 = percentage2;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static GtkCssValue *
|
||||
gtk_css_color_value_new_mix (GtkCssValue *color1,
|
||||
GtkCssValue *color2,
|
||||
@ -684,7 +902,90 @@ gtk_css_color_value_can_parse (GtkCssParser *parser)
|
||||
|| gtk_css_parser_has_function (parser, "hwb")
|
||||
|| gtk_css_parser_has_function (parser, "oklab")
|
||||
|| gtk_css_parser_has_function (parser, "oklch")
|
||||
|| gtk_css_parser_has_function (parser, "color");
|
||||
|| gtk_css_parser_has_function (parser, "color")
|
||||
|| gtk_css_parser_has_function (parser, "color-mix");
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkCssColorSpace color_space;
|
||||
GtkCssHueInterpolation hue_interpolation;
|
||||
GtkCssValue *color1;
|
||||
GtkCssValue *color2;
|
||||
float percentage1;
|
||||
float percentage2;
|
||||
} ColorMixData;
|
||||
|
||||
static gboolean
|
||||
parse_color_mix_component (GtkCssParser *parser,
|
||||
GtkCssValue **value,
|
||||
float *percentage)
|
||||
{
|
||||
*percentage = -1;
|
||||
|
||||
if (gtk_css_color_value_can_parse (parser))
|
||||
{
|
||||
*value = gtk_css_color_value_parse (parser);
|
||||
if (*value == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gtk_css_number_value_can_parse (parser))
|
||||
{
|
||||
GtkCssValue *val;
|
||||
val = gtk_css_number_value_parse (parser, GTK_CSS_PARSE_PERCENT | GTK_CSS_POSITIVE_ONLY);
|
||||
if (val == NULL)
|
||||
return FALSE;
|
||||
|
||||
*percentage = gtk_css_number_value_get_canonical (val, 100);
|
||||
|
||||
if (*percentage > 100)
|
||||
{
|
||||
gtk_css_parser_error_syntax (parser, "Values above 100%% are not allowed");
|
||||
gtk_css_value_unref (val);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gtk_css_value_unref (val);
|
||||
}
|
||||
|
||||
if (*value == NULL)
|
||||
{
|
||||
*value = gtk_css_color_value_parse (parser);
|
||||
if (*value == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
parse_color_mix (GtkCssParser *parser,
|
||||
guint arg,
|
||||
gpointer data_)
|
||||
{
|
||||
ColorMixData *data = data_;
|
||||
|
||||
switch (arg)
|
||||
{
|
||||
case 0:
|
||||
if (!gtk_css_color_interpolation_method_parse (parser, &data->color_space, &data->hue_interpolation))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
if (!parse_color_mix_component (parser, &data->color1, &data->percentage1))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 2:
|
||||
if (!parse_color_mix_component (parser, &data->color2, &data->percentage2))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@ -695,9 +996,9 @@ typedef struct
|
||||
} ColorFunctionData;
|
||||
|
||||
static guint
|
||||
parse_color_mix (GtkCssParser *parser,
|
||||
guint arg,
|
||||
gpointer data_)
|
||||
parse_legacy_mix (GtkCssParser *parser,
|
||||
guint arg,
|
||||
gpointer data_)
|
||||
{
|
||||
ColorFunctionData *data = data_;
|
||||
|
||||
@ -1485,6 +1786,31 @@ gtk_css_color_value_parse (GtkCssParser *parser)
|
||||
|
||||
return gtk_css_color_value_new_color (data.color_space, FALSE, data.values, data.missing);
|
||||
}
|
||||
else if (gtk_css_parser_has_function (parser, "color-mix"))
|
||||
{
|
||||
ColorMixData data;
|
||||
|
||||
data.color1 = NULL;
|
||||
data.color2 = NULL;
|
||||
|
||||
if (gtk_css_parser_consume_function (parser, 3, 3, parse_color_mix, &data))
|
||||
{
|
||||
value = gtk_css_color_value_new_color_mix (data.color_space,
|
||||
data.hue_interpolation,
|
||||
data.color1,
|
||||
data.color2,
|
||||
data.percentage1,
|
||||
data.percentage2);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = NULL;
|
||||
}
|
||||
|
||||
g_clear_pointer (&data.color1, gtk_css_value_unref);
|
||||
g_clear_pointer (&data.color2, gtk_css_value_unref);
|
||||
return value;
|
||||
}
|
||||
else if (gtk_css_parser_has_function (parser, "lighter"))
|
||||
{
|
||||
ColorFunctionData data = { NULL, };
|
||||
@ -1537,7 +1863,7 @@ gtk_css_color_value_parse (GtkCssParser *parser)
|
||||
{
|
||||
ColorFunctionData data = { NULL, };
|
||||
|
||||
if (gtk_css_parser_consume_function (parser, 3, 3, parse_color_mix, &data))
|
||||
if (gtk_css_parser_consume_function (parser, 3, 3, parse_legacy_mix, &data))
|
||||
value = gtk_css_color_value_new_mix (data.color, data.color2, data.value);
|
||||
else
|
||||
value = NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user