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:
parent
03aad0d99e
commit
86a0797819
3 changed files with 91 additions and 125 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue