gsk: Add cross-fade node

And implement stack crossfades with it.
This commit is contained in:
Benjamin Otte 2016-12-17 07:44:10 +01:00
parent 3e4fd32b54
commit 30438c6e8b
7 changed files with 186 additions and 25 deletions

View File

@ -51,6 +51,7 @@ gsk_rounded_clip_node_new
gsk_rounded_clip_node_get_child
GskBlendMode
gsk_blend_node_new
gsk_cross_fade_node_new
<SUBSECTION Standard>
GSK_IS_RENDER_NODE
GSK_RENDER_NODE

View File

@ -38,6 +38,7 @@
* @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
* @GSK_CROSS_FADE_NODE: A node the cross-fades between two children
*
* The type of a node determines what the node is rendering.
*
@ -55,7 +56,8 @@ typedef enum {
GSK_OPACITY_NODE,
GSK_CLIP_NODE,
GSK_ROUNDED_CLIP_NODE,
GSK_BLEND_NODE
GSK_BLEND_NODE,
GSK_CROSS_FADE_NODE
} GskRenderNodeType;
/**

View File

@ -117,6 +117,11 @@ GskRenderNode * gsk_blend_node_new (GskRenderNode
GskRenderNode *top,
GskBlendMode blend_mode);
GDK_AVAILABLE_IN_3_90
GskRenderNode * gsk_cross_fade_node_new (GskRenderNode *start,
GskRenderNode *end,
double progress);
GDK_AVAILABLE_IN_3_90
void gsk_render_node_set_scaling_filter (GskRenderNode *node,
GskScalingFilter min_filter,

View File

@ -1402,3 +1402,139 @@ gsk_blend_node_get_blend_mode (GskRenderNode *node)
return self->blend_mode;
}
/*** GSK_CROSS_FADE_NODE ***/
typedef struct _GskCrossFadeNode GskCrossFadeNode;
struct _GskCrossFadeNode
{
GskRenderNode render_node;
GskRenderNode *start;
GskRenderNode *end;
double progress;
};
static void
gsk_cross_fade_node_finalize (GskRenderNode *node)
{
GskCrossFadeNode *self = (GskCrossFadeNode *) node;
gsk_render_node_unref (self->start);
gsk_render_node_unref (self->end);
}
static void
gsk_cross_fade_node_make_immutable (GskRenderNode *node)
{
GskCrossFadeNode *self = (GskCrossFadeNode *) node;
gsk_render_node_make_immutable (self->start);
gsk_render_node_make_immutable (self->end);
}
static void
gsk_cross_fade_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskCrossFadeNode *self = (GskCrossFadeNode *) node;
cairo_push_group (cr);
gsk_render_node_draw (self->start, cr);
cairo_push_group (cr);
gsk_render_node_draw (self->end, cr);
cairo_pop_group_to_source (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint_with_alpha (cr, self->progress);
cairo_pop_group_to_source (cr); /* resets operator */
cairo_paint (cr);
}
static void
gsk_cross_fade_node_get_bounds (GskRenderNode *node,
graphene_rect_t *bounds)
{
GskCrossFadeNode *self = (GskCrossFadeNode *) node;
graphene_rect_t start_bounds, end_bounds;
gsk_render_node_get_bounds (self->start, &start_bounds);
gsk_render_node_get_bounds (self->end, &end_bounds);
graphene_rect_union (&start_bounds, &end_bounds, bounds);
}
static const GskRenderNodeClass GSK_CROSS_FADE_NODE_CLASS = {
GSK_CROSS_FADE_NODE,
sizeof (GskCrossFadeNode),
"GskCrossFadeNode",
gsk_cross_fade_node_finalize,
gsk_cross_fade_node_make_immutable,
gsk_cross_fade_node_draw,
gsk_cross_fade_node_get_bounds
};
/**
* gsk_cross_fade_node_new:
* @start: The start node to be drawn
* @end: The node to be cross_fadeed onto the @start node
* @progress: How far the fade has progressed from start to end. The value will
* be clamped to the range [0 ... 1]
*
* Creates a #GskRenderNode that will do a cross-fade between @start and @end.
*
* Returns: A new #GskRenderNode
*
* Since: 3.90
*/
GskRenderNode *
gsk_cross_fade_node_new (GskRenderNode *start,
GskRenderNode *end,
double progress)
{
GskCrossFadeNode *self;
g_return_val_if_fail (GSK_IS_RENDER_NODE (start), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (end), NULL);
self = (GskCrossFadeNode *) gsk_render_node_new (&GSK_CROSS_FADE_NODE_CLASS);
self->start = gsk_render_node_ref (start);
self->end = gsk_render_node_ref (end);
self->progress = CLAMP (progress, 0.0, 1.0);
return &self->render_node;
}
GskRenderNode *
gsk_cross_fade_node_get_start_child (GskRenderNode *node)
{
GskCrossFadeNode *self = (GskCrossFadeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_CROSS_FADE_NODE), NULL);
return self->start;
}
GskRenderNode *
gsk_cross_fade_node_get_end_child (GskRenderNode *node)
{
GskCrossFadeNode *self = (GskCrossFadeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_CROSS_FADE_NODE), NULL);
return self->end;
}
double
gsk_cross_fade_node_get_progress (GskRenderNode *node)
{
GskCrossFadeNode *self = (GskCrossFadeNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_CROSS_FADE_NODE), 0.0);
return self->progress;
}

