Add rec2020 and rec2020-linear

These are wide-gamut SDR colorstates and can be useful
as compoisting color states.
This commit is contained in:
Matthias Clasen
2024-07-10 16:23:53 -04:00
parent ad9887457f
commit acce932e8d
6 changed files with 208 additions and 12 deletions

View File

@@ -142,6 +142,32 @@ gdk_color_state_get_oklch (void)
return GDK_COLOR_STATE_OKLCH;
}
/**
* gdk_color_state_get_rec2020:
*
* Returns the color state object representing the rec2020 color space.
*
* Since: 4.16
*/
GdkColorState *
gdk_color_state_get_rec2020 (void)
{
return GDK_COLOR_STATE_REC2020;
}
/**
* gdk_color_state_get_rec2020_linear:
*
* Returns the color state object representing the linear rec2020 color space.
*
* Since: 4.16
*/
GdkColorState *
gdk_color_state_get_rec2020_linear (void)
{
return GDK_COLOR_STATE_REC2020_LINEAR;
}
/**
* gdk_color_state_equal:
* @self: a `GdkColorState`
@@ -415,6 +441,53 @@ gdk_default_xyz_to_oklab (GdkColorState *self,
}
}
static inline float
rec2020_eotf (float v)
{
float alpha = 1.09929682680944;
float beta = 0.018053968510807;
int sign = v < 0 ? -1 : 1;
float abs = fabsf (v);
if (abs < beta * 4.5 )
return v/ 4.5;
else
return sign * powf ((abs + alpha - 1) / alpha, 1.0 / 0.45);
}
static inline float
rec2020_oetf (float v)
{
float alpha = 1.09929682680944;
float beta = 0.018053968510807;
int sign = v < 0 ? -1 : 1;
float abs = fabsf (v);
if (abs > beta)
return sign * (alpha * powf (abs, 0.45) - (alpha - 1));
else
return 4.5 * v;
}
COORDINATE_TRANSFORM(gdk_default_rec2020_to_rec2020_linear, rec2020_eotf)
COORDINATE_TRANSFORM(gdk_default_rec2020_linear_to_rec2020, rec2020_oetf)
static const float rec2020_linear_to_xyz[3][3] = {
{ (63426534.0 / 99577255.0), (20160776.0 / 139408157.0), (47086771.0 / 278816314.0) },
{ (26158966.0 / 99577255.0), (472592308.0 / 697040785.0), (8267143.0 / 139408157.0) },
{ ( 0 / 1), (19567812.0 / 697040785.0), (295819943.0 / 278816314.0) },
};
static const float xyz_to_rec2020_linear[3][3] = {
{ (30757411.0 / 17917100.0), - (6372589.0 / 17917100.0), - (4539589.0 / 17917100.0) },
{ - (19765991.0 / 29648200.0), (47925759.0 / 29648200.0), (467509.0 / 29648200.0) },
{ (792561.0 / 44930125.0), - (1921689.0 / 44930125.0), (42328811.0 / 44930125.0) },
};
LINEAR_TRANSFORM(gdk_default_rec2020_linear_to_xyz, rec2020_linear_to_xyz)
LINEAR_TRANSFORM(gdk_default_xyz_to_rec2020_linear, xyz_to_rec2020_linear)
#define CONCAT(name, f1, f2) \
static void \
name (GdkColorState *self, \
@@ -429,6 +502,8 @@ CONCAT(gdk_default_xyz_to_srgb, gdk_default_xyz_to_srgb_linear, gdk_default_srgb
CONCAT(gdk_default_srgb_to_xyz, gdk_default_srgb_to_srgb_linear, gdk_default_srgb_linear_to_xyz);
CONCAT(gdk_default_oklch_to_xyz, gdk_default_oklch_to_oklab, gdk_default_oklab_to_xyz);
CONCAT(gdk_default_xyz_to_oklch, gdk_default_xyz_to_oklab, gdk_default_oklab_to_oklch);
CONCAT(gdk_default_rec2020_to_xyz, gdk_default_rec2020_to_rec2020_linear, gdk_default_rec2020_linear_to_xyz);
CONCAT(gdk_default_xyz_to_rec2020, gdk_default_xyz_to_rec2020_linear, gdk_default_rec2020_linear_to_rec2020);
/* }}} */
@@ -484,6 +559,8 @@ GdkDefaultColorState gdk_default_color_states[] = {
[GDK_COLOR_STATE_ID_SRGB_LINEAR] = gdk_default_xyz_to_srgb_linear,
[GDK_COLOR_STATE_ID_OKLAB] = gdk_default_xyz_to_oklab,
[GDK_COLOR_STATE_ID_OKLCH] = gdk_default_xyz_to_oklch,
[GDK_COLOR_STATE_ID_REC2020] = gdk_default_xyz_to_rec2020,
[GDK_COLOR_STATE_ID_REC2020_LINEAR] = gdk_default_xyz_to_rec2020_linear,
},
},
[GDK_COLOR_STATE_ID_OKLAB] = {
@@ -512,6 +589,32 @@ GdkDefaultColorState gdk_default_color_states[] = {
[GDK_COLOR_STATE_ID_XYZ] = gdk_default_oklch_to_xyz,
},
},
[GDK_COLOR_STATE_ID_REC2020] = {
.parent = {
.klass = &GDK_DEFAULT_COLOR_STATE_CLASS,
.ref_count = 0,
.depth = GDK_MEMORY_FLOAT16,
.rendering_color_state = GDK_COLOR_STATE_REC2020_LINEAR,
},
.name = "rec2020",
.no_srgb = NULL,
.convert_to = {
[GDK_COLOR_STATE_ID_XYZ] = gdk_default_rec2020_to_xyz,
},
},
[GDK_COLOR_STATE_ID_REC2020_LINEAR] = {
.parent = {
.klass = &GDK_DEFAULT_COLOR_STATE_CLASS,
.ref_count = 0,
.depth = GDK_MEMORY_FLOAT16,
.rendering_color_state = GDK_COLOR_STATE_REC2020_LINEAR,
},
.name = "rec2020-linear",
.no_srgb = NULL,
.convert_to = {
[GDK_COLOR_STATE_ID_XYZ] = gdk_default_rec2020_linear_to_xyz,
},
},
};
/* }}} */

