css: Support some color spaces
Support the hwb(), oklab() and oklch() functions for specifying colors in these color spaces. See https://bottosson.github.io/posts/oklab/ and https://www.w3.org/TR/css-color-4/. Some tests included.
This commit is contained in:
@@ -128,8 +128,8 @@ install_headers(gdk_deprecated_headers, subdir: 'gtk-4.0/gdk/deprecated')
|
||||
gdk_sources = gdk_public_sources + gdk_deprecated_sources
|
||||
|
||||
gdk_private_h_sources = files([
|
||||
'gdkeventsprivate.h',
|
||||
'gdkdevicetoolprivate.h',
|
||||
'gdkeventsprivate.h',
|
||||
'gdkhslaprivate.h',
|
||||
'gdkmonitorprivate.h',
|
||||
'gdkseatdefaultprivate.h',
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gtkcolorutils.h"
|
||||
#include "gtkcolorutilsprivate.h"
|
||||
#include "gdkhslaprivate.h"
|
||||
#include <math.h>
|
||||
|
||||
/* Converts from RGB to HSV */
|
||||
static void
|
||||
@@ -226,3 +228,135 @@ gtk_rgb_to_hsv (float r, float g, float b,
|
||||
if (v)
|
||||
*v = b;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_rgb_to_hwb (float red, float green, float blue,
|
||||
float *hue, float *white, float *black)
|
||||
{
|
||||
GdkRGBA rgba = (GdkRGBA) { red, green, blue, 1 };
|
||||
GdkHSLA hsla;
|
||||
|
||||
_gdk_hsla_init_from_rgba (&hsla, &rgba);
|
||||
|
||||
*hue = hsla.hue;
|
||||
*white = MIN (MIN (red, green), blue);
|
||||
*black = (1 - MAX (MAX (red, green), blue));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_hwb_to_rgb (float hue, float white, float black,
|
||||
float *red, float *green, float *blue)
|
||||
{
|
||||
GdkHSLA hsla;
|
||||
GdkRGBA rgba;
|
||||
|
||||
if (white + black >= 1)
|
||||
{
|
||||
float gray = white / (white + black);
|
||||
|
||||
*red = gray;
|
||||
*green = gray;
|
||||
*blue = gray;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
hsla.hue = hue;
|
||||
hsla.saturation = 1.0;
|
||||
hsla.lightness = 0.5;
|
||||
|
||||
_gdk_rgba_init_from_hsla (&rgba, &hsla);
|
||||
|
||||
*red = rgba.red * (1 - white - black) + white;
|
||||
*green = rgba.green * (1 - white - black) + white;
|
||||
*blue = rgba.blue * (1 - white - black) + white;
|
||||
}
|
||||
|
||||
#define DEG_TO_RAD(x) ((x) * G_PI / 180)
|
||||
#define RAD_TO_DEG(x) ((x) * 180 / G_PI)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
static float
|
||||
apply_gamma (float v)
|
||||
{
|
||||
if (v > 0.0031308)
|
||||
return 1.055 * pow (v, 1/2.4) - 0.055;
|
||||
else
|
||||
return 12.92 * v;
|
||||
}
|
||||
|
||||
static float
|
||||
unapply_gamma (float v)
|
||||
{
|
||||
if (v >= 0.04045)
|
||||
return pow (((v + 0.055)/(1 + 0.055)), 2.4);
|
||||
else
|
||||
return v / 12.92;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_oklab_to_rgb (float L, float a, float b,
|
||||
float *red, float *green, float *blue)
|
||||
{
|
||||
float l = L + 0.3963377774f * a + 0.2158037573f * b;
|
||||
float m = L - 0.1055613458f * a - 0.0638541728f * b;
|
||||
float s = L - 0.0894841775f * a - 1.2914855480f * b;
|
||||
|
||||
l = powf (l, 3);
|
||||
m = powf (m, 3);
|
||||
s = powf (s, 3);
|
||||
|
||||
float linear_red = +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s;
|
||||
float linear_green = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s;
|
||||
float linear_blue = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s;
|
||||
|
||||
*red = apply_gamma (linear_red);
|
||||
*green = apply_gamma (linear_green);
|
||||
*blue = apply_gamma (linear_blue);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_rgb_to_oklab (float red, float green, float blue,
|
||||
float *L, float *a, float *b)
|
||||
{
|
||||
float linear_red = unapply_gamma (red);
|
||||
float linear_green = unapply_gamma (green);
|
||||
float linear_blue = unapply_gamma (blue);
|
||||
|
||||
float l = 0.4122214708f * linear_red + 0.5363325363f * linear_green + 0.0514459929f * linear_blue;
|
||||
float m = 0.2119034982f * linear_red + 0.6806995451f * linear_green + 0.1073969566f * linear_blue;
|
||||
float s = 0.0883024619f * linear_red + 0.2817188376f * linear_green + 0.6299787005f * linear_blue;
|
||||
|
||||
l = cbrtf (l);
|
||||
m = cbrtf (m);
|
||||
s = cbrtf (s);
|
||||
|
||||
*L = 0.2104542553f*l + 0.7936177850f*m - 0.0040720468f*s;
|
||||
*a = 1.9779984951f*l - 2.4285922050f*m + 0.4505937099f*s;
|
||||
*b = 0.0259040371f*l + 0.7827717662f*m - 0.8086757660f*s;
|
||||
}
|
||||
|
||||
40
gtk/gtkcolorutilsprivate.h
Normal file
40
gtk/gtkcolorutilsprivate.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 2024 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gtkcolorutils.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gtk_rgb_to_hwb (float red, float green, float blue,
|
||||
float *hue, float *white, float *black);
|
||||
void gtk_hwb_to_rgb (float hue, float white, float black,
|
||||
float *red, float *green, float *blue);
|
||||
|
||||
void gtk_oklab_to_oklch (float L, float a, float b,
|
||||
float *L2, float *C, float *H);
|
||||
void gtk_oklch_to_oklab (float L, float C, float H,
|
||||
float *L2, float *a, float *b);
|
||||
|
||||
void gtk_oklab_to_rgb (float L, float a, float b,
|
||||
float *red, float *green, float *blue);
|
||||
void gtk_rgb_to_oklab (float red, float green, float blue,
|
||||
float *L, float *a, float *b);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "gdk/gdkhslaprivate.h"
|
||||
#include "gdk/gdkrgbaprivate.h"
|
||||
#include "gtkcolorutilsprivate.h"
|
||||
|
||||
typedef enum {
|
||||
COLOR_TYPE_LITERAL,
|
||||
@@ -694,7 +695,10 @@ gtk_css_color_value_can_parse (GtkCssParser *parser)
|
||||
|| 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, "rgba");
|
||||
|| gtk_css_parser_has_function (parser, "rgba")
|
||||
|| gtk_css_parser_has_function (parser, "hwb")
|
||||
|| gtk_css_parser_has_function (parser, "oklab")
|
||||
|| gtk_css_parser_has_function (parser, "oklch");
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@@ -800,6 +804,25 @@ parse_hsl_channel_value (GtkCssParser *parser,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_hwb_channel_value (GtkCssParser *parser,
|
||||
float *value)
|
||||
{
|
||||
GtkCssNumberParseFlags flags = GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_NUMBER;
|
||||
GtkCssValue *val;
|
||||
|
||||
val = gtk_css_number_value_parse (parser, flags);
|
||||
if (val == NULL)
|
||||
return FALSE;
|
||||
|
||||
*value = gtk_css_number_value_get_canonical (val, 100);
|
||||
*value = CLAMP (*value, 0.0, 100.0);
|
||||
|
||||
gtk_css_value_unref (val);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_hue_value (GtkCssParser *parser,
|
||||
float *value)
|
||||
@@ -817,6 +840,62 @@ parse_hue_value (GtkCssParser *parser,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_ok_L_value (GtkCssParser *parser,
|
||||
float *value)
|
||||
{
|
||||
GtkCssNumberParseFlags flags = GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_NUMBER;
|
||||
GtkCssValue *val;
|
||||
|
||||
val = gtk_css_number_value_parse (parser, flags);
|
||||
if (val == NULL)
|
||||
return FALSE;
|
||||
|
||||
*value = gtk_css_number_value_get_canonical (val, 1);
|
||||
*value = CLAMP (*value, 0.0, 1.0);
|
||||
|
||||
gtk_css_value_unref (val);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_ok_C_value (GtkCssParser *parser,
|
||||
float *value)
|
||||
{
|
||||
GtkCssNumberParseFlags flags = GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_NUMBER;
|
||||
GtkCssValue *val;
|
||||
|
||||
val = gtk_css_number_value_parse (parser, flags);
|
||||
if (val == NULL)
|
||||
return FALSE;
|
||||
|
||||
*value = gtk_css_number_value_get_canonical (val, 1);
|
||||
*value = MAX (*value, 0.0);
|
||||
|
||||
gtk_css_value_unref (val);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_ok_ab_value (GtkCssParser *parser,
|
||||
float *value)
|
||||
{
|
||||
GtkCssNumberParseFlags flags = GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_NUMBER;
|
||||
GtkCssValue *val;
|
||||
|
||||
val = gtk_css_number_value_parse (parser, flags);
|
||||
if (val == NULL)
|
||||
return FALSE;
|
||||
|
||||
*value = gtk_css_number_value_get_canonical (val, 0.4);
|
||||
|
||||
gtk_css_value_unref (val);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
parse_rgba_color_channel (GtkCssParser *parser,
|
||||
guint arg,
|
||||
@@ -890,8 +969,129 @@ parse_hsla_color_channel (GtkCssParser *parser,
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
float hue, white, black, alpha;
|
||||
} HwbData;
|
||||
|
||||
static guint
|
||||
parse_hwb_color_channel (GtkCssParser *parser,
|
||||
guint arg,
|
||||
ColorSyntax syntax,
|
||||
gpointer data)
|
||||
{
|
||||
HwbData *hwb = data;
|
||||
|
||||
switch (arg)
|
||||
{
|
||||
case 0:
|
||||
if (!parse_hue_value (parser, &hwb->hue))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
if (!parse_hwb_channel_value (parser, &hwb->white))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 2:
|
||||
if (!parse_hwb_channel_value (parser, &hwb->black))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 3:
|
||||
if (!parse_alpha_value (parser, &hwb->alpha, syntax))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
float L, a, b, alpha;
|
||||
} LabData;
|
||||
|
||||
static guint
|
||||
parse_oklab_color_channel (GtkCssParser *parser,
|
||||
guint arg,
|
||||
ColorSyntax syntax,
|
||||
gpointer data)
|
||||
{
|
||||
LabData *oklab = data;
|
||||
|
||||
switch (arg)
|
||||
{
|
||||
case 0:
|
||||
if (!parse_ok_L_value (parser, &oklab->L))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
if (!parse_ok_ab_value (parser, &oklab->a))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 2:
|
||||
if (!parse_ok_ab_value (parser, &oklab->b))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 3:
|
||||
if (!parse_alpha_value (parser, &oklab->alpha, syntax))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
float L, C, H, alpha;
|
||||
} LchData;
|
||||
|
||||
static guint
|
||||
parse_oklch_color_channel (GtkCssParser *parser,
|
||||
guint arg,
|
||||
ColorSyntax syntax,
|
||||
gpointer data)
|
||||
{
|
||||
LchData *oklch = data;
|
||||
|
||||
switch (arg)
|
||||
{
|
||||
case 0:
|
||||
if (!parse_ok_L_value (parser, &oklch->L))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 1:
|
||||
if (!parse_ok_C_value (parser, &oklch->C))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 2:
|
||||
if (!parse_hue_value (parser, &oklch->H))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
case 3:
|
||||
if (!parse_alpha_value (parser, &oklch->alpha, syntax))
|
||||
return 0;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_color_function (GtkCssParser *self,
|
||||
ColorSyntax syntax,
|
||||
gboolean allow_alpha,
|
||||
gboolean require_alpha,
|
||||
guint (* parse_func) (GtkCssParser *, guint, ColorSyntax, gpointer),
|
||||
@@ -903,7 +1103,6 @@ parse_color_function (GtkCssParser *self,
|
||||
guint arg;
|
||||
guint min_args = 3;
|
||||
guint max_args = 4;
|
||||
ColorSyntax syntax = COLOR_SYNTAX_DETECTING;
|
||||
|
||||
token = gtk_css_parser_get_token (self);
|
||||
g_return_val_if_fail (gtk_css_token_is (token, GTK_CSS_TOKEN_FUNCTION), FALSE);
|
||||
@@ -1029,7 +1228,7 @@ gtk_css_color_value_parse (GtkCssParser *parser)
|
||||
|
||||
has_alpha = gtk_css_parser_has_function (parser, "rgba");
|
||||
|
||||
if (!parse_color_function (parser, has_alpha, has_alpha, parse_rgba_color_channel, &data))
|
||||
if (!parse_color_function (parser, COLOR_SYNTAX_DETECTING, has_alpha, has_alpha, parse_rgba_color_channel, &data))
|
||||
return NULL;
|
||||
|
||||
return gtk_css_color_value_new_literal (&rgba);
|
||||
@@ -1040,11 +1239,65 @@ gtk_css_color_value_parse (GtkCssParser *parser)
|
||||
|
||||
hsla.alpha = 1.0;
|
||||
|
||||
if (!parse_color_function (parser, TRUE, FALSE, parse_hsla_color_channel, &hsla))
|
||||
if (!parse_color_function (parser, COLOR_SYNTAX_DETECTING, TRUE, FALSE, parse_hsla_color_channel, &hsla))
|
||||
return NULL;
|
||||
|
||||
_gdk_rgba_init_from_hsla (&rgba, &hsla);
|
||||
|
||||
return gtk_css_color_value_new_literal (&rgba);
|
||||
}
|
||||
else if (gtk_css_parser_has_function (parser, "hwb"))
|
||||
{
|
||||
HwbData hwb = { 0, };
|
||||
float red, green, blue;
|
||||
|
||||
hwb.alpha = 1.0;
|
||||
|
||||
if (!parse_color_function (parser, COLOR_SYNTAX_MODERN, TRUE, FALSE, parse_hwb_color_channel, &hwb))
|
||||
return NULL;
|
||||
|
||||
hwb.white /= 100.0;
|
||||
hwb.black /= 100.0;
|
||||
|
||||
gtk_hwb_to_rgb (hwb.hue, hwb.white, hwb.black, &red, &green, &blue);
|
||||
|
||||
rgba.red = red;
|
||||
rgba.green = green;
|
||||
rgba.blue = blue;
|
||||
rgba.alpha = hwb.alpha;
|
||||
|
||||
return gtk_css_color_value_new_literal (&rgba);
|
||||
}
|
||||
else if (gtk_css_parser_has_function (parser, "oklab"))
|
||||
{
|
||||
LabData oklab = { 0, };
|
||||
|
||||
oklab.alpha = 1.0;
|
||||
|
||||
if (!parse_color_function (parser, COLOR_SYNTAX_MODERN, TRUE, FALSE, parse_oklab_color_channel, &oklab))
|
||||
return NULL;
|
||||
|
||||
gtk_oklab_to_rgb (oklab.L, oklab.a, oklab.b, &rgba.red, &rgba.green, &rgba.blue);
|
||||
rgba.alpha = oklab.alpha;
|
||||
|
||||
return gtk_css_color_value_new_literal (&rgba);
|
||||
}
|
||||
else if (gtk_css_parser_has_function (parser, "oklch"))
|
||||
{
|
||||
LchData oklch = { 0, };
|
||||
float L, a, b;
|
||||
float red, green, blue;
|
||||
|
||||
oklch.alpha = 1.0;
|
||||
|
||||
if (!parse_color_function (parser, COLOR_SYNTAX_MODERN, TRUE, FALSE, parse_oklch_color_channel, &oklch))
|
||||
return NULL;
|
||||
|
||||
gtk_oklch_to_oklab (oklch.L, oklch.C, oklch.H, &L, &a, &b);
|
||||
gtk_oklab_to_rgb (L, a, b, &red, &green, &blue);
|
||||
|
||||
rgba.alpha = oklch.alpha;
|
||||
|
||||
return gtk_css_color_value_new_literal (&rgba);
|
||||
}
|
||||
else if (gtk_css_parser_has_function (parser, "lighter"))
|
||||
|
||||
@@ -14,39 +14,36 @@
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <gtk/gtk.h>
|
||||
#include <gtk/gtkcolorutils.h>
|
||||
|
||||
struct {
|
||||
float r, g, b;
|
||||
float h, s, v;
|
||||
} tests[] = {
|
||||
{ 0, 0, 0, 0, 0, 0 },
|
||||
{ 1, 1, 1, 0, 0, 1 },
|
||||
{ 1, 0, 0, 0, 1, 1 },
|
||||
{ 1, 1, 0, 1.0 / 6.0, 1, 1 },
|
||||
{ 0, 1, 0, 2.0 / 6.0, 1, 1 },
|
||||
{ 0, 1, 1, 3.0 / 6.0, 1, 1 },
|
||||
{ 0, 0, 1, 4.0 / 6.0, 1, 1 },
|
||||
{ 1, 0, 1, 5.0 / 6.0, 1, 1 },
|
||||
};
|
||||
|
||||
/* Close enough for float precision to match, even with some
|
||||
* rounding errors */
|
||||
#define EPSILON 1e-6
|
||||
#include <gtk/gtkcolorutilsprivate.h>
|
||||
|
||||
static void
|
||||
test_roundtrips (void)
|
||||
test_roundtrips_rgb_hsv (void)
|
||||
{
|
||||
struct {
|
||||
float r, g, b;
|
||||
float h, s, v;
|
||||
} tests[] = {
|
||||
{ 0, 0, 0, 0, 0, 0 },
|
||||
{ 1, 1, 1, 0, 0, 1 },
|
||||
{ 1, 0, 0, 0, 1, 1 },
|
||||
{ 1, 1, 0, 1.0 / 6.0, 1, 1 },
|
||||
{ 0, 1, 0, 2.0 / 6.0, 1, 1 },
|
||||
{ 0, 1, 1, 3.0 / 6.0, 1, 1 },
|
||||
{ 0, 0, 1, 4.0 / 6.0, 1, 1 },
|
||||
{ 1, 0, 1, 5.0 / 6.0, 1, 1 },
|
||||
};
|
||||
const float EPSILON = 1e-6;
|
||||
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
float r, g, b;
|
||||
float h, s, v;
|
||||
|
||||
g_print ("color %u\n", i);
|
||||
gtk_hsv_to_rgb (tests[i].h, tests[i].s, tests[i].v, &r, &g, &b);
|
||||
g_assert_cmpfloat_with_epsilon (r, tests[i].r, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (g, tests[i].g, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (b, tests[i].b, EPSILON);
|
||||
|
||||
gtk_rgb_to_hsv (tests[i].r, tests[i].g, tests[i].b, &h, &s, &v);
|
||||
g_assert_cmpfloat_with_epsilon (h, tests[i].h, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (s, tests[i].s, EPSILON);
|
||||
@@ -54,13 +51,87 @@ test_roundtrips (void)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_roundtrips_rgb_hwb (void)
|
||||
{
|
||||
struct {
|
||||
float r, g, b;
|
||||
float hue, white, black;
|
||||
} tests[] = {
|
||||
{ 0, 0, 0, 0, 0, 1 },
|
||||
{ 1, 1, 1, 0, 1, 0 },
|
||||
{ 1, 0, 0, 0, 0, 0 },
|
||||
{ 1, 1, 0, 60, 0, 0 },
|
||||
{ 0, 1, 0, 120, 0, 0 },
|
||||
{ 0, 1, 1, 180, 0, 0 },
|
||||
{ 0, 0, 1, 240, 0, 0 },
|
||||
{ 1, 0, 1, 300, 0, 0 },
|
||||
{ 0.5, 0.5, 0.5, 0, 0.5, 0.5 },
|
||||
};
|
||||
const float EPSILON = 1e-6;
|
||||
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
float r, g, b;
|
||||
float hue, white, black;
|
||||
|
||||
gtk_hwb_to_rgb (tests[i].hue, tests[i].white, tests[i].black, &r, &g, &b);
|
||||
g_assert_cmpfloat_with_epsilon (r, tests[i].r, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (g, tests[i].g, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (b, tests[i].b, EPSILON);
|
||||
|
||||
gtk_rgb_to_hwb (tests[i].r, tests[i].g, tests[i].b, &hue, &white, &black);
|
||||
g_assert_cmpfloat_with_epsilon (hue, tests[i].hue, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (white, tests[i].white, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (black, tests[i].black, EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_roundtrips_rgb_oklab (void)
|
||||
{
|
||||
struct {
|
||||
float red, green, blue;
|
||||
float L, a, b;
|
||||
} tests[] = {
|
||||
{ 0, 0, 0, 0, 0, 0 },
|
||||
{ 1, 1, 1, 1, 0, 0 },
|
||||
{ 1, 0, 0, 0.62796, 0.22486, 0.12585 },
|
||||
{ 1, 1, 0, 0.96798, -0.07137, 0.19857 },
|
||||
{ 0, 1, 0, 0.86644, -0.23389, 0.17950 },
|
||||
{ 0, 1, 1, 0.90540, -0.14944, -0.03940 },
|
||||
{ 0, 0, 1, 0.45201, -0.03246, -0.31153 },
|
||||
{ 1, 0, 1, 0.70167, 0.27457, -0.16916 },
|
||||
{ 0.5, 0.5, 0.5, 0.598181, 0.00000, 0.00000 },
|
||||
};
|
||||
const float EPSILON = 1e-3;
|
||||
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
float red, green, blue;
|
||||
float L, a, b;
|
||||
|
||||
gtk_oklab_to_rgb (tests[i].L, tests[i].a, tests[i].b, &red, &green, &blue);
|
||||
g_assert_cmpfloat_with_epsilon (red, tests[i].red, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (green, tests[i].green, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (blue, tests[i].blue, EPSILON);
|
||||
|
||||
gtk_rgb_to_oklab (tests[i].red, tests[i].green, tests[i].blue, &L, &a, &b);
|
||||
g_assert_cmpfloat_with_epsilon (L, tests[i].L, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (a, tests[i].a, EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (b, tests[i].b, EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gtk_test_init (&argc, &argv);
|
||||
|
||||
g_test_add_func ("/color/roundtrips", test_roundtrips);
|
||||
g_test_add_func ("/color/roundtrips/rgb-hsv", test_roundtrips_rgb_hsv);
|
||||
g_test_add_func ("/color/roundtrips/rgb-hwb", test_roundtrips_rgb_hwb);
|
||||
g_test_add_func ("/color/roundtrips/rgb-oklab", test_roundtrips_rgb_oklab);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user