Compare commits

...

5 Commits

Author SHA1 Message Date
Matthias Clasen
4b5ead5718 color: Add gdk_color_clamp_to_gamut
This is a very primitive way to get a color into gamut: just
clamp it.

Tests included.
2024-08-13 23:03:51 -04:00
Matthias Clasen
ffb3b9bdd1 colorstate: Add ranges
This will be useful in gamut handling.
2024-08-13 23:03:51 -04:00
Matthias Clasen
2a995a2f79 wayland: Support explicit primaries
Use the new primaries color state support to handle the situation
where the compositor gives us numeric primaries instead of named
ones.
2024-08-13 21:47:51 -04:00
Matthias Clasen
0adb7b8924 colorstate: Allow defining color states from primaries
The math for deriving the matrix from the primaries is taken
from

https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html#PRIMARY_CONVERSION

For a colorstate that was constructed from primaries, return
a cicp tuple of 2/tf/0/1. We interpret the 'unspecified' value
for the primaries to mean: primaries were manually specified.
2024-08-13 21:47:39 -04:00
Matthias Clasen
578428d834 Add primaries to our color states
This will be useful in the future.

The numbers here are taken from ITU-T H.273.
2024-08-13 21:47:39 -04:00
7 changed files with 673 additions and 34 deletions

View File

@@ -298,4 +298,46 @@ gdk_color_to_string (const GdkColor *self)
return g_string_free (string, FALSE);
}
gboolean
gdk_color_in_gamut (const GdkColor *self,
GdkColorState *color_state)
{
float values[4];
const float *range;
gdk_color_to_float (self, color_state, values);
range = gdk_color_state_get_range (color_state);
return range[0] <= values[0] && values[0] <= range[1] &&
range[2] <= values[1] && values[1] <= range[3] &&
range[4] <= values[2] && values[2] <= range[5] &&
range[6] <= values[3] && values[3] <= range[7];
}
void
gdk_color_clamp_to_gamut (GdkColor *self,
GdkColorState *color_state)
{
GdkColor tmp;
const float *range;
gdk_color_to_float (self, color_state, tmp.values);
range = gdk_color_state_get_range (color_state);
if (range[0] > tmp.values[0] || tmp.values[0] > range[1] ||
range[2] > tmp.values[1] || tmp.values[1] > range[3] ||
range[4] > tmp.values[2] || tmp.values[2] > range[5] ||
range[6] > tmp.values[3] || tmp.values[3] > range[7])
{
tmp.color_state = color_state;
tmp.values[0] = CLAMP (tmp.values[0], range[0], range[1]);
tmp.values[1] = CLAMP (tmp.values[1], range[2], range[3]);
tmp.values[2] = CLAMP (tmp.values[2], range[4], range[5]);
tmp.values[3] = CLAMP (tmp.values[3], range[6], range[7]);
gdk_color_to_float (&tmp, self->color_state, self->values);
}
}

View File

