From d907c0a42e23b4b7f41d1af7edd07a860acefa95 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 15 Jul 2024 08:08:57 -0400 Subject: [PATCH] colorstate: Add cicp support This adds machinery to create colorstate objects from cicp tuples, as well as a function to return a cicp tuple for a colorstate. Still missing: a conversion shader for non-default colorstates. --- gdk/gdkcicpparamsprivate.h | 84 +++++++ gdk/gdkcolorstate.c | 491 ++++++++++++++++++++++++++++++++++--- gdk/gdkcolorstateprivate.h | 21 ++ 3 files changed, 557 insertions(+), 39 deletions(-) create mode 100644 gdk/gdkcicpparamsprivate.h diff --git a/gdk/gdkcicpparamsprivate.h b/gdk/gdkcicpparamsprivate.h new file mode 100644 index 0000000000..a55ea96273 --- /dev/null +++ b/gdk/gdkcicpparamsprivate.h @@ -0,0 +1,84 @@ +#pragma once + +typedef struct _GdkCicp GdkCicp; + +typedef enum +{ + GDK_CICP_RANGE_NARROW, + GDK_CICP_RANGE_FULL, +} GdkCicpRange; + +struct _GdkCicp +{ + guint color_primaries; + guint transfer_function; + guint matrix_coefficients; + GdkCicpRange range; +}; + +/*< private > + * gdk_cicp_equal: + * @p1: a `GdkCicp` + * @p2: another `GdkCicp` + * + * Compare two cicp tuples for equality. + * + * Note that several cicp values are 'functionally equivalent'. + * If you are interested in that notion, use gdk_cicp_equivalent(). + * + * Returns: whether @p1 and @p2 are equal + */ +static inline gboolean +gdk_cicp_equal (const GdkCicp *p1, + const GdkCicp *p2) +{ + return p1->color_primaries == p2->color_primaries && + p1->transfer_function == p2->transfer_function && + p1->matrix_coefficients == p2->matrix_coefficients && + p1->range == p2->range; +} + +static inline void +gdk_cicp_normalize (const GdkCicp *orig, + GdkCicp *out) +{ + memcpy (out, orig, sizeof (GdkCicp)); + + /* ntsc */ + if (out->color_primaries == 6) + out->color_primaries = 5; + + /* bt709 */ + if (out->transfer_function == 6 || + out->transfer_function == 14 || + out->transfer_function == 15) + out->transfer_function = 1; + + /* bt601 */ + if (out->matrix_coefficients == 6) + out->matrix_coefficients = 5; +} + +/*< private > + * gdk_cicp_equivalent: + * @p1: a `GdkCicp` + * @p2: another `GdkCicp` + * + * Determine whether two cicp tuples are functionally equivalent. + * + * Returns: whether @p1 and @p2 are functionally equivalent + */ +static inline gboolean +gdk_cicp_equivalent (const GdkCicp *p1, + const GdkCicp *p2) +{ + GdkCicp n1, n2; + + if (gdk_cicp_equal (p1, p2)) + return TRUE; + + gdk_cicp_normalize (p1, &n1); + gdk_cicp_normalize (p2, &n2); + + return gdk_cicp_equal (&n1, &n2); +} diff --git a/gdk/gdkcolorstate.c b/gdk/gdkcolorstate.c index 5db456ed2b..5afe8c3c70 100644 --- a/gdk/gdkcolorstate.c +++ b/gdk/gdkcolorstate.c @@ -22,6 +22,8 @@ #include +#include + /** * GdkColorState: * @@ -193,6 +195,12 @@ gboolean /* }}} */ /* {{{ Conversion functions */ +typedef float (* GdkTransferFunc) (float v); +typedef const float GdkColorMatrix[3][3]; + +#define IDENTITY ((float**)0) +#define NONE ((GdkTransferFunc)0) + #define TRANSFORM(name, eotf, matrix, oetf) \ static void \ name (GdkColorState *self, \ @@ -201,9 +209,12 @@ name (GdkColorState *self, \ { \ for (gsize i = 0; i < n_values; i++) \ { \ - values[i][0] = eotf (values[i][0]); \ - values[i][1] = eotf (values[i][1]); \ - values[i][2] = eotf (values[i][2]); \ + if (eotf != NONE) \ + { \ + values[i][0] = eotf (values[i][0]); \ + values[i][1] = eotf (values[i][1]); \ + values[i][2] = eotf (values[i][2]); \ + } \ if ((float **)matrix != IDENTITY) \ { \ float res[3]; \ @@ -214,9 +225,12 @@ name (GdkColorState *self, \ values[i][1] = res[1]; \ values[i][2] = res[2]; \ } \ - values[i][0] = oetf (values[i][0]); \ - values[i][1] = oetf (values[i][1]); \ - values[i][2] = oetf (values[i][2]); \ + if (oetf != NONE) \ + { \ + values[i][0] = oetf (values[i][0]); \ + values[i][1] = oetf (values[i][1]); \ + values[i][2] = oetf (values[i][2]); \ + } \ } \ } @@ -265,29 +279,116 @@ pq_oetf (float v) return powf (((c1 + (c2 * powf (x, n))) / (1 + (c3 * powf (x, n)))), m); } -/* These matrices are derived by combining the standard abc_to_xyz onces: - * - * rec2020_to_srgb = srgb_to_xyz⁻¹ * rec2020_to_xyz - * srgb_to_rec2020 = rec2020_to_xyz⁻¹ * srgb_to_xyz - * - * These values were used here: - * - * static const float srgb_to_xyz[3][3] = { - * { 0.4125288, 0.3581642, 0.1774037 }, - * { 0.2127102, 0.7163284, 0.0709615 }, - * { 0.0193373, 0.1193881, 0.9343260 }, - * } - * - * static const float rec2020_to_xyz[3][3] = { - * { 0.6369615, 0.1448079, 0.1663273 }, - * { 0.2627016, 0.6788934, 0.0584050 }, - * { 0.0000000, 0.0281098, 1.0449416 }, - * }; - * - * See http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html +static inline float +bt709_eotf (float v) +{ + if (v < 0.081) + return v / 4.5; + else + return powf ((v + 0.099) / 1.099, 1/0.45); +} + +static inline float +bt709_oetf (float v) +{ + if (v < 0.081) + return v * 4.5; + else + return 1.099 * powf (v, 0.45) - 0.099; +} + +static inline float +hlg_eotf (float v) +{ + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + + if (v <= 0.5) + return (v * v) / 3; + else + return expf (((v - c) / a) + b) / 12.0; +} + +static inline float +hlg_oetf (float v) +{ + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + + if (v <= 1/12.0) + return sqrtf (3 * v); + else + return a * logf (12 * v - b) + c; +} + +/* See http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html * for how to derive the abc_to_xyz matrices from chromaticity coordinates. */ +static const float srgb_to_xyz[3][3] = { + { 0.4125288, 0.3581642, 0.1774037 }, + { 0.2127102, 0.7163284, 0.0709615 }, + { 0.0193373, 0.1193881, 0.9343260 }, +}; + +static const float xyz_to_srgb[3][3] = { + { 3.2409699, -1.5373832, -0.4986108 }, + { -0.9692436, 1.8759675, 0.0415551 }, + { 0.0556301, -0.2039770, 1.0569715 }, +}; + +static const float rec2020_to_xyz[3][3] = { + { 0.6369615, 0.1448079, 0.1663273 }, + { 0.2627016, 0.6788934, 0.0584050 }, + { 0.0000000, 0.0281098, 1.0449416 }, +}; + +static const float xyz_to_rec2020[3][3] = { + { 1.7166512, -0.3556708, -0.2533663 }, + { -0.6666844, 1.6164812, 0.0157685 }, + { 0.0176399, -0.0427706, 0.9421031 }, +}; + +static const float pal_to_xyz[3][3] = { + { 0.4305538, 0.3415498, 0.1783523 }, + { 0.2220043, 0.7066548, 0.0713409 }, + { 0.0201822, 0.1295534, 0.9393222 }, +}; + +static const float xyz_to_pal[3][3] = { + { 3.0633611, -1.3933902, -0.4758237 }, + { -0.9692436, 1.8759675, 0.0415551 }, + { 0.0678610, -0.2287993, 1.0690896 }, +}; + +static const float ntsc_to_xyz[3][3] = { + { 0.3935209, 0.3652581, 0.1916769 }, + { 0.2123764, 0.7010599, 0.0865638 }, + { 0.0187391, 0.1119339, 0.9583847 }, +}; + +static const float xyz_to_ntsc[3][3] = { + { 3.5060033, -1.7397907, -0.5440583 }, + { -1.0690476, 1.9777789, 0.0351714 }, + { 0.0563066, -0.1969757, 1.0499523 }, +}; + +static const float p3_to_xyz[3][3] = { + { 0.4865709, 0.2656677, 0.1982173 }, + { 0.2289746, 0.6917385, 0.0792869 }, + { 0.0000000, 0.0451134, 1.0439444 }, +}; + +static const float xyz_to_p3[3][3] = { + { 2.4934969, -0.9313836, -0.4027108 }, + { -0.8294890, 1.7626641, 0.0236247 }, + { 0.0358458, -0.0761724, 0.9568845 }, +}; + +/* premultiplied matrices for default conversions */ + static const float rec2020_to_srgb[3][3] = { { 1.659944, -0.588220, -0.071724 }, { -0.124350, 1.132559, -0.008210 }, @@ -300,9 +401,6 @@ static const float srgb_to_rec2020[3][3] = { { 0.016649, 0.089510, 0.893842 }, }; -#define IDENTITY ((float**)0) -#define NONE(x) x - TRANSFORM(gdk_default_srgb_to_srgb_linear, srgb_eotf, IDENTITY, NONE); TRANSFORM(gdk_default_srgb_linear_to_srgb, NONE, IDENTITY, srgb_oetf) TRANSFORM(gdk_default_rec2100_pq_to_rec2100_linear, pq_eotf, IDENTITY, NONE) @@ -316,9 +414,6 @@ TRANSFORM(gdk_default_rec2100_linear_to_srgb, NONE, rec2020_to_srgb, TRANSFORM(gdk_default_srgb_to_rec2100_pq, srgb_eotf, srgb_to_rec2020, pq_oetf) TRANSFORM(gdk_default_rec2100_pq_to_srgb, pq_eotf, rec2020_to_srgb, srgb_oetf) -#undef IDENTITY -#undef NONE - /* }}} */ /* {{{ Default implementation */ /* {{{ Vfuncs */ @@ -352,22 +447,28 @@ gdk_default_color_state_get_convert_to (GdkColorState *color_state, { GdkDefaultColorState *self = (GdkDefaultColorState *) color_state; - if (!GDK_IS_DEFAULT_COLOR_STATE (target)) - return NULL; + if (GDK_IS_DEFAULT_COLOR_STATE (target)) + return self->convert_to[GDK_DEFAULT_COLOR_STATE_ID (target)]; - return self->convert_to[GDK_DEFAULT_COLOR_STATE_ID (target)]; + return NULL; } static GdkFloatColorConvert gdk_default_color_state_get_convert_from (GdkColorState *color_state, GdkColorState *source) { - GdkDefaultColorState *self = (GdkDefaultColorState *) source; + /* This is ok because the default-to-default conversion functions + * don't use the passed colorstate at all. + */ + return gdk_default_color_state_get_convert_to (source, color_state); +} - if (!GDK_IS_DEFAULT_COLOR_STATE (color_state)) - return NULL; +static const GdkCicp * +gdk_default_color_state_get_cicp (GdkColorState *color_state) +{ + GdkDefaultColorState *self = (GdkDefaultColorState *) color_state; - return self->convert_to[GDK_DEFAULT_COLOR_STATE_ID (color_state)]; + return &self->cicp; } /* }}} */ @@ -379,6 +480,8 @@ GdkColorStateClass GDK_DEFAULT_COLOR_STATE_CLASS = { .get_name = gdk_default_color_state_get_name, .get_no_srgb_tf = gdk_default_color_state_get_no_srgb_tf, .get_convert_to = gdk_default_color_state_get_convert_to, + .get_convert_from = gdk_default_color_state_get_convert_from, + .get_cicp = gdk_default_color_state_get_cicp, }; GdkDefaultColorState gdk_default_color_states[] = { @@ -396,6 +499,7 @@ GdkDefaultColorState gdk_default_color_states[] = { [GDK_COLOR_STATE_ID_REC2100_PQ] = gdk_default_srgb_to_rec2100_pq, [GDK_COLOR_STATE_ID_REC2100_LINEAR] = gdk_default_srgb_to_rec2100_linear, }, + .cicp = { 1, 13, 0, 1 }, }, [GDK_COLOR_STATE_ID_SRGB_LINEAR] = { .parent = { @@ -411,6 +515,7 @@ GdkDefaultColorState gdk_default_color_states[] = { [GDK_COLOR_STATE_ID_REC2100_PQ] = gdk_default_srgb_linear_to_rec2100_pq, [GDK_COLOR_STATE_ID_REC2100_LINEAR] = gdk_default_srgb_linear_to_rec2100_linear, }, + .cicp = { 1, 8, 0, 1 }, }, [GDK_COLOR_STATE_ID_REC2100_PQ] = { .parent = { @@ -426,6 +531,7 @@ GdkDefaultColorState gdk_default_color_states[] = { [GDK_COLOR_STATE_ID_SRGB_LINEAR] = gdk_default_rec2100_pq_to_srgb_linear, [GDK_COLOR_STATE_ID_REC2100_LINEAR] = gdk_default_rec2100_pq_to_rec2100_linear, }, + .cicp = { 9, 16, 0, 1 }, }, [GDK_COLOR_STATE_ID_REC2100_LINEAR] = { .parent = { @@ -441,9 +547,316 @@ GdkDefaultColorState gdk_default_color_states[] = { [GDK_COLOR_STATE_ID_SRGB_LINEAR] = gdk_default_rec2100_linear_to_srgb_linear, [GDK_COLOR_STATE_ID_REC2100_PQ] = gdk_default_rec2100_linear_to_rec2100_pq, }, + .cicp = { 9, 8, 0, 1 }, }, }; + /* }}} */ +/* {{{ Cicp implementation */ + +typedef struct _GdkCicpColorState GdkCicpColorState; +struct _GdkCicpColorState +{ + GdkColorState parent; + + GdkColorState *no_srgb; + + const char *name; + + GdkTransferFunc eotf; + GdkTransferFunc oetf; + + float to_srgb[3][3]; + float to_rec2020[3][3]; + float from_srgb[3][3]; + float from_rec2020[3][3]; + + GdkCicp cicp; +}; + +/* {{{ Conversion functions */ + +#define cicp ((GdkCicpColorState *)self) + +TRANSFORM(gdk_cicp_to_srgb, cicp->eotf, cicp->to_srgb, srgb_oetf) +TRANSFORM(gdk_cicp_to_srgb_linear, cicp->eotf, cicp->to_srgb, NONE) +TRANSFORM(gdk_cicp_to_rec2100_pq, cicp->eotf, cicp->to_rec2020, pq_oetf) +TRANSFORM(gdk_cicp_to_rec2100_linear, cicp->eotf, cicp->to_rec2020, NONE) +TRANSFORM(gdk_cicp_from_srgb, srgb_eotf, cicp->from_srgb, cicp->oetf) +TRANSFORM(gdk_cicp_from_srgb_linear, NONE, cicp->from_srgb, cicp->oetf) +TRANSFORM(gdk_cicp_from_rec2100_pq, pq_eotf, cicp->from_rec2020, cicp->oetf) +TRANSFORM(gdk_cicp_from_rec2100_linear, NONE, cicp->from_rec2020, cicp->oetf) + +#undef cicp + + /* }}} */ +/* {{{ Vfuncs */ + +static void +gdk_cicp_color_state_free (GdkColorState *cs) +{ + GdkCicpColorState *self = (GdkCicpColorState *) cs; + + if (self->no_srgb) + gdk_color_state_unref (self->no_srgb); + + g_free (self); +} + +static gboolean +gdk_cicp_color_state_equal (GdkColorState *self, + GdkColorState *other) +{ + GdkCicpColorState *cs1 = (GdkCicpColorState *) self; + GdkCicpColorState *cs2 = (GdkCicpColorState *) other; + + return gdk_cicp_equal (&cs1->cicp, &cs2->cicp); +} + +static const char * +gdk_cicp_color_state_get_name (GdkColorState *self) +{ + GdkCicpColorState *cs = (GdkCicpColorState *) self; + + return cs->name; +} + +static GdkColorState * +gdk_cicp_color_state_get_no_srgb_tf (GdkColorState *self) +{ + GdkCicpColorState *cs = (GdkCicpColorState *) self; + + return cs->no_srgb; +} + +static GdkFloatColorConvert +gdk_cicp_color_state_get_convert_to (GdkColorState *self, + GdkColorState *target) +{ + if (!GDK_IS_DEFAULT_COLOR_STATE (target)) + return NULL; + + switch (GDK_DEFAULT_COLOR_STATE_ID (target)) + { + case GDK_COLOR_STATE_ID_SRGB: + return gdk_cicp_to_srgb; + case GDK_COLOR_STATE_ID_SRGB_LINEAR: + return gdk_cicp_to_srgb_linear; + case GDK_COLOR_STATE_ID_REC2100_PQ: + return gdk_cicp_to_rec2100_pq; + case GDK_COLOR_STATE_ID_REC2100_LINEAR: + return gdk_cicp_to_rec2100_linear; + + case GDK_COLOR_STATE_N_IDS: + default: + g_assert_not_reached (); + } + + return NULL; +} + +static GdkFloatColorConvert +gdk_cicp_color_state_get_convert_from (GdkColorState *self, + GdkColorState *source) +{ + if (!GDK_IS_DEFAULT_COLOR_STATE (source)) + return NULL; + + switch (GDK_DEFAULT_COLOR_STATE_ID (source)) + { + case GDK_COLOR_STATE_ID_SRGB: + return gdk_cicp_from_srgb; + case GDK_COLOR_STATE_ID_SRGB_LINEAR: + return gdk_cicp_from_srgb_linear; + case GDK_COLOR_STATE_ID_REC2100_PQ: + return gdk_cicp_from_rec2100_pq; + case GDK_COLOR_STATE_ID_REC2100_LINEAR: + return gdk_cicp_from_rec2100_linear; + + case GDK_COLOR_STATE_N_IDS: + default: + g_assert_not_reached (); + } + + return NULL; +} + +static const GdkCicp * +gdk_cicp_color_state_get_cicp (GdkColorState *color_state) +{ + GdkCicpColorState *self = (GdkCicpColorState *) color_state; + + return &self->cicp; +} + +/* }}} */ + +static const +GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = { + .free = gdk_cicp_color_state_free, + .equal = gdk_cicp_color_state_equal, + .get_name = gdk_cicp_color_state_get_name, + .get_no_srgb_tf = gdk_cicp_color_state_get_no_srgb_tf, + .get_convert_to = gdk_cicp_color_state_get_convert_to, + .get_convert_from = gdk_cicp_color_state_get_convert_from, + .get_cicp = gdk_cicp_color_state_get_cicp, +}; + +static inline void +multiply (float res[3][3], + const float m1[3][3], + const float m2[3][3]) +{ + if ((float **) m1 == IDENTITY) + memcpy (res, m2, sizeof (float) * 3 * 3); + else if ((float **) m2 == IDENTITY) + memcpy (res, m1, sizeof (float) * 3 * 3); + else + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + res[i][j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] + m1[i][2] * m2[2][j]; +} + +GdkColorState * +gdk_color_state_new_for_cicp (const GdkCicp *cicp, + GError **error) +{ + GdkCicpColorState *self; + GdkTransferFunc eotf; + GdkTransferFunc oetf; + gconstpointer to_xyz; + gconstpointer from_xyz; + + if (cicp->range == GDK_CICP_RANGE_NARROW || cicp->matrix_coefficients != 0) + { + g_set_error (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + _("cicp: Narrow range or YUV not supported")); + return NULL; + } + + if (cicp->color_primaries == 2 || + cicp->transfer_function == 2 || + cicp->matrix_coefficients == 2) + { + g_set_error (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + _("cicp: Unspecified parameters not supported")); + return NULL; + } + + for (guint i = 0; i < GDK_COLOR_STATE_N_IDS; i++) + { + if (gdk_cicp_equivalent (cicp, &gdk_default_color_states[i].cicp)) + return (GdkColorState *) &gdk_default_color_states[i]; + } + + switch (cicp->transfer_function) + { + case 1: + case 6: + case 14: + case 15: + eotf = bt709_eotf; + oetf = bt709_oetf; + break; + case 8: + eotf = NONE; + oetf = NONE; + break; + case 13: + eotf = srgb_eotf; + oetf = srgb_oetf; + break; + case 16: + eotf = pq_eotf; + oetf = pq_oetf; + break; + case 18: + eotf = hlg_eotf; + oetf = hlg_oetf; + break; + default: + g_set_error (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + _("cicp: Transfer function %u not supported"), + cicp->transfer_function); + return NULL; + } + + switch (cicp->color_primaries) + { + case 1: + to_xyz = srgb_to_xyz; + from_xyz = xyz_to_srgb; + break; + case 5: + to_xyz = pal_to_xyz; + from_xyz = xyz_to_pal; + break; + case 6: + to_xyz = ntsc_to_xyz; + from_xyz = xyz_to_ntsc; + break; + case 9: + to_xyz = rec2020_to_xyz; + from_xyz = xyz_to_rec2020; + break; + case 10: + to_xyz = IDENTITY; + from_xyz = IDENTITY; + break; + case 12: + to_xyz = p3_to_xyz; + from_xyz = xyz_to_p3; + break; + default: + g_set_error (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + _("cicp: Color primaries %u not supported"), + cicp->color_primaries); + return NULL; + } + + self = g_new0 (GdkCicpColorState, 1); + + self->parent.klass = &GDK_CICP_COLOR_STATE_CLASS; + self->parent.ref_count = 1; + + /* sRGB is special-cased by being a default colorstate */ + self->parent.rendering_color_state = GDK_COLOR_STATE_REC2100_LINEAR; + + self->parent.depth = GDK_MEMORY_FLOAT16; + + memcpy (&self->cicp, cicp, sizeof (GdkCicp)); + + self->eotf = eotf; + self->oetf = oetf; + + multiply (self->to_srgb, xyz_to_srgb, to_xyz); + multiply (self->to_rec2020, xyz_to_rec2020, to_xyz); + multiply (self->from_srgb, from_xyz, srgb_to_xyz); + multiply (self->from_rec2020, from_xyz, rec2020_to_xyz); + + self->name = g_strdup_printf ("cicp-%u/%u/%u/%u", + cicp->color_primaries, + cicp->transfer_function, + cicp->matrix_coefficients, + cicp->range); + + if (cicp->transfer_function == 13) + { + GdkCicp no_srgb; + + memcpy (&no_srgb, cicp, sizeof (GdkCicp)); + no_srgb.transfer_function = 8; + + self->no_srgb = gdk_color_state_new_for_cicp (&no_srgb, NULL); + } + + return (GdkColorState *) self; +} + /* }}} */ /* {{{ Private API */ diff --git a/gdk/gdkcolorstateprivate.h b/gdk/gdkcolorstateprivate.h index 8b62f9f8ad..5a1ce07323 100644 --- a/gdk/gdkcolorstateprivate.h +++ b/gdk/gdkcolorstateprivate.h @@ -6,6 +6,8 @@ #include "gdkmemoryformatprivate.h" #include "gdkrgba.h" +#include "gdkcicpparamsprivate.h" + typedef enum { GDK_COLOR_STATE_ID_SRGB, @@ -27,6 +29,7 @@ struct _GdkColorState GdkColorState *rendering_color_state; }; +/* Note: self may be the source or the target colorstate */ typedef void (* GdkFloatColorConvert)(GdkColorState *self, float (*values)[4], gsize n_values); @@ -42,6 +45,7 @@ struct _GdkColorStateClass GdkColorState *target); GdkFloatColorConvert (* get_convert_from) (GdkColorState *self, GdkColorState *source); + const GdkCicp * (* get_cicp) (GdkColorState *self); }; typedef struct _GdkDefaultColorState GdkDefaultColorState; @@ -53,6 +57,8 @@ struct _GdkDefaultColorState const char *name; GdkColorState *no_srgb; GdkFloatColorConvert convert_to[GDK_COLOR_STATE_N_IDS]; + + GdkCicp cicp; }; extern GdkDefaultColorState gdk_default_color_states[GDK_COLOR_STATE_N_IDS]; @@ -133,6 +139,9 @@ _gdk_color_state_equal (GdkColorState *self, return self->klass->equal (self, other); } +/* Note: the functions returned from this expect the source + * color state to be passed as self + */ static inline GdkFloatColorConvert gdk_color_state_get_convert_to (GdkColorState *self, GdkColorState *target) @@ -140,6 +149,9 @@ gdk_color_state_get_convert_to (GdkColorState *self, return self->klass->get_convert_to (self, target); } +/* Note: the functions returned from this expect the target + * color state to be passed as self + */ static inline GdkFloatColorConvert gdk_color_state_get_convert_from (GdkColorState *self, GdkColorState *source) @@ -165,3 +177,12 @@ gdk_color_state_from_rgba (GdkColorState *self, convert = gdk_color_state_get_convert_to (GDK_COLOR_STATE_SRGB, self); convert (GDK_COLOR_STATE_SRGB, (float(*)[4]) out_color, 1); } + +static inline const GdkCicp * +gdk_color_state_get_cicp (GdkColorState *self) +{ + return self->klass->get_cicp (self); +} + +GdkColorState * gdk_color_state_new_for_cicp (const GdkCicp *cicp, + GError **error);