cursor-renderer/native: Unpremultiply for color state transformation

Without doing this, a non-linear color state transformation could result
in premultiplied colour values larger than the alpha value, which
manifested with artifacts such as parts of the cursor shining brighter
than SDR white (with HDR enabled).

This was reported in the GNOME Shell Matrix room on August 8th 2024, and
I later hit it myself.

v2:
* Fix add_pipeline_snippet function formatting. (Sebastian Wick)

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4224>
This commit is contained in:
Michel Dänzer 2025-01-20 12:24:35 +01:00 committed by Marge Bot
parent c945826a69
commit ad754af151

View File

@ -89,6 +89,9 @@ struct _MetaCursorRendererNativePrivate
gboolean input_disconnected;
GMutex input_mutex;
GCond input_cond;
CoglSnippet *premult_snippet;
CoglSnippet *unpremult_snippet;
};
typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate;
@ -183,6 +186,8 @@ meta_cursor_renderer_native_finalize (GObject *object)
g_clear_signal_handler (&priv->texture_changed_handler_id,
priv->current_cursor);
g_clear_object (&priv->current_cursor);
g_clear_object (&priv->premult_snippet);
g_clear_object (&priv->unpremult_snippet);
g_clear_handle_id (&priv->animation_timeout_id, g_source_remove);
G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object);
@ -725,6 +730,40 @@ load_cursor_sprite_gbm_buffer_for_crtc (MetaCursorRendererNative *native,
return TRUE;
}
static void
add_pipeline_snippet (CoglPipeline *pipeline,
CoglSnippet **snippet,
const char *snippet_source)
{
if (!*snippet)
*snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, "",
snippet_source);
cogl_pipeline_add_snippet (pipeline, *snippet);
}
static void
add_pipeline_premultiply (MetaCursorRendererNative *cursor_renderer_native,
CoglPipeline *pipeline)
{
MetaCursorRendererNativePrivate *priv =
meta_cursor_renderer_native_get_instance_private (cursor_renderer_native);
add_pipeline_snippet (pipeline, &priv->premult_snippet,
" cogl_color_out.rgb *= cogl_color_out.a;\n");
}
static void
add_pipeline_unpremultiply (MetaCursorRendererNative *cursor_renderer_native,
CoglPipeline *pipeline)
{
MetaCursorRendererNativePrivate *priv =
meta_cursor_renderer_native_get_instance_private (cursor_renderer_native);
add_pipeline_snippet (pipeline, &priv->unpremult_snippet,
" cogl_color_out.rgb /= cogl_color_out.a;\n");
}
static CoglTexture *
scale_and_transform_cursor_sprite_cpu (MetaCursorRendererNative *cursor_renderer_native,
ClutterColorState *target_color_state,
@ -772,11 +811,17 @@ scale_and_transform_cursor_sprite_cpu (MetaCursorRendererNative *cursor_renderer
cogl_pipeline_set_layer_texture (pipeline, 0, src_texture);
cogl_pipeline_set_layer_matrix (pipeline, 0, matrix);
if (cogl_texture_get_premultiplied (src_texture))
add_pipeline_unpremultiply (cursor_renderer_native, pipeline);
color_state = meta_cursor_sprite_get_color_state (cursor_sprite);
clutter_color_state_add_pipeline_transform (color_state,
target_color_state,
pipeline);
if (cogl_texture_get_premultiplied (dst_texture))
add_pipeline_premultiply (cursor_renderer_native, pipeline);
cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
COGL_BUFFER_BIT_COLOR,
0.0f, 0.0f, 0.0f, 0.0f);