gsk: Add blend nodes

Implement blend mode support in GTK background compositing with it.
This commit is contained in:
Benjamin Otte 2016-12-15 04:24:37 +01:00
parent 3e26fadb24
commit cf520b7a1f
10 changed files with 240 additions and 80 deletions

View File

@ -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
<SUBSECTION Standard>
GSK_IS_RENDER_NODE
GSK_RENDER_NODE

View File

@ -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;
/**

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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<Mode%u>", 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
{

View File

@ -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;

View File

@ -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