[gtk/wip/otte/lottie: 4/27] gsk: Add GskFillNode




commit 19161b89ade33befc3eef98e64d30f1f3f213757
Author: Benjamin Otte <otte redhat com>
Date:   Wed Nov 11 00:44:32 2020 +0100

    gsk: Add GskFillNode
    
    Take a rendernode as source and a GskPath and fill the region in the
    path just like cairo_fill() would.

 docs/reference/gsk/gsk4-sections.txt |   8 ++
 gsk/broadway/gskbroadwayrenderer.c   |   2 +
 gsk/gl/gskglrenderer.c               |   1 +
 gsk/gskenums.h                       |  29 ++++++
 gsk/gskrendernode.h                  |  15 +++
 gsk/gskrendernodeimpl.c              | 189 +++++++++++++++++++++++++++++++++++
 gsk/gskrendernodeparser.c            |  15 +++
 gsk/vulkan/gskvulkanrenderpass.c     |   1 +
 gtk/inspector/recorder.c             |  16 +++
 9 files changed, 276 insertions(+)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 4a8b64b624..397f1197fe 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -140,6 +140,12 @@ gsk_rounded_clip_node_new
 gsk_rounded_clip_node_get_child
 gsk_rounded_clip_node_get_clip
 <SUBSECTION>
+GskFillRule
+gsk_fill_node_new
+gsk_fill_node_get_child
+gsk_fill_node_get_path
+gsk_fill_node_get_fill_rule
+<SUBSECTION>
 GskShadow
 gsk_shadow_node_new
 gsk_shadow_node_get_shadow
@@ -192,6 +198,7 @@ GSK_TYPE_COLOR_NODE
 GSK_TYPE_CONTAINER_NODE
 GSK_TYPE_CROSS_FADE_NODE
 GSK_TYPE_DEBUG_NODE
+GSK_TYPE_FILL_NODE
 GSK_TYPE_INSET_SHADOW_NODE
 GSK_TYPE_LINEAR_GRADIENT_NODE
 GSK_TYPE_OPACITY_NODE
@@ -217,6 +224,7 @@ gsk_color_node_get_type
 gsk_container_node_get_type
 gsk_cross_fade_node_get_type
 gsk_debug_node_get_type
+gsk_fill_node_get_type
 gsk_inset_shadow_node_get_type
 gsk_linear_gradient_node_get_type
 gsk_opacity_node_get_type
diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c
index 80a3f84968..d45ea9b8e5 100644
--- a/gsk/broadway/gskbroadwayrenderer.c
+++ b/gsk/broadway/gskbroadwayrenderer.c
@@ -268,6 +268,7 @@ collect_reused_child_nodes (GskRenderer *renderer,
     case GSK_BLEND_NODE:
     case GSK_CROSS_FADE_NODE:
     case GSK_BLUR_NODE:
+    case GSK_FILL_NODE:
 
     default:
 
@@ -853,6 +854,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
     case GSK_CROSS_FADE_NODE:
     case GSK_BLUR_NODE:
     case GSK_GL_SHADER_NODE:
+    case GSK_FILL_NODE:
     default:
       break; /* Fallback */
     }
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 1d644da24c..d92fa2984e 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -3723,6 +3723,7 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
 
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_FILL_NODE:
     case GSK_CAIRO_NODE:
     default:
       {
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 0cd0c284fc..f28cc0401c 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -71,6 +71,7 @@ typedef enum {
   GSK_REPEAT_NODE,
   GSK_CLIP_NODE,
   GSK_ROUNDED_CLIP_NODE,
+  GSK_FILL_NODE,
   GSK_SHADOW_NODE,
   GSK_BLEND_NODE,
   GSK_CROSS_FADE_NODE,
@@ -165,6 +166,34 @@ typedef enum {
   GSK_CORNER_BOTTOM_LEFT
 } GskCorner;
 
+/**
+ * GskFillRule:
+ * @GSK_FILL_RULE_WINDING: If the path crosses the ray from
+ *   left-to-right, counts +1. If the path crosses the ray
+ *   from right to left, counts -1. (Left and right are determined
+ *   from the perspective of looking along the ray from the starting
+ *   point.) If the total count is non-zero, the point will be filled.
+ * @GSK_FILL_RULE_EVEN_ODD: Counts the total number of
+ *   intersections, without regard to the orientation of the contour. If
+ *   the total number of intersections is odd, the point will be
+ *   filled.
+ *
+ * #GskFillRule is used to select how paths are filled, for example in
+ * gsk_fill_node_new(). Whether or not a point is included in the fill is
+ * determined by taking a ray from that point to infinity and looking
+ * at intersections with the path. The ray can be in any direction,
+ * as long as it doesn't pass through the end point of a segment
+ * or have a tricky intersection such as intersecting tangent to the path.
+ * (Note that filling is not actually implemented in this way. This
+ * is just a description of the rule that is applied.)
+ *
+ * New entries may be added in future versions.
+ **/
+typedef enum {
+  GSK_FILL_RULE_WINDING,
+  GSK_FILL_RULE_EVEN_ODD
+} GskFillRule;
+
 /**
  * GskSerializationError:
  * @GSK_SERIALIZATION_UNSUPPORTED_FORMAT: The format can not be
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index dba436afda..3fdcb63e10 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -131,6 +131,7 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 #define GSK_TYPE_REPEAT_NODE                    (gsk_repeat_node_get_type())
 #define GSK_TYPE_CLIP_NODE                      (gsk_clip_node_get_type())
 #define GSK_TYPE_ROUNDED_CLIP_NODE              (gsk_rounded_clip_node_get_type())
+#define GSK_TYPE_FILL_NODE                      (gsk_fill_node_get_type())
 #define GSK_TYPE_SHADOW_NODE                    (gsk_shadow_node_get_type())
 #define GSK_TYPE_BLEND_NODE                     (gsk_blend_node_get_type())
 #define GSK_TYPE_CROSS_FADE_NODE                (gsk_cross_fade_node_get_type())
@@ -156,6 +157,7 @@ typedef struct _GskColorMatrixNode              GskColorMatrixNode;
 typedef struct _GskRepeatNode                   GskRepeatNode;
 typedef struct _GskClipNode                     GskClipNode;
 typedef struct _GskRoundedClipNode              GskRoundedClipNode;
+typedef struct _GskFillNode                     GskFillNode;
 typedef struct _GskShadowNode                   GskShadowNode;
 typedef struct _GskBlendNode                    GskBlendNode;
 typedef struct _GskCrossFadeNode                GskCrossFadeNode;
@@ -397,6 +399,19 @@ GskRenderNode *         gsk_rounded_clip_node_get_child         (GskRenderNode
 GDK_AVAILABLE_IN_ALL
 const GskRoundedRect *  gsk_rounded_clip_node_get_clip          (GskRenderNode            *node);
 
+GDK_AVAILABLE_IN_ALL
+GType                   gsk_fill_node_get_type                  (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_fill_node_new                       (GskRenderNode            *child,
+                                                                 GskPath                  *path,
+                                                                 GskFillRule               fill_rule);
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_fill_node_get_child                 (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+GskPath *               gsk_fill_node_get_path                  (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+GskFillRule             gsk_fill_node_get_fill_rule             (GskRenderNode            *node);
+
 GDK_AVAILABLE_IN_ALL
 GType                   gsk_shadow_node_get_type                (void) G_GNUC_CONST;
 GDK_AVAILABLE_IN_ALL
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index e5228d88c9..1586e49cfc 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -23,6 +23,7 @@
 #include "gskcairoblurprivate.h"
 #include "gskdebugprivate.h"
 #include "gskdiffprivate.h"
+#include "gskpath.h"
 #include "gskrendererprivate.h"
 #include "gskroundedrectprivate.h"
 #include "gsktransformprivate.h"
@@ -3251,6 +3252,177 @@ gsk_rounded_clip_node_get_clip (GskRenderNode *node)
   return &self->clip;
 }
 
+/*** GSK_FILL_NODE ***/
+
+struct _GskFillNode
+{
+  GskRenderNode render_node;
+
+  GskRenderNode *child;
+  GskPath *path;
+  GskFillRule fill_rule;
+};
+
+static void
+gsk_fill_node_finalize (GskRenderNode *node)
+{
+  GskFillNode *self = (GskFillNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_FILL_NODE));
+
+  gsk_render_node_unref (self->child);
+  gsk_path_unref (self->path);
+
+  parent_class->finalize (node);
+}
+
+static void
+gsk_fill_node_draw (GskRenderNode *node,
+                    cairo_t       *cr)
+{
+  GskFillNode *self = (GskFillNode *) node;
+
+  cairo_save (cr);
+
+  switch (self->fill_rule)
+  {
+    case GSK_FILL_RULE_WINDING:
+      cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
+      break;
+    case GSK_FILL_RULE_EVEN_ODD:
+      cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+  gsk_path_to_cairo (self->path, cr);
+  cairo_clip (cr);
+
+  gsk_render_node_draw (self->child, cr);
+
+  cairo_restore (cr);
+}
+
+static void
+gsk_fill_node_diff (GskRenderNode  *node1,
+                    GskRenderNode  *node2,
+                    cairo_region_t *region)
+{
+  GskFillNode *self1 = (GskFillNode *) node1;
+  GskFillNode *self2 = (GskFillNode *) node2;
+
+  if (self1->path == self2->path)
+    {
+      cairo_region_t *sub;
+      cairo_rectangle_int_t clip_rect;
+      graphene_rect_t rect;
+
+      sub = cairo_region_create();
+      gsk_render_node_diff (self1->child, self2->child, sub);
+      graphene_rect_union (&node1->bounds, &node2->bounds, &rect);
+      rectangle_init_from_graphene (&clip_rect, &rect);
+      cairo_region_intersect_rectangle (sub, &clip_rect);
+      cairo_region_union (region, sub);
+      cairo_region_destroy (sub);
+    }
+  else
+    {
+      gsk_render_node_diff_impossible (node1, node2, region);
+    }
+}
+
+/**
+ * gsk_fill_node_new:
+ * @child: The node to fill the area with
+ * @path: The path describing the area to fill
+ * @fill_rule: The fill rule to use
+ *
+ * Creates a #GskRenderNode that will fill the @child in the area
+ * given by @path and @fill_rule.
+ *
+ * Returns: (transfer none) (type GskFillNode): A new #GskRenderNode
+ */
+GskRenderNode *
+gsk_fill_node_new (GskRenderNode *child,
+                   GskPath       *path,
+                   GskFillRule    fill_rule)
+{
+  GskFillNode *self;
+  GskRenderNode *node;
+  graphene_rect_t path_bounds;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
+  g_return_val_if_fail (path != NULL, NULL);
+
+  self = gsk_render_node_alloc (GSK_FILL_NODE);
+  node = (GskRenderNode *) self;
+
+  self->child = gsk_render_node_ref (child);
+  self->path = gsk_path_ref (path);
+
+  if (gsk_path_get_bounds (path, &path_bounds))
+    graphene_rect_intersection (&path_bounds, &child->bounds, &node->bounds);
+  else
+    graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
+
+  return node;
+}
+
+/**
+ * gsk_fill_node_get_child:
+ * @node: (type GskFillNode): a fill #GskRenderNode
+ *
+ * Gets the child node that is getting drawn by the given @node.
+ *
+ * Returns: (transfer none): The child that is getting drawn
+ **/
+GskRenderNode *
+gsk_fill_node_get_child (GskRenderNode *node)
+{
+  GskFillNode *self = (GskFillNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
+
+  return self->child;
+}
+
+/**
+ * gsk_fill_node_get_path:
+ * @node: (type GskFillNode): a fill #GskRenderNode
+ *
+ * Retrievs the path used to describe the area filled with the contents of
+ * the @node.
+ *
+ * Returns: (transfer none): a #GskPath
+ */
+GskPath *
+gsk_fill_node_get_path (GskRenderNode *node)
+{
+  GskFillNode *self = (GskFillNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
+
+  return self->path;
+}
+
+/**
+ * gsk_fill_node_get_fill_rule:
+ * @node: (type GskFillNode): a fill #GskRenderNode
+ *
+ * Retrievs the fill rule used to determine how the path is filled.
+ *
+ * Returns: a #GskFillRule
+ */
+GskFillRule
+gsk_fill_node_get_fill_rule (GskRenderNode *node)
+{
+  GskFillNode *self = (GskFillNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), GSK_FILL_RULE_WINDING);
+
+  return self->fill_rule;
+}
+
 /*** GSK_SHADOW_NODE ***/
 
 struct _GskShadowNode
@@ -4762,6 +4934,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_color_matrix_node, GSK_COLOR_MATRIX_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeat_node, GSK_REPEAT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_clip_node, GSK_CLIP_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_rounded_clip_node, GSK_ROUNDED_CLIP_NODE)
+GSK_DEFINE_RENDER_NODE_TYPE (gsk_fill_node, GSK_FILL_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_shadow_node, GSK_SHADOW_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
@@ -5045,6 +5218,22 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_ROUNDED_CLIP_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_FILL_NODE,
+      sizeof (GskFillNode),
+      NULL,
+      gsk_fill_node_finalize,
+      gsk_fill_node_draw,
+      NULL,
+      gsk_fill_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskFillNode"), &node_info);
+    gsk_render_node_types[GSK_FILL_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index fd62b6d4c3..ed565f8e53 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -23,6 +23,7 @@
 
 #include "gskrendernodeparserprivate.h"
 
+#include "gskpath.h"
 #include "gskroundedrectprivate.h"
 #include "gskrendernodeprivate.h"
 #include "gsktransformprivate.h"
@@ -2407,6 +2408,20 @@ render_node_print (Printer       *p,
         append_node_param (p, "child", gsk_rounded_clip_node_get_child (node));
         append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_get_clip (node));
 
+        end_node (p);
+      }
+      break;
+
+    case GSK_FILL_NODE:
+      {
+        char *path_str;
+
+        start_node (p, "fill");
+
+        append_node_param (p, "child", gsk_fill_node_get_child (node));
+        path_str = gsk_path_to_string (gsk_fill_node_get_path (node));
+        append_string_param (p, "path", path_str);
+        g_free (path_str);
 
         end_node (p);
       }
diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c
index 103412487f..d20f2a2306 100644
--- a/gsk/vulkan/gskvulkanrenderpass.c
+++ b/gsk/vulkan/gskvulkanrenderpass.c
@@ -259,6 +259,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
     case GSK_SHADOW_NODE:
     case GSK_RADIAL_GRADIENT_NODE:
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_FILL_NODE:
     default:
       FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
 
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index dd9a1de67e..9a8fb39b0a 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -160,6 +160,9 @@ create_list_model_for_render_node (GskRenderNode *node)
     case GSK_ROUNDED_CLIP_NODE:
       return create_render_node_list_model ((GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, 
1);
 
+    case GSK_FILL_NODE:
+      return create_render_node_list_model ((GskRenderNode *[1]) { gsk_fill_node_get_child (node) }, 1);
+
     case GSK_SHADOW_NODE:
       return create_render_node_list_model ((GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, 1);
 
@@ -279,6 +282,8 @@ node_type_name (GskRenderNodeType type)
       return "Clip";
     case GSK_ROUNDED_CLIP_NODE:
       return "Rounded Clip";
+    case GSK_FILL_NODE:
+      return "Fill";
     case GSK_SHADOW_NODE:
       return "Shadow";
     case GSK_BLEND_NODE:
@@ -317,6 +322,7 @@ node_name (GskRenderNode *node)
     case GSK_REPEAT_NODE:
     case GSK_CLIP_NODE:
     case GSK_ROUNDED_CLIP_NODE:
+    case GSK_FILL_NODE:
     case GSK_SHADOW_NODE:
     case GSK_BLEND_NODE:
     case GSK_CROSS_FADE_NODE:
@@ -1034,6 +1040,16 @@ populate_render_node_properties (GtkListStore  *store,
       }
       break;
 
+    case GSK_FILL_NODE:
+      {
+        GskPath *path = gsk_fill_node_get_path (node);
+
+        tmp = gsk_path_to_string (path);
+        add_text_row (store, "Path", tmp);
+        g_free (tmp);
+      }
+      break;
+
     case GSK_CONTAINER_NODE:
       tmp = g_strdup_printf ("%d", gsk_container_node_get_n_children (node));
       add_text_row (store, "Children", tmp);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]