clutter/color-state: Encapsulate primaries, tf and luminance

These properties now are tagged unions:
- ClutterColorimetry:
    Can be from colorspace or primaries;
- ClutterEOTF:
    Can be from known tf or custom gamma exp (next commit);
- ClutterLuminance:
    Can be defined explicitly or derived;

Make the color management protocol use them too.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4020>
This commit is contained in:
Joan Torres 2024-09-20 12:02:28 +02:00 committed by Marge Bot
parent 8d9c70acda
commit eff17bf1b5
5 changed files with 369 additions and 304 deletions

View File

@ -67,12 +67,10 @@ typedef struct _ClutterColorStatePrivate
ClutterContext *context;
unsigned int id;
ClutterColorspace colorspace;
ClutterPrimaries *primaries;
ClutterTransferFunction transfer_function;
float min_lum;
float max_lum;
float ref_lum;
ClutterColorimetry colorimetry;
ClutterEOTF eotf;
ClutterLuminance luminance;
} ClutterColorStatePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (ClutterColorState,
@ -109,8 +107,8 @@ clutter_color_transform_key_init (ClutterColorTransformKey *key,
ClutterColorStatePrivate *target_priv =
clutter_color_state_get_instance_private (target_color_state);
key->source.transfer_function = priv->transfer_function;
key->target.transfer_function = target_priv->transfer_function;
key->source.transfer_function = priv->eotf.tf_name;
key->target.transfer_function = target_priv->eotf.tf_name;
}
static const char *
@ -130,9 +128,9 @@ clutter_colorspace_to_string (ClutterColorspace colorspace)
}
static const char *
clutter_transfer_function_to_string (ClutterTransferFunction transfer_function)
clutter_eotf_to_string (ClutterEOTF eotf)
{
switch (transfer_function)
switch (eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
return "default";
@ -159,95 +157,76 @@ clutter_color_state_get_id (ClutterColorState *color_state)
return priv->id;
}
ClutterColorspace
clutter_color_state_get_colorspace (ClutterColorState *color_state)
const ClutterColorimetry *
clutter_color_state_get_colorimetry (ClutterColorState *color_state)
{
ClutterColorStatePrivate *priv;
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state),
CLUTTER_COLORSPACE_DEFAULT);
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state), NULL);
priv = clutter_color_state_get_instance_private (color_state);
return priv->colorspace;
return &priv->colorimetry;
}
ClutterTransferFunction
clutter_color_state_get_transfer_function (ClutterColorState *color_state)
const ClutterEOTF *
clutter_color_state_get_eotf (ClutterColorState *color_state)
{
ClutterColorStatePrivate *priv;
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state),
CLUTTER_TRANSFER_FUNCTION_DEFAULT);
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state), NULL);
priv = clutter_color_state_get_instance_private (color_state);
return priv->transfer_function;
return &priv->eotf;
}
void
clutter_transfer_function_get_default_luminances (ClutterTransferFunction transfer_function,
float *min_lum_out,
float *max_lum_out,
float *ref_lum_out)
{
float min_lum = -1.0f, max_lum = -1.0f, ref_lum = -1.0f;
static const ClutterLuminance sdr_default_luminance = {
.type = CLUTTER_LUMINANCE_TYPE_DERIVED,
.min = 0.2f,
.max = 80.0f,
.ref = 80.0f,
};
switch (transfer_function)
static const ClutterLuminance pq_default_luminance = {
.type = CLUTTER_LUMINANCE_TYPE_DERIVED,
.min = 0.005f,
.max = 10000.0f,
.ref = 203.0f,
};
const ClutterLuminance *
clutter_eotf_get_default_luminance (ClutterEOTF eotf)
{
switch (eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
case CLUTTER_TRANSFER_FUNCTION_SRGB:
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
min_lum = 0.2f;
max_lum = 80.0f;
ref_lum = 80.0f;
break;
return &sdr_default_luminance;
case CLUTTER_TRANSFER_FUNCTION_PQ:
min_lum = 0.005f;
max_lum = 10000.0f;
ref_lum = 203.0f;
break;
return &pq_default_luminance;
}
if (min_lum_out)
*min_lum_out = min_lum;
if (max_lum_out)
*max_lum_out = max_lum;
if (ref_lum_out)
*ref_lum_out = ref_lum;
g_assert_not_reached ();
}
void
clutter_color_state_get_luminances (ClutterColorState *color_state,
float *min_lum_out,
float *max_lum_out,
float *ref_lum_out)
const ClutterLuminance *
clutter_color_state_get_luminance (ClutterColorState *color_state)
{
ClutterColorStatePrivate *priv;
float min_lum, max_lum, ref_lum;
g_return_if_fail (CLUTTER_IS_COLOR_STATE (color_state));
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state), NULL);
priv = clutter_color_state_get_instance_private (color_state);
clutter_transfer_function_get_default_luminances (priv->transfer_function,
&min_lum,
&max_lum,
&ref_lum);
if (priv->min_lum >= 0.0f)
min_lum = priv->min_lum;
if (priv->max_lum >= 0.0f)
max_lum = priv->max_lum;
if (priv->ref_lum >= 0.0f)
ref_lum = priv->ref_lum;
if (min_lum_out)
*min_lum_out = min_lum;
if (max_lum_out)
*max_lum_out = max_lum;
if (ref_lum_out)
*ref_lum_out = ref_lum;
switch (priv->luminance.type)
{
case CLUTTER_LUMINANCE_TYPE_DERIVED:
return clutter_eotf_get_default_luminance (priv->eotf);
case CLUTTER_LUMINANCE_TYPE_EXPLICIT:
return &priv->luminance;
}
}
void
@ -273,7 +252,8 @@ clutter_color_state_finalize (GObject *object)
ClutterColorStatePrivate *priv =
clutter_color_state_get_instance_private (color_state);
g_clear_pointer (&priv->primaries, g_free);
if (priv->colorimetry.type == CLUTTER_COLORIMETRY_TYPE_PRIMARIES)
g_clear_pointer (&priv->colorimetry.primaries, g_free);
G_OBJECT_CLASS (clutter_color_state_parent_class)->finalize (object);
}
@ -336,12 +316,31 @@ clutter_color_state_new_full (ClutterContext *context,
priv->context = context;
priv->id = clutter_color_manager_get_next_id (color_manager);
priv->colorspace = colorspace;
priv->transfer_function = transfer_function;
priv->primaries = g_memdup2 (primaries, sizeof (*primaries));
priv->min_lum = min_lum;
priv->max_lum = max_lum;
priv->ref_lum = ref_lum;
if (primaries)
{
priv->colorimetry.type = CLUTTER_COLORIMETRY_TYPE_PRIMARIES;
priv->colorimetry.primaries = g_memdup2 (primaries, sizeof (*primaries));
}
else
{
priv->colorimetry.type = CLUTTER_COLORIMETRY_TYPE_COLORSPACE;
priv->colorimetry.colorspace = colorspace;
}
priv->eotf.type = CLUTTER_EOTF_TYPE_NAMED;
priv->eotf.tf_name = transfer_function;
if (min_lum >= 0.0f && max_lum > 0.0f && ref_lum >= 0.0f)
{
priv->luminance.type = CLUTTER_LUMINANCE_TYPE_EXPLICIT;
priv->luminance.min = min_lum;
priv->luminance.max = max_lum;
priv->luminance.ref = ref_lum;
}
else
{
priv->luminance.type = CLUTTER_LUMINANCE_TYPE_DERIVED;
}
return color_state;
}
@ -462,15 +461,11 @@ append_shader_description (GString *snippet_source,
clutter_color_state_get_instance_private (color_state);
ClutterColorStatePrivate *target_priv =
clutter_color_state_get_instance_private (target_color_state);
const char *transfer_function =
clutter_transfer_function_to_string (priv->transfer_function);
const char *target_transfer_function =
clutter_transfer_function_to_string (target_priv->transfer_function);
g_string_append_printf (snippet_source,
" // %s to %s\n",
transfer_function,
target_transfer_function);
clutter_eotf_to_string (priv->eotf),
clutter_eotf_to_string (target_priv->eotf));
}
static const TransferFunction *
@ -479,7 +474,7 @@ get_eotf (ClutterColorState *color_state)
ClutterColorStatePrivate *priv =
clutter_color_state_get_instance_private (color_state);
switch (priv->transfer_function)
switch (priv->eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_PQ:
return &pq_eotf;
@ -491,7 +486,7 @@ get_eotf (ClutterColorState *color_state)
}
g_warning ("Unhandled tranfer function %s",
clutter_transfer_function_to_string (priv->transfer_function));
clutter_eotf_to_string (priv->eotf));
return NULL;
}
@ -501,7 +496,7 @@ get_inv_eotf (ClutterColorState *color_state)
ClutterColorStatePrivate *priv =
clutter_color_state_get_instance_private (color_state);
switch (priv->transfer_function)
switch (priv->eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_PQ:
return &pq_inv_eotf;
@ -513,7 +508,7 @@ get_inv_eotf (ClutterColorState *color_state)
}
g_warning ("Unhandled tranfer function %s",
clutter_transfer_function_to_string (priv->transfer_function));
clutter_eotf_to_string (priv->eotf));
return NULL;
}
@ -546,31 +541,30 @@ static const ClutterPrimaries bt2020_primaries = {
.w_x = 0.3127f, .w_y = 0.3290f,
};
const ClutterPrimaries *
clutter_color_state_get_primaries (ClutterColorState *color_state)
static const ClutterPrimaries *
get_primaries (ClutterColorState *color_state)
{
ClutterColorStatePrivate *priv;
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state),
NULL);
priv = clutter_color_state_get_instance_private (color_state);
if (priv->primaries)
return priv->primaries;
switch (priv->colorspace)
switch (priv->colorimetry.type)
{
case CLUTTER_COLORSPACE_DEFAULT:
case CLUTTER_COLORSPACE_SRGB:
return &srgb_primaries;
case CLUTTER_COLORSPACE_BT2020:
return &bt2020_primaries;
case CLUTTER_COLORIMETRY_TYPE_PRIMARIES:
return priv->colorimetry.primaries;
case CLUTTER_COLORIMETRY_TYPE_COLORSPACE:
switch (priv->colorimetry.colorspace)
{
case CLUTTER_COLORSPACE_DEFAULT:
case CLUTTER_COLORSPACE_SRGB:
return &srgb_primaries;
case CLUTTER_COLORSPACE_BT2020:
return &bt2020_primaries;
}
g_warning ("Unhandled colorspace %s",
clutter_colorspace_to_string (priv->colorimetry.colorspace));
}
g_warning ("Unhandled colorspace %s",
clutter_colorspace_to_string (priv->colorspace));
return &srgb_primaries;
}
@ -592,8 +586,8 @@ primaries_white_point_equal (ClutterColorState *color_state,
const ClutterPrimaries *primaries;
const ClutterPrimaries *other_primaries;
primaries = clutter_color_state_get_primaries (color_state);
other_primaries = clutter_color_state_get_primaries (other_color_state);
primaries = get_primaries (color_state);
other_primaries = get_primaries (other_color_state);
return chromaticity_equal (primaries->w_x, primaries->w_y,
other_primaries->w_x, other_primaries->w_y);
@ -660,8 +654,7 @@ get_color_space_trans_matrices (ClutterColorState *color_state,
graphene_matrix_t *rgb_to_xyz,
graphene_matrix_t *xyz_to_rgb)
{
const ClutterPrimaries *primaries =
clutter_color_state_get_primaries (color_state);
const ClutterPrimaries *primaries = get_primaries (color_state);
graphene_matrix_t coefficients_mat;
graphene_matrix_t inv_primaries_mat;
graphene_matrix_t primaries_mat;
@ -731,10 +724,8 @@ get_chromatic_adaptation (ClutterColorState *color_state,
ClutterColorState *target_color_state,
graphene_matrix_t *chromatic_adaptation)
{
const ClutterPrimaries *source_primaries =
clutter_color_state_get_primaries (color_state);
const ClutterPrimaries *target_primaries =
clutter_color_state_get_primaries (target_color_state);
const ClutterPrimaries *source_primaries = get_primaries (color_state);
const ClutterPrimaries *target_primaries = get_primaries (target_color_state);
graphene_matrix_t coefficients_mat;
graphene_matrix_t bradford_mat, inv_bradford_mat;
graphene_vec3_t src_white_point_XYZ, dst_white_point_XYZ;
@ -943,20 +934,15 @@ static float
get_luminance_mapping (ClutterColorState *color_state,
ClutterColorState *target_color_state)
{
float min_lum, max_lum, ref_lum;
float target_min_lum, target_max_lum, target_ref_lum;
const ClutterLuminance *lum;
const ClutterLuminance *target_lum;
clutter_color_state_get_luminances (color_state,
&min_lum, &max_lum, &ref_lum);
clutter_color_state_get_luminances (target_color_state,
&target_min_lum,
&target_max_lum,
&target_ref_lum);
lum = clutter_color_state_get_luminance (color_state);
target_lum = clutter_color_state_get_luminance (target_color_state);
/* this is a very basic, non-contrast preserving way of matching the reference
* luminance level */
return (target_ref_lum / ref_lum) * (max_lum / target_max_lum);
return (target_lum->ref / lum->ref) * (lum->max / target_lum->max);
}
void
@ -1028,25 +1014,20 @@ static gboolean
luminances_equal (ClutterColorState *color_state,
ClutterColorState *other_color_state)
{
float min_lum, max_lum, ref_lum;
float other_min_lum, other_max_lum, other_ref_lum;
const ClutterLuminance *lum;
const ClutterLuminance *other_lum;
clutter_color_state_get_luminances (color_state,
&min_lum, &max_lum, &ref_lum);
lum = clutter_color_state_get_luminance (color_state);
other_lum = clutter_color_state_get_luminance (other_color_state);
clutter_color_state_get_luminances (other_color_state,
&other_min_lum,
&other_max_lum,
&other_ref_lum);
return luminance_value_approx_equal (min_lum, other_min_lum, 0.1f) &&
luminance_value_approx_equal (max_lum, other_max_lum, 0.1f) &&
luminance_value_approx_equal (ref_lum, other_ref_lum, 0.1f);
return luminance_value_approx_equal (lum->min, other_lum->min, 0.1f) &&
luminance_value_approx_equal (lum->max, other_lum->max, 0.1f) &&
luminance_value_approx_equal (lum->ref, other_lum->ref, 0.1f);
}
static gboolean
primaries_equal (ClutterColorState *color_state,
ClutterColorState *other_color_state)
colorimetry_equal (ClutterColorState *color_state,
ClutterColorState *other_color_state)
{
ClutterColorStatePrivate *priv;
ClutterColorStatePrivate *other_priv;
@ -1056,12 +1037,12 @@ primaries_equal (ClutterColorState *color_state,
priv = clutter_color_state_get_instance_private (color_state);
other_priv = clutter_color_state_get_instance_private (other_color_state);
if (priv->colorspace != CLUTTER_COLORSPACE_DEFAULT &&
other_priv->colorspace != CLUTTER_COLORSPACE_DEFAULT)
return priv->colorspace == other_priv->colorspace;
if (priv->colorimetry.type == CLUTTER_COLORIMETRY_TYPE_COLORSPACE &&
other_priv->colorimetry.type == CLUTTER_COLORIMETRY_TYPE_COLORSPACE)
return priv->colorimetry.colorspace == other_priv->colorimetry.colorspace;
primaries = clutter_color_state_get_primaries (color_state);
other_primaries = clutter_color_state_get_primaries (other_color_state);
primaries = get_primaries (color_state);
other_primaries = get_primaries (other_color_state);
return chromaticity_equal (primaries->r_x, primaries->r_y,
other_primaries->r_x, other_primaries->r_y) &&
@ -1073,13 +1054,27 @@ primaries_equal (ClutterColorState *color_state,
other_primaries->w_x, other_primaries->w_y);
}
gboolean
clutter_color_state_equals (ClutterColorState *color_state,
ClutterColorState *other_color_state)
static gboolean
eotf_equal (ClutterColorState *color_state,
ClutterColorState *other_color_state)
{
ClutterColorStatePrivate *priv;
ClutterColorStatePrivate *other_priv;
priv = clutter_color_state_get_instance_private (color_state);
other_priv = clutter_color_state_get_instance_private (other_color_state);
if (priv->eotf.type == CLUTTER_EOTF_TYPE_NAMED &&
other_priv->eotf.type == CLUTTER_EOTF_TYPE_NAMED)
return priv->eotf.tf_name == other_priv->eotf.tf_name;
return FALSE;
}
gboolean
clutter_color_state_equals (ClutterColorState *color_state,
ClutterColorState *other_color_state)
{
if (color_state == other_color_state)
return TRUE;
@ -1089,31 +1084,28 @@ clutter_color_state_equals (ClutterColorState *color_state,
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state), FALSE);
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (other_color_state), FALSE);
priv = clutter_color_state_get_instance_private (color_state);
other_priv = clutter_color_state_get_instance_private (other_color_state);
return priv->transfer_function == other_priv->transfer_function &&
primaries_equal (color_state, other_color_state) &&
return colorimetry_equal (color_state, other_color_state) &&
eotf_equal (color_state, other_color_state) &&
luminances_equal (color_state, other_color_state);
}
static char *
primaries_to_string (ClutterColorState *color_state)
clutter_colorimetry_to_string (ClutterColorimetry colorimetry)
{
ClutterColorStatePrivate *priv;
const ClutterPrimaries *primaries;
priv = clutter_color_state_get_instance_private (color_state);
if (priv->colorspace != CLUTTER_COLORSPACE_DEFAULT)
return g_strdup (clutter_colorspace_to_string (priv->colorspace));
primaries = clutter_color_state_get_primaries (color_state);
return g_strdup_printf ("[R: %f, %f G: %f, %f B: %f, %f W: %f, %f]",
primaries->r_x, primaries->r_y,
primaries->g_x, primaries->g_y,
primaries->b_x, primaries->b_y,
primaries->w_x, primaries->w_y);
switch (colorimetry.type)
{
case CLUTTER_COLORIMETRY_TYPE_COLORSPACE:
return g_strdup (clutter_colorspace_to_string (colorimetry.colorspace));
case CLUTTER_COLORIMETRY_TYPE_PRIMARIES:
primaries = colorimetry.primaries;
return g_strdup_printf ("[R: %f, %f G: %f, %f B: %f, %f W: %f, %f]",
primaries->r_x, primaries->r_y,
primaries->g_x, primaries->g_y,
primaries->b_x, primaries->b_y,
primaries->w_x, primaries->w_y);
}
}
char *
@ -1122,18 +1114,17 @@ clutter_color_state_to_string (ClutterColorState *color_state)
ClutterColorStatePrivate *priv;
g_autofree char *primaries_name = NULL;
const char *transfer_function_name;
float min_lum, max_lum, ref_lum;
const ClutterLuminance *lum;
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state), FALSE);
priv = clutter_color_state_get_instance_private (color_state);
primaries_name = primaries_to_string (color_state);
primaries_name = clutter_colorimetry_to_string (priv->colorimetry);
transfer_function_name =
clutter_transfer_function_to_string (priv->transfer_function);
transfer_function_name = clutter_eotf_to_string (priv->eotf);
clutter_color_state_get_luminances (color_state, &min_lum, &max_lum, &ref_lum);
lum = clutter_color_state_get_luminance (color_state);
return g_strdup_printf ("ClutterColorState %d "
"(primaries: %s, transfer function: %s, "
@ -1141,9 +1132,9 @@ clutter_color_state_to_string (ClutterColorState *color_state)
priv->id,
primaries_name,
transfer_function_name,
min_lum,
max_lum,
ref_lum);
lum->min,
lum->max,
lum->ref);
}
ClutterEncodingRequiredFormat
@ -1155,7 +1146,7 @@ clutter_color_state_required_format (ClutterColorState *color_state)
priv = clutter_color_state_get_instance_private (color_state);
switch (priv->transfer_function)
switch (priv->eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
return CLUTTER_ENCODING_REQUIRED_FORMAT_FP16;
@ -1192,12 +1183,14 @@ clutter_color_state_get_blending (ClutterColorState *color_state,
{
ClutterColorStatePrivate *priv;
ClutterTransferFunction blending_tf;
ClutterColorspace colorspace;
ClutterPrimaries *primaries;
g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state), FALSE);
priv = clutter_color_state_get_instance_private (color_state);
switch (priv->transfer_function)
switch (priv->eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_PQ:
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
@ -1206,7 +1199,7 @@ clutter_color_state_get_blending (ClutterColorState *color_state,
/* effectively this means we will blend sRGB content in sRGB, not linear */
case CLUTTER_TRANSFER_FUNCTION_SRGB:
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
blending_tf = priv->transfer_function;
blending_tf = priv->eotf.tf_name;
break;
default:
g_assert_not_reached ();
@ -1215,14 +1208,26 @@ clutter_color_state_get_blending (ClutterColorState *color_state,
if (force)
blending_tf = CLUTTER_TRANSFER_FUNCTION_LINEAR;
if (blending_tf == priv->transfer_function)
if (priv->eotf.tf_name == blending_tf)
return g_object_ref (color_state);
switch (priv->colorimetry.type)
{
case CLUTTER_COLORIMETRY_TYPE_COLORSPACE:
colorspace = priv->colorimetry.colorspace;
primaries = NULL;
break;
case CLUTTER_COLORIMETRY_TYPE_PRIMARIES:
colorspace = CLUTTER_COLORSPACE_DEFAULT;
primaries = priv->colorimetry.primaries;
break;
}
return clutter_color_state_new_full (priv->context,
priv->colorspace,
colorspace,
blending_tf,
priv->primaries,
priv->min_lum,
priv->max_lum,
priv->ref_lum);
primaries,
priv->luminance.min,
priv->luminance.max,
priv->luminance.ref);
}

View File

@ -47,6 +47,23 @@ typedef enum
CLUTTER_TRANSFER_FUNCTION_LINEAR,
} ClutterTransferFunction;
typedef enum
{
CLUTTER_COLORIMETRY_TYPE_COLORSPACE,
CLUTTER_COLORIMETRY_TYPE_PRIMARIES,
} ClutterColorimetryType;
typedef enum
{
CLUTTER_EOTF_TYPE_NAMED,
} ClutterEOTFType;
typedef enum
{
CLUTTER_LUMINANCE_TYPE_DERIVED,
CLUTTER_LUMINANCE_TYPE_EXPLICIT,
} ClutterLuminanceType;
typedef struct _ClutterPrimaries
{
float r_x, r_y;
@ -55,6 +72,30 @@ typedef struct _ClutterPrimaries
float w_x, w_y;
} ClutterPrimaries;
typedef struct _ClutterColorimetry
{
ClutterColorimetryType type : 1;
union
{
ClutterColorspace colorspace;
ClutterPrimaries *primaries;
};
} ClutterColorimetry;
typedef struct _ClutterEOTF
{
ClutterEOTFType type : 1;
ClutterTransferFunction tf_name;
} ClutterEOTF;
typedef struct _ClutterLuminance
{
ClutterLuminanceType type : 1;
float min;
float max;
float ref;
} ClutterLuminance;
#define CLUTTER_TYPE_COLOR_STATE (clutter_color_state_get_type ())
CLUTTER_EXPORT
G_DECLARE_FINAL_TYPE (ClutterColorState, clutter_color_state,
@ -82,19 +123,13 @@ CLUTTER_EXPORT
unsigned int clutter_color_state_get_id (ClutterColorState *color_state);
CLUTTER_EXPORT
ClutterColorspace clutter_color_state_get_colorspace (ClutterColorState *color_state);
const ClutterColorimetry * clutter_color_state_get_colorimetry (ClutterColorState *color_state);
CLUTTER_EXPORT
ClutterTransferFunction clutter_color_state_get_transfer_function (ClutterColorState *color_state);
const ClutterEOTF * clutter_color_state_get_eotf (ClutterColorState *color_state);
CLUTTER_EXPORT
const ClutterPrimaries * clutter_color_state_get_primaries (ClutterColorState *color_state);
CLUTTER_EXPORT
void clutter_color_state_get_luminances (ClutterColorState *color_state,
float *min_lum_out,
float *max_lum_out,
float *ref_lum_out);
const ClutterLuminance * clutter_color_state_get_luminance (ClutterColorState *color_state);
CLUTTER_EXPORT
void clutter_color_state_add_pipeline_transform (ClutterColorState *color_state,
@ -119,10 +154,7 @@ ClutterColorState * clutter_color_state_get_blending (ClutterColorState *color_s
gboolean force);
CLUTTER_EXPORT
void clutter_transfer_function_get_default_luminances (ClutterTransferFunction transfer_function,
float *min_lum_out,
float *max_lum_out,
float *ref_lum_out);
const ClutterLuminance * clutter_eotf_get_default_luminance (ClutterEOTF eotf);
CLUTTER_EXPORT
void clutter_primaries_ensure_normalized_range (ClutterPrimaries *primaries);

View File

@ -639,30 +639,40 @@ get_color_space_from_monitor (MetaMonitor *monitor)
g_assert_not_reached ();
}
static ClutterTransferFunction
get_transfer_function_from_monitor (MetaMonitor *monitor)
static ClutterEOTF
get_eotf_from_monitor (MetaMonitor *monitor)
{
ClutterEOTF eotf;
const MetaOutputHdrMetadata *hdr_metadata =
meta_monitor_get_hdr_metadata (monitor);
eotf.type = CLUTTER_EOTF_TYPE_NAMED;
if (!hdr_metadata->active)
return CLUTTER_TRANSFER_FUNCTION_DEFAULT;
{
eotf.tf_name = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
return eotf;
}
switch (hdr_metadata->eotf)
{
case META_OUTPUT_HDR_METADATA_EOTF_PQ:
return CLUTTER_TRANSFER_FUNCTION_PQ;
eotf.tf_name = CLUTTER_TRANSFER_FUNCTION_PQ;
break;
case META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_SDR:
return CLUTTER_TRANSFER_FUNCTION_DEFAULT;
eotf.tf_name = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
break;
case META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_HDR:
g_warning ("Unhandled HDR EOTF (traditional gamma hdr)");
return CLUTTER_TRANSFER_FUNCTION_DEFAULT;
eotf.tf_name = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
break;
case META_OUTPUT_HDR_METADATA_EOTF_HLG:
g_warning ("Unhandled HDR EOTF (HLG)");
return CLUTTER_TRANSFER_FUNCTION_DEFAULT;
eotf.tf_name = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
break;
}
g_assert_not_reached ();
return eotf;
}
static UpdateResult
@ -676,28 +686,28 @@ update_color_state (MetaColorDevice *color_device)
ClutterContext *clutter_context = meta_backend_get_clutter_context (backend);
g_autoptr (ClutterColorState) color_state = NULL;
ClutterColorspace colorspace;
ClutterTransferFunction transfer_function;
float min_lum, max_lum, ref_lum;
ClutterEOTF eotf;
const ClutterLuminance *luminance;
float reference_luminance_factor;
float new_ref_luminance;
UpdateResult result = 0;
colorspace = get_color_space_from_monitor (monitor);
transfer_function = get_transfer_function_from_monitor (monitor);
eotf = get_eotf_from_monitor (monitor);
clutter_transfer_function_get_default_luminances (transfer_function,
&min_lum,
&max_lum,
&ref_lum);
luminance = clutter_eotf_get_default_luminance (eotf);
reference_luminance_factor =
meta_debug_control_get_luminance_percentage (debug_control) / 100.0f;
ref_lum = ref_lum * reference_luminance_factor;
new_ref_luminance = luminance->ref * reference_luminance_factor;
color_state = clutter_color_state_new_full (clutter_context,
colorspace,
transfer_function,
eotf.tf_name,
NULL,
min_lum, max_lum, ref_lum);
luminance->min,
luminance->max,
new_ref_luminance);
if (!color_device->color_state ||
!clutter_color_state_equals (color_device->color_state, color_state))

View File

@ -29,14 +29,15 @@ actor_color_state_default (void)
{
ClutterActor *actor;
ClutterColorState *color_state;
ClutterColorspace colorspace;
const ClutterColorimetry *colorimetry;
actor = clutter_actor_new ();
color_state = clutter_actor_get_color_state (actor);
colorspace = clutter_color_state_get_colorspace (color_state);
colorimetry = clutter_color_state_get_colorimetry (color_state);
g_assert_cmpuint (colorspace, ==, CLUTTER_COLORSPACE_DEFAULT);
g_assert_cmpuint (colorimetry->type, ==, CLUTTER_COLORIMETRY_TYPE_COLORSPACE);
g_assert_cmpuint (colorimetry->colorspace, ==, CLUTTER_COLORSPACE_DEFAULT);
clutter_actor_destroy (actor);
}
@ -49,8 +50,8 @@ actor_color_state_passed (void)
ClutterContext *context = clutter_test_get_context ();
ClutterActor *actor;
ClutterColorState *color_state;
ClutterColorspace colorspace;
ClutterTransferFunction transfer_function;
const ClutterColorimetry *colorimetry;
const ClutterEOTF *eotf;
color_state = clutter_color_state_new (context,
CLUTTER_COLORSPACE_BT2020,
@ -70,11 +71,13 @@ actor_color_state_passed (void)
g_critical ("Failed to create actor with provided color state.");
color_state = clutter_actor_get_color_state (actor);
colorspace = clutter_color_state_get_colorspace (color_state);
transfer_function = clutter_color_state_get_transfer_function (color_state);
colorimetry = clutter_color_state_get_colorimetry (color_state);
eotf = clutter_color_state_get_eotf (color_state);
g_assert_cmpuint (colorspace, ==, CLUTTER_COLORSPACE_BT2020);
g_assert_cmpuint (transfer_function, ==, CLUTTER_TRANSFER_FUNCTION_PQ);
g_assert_cmpuint (colorimetry->type, ==, CLUTTER_COLORIMETRY_TYPE_COLORSPACE);
g_assert_cmpuint (colorimetry->colorspace, ==, CLUTTER_COLORSPACE_BT2020);
g_assert_cmpuint (eotf->type, ==, CLUTTER_EOTF_TYPE_NAMED);
g_assert_cmpuint (eotf->tf_name, ==, CLUTTER_TRANSFER_FUNCTION_PQ);
clutter_actor_destroy (actor);
}
@ -86,8 +89,8 @@ actor_change_color_state (void)
ClutterContext *context = clutter_test_get_context ();
ClutterActor *actor;
ClutterColorState *color_state;
ClutterColorspace colorspace;
ClutterTransferFunction transfer_function;
const ClutterColorimetry *colorimetry;
const ClutterEOTF *eotf;
actor = clutter_actor_new ();
@ -101,11 +104,13 @@ actor_change_color_state (void)
clutter_actor_set_color_state (actor, color_state);
color_state = clutter_actor_get_color_state (actor);
colorspace = clutter_color_state_get_colorspace (color_state);
transfer_function = clutter_color_state_get_transfer_function (color_state);
colorimetry = clutter_color_state_get_colorimetry (color_state);
eotf = clutter_color_state_get_eotf (color_state);
g_assert_cmpuint (colorspace, ==, CLUTTER_COLORSPACE_BT2020);
g_assert_cmpuint (transfer_function, ==, CLUTTER_TRANSFER_FUNCTION_PQ);
g_assert_cmpuint (colorimetry->type, ==, CLUTTER_COLORIMETRY_TYPE_COLORSPACE);
g_assert_cmpuint (colorimetry->colorspace, ==, CLUTTER_COLORSPACE_BT2020);
g_assert_cmpuint (eotf->type, ==, CLUTTER_EOTF_TYPE_NAMED);
g_assert_cmpuint (eotf->tf_name, ==, CLUTTER_TRANSFER_FUNCTION_PQ);
clutter_actor_destroy (actor);
}
@ -115,19 +120,21 @@ actor_unset_color_state (void)
{
ClutterActor *actor;
ClutterColorState *color_state;
ClutterColorspace colorspace;
ClutterTransferFunction transfer_function;
const ClutterColorimetry *colorimetry;
const ClutterEOTF *eotf;
actor = clutter_actor_new ();
clutter_actor_unset_color_state (actor);
color_state = clutter_actor_get_color_state (actor);
colorspace = clutter_color_state_get_colorspace (color_state);
transfer_function = clutter_color_state_get_transfer_function (color_state);
colorimetry = clutter_color_state_get_colorimetry (color_state);
eotf = clutter_color_state_get_eotf (color_state);
g_assert_cmpuint (colorspace, ==, CLUTTER_COLORSPACE_DEFAULT);
g_assert_cmpuint (transfer_function, ==, CLUTTER_TRANSFER_FUNCTION_DEFAULT);
g_assert_cmpuint (colorimetry->type, ==, CLUTTER_COLORIMETRY_TYPE_COLORSPACE);
g_assert_cmpuint (colorimetry->colorspace, ==, CLUTTER_COLORSPACE_DEFAULT);
g_assert_cmpuint (eotf->type, ==, CLUTTER_EOTF_TYPE_NAMED);
g_assert_cmpuint (eotf->tf_name, ==, CLUTTER_TRANSFER_FUNCTION_DEFAULT);
clutter_actor_destroy (actor);
}

