From fcbd2e38403ea4ae6907ff698bc584d72ae54eb6 Mon Sep 17 00:00:00 2001 From: Joan Torres Date: Thu, 12 Sep 2024 16:18:54 +0200 Subject: [PATCH] clutter/color-state: Add support for gamma TF This TF can't be defined as a TransferFunction enum because it needs a gamma_exp value too. Add to EOTFType enum a new type: EOTF_TYPE_GAMMA. With this new type, now EOTFs are unions that can have either a TransferFunction enum or a gamma_exp. Set gamma_exp as uniform. Add the support of it in the color management protocol. Part-of: --- clutter/clutter/clutter-color-state-private.h | 4 +- clutter/clutter/clutter-color-state.c | 241 ++++++++++++++---- clutter/clutter/clutter-color-state.h | 8 +- src/backends/meta-color-device.c | 17 +- src/wayland/meta-wayland-color-management.c | 76 +++++- 5 files changed, 285 insertions(+), 61 deletions(-) diff --git a/clutter/clutter/clutter-color-state-private.h b/clutter/clutter/clutter-color-state-private.h index 96b94d8fb..b44ff9539 100644 --- a/clutter/clutter/clutter-color-state-private.h +++ b/clutter/clutter/clutter-color-state-private.h @@ -27,10 +27,10 @@ typedef struct _ClutterColorTransformKey { struct { - ClutterTransferFunction transfer_function; + guint eotf_key; } source; struct { - ClutterTransferFunction transfer_function; + guint eotf_key; } target; } ClutterColorTransformKey; diff --git a/clutter/clutter/clutter-color-state.c b/clutter/clutter/clutter-color-state.c index f52b227cf..06716bade 100644 --- a/clutter/clutter/clutter-color-state.c +++ b/clutter/clutter/clutter-color-state.c @@ -56,6 +56,8 @@ #define UNIFORM_NAME_LUMINANCE_MAPPING "luminance_mapping" #define UNIFORM_NAME_COLOR_SPACE_MAPPING "color_space_mapping" +#define UNIFORM_NAME_GAMMA_EXP "gamma_exp" +#define UNIFORM_NAME_INV_GAMMA_EXP "inv_gamma_exp" struct _ClutterColorState { @@ -82,8 +84,8 @@ clutter_color_transform_key_hash (gconstpointer data) { const ClutterColorTransformKey *key = data; - return (key->source.transfer_function ^ - key->target.transfer_function); + return (key->source.eotf_key ^ + key->target.eotf_key); } gboolean @@ -93,8 +95,20 @@ clutter_color_transform_key_equal (gconstpointer data1, const ClutterColorTransformKey *key1 = data1; const ClutterColorTransformKey *key2 = data2; - return (key1->source.transfer_function == key2->source.transfer_function && - key1->target.transfer_function == key2->target.transfer_function); + return (key1->source.eotf_key == key2->source.eotf_key && + key1->target.eotf_key == key2->target.eotf_key); +} + +static guint +get_eotf_key (ClutterEOTF eotf) +{ + switch (eotf.type) + { + case CLUTTER_EOTF_TYPE_NAMED: + return eotf.tf_name << 1; + case CLUTTER_EOTF_TYPE_GAMMA: + return 1; + } } void @@ -107,8 +121,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->eotf.tf_name; - key->target.transfer_function = target_priv->eotf.tf_name; + key->source.eotf_key = get_eotf_key (priv->eotf); + key->target.eotf_key = get_eotf_key (target_priv->eotf); } static const char * @@ -128,14 +142,20 @@ clutter_colorspace_to_string (ClutterColorspace colorspace) static const char * clutter_eotf_to_string (ClutterEOTF eotf) { - switch (eotf.tf_name) + switch (eotf.type) { - case CLUTTER_TRANSFER_FUNCTION_SRGB: - return "sRGB"; - case CLUTTER_TRANSFER_FUNCTION_PQ: - return "PQ"; - case CLUTTER_TRANSFER_FUNCTION_LINEAR: - return "linear"; + case CLUTTER_EOTF_TYPE_GAMMA: + return "gamma"; + case CLUTTER_EOTF_TYPE_NAMED: + switch (eotf.tf_name) + { + case CLUTTER_TRANSFER_FUNCTION_SRGB: + return "sRGB"; + case CLUTTER_TRANSFER_FUNCTION_PQ: + return "PQ"; + case CLUTTER_TRANSFER_FUNCTION_LINEAR: + return "linear"; + } } g_assert_not_reached (); @@ -194,13 +214,19 @@ static const ClutterLuminance pq_default_luminance = { const ClutterLuminance * clutter_eotf_get_default_luminance (ClutterEOTF eotf) { - switch (eotf.tf_name) + switch (eotf.type) { - case CLUTTER_TRANSFER_FUNCTION_SRGB: - case CLUTTER_TRANSFER_FUNCTION_LINEAR: + case CLUTTER_EOTF_TYPE_GAMMA: return &sdr_default_luminance; - case CLUTTER_TRANSFER_FUNCTION_PQ: - return &pq_default_luminance; + case CLUTTER_EOTF_TYPE_NAMED: + switch (eotf.tf_name) + { + case CLUTTER_TRANSFER_FUNCTION_SRGB: + case CLUTTER_TRANSFER_FUNCTION_LINEAR: + return &sdr_default_luminance; + case CLUTTER_TRANSFER_FUNCTION_PQ: + return &pq_default_luminance; + } } g_assert_not_reached (); @@ -280,7 +306,7 @@ clutter_color_state_new (ClutterContext *context, { return clutter_color_state_new_full (context, colorspace, transfer_function, NULL, - -1.0f, -1.0f, -1.0f); + -1.0f, -1.0f, -1.0f, -1.0f); } /** @@ -296,6 +322,7 @@ clutter_color_state_new_full (ClutterContext *context, ClutterColorspace colorspace, ClutterTransferFunction transfer_function, ClutterPrimaries *primaries, + float gamma_exp, float min_lum, float max_lum, float ref_lum) @@ -322,8 +349,16 @@ clutter_color_state_new_full (ClutterContext *context, priv->colorimetry.colorspace = colorspace; } - priv->eotf.type = CLUTTER_EOTF_TYPE_NAMED; - priv->eotf.tf_name = transfer_function; + if (gamma_exp >= 1.0f) + { + priv->eotf.type = CLUTTER_EOTF_TYPE_GAMMA; + priv->eotf.gamma_exp = gamma_exp; + } + else + { + 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) { @@ -386,6 +421,36 @@ static const char pq_inv_eotf_source[] = " return vec4 (pq_inv_eotf (color.rgb), color.a);\n" "}\n"; +static const char gamma_eotf_source[] = + "uniform float " UNIFORM_NAME_GAMMA_EXP ";\n" + "// gamma_eotf:\n" + "// @color: Normalized ([0,1]) electrical signal value\n" + "// Returns: tristimulus values ([0,1])\n" + "vec3 gamma_eotf (vec3 color)\n" + "{\n" + " return pow (color, vec3 (" UNIFORM_NAME_GAMMA_EXP "));\n" + "}\n" + "\n" + "vec4 gamma_eotf (vec4 color)\n" + "{\n" + " return vec4 (gamma_eotf (color.rgb), color.a);\n" + "}\n"; + +static const char gamma_inv_eotf_source[] = + "uniform float " UNIFORM_NAME_INV_GAMMA_EXP ";\n" + "// gamma_inv_eotf:\n" + "// @color: Normalized tristimulus values ([0,1])" + "// Returns: Normalized ([0,1]) electrical signal value\n" + "vec3 gamma_inv_eotf (vec3 color)\n" + "{\n" + " return pow (color, vec3 (" UNIFORM_NAME_INV_GAMMA_EXP "));\n" + "}\n" + "\n" + "vec4 gamma_inv_eotf (vec4 color)\n" + "{\n" + " return vec4 (gamma_inv_eotf (color.rgb), color.a);\n" + "}\n"; + static const char srgb_eotf_source[] = "// srgb_eotf:\n" "// @color: Normalized ([0,1]) electrical signal value.\n" @@ -437,6 +502,16 @@ static const TransferFunction pq_inv_eotf = { .name = "pq_inv_eotf", }; +static const TransferFunction gamma_eotf = { + .source = gamma_eotf_source, + .name = "gamma_eotf", +}; + +static const TransferFunction gamma_inv_eotf = { + .source = gamma_inv_eotf_source, + .name = "gamma_inv_eotf", +}; + static const TransferFunction srgb_eotf = { .source = srgb_eotf_source, .name = "srgb_eotf", @@ -469,14 +544,20 @@ get_eotf (ClutterColorState *color_state) ClutterColorStatePrivate *priv = clutter_color_state_get_instance_private (color_state); - switch (priv->eotf.tf_name) + switch (priv->eotf.type) { - case CLUTTER_TRANSFER_FUNCTION_PQ: - return &pq_eotf; - case CLUTTER_TRANSFER_FUNCTION_SRGB: - return &srgb_eotf; - case CLUTTER_TRANSFER_FUNCTION_LINEAR: - return NULL; + case CLUTTER_EOTF_TYPE_GAMMA: + return &gamma_eotf; + case CLUTTER_EOTF_TYPE_NAMED: + switch (priv->eotf.tf_name) + { + case CLUTTER_TRANSFER_FUNCTION_PQ: + return &pq_eotf; + case CLUTTER_TRANSFER_FUNCTION_SRGB: + return &srgb_eotf; + case CLUTTER_TRANSFER_FUNCTION_LINEAR: + return NULL; + } } g_warning ("Unhandled tranfer function %s", @@ -490,14 +571,20 @@ get_inv_eotf (ClutterColorState *color_state) ClutterColorStatePrivate *priv = clutter_color_state_get_instance_private (color_state); - switch (priv->eotf.tf_name) + switch (priv->eotf.type) { - case CLUTTER_TRANSFER_FUNCTION_PQ: - return &pq_inv_eotf; - case CLUTTER_TRANSFER_FUNCTION_SRGB: - return &srgb_inv_eotf; - case CLUTTER_TRANSFER_FUNCTION_LINEAR: - return NULL; + case CLUTTER_EOTF_TYPE_GAMMA: + return &gamma_inv_eotf; + case CLUTTER_EOTF_TYPE_NAMED: + switch (priv->eotf.tf_name) + { + case CLUTTER_TRANSFER_FUNCTION_PQ: + return &pq_inv_eotf; + case CLUTTER_TRANSFER_FUNCTION_SRGB: + return &srgb_inv_eotf; + case CLUTTER_TRANSFER_FUNCTION_LINEAR: + return NULL; + } } g_warning ("Unhandled tranfer function %s", @@ -942,11 +1029,39 @@ clutter_color_state_update_uniforms (ClutterColorState *color_state, ClutterColorState *target_color_state, CoglPipeline *pipeline) { + const ClutterEOTF *eotf; + const ClutterEOTF *target_eotf; float luminance_mapping; float color_space_mapping[9] = { 0 }; + int uniform_location_gamma_exp; + int uniform_location_inv_gamma_exp; int uniform_location_luminance_mapping; int uniform_location_color_space_mapping; + eotf = clutter_color_state_get_eotf (color_state); + if (eotf->type == CLUTTER_EOTF_TYPE_GAMMA) + { + uniform_location_gamma_exp = + cogl_pipeline_get_uniform_location (pipeline, + UNIFORM_NAME_GAMMA_EXP); + + cogl_pipeline_set_uniform_1f (pipeline, + uniform_location_gamma_exp, + eotf->gamma_exp); + } + + target_eotf = clutter_color_state_get_eotf (target_color_state); + if (target_eotf->type == CLUTTER_EOTF_TYPE_GAMMA) + { + uniform_location_inv_gamma_exp = + cogl_pipeline_get_uniform_location (pipeline, + UNIFORM_NAME_INV_GAMMA_EXP); + + cogl_pipeline_set_uniform_1f (pipeline, + uniform_location_inv_gamma_exp, + 1.0f / target_eotf->gamma_exp); + } + luminance_mapping = get_luminance_mapping (color_state, target_color_state); uniform_location_luminance_mapping = @@ -1060,6 +1175,14 @@ eotf_equal (ClutterColorState *color_state, other_priv->eotf.type == CLUTTER_EOTF_TYPE_NAMED) return priv->eotf.tf_name == other_priv->eotf.tf_name; + if (priv->eotf.type == CLUTTER_EOTF_TYPE_GAMMA && + other_priv->eotf.type == CLUTTER_EOTF_TYPE_GAMMA) + { + return G_APPROX_VALUE (priv->eotf.gamma_exp, + other_priv->eotf.gamma_exp, + 0.0001f); + } + return FALSE; } @@ -1138,14 +1261,20 @@ clutter_color_state_required_format (ClutterColorState *color_state) priv = clutter_color_state_get_instance_private (color_state); - switch (priv->eotf.tf_name) + switch (priv->eotf.type) { - case CLUTTER_TRANSFER_FUNCTION_LINEAR: - return CLUTTER_ENCODING_REQUIRED_FORMAT_FP16; - case CLUTTER_TRANSFER_FUNCTION_PQ: - return CLUTTER_ENCODING_REQUIRED_FORMAT_UINT10; - case CLUTTER_TRANSFER_FUNCTION_SRGB: + case CLUTTER_EOTF_TYPE_GAMMA: return CLUTTER_ENCODING_REQUIRED_FORMAT_UINT8; + case CLUTTER_EOTF_TYPE_NAMED: + switch (priv->eotf.tf_name) + { + case CLUTTER_TRANSFER_FUNCTION_LINEAR: + return CLUTTER_ENCODING_REQUIRED_FORMAT_FP16; + case CLUTTER_TRANSFER_FUNCTION_PQ: + return CLUTTER_ENCODING_REQUIRED_FORMAT_UINT10; + case CLUTTER_TRANSFER_FUNCTION_SRGB: + return CLUTTER_ENCODING_REQUIRED_FORMAT_UINT8; + } } g_assert_not_reached (); @@ -1181,24 +1310,33 @@ clutter_color_state_get_blending (ClutterColorState *color_state, priv = clutter_color_state_get_instance_private (color_state); - switch (priv->eotf.tf_name) + switch (priv->eotf.type) { - case CLUTTER_TRANSFER_FUNCTION_PQ: - case CLUTTER_TRANSFER_FUNCTION_LINEAR: + case CLUTTER_EOTF_TYPE_NAMED: + switch (priv->eotf.tf_name) + { + case CLUTTER_TRANSFER_FUNCTION_PQ: + case CLUTTER_TRANSFER_FUNCTION_LINEAR: + blending_tf = CLUTTER_TRANSFER_FUNCTION_LINEAR; + break; + /* effectively this means we will blend sRGB content in sRGB, not linear */ + case CLUTTER_TRANSFER_FUNCTION_SRGB: + blending_tf = priv->eotf.tf_name; + break; + default: + g_assert_not_reached (); + } + break; + case CLUTTER_EOTF_TYPE_GAMMA: blending_tf = CLUTTER_TRANSFER_FUNCTION_LINEAR; break; - /* effectively this means we will blend sRGB content in sRGB, not linear */ - case CLUTTER_TRANSFER_FUNCTION_SRGB: - blending_tf = priv->eotf.tf_name; - break; - default: - g_assert_not_reached (); } if (force) blending_tf = CLUTTER_TRANSFER_FUNCTION_LINEAR; - if (priv->eotf.tf_name == blending_tf) + if (priv->eotf.type == CLUTTER_EOTF_TYPE_NAMED && + priv->eotf.tf_name == blending_tf) return g_object_ref (color_state); switch (priv->colorimetry.type) @@ -1217,6 +1355,7 @@ clutter_color_state_get_blending (ClutterColorState *color_state, colorspace, blending_tf, primaries, + -1.0f, priv->luminance.min, priv->luminance.max, priv->luminance.ref); diff --git a/clutter/clutter/clutter-color-state.h b/clutter/clutter/clutter-color-state.h index b5ced5eea..7affbec1f 100644 --- a/clutter/clutter/clutter-color-state.h +++ b/clutter/clutter/clutter-color-state.h @@ -54,6 +54,7 @@ typedef enum typedef enum { CLUTTER_EOTF_TYPE_NAMED, + CLUTTER_EOTF_TYPE_GAMMA, } ClutterEOTFType; typedef enum @@ -83,7 +84,11 @@ typedef struct _ClutterColorimetry typedef struct _ClutterEOTF { ClutterEOTFType type : 1; - ClutterTransferFunction tf_name; + union + { + ClutterTransferFunction tf_name; + float gamma_exp; + }; } ClutterEOTF; typedef struct _ClutterLuminance @@ -110,6 +115,7 @@ ClutterColorState * clutter_color_state_new_full (ClutterContext *conte ClutterColorspace colorspace, ClutterTransferFunction transfer_function, ClutterPrimaries *primaries, + float gamma_exp, float min_lum, float max_lum, float ref_lum); diff --git a/src/backends/meta-color-device.c b/src/backends/meta-color-device.c index 5918fbe71..42eec217c 100644 --- a/src/backends/meta-color-device.c +++ b/src/backends/meta-color-device.c @@ -687,7 +687,9 @@ update_color_state (MetaColorDevice *color_device) g_autoptr (ClutterColorState) color_state = NULL; ClutterColorspace colorspace; ClutterEOTF eotf; + ClutterTransferFunction transfer_function; const ClutterLuminance *luminance; + float gamma_exp; float reference_luminance_factor; float new_ref_luminance; UpdateResult result = 0; @@ -701,10 +703,23 @@ update_color_state (MetaColorDevice *color_device) meta_debug_control_get_luminance_percentage (debug_control) / 100.0f; new_ref_luminance = luminance->ref * reference_luminance_factor; + switch (eotf.type) + { + case CLUTTER_EOTF_TYPE_NAMED: + transfer_function = eotf.tf_name; + gamma_exp = -1.0f; + break; + case CLUTTER_EOTF_TYPE_GAMMA: + transfer_function = CLUTTER_TRANSFER_FUNCTION_SRGB; + gamma_exp = eotf.gamma_exp; + break; + } + color_state = clutter_color_state_new_full (clutter_context, colorspace, - eotf.tf_name, + transfer_function, NULL, + gamma_exp, luminance->min, luminance->max, new_ref_luminance); diff --git a/src/wayland/meta-wayland-color-management.c b/src/wayland/meta-wayland-color-management.c index c7d231436..7ac7c3394 100644 --- a/src/wayland/meta-wayland-color-management.c +++ b/src/wayland/meta-wayland-color-management.c @@ -184,6 +184,14 @@ wayland_tf_to_clutter (enum xx_color_manager_v4_transfer_function tf, { switch (tf) { + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22: + eotf->type = CLUTTER_EOTF_TYPE_GAMMA; + eotf->gamma_exp = 2.2f; + return TRUE; + case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28: + eotf->type = CLUTTER_EOTF_TYPE_GAMMA; + eotf->gamma_exp = 2.8f; + return TRUE; case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: eotf->type = CLUTTER_EOTF_TYPE_NAMED; eotf->tf_name = CLUTTER_TRANSFER_FUNCTION_SRGB; @@ -374,8 +382,24 @@ send_information (struct wl_resource *info_resource, } 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); + switch (eotf->type) + { + case CLUTTER_EOTF_TYPE_NAMED: + tf = clutter_tf_to_wayland (eotf->tf_name); + xx_image_description_info_v4_send_tf_named (info_resource, tf); + break; + case CLUTTER_EOTF_TYPE_GAMMA: + if (G_APPROX_VALUE (eotf->gamma_exp, 2.2f, 0.0001f)) + xx_image_description_info_v4_send_tf_named (info_resource, + XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22); + else if (G_APPROX_VALUE (eotf->gamma_exp, 2.8f, 0.0001f)) + xx_image_description_info_v4_send_tf_named (info_resource, + XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28); + else + xx_image_description_info_v4_send_tf_power (info_resource, + float_to_scaled_uint32 (eotf->gamma_exp)); + break; + } lum = clutter_color_state_get_luminance (color_state); xx_image_description_info_v4_send_luminances (info_resource, @@ -861,6 +885,8 @@ creator_params_create (struct wl_client *client, MetaWaylandImageDescription *image_desc; ClutterColorspace colorspace; ClutterPrimaries *primaries; + ClutterTransferFunction tf_name; + float gamma_exp; if (!creator_params->is_colorimetry_set || !creator_params->is_eotf_set) { @@ -888,10 +914,23 @@ creator_params_create (struct wl_client *client, break; } + switch (creator_params->eotf.type) + { + case CLUTTER_EOTF_TYPE_NAMED: + tf_name = creator_params->eotf.tf_name; + gamma_exp = -1.0f; + break; + case CLUTTER_EOTF_TYPE_GAMMA: + tf_name = CLUTTER_TRANSFER_FUNCTION_SRGB; + gamma_exp = creator_params->eotf.gamma_exp; + break; + } + color_state = clutter_color_state_new_full (clutter_context, colorspace, - creator_params->eotf.tf_name, + tf_name, primaries, + gamma_exp, creator_params->lum.min, creator_params->lum.max, creator_params->lum.ref); @@ -944,9 +983,28 @@ creator_params_set_tf_power (struct wl_client *client, struct wl_resource *resource, uint32_t eexp) { - wl_resource_post_error (resource, - XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, - "Setting power based transfer characteristics is not supported"); + MetaWaylandCreatorParams *creator_params = + wl_resource_get_user_data (resource); + + if (creator_params->is_eotf_set) + { + wl_resource_post_error (resource, + XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, + "The transfer characteristics were already set"); + return; + } + + if (eexp < 10000 || eexp > 100000) + { + wl_resource_post_error (resource, + XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, + "The exponent must be between 1.0 and 10.0"); + return; + } + + creator_params->eotf.type = CLUTTER_EOTF_TYPE_GAMMA; + creator_params->eotf.gamma_exp = scaled_uint32_to_float (eexp); + creator_params->is_eotf_set = TRUE; } static void @@ -1354,8 +1412,14 @@ color_manager_send_supported_events (struct wl_resource *resource) XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC); xx_color_manager_v4_send_supported_feature (resource, XX_COLOR_MANAGER_V4_FEATURE_SET_PRIMARIES); + xx_color_manager_v4_send_supported_feature (resource, + XX_COLOR_MANAGER_V4_FEATURE_SET_TF_POWER); xx_color_manager_v4_send_supported_feature (resource, XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES); + xx_color_manager_v4_send_supported_tf_named (resource, + XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22); + xx_color_manager_v4_send_supported_tf_named (resource, + XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA28); xx_color_manager_v4_send_supported_tf_named (resource, XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB); xx_color_manager_v4_send_supported_tf_named (resource,