1
0
Fork 0

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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4020>
This commit is contained in:
Joan Torres 2024-09-12 16:18:54 +02:00 committed by Marge Bot
parent 3b53f7cb6c
commit fcbd2e3840
5 changed files with 285 additions and 61 deletions

View file

@ -27,10 +27,10 @@
typedef struct _ClutterColorTransformKey
{
struct {
ClutterTransferFunction transfer_function;
guint eotf_key;
} source;
struct {
ClutterTransferFunction transfer_function;
guint eotf_key;
} target;
} ClutterColorTransformKey;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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,