1
0
Fork 0

clutter/color-state: Match reference luminance

This uses the luminance levels of the color states to anchor the white
of content instead of hard-coding the levels.

This also starts using uniforms for parts of the mapping which means we
don't have to generate and compile a shader when the luminance levels
change.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3953>
This commit is contained in:
Sebastian Wick 2024-07-19 00:38:27 +02:00 committed by Marge Bot
parent 03aad0d99e
commit 86a0797819
3 changed files with 91 additions and 125 deletions

View file

@ -54,6 +54,8 @@
#include "clutter/clutter-enum-types.h"
#include "clutter/clutter-private.h"
#define UNIFORM_NAME_LUMINANCE_MAPPING "luminance_mapping"
enum
{
PROP_0,
@ -505,9 +507,9 @@ clutter_color_state_new_full (ClutterContext *context,
static const char pq_eotf_source[] =
"// pq_eotf:\n"
"// @pq: Normalized ([0,1]) electrical signal value\n"
"// Returns: Luminance in cd/m²\n"
"vec3 pq_eotf (vec3 pq)\n"
"// @color: Normalized ([0,1]) electrical signal value\n"
"// Returns: tristimulus values ([0,1])\n"
"vec3 pq_eotf (vec3 color)\n"
"{\n"
" const float c1 = 0.8359375;\n"
" const float c2 = 18.8515625;\n"
@ -516,44 +518,43 @@ static const char pq_eotf_source[] =
" const float oo_m1 = 1.0 / 0.1593017578125;\n"
" const float oo_m2 = 1.0 / 78.84375;\n"
"\n"
" vec3 num = max (pow (pq, vec3 (oo_m2)) - c1, vec3 (0.0));\n"
" vec3 den = c2 - c3 * pow (pq, vec3 (oo_m2));\n"
" vec3 num = max (pow (color, vec3 (oo_m2)) - c1, vec3 (0.0));\n"
" vec3 den = c2 - c3 * pow (color, vec3 (oo_m2));\n"
"\n"
" return 10000.0 * pow (num / den, vec3 (oo_m1));\n"
" return pow (num / den, vec3 (oo_m1));\n"
"}\n"
"\n"
"vec4 pq_eotf (vec4 pq)\n"
"vec4 pq_eotf (vec4 color)\n"
"{\n"
" return vec4 (pq_eotf (pq.rgb), pq.a);\n"
" return vec4 (pq_eotf (color.rgb), color.a);\n"
"}\n";
static const char pq_inv_eotf_source[] =
"// pq_inv_eotf:\n"
"// @nits: Optical signal value in cd/m²\n"
"// @color: Normalized tristimulus values ([0,1])"
"// Returns: Normalized ([0,1]) electrical signal value\n"
"vec3 pq_inv_eotf (vec3 nits)\n"
"vec3 pq_inv_eotf (vec3 color)\n"
"{\n"
" vec3 normalized = clamp (nits / 10000.0, vec3 (0), vec3 (1));\n"
" float m1 = 0.1593017578125;\n"
" float m2 = 78.84375;\n"
" float c1 = 0.8359375;\n"
" float c2 = 18.8515625;\n"
" float c3 = 18.6875;\n"
" vec3 normalized_pow_m1 = pow (normalized, vec3 (m1));\n"
" vec3 num = vec3 (c1) + c2 * normalized_pow_m1;\n"
" vec3 denum = vec3 (1.0) + c3 * normalized_pow_m1;\n"
" vec3 color_pow_m1 = pow (color, vec3 (m1));\n"
" vec3 num = vec3 (c1) + c2 * color_pow_m1;\n"
" vec3 denum = vec3 (1.0) + c3 * color_pow_m1;\n"
" return pow (num / denum, vec3 (m2));\n"
"}\n"
"\n"
"vec4 pq_inv_eotf (vec4 nits)\n"
"vec4 pq_inv_eotf (vec4 color)\n"
"{\n"
" return vec4 (pq_inv_eotf (nits.rgb), nits.a);\n"
" return vec4 (pq_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"
"// Returns: Normalized luminance ([0,1])\n"
"// Returns: Normalized tristimulus values ([0,1])\n"
"vec3 srgb_eotf (vec3 color)\n"
"{\n"
" bvec3 is_low = lessThanEqual (color, vec3 (0.04045));\n"
@ -569,7 +570,7 @@ static const char srgb_eotf_source[] =
static const char srgb_inv_eotf_source[] =
"// srgb_inv_eotf:\n"
"// @color: Normalized ([0,1]) optical signal value\n"
"// @color: Normalized ([0,1]) tristimulus values\n"
"// Returns: Normalized ([0,1]) electrical signal value\n"
"vec3 srgb_inv_eotf (vec3 color)\n"
"{\n"
@ -585,20 +586,6 @@ static const char srgb_inv_eotf_source[] =
" return vec4 (srgb_inv_eotf (color.rgb), color.a);\n"
"}\n";
/* Luminance gain default value (203) retrieved from
* https://github.com/w3c/ColorWeb-CG/blob/feature/add-mastering-display-info/hdr_html_canvas_element.md#srgb-to-rec2100-pq */
static const char srgb_luminance_gain_source[] =
"vec3 srgb_luminance_gain (vec3 value)\n"
"{\n"
" return 203.0 * value;\n"
"}\n";
static const char pq_luminance_clamp_source[] =
"vec3 pq_luminance_clamp (vec3 value)\n"
"{\n"
" return clamp (value, 0.0, 203.0) / 203.0;\n"
"}\n";
/* Calculated using:
* numpy.dot(colour.models.RGB_COLOURSPACE_BT2020.matrix_XYZ_to_RGB,
* colour.models.RGB_COLOURSPACE_BT709.matrix_RGB_to_XYZ)
@ -652,16 +639,6 @@ static const TransferFunction srgb_inv_eotf = {
.name = "srgb_inv_eotf",
};
static const TransferFunction srgb_luminance_gain = {
.source = srgb_luminance_gain_source,
.name = "srgb_luminance_gain",
};
static const TransferFunction pq_luminance_clamp = {
.source = pq_luminance_clamp_source,
.name = "pq_luminance_clamp",
};
static const MatrixMultiplication bt709_to_bt2020 = {
.source = bt709_to_bt2020_matrix_source,
.name = "bt709_to_bt2020",
@ -741,85 +718,17 @@ get_inv_eotf (ClutterColorState *color_state)
return NULL;
}
static const TransferFunction *
get_denormalize_function (ClutterColorState *color_state,
ClutterColorState *target_color_state)
{
ClutterColorStatePrivate *priv =
clutter_color_state_get_instance_private (color_state);
ClutterColorStatePrivate *target_priv =
clutter_color_state_get_instance_private (target_color_state);
switch (priv->transfer_function)
{
case CLUTTER_TRANSFER_FUNCTION_SRGB:
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
switch (target_priv->transfer_function)
{
case CLUTTER_TRANSFER_FUNCTION_PQ:
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
return &srgb_luminance_gain;
case CLUTTER_TRANSFER_FUNCTION_SRGB:
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
return NULL;
}
break;
case CLUTTER_TRANSFER_FUNCTION_PQ:
switch (target_priv->transfer_function)
{
case CLUTTER_TRANSFER_FUNCTION_PQ:
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
return NULL;
case CLUTTER_TRANSFER_FUNCTION_SRGB:
case CLUTTER_TRANSFER_FUNCTION_DEFAULT:
return &pq_luminance_clamp;
}
break;
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
return NULL;
}
g_return_val_if_reached (NULL);
}
static void
get_transfer_functions (ClutterColorState *color_state,
ClutterColorState *target_color_state,
const TransferFunction **pre_transfer_function,
const TransferFunction **denormalize_function,
const TransferFunction **post_transfer_function)
{
ClutterColorStatePrivate *priv =
clutter_color_state_get_instance_private (color_state);
ClutterColorStatePrivate *target_priv =
clutter_color_state_get_instance_private (target_color_state);
if (priv->colorspace == target_priv->colorspace &&
priv->transfer_function == target_priv->transfer_function)
if (clutter_color_state_equals (color_state, target_color_state))
return;
if (priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR &&
target_priv->transfer_function == CLUTTER_TRANSFER_FUNCTION_LINEAR)
{
*pre_transfer_function = get_eotf (color_state);
*denormalize_function = get_denormalize_function (color_state,
target_color_state);
}
else if (priv->transfer_function == CLUTTER_TRANSFER_FUNCTION_LINEAR &&
target_priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR)
{
*denormalize_function = get_denormalize_function (color_state,
target_color_state);
*post_transfer_function = get_inv_eotf (target_color_state);
}
else if (priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR &&
target_priv->transfer_function != CLUTTER_TRANSFER_FUNCTION_LINEAR)
{
*pre_transfer_function = get_eotf (color_state);
*denormalize_function = get_denormalize_function (color_state,
target_color_state);
*post_transfer_function = get_inv_eotf (target_color_state);
}
*pre_transfer_function = get_eotf (color_state);
*post_transfer_function = get_inv_eotf (target_color_state);
}
static const MatrixMultiplication *
@ -869,7 +778,6 @@ clutter_color_state_get_transform_snippet (ClutterColorState *color_state,
CoglSnippet *snippet;
const MatrixMultiplication *color_space_mapping = NULL;
const TransferFunction *pre_transfer_function = NULL;
const TransferFunction *denormalize_function = NULL;
const TransferFunction *post_transfer_function = NULL;
g_autoptr (GString) globals_source = NULL;
g_autoptr (GString) snippet_source = NULL;
@ -890,19 +798,19 @@ clutter_color_state_get_transform_snippet (ClutterColorState *color_state,
get_transfer_functions (color_state, target_color_state,
&pre_transfer_function,
&denormalize_function,
&post_transfer_function);
globals_source = g_string_new (NULL);
if (pre_transfer_function)
g_string_append_printf (globals_source, "%s\n", pre_transfer_function->source);
if (denormalize_function)
g_string_append_printf (globals_source, "%s\n", denormalize_function->source);
if (post_transfer_function)
g_string_append_printf (globals_source, "%s\n", post_transfer_function->source);
if (color_space_mapping)
g_string_append_printf (globals_source, "%s\n", color_space_mapping->source);
g_string_append (globals_source,
"uniform float " UNIFORM_NAME_LUMINANCE_MAPPING ";\n");
/*
* The following statements generate a shader snippet that transforms colors
* from one color state (transfer function, color space, color encoding) into
@ -938,12 +846,10 @@ clutter_color_state_get_transform_snippet (ClutterColorState *color_state,
pre_transfer_function->name);
}
if (denormalize_function)
{
g_string_append_printf (snippet_source,
" color_state_color = %s (color_state_color);\n",
denormalize_function->name);
}
g_string_append (snippet_source,
" color_state_color = "
UNIFORM_NAME_LUMINANCE_MAPPING " * color_state_color;\n");
if (color_space_mapping)
{
g_string_append_printf (snippet_source,
@ -974,6 +880,45 @@ clutter_color_state_get_transform_snippet (ClutterColorState *color_state,
return snippet;
}
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;
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);
/* 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);
}
void
clutter_color_state_update_uniforms (ClutterColorState *color_state,
ClutterColorState *target_color_state,
CoglPipeline *pipeline)
{
float luminance_mapping;
int uniform_location_luminance_mapping;
luminance_mapping = get_luminance_mapping (color_state, target_color_state);
uniform_location_luminance_mapping =
cogl_pipeline_get_uniform_location (pipeline,
UNIFORM_NAME_LUMINANCE_MAPPING);
cogl_pipeline_set_uniform_1f (pipeline,
uniform_location_luminance_mapping,
luminance_mapping);
}
void
clutter_color_state_add_pipeline_transform (ClutterColorState *color_state,
ClutterColorState *target_color_state,
@ -987,6 +932,10 @@ clutter_color_state_add_pipeline_transform (ClutterColorState *color_state,
snippet = clutter_color_state_get_transform_snippet (color_state,
target_color_state);
cogl_pipeline_add_snippet (pipeline, snippet);
clutter_color_state_update_uniforms (color_state,
target_color_state,
pipeline);
}
static gboolean

View file

@ -74,6 +74,12 @@ void clutter_color_state_add_pipeline_transform (ClutterColorState *color_state,
ClutterColorState *target_color_state,
CoglPipeline *pipeline);
CLUTTER_EXPORT
void clutter_color_state_update_uniforms (ClutterColorState *color_state,
ClutterColorState *target_color_state,
CoglPipeline *pipeline);
CLUTTER_EXPORT
gboolean clutter_color_state_equals (ClutterColorState *color_state,
ClutterColorState *other_color_state);

View file

@ -116,9 +116,20 @@ clutter_pipeline_cache_get_pipeline (ClutterPipelineCache *pipeline_cache,
pipeline = g_hash_table_lookup (group_entry->slots[slot], &key);
if (pipeline)
return cogl_pipeline_copy (pipeline);
{
CoglPipeline *new_pipeline;
new_pipeline = cogl_pipeline_copy (pipeline);
clutter_color_state_update_uniforms (source_color_state,
target_color_state,
new_pipeline);
return new_pipeline;
}
else
return NULL;
{
return NULL;
}
}
/**