clutter/color-state: Add do_transform method

This is a CPU-based transformation method that performs the
same transformation implemented on GPU shaders.

The transformation gets an array of pixels in RGB float format and
returns an array of pixels transformed.

This will be used in the next commit to compare the results
between CPU and GPU, validating the shader implementations.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4230>
This commit is contained in:
Joan Torres 2025-01-22 19:58:53 +01:00 committed by Sebastian Wick
parent ca977ce2bb
commit 9022b39a50
3 changed files with 233 additions and 0 deletions

View File

@ -187,6 +187,139 @@ clutter_eotf_get_default_luminance (ClutterEOTF eotf)
g_assert_not_reached (); g_assert_not_reached ();
} }
static float
clutter_eotf_apply_srgb (float input)
{
if (input <= 0.04045f)
return input / 12.92f;
else
return powf ((input + 0.055f) / 1.055f, 12.0f / 5.0f);
}
static float
clutter_eotf_apply_srgb_inv (float input)
{
if (input <= 0.0031308f)
return input * 12.92f;
else
return powf (input, (5.0f / 12.0f)) * 1.055f - 0.055f;
}
static float
clutter_eotf_apply_pq (float input)
{
float c1, c2, c3, oo_m1, oo_m2, num, den;
c1 = 0.8359375f;
c2 = 18.8515625f;
c3 = 18.6875f;
oo_m1 = 1.0f / 0.1593017f;
oo_m2 = 1.0f / 78.84375f;
num = MAX (powf (input, oo_m2) - c1, 0.0f);
den = c2 - c3 * powf (input, oo_m2);
return powf (num / den, oo_m1);
}
static float
clutter_eotf_apply_pq_inv (float input)
{
float c1, c2, c3, m1, m2, in_pow_m1, num, den;
c1 = 0.8359375f;
c2 = 18.8515625f;
c3 = 18.6875f;
m1 = 0.1593017f;
m2 = 78.84375f;
in_pow_m1 = powf (input, m1);
num = c1 + c2 * in_pow_m1;
den = 1.0f + c3 * in_pow_m1;
return powf (num / den, m2);
}
static float
clutter_eotf_apply_bt709 (float input)
{
if (input < 0.08124f)
return input / 4.5f;
else
return powf ((input + 0.099f) / 1.099f, 1.0f / 0.45f);
}
static float
clutter_eotf_apply_bt709_inv (float input)
{
if (input < 0.018f)
return input * 4.5f;
else
return 1.099f * powf (input, 0.45f) - 0.099f;
}
static float
clutter_eotf_apply_gamma (float input,
float gamma_exp)
{
/* This avoids returning nan */
return G_APPROX_VALUE (input, 0.0f, FLT_EPSILON) ?
0.0f :
powf (input, gamma_exp);
}
static float
clutter_eotf_apply (ClutterEOTF eotf,
float input)
{
switch (eotf.type)
{
case CLUTTER_EOTF_TYPE_NAMED:
switch (eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_SRGB:
return clutter_eotf_apply_srgb (input);
case CLUTTER_TRANSFER_FUNCTION_PQ:
return clutter_eotf_apply_pq (input);
case CLUTTER_TRANSFER_FUNCTION_BT709:
return clutter_eotf_apply_bt709 (input);
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
return input;
}
break;
case CLUTTER_EOTF_TYPE_GAMMA:
return clutter_eotf_apply_gamma (input, eotf.gamma_exp);
}
g_warning ("Didn't apply tranfer function %s",
clutter_eotf_to_string (eotf));
return input;
}
static float
clutter_eotf_apply_inv (ClutterEOTF eotf,
float input)
{
switch (eotf.type)
{
case CLUTTER_EOTF_TYPE_NAMED:
switch (eotf.tf_name)
{
case CLUTTER_TRANSFER_FUNCTION_SRGB:
return clutter_eotf_apply_srgb_inv (input);
case CLUTTER_TRANSFER_FUNCTION_PQ:
return clutter_eotf_apply_pq_inv (input);
case CLUTTER_TRANSFER_FUNCTION_BT709:
return clutter_eotf_apply_bt709_inv (input);
case CLUTTER_TRANSFER_FUNCTION_LINEAR:
return input;
}
break;
case CLUTTER_EOTF_TYPE_GAMMA:
return clutter_eotf_apply_gamma (input, 1.0f / eotf.gamma_exp);
}
g_warning ("Didn't apply inv tranfer function %s",
clutter_eotf_to_string (eotf));
return input;
}
/* Primaries and white point retrieved from: /* Primaries and white point retrieved from:
* https://www.color.org */ * https://www.color.org */
static const ClutterPrimaries srgb_primaries = { static const ClutterPrimaries srgb_primaries = {
@ -1203,6 +1336,72 @@ clutter_color_state_params_update_uniforms (ClutterColorState *color_state,
pipeline); pipeline);
} }
static void
clutter_color_state_params_do_transform (ClutterColorState *color_state,
ClutterColorState *target_color_state,
const float *input,
float *output,
int n_samples)
{
ClutterColorStateParams *color_state_params =
CLUTTER_COLOR_STATE_PARAMS (color_state);
ClutterColorStateParams *target_color_state_params =
CLUTTER_COLOR_STATE_PARAMS (target_color_state);
ClutterEOTF eotf = color_state_params->eotf;
ClutterEOTF target_eotf = target_color_state_params->eotf;
int i;
float result[3];
float color_trans_mat[9];
float lum_mapping;
graphene_matrix_t g_color_trans_mat;
graphene_vec3_t g_result;
get_color_space_mapping_matrix (color_state_params,
target_color_state_params,
color_trans_mat);
graphene_matrix_init_from_float (
&g_color_trans_mat,
(float [16]) {
color_trans_mat[0], color_trans_mat[1], color_trans_mat[2], 0.0f,
color_trans_mat[3], color_trans_mat[4], color_trans_mat[5], 0.0f,
color_trans_mat[6], color_trans_mat[7], color_trans_mat[8], 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
});
lum_mapping = get_luminance_mapping (color_state_params,
target_color_state_params);
for (i = 0; i < n_samples; i++)
{
/* EOTF */
result[0] = clutter_eotf_apply (eotf, input[0]);
result[1] = clutter_eotf_apply (eotf, input[1]);
result[2] = clutter_eotf_apply (eotf, input[2]);
/* Luminance mapping */
result[0] = result[0] * lum_mapping;
result[1] = result[1] * lum_mapping;
result[2] = result[2] * lum_mapping;
/* Color space mapping */
graphene_vec3_init_from_float (&g_result, result);
graphene_matrix_transform_vec3 (&g_color_trans_mat, &g_result, &g_result);
graphene_vec3_to_float (&g_result, result);
/* Inverse EOTF */
result[0] = clutter_eotf_apply_inv (target_eotf, result[0]);
result[1] = clutter_eotf_apply_inv (target_eotf, result[1]);
result[2] = clutter_eotf_apply_inv (target_eotf, result[2]);
output[0] = CLAMP (result[0], 0.0f, 1.0f);
output[1] = CLAMP (result[1], 0.0f, 1.0f);
output[2] = CLAMP (result[2], 0.0f, 1.0f);
input += 3;
output += 3;
}
}
static gboolean static gboolean
clutter_color_state_params_equals (ClutterColorState *color_state, clutter_color_state_params_equals (ClutterColorState *color_state,
ClutterColorState *other_color_state) ClutterColorState *other_color_state)
@ -1336,6 +1535,7 @@ clutter_color_state_params_class_init (ClutterColorStateParamsClass *klass)
color_state_class->init_color_transform_key = clutter_color_state_params_init_color_transform_key; color_state_class->init_color_transform_key = clutter_color_state_params_init_color_transform_key;
color_state_class->create_transform_snippet = clutter_color_state_params_create_transform_snippet; color_state_class->create_transform_snippet = clutter_color_state_params_create_transform_snippet;
color_state_class->update_uniforms = clutter_color_state_params_update_uniforms; color_state_class->update_uniforms = clutter_color_state_params_update_uniforms;
color_state_class->do_transform = clutter_color_state_params_do_transform;
color_state_class->equals = clutter_color_state_params_equals; color_state_class->equals = clutter_color_state_params_equals;
color_state_class->to_string = clutter_color_state_params_to_string; color_state_class->to_string = clutter_color_state_params_to_string;
color_state_class->required_format = clutter_color_state_params_required_format; color_state_class->required_format = clutter_color_state_params_required_format;

