gsk: Introduce mask nodes
authorMatthias Clasen <mclasen@redhat.com>
Sat, 11 Feb 2023 13:59:06 +0000 (08:59 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Sun, 12 Feb 2023 13:35:25 +0000 (08:35 -0500)
Add GskMaskNode, and support it in the render node
parser, in the inspector and in GtkSnapshot.

The rendering is just fallback for now.

Based on old work by Timm Bäder.

gsk/broadway/gskbroadwayrenderer.c
gsk/gl/gskglrenderjob.c
gsk/gskenums.h
gsk/gskrendernode.h
gsk/gskrendernodeimpl.c
gsk/gskrendernodeparser.c
gsk/vulkan/gskvulkanrenderpass.c
gtk/inspector/recorder.c

index 6fee960edf39b931bab753eff422c4340ba3160e..0408c25cff01d8b63150a9aba4cd2e5b171a6df7 100644 (file)
@@ -270,6 +270,7 @@ collect_reused_child_nodes (GskRenderer *renderer,
     case GSK_BLEND_NODE:
     case GSK_CROSS_FADE_NODE:
     case GSK_BLUR_NODE:
+    case GSK_MASK_NODE:
 
     default:
 
@@ -846,6 +847,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
       }
       break; /* Fallback */
 
+    case GSK_MASK_NODE:
     case GSK_TEXTURE_SCALE_NODE:
     case GSK_TEXT_NODE:
     case GSK_RADIAL_GRADIENT_NODE:
index b26a691cc554c6bb55d63d32668ffd3a3ac4dc14..2db40bd25fb200893ab4f30d13b5345135a1e791 100644 (file)
@@ -3869,6 +3869,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob      *job,
         gsk_gl_render_job_visit_as_fallback (job, node);
     break;
 
+    case GSK_MASK_NODE:
+      gsk_gl_render_job_visit_as_fallback (job, node);
+    break;
+
     case GSK_OPACITY_NODE:
       gsk_gl_render_job_visit_opacity_node (job, node);
     break;
index 0c92555cfd79ff2e670d469c9869e558d1e5a932..7b89dac642c0e9715a5c79dfeb8b251c53c5b4ac 100644 (file)
@@ -46,6 +46,7 @@
  * @GSK_ROUNDED_CLIP_NODE: A node that clips its child to a rounded rectangle
  * @GSK_SHADOW_NODE: A node that draws a shadow below its child
  * @GSK_BLEND_NODE: A node that blends two children together
+ * @GSK_MASK_NODE: A node that masks one child with another
  * @GSK_CROSS_FADE_NODE: A node that cross-fades between two children
  * @GSK_TEXT_NODE: A node containing a glyph string
  * @GSK_BLUR_NODE: A node that applies a blur
@@ -77,6 +78,7 @@ typedef enum {
   GSK_ROUNDED_CLIP_NODE,
   GSK_SHADOW_NODE,
   GSK_BLEND_NODE,
+  GSK_MASK_NODE,
   GSK_CROSS_FADE_NODE,
   GSK_TEXT_NODE,
   GSK_BLUR_NODE,
index fb6e1f51f699e3d34937b7d73bc7332770f0b156..7147b0b848f9756ef491e15d57d00a887c79b8fb 100644 (file)
@@ -164,6 +164,7 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 #define GSK_TYPE_CROSS_FADE_NODE                (gsk_cross_fade_node_get_type())
 #define GSK_TYPE_TEXT_NODE                      (gsk_text_node_get_type())
 #define GSK_TYPE_BLUR_NODE                      (gsk_blur_node_get_type())
+#define GSK_TYPE_MASK_NODE                      (gsk_mask_node_get_type())
 #define GSK_TYPE_GL_SHADER_NODE                 (gsk_gl_shader_node_get_type())
 
 typedef struct _GskDebugNode                    GskDebugNode;
@@ -191,6 +192,7 @@ typedef struct _GskBlendNode                    GskBlendNode;
 typedef struct _GskCrossFadeNode                GskCrossFadeNode;
 typedef struct _GskTextNode                     GskTextNode;
 typedef struct _GskBlurNode                     GskBlurNode;
+typedef struct _GskMaskNode                     GskMaskNode;
 typedef struct _GskGLShaderNode                 GskGLShaderNode;
 
 GDK_AVAILABLE_IN_ALL
@@ -529,8 +531,18 @@ GskRenderNode *         gsk_blur_node_get_child                 (const GskRender
 GDK_AVAILABLE_IN_ALL
 float                   gsk_blur_node_get_radius                (const GskRenderNode      *node) G_GNUC_PURE;
 
+GDK_AVAILABLE_IN_4_10
+GType                  gsk_mask_node_get_type                   (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_4_10
+GskRenderNode *        gsk_mask_node_new                        (GskRenderNode            *source,
+                                                                 GskRenderNode            *mask);
+GDK_AVAILABLE_IN_4_10
+GskRenderNode *        gsk_mask_node_get_source                 (const GskRenderNode      *node);
+GDK_AVAILABLE_IN_4_10
+GskRenderNode *        gsk_mask_node_get_mask                   (const GskRenderNode      *node);
+
 GDK_AVAILABLE_IN_ALL
-GType                   gsk_gl_shader_node_get_type              (void) G_GNUC_CONST;
+GType                   gsk_gl_shader_node_get_type             (void) G_GNUC_CONST;
 GDK_AVAILABLE_IN_ALL
 GskRenderNode *         gsk_gl_shader_node_new                  (GskGLShader              *shader,
                                                                  const graphene_rect_t    *bounds,
index 376d32cb4ef62af5a5f5fbe01912fb9a52e8871f..f9de3752d3c8135d6728ea03280eeacca58b75bd 100644 (file)
@@ -5184,6 +5184,135 @@ gsk_blur_node_get_radius (const GskRenderNode *node)
   return self->radius;
 }
 
+/* }}} */
+/* {{{ GSK_MASK_NODE */
+
+/**
+ * GskMaskNode:
+ *
+ * A render node masking one child node with another.
+ *
+ * Since: 4.10
+ */
+typedef struct _GskMaskNode GskMaskNode;
+
+struct _GskMaskNode
+{
+  GskRenderNode render_node;
+
+  GskRenderNode *mask;
+  GskRenderNode *source;
+};
+
+static void
+gsk_mask_node_finalize (GskRenderNode *node)
+{
+  GskMaskNode *self = (GskMaskNode *) node;
+
+  gsk_render_node_unref (self->source);
+  gsk_render_node_unref (self->mask);
+}
+
+static void
+gsk_mask_node_draw (GskRenderNode *node,
+                    cairo_t       *cr)
+{
+  GskMaskNode *self = (GskMaskNode *) node;
+  cairo_pattern_t *mask_pattern;
+
+  cairo_push_group (cr);
+  gsk_render_node_draw (self->source, cr);
+  cairo_pop_group_to_source (cr);
+
+  cairo_push_group (cr);
+  gsk_render_node_draw (self->mask, cr);
+  mask_pattern = cairo_pop_group (cr);
+
+  cairo_mask (cr, mask_pattern);
+}
+
+static void
+gsk_mask_node_diff (GskRenderNode  *node1,
+                    GskRenderNode  *node2,
+                    cairo_region_t *region)
+{
+  GskMaskNode *self1 = (GskMaskNode *) node1;
+  GskMaskNode *self2 = (GskMaskNode *) node2;
+
+  gsk_render_node_diff (self1->source, self2->source, region);
+  gsk_render_node_diff (self1->mask, self2->mask, region);
+}
+
+/**
+ * gsk_mask_node_new:
+ * @source: The bottom node to be drawn
+ * @mask: The node to be blended onto the @bottom node
+ *
+ * Creates a `GskRenderNode` that will use @blend_mode to blend the @top
+ * node onto the @bottom node.
+ *
+ * Returns: (transfer full) (type GskMaskNode): A new `GskRenderNode`
+ *
+ * Since: 4.10
+ */
+GskRenderNode *
+gsk_mask_node_new (GskRenderNode *source,
+                   GskRenderNode *mask)
+{
+  GskMaskNode *self;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (source), NULL);
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (mask), NULL);
+
+  self = gsk_render_node_alloc (GSK_MASK_NODE);
+  self->source = gsk_render_node_ref (source);
+  self->mask = gsk_render_node_ref (mask);
+
+  graphene_rect_union (&source->bounds, &mask->bounds, &self->render_node.bounds);
+
+  return &self->render_node;
+}
+
+/**
+ * gsk_mask_node_get_source:
+ * @node: (type GskBlendNode): a mask `GskRenderNode`
+ *
+ * Retrieves the source `GskRenderNode` child of the @node.
+ *
+ * Returns: (transfer none): the source child node
+ *
+ * Since: 4.10
+ */
+GskRenderNode *
+gsk_mask_node_get_source (const GskRenderNode *node)
+{
+  const GskMaskNode *self = (const GskMaskNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL);
+
+  return self->source;
+}
+
+/**
+ * gsk_mask_node_get_mask:
+ * @node: (type GskBlendNode): a mask `GskRenderNode`
+ *
+ * Retrieves the mask `GskRenderNode` child of the @node.
+ *
+ * Returns: (transfer none): the mask child node
+ *
+ * Since: 4.10
+ */
+GskRenderNode *
+gsk_mask_node_get_mask (const GskRenderNode *node)
+{
+  const GskMaskNode *self = (const GskMaskNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_MASK_NODE), NULL);
+
+  return self->mask;
+}
+
 /* }}} */
 /* {{{ GSK_DEBUG_NODE */
 
@@ -5556,6 +5685,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_text_node, GSK_TEXT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_blur_node, GSK_BLUR_NODE)
+GSK_DEFINE_RENDER_NODE_TYPE (gsk_mask_node, GSK_MASK_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
 
@@ -5946,6 +6076,22 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_BLUR_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_MASK_NODE,
+      sizeof (GskMaskNode),
+      NULL,
+      gsk_mask_node_finalize,
+      gsk_mask_node_draw,
+      NULL,
+      gsk_mask_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskMaskNode"), &node_info);
+    gsk_render_node_types[GSK_MASK_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
index ee27eda5a7ab8c9b9ff22e5c839cdd8c4cde1017..671ea34c02bbada4884b189c7c3d711e9cd6a971 100644 (file)
@@ -1376,6 +1376,31 @@ parse_glshader_node (GtkCssParser *parser)
   return node;
 }
 
+static GskRenderNode *
+parse_mask_node (GtkCssParser *parser)
+{
+  GskRenderNode *source = NULL;
+  GskRenderNode *mask = NULL;
+  const Declaration declarations[] = {
+    { "source", parse_node, clear_node, &source },
+    { "mask", parse_node, clear_node, &mask },
+  };
+  GskRenderNode *result;
+
+  parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
+  if (source == NULL)
+    source = create_default_render_node ();
+  if (mask == NULL)
+    mask = gsk_color_node_new (&GDK_RGBA("AAFF00"), &GRAPHENE_RECT_INIT (0, 0, 50, 50));
+
+  result = gsk_mask_node_new (source, mask);
+
+  gsk_render_node_unref (source);
+  gsk_render_node_unref (mask);
+
+  return result;
+}
+
 static GskRenderNode *
 parse_border_node (GtkCssParser *parser)
 {
@@ -1914,6 +1939,7 @@ parse_node (GtkCssParser *parser,
     { "texture-scale", parse_texture_scale_node },
     { "transform", parse_transform_node },
     { "glshader", parse_glshader_node },
+    { "mask", parse_mask_node },
   };
   GskRenderNode **node_p = out_node;
   guint i;
@@ -3100,6 +3126,17 @@ render_node_print (Printer       *p,
       }
       break;
 
+    case GSK_MASK_NODE:
+      {
+        start_node (p, "mask");
+
+        append_node_param (p, "source", gsk_mask_node_get_source (node));
+        append_node_param (p, "mask", gsk_mask_node_get_mask (node));
+
+        end_node (p);
+      }
+      break;
+
     case GSK_NOT_A_RENDER_NODE:
       g_assert_not_reached ();
       break;
index 3008c42154ddf2e8b99a014be21e0c7577e0b489..38a2f806e565a128f13a56684e57bbb6cdd23927 100644 (file)
@@ -504,6 +504,9 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
       g_array_append_val (self->render_ops, op);
       return;
 
+    case GSK_MASK_NODE:
+      goto fallback;
+
     case GSK_COLOR_MATRIX_NODE:
       if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
         pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX;
index 53528a26cbfb046023e7028849db65085dc32828..9f75b1ab6ec5f2b9d7d766a0a3930cb537865b4d 100644 (file)
@@ -305,6 +305,10 @@ create_list_model_for_render_node (GskRenderNode *node)
       return create_render_node_list_model ((GskRenderNode *[2]) { gsk_blend_node_get_bottom_child (node),
                                                                    gsk_blend_node_get_top_child (node) }, 2);
 
+    case GSK_MASK_NODE:
+      return create_render_node_list_model ((GskRenderNode *[2]) { gsk_mask_node_get_source (node),
+                                                                   gsk_mask_node_get_mask (node) }, 2);
+
     case GSK_CROSS_FADE_NODE:
       return create_render_node_list_model ((GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child (node),
                                                                    gsk_cross_fade_node_get_end_child (node) }, 2);
@@ -425,6 +429,8 @@ node_type_name (GskRenderNodeType type)
       return "Shadow";
     case GSK_BLEND_NODE:
       return "Blend";
+    case GSK_MASK_NODE:
+      return "Mask";
     case GSK_CROSS_FADE_NODE:
       return "CrossFade";
     case GSK_TEXT_NODE:
@@ -462,6 +468,7 @@ node_name (GskRenderNode *node)
     case GSK_ROUNDED_CLIP_NODE:
     case GSK_SHADOW_NODE:
     case GSK_BLEND_NODE:
+    case GSK_MASK_NODE:
     case GSK_CROSS_FADE_NODE:
     case GSK_TEXT_NODE:
     case GSK_BLUR_NODE:
@@ -1132,6 +1139,9 @@ populate_render_node_properties (GListStore    *store,
       }
       break;
 
+    case GSK_MASK_NODE:
+      break;
+
     case GSK_BLUR_NODE:
       add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
       break;