From 01ff20209401858ef362bc6daa56a7067cf6d26f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 10 Jul 2024 16:16:07 -0400 Subject: [PATCH] colorstate: Add xyz This is mainly to test that our colorstate machinery is exensible. We will also use xyz as the central hub for converting between arbitrary color states. --- gdk/gdkcolorstate.c | 85 ++++++++++++++++++++++++++++++ gdk/gdkcolorstate.h | 3 ++ gdk/gdkcolorstateprivate.h | 2 + gsk/gpu/shaders/enums.glsl | 1 + gsk/gpu/shaders/gskgpuconvert.glsl | 38 +++++++++++++ testsuite/gdk/colorstate.c | 1 + 6 files changed, 130 insertions(+) diff --git a/gdk/gdkcolorstate.c b/gdk/gdkcolorstate.c index 7829961c8b..62db5febfe 100644 --- a/gdk/gdkcolorstate.c +++ b/gdk/gdkcolorstate.c @@ -103,6 +103,19 @@ gdk_color_state_get_srgb_linear (void) return GDK_COLOR_STATE_SRGB_LINEAR; } +/** + * gdk_color_state_get_xyz: + * + * Returns the color state object representing the XYZ color space. + * + * Since: 4.16 + */ +GdkColorState * +gdk_color_state_get_xyz (void) +{ + return GDK_COLOR_STATE_XYZ; +} + /** * gdk_color_state_equal: * @self: a `GdkColorState` @@ -202,6 +215,62 @@ srgb_eotf (float v) COORDINATE_TRANSFORM(gdk_default_srgb_to_srgb_linear, srgb_eotf) COORDINATE_TRANSFORM(gdk_default_srgb_linear_to_srgb, srgb_oetf) +static inline void +vec3_multiply (const float matrix[3][3], + const float vec[3], + float res[3]) +{ + res[0] = matrix[0][0] * vec[0] + matrix[0][1] * vec[1] + matrix[0][2] * vec[2]; + res[1] = matrix[1][0] * vec[0] + matrix[1][1] * vec[1] + matrix[1][2] * vec[2]; + res[2] = matrix[2][0] * vec[0] + matrix[2][1] * vec[1] + matrix[2][2] * vec[2]; +} + +#define LINEAR_TRANSFORM(name, matrix) \ +static void \ +name (GdkColorState *self, \ + float (*values)[4], \ + gsize n_values) \ +{ \ + for (gsize i = 0; i < n_values; i++) \ + { \ + float res[3]; \ +\ + vec3_multiply (matrix, values[i], res); \ +\ + values[i][0] = res[0]; \ + values[i][1] = res[1]; \ + values[i][2] = res[2]; \ + } \ +} + +static const float srgb_linear_to_xyz[3][3] = { + { (506752.0 / 1228815.0), (87881.0 / 245763.0), (12673.0 / 70218.0) }, + { (87098.0 / 409605.0), (175762.0 / 245763.0), (12673.0 / 175545.0) }, + { ( 7918.0 / 409605.0), (87881.0 / 737289.0), (1001167.0 / 1053270.0) }, +}; + +static const float xyz_to_srgb_linear[3][3] = { + { (12831.0 / 3959.0), - (329.0 / 214.0), - (1974.0 / 3959.0) }, + { - (851781.0 / 878810.0), (1648619.0 / 878810.0), (36519.0 / 878810.0) }, + { (705.0 / 12673.0), - (2585.0 / 12673.0), (705.0 / 667.0) }, +}; + +LINEAR_TRANSFORM(gdk_default_xyz_to_srgb_linear, xyz_to_srgb_linear) +LINEAR_TRANSFORM(gdk_default_srgb_linear_to_xyz, srgb_linear_to_xyz) + +#define CONCAT(name, f1, f2) \ +static void \ +name (GdkColorState *self, \ + float (*values)[4], \ + gsize n_values) \ +{ \ + f1 (self, values, n_values); \ + f2 (self, values, n_values); \ +} + +CONCAT(gdk_default_xyz_to_srgb, gdk_default_xyz_to_srgb_linear, gdk_default_srgb_linear_to_srgb); +CONCAT(gdk_default_srgb_to_xyz, gdk_default_srgb_to_srgb_linear, gdk_default_srgb_linear_to_xyz); + /* }}} */ static const @@ -225,6 +294,7 @@ GdkDefaultColorState gdk_default_color_states[] = { .no_srgb = GDK_COLOR_STATE_SRGB_LINEAR, .convert_to = { [GDK_COLOR_STATE_ID_SRGB_LINEAR] = gdk_default_srgb_to_srgb_linear, + [GDK_COLOR_STATE_ID_XYZ] = gdk_default_srgb_to_xyz, }, }, [GDK_COLOR_STATE_ID_SRGB_LINEAR] = { @@ -238,6 +308,21 @@ GdkDefaultColorState gdk_default_color_states[] = { .no_srgb = NULL, .convert_to = { [GDK_COLOR_STATE_ID_SRGB] = gdk_default_srgb_linear_to_srgb, + [GDK_COLOR_STATE_ID_XYZ] = gdk_default_srgb_linear_to_xyz, + }, + }, + [GDK_COLOR_STATE_ID_XYZ] = { + .parent = { + .klass = &GDK_DEFAULT_COLOR_STATE_CLASS, + .ref_count = 0, + .depth = GDK_MEMORY_FLOAT16, + .rendering_color_state = GDK_COLOR_STATE_XYZ, + }, + .name = "xyz", + .no_srgb = NULL, + .convert_to = { + [GDK_COLOR_STATE_ID_SRGB] = gdk_default_xyz_to_srgb, + [GDK_COLOR_STATE_ID_SRGB_LINEAR] = gdk_default_xyz_to_srgb_linear, }, }, }; diff --git a/gdk/gdkcolorstate.h b/gdk/gdkcolorstate.h index cfd51210ea..3bf899ac65 100644 --- a/gdk/gdkcolorstate.h +++ b/gdk/gdkcolorstate.h @@ -43,6 +43,9 @@ GdkColorState * gdk_color_state_get_srgb (void); GDK_AVAILABLE_IN_4_16 GdkColorState * gdk_color_state_get_srgb_linear (void); +GDK_AVAILABLE_IN_4_16 +GdkColorState * gdk_color_state_get_xyz (void); + GDK_AVAILABLE_IN_4_16 gboolean gdk_color_state_equal (GdkColorState *self, GdkColorState *other); diff --git a/gdk/gdkcolorstateprivate.h b/gdk/gdkcolorstateprivate.h index 7afc55efbe..498e12751d 100644 --- a/gdk/gdkcolorstateprivate.h +++ b/gdk/gdkcolorstateprivate.h @@ -10,6 +10,7 @@ typedef enum { GDK_COLOR_STATE_ID_SRGB, GDK_COLOR_STATE_ID_SRGB_LINEAR, + GDK_COLOR_STATE_ID_XYZ, GDK_COLOR_STATE_N_IDS } GdkColorStateId; @@ -55,6 +56,7 @@ 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_IS_DEFAULT_COLOR_STATE(c) ((GdkDefaultColorState *) (c) >= &gdk_default_color_states[0] && \ (GdkDefaultColorState *) (c) < &gdk_default_color_states[GDK_COLOR_STATE_N_IDS]) diff --git a/gsk/gpu/shaders/enums.glsl b/gsk/gpu/shaders/enums.glsl index 2823f4c930..4983b20ece 100644 --- a/gsk/gpu/shaders/enums.glsl +++ b/gsk/gpu/shaders/enums.glsl @@ -54,6 +54,7 @@ #define GDK_COLOR_STATE_ID_SRGB 0u #define GDK_COLOR_STATE_ID_SRGB_LINEAR 1u +#define GDK_COLOR_STATE_ID_XYZ 2u #define TOP 0u #define RIGHT 1u diff --git a/gsk/gpu/shaders/gskgpuconvert.glsl b/gsk/gpu/shaders/gskgpuconvert.glsl index 453933d186..d6127b5ad0 100644 --- a/gsk/gpu/shaders/gskgpuconvert.glsl +++ b/gsk/gpu/shaders/gskgpuconvert.glsl @@ -44,6 +44,32 @@ srgb_linear_to_srgb (vec4 color) color.a); } +vec4 +srgb_linear_to_xyz (vec4 color) +{ + mat3 m = mat3 (506752.0 / 1228815.0, 87881.0 / 245763.0, 12673.0 / 70218.0, + 87098.0 / 409605.0, 175762.0 / 245763.0, 12673.0 / 175545.0, + 7918.0 / 409605.0, 87881.0 / 737289.0, 1001167.0 / 1053270.0); + + return vec4 (color.rgb * m, color.a); +} + +vec4 +xyz_to_srgb_linear (vec4 color) +{ + mat3 m = mat3 ( 12831.0 / 3959.0, -329.0 / 214.0, -1974.0 / 3959.0, + -851781.0 / 878810.0, 1648619.0 / 878810.0, 36519.0 / 878810.0, + 705.0 / 12673.0, -2585.0 / 12673.0, 705.0 / 667.0); + + return vec4 (color.xyz * m, color.a); +} + +#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) + + #define PAIR(_from_cs, _to_cs) ((_from_cs) << 16 | (_to_cs)) bool @@ -60,6 +86,18 @@ do_conversion (vec4 color, case PAIR (GDK_COLOR_STATE_ID_SRGB_LINEAR, GDK_COLOR_STATE_ID_SRGB): result = srgb_linear_to_srgb (color); break; + case PAIR (GDK_COLOR_STATE_ID_SRGB_LINEAR, GDK_COLOR_STATE_ID_XYZ): + result = srgb_linear_to_xyz (color); + break; + case PAIR (GDK_COLOR_STATE_ID_SRGB, GDK_COLOR_STATE_ID_XYZ): + result = srgb_to_xyz (color); + break; + case PAIR (GDK_COLOR_STATE_ID_XYZ, GDK_COLOR_STATE_ID_SRGB_LINEAR): + result = xyz_to_srgb_linear (color); + break; + case PAIR (GDK_COLOR_STATE_ID_XYZ, GDK_COLOR_STATE_ID_SRGB): + result = xyz_to_srgb (color); + break; default: return false; diff --git a/testsuite/gdk/colorstate.c b/testsuite/gdk/colorstate.c index 28746e5fb3..67144b4c30 100644 --- a/testsuite/gdk/colorstate.c +++ b/testsuite/gdk/colorstate.c @@ -111,6 +111,7 @@ main (int argc, char *argv[]) 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); return g_test_run (); }