View File

@ -64,6 +64,10 @@ 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);
GskRenderNode * gsk_cross_fade_node_get_start_child (GskRenderNode *node);
GskRenderNode * gsk_cross_fade_node_get_end_child (GskRenderNode *node);
double gsk_cross_fade_node_get_progress (GskRenderNode *node);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_PRIVATE_H__ */

View File

@ -1913,37 +1913,41 @@ gtk_stack_snapshot_crossfade (GtkWidget *widget,
GtkStack *stack = GTK_STACK (widget);
GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
gdouble progress = gtk_progress_tracker_get_progress (&priv->tracker, FALSE);
cairo_t *cr;
GskRenderNode *end_node, *node;
char *name;
cr = gtk_snapshot_append_cairo_node (snapshot,
&GRAPHENE_RECT_INIT(
0, 0,
gtk_widget_get_allocated_width (widget),
gtk_widget_get_allocated_height (widget)
),
"GtkStackCrossfade");
gtk_container_propagate_draw (GTK_CONTAINER (stack),
gtk_snapshot_push (snapshot, TRUE, "GtkStackCrossFadeEnd");
gtk_container_snapshot_child (GTK_CONTAINER (stack),
priv->visible_child->widget,
cr);
/* Multiply alpha by progress */
cairo_set_source_rgba (cr, 1, 1, 1, progress);
cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
snapshot);
end_node = gtk_snapshot_pop (snapshot);
if (priv->last_visible_node)
{
cairo_push_group (cr);
cairo_translate (cr, priv->last_visible_surface_allocation.x, priv->last_visible_surface_allocation.y);
gsk_render_node_draw (priv->last_visible_node, cr);
cairo_pop_group_to_source (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
cairo_paint_with_alpha (cr, MAX (1.0 - progress, 0));
graphene_matrix_t identity;
GskRenderNode *start_node;
graphene_matrix_init_identity (&identity);
gtk_snapshot_push_transform (snapshot, &identity, "CrossFadeStart");
gtk_snapshot_append_node (snapshot, priv->last_visible_node);
start_node = gtk_snapshot_pop (snapshot);
node = gsk_cross_fade_node_new (start_node, end_node, progress);
gsk_render_node_unref (start_node);
}
else
{
node = gsk_opacity_node_new (end_node, 1.0 - progress);
}
cairo_destroy (cr);
name = g_strdup_printf ("CrossFade<%g>", progress);
gsk_render_node_set_name (node, name);
g_free (name);
gtk_snapshot_append_node (snapshot, node);
gsk_render_node_unref (node);
gsk_render_node_unref (end_node);
}
static void

View File

@ -555,6 +555,15 @@ append_node (GtkTreeModelRenderNode *nodemodel,
}
break;
case GSK_CROSS_FADE_NODE:
{
int elt_index = priv->nodes->len - 1;
append_node (nodemodel, gsk_cross_fade_node_get_start_child (node), elt_index);
append_node (nodemodel, gsk_cross_fade_node_get_end_child (node), elt_index);
}
break;
case GSK_CONTAINER_NODE:
{
gint elt_index;