diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c index 3b7c5173a8..c765cf94a8 100644 --- a/gtk/gtksnapshot.c +++ b/gtk/gtksnapshot.c @@ -91,6 +91,14 @@ struct _GtkSnapshotState { struct { graphene_rect_t bounds; } clip; + struct { + GskGLShader *shader; + guchar *args; + graphene_rect_t bounds; + int n_children; + int node_idx; + GskRenderNode **nodes; + } glshader; struct { GskRoundedRect bounds; } rounded_clip; @@ -812,6 +820,154 @@ gtk_snapshot_push_clip (GtkSnapshot *snapshot, gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &state->data.clip.bounds); } +static GskRenderNode * +maybe_clip (GskRenderNode *node, + const graphene_rect_t *bounds) +{ + if (node && + !graphene_rect_contains_rect (bounds, &node->bounds)) + { + return gsk_clip_node_new (node, bounds); + } + + return gsk_render_node_ref (node); +} + +static GskRenderNode * +gtk_snapshot_collect_glshader (GtkSnapshot *snapshot, + GtkSnapshotState *state, + GskRenderNode **nodes, + guint n_nodes) +{ + GskRenderNode *shader_node = NULL, *child_node; + GdkRGBA transparent = { 0, 0, 0, 0 }; + + child_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes); + + if (child_node == NULL) + child_node = gsk_color_node_new (&transparent, &state->data.glshader.bounds); + + state->data.glshader.nodes[state->data.glshader.node_idx] = child_node; + + if (state->data.glshader.node_idx != state->data.glshader.n_children) + return NULL; /* Not last */ + + /* This is the last pop */ + + shader_node = NULL; + + if (state->data.glshader.bounds.size.width != 0 && + state->data.glshader.bounds.size.height != 0) + { + GskRenderNode *fallback_node = maybe_clip (state->data.glshader.nodes[0], + &state->data.glshader.bounds); + shader_node = gsk_glshader_node_new (state->data.glshader.shader, + &state->data.glshader.bounds, + state->data.glshader.args, + fallback_node, + &state->data.glshader.nodes[1], + state->data.glshader.n_children); + gsk_render_node_unref (fallback_node); + } + + g_object_unref (state->data.glshader.shader); + for (guint i = 0; i < state->data.glshader.n_children + 1; i++) + gsk_render_node_unref (state->data.glshader.nodes[i]); + g_free (state->data.glshader.nodes); + + return shader_node; +} + +/** + * gtk_snapshot_push_glshader_va: + * @snapshot: a #GtkSnapshot + * @shader: The code to run + * @bounds: the rectangle to render into + * @n_children: The number of extra nodes given as argument to the shader as textures. + * @uniforms: Values for the uniforms of the shader in this instance, ending with NULL + * + * Renders a rectangle with output from a GLSL shader. This is the same as + * gtk_snapshot_push_glshader() but with a va_list instead of a varargs argument, + * so see that for details. + */ +void +gtk_snapshot_push_glshader_va (GtkSnapshot *snapshot, + GskGLShader *shader, + const graphene_rect_t *bounds, + int n_children, + va_list uniforms) +{ + GtkSnapshotState *state; + float scale_x, scale_y, dx, dy; + GskRenderNode **nodes; + int node_idx; + graphene_rect_t transformed_bounds; + + gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy); + + state = gtk_snapshot_push_state (snapshot, + gtk_snapshot_get_current_state (snapshot)->transform, + gtk_snapshot_collect_glshader); + gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &transformed_bounds); + state->data.glshader.bounds = transformed_bounds; + state->data.glshader.shader = g_object_ref (shader); + state->data.glshader.args = gsk_glshader_format_uniform_data_va (shader, uniforms); + state->data.glshader.n_children = n_children; + nodes = g_new0 (GskRenderNode *, n_children + 1); + node_idx = n_children; /* We pop in reverse order */ + + state->data.glshader.node_idx = node_idx--; + state->data.glshader.nodes = nodes; + + for (int i = 0; i < n_children; i++) + { + state = gtk_snapshot_push_state (snapshot, + gtk_snapshot_get_current_state (snapshot)->transform, + gtk_snapshot_collect_glshader); + state->data.glshader.node_idx = node_idx--; + state->data.glshader.n_children = n_children; + state->data.glshader.nodes = nodes; + state->data.glshader.bounds = transformed_bounds; + } +} + +/** + * gtk_snapshot_push_glshader: + * @snapshot: a #GtkSnapshot + * @shader: The code to run + * @bounds: the rectangle to render into + * @n_children: The number of extra nodes given as argument to the shader as textures. + * @uniforms: Values for the uniforms of the shader in this instance, ending with NULL + * + * Push a #GskGLShaderNode with a specific #GskGLShader and a set of uniform values + * to use while rendering. Additionally this takes a fallback node and a list of + * @n_children other nodes which will be passed to the #GskGLShaderNode. + * + * The fallback node is used if GLSL shaders are not supported by the backend, or if + * there is any problem compiling the shader. The fallback node needs to be pushed + * directly after the gtk_snapshot_push_glshader() call up until the first call to gtk_snapshot_pop(). + * + * If @n_children > 0, then it is expected that you (after the fallback call gtk_snapshot_pop() + * @n_children times. Each of these will generate a node that is passed as a child to the + * glshader node, which in turn will render these to textures and pass as arguments to the + * shader. + * + * For details on how to write shaders, see #GskGLShader. + */ +void +gtk_snapshot_push_glshader (GtkSnapshot *snapshot, + GskGLShader *shader, + const graphene_rect_t *bounds, + int n_children, + ...) +{ + va_list args; + + va_start (args, n_children); + gtk_snapshot_push_glshader_va (snapshot, shader, bounds, n_children, args); + va_end (args); +} + static GskRenderNode * gtk_snapshot_collect_rounded_clip (GtkSnapshot *snapshot, GtkSnapshotState *state, diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h index e3c0e17e65..0786d5e2ae 100644 --- a/gtk/gtksnapshot.h +++ b/gtk/gtksnapshot.h @@ -99,6 +99,18 @@ GDK_AVAILABLE_IN_ALL void gtk_snapshot_push_cross_fade (GtkSnapshot *snapshot, double progress); GDK_AVAILABLE_IN_ALL +void gtk_snapshot_push_glshader (GtkSnapshot *snapshot, + GskGLShader *shader, + const graphene_rect_t *bounds, + int n_children, + ...); +GDK_AVAILABLE_IN_ALL +void gtk_snapshot_push_glshader_va (GtkSnapshot *snapshot, + GskGLShader *shader, + const graphene_rect_t *bounds, + int n_children, + va_list uniforms); +GDK_AVAILABLE_IN_ALL void gtk_snapshot_pop (GtkSnapshot *snapshot); GDK_AVAILABLE_IN_ALL