View File

@@ -52,6 +52,12 @@ GdkColorState * gdk_color_state_get_oklab (void);
GDK_AVAILABLE_IN_4_16
GdkColorState * gdk_color_state_get_oklch (void);
GDK_AVAILABLE_IN_4_16
GdkColorState * gdk_color_state_get_rec2020 (void);
GDK_AVAILABLE_IN_4_16
GdkColorState * gdk_color_state_get_rec2020_linear (void);
GDK_AVAILABLE_IN_4_16
gboolean gdk_color_state_equal (GdkColorState *self,
GdkColorState *other);

View File

@@ -13,6 +13,8 @@ typedef enum
GDK_COLOR_STATE_ID_XYZ,
GDK_COLOR_STATE_ID_OKLAB,
GDK_COLOR_STATE_ID_OKLCH,
GDK_COLOR_STATE_ID_REC2020,
GDK_COLOR_STATE_ID_REC2020_LINEAR,
GDK_COLOR_STATE_N_IDS
} GdkColorStateId;
@@ -56,11 +58,13 @@ struct _GdkDefaultColorState
extern GdkDefaultColorState gdk_default_color_states[GDK_COLOR_STATE_N_IDS];
#define GDK_COLOR_STATE_SRGB ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_SRGB])
#define GDK_COLOR_STATE_SRGB_LINEAR ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_SRGB_LINEAR])
#define GDK_COLOR_STATE_XYZ ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_XYZ])
#define GDK_COLOR_STATE_OKLAB ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_OKLAB])
#define GDK_COLOR_STATE_OKLCH ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_OKLCH])
#define GDK_COLOR_STATE_SRGB ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_SRGB])
#define GDK_COLOR_STATE_SRGB_LINEAR ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_SRGB_LINEAR])
#define GDK_COLOR_STATE_XYZ ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_XYZ])
#define GDK_COLOR_STATE_OKLAB ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_OKLAB])
#define GDK_COLOR_STATE_OKLCH ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_OKLCH])
#define GDK_COLOR_STATE_REC2020 ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_REC2020])
#define GDK_COLOR_STATE_REC2020_LINEAR ((GdkColorState *) &gdk_default_color_states[GDK_COLOR_STATE_ID_REC2020_LINEAR])
#define GDK_IS_DEFAULT_COLOR_STATE(c) ((GdkDefaultColorState *) (c) >= &gdk_default_color_states[0] && \
(GdkDefaultColorState *) (c) < &gdk_default_color_states[GDK_COLOR_STATE_N_IDS])

