[gtk/mask-nodes: 3/5] wip: Render fill nodes using a mask shader




commit 310dccf03b98b5640e41df64174852c10fe1d21a
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Dec 14 20:35:39 2020 -0500

    wip: Render fill nodes using a mask shader
    
    This is not quite finished yet.

 gsk/gl/gskglrenderer.c  | 56 ++++++++++++++++++++++++++++++++++++-
 gsk/gskrendernode.h     |  2 ++
 gsk/gskrendernodeimpl.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+), 1 deletion(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 0b8836817b..422d2e9c6d 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -2732,6 +2732,57 @@ render_mask_node (GskGLRenderer   *self,
   load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
 }
 
+static inline void
+render_fill_node (GskGLRenderer   *self,
+                  GskRenderNode   *node,
+                  RenderOpBuilder *builder)
+{
+  GskRenderNode *child = gsk_fill_node_get_child (node);
+  GskRenderNode *mask_node;
+  TextureRegion child_region;
+  TextureRegion mask_region;
+  gboolean is_offscreen1, is_offscreen2;
+  OpMask *op;
+
+  if (!add_offscreen_ops (self, builder,
+                          &node->bounds,
+                          child,
+                          &child_region, &is_offscreen1,
+                          FORCE_OFFSCREEN | RESET_CLIP))
+    {
+      gsk_gl_renderer_add_render_ops (self, child, builder);
+      return;
+    }
+
+  /* FIXME: Figure out how to make the texture coordinates set
+   * up properly without a temporary node
+   */
+  mask_node = gsk_texture_node_new (gsk_fill_node_get_mask (node),
+                                    &node->bounds);
+
+  if (!add_offscreen_ops (self, builder,
+                          &node->bounds,
+                          mask_node,
+                          &mask_region, &is_offscreen2,
+                          FORCE_OFFSCREEN | RESET_CLIP))
+    {
+      gsk_render_node_unref (mask_node);
+      gsk_gl_renderer_add_render_ops (self, child, builder);
+      return;
+    }
+
+  gsk_render_node_unref (mask_node);
+
+  ops_set_program (builder, &self->programs->mask_program);
+
+  op = ops_begin (builder, OP_CHANGE_MASK);
+  op->mask = mask_region.texture_id;
+
+  ops_set_texture (builder, child_region.texture_id);
+
+  load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
+}
+
 static inline void
 render_blend_node (GskGLRenderer   *self,
                    GskRenderNode   *node,
@@ -3840,9 +3891,12 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
       render_mask_node (self, node, builder);
     break;
 
+    case GSK_FILL_NODE:
+      render_fill_node (self, node, builder);
+    break;
+
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
-    case GSK_FILL_NODE:
     case GSK_STROKE_NODE:
     case GSK_CAIRO_NODE:
     default:
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 0e0e893e12..dd0e2ce1ee 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -461,6 +461,8 @@ 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
+GdkTexture *            gsk_fill_node_get_mask                  (GskRenderNode            *node);
 
 GDK_AVAILABLE_IN_ALL
 GType                   gsk_stroke_node_get_type                (void) G_GNUC_CONST;
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 92b5a85ed1..99914e92e9 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -3670,6 +3670,7 @@ struct _GskFillNode
   GskRenderNode *child;
   GskPath *path;
   GskFillRule fill_rule;
+  GdkTexture *mask;
 };
 
 static void
@@ -3680,6 +3681,7 @@ gsk_fill_node_finalize (GskRenderNode *node)
 
   gsk_render_node_unref (self->child);
   gsk_path_unref (self->path);
+  g_clear_object (&self->mask);
 
   parent_class->finalize (node);
 }
@@ -3740,6 +3742,65 @@ gsk_fill_node_diff (GskRenderNode  *node1,
     }
 }
 
+static GdkTexture *
+make_path_mask (GskRenderNode *node)
+{
+  GskFillNode *self = (GskFillNode *) node;
+  graphene_rect_t bounds;
+  cairo_surface_t *surface;
+  cairo_t *cr;
+  int width;
+  int height;
+  int stride;
+  guchar *buffer;
+  GBytes *bytes;
+  GdkTexture *mask;
+
+  gsk_render_node_get_bounds (node, &bounds);
+  width = ceilf (bounds.size.width);
+  height = ceilf (bounds.size.height);
+  stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
+
+  buffer = g_malloc0 (stride * height);
+  surface = cairo_image_surface_create_for_data (buffer,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 width, height,
+                                                 stride);
+
+  cr = cairo_create (surface);
+
+  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);
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+
+  bytes = g_bytes_new_take (buffer, stride * height);
+  mask = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride);
+  g_bytes_unref (bytes);
+
+  gdk_texture_save_to_png (mask, "mask.png");
+
+  cairo_surface_destroy (surface);
+
+  return mask;
+}
+
 /**
  * gsk_fill_node_new:
  * @child: The node to fill the area with
@@ -3775,6 +3836,8 @@ gsk_fill_node_new (GskRenderNode *child,
   else
     graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
 
+  self->mask = make_path_mask (node);
+
   return node;
 }
 
@@ -3833,6 +3896,16 @@ gsk_fill_node_get_fill_rule (GskRenderNode *node)
   return self->fill_rule;
 }
 
+GdkTexture *
+gsk_fill_node_get_mask (GskRenderNode *node)
+{
+  GskFillNode *self = (GskFillNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_FILL_NODE), NULL);
+
+  return self->mask;
+}
+
 /*** GSK_STROKE_NODE ***/
 
 struct _GskStrokeNode


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