From 5d7edde37ddcd5aa517a241a9499cfd0445f1076 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Mon, 29 Jun 2020 15:26:58 -0300 Subject: [PATCH] Introduce ClutterBlitNode It is not possible to express a blit operation using paint nodes as of now. This is a requirement for GNOME Shell, e.g., to implement its blur effect. Add a new ClutterBlitNode node that takes two framebuffers as input, and blits source into dest according to added rectangles. Because this paint node uses the rectangles in a different way compared to all the other nodes, add an auxiliary method to ensure all blit operations are valid. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1340 --- clutter/clutter/clutter-paint-nodes.c | 203 ++++++++++++++++++++++++++ clutter/clutter/clutter-paint-nodes.h | 32 ++++ 2 files changed, 235 insertions(+) diff --git a/clutter/clutter/clutter-paint-nodes.c b/clutter/clutter/clutter-paint-nodes.c index 3b20792e4..382a8b832 100644 --- a/clutter/clutter/clutter-paint-nodes.c +++ b/clutter/clutter/clutter-paint-nodes.c @@ -1625,3 +1625,206 @@ clutter_layer_node_new_with_framebuffer (CoglFramebuffer *framebuffer, return (ClutterPaintNode *) res; } + +/* + * ClutterBlitNode + */ + +struct _ClutterBlitNode +{ + ClutterPaintNode parent_instance; + + CoglFramebuffer *source; + CoglFramebuffer *dest; +}; + +struct _ClutterBlitNodeClass +{ + ClutterPaintNodeClass parent_class; +}; + +G_DEFINE_TYPE (ClutterBlitNode, clutter_blit_node, CLUTTER_TYPE_PAINT_NODE) + +static gboolean +clutter_blit_node_pre_draw (ClutterPaintNode *node, + ClutterPaintContext *paint_context) +{ + return TRUE; +} + +static void +clutter_blit_node_draw (ClutterPaintNode *node, + ClutterPaintContext *paint_context) +{ + ClutterBlitNode *blit_node = CLUTTER_BLIT_NODE (node); + g_autoptr (GError) error = NULL; + CoglFramebuffer *source; + guint i; + + if (node->operations == NULL) + return; + + source = blit_node->source; + if (!source) + source = clutter_paint_context_get_framebuffer (paint_context); + + for (i = 0; i < node->operations->len; i++) + { + const ClutterPaintOperation *op; + float op_width, op_height; + + op = &g_array_index (node->operations, ClutterPaintOperation, i); + + switch (op->opcode) + { + case PAINT_OP_INVALID: + break; + + case PAINT_OP_TEX_RECT: + op_width = op->op.texrect[6] - op->op.texrect[4]; + op_height = op->op.texrect[7] - op->op.texrect[5]; + + cogl_blit_framebuffer (source, + blit_node->dest, + op->op.texrect[0], + op->op.texrect[1], + op->op.texrect[4], + op->op.texrect[5], + op_width, + op_height, + &error); + + if (error) + { + g_warning ("Error blitting framebuffers: %s", error->message); + return; + } + break; + + case PAINT_OP_MULTITEX_RECT: + case PAINT_OP_PRIMITIVE: + break; + } + } +} + +static void +clutter_blit_node_finalize (ClutterPaintNode *node) +{ + ClutterBlitNode *blit_node = CLUTTER_BLIT_NODE (node); + + cogl_clear_object (&blit_node->source); + cogl_clear_object (&blit_node->dest); + + CLUTTER_PAINT_NODE_CLASS (clutter_layer_node_parent_class)->finalize (node); +} + +static JsonNode * +clutter_blit_node_serialize (ClutterPaintNode *node) +{ + ClutterBlitNode *blit_node = CLUTTER_BLIT_NODE (node); + g_autoptr (JsonBuilder) builder = NULL; + g_autofree char *source_ptr = NULL; + g_autofree char *dest_ptr = NULL; + + builder = json_builder_new (); + + source_ptr = g_strdup_printf ("%p", blit_node->source); + dest_ptr = g_strdup_printf ("%p", blit_node->dest); + + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "source"); + json_builder_add_string_value (builder, source_ptr); + json_builder_end_object (builder); + + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "dest"); + json_builder_add_string_value (builder, dest_ptr); + json_builder_end_object (builder); + + return json_builder_get_root (builder); +} + +static void +clutter_blit_node_class_init (ClutterTransformNodeClass *klass) +{ + ClutterPaintNodeClass *node_class; + + node_class = CLUTTER_PAINT_NODE_CLASS (klass); + node_class->pre_draw = clutter_blit_node_pre_draw; + node_class->draw = clutter_blit_node_draw; + node_class->finalize = clutter_blit_node_finalize; + node_class->serialize = clutter_blit_node_serialize; +} + +static void +clutter_blit_node_init (ClutterBlitNode *self) +{ +} + +/** + * clutter_blit_node_new: + * @source: (nullable): the source #CoglFramebuffer + * @dest: the destination #CoglFramebuffer + * + * Creates a new #ClutterBlitNode that blits @source into @dest. + * + * If @source is %NULL, the most recent framebuffer stacked into a + * #ClutterPaintContext is used. + * + * You must only add rectangles using clutter_blit_node_add_blit_rectangle(). + * + * Return value: (transfer full): the newly created #ClutterBlitNode. + * Use clutter_paint_node_unref() when done. + */ +ClutterPaintNode * +clutter_blit_node_new (CoglFramebuffer *source, + CoglFramebuffer *dest) +{ + ClutterBlitNode *res; + + g_return_val_if_fail (cogl_is_framebuffer (dest), NULL); + + res = _clutter_paint_node_create (CLUTTER_TYPE_BLIT_NODE); + res->source = source ? cogl_object_ref (source) : NULL; + res->dest = cogl_object_ref (dest); + + return (ClutterPaintNode *) res; +} + +/** + * clutter_blit_node_add_blit_rectangle: + * @blit_node: a #ClutterBlitNode + * @src_x: Source x position + * @src_y: Source y position + * @dst_x: Destination x position + * @dst_y: Destination y position + * @width: Width of region to copy + * @height: Height of region to copy + * + * Adds a new blit rectangle to the stack of rectangles. All the + * constraints of cogl_blit_framebuffer() apply here. + */ +void +clutter_blit_node_add_blit_rectangle (ClutterBlitNode *blit_node, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height) +{ + g_return_if_fail (CLUTTER_IS_BLIT_NODE (blit_node)); + + clutter_paint_node_add_texture_rectangle (CLUTTER_PAINT_NODE (blit_node), + &(ClutterActorBox) { + src_x, + src_y, + src_x + width, + src_y + height, + }, + dst_x, + dst_y, + dst_x + width, + dst_y + height); +} diff --git a/clutter/clutter/clutter-paint-nodes.h b/clutter/clutter/clutter-paint-nodes.h index 5303718ba..0a0ee743f 100644 --- a/clutter/clutter/clutter-paint-nodes.h +++ b/clutter/clutter/clutter-paint-nodes.h @@ -236,6 +236,38 @@ GType clutter_transform_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_transform_node_new (const CoglMatrix *projection); +#define CLUTTER_TYPE_BLIT_NODE (clutter_blit_node_get_type ()) +#define CLUTTER_BLIT_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BLIT_NODE, ClutterBlitNode)) +#define CLUTTER_IS_BLIT_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BLIT_NODE)) + +/* + * ClutterBlitNode: + * + * The #ClutterLayerNode structure is an opaque + * type whose members cannot be directly accessed. + * + * Since: 1.10 + */ +typedef struct _ClutterBlitNode ClutterBlitNode; +typedef struct _ClutterPaintNodeClass ClutterBlitNodeClass; + +CLUTTER_EXPORT +GType clutter_blit_node_get_type (void) G_GNUC_CONST; + +CLUTTER_EXPORT +ClutterPaintNode * clutter_blit_node_new (CoglFramebuffer *source, + CoglFramebuffer *dest); + + +CLUTTER_EXPORT +void clutter_blit_node_add_blit_rectangle (ClutterBlitNode *blit_node, + int src_x, + int src_y, + int dst_x, + int dst_y, + int width, + int height); + G_END_DECLS #endif /* __CLUTTER_PAINT_NODES_H__ */