View File

@@ -57,6 +57,8 @@
#define GDK_COLOR_STATE_ID_XYZ 2u
#define GDK_COLOR_STATE_ID_OKLAB 3u
#define GDK_COLOR_STATE_ID_OKLCH 4u
#define GDK_COLOR_STATE_ID_REC2020 5u
#define GDK_COLOR_STATE_ID_REC2020_LINEAR 6u
#define TOP 0u
#define RIGHT 1u

View File

@@ -153,12 +153,81 @@ xyz_to_oklab (vec4 color)
return vec4 (lab, color.a);
}
float
rec2020_eotf (float v)
{
float alpha = 1.09929682680944;
float beta = 0.018053968510807;
float sign = v < 0.0 ? -1.0 : 1.0;
float vabs = abs (v);
if (vabs < beta * 4.5)
return v / 4.5;
else
return sign * pow ((vabs + alpha - 1.0) / alpha, 1.0 / 0.45);
}
float
rec2020_oetf (float v)
{
float alpha = 1.09929682680944;
float beta = 0.018053968510807;
float sign = v < 0.0 ? -1.0 : 1.0;
float vabs = abs (v);
if (vabs > beta)
return sign * (alpha * pow (vabs, 0.45) - (alpha - 1.0));
else
return 4.5 * v;
}
vec4
rec2020_to_rec2020_linear (vec4 color)
{
return vec4 (rec2020_eotf (color.r),
rec2020_eotf (color.g),
rec2020_eotf (color.b),
color.a);
}
vec4
rec2020_linear_to_rec2020 (vec4 color)
{
return vec4 (rec2020_oetf (color.r),
rec2020_oetf (color.g),
rec2020_oetf (color.b),
color.a);
}
vec4
rec2020_linear_to_xyz (vec4 color)
{
mat3 m = mat3 (63426534.0 / 99577255.0, 20160776.0 / 139408157.0, 47086771.0 / 278816314.0,
26158966.0 / 99577255.0, 472592308.0 / 697040785.0, 8267143.0 / 139408157.0,
0.0, 19567812.0 / 697040785.0, 295819943.0 / 278816314.0);
return vec4 (color.rgb * m, color.a);
}
vec4
xyz_to_rec2020_linear (vec4 color)
{
mat3 m = mat3 ( 30757411.0 / 17917100.0, -6372589.0 / 17917100.0, -4539589.0 / 17917100.0,
-19765991.0 / 29648200.0, 47925759.0 / 29648200.0, 467509.0 / 29648200.0,
792561.0 / 44930125.0, -1921689.0 / 44930125.0, 42328811.0 / 44930125.0);
return vec4 (color.xyz * m, color.z);
}
#define CONCAT(f, f1, f2) vec4 f(vec4 color) { return f2(f1(color)); }
CONCAT(srgb_to_xyz, srgb_to_srgb_linear, srgb_linear_to_xyz)
CONCAT(xyz_to_srgb, xyz_to_srgb_linear, srgb_linear_to_srgb)
CONCAT(oklch_to_xyz, oklch_to_oklab, oklab_to_xyz)
CONCAT(xyz_to_oklch, xyz_to_oklab, oklab_to_oklch)
CONCAT(rec2020_to_xyz, rec2020_to_rec2020_linear, rec2020_linear_to_xyz)
CONCAT(xyz_to_rec2020, xyz_to_rec2020_linear, rec2020_linear_to_rec2020)
#define PAIR(_from_cs, _to_cs) ((_from_cs) << 16 | (_to_cs))
@@ -188,6 +257,12 @@ do_conversion (vec4 color,
case PAIR (GDK_COLOR_STATE_ID_OKLCH, GDK_COLOR_STATE_ID_XYZ):
result = oklch_to_xyz (color);
break;
case PAIR (GDK_COLOR_STATE_ID_REC2020, GDK_COLOR_STATE_ID_XYZ):
result = rec2020_to_xyz (color);
break;
case PAIR (GDK_COLOR_STATE_ID_REC2020_LINEAR, GDK_COLOR_STATE_ID_XYZ):
result = rec2020_linear_to_xyz (color);
break;
case PAIR (GDK_COLOR_STATE_ID_XYZ, GDK_COLOR_STATE_ID_SRGB_LINEAR):
result = xyz_to_srgb_linear (color);
break;
@@ -200,6 +275,12 @@ do_conversion (vec4 color,
case PAIR (GDK_COLOR_STATE_ID_XYZ, GDK_COLOR_STATE_ID_OKLCH):
result = xyz_to_oklch (color);
break;
case PAIR (GDK_COLOR_STATE_ID_XYZ, GDK_COLOR_STATE_ID_REC2020):
result = xyz_to_rec2020 (color);
break;
case PAIR (GDK_COLOR_STATE_ID_XYZ, GDK_COLOR_STATE_ID_REC2020_LINEAR):
result = xyz_to_rec2020_linear (color);
break;
default:
return false;

View File

@@ -50,7 +50,7 @@ image_distance (const guchar *data,
static void
test_convert (gconstpointer testdata)
{
GdkColorState *cs;
GdkColorState *cs = (GdkColorState *) testdata;
char *path;
GdkTexture *texture;
GdkTextureDownloader *downloader;
@@ -62,8 +62,6 @@ test_convert (gconstpointer testdata)
gsize size;
gsize stride;
cs = gdk_color_state_get_by_id ((GdkColorStateId) GPOINTER_TO_UINT (testdata));
path = g_test_build_filename (G_TEST_DIST, "image-data", "image.png", NULL);
texture = gdk_texture_new_from_filename (path, &error);
@@ -110,10 +108,12 @@ main (int argc, char *argv[])
(g_test_init) (&argc, &argv, NULL);
g_test_add_func ("/colorstate/srgb", test_srgb);
g_test_add_data_func ("/colorstate/convert/srgb<->srgb-linear", GUINT_TO_POINTER (GDK_COLOR_STATE_ID_SRGB_LINEAR), test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->xyz", GUINT_TO_POINTER (GDK_COLOR_STATE_ID_XYZ), test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->oklab", GUINT_TO_POINTER (GDK_COLOR_STATE_ID_OKLAB), test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->oklch", GUINT_TO_POINTER (GDK_COLOR_STATE_ID_OKLCH), test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->srgb-linear", GDK_COLOR_STATE_SRGB_LINEAR, test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->xyz", GDK_COLOR_STATE_XYZ, test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->oklab", GDK_COLOR_STATE_OKLAB, test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->oklch", GDK_COLOR_STATE_OKLCH, test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->rec2020", GDK_COLOR_STATE_REC2020, test_convert);
g_test_add_data_func ("/colorstate/convert/srgb<->rec2020-linear", GDK_COLOR_STATE_REC2020_LINEAR, test_convert);
return g_test_run ();
}