@@ -158,15 +158,15 @@ static const float identity[9] = {
};
static const float srgb_to_xyz[9] = {
0.4124564, 0.3575761, 0.1804375,
0.2126729, 0.7151522, 0.0721750,
0.0193339, 0.1191920, 0.9503041,
0.412391, 0.357584, 0.180481,
0.212639, 0.715169, 0.072192,
0.019331, 0.119195, 0.950532
};
static const float xyz_to_srgb[9] = {
3.2404542, -1.5371385, -0.4985314,
-0.9692660, 1.8760108, 0.0415560,
0.0556434, -0.2040259, 1.0572252,
3.240970, -1.537383, -0.498611,
-0.969244, 1.875968, 0.041555,
0.055630, -0.203977, 1.056972
};
static const float rec2020_to_xyz[9] = {
@@ -230,3 +230,56 @@ static const float srgb_to_rec2020[9] = {
0.069108, 0.919519, 0.011360,
0.016394, 0.088011, 0.895380,
};
/* primaries */
static const float srgb_primaries[8] = {
0.640, 0.330,
0.300, 0.600,
0.150, 0.060,
0.3127, 0.3290
};
static const float pal_primaries[8] = {
0.64, 0.33,
0.29, 0.60,
0.15, 0.06,
0.3127, 0.3290
};
static const float ntsc_primaries[8] = {
0.630, 0.340,
0.310, 0.595,
0.155, 0.070,
0.3127, 0.3290
};
static const float rec2020_primaries[8] = {
0.708, 0.292,
0.170, 0.797,
0.131, 0.046,
0.3127, 0.3290
};
static const float p3_primaries[8] = {
0.680, 0.320,
0.265, 0.690,
0.150, 0.060,
0.3127, 0.3290
};
/* ranges */
static const float default_range[8] = {
0, 1,
0, 1,
0, 1,
0, 1
};
static const float rec2100_linear_range[8] = {
0, 49.2610855,
0, 49.2610855,
0, 49.2610855,
0, 1,
};

View File

@@ -102,6 +102,12 @@ void gdk_color_print (const GdkColor *self,
char * gdk_color_to_string (const GdkColor *self);
gboolean gdk_color_in_gamut (const GdkColor *self,
GdkColorState *color_state);
void gdk_color_clamp_to_gamut (GdkColor *self,
GdkColorState *color_state);
#include "gdkcolorimpl.h"
G_END_DECLS

View File

@@ -24,6 +24,8 @@
#include "gdkcolordefs.h"
#include <graphene.h>
#include <glib/gi18n-lib.h>
/**
@@ -331,6 +333,22 @@ gdk_default_color_state_get_cicp (GdkColorState *color_state)
return &self->cicp;
}
static const float *
gdk_default_color_state_get_primaries (GdkColorState *color_state)
{
GdkDefaultColorState *self = (GdkDefaultColorState *) color_state;
return self->primaries;
}
static const float *
gdk_default_color_state_get_range (GdkColorState *color_state)
{
GdkDefaultColorState *self = (GdkDefaultColorState *) color_state;
return self->range;
}
/* }}} */
static const
@@ -342,6 +360,8 @@ GdkColorStateClass GDK_DEFAULT_COLOR_STATE_CLASS = {
.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,
.get_primaries = gdk_default_color_state_get_primaries,
.get_range = gdk_default_color_state_get_range,
};
GdkDefaultColorState gdk_default_color_states[] = {
@@ -359,6 +379,8 @@ 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,
},
.primaries = srgb_primaries,
.range = default_range,
.cicp = { 1, 13, 0, 1 },
},
[GDK_COLOR_STATE_ID_SRGB_LINEAR] = {
@@ -375,6 +397,8 @@ 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,
},
.primaries = srgb_primaries,
.range = default_range,
.cicp = { 1, 8, 0, 1 },
},
[GDK_COLOR_STATE_ID_REC2100_PQ] = {
@@ -391,6 +415,8 @@ 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,
},
.primaries = rec2020_primaries,
.range = default_range,
.cicp = { 9, 16, 0, 1 },
},
[GDK_COLOR_STATE_ID_REC2100_LINEAR] = {
@@ -407,11 +433,13 @@ 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,
},
.primaries = rec2020_primaries,
.range = rec2100_linear_range,
.cicp = { 9, 8, 0, 1 },
},
};
/* }}} */
/* }}} */
/* {{{ Cicp implementation */
typedef struct _GdkCicpColorState GdkCicpColorState;
@@ -432,6 +460,9 @@ struct _GdkCicpColorState
float *from_rec2020;
GdkCicp cicp;
const float *primaries;
const float *range;
};
/* {{{ Conversion functions */
@@ -554,6 +585,22 @@ gdk_cicp_color_state_get_cicp (GdkColorState *color_state)
return &self->cicp;
}
static const float *
gdk_cicp_color_state_get_primaries (GdkColorState *color_state)
{
GdkCicpColorState *self = (GdkCicpColorState *) color_state;
return self->primaries;
}
static const float *
gdk_cicp_color_state_get_range (GdkColorState *color_state)
{
GdkCicpColorState *self = (GdkCicpColorState *) color_state;
return self->range;
}
/* }}} */
static const
@@ -565,6 +612,8 @@ GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = {
.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,
.get_primaries = gdk_cicp_color_state_get_primaries,
.get_range = gdk_cicp_color_state_get_range,
};
static inline float *
@@ -591,6 +640,7 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
GdkTransferFunc oetf;
gconstpointer to_xyz;
gconstpointer from_xyz;
const float *primaries;
if (cicp->range == GDK_CICP_RANGE_NARROW || cicp->matrix_coefficients != 0)
{
@@ -660,22 +710,27 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
switch (cicp->color_primaries)
{
case 1:
primaries = srgb_primaries;
to_xyz = srgb_to_xyz;
from_xyz = xyz_to_srgb;
break;
case 5:
primaries = pal_primaries;
to_xyz = pal_to_xyz;
from_xyz = xyz_to_pal;
break;
case 6:
primaries = ntsc_primaries;
to_xyz = ntsc_to_xyz;
from_xyz = xyz_to_ntsc;
break;
case 9:
primaries = rec2020_primaries;
to_xyz = rec2020_to_xyz;
from_xyz = xyz_to_rec2020;
break;
case 12:
primaries = p3_primaries;
to_xyz = p3_to_xyz;
from_xyz = xyz_to_p3;
break;
@@ -702,6 +757,9 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
self->eotf = eotf;
self->oetf = oetf;
self->primaries = primaries;
self->range = default_range;
self->to_srgb = multiply (g_new (float, 9), xyz_to_srgb, to_xyz);
self->to_rec2020 = multiply (g_new (float, 9), xyz_to_rec2020, to_xyz);
self->from_srgb = multiply (g_new (float, 9), from_xyz, srgb_to_xyz);
@@ -726,6 +784,313 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
return (GdkColorState *) self;
}
/* }}} */
/* {{{ Primaries implementation */
typedef struct _GdkPrimariesColorState GdkPrimariesColorState;
struct _GdkPrimariesColorState
{
GdkColorState parent;
char *name;
GdkTransferFunc eotf;
GdkTransferFunc oetf;
float primaries[8];
float to_srgb[9];
float to_rec2020[9];
float from_srgb[9];
float from_rec2020[9];
GdkCicp cicp;
};
/* {{{ Conversion functions */
#define prim ((GdkPrimariesColorState *)self)
TRANSFORM(gdk_primaries_to_srgb, prim->eotf, prim->to_srgb, srgb_oetf)
TRANSFORM(gdk_primaries_to_srgb_linear, prim->eotf, prim->to_srgb, NONE)
TRANSFORM(gdk_primaries_to_rec2100_pq, prim->eotf, prim->to_rec2020, pq_oetf)
TRANSFORM(gdk_primaries_to_rec2100_linear, prim->eotf, prim->to_rec2020, NONE)
TRANSFORM(gdk_primaries_from_srgb, srgb_eotf, prim->from_srgb, prim->oetf)
TRANSFORM(gdk_primaries_from_srgb_linear, NONE, prim->from_srgb, prim->oetf)
TRANSFORM(gdk_primaries_from_rec2100_pq, pq_eotf, prim->from_rec2020, prim->oetf)
TRANSFORM(gdk_primaries_from_rec2100_linear, NONE, prim->from_rec2020, prim->oetf)
#undef prim
/* }}} */
/* {{{ Vfuncs */
static void
gdk_primaries_color_state_free (GdkColorState *cs)
{
GdkPrimariesColorState *self = (GdkPrimariesColorState *) cs;
g_free (self->name);
g_free (self);
}
static gboolean
gdk_primaries_color_state_equal (GdkColorState *self,
GdkColorState *other)
{
GdkPrimariesColorState *cs1 = (GdkPrimariesColorState *) self;
GdkPrimariesColorState *cs2 = (GdkPrimariesColorState *) other;
return memcmp (cs1->primaries, cs2->primaries, sizeof (float) * 8) == 0 &&
cs1->eotf == cs2->eotf;
}
static const char *
gdk_primaries_color_state_get_name (GdkColorState *self)
{
GdkPrimariesColorState *cs = (GdkPrimariesColorState *) self;
return cs->name;
}
static GdkColorState *
gdk_primaries_color_state_get_no_srgb_tf (GdkColorState *self)
{
return NULL;
}
static GdkFloatColorConvert
gdk_primaries_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_primaries_to_srgb;
case GDK_COLOR_STATE_ID_SRGB_LINEAR:
return gdk_primaries_to_srgb_linear;
case GDK_COLOR_STATE_ID_REC2100_PQ:
return gdk_primaries_to_rec2100_pq;
case GDK_COLOR_STATE_ID_REC2100_LINEAR:
return gdk_primaries_to_rec2100_linear;
case GDK_COLOR_STATE_N_IDS:
default:
g_assert_not_reached ();
}
return NULL;
}
static GdkFloatColorConvert
gdk_primaries_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_primaries_from_srgb;
case GDK_COLOR_STATE_ID_SRGB_LINEAR:
return gdk_primaries_from_srgb_linear;
case GDK_COLOR_STATE_ID_REC2100_PQ:
return gdk_primaries_from_rec2100_pq;
case GDK_COLOR_STATE_ID_REC2100_LINEAR:
return gdk_primaries_from_rec2100_linear;
case GDK_COLOR_STATE_N_IDS:
default:
g_assert_not_reached ();
}
return NULL;
}
static const GdkCicp *
gdk_primaries_color_state_get_cicp (GdkColorState *color_state)
{
GdkPrimariesColorState *self = (GdkPrimariesColorState *) color_state;
return &self->cicp;
}
static const float *
gdk_primaries_color_state_get_primaries (GdkColorState *color_state)
{
GdkPrimariesColorState *self = (GdkPrimariesColorState *) color_state;
return self->primaries;
}
/* }}} */
static const
GdkColorStateClass GDK_PRIMARIES_COLOR_STATE_CLASS = {
.free = gdk_primaries_color_state_free,
.equal = gdk_primaries_color_state_equal,
.get_name = gdk_primaries_color_state_get_name,
.get_no_srgb_tf = gdk_primaries_color_state_get_no_srgb_tf,
.get_convert_to = gdk_primaries_color_state_get_convert_to,
.get_convert_from = gdk_primaries_color_state_get_convert_from,
.get_cicp = gdk_primaries_color_state_get_cicp,
.get_primaries = gdk_primaries_color_state_get_primaries,
};
static void
compute_to_xyz_from_primaries (float to_xyz[9],
const float primaries[8])
{
float rx, ry, gx, gy, bx, by, wx, wy;
float rY, bY, gY;
rx = primaries[0]; ry = primaries[1];
gx = primaries[2]; gy = primaries[3];
bx = primaries[4]; by = primaries[5];
wx = primaries[6]; wy = primaries[7];
bY = (((1 - wx)/wy - (1 - rx)/ry)*(gx/gy - rx/ry) - (wx/wy - rx/ry)*((1 - gx)/gy - (1 - rx)/ry)) /
(((1 - bx)/by - (1 - rx)/ry)*(gx/gy - rx/ry) - (bx/by - rx/ry)*((1 - gx)/gy - (1 - rx)/ry));
gY = (wx/wy - rx/ry - bY*(bx/by - rx/ry)) / (gx/gy - rx/ry);
rY = 1 - gY - bY;
to_xyz[0] = rY/ry * rx; to_xyz[1] = gY/gy * gx; to_xyz[2] = bY/by * bx;
to_xyz[3] = rY; to_xyz[4] = gY; to_xyz[5] = bY;
to_xyz[6] = rY/ry * (1-rx-ry); to_xyz[7] = gY/gy * (1-gx-gy); to_xyz[8] = bY/by * (1-bx-by);
}
static void
invert (float res[9],
const float a[9])
{
graphene_matrix_t m, m2;
float v[16];
graphene_matrix_init_from_float (&m,
(const float *)&(float[]) {
a[0], a[1], a[2], 0,
a[3], a[4], a[5], 0,
a[6], a[7], a[8], 0,
0, 0, 0, 1
});
graphene_matrix_inverse (&m, &m2);
graphene_matrix_to_float (&m2, v);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
res[3 * i + j] = v[4 * i + j];
}
GdkColorState *
gdk_color_state_new_for_primaries (const float primaries[8],
guint transfer_function,
GError **error)
{
GdkPrimariesColorState *self;
float to_xyz[9];
float from_xyz[9];
GdkTransferFunc eotf;
GdkTransferFunc oetf;
if (memcmp (primaries, srgb_primaries, sizeof (float) * 8) == 0)
return gdk_color_state_new_for_cicp (&(GdkCicp) { 1, transfer_function, 0, GDK_CICP_RANGE_FULL },
error);
else if (memcmp (primaries, pal_primaries, sizeof (float) * 8) == 0)
return gdk_color_state_new_for_cicp (&(GdkCicp) { 5, transfer_function, 0, GDK_CICP_RANGE_FULL },
error);
else if (memcmp (primaries, ntsc_primaries, sizeof (float) * 8) == 0)
return gdk_color_state_new_for_cicp (&(GdkCicp) { 6, transfer_function, 0, GDK_CICP_RANGE_FULL },
error);
else if (memcmp (primaries, rec2020_primaries, sizeof (float) * 8) == 0)
return gdk_color_state_new_for_cicp (&(GdkCicp) { 9, transfer_function, 0, GDK_CICP_RANGE_FULL },
error);
else if (memcmp (primaries, p3_primaries, sizeof (float) * 8) == 0)
return gdk_color_state_new_for_cicp (&(GdkCicp) { 12, transfer_function, 0, GDK_CICP_RANGE_FULL },
error);
compute_to_xyz_from_primaries (to_xyz, primaries);
invert (from_xyz, to_xyz);
switch (transfer_function)
{
case 1:
case 6:
case 14:
case 15:
eotf = bt709_eotf;
oetf = bt709_oetf;
break;
case 4:
eotf = gamma22_eotf;
oetf = gamma22_oetf;
break;
case 5:
eotf = gamma28_eotf;
oetf = gamma28_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,
_("primaries: Transfer function %u not supported"),
transfer_function);
return NULL;
}
self = g_new0 (GdkPrimariesColorState, 1);
self->parent.klass = &GDK_PRIMARIES_COLOR_STATE_CLASS;
self->parent.ref_count = 1;
self->parent.rendering_color_state = GDK_COLOR_STATE_REC2100_LINEAR;
self->parent.depth = GDK_MEMORY_FLOAT16;
self->eotf = eotf;
self->oetf = oetf;
self->cicp.color_primaries = 2;
self->cicp.transfer_function = transfer_function;
self->cicp.matrix_coefficients = 0;
self->cicp.range = GDK_CICP_RANGE_FULL;
memcpy (self->primaries, primaries, sizeof (float) * 8);
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 ("primaries(%f,%f,%f,%f,%f,%f,%f,%f)",
primaries[0], primaries[1], primaries[2],
primaries[3], primaries[4], primaries[5],
primaries[6], primaries[7]);
return (GdkColorState *) self;
}
/* }}} */
/* {{{ Private API */

