From 03aad0d99eeeb10d896cd5f7429660a1d2373149 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Fri, 19 Jul 2024 00:08:51 +0200 Subject: [PATCH] clutter/color-state: Add min/max/ref luminances They represent the minimum and maximum luminance levels of the primary color volume and the reference luminance level (reference white, SDR white, ...) in the reference viewing environment. They help anchoring the white level, optionally help with preserving the dynamic range and help with adjusting from a "dark" to a "bright" viewing environment. The values have defaults which depend on the transfer characteristics. This reflects the wayland color management protocol. Part-of: --- clutter/clutter/clutter-color-state.c | 217 ++++++++++++++++++++++++-- clutter/clutter/clutter-color-state.h | 20 +++ 2 files changed, 226 insertions(+), 11 deletions(-) diff --git a/clutter/clutter/clutter-color-state.c b/clutter/clutter/clutter-color-state.c index d6dec5075..bb9e6c271 100644 --- a/clutter/clutter/clutter-color-state.c +++ b/clutter/clutter/clutter-color-state.c @@ -61,6 +61,9 @@ enum PROP_CONTEXT, PROP_COLORSPACE, PROP_TRANSFER_FUNCTION, + PROP_MIN_LUMINANCE, + PROP_MAX_LUMINANCE, + PROP_REF_LUMINANCE, N_PROPS }; @@ -79,6 +82,9 @@ typedef struct _ClutterColorStatePrivate unsigned int id; ClutterColorspace colorspace; ClutterTransferFunction transfer_function; + float min_lum; + float max_lum; + float ref_lum; } ClutterColorStatePrivate; G_DEFINE_TYPE_WITH_PRIVATE (ClutterColorState, @@ -197,6 +203,71 @@ clutter_color_state_get_transfer_function (ClutterColorState *color_state) return priv->transfer_function; } +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; + + switch (transfer_function) + { + 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; + case CLUTTER_TRANSFER_FUNCTION_PQ: + min_lum = 0.005f; + max_lum = 10000.0f; + ref_lum = 203.0f; + break; + } + + 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; +} + +void +clutter_color_state_get_luminances (ClutterColorState *color_state, + float *min_lum_out, + float *max_lum_out, + float *ref_lum_out) +{ + ClutterColorStatePrivate *priv; + float min_lum, max_lum, ref_lum; + + g_return_if_fail (CLUTTER_IS_COLOR_STATE (color_state)); + + 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; +} + static void clutter_color_state_constructed (GObject *object) { @@ -237,6 +308,18 @@ clutter_color_state_set_property (GObject *object, priv->transfer_function = g_value_get_enum (value); break; + case PROP_MIN_LUMINANCE: + priv->min_lum = g_value_get_float (value); + break; + + case PROP_MAX_LUMINANCE: + priv->max_lum = g_value_get_float (value); + break; + + case PROP_REF_LUMINANCE: + priv->ref_lum = g_value_get_float (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -260,13 +343,23 @@ clutter_color_state_get_property (GObject *object, break; case PROP_COLORSPACE: - g_value_set_enum (value, - clutter_color_state_get_colorspace (color_state)); + g_value_set_enum (value, priv->colorspace); break; case PROP_TRANSFER_FUNCTION: - g_value_set_enum (value, - clutter_color_state_get_transfer_function (color_state)); + g_value_set_enum (value, priv->transfer_function); + break; + + case PROP_MIN_LUMINANCE: + g_value_set_float (value, priv->min_lum); + break; + + case PROP_MAX_LUMINANCE: + g_value_set_float (value, priv->max_lum); + break; + + case PROP_REF_LUMINANCE: + g_value_set_float (value, priv->ref_lum); break; default: @@ -323,6 +416,42 @@ clutter_color_state_class_init (ClutterColorStateClass *klass) G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + /** + * ClutterColorState:min-luminance: + * + * Minimum luminance. + */ + obj_props[PROP_MIN_LUMINANCE] = + g_param_spec_float ("min-luminance", NULL, NULL, + -1.0f, 10000.0f, 0.0f, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + + /** + * ClutterColorState:max-luminance: + * + * Maximum luminance. + */ + obj_props[PROP_MAX_LUMINANCE] = + g_param_spec_float ("max-luminance", NULL, NULL, + -1.0f, 10000.0f, 0.0f, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + + /** + * ClutterColorState:ref-luminance: + * + * Reference luminance. + */ + obj_props[PROP_REF_LUMINANCE] = + g_param_spec_float ("ref-luminance", NULL, NULL, + -1.0f, 10000.0f, 0.0f, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_properties (gobject_class, N_PROPS, obj_props); } @@ -342,11 +471,35 @@ ClutterColorState * clutter_color_state_new (ClutterContext *context, ClutterColorspace colorspace, ClutterTransferFunction transfer_function) +{ + return clutter_color_state_new_full (context, + colorspace, transfer_function, + -1.0f, -1.0f, -1.0f); +} + +/** + * clutter_color_state_new_full: + * + * Create a new ClutterColorState object with all possible parameters. Some + * arguments might not be valid to set with other arguments. + * + * Return value: A new ClutterColorState object. + **/ +ClutterColorState * +clutter_color_state_new_full (ClutterContext *context, + ClutterColorspace colorspace, + ClutterTransferFunction transfer_function, + float min_lum, + float max_lum, + float ref_lum) { return g_object_new (CLUTTER_TYPE_COLOR_STATE, "context", context, "colorspace", colorspace, "transfer-function", transfer_function, + "min-luminance", min_lum, + "max-luminance", max_lum, + "ref-luminance", ref_lum, NULL); } @@ -836,6 +989,37 @@ clutter_color_state_add_pipeline_transform (ClutterColorState *color_state, cogl_pipeline_add_snippet (pipeline, snippet); } +static gboolean +luminance_value_approx_equal (float lum, + float other_lum, + float epsilon) +{ + if (lum == 0.0f || other_lum == 0.0f) + return lum == other_lum; + + return G_APPROX_VALUE (lum / other_lum, 1.0f, epsilon); +} + +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; + + clutter_color_state_get_luminances (color_state, + &min_lum, &max_lum, &ref_lum); + + 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); +} + gboolean clutter_color_state_equals (ClutterColorState *color_state, ClutterColorState *other_color_state) @@ -855,8 +1039,9 @@ clutter_color_state_equals (ClutterColorState *color_state, priv = clutter_color_state_get_instance_private (color_state); other_priv = clutter_color_state_get_instance_private (other_color_state); - return (priv->colorspace == other_priv->colorspace && - priv->transfer_function == other_priv->transfer_function); + return priv->colorspace == other_priv->colorspace && + priv->transfer_function == other_priv->transfer_function && + luminances_equal (color_state, other_color_state); } static char * @@ -884,6 +1069,7 @@ clutter_color_state_to_string (ClutterColorState *color_state) ClutterColorStatePrivate *priv; g_autofree char *colorspace_name = NULL; g_autofree char *transfer_function_name = NULL; + float min_lum, max_lum, ref_lum; g_return_val_if_fail (CLUTTER_IS_COLOR_STATE (color_state), FALSE); @@ -893,11 +1079,17 @@ clutter_color_state_to_string (ClutterColorState *color_state) transfer_function_name = enum_to_string (CLUTTER_TYPE_TRANSFER_FUNCTION, priv->transfer_function); + clutter_color_state_get_luminances (color_state, &min_lum, &max_lum, &ref_lum); + return g_strdup_printf ("ClutterColorState %d " - "(colorspace: %s, transfer function: %s)", + "(colorspace: %s, transfer function: %s, " + "min lum: %f, max lum: %f, ref lum: %f)", priv->id, colorspace_name, - transfer_function_name); + transfer_function_name, + min_lum, + max_lum, + ref_lum); } ClutterEncodingRequiredFormat @@ -972,7 +1164,10 @@ clutter_color_state_get_blending (ClutterColorState *color_state, if (blending_tf == priv->transfer_function) return g_object_ref (color_state); - return clutter_color_state_new (priv->context, - priv->colorspace, - blending_tf); + return clutter_color_state_new_full (priv->context, + priv->colorspace, + blending_tf, + priv->min_lum, + priv->max_lum, + priv->ref_lum); } diff --git a/clutter/clutter/clutter-color-state.h b/clutter/clutter/clutter-color-state.h index c2afc66b3..d2a5d7792 100644 --- a/clutter/clutter/clutter-color-state.h +++ b/clutter/clutter/clutter-color-state.h @@ -43,6 +43,14 @@ ClutterColorState * clutter_color_state_new (ClutterContext *context, ClutterColorspace colorspace, ClutterTransferFunction transfer_function); +CLUTTER_EXPORT +ClutterColorState * clutter_color_state_new_full (ClutterContext *context, + ClutterColorspace colorspace, + ClutterTransferFunction transfer_function, + float min_lum, + float max_lum, + float ref_lum); + CLUTTER_EXPORT char * clutter_color_state_to_string (ClutterColorState *color_state); @@ -55,6 +63,12 @@ ClutterColorspace clutter_color_state_get_colorspace (ClutterColorState *color_s CLUTTER_EXPORT ClutterTransferFunction clutter_color_state_get_transfer_function (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); + CLUTTER_EXPORT void clutter_color_state_add_pipeline_transform (ClutterColorState *color_state, ClutterColorState *target_color_state, @@ -71,4 +85,10 @@ CLUTTER_EXPORT ClutterColorState * clutter_color_state_get_blending (ClutterColorState *color_state, 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); + G_END_DECLS