gdk_rgba_parse: Support HSL colors

This commit is contained in:
James Westman 2021-08-28 21:08:47 -05:00
parent 3d27ff1177
commit 0782c8a051
No known key found for this signature in database
GPG Key ID: CE2DBA0ADB654EA6
12 changed files with 195 additions and 40 deletions

View File

@ -17,12 +17,12 @@
#include "config.h" #include "config.h"
#include "gtkhslaprivate.h" #include "gdkhslaprivate.h"
#include <math.h> #include <math.h>
void void
_gtk_hsla_init_from_rgba (GtkHSLA *hsla, _gdk_hsla_init_from_rgba (GdkHSLA *hsla,
const GdkRGBA *rgba) const GdkRGBA *rgba)
{ {
float min; float min;
@ -31,21 +31,21 @@ _gtk_hsla_init_from_rgba (GtkHSLA *hsla,
float green; float green;
float blue; float blue;
float delta; float delta;
g_return_if_fail (hsla != NULL); g_return_if_fail (hsla != NULL);
g_return_if_fail (rgba != NULL); g_return_if_fail (rgba != NULL);
red = rgba->red; red = rgba->red;
green = rgba->green; green = rgba->green;
blue = rgba->blue; blue = rgba->blue;
if (red > green) if (red > green)
{ {
if (red > blue) if (red > blue)
max = red; max = red;
else else
max = blue; max = blue;
if (green < blue) if (green < blue)
min = green; min = green;
else else
@ -57,25 +57,25 @@ _gtk_hsla_init_from_rgba (GtkHSLA *hsla,
max = green; max = green;
else else
max = blue; max = blue;
if (red < blue) if (red < blue)
min = red; min = red;
else else
min = blue; min = blue;
} }
hsla->lightness = (max + min) / 2; hsla->lightness = (max + min) / 2;
hsla->saturation = 0; hsla->saturation = 0;
hsla->hue = 0; hsla->hue = 0;
hsla->alpha = rgba->alpha; hsla->alpha = rgba->alpha;
if (max != min) if (max != min)
{ {
if (hsla->lightness <= 0.5) if (hsla->lightness <= 0.5)
hsla->saturation = (max - min) / (max + min); hsla->saturation = (max - min) / (max + min);
else else
hsla->saturation = (max - min) / (2 - max - min); hsla->saturation = (max - min) / (2 - max - min);
delta = max -min; delta = max -min;
if (red == max) if (red == max)
hsla->hue = (green - blue) / delta; hsla->hue = (green - blue) / delta;
@ -83,7 +83,7 @@ _gtk_hsla_init_from_rgba (GtkHSLA *hsla,
hsla->hue = 2 + (blue - red) / delta; hsla->hue = 2 + (blue - red) / delta;
else if (blue == max) else if (blue == max)
hsla->hue = 4 + (red - green) / delta; hsla->hue = 4 + (red - green) / delta;
hsla->hue *= 60; hsla->hue *= 60;
if (hsla->hue < 0.0) if (hsla->hue < 0.0)
hsla->hue += 360; hsla->hue += 360;
@ -92,22 +92,22 @@ _gtk_hsla_init_from_rgba (GtkHSLA *hsla,
void void
_gdk_rgba_init_from_hsla (GdkRGBA *rgba, _gdk_rgba_init_from_hsla (GdkRGBA *rgba,
const GtkHSLA *hsla) const GdkHSLA *hsla)
{ {
float hue; float hue;
float lightness; float lightness;
float saturation; float saturation;
float m1, m2; float m1, m2;
lightness = hsla->lightness; lightness = hsla->lightness;
saturation = hsla->saturation; saturation = hsla->saturation;
if (lightness <= 0.5) if (lightness <= 0.5)
m2 = lightness * (1 + saturation); m2 = lightness * (1 + saturation);
else else
m2 = lightness + saturation - lightness * saturation; m2 = lightness + saturation - lightness * saturation;
m1 = 2 * lightness - m2; m1 = 2 * lightness - m2;
rgba->alpha = hsla->alpha; rgba->alpha = hsla->alpha;
if (saturation == 0) if (saturation == 0)
@ -123,7 +123,7 @@ _gdk_rgba_init_from_hsla (GdkRGBA *rgba,
hue -= 360; hue -= 360;
while (hue < 0) while (hue < 0)
hue += 360; hue += 360;
if (hue < 60) if (hue < 60)
rgba->red = m1 + (m2 - m1) * hue / 60; rgba->red = m1 + (m2 - m1) * hue / 60;
else if (hue < 180) else if (hue < 180)
@ -132,13 +132,13 @@ _gdk_rgba_init_from_hsla (GdkRGBA *rgba,
rgba->red = m1 + (m2 - m1) * (240 - hue) / 60; rgba->red = m1 + (m2 - m1) * (240 - hue) / 60;
else else
rgba->red = m1; rgba->red = m1;
hue = hsla->hue; hue = hsla->hue;
while (hue > 360) while (hue > 360)
hue -= 360; hue -= 360;
while (hue < 0) while (hue < 0)
hue += 360; hue += 360;
if (hue < 60) if (hue < 60)
rgba->green = m1 + (m2 - m1) * hue / 60; rgba->green = m1 + (m2 - m1) * hue / 60;
else if (hue < 180) else if (hue < 180)
@ -147,13 +147,13 @@ _gdk_rgba_init_from_hsla (GdkRGBA *rgba,
rgba->green = m1 + (m2 - m1) * (240 - hue) / 60; rgba->green = m1 + (m2 - m1) * (240 - hue) / 60;
else else
rgba->green = m1; rgba->green = m1;
hue = hsla->hue - 120; hue = hsla->hue - 120;
while (hue > 360) while (hue > 360)
hue -= 360; hue -= 360;
while (hue < 0) while (hue < 0)
hue += 360; hue += 360;
if (hue < 60) if (hue < 60)
rgba->blue = m1 + (m2 - m1) * hue / 60; rgba->blue = m1 + (m2 - m1) * hue / 60;
else if (hue < 180) else if (hue < 180)
@ -166,8 +166,8 @@ _gdk_rgba_init_from_hsla (GdkRGBA *rgba,
} }
void void
_gtk_hsla_shade (GtkHSLA *dest, _gdk_hsla_shade (GdkHSLA *dest,
const GtkHSLA *src, const GdkHSLA *src,
float factor) float factor)
{ {
g_return_if_fail (dest != NULL); g_return_if_fail (dest != NULL);

View File

@ -22,23 +22,23 @@
G_BEGIN_DECLS G_BEGIN_DECLS
typedef struct _GtkHSLA GtkHSLA; typedef struct _GdkHSLA GdkHSLA;
struct _GtkHSLA { struct _GdkHSLA {
float hue; float hue;
float saturation; float saturation;
float lightness; float lightness;
float alpha; float alpha;
}; };
void _gtk_hsla_init_from_rgba (GtkHSLA *hsla, void _gdk_hsla_init_from_rgba (GdkHSLA *hsla,
const GdkRGBA *rgba); const GdkRGBA *rgba);
/* Yes, I can name that function like this! */ /* Yes, I can name that function like this! */
void _gdk_rgba_init_from_hsla (GdkRGBA *rgba, void _gdk_rgba_init_from_hsla (GdkRGBA *rgba,
const GtkHSLA *hsla); const GdkHSLA *hsla);
void _gtk_hsla_shade (GtkHSLA *dest, void _gdk_hsla_shade (GdkHSLA *dest,
const GtkHSLA *src, const GdkHSLA *src,
float factor); float factor);
G_END_DECLS G_END_DECLS

View File

@ -30,6 +30,7 @@
#include <errno.h> #include <errno.h>
#include <math.h> #include <math.h>
#include "gdkhslaprivate.h"
G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba, G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba,
gdk_rgba_copy, gdk_rgba_free) gdk_rgba_copy, gdk_rgba_free)
@ -186,6 +187,7 @@ gdk_rgba_parse (GdkRGBA *rgba,
const char *spec) const char *spec)
{ {
gboolean has_alpha; gboolean has_alpha;
gboolean is_hsl;
double r, g, b, a; double r, g, b, a;
char *str = (char *) spec; char *str = (char *) spec;
char *p; char *p;
@ -196,11 +198,26 @@ gdk_rgba_parse (GdkRGBA *rgba,
if (strncmp (str, "rgba", 4) == 0) if (strncmp (str, "rgba", 4) == 0)
{ {
has_alpha = TRUE; has_alpha = TRUE;
is_hsl = FALSE;
str += 4; str += 4;
} }
else if (strncmp (str, "rgb", 3) == 0) else if (strncmp (str, "rgb", 3) == 0)
{ {
has_alpha = FALSE; has_alpha = FALSE;
is_hsl = FALSE;
a = 1;
str += 3;
}
else if (strncmp (str, "hsla", 4) == 0)
{
has_alpha = TRUE;
is_hsl = TRUE;
str += 4;
}
else if (strncmp (str, "hsl", 3) == 0)
{
has_alpha = FALSE;
is_hsl = TRUE;
a = 1; a = 1;
str += 3; str += 3;
} }
@ -291,10 +308,22 @@ gdk_rgba_parse (GdkRGBA *rgba,
if (rgba) if (rgba)
{ {
rgba->red = CLAMP (r, 0, 1); if (is_hsl)
rgba->green = CLAMP (g, 0, 1); {
rgba->blue = CLAMP (b, 0, 1); GdkHSLA hsla;
rgba->alpha = CLAMP (a, 0, 1); hsla.hue = r * 255;
hsla.saturation = CLAMP (g, 0, 1);
hsla.lightness = CLAMP (b, 0, 1);
hsla.alpha = CLAMP (a, 0, 1);
_gdk_rgba_init_from_hsla (rgba, &hsla);
}
else
{
rgba->red = CLAMP (r, 0, 1);
rgba->green = CLAMP (g, 0, 1);
rgba->blue = CLAMP (b, 0, 1);
rgba->alpha = CLAMP (a, 0, 1);
}
} }
return TRUE; return TRUE;
@ -462,6 +491,47 @@ parse_color_channel (GtkCssParser *parser,
} }
} }
static guint
parse_hsla_color_channel (GtkCssParser *parser,
guint arg,
gpointer data)
{
GdkHSLA *hsla = data;
double dvalue;
switch (arg)
{
case 0:
if (!gtk_css_parser_consume_number (parser, &dvalue))
return 0;
hsla->hue = dvalue;
return 1;
case 1:
if (!gtk_css_parser_consume_percentage (parser, &dvalue))
return 0;
hsla->saturation = CLAMP (dvalue, 0.0, 100.0) / 100.0;
return 1;
case 2:
if (!gtk_css_parser_consume_percentage (parser, &dvalue))
return 0;
hsla->lightness = CLAMP (dvalue, 0.0, 100.0) / 100.0;
return 1;
case 3:
if (!gtk_css_parser_consume_number (parser, &dvalue))
return 0;
hsla->alpha = CLAMP (dvalue, 0.0, 1.0) / 1.0;
return 1;
default:
g_assert_not_reached ();
return 0;
}
}
static gboolean static gboolean
rgba_init_chars (GdkRGBA *rgba, rgba_init_chars (GdkRGBA *rgba,
const char s[8]) const char s[8])
@ -501,6 +571,18 @@ gdk_rgba_parser_parse (GtkCssParser *parser,
{ {
return gtk_css_parser_consume_function (parser, 4, 4, parse_color_channel, rgba); return gtk_css_parser_consume_function (parser, 4, 4, parse_color_channel, rgba);
} }
else if (gtk_css_token_is_function (token, "hsl") || gtk_css_token_is_function (token, "hsla"))
{
GdkHSLA hsla;
hsla.alpha = 1.0;
if (!gtk_css_parser_consume_function (parser, 3, 4, parse_hsla_color_channel, &hsla))
return FALSE;
_gdk_rgba_init_from_hsla (rgba, &hsla);
return TRUE;
}
else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID) || else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID) ||
gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_UNRESTRICTED)) gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_UNRESTRICTED))
{ {

View File

@ -27,6 +27,7 @@ gdk_public_sources = files([
'gdkglcontext.c', 'gdkglcontext.c',
'gdkglobals.c', 'gdkglobals.c',
'gdkgltexture.c', 'gdkgltexture.c',
'gdkhsla.c',
'gdkkeys.c', 'gdkkeys.c',
'gdkkeyuni.c', 'gdkkeyuni.c',
'gdkmemorytexture.c', 'gdkmemorytexture.c',
@ -107,6 +108,7 @@ gdk_sources = gdk_public_sources
gdk_private_h_sources = files([ gdk_private_h_sources = files([
'gdkeventsprivate.h', 'gdkeventsprivate.h',
'gdkdevicetoolprivate.h', 'gdkdevicetoolprivate.h',
'gdkhslaprivate.h',
'gdkmonitorprivate.h', 'gdkmonitorprivate.h',
'gdkseatdefaultprivate.h', 'gdkseatdefaultprivate.h',
'gdktoplevelsizeprivate.h', 'gdktoplevelsizeprivate.h',

View File

@ -20,10 +20,10 @@
#include "gtkcsscolorvalueprivate.h" #include "gtkcsscolorvalueprivate.h"
#include "gtkcssstylepropertyprivate.h" #include "gtkcssstylepropertyprivate.h"
#include "gtkhslaprivate.h"
#include "gtkprivate.h" #include "gtkprivate.h"
#include "gtkstylepropertyprivate.h" #include "gtkstylepropertyprivate.h"
#include "gdk/gdkhslaprivate.h"
#include "gdk/gdkrgbaprivate.h" #include "gdk/gdkrgbaprivate.h"
typedef enum { typedef enum {
@ -309,10 +309,10 @@ apply_shade (const GdkRGBA *in,
GdkRGBA *out, GdkRGBA *out,
double factor) double factor)
{ {
GtkHSLA hsla; GdkHSLA hsla;
_gtk_hsla_init_from_rgba (&hsla, in); _gdk_hsla_init_from_rgba (&hsla, in);
_gtk_hsla_shade (&hsla, &hsla, factor); _gdk_hsla_shade (&hsla, &hsla, factor);
_gdk_rgba_init_from_hsla (out, &hsla); _gdk_rgba_init_from_hsla (out, &hsla);
} }
@ -699,6 +699,8 @@ gtk_css_color_value_can_parse (GtkCssParser *parser)
|| gtk_css_parser_has_function (parser, "shade") || gtk_css_parser_has_function (parser, "shade")
|| gtk_css_parser_has_function (parser, "alpha") || gtk_css_parser_has_function (parser, "alpha")
|| gtk_css_parser_has_function (parser, "mix") || gtk_css_parser_has_function (parser, "mix")
|| gtk_css_parser_has_function (parser, "hsl")
|| gtk_css_parser_has_function (parser, "hsla")
|| gtk_css_parser_has_function (parser, "rgb") || gtk_css_parser_has_function (parser, "rgb")
|| gtk_css_parser_has_function (parser, "rgba"); || gtk_css_parser_has_function (parser, "rgba");
} }

View File

@ -33,10 +33,10 @@
#include "gtkcssrepeatvalueprivate.h" #include "gtkcssrepeatvalueprivate.h"
#include "gtkcsscolorvalueprivate.h" #include "gtkcsscolorvalueprivate.h"
#include "gtkcssstyleprivate.h" #include "gtkcssstyleprivate.h"
#include "gtkhslaprivate.h"
#include "gtkroundedboxprivate.h" #include "gtkroundedboxprivate.h"
#include "gtksnapshotprivate.h" #include "gtksnapshotprivate.h"
#include "gdk/gdkhslaprivate.h"
#include "gsk/gskroundedrectprivate.h" #include "gsk/gskroundedrectprivate.h"
typedef struct _GtkBorderImage GtkBorderImage; typedef struct _GtkBorderImage GtkBorderImage;
@ -513,10 +513,10 @@ color_shade (const GdkRGBA *color,
double factor, double factor,
GdkRGBA *color_return) GdkRGBA *color_return)
{ {
GtkHSLA hsla; GdkHSLA hsla;
_gtk_hsla_init_from_rgba (&hsla, color); _gdk_hsla_init_from_rgba (&hsla, color);
_gtk_hsla_shade (&hsla, &hsla, factor); _gdk_hsla_shade (&hsla, &hsla, factor);
_gdk_rgba_init_from_hsla (color_return, &hsla); _gdk_rgba_init_from_hsla (color_return, &hsla);
} }

View File

@ -111,7 +111,6 @@ gtk_private_sources = files([
'gtkfilechooserutils.c', 'gtkfilechooserutils.c',
'gtkfilesystemmodel.c', 'gtkfilesystemmodel.c',
'gtkgizmo.c', 'gtkgizmo.c',
'gtkhsla.c',
'gtkiconcache.c', 'gtkiconcache.c',
'gtkiconcachevalidator.c', 'gtkiconcachevalidator.c',
'gtkiconhelper.c', 'gtkiconhelper.c',

View File

@ -0,0 +1,23 @@
a {
color: hsl(0, 0%, 0%);
}
b {
color: hsl(120, 100%, 50%);
}
c {
color: hsl(360, 100%, 50%);
}
d {
color: hsl(-314.159, 50%, 50%);
}
e {
color: hsl(0, 0%, 0%, 0.5);
}
f {
color: hsl(1, 2, 3);
}

View File

@ -0,0 +1 @@
hsl.css:22:17-18: error: GTK_CSS_PARSER_ERROR_SYNTAX

View File

@ -0,0 +1,19 @@
a {
color: rgb(0,0,0);
}
b {
color: rgb(0,255,0);
}
c {
color: rgb(255,0,0);
}
d {
color: rgb(191,161,64);
}
e {
color: rgba(0,0,0,0.5);
}

View File

@ -358,6 +358,9 @@ test_data = [
'freed-string-in-error-messages.css', 'freed-string-in-error-messages.css',
'freed-string-in-error-messages.errors', 'freed-string-in-error-messages.errors',
'freed-string-in-error-messages.ref.css', 'freed-string-in-error-messages.ref.css',
'hsl.css',
'hsl.errors',
'hsl.ref.css',
'import-cyclic-1.css', 'import-cyclic-1.css',
'import-cyclic-1.errors', 'import-cyclic-1.errors',
'import-cyclic-1.ref.css', 'import-cyclic-1.ref.css',

View File

@ -65,6 +65,30 @@ test_color_parse (void)
res = gdk_rgba_parse (&color, "#0080ff88"); res = gdk_rgba_parse (&color, "#0080ff88");
g_assert_true (res); g_assert_true (res);
g_assert_true (gdk_rgba_equal (&color, &expected)); g_assert_true (gdk_rgba_equal (&color, &expected));
expected.red = 1.0;
expected.green = 0.0;
expected.blue = 0.0;
expected.alpha = 1.0;
res = gdk_rgba_parse (&color, "hsl (0, 100%, 50%)");
g_assert_true (res);
g_assert_true (gdk_rgba_equal (&color, &expected));
expected.red = 0.0;
expected.green = 1.0;
expected.blue = 0.0;
expected.alpha = 0.1;
res = gdk_rgba_parse (&color, "hsla (120, 255, 50%, 0.1)");
g_assert_true (res);
g_assert_true (gdk_rgba_equal (&color, &expected));
expected.red = 0.0;
expected.green = 0.5;
expected.blue = 0.5;
expected.alpha = 1.0;
res = gdk_rgba_parse (&color, "hsl(180, 100%, 25%)");
g_assert_true (res);
g_assert_true (gdk_rgba_equal (&color, &expected));
} }
static void static void