View File

@@ -45,6 +45,8 @@ struct _GdkColorStateClass
GdkFloatColorConvert (* get_convert_from) (GdkColorState *self,
GdkColorState *source);
const GdkCicp * (* get_cicp) (GdkColorState *self);
const float * (* get_primaries) (GdkColorState *self);
const float * (* get_range) (GdkColorState *self);
};
typedef struct _GdkDefaultColorState GdkDefaultColorState;
@@ -58,6 +60,9 @@ struct _GdkDefaultColorState
GdkFloatColorConvert convert_to[GDK_COLOR_STATE_N_IDS];
GdkCicp cicp;
const float *primaries;
const float *range;
};
extern GdkDefaultColorState gdk_default_color_states[GDK_COLOR_STATE_N_IDS];
@@ -77,6 +82,10 @@ GdkColorState * gdk_color_state_get_no_srgb_tf (GdkColorState
GdkColorState * gdk_color_state_new_for_cicp (const GdkCicp *cicp,
GError **error);
GdkColorState * gdk_color_state_new_for_primaries (const float primaries[8],
guint transfer_function,
GError **error);
static inline GdkColorState *
gdk_color_state_get_rendering_color_state (GdkColorState *self)
{
@@ -167,6 +176,18 @@ gdk_color_state_get_cicp (GdkColorState *self)
return self->klass->get_cicp (self);
}
static inline const float *
gdk_color_state_get_primaries (GdkColorState *self)
{
return self->klass->get_primaries (self);
}
static inline const float *
gdk_color_state_get_range (GdkColorState *self)
{
return self->klass->get_range (self);
}
static inline void
gdk_color_state_convert_color (GdkColorState *src_cs,
const float src[4],
@@ -213,4 +234,3 @@ gdk_color_state_from_rgba (GdkColorState *self,
self,
out_color);
}

View File

@@ -67,6 +67,18 @@ cicp_to_wl_transfer (uint tf)
return 0;
}
static inline float
wl_to_primary_coord (uint p)
{
return p / 10000.0;
}
static inline uint
wl_from_primary_coord (float p)
{
return p * 10000;
}
struct _GdkWaylandColor
{
GdkWaylandDisplay *display;
@@ -281,50 +293,85 @@ create_image_desc (GdkWaylandColor *color,
gboolean sync)
{
CsImageDescListenerData data;
struct xx_image_description_creator_params_v4 *creator;
struct xx_image_description_creator_params_v4 *creator = NULL;
struct xx_image_description_v4 *desc;
const GdkCicp *cicp;
GdkCicp norm;
uint32_t primaries, tf;
cicp = gdk_color_state_get_cicp (cs);
if (!cicp)
if (cicp && cicp->color_primaries != 2)
{
GDK_DEBUG (MISC, "Unsupported color state %s: Not a CICP colorstate",
gdk_color_state_get_name (cs));
g_hash_table_insert (color->cs_to_desc, gdk_color_state_ref (cs), NULL);
return;
}
gdk_cicp_normalize (cicp, &norm);
primaries = cicp_to_wl_primaries (norm.color_primaries);
tf = cicp_to_wl_transfer (norm.transfer_function);
GdkCicp norm;
uint32_t primaries_named;
uint32_t tf_named;
if ((color->color_manager_supported.primaries & (1 << primaries)) == 0 ||
(color->color_manager_supported.transfers & (1 << tf)) == 0)
gdk_cicp_normalize (cicp, &norm);
primaries_named = cicp_to_wl_primaries (norm.color_primaries);
tf_named = cicp_to_wl_transfer (norm.transfer_function);
if ((color->color_manager_supported.primaries & (1 << primaries_named)) == 0 ||
(color->color_manager_supported.transfers & (1 << tf_named)) == 0)
{
GDK_DEBUG (MISC, "Unsupported color state %s: Primaries or transfer function unsupported",
gdk_color_state_get_name (cs));
g_hash_table_insert (color->cs_to_desc, gdk_color_state_ref (cs), NULL);
return;
}
creator = xx_color_manager_v4_new_parametric_creator (color->color_manager);
xx_image_description_creator_params_v4_set_primaries_named (creator, primaries_named);
xx_image_description_creator_params_v4_set_tf_named (creator, tf_named);
}
else if (cicp && cicp->color_primaries == 2)
{
GDK_DEBUG (MISC, "Unsupported color state %s: Primaries or transfer function unsupported",
const float *primaries = gdk_color_state_get_primaries (cs);
GdkCicp norm;
uint32_t tf_named;
gdk_cicp_normalize (cicp, &norm);
tf_named = cicp_to_wl_transfer (norm.transfer_function);
if ((color->color_manager_supported.features & (1 << XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_SET_PRIMARIES)) == 0 ||
(color->color_manager_supported.transfers & (1 << tf_named)) == 0)
{
GDK_DEBUG (MISC, "Unsupported color state %s: Primaries or transfer function unsupported",
gdk_color_state_get_name (cs));
g_hash_table_insert (color->cs_to_desc, gdk_color_state_ref (cs), NULL);
return;
}
creator = xx_color_manager_v4_new_parametric_creator (color->color_manager);
xx_image_description_creator_params_v4_set_primaries (creator,
wl_from_primary_coord (primaries[0]),
wl_from_primary_coord (primaries[1]),
wl_from_primary_coord (primaries[2]),
wl_from_primary_coord (primaries[3]),
wl_from_primary_coord (primaries[4]),
wl_from_primary_coord (primaries[5]),
wl_from_primary_coord (primaries[6]),
wl_from_primary_coord (primaries[7]));
xx_image_description_creator_params_v4_set_tf_named (creator, tf_named);
}
else
{
GDK_DEBUG (MISC, "Unsupported color state %s: Not a CICP colorstate and no primaries",
gdk_color_state_get_name (cs));
g_hash_table_insert (color->cs_to_desc, gdk_color_state_ref (cs), NULL);
return;
}
desc = xx_image_description_creator_params_v4_create (creator);
data.color = color;
data.color_state = cs;
data.sync = sync;
data.done = FALSE;
creator = xx_color_manager_v4_new_parametric_creator (color->color_manager);
xx_image_description_creator_params_v4_set_primaries_named (creator, primaries);
xx_image_description_creator_params_v4_set_tf_named (creator, tf);
desc = xx_image_description_creator_params_v4_create (creator);
if (sync)
{
struct wl_event_queue *event_queue;
event_queue = wl_display_create_queue (color->display->wl_display);
wl_proxy_set_queue ((struct wl_proxy *) desc, event_queue);
xx_image_description_v4_add_listener (desc, &cs_image_desc_listener, &data);
@@ -478,8 +525,23 @@ gdk_color_state_from_image_description_bits (ImageDescription *desc)
return gdk_color_state_new_for_cicp (&cicp, NULL);
}
else
return NULL;
else if (desc->has_primaries && desc->has_tf_named)
{
float primaries[8];
primaries[0] = wl_to_primary_coord (desc->r_x);
primaries[1] = wl_to_primary_coord (desc->r_y);
primaries[2] = wl_to_primary_coord (desc->g_x);
primaries[3] = wl_to_primary_coord (desc->g_y);
primaries[4] = wl_to_primary_coord (desc->b_x);
primaries[5] = wl_to_primary_coord (desc->b_y);
primaries[6] = wl_to_primary_coord (desc->w_x);
primaries[7] = wl_to_primary_coord (desc->w_y);
return gdk_color_state_new_for_primaries (primaries, desc->tf_named, NULL);
}
return NULL;
}
static void

View File

@@ -51,6 +51,20 @@ static MatrixTest matrices[] = {
{ "srgb<>rec2020", rec2020_to_srgb, srgb_to_rec2020 },
};
typedef struct
{
const char *name;
const float *primaries;
const float *to_xyz;
} PrimaryTest;
static PrimaryTest primary_tests[] = {
{ "srgb", srgb_primaries, srgb_to_xyz },
{ "pal", pal_primaries, pal_to_xyz },
{ "ntsc", ntsc_primaries, ntsc_to_xyz },
{ "rec2020", rec2020_primaries, rec2020_to_xyz },
{ "p3", p3_primaries, p3_to_xyz },
};
#define IDX(i,j) 3*i+j
static inline void
multiply (float res[9],
@@ -113,6 +127,44 @@ test_matrix (gconstpointer data)
g_assert_cmpfloat_with_epsilon (norm (res2), 0, 0.001);
}
static void
compute_to_xyz_from_primaries (const float primaries[8],
float to_xyz[9])
{
float rx, ry, gx, gy, bx, by, wx, wy;
float rY, bY, gY;
rx = primaries[0]; ry = primaries[1];
gx = primaries[2]; gy = primaries[3];
bx = primaries[4]; by = primaries[5];
wx = primaries[6]; wy = primaries[7];
bY = (((1 - wx)/wy - (1 - rx)/ry)*(gx/gy - rx/ry) - (wx/wy - rx/ry)*((1 - gx)/gy - (1 - rx)/ry)) /
(((1 - bx)/by - (1 - rx)/ry)*(gx/gy - rx/ry) - (bx/by - rx/ry)*((1 - gx)/gy - (1 - rx)/ry));
gY = (wx/wy - rx/ry - bY*(bx/by - rx/ry)) / (gx/gy - rx/ry);
rY = 1 - gY - bY;
to_xyz[0] = rY/ry * rx; to_xyz[1] = gY/gy * gx; to_xyz[2] = bY/by * bx;
to_xyz[3] = rY; to_xyz[4] = gY; to_xyz[5] = bY;
to_xyz[6] = rY/ry * (1-rx-ry); to_xyz[7] = gY/gy * (1-gx-gy); to_xyz[8] = bY/by * (1-bx-by);
}
static void
test_primaries (gconstpointer data)
{
float res[9], res2[9];
PrimaryTest *test = (PrimaryTest *) data;
compute_to_xyz_from_primaries (test->primaries, res);
difference (res2, res, test->to_xyz);
g_assert_cmpfloat_with_epsilon (norm (res2), 0, 0.00001);
}
static void
test_srgb_to_rec2020 (void)
{
@@ -158,6 +210,36 @@ test_color_mislabel (void)
g_assert_true (red1 != red2);
}
static void
test_color_gamut (void)
{
GdkColor color = GDK_COLOR_SRGB (1, 0, 0, 1);
g_assert_true (gdk_color_in_gamut (&color, GDK_COLOR_STATE_SRGB));
g_assert_true (gdk_color_in_gamut (&color, GDK_COLOR_STATE_SRGB_LINEAR));
g_assert_true (gdk_color_in_gamut (&color, GDK_COLOR_STATE_REC2100_PQ));
g_assert_true (gdk_color_in_gamut (&color, GDK_COLOR_STATE_REC2100_LINEAR));
gdk_color_finish (&color);
gdk_color_init (&color, GDK_COLOR_STATE_REC2100_PQ, (float[]) {0.9, 0.9, 0.9, 1});
g_assert_false (gdk_color_in_gamut (&color, GDK_COLOR_STATE_SRGB));
g_assert_false (gdk_color_in_gamut (&color, GDK_COLOR_STATE_SRGB_LINEAR));
g_assert_true (gdk_color_in_gamut (&color, GDK_COLOR_STATE_REC2100_PQ));
g_assert_true (gdk_color_in_gamut (&color, GDK_COLOR_STATE_REC2100_LINEAR));
gdk_color_clamp_to_gamut (&color, GDK_COLOR_STATE_SRGB);
/* clamping an out-of-gamut gray to srgb yields media white */
g_assert_cmpfloat_with_epsilon (color.red, 0.58, 0.001);
g_assert_cmpfloat_with_epsilon (color.green, 0.58, 0.001);
g_assert_cmpfloat_with_epsilon (color.blue, 0.58, 0.001);
g_assert_cmpfloat_with_epsilon (color.alpha, 1, 0.001);
gdk_color_finish (&color);
}
int
main (int argc, char *argv[])
{
@@ -179,9 +261,18 @@ main (int argc, char *argv[])
g_free (path);
}
for (guint i = 0; i < G_N_ELEMENTS (primary_tests); i++)
{
PrimaryTest *test = &primary_tests[i];
char *path = g_strdup_printf ("/colorstate/primaries/%s", test->name);
g_test_add_data_func (path, test, test_primaries);
g_free (path);
}
g_test_add_func ("/colorstate/matrix/srgb_to_rec2020", test_srgb_to_rec2020);
g_test_add_func ("/colorstate/matrix/rec2020_to_srgb", test_rec2020_to_srgb);
g_test_add_func ("/color/mislabel", test_color_mislabel);
g_test_add_func ("/color/gamut", test_color_gamut);
return g_test_run ();
}