From cf520b7a1f932100a0b87e85a4501d56e30762cc Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 15 Dec 2016 04:24:37 +0100 Subject: [PATCH] gsk: Add blend nodes Implement blend mode support in GTK background compositing with it. --- docs/reference/gsk/gsk4-sections.txt | 4 +- gsk/gskenums.h | 4 +- gsk/gskglrenderer.c | 2 +- gsk/gskrendernode.c | 41 ------ gsk/gskrendernode.h | 5 +- gsk/gskrendernodeimpl.c | 178 +++++++++++++++++++++++++ gsk/gskrendernodeprivate.h | 10 +- gtk/gtkrenderbackground.c | 46 ++++++- gtk/inspector/gtktreemodelrendernode.c | 11 ++ gtk/inspector/recorder.c | 19 --- 10 files changed, 240 insertions(+), 80 deletions(-) diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt index a12c28d938..c0448644b4 100644 --- a/docs/reference/gsk/gsk4-sections.txt +++ b/docs/reference/gsk/gsk4-sections.txt @@ -29,8 +29,6 @@ gsk_render_node_unref GskRenderNodeType gsk_render_node_get_node_type gsk_render_node_draw -GskBlendMode -gsk_render_node_set_blend_mode GskScalingFilter gsk_render_node_set_scaling_filters gsk_render_node_set_name @@ -49,6 +47,8 @@ gsk_clip_node_new gsk_clip_node_get_child gsk_rounded_clip_node_new gsk_rounded_clip_node_get_child +GskBlendMode +gsk_blend_node_new GSK_IS_RENDER_NODE GSK_RENDER_NODE diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 145d03546c..d647495d6d 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -34,6 +34,7 @@ * @GSK_OPACITY_NODE: A node that changes the opacity of its child * @GSK_CLIP_NODE: A node that clips its child to a rectangular area * @GSK_ROUNDED_CLIP_NODE: A node that clips its child to a rounded rectangle + * @GSK_BLEND_NODE: A node the blends two children together * * The type of a node determines what the node is rendering. * @@ -48,7 +49,8 @@ typedef enum { GSK_TRANSFORM_NODE, GSK_OPACITY_NODE, GSK_CLIP_NODE, - GSK_ROUNDED_CLIP_NODE + GSK_ROUNDED_CLIP_NODE, + GSK_BLEND_NODE } GskRenderNodeType; /** diff --git a/gsk/gskglrenderer.c b/gsk/gskglrenderer.c index 7812121131..1c09244b5e 100644 --- a/gsk/gskglrenderer.c +++ b/gsk/gskglrenderer.c @@ -644,7 +644,7 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, item.opacity = 1.0; - item.blend_mode = gsk_render_node_get_blend_mode (node); + item.blend_mode = GSK_BLEND_MODE_DEFAULT; /* Back-pointer to the parent node */ if (parent != NULL) diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c index ab41235c4d..20d9bef092 100644 --- a/gsk/gskrendernode.c +++ b/gsk/gskrendernode.c @@ -237,47 +237,6 @@ gsk_render_node_get_name (GskRenderNode *node) return node->name; } -/** - * gsk_render_node_set_blend_mode: - * @node: a #GskRenderNode - * @blend_mode: the blend mode to be applied to the node's children - * - * Sets the blend mode to be used when rendering the children - * of the @node. - * - * The default value is %GSK_BLEND_MODE_DEFAULT. - * - * Since: 3.90 - */ -void -gsk_render_node_set_blend_mode (GskRenderNode *node, - GskBlendMode blend_mode) -{ - g_return_if_fail (GSK_IS_RENDER_NODE (node)); - g_return_if_fail (node->is_mutable); - - if (node->blend_mode == blend_mode) - return; - - node->blend_mode = blend_mode; -} - -/* - * gsk_render_node_get_blend_mode: - * @node: a #GskRenderNode - * - * Retrieves the blend mode set by gsk_render_node_set_blend_mode(). - * - * Returns: the blend mode - */ -GskBlendMode -gsk_render_node_get_blend_mode (GskRenderNode *node) -{ - g_return_val_if_fail (GSK_IS_RENDER_NODE (node), GSK_BLEND_MODE_DEFAULT); - - return node->blend_mode; -} - /*< private > * gsk_render_node_make_immutable: * @node: a #GskRenderNode diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index 708a77dbbe..5d92c73729 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -93,8 +93,9 @@ GDK_AVAILABLE_IN_3_90 GskRenderNode * gsk_rounded_clip_node_get_child (GskRenderNode *node); GDK_AVAILABLE_IN_3_90 -void gsk_render_node_set_blend_mode (GskRenderNode *node, - GskBlendMode blend_mode); +GskRenderNode * gsk_blend_node_new (GskRenderNode *bottom, + GskRenderNode *top, + GskBlendMode blend_mode); GDK_AVAILABLE_IN_3_90 void gsk_render_node_set_scaling_filter (GskRenderNode *node, diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 9dbfd3f23d..90f90f184a 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -1068,3 +1068,181 @@ gsk_rounded_clip_node_peek_clip (GskRenderNode *node) return &self->clip; } +/*** GSK_BLEND_NODE ***/ + +typedef struct _GskBlendNode GskBlendNode; + +struct _GskBlendNode +{ + GskRenderNode render_node; + + GskRenderNode *bottom; + GskRenderNode *top; + GskBlendMode blend_mode; +}; + +static cairo_operator_t +gsk_blend_mode_to_cairo_operator (GskBlendMode blend_mode) +{ + switch (blend_mode) + { + default: + g_assert_not_reached (); + case GSK_BLEND_MODE_DEFAULT: + return CAIRO_OPERATOR_OVER; + case GSK_BLEND_MODE_MULTIPLY: + return CAIRO_OPERATOR_MULTIPLY; + case GSK_BLEND_MODE_SCREEN: + return CAIRO_OPERATOR_SCREEN; + case GSK_BLEND_MODE_OVERLAY: + return CAIRO_OPERATOR_OVERLAY; + case GSK_BLEND_MODE_DARKEN: + return CAIRO_OPERATOR_DARKEN; + case GSK_BLEND_MODE_LIGHTEN: + return CAIRO_OPERATOR_LIGHTEN; + case GSK_BLEND_MODE_COLOR_DODGE: + return CAIRO_OPERATOR_COLOR_DODGE; + case GSK_BLEND_MODE_COLOR_BURN: + return CAIRO_OPERATOR_COLOR_BURN; + case GSK_BLEND_MODE_HARD_LIGHT: + return CAIRO_OPERATOR_HARD_LIGHT; + case GSK_BLEND_MODE_SOFT_LIGHT: + return CAIRO_OPERATOR_SOFT_LIGHT; + case GSK_BLEND_MODE_DIFFERENCE: + return CAIRO_OPERATOR_DIFFERENCE; + case GSK_BLEND_MODE_EXCLUSION: + return CAIRO_OPERATOR_EXCLUSION; + case GSK_BLEND_MODE_COLOR: + return CAIRO_OPERATOR_HSL_COLOR; + case GSK_BLEND_MODE_HUE: + return CAIRO_OPERATOR_HSL_HUE; + case GSK_BLEND_MODE_SATURATION: + return CAIRO_OPERATOR_HSL_SATURATION; + case GSK_BLEND_MODE_LUMINOSITY: + return CAIRO_OPERATOR_HSL_LUMINOSITY; + } +} + +static void +gsk_blend_node_finalize (GskRenderNode *node) +{ + GskBlendNode *self = (GskBlendNode *) node; + + gsk_render_node_unref (self->bottom); + gsk_render_node_unref (self->top); +} + +static void +gsk_blend_node_make_immutable (GskRenderNode *node) +{ + GskBlendNode *self = (GskBlendNode *) node; + + gsk_render_node_make_immutable (self->bottom); + gsk_render_node_make_immutable (self->top); +} + +static void +gsk_blend_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskBlendNode *self = (GskBlendNode *) node; + + cairo_push_group (cr); + gsk_render_node_draw (self->bottom, cr); + + cairo_push_group (cr); + gsk_render_node_draw (self->top, cr); + + cairo_pop_group_to_source (cr); + cairo_set_operator (cr, gsk_blend_mode_to_cairo_operator (self->blend_mode)); + cairo_paint (cr); + + cairo_pop_group_to_source (cr); /* resets operator */ + cairo_paint (cr); +} + +static void +gsk_blend_node_get_bounds (GskRenderNode *node, + graphene_rect_t *bounds) +{ + GskBlendNode *self = (GskBlendNode *) node; + graphene_rect_t bottom_bounds, top_bounds; + + gsk_render_node_get_bounds (self->bottom, &bottom_bounds); + gsk_render_node_get_bounds (self->top, &top_bounds); + + graphene_rect_union (&bottom_bounds, &top_bounds, bounds); +} + +static const GskRenderNodeClass GSK_BLEND_NODE_CLASS = { + GSK_BLEND_NODE, + sizeof (GskBlendNode), + "GskBlendNode", + gsk_blend_node_finalize, + gsk_blend_node_make_immutable, + gsk_blend_node_draw, + gsk_blend_node_get_bounds +}; + +/** + * gsk_blend_node_new: + * @bottom: The bottom node to be drawn + * @top: The node to be blended onto the @bottom node + * @blend_mode: The blend mode to use + * + * Creates a #GskRenderNode that will use @blend_mode to blend the @top + * node onto the @bottom node. + * + * Returns: A new #GskRenderNode + * + * Since: 3.90 + */ +GskRenderNode * +gsk_blend_node_new (GskRenderNode *bottom, + GskRenderNode *top, + GskBlendMode blend_mode) +{ + GskBlendNode *self; + + g_return_val_if_fail (GSK_IS_RENDER_NODE (bottom), NULL); + g_return_val_if_fail (GSK_IS_RENDER_NODE (top), NULL); + + self = (GskBlendNode *) gsk_render_node_new (&GSK_BLEND_NODE_CLASS); + + self->bottom = gsk_render_node_ref (bottom); + self->top = gsk_render_node_ref (top); + self->blend_mode = blend_mode; + + return &self->render_node; +} + +GskRenderNode * +gsk_blend_node_get_bottom_child (GskRenderNode *node) +{ + GskBlendNode *self = (GskBlendNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_BLEND_NODE), NULL); + + return self->bottom; +} + +GskRenderNode * +gsk_blend_node_get_top_child (GskRenderNode *node) +{ + GskBlendNode *self = (GskBlendNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_BLEND_NODE), NULL); + + return self->top; +} + +GskBlendMode +gsk_blend_node_get_blend_mode (GskRenderNode *node) +{ + GskBlendNode *self = (GskBlendNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_BLEND_NODE), GSK_BLEND_MODE_DEFAULT); + + return self->blend_mode; +} + diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h index 98f8fc35a6..e4b9ee4209 100644 --- a/gsk/gskrendernodeprivate.h +++ b/gsk/gskrendernodeprivate.h @@ -19,12 +19,6 @@ struct _GskRenderNode /* Use for debugging */ char *name; - /* Paint opacity */ - double opacity; - - /* Blend mode */ - GskBlendMode blend_mode; - /* Scaling filters */ GskScalingFilter min_filter; GskScalingFilter mag_filter; @@ -66,7 +60,9 @@ const GskRoundedRect * gsk_rounded_clip_node_peek_clip (GskRenderNode *node); void gsk_transform_node_get_transform (GskRenderNode *node, graphene_matrix_t *transform); -GskBlendMode gsk_render_node_get_blend_mode (GskRenderNode *node); +GskRenderNode * gsk_blend_node_get_bottom_child (GskRenderNode *node); +GskRenderNode * gsk_blend_node_get_top_child (GskRenderNode *node); +GskBlendMode gsk_blend_node_get_blend_node (GskRenderNode *node); G_END_DECLS diff --git a/gtk/gtkrenderbackground.c b/gtk/gtkrenderbackground.c index 0bedcf1f61..10f54c6878 100644 --- a/gtk/gtkrenderbackground.c +++ b/gtk/gtkrenderbackground.c @@ -683,22 +683,54 @@ gtk_css_style_snapshot_background (GtkCssStyle *style, */ if (_gtk_theming_background_needs_push_group (style)) { - cairo_t *cr; + GtkCssValue *blend_modes; + GskBlendMode blend_mode; - cr = gtk_snapshot_append_cairo_node (snapshot, - &GRAPHENE_RECT_INIT (0, 0, width, height), - "Background"); + blend_modes = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE); - _gtk_theming_background_paint_color (&bg, cr, bg_color, background_image); + gtk_snapshot_push (snapshot, TRUE, "BackgroundBlendGroup"); + + gtk_theming_background_snapshot_color (&bg, snapshot, bg_color, background_image); number_of_layers = _gtk_css_array_value_get_n_values (background_image); for (idx = number_of_layers - 1; idx >= 0; idx--) { - gtk_theming_background_paint_layer (&bg, idx, cr); + blend_mode = _gtk_css_blend_mode_value_get (_gtk_css_array_value_get_nth (blend_modes, idx)); + + if (blend_mode == GSK_BLEND_MODE_DEFAULT) + { + gtk_theming_background_snapshot_layer (&bg, idx, snapshot); + } + else + { + GskRenderNode *bottom, *top, *blend; + + bottom = gtk_snapshot_pop (snapshot); + + gtk_snapshot_push (snapshot, TRUE, "BackgroundBlendGroup", blend_mode); + gtk_theming_background_snapshot_layer (&bg, idx, snapshot); + top = gtk_snapshot_pop (snapshot); + + /* XXX: Is this necessary? Do we need a NULL node? */ + if (top == NULL) + top = gsk_container_node_new (NULL, 0); + if (bottom == NULL) + bottom = gsk_container_node_new (NULL, 0); + + blend = gsk_blend_node_new (bottom, top, blend_mode); + gsk_render_node_set_name (blend, "BackgroundBlend"); + + gtk_snapshot_push (snapshot, TRUE, "BackgroundBlendGroup"); + gtk_snapshot_append_node (snapshot, blend); + + gsk_render_node_unref (blend); + gsk_render_node_unref (top); + gsk_render_node_unref (bottom); + } } - cairo_destroy (cr); + gtk_snapshot_pop_and_append (snapshot); } else { diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c index ce470ee9df..5426250e0e 100644 --- a/gtk/inspector/gtktreemodelrendernode.c +++ b/gtk/inspector/gtktreemodelrendernode.c @@ -19,6 +19,8 @@ #include "gtktreemodelrendernode.h" +#include "gsk/gskrendernodeprivate.h" + typedef struct _TreeElement TreeElement; /* This is an array of all nodes and the index of their parent. When adding a node, @@ -542,6 +544,15 @@ append_node (GtkTreeModelRenderNode *nodemodel, append_node (nodemodel, gsk_rounded_clip_node_get_child (node), priv->nodes->len - 1); break; + case GSK_BLEND_NODE: + { + int elt_index = priv->nodes->len - 1; + + append_node (nodemodel, gsk_blend_node_get_bottom_child (node), elt_index); + append_node (nodemodel, gsk_blend_node_get_top_child (node), elt_index); + } + break; + case GSK_CONTAINER_NODE: { gint elt_index; diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 1a0f5d1fce..839e828991 100644 --- a/gtk/inspector/recorder.c +++ b/gtk/inspector/recorder.c @@ -133,9 +133,7 @@ populate_render_node_properties (GtkListStore *store, GskRenderNode *node) { graphene_rect_t bounds; - int i; char *tmp; - GEnumClass *class; gtk_list_store_clear (store); @@ -161,23 +159,6 @@ populate_render_node_properties (GtkListStore *store, 0, "Has Texture", 1, gsk_render_node_get_node_type (node) == GSK_TEXTURE_NODE ? "TRUE" : "FALSE", -1); - - class = g_type_class_ref (gsk_blend_mode_get_type ()); - for (i = 0; i < class->n_values; i++) - { - if (class->values[i].value == gsk_render_node_get_blend_mode (node)) - { - tmp = g_strdup (class->values[i].value_nick); - break; - } - } - g_type_class_unref (class); - - gtk_list_store_insert_with_values (store, NULL, -1, - 0, "Blendmode", - 1, tmp, - -1); - g_free (tmp); } static void