From 4a69e700007c597934e77706aad38517b6d091c7 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 | 191 ++++++++++++++++++++++++++ clutter/clutter/clutter-paint-nodes.h | 30 ++++ 2 files changed, 221 insertions(+) diff --git a/clutter/clutter/clutter-paint-nodes.c b/clutter/clutter/clutter-paint-nodes.c index 86f605997..3bce5f615 100644 --- a/clutter/clutter/clutter-paint-nodes.c +++ b/clutter/clutter/clutter-paint-nodes.c @@ -1625,3 +1625,194 @@ clutter_layer_node_new_with_framebuffer (CoglFramebuffer *framebuffer, return (ClutterPaintNode *) res; } + +/* + * ClutterBlitNode + */ + +struct _ClutterBlitNode +{ + ClutterPaintNode parent_instance; + + CoglFramebuffer *src; + CoglFramebuffer *dst; +}; + +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; + guint i; + + if (node->operations == NULL) + return; + + 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 (blit_node->src, + blit_node->dst, + 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->src); + cogl_clear_object (&blit_node->dst); + + CLUTTER_PAINT_NODE_CLASS (clutter_blit_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 *src_ptr = NULL; + g_autofree char *dst_ptr = NULL; + + builder = json_builder_new (); + + src_ptr = g_strdup_printf ("%p", blit_node->src); + dst_ptr = g_strdup_printf ("%p", blit_node->dst); + + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "source"); + json_builder_add_string_value (builder, src_ptr); + json_builder_end_object (builder); + + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "dst"); + json_builder_add_string_value (builder, dst_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: + * @src: the source #CoglFramebuffer + * @dst: the destination #CoglFramebuffer + * + * Creates a new #ClutterBlitNode that blits @src into @dst. + * + * 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 *src, + CoglFramebuffer *dst) +{ + ClutterBlitNode *res; + + g_return_val_if_fail (cogl_is_framebuffer (src), NULL); + g_return_val_if_fail (cogl_is_framebuffer (dst), NULL); + + res = _clutter_paint_node_create (CLUTTER_TYPE_BLIT_NODE); + res->src = cogl_object_ref (src); + res->dst = cogl_object_ref (dst); + + 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) { + .x1 = src_x, + .y1 = src_y, + .x2 = src_x + width, + .y2 = 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..f2b2dc1eb 100644 --- a/clutter/clutter/clutter-paint-nodes.h +++ b/clutter/clutter/clutter-paint-nodes.h @@ -236,6 +236,36 @@ 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. + */ +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 *src, + CoglFramebuffer *dst); + + +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__ */