View File

@ -269,6 +269,26 @@ clutter_color_state_update_uniforms (ClutterColorState *color_state,
pipeline); pipeline);
} }
void
clutter_color_state_do_transform (ClutterColorState *color_state,
ClutterColorState *target_color_state,
const float *input,
float *output,
int n_samples)
{
ClutterColorStateClass *color_state_class =
CLUTTER_COLOR_STATE_GET_CLASS (color_state);
g_return_if_fail (CLUTTER_IS_COLOR_STATE (color_state));
g_return_if_fail (CLUTTER_IS_COLOR_STATE (target_color_state));
color_state_class->do_transform (color_state,
target_color_state,
input,
output,
n_samples);
}
void void
clutter_color_state_add_pipeline_transform (ClutterColorState *color_state, clutter_color_state_add_pipeline_transform (ClutterColorState *color_state,
ClutterColorState *target_color_state, ClutterColorState *target_color_state,

View File

@ -54,6 +54,12 @@ struct _ClutterColorStateClass
ClutterColorState *target_color_state, ClutterColorState *target_color_state,
CoglPipeline *pipeline); CoglPipeline *pipeline);
void (* do_transform) (ClutterColorState *color_state,
ClutterColorState *target_color_state,
const float *input,
float *output,
int n_samples);
gboolean (* equals) (ClutterColorState *color_state, gboolean (* equals) (ClutterColorState *color_state,
ClutterColorState *other_color_state); ClutterColorState *other_color_state);
@ -81,6 +87,13 @@ void clutter_color_state_update_uniforms (ClutterColorState *color_state,
ClutterColorState *target_color_state, ClutterColorState *target_color_state,
CoglPipeline *pipeline); CoglPipeline *pipeline);
CLUTTER_EXPORT
void clutter_color_state_do_transform (ClutterColorState *color_state,
ClutterColorState *target_color_state,
const float *input,
float *output,
int n_samples);
CLUTTER_EXPORT CLUTTER_EXPORT
gboolean clutter_color_state_equals (ClutterColorState *color_state, gboolean clutter_color_state_equals (ClutterColorState *color_state,
ClutterColorState *other_color_state); ClutterColorState *other_color_state);