View File

@ -118,10 +118,13 @@ typedef struct _MetaWaylandCreatorParams
MetaWaylandColorManager *color_manager;
struct wl_resource *resource;
ClutterColorspace colorspace;
ClutterTransferFunction transfer_function;
ClutterPrimaries *primaries;
float min_lum, max_lum, ref_lum;
ClutterColorimetry colorimetry;
ClutterEOTF eotf;
ClutterLuminance lum;
gboolean is_colorimetry_set;
gboolean is_eotf_set;
gboolean is_luminance_set;
} MetaWaylandCreatorParams;
static void meta_wayland_color_management_surface_free (MetaWaylandColorManagementSurface *cm_surface);
@ -177,15 +180,17 @@ float_to_scaled_uint32 (float value)
static gboolean
wayland_tf_to_clutter (enum xx_color_manager_v4_transfer_function tf,
ClutterTransferFunction *tf_out)
ClutterEOTF *eotf)
{
switch (tf)
{
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB:
*tf_out = CLUTTER_TRANSFER_FUNCTION_SRGB;
eotf->type = CLUTTER_EOTF_TYPE_NAMED;
eotf->tf_name = CLUTTER_TRANSFER_FUNCTION_SRGB;
return TRUE;
case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ:
*tf_out = CLUTTER_TRANSFER_FUNCTION_PQ;
eotf->type = CLUTTER_EOTF_TYPE_NAMED;
eotf->tf_name = CLUTTER_TRANSFER_FUNCTION_PQ;
return TRUE;
default:
return FALSE;
@ -210,15 +215,17 @@ clutter_tf_to_wayland (ClutterTransferFunction tf)
static gboolean
wayland_primaries_to_clutter (enum xx_color_manager_v4_primaries primaries,
ClutterColorspace *primaries_out)
ClutterColorimetry *colorimetry)
{
switch (primaries)
{
case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB:
*primaries_out = CLUTTER_COLORSPACE_SRGB;
colorimetry->type = CLUTTER_COLORIMETRY_TYPE_COLORSPACE;
colorimetry->colorspace = CLUTTER_COLORSPACE_SRGB;
return TRUE;
case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020:
*primaries_out = CLUTTER_COLORSPACE_BT2020;
colorimetry->type = CLUTTER_COLORIMETRY_TYPE_COLORSPACE;
colorimetry->colorspace = CLUTTER_COLORSPACE_BT2020;
return TRUE;
default:
return FALSE;
@ -340,47 +347,43 @@ static void
send_information (struct wl_resource *info_resource,
ClutterColorState *color_state)
{
ClutterColorspace clutter_colorspace;
const ClutterPrimaries *clutter_primaries;
ClutterTransferFunction clutter_tf;
enum xx_color_manager_v4_primaries primaries;
enum xx_color_manager_v4_transfer_function tf;
float min_lum, max_lum, ref_lum;
const ClutterColorimetry *colorimetry;
const ClutterEOTF *eotf;
const ClutterLuminance *lum;
clutter_colorspace = clutter_color_state_get_colorspace (color_state);
if (clutter_colorspace != CLUTTER_COLORSPACE_DEFAULT)
colorimetry = clutter_color_state_get_colorimetry (color_state);
switch (colorimetry->type)
{
enum xx_color_manager_v4_primaries primaries;
primaries = clutter_primaries_to_wayland (clutter_colorspace);
case CLUTTER_COLORIMETRY_TYPE_COLORSPACE:
primaries = clutter_primaries_to_wayland (colorimetry->colorspace);
xx_image_description_info_v4_send_primaries_named (info_resource,
primaries);
}
clutter_primaries = clutter_color_state_get_primaries (color_state);
if (clutter_primaries)
{
break;
case CLUTTER_COLORIMETRY_TYPE_PRIMARIES:
xx_image_description_info_v4_send_primaries (
info_resource,
float_to_scaled_uint32 (clutter_primaries->r_x),
float_to_scaled_uint32 (clutter_primaries->r_y),
float_to_scaled_uint32 (clutter_primaries->g_x),
float_to_scaled_uint32 (clutter_primaries->g_y),
float_to_scaled_uint32 (clutter_primaries->b_x),
float_to_scaled_uint32 (clutter_primaries->b_y),
float_to_scaled_uint32 (clutter_primaries->w_x),
float_to_scaled_uint32 (clutter_primaries->w_y));
float_to_scaled_uint32 (colorimetry->primaries->r_x),
float_to_scaled_uint32 (colorimetry->primaries->r_y),
float_to_scaled_uint32 (colorimetry->primaries->g_x),
float_to_scaled_uint32 (colorimetry->primaries->g_y),
float_to_scaled_uint32 (colorimetry->primaries->b_x),
float_to_scaled_uint32 (colorimetry->primaries->b_y),
float_to_scaled_uint32 (colorimetry->primaries->w_x),
float_to_scaled_uint32 (colorimetry->primaries->w_y));
break;
}
clutter_tf = clutter_color_state_get_transfer_function (color_state);
tf = clutter_tf_to_wayland (clutter_tf);
eotf = clutter_color_state_get_eotf (color_state);
tf = clutter_tf_to_wayland (eotf->tf_name);
xx_image_description_info_v4_send_tf_named (info_resource, tf);
clutter_color_state_get_luminances (color_state,
&min_lum, &max_lum, &ref_lum);
lum = clutter_color_state_get_luminance (color_state);
xx_image_description_info_v4_send_luminances (info_resource,
float_to_scaled_uint32 (min_lum),
(uint32_t) max_lum,
(uint32_t) ref_lum);
float_to_scaled_uint32 (lum->min),
(uint32_t) lum->max,
(uint32_t) lum->ref);
}
static void
@ -825,20 +828,16 @@ meta_wayland_creator_params_new (MetaWaylandColorManager *color_manager,
creator_params->color_manager = color_manager;
creator_params->resource = resource;
creator_params->colorspace = CLUTTER_COLORSPACE_DEFAULT;
creator_params->transfer_function = CLUTTER_TRANSFER_FUNCTION_DEFAULT;
creator_params->min_lum = -1.0f;
creator_params->max_lum = -1.0f;
creator_params->ref_lum = -1.0f;
return creator_params;
}
static void
meta_wayland_creator_params_free (MetaWaylandCreatorParams *creator_params)
{
g_clear_pointer (&creator_params->primaries, g_free);
if (creator_params->is_colorimetry_set &&
creator_params->colorimetry.type == CLUTTER_COLORIMETRY_TYPE_PRIMARIES)
g_clear_pointer (&creator_params->colorimetry.primaries, g_free);
g_free (creator_params);
}
@ -862,10 +861,10 @@ creator_params_create (struct wl_client *client,
struct wl_resource *image_desc_resource;
g_autoptr (ClutterColorState) color_state = NULL;
MetaWaylandImageDescription *image_desc;
ClutterColorspace colorspace;
ClutterPrimaries *primaries;
if ((creator_params->colorspace == CLUTTER_COLORSPACE_DEFAULT &&
!creator_params->primaries) ||
creator_params->transfer_function == CLUTTER_TRANSFER_FUNCTION_DEFAULT)
if (!creator_params->is_colorimetry_set || !creator_params->is_eotf_set)
{
wl_resource_post_error (resource,
XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCOMPLETE_SET,
@ -879,14 +878,25 @@ creator_params_create (struct wl_client *client,
wl_resource_get_version (resource),
id);
color_state =
clutter_color_state_new_full (clutter_context,
creator_params->colorspace,
creator_params->transfer_function,
creator_params->primaries,
creator_params->min_lum,
creator_params->max_lum,
creator_params->ref_lum);
switch (creator_params->colorimetry.type)
{
case CLUTTER_COLORIMETRY_TYPE_COLORSPACE:
colorspace = creator_params->colorimetry.colorspace;
primaries = NULL;
break;
case CLUTTER_COLORIMETRY_TYPE_PRIMARIES:
colorspace = CLUTTER_COLORSPACE_DEFAULT;
primaries = creator_params->colorimetry.primaries;
break;
}
color_state = clutter_color_state_new_full (clutter_context,
colorspace,
creator_params->eotf.tf_name,
primaries,
creator_params->lum.min,
creator_params->lum.max,
creator_params->lum.ref);
image_desc =
meta_wayland_image_description_new_color_state (color_manager,
@ -909,9 +919,9 @@ creator_params_set_tf_named (struct wl_client *client,
{
MetaWaylandCreatorParams *creator_params =
wl_resource_get_user_data (resource);
ClutterTransferFunction clutter_tf;
ClutterEOTF eotf;
if (creator_params->transfer_function != CLUTTER_TRANSFER_FUNCTION_DEFAULT)
if (creator_params->is_eotf_set)
{
wl_resource_post_error (resource,
XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET,
@ -919,7 +929,7 @@ creator_params_set_tf_named (struct wl_client *client,
return;
}
if (!wayland_tf_to_clutter (tf, &clutter_tf))
if (!wayland_tf_to_clutter (tf, &eotf))
{
wl_resource_post_error (resource,
XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF,
@ -927,7 +937,8 @@ creator_params_set_tf_named (struct wl_client *client,
return;
}
creator_params->transfer_function = clutter_tf;
creator_params->eotf = eotf;
creator_params->is_eotf_set = TRUE;
}
static void
@ -947,10 +958,9 @@ creator_params_set_primaries_named (struct wl_client *client,
{
MetaWaylandCreatorParams *creator_params =
wl_resource_get_user_data (resource);
ClutterColorspace colorspace;
ClutterColorimetry colorimetry;
if (creator_params->colorspace != CLUTTER_COLORSPACE_DEFAULT ||
creator_params->primaries)
if (creator_params->is_colorimetry_set)
{
wl_resource_post_error (resource,
XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET,
@ -958,7 +968,7 @@ creator_params_set_primaries_named (struct wl_client *client,
return;
}
if (!wayland_primaries_to_clutter (primaries, &colorspace))
if (!wayland_primaries_to_clutter (primaries, &colorimetry))
{
wl_resource_post_error (resource,
XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES,
@ -966,7 +976,8 @@ creator_params_set_primaries_named (struct wl_client *client,
return;
}
creator_params->colorspace = colorspace;
creator_params->colorimetry = colorimetry;
creator_params->is_colorimetry_set = TRUE;
}
static void
@ -985,8 +996,7 @@ creator_params_set_primaries (struct wl_client *client,
wl_resource_get_user_data (resource);
ClutterPrimaries *primaries;
if (creator_params->colorspace != CLUTTER_COLORSPACE_DEFAULT ||
creator_params->primaries)
if (creator_params->is_colorimetry_set)
{
wl_resource_post_error (resource,
XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET,
@ -1017,7 +1027,9 @@ creator_params_set_primaries (struct wl_client *client,
clutter_primaries_ensure_normalized_range (primaries);
}
creator_params->primaries = primaries;
creator_params->colorimetry.type = CLUTTER_COLORIMETRY_TYPE_PRIMARIES;
creator_params->colorimetry.primaries = primaries;
creator_params->is_colorimetry_set = TRUE;
}
static void
@ -1031,9 +1043,7 @@ creator_params_set_luminance (struct wl_client *client,
wl_resource_get_user_data (resource);
float min, max, ref;
if (creator_params->min_lum >= 0.0f ||
creator_params->max_lum >= 0.0f ||
creator_params->ref_lum >= 0.0f)
if (creator_params->is_luminance_set)
{
wl_resource_post_error (resource,
XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET,
@ -1061,9 +1071,10 @@ creator_params_set_luminance (struct wl_client *client,
return;
}
creator_params->min_lum = min;
creator_params->max_lum = max;
creator_params->ref_lum = ref;
creator_params->lum.min = min;
creator_params->lum.max = max;
creator_params->lum.ref = ref;
creator_params->is_luminance_set = TRUE;
}
static void