[gtk+] gsk: Add GskRepeatNode



commit b993acfe2c7b81e6ed4c35eb4f30bac29f43254e
Author: Benjamin Otte <otte redhat com>
Date:   Sun Jan 1 19:52:18 2017 +0100

    gsk: Add GskRepeatNode
    
    Also add gtk_snapshot_push_repeat() and use that to draw backgrounds.
    
    With that change, CSS background snapshots are created without Cairo
    nodes.

 docs/reference/gsk/gsk4-sections.txt   |    1 +
 docs/reference/gtk/gtk4-sections.txt   |    1 +
 gsk/gskenums.h                         |    3 +
 gsk/gskrendernode.h                    |    5 +
 gsk/gskrendernodeimpl.c                |  171 ++++++++++++++++++++++++++++++++
 gsk/gskrendernodeprivate.h             |    3 +
 gtk/gtkrenderbackground.c              |   71 ++++++--------
 gtk/gtksnapshot.c                      |   70 +++++++++++++
 gtk/gtksnapshot.h                      |    6 +
 gtk/inspector/gtktreemodelrendernode.c |    4 +
 gtk/inspector/recorder.c               |    2 +
 11 files changed, 297 insertions(+), 40 deletions(-)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 158b415..a4498ca 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -56,6 +56,7 @@ gsk_transform_node_get_child
 gsk_opacity_node_new
 gsk_opacity_node_get_child
 gsk_color_matrix_node_new
+gsk_repeat_node_new
 gsk_clip_node_new
 gsk_clip_node_get_child
 gsk_rounded_clip_node_new
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index e3d3bd2..0791d1e 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -4459,6 +4459,7 @@ gtk_snapshot_push_node
 gtk_snapshot_push_transform
 gtk_snapshot_push_opacity
 gtk_snapshot_push_color_matrix
+gtk_snapshot_push_repeat
 gtk_snapshot_push_clip
 gtk_snapshot_push_rounded_clip
 gtk_snapshot_pop
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 060febb..bc828b0 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -38,6 +38,8 @@
  * @GSK_TRANSFORM_NODE: A node that renders its child after applying a
  *     matrix transform
  * @GSK_OPACITY_NODE: A node that changes the opacity of its child
+ * @GSK_COLOR_MATRIX_NODE: A node that applies a color matrix to every pixel
+ * @GSK_REPEAT_NODE: A node that repeats the child's contents
  * @GSK_CLIP_NODE: A node that clips its child to a rectangular area
  * @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
@@ -62,6 +64,7 @@ typedef enum {
   GSK_TRANSFORM_NODE,
   GSK_OPACITY_NODE,
   GSK_COLOR_MATRIX_NODE,
+  GSK_REPEAT_NODE,
   GSK_CLIP_NODE,
   GSK_ROUNDED_CLIP_NODE,
   GSK_SHADOW_NODE,
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index e4a75ab..f46c283 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -140,6 +140,11 @@ GskRenderNode *         gsk_color_matrix_node_new               (GskRenderNode
                                                                  const graphene_vec4_t    *color_offset);
 
 GDK_AVAILABLE_IN_3_90
+GskRenderNode *         gsk_repeat_node_new                     (const graphene_rect_t    *bounds,
+                                                                 GskRenderNode            *child,
+                                                                 const graphene_rect_t    *child_bounds);
+
+GDK_AVAILABLE_IN_3_90
 GskRenderNode *         gsk_clip_node_new                       (GskRenderNode            *child,
                                                                  const graphene_rect_t    *clip);
 GDK_AVAILABLE_IN_3_90
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 9a4203f..af356d5 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -2581,6 +2581,177 @@ gsk_color_matrix_node_peek_color_offset (GskRenderNode *node)
   return &self->color_offset;
 }
 
+/*** GSK_REPEAT_NODE ***/
+
+typedef struct _GskRepeatNode GskRepeatNode;
+
+struct _GskRepeatNode
+{
+  GskRenderNode render_node;
+
+  GskRenderNode *child;
+  graphene_rect_t child_bounds;
+};
+
+static void
+gsk_repeat_node_finalize (GskRenderNode *node)
+{
+  GskRepeatNode *self = (GskRepeatNode *) node;
+
+  gsk_render_node_unref (self->child);
+}
+
+static void
+gsk_repeat_node_draw (GskRenderNode *node,
+                      cairo_t       *cr)
+{
+  GskRepeatNode *self = (GskRepeatNode *) node;
+  cairo_pattern_t *pattern;
+  cairo_surface_t *surface;
+  cairo_t *surface_cr;
+
+  surface = cairo_surface_create_similar (cairo_get_target (cr),
+                                          CAIRO_CONTENT_COLOR_ALPHA,
+                                          ceilf (self->child_bounds.size.width),
+                                          ceilf (self->child_bounds.size.height));
+  surface_cr = cairo_create (surface);
+  cairo_translate (surface_cr,
+                   - self->child_bounds.origin.x,
+                   - self->child_bounds.origin.y);
+  gsk_render_node_draw (self->child, surface_cr);
+  cairo_destroy (surface_cr);
+
+  pattern = cairo_pattern_create_for_surface (surface);
+  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+  cairo_pattern_set_matrix (pattern,
+                            &(cairo_matrix_t) {
+                                .xx = 1.0,
+                                .yy = 1.0,
+                                .x0 = - self->child_bounds.origin.x,
+                                .y0 = - self->child_bounds.origin.y
+                            });
+
+  cairo_set_source (cr, pattern);
+  cairo_paint (cr);
+
+  cairo_pattern_destroy (pattern);
+  cairo_surface_destroy (surface);
+}
+
+#define GSK_REPEAT_NODE_VARIANT_TYPE "(dddddddduv)"
+
+static GVariant *
+gsk_repeat_node_serialize (GskRenderNode *node)
+{
+  GskRepeatNode *self = (GskRepeatNode *) node;
+
+  return g_variant_new (GSK_REPEAT_NODE_VARIANT_TYPE,
+                        (double) node->bounds.origin.x, (double) node->bounds.origin.y,
+                        (double) node->bounds.size.width, (double) node->bounds.size.height,
+                        (double) self->child_bounds.origin.x, (double) self->child_bounds.origin.y,
+                        (double) self->child_bounds.size.width, (double) self->child_bounds.size.height,
+                        (guint32) gsk_render_node_get_node_type (self->child),
+                        gsk_render_node_serialize_node (self->child));
+}
+
+static GskRenderNode *
+gsk_repeat_node_deserialize (GVariant  *variant,
+                             GError   **error)
+{
+  double x, y, width, height, child_x, child_y, child_width, child_height;
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *result, *child;
+
+  if (!check_variant_type (variant, GSK_REPEAT_NODE_VARIANT_TYPE, error))
+    return NULL;
+
+  g_variant_get (variant, GSK_REPEAT_NODE_VARIANT_TYPE,
+                 &x, &y, &width, &height,
+                 &child_x, &child_y, &child_width, &child_height,
+                 &child_type, &child_variant);
+
+  child = gsk_render_node_deserialize_node (child_type, child_variant, error);
+  g_variant_unref (child_variant);
+
+  if (child == NULL)
+    return NULL;
+
+  result = gsk_repeat_node_new (&GRAPHENE_RECT_INIT (x, y, width, height),
+                                child,
+                                &GRAPHENE_RECT_INIT (child_x, child_y, child_width, child_height));
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
+static const GskRenderNodeClass GSK_REPEAT_NODE_CLASS = {
+  GSK_REPEAT_NODE,
+  sizeof (GskRepeatNode),
+  "GskRepeatNode",
+  gsk_repeat_node_finalize,
+  gsk_repeat_node_draw,
+  gsk_repeat_node_serialize,
+  gsk_repeat_node_deserialize
+};
+
+/**
+ * gsk_repeat_node_new:
+ * @bounds: The bounds of the area to be painted
+ * @child: The child to repeat
+ * @child_bounds: (optional): The area of the child to repeat or %NULL to
+ *     use the child's bounds
+ *
+ * Creates a #GskRenderNode that will repeat the drawing of @child across
+ * the given @bounds.
+ *
+ * Returns: A new #GskRenderNode
+ *
+ * Since: 3.90
+ */
+GskRenderNode *
+gsk_repeat_node_new (const graphene_rect_t *bounds,
+                     GskRenderNode         *child,
+                     const graphene_rect_t *child_bounds)
+{
+  GskRepeatNode *self;
+
+  g_return_val_if_fail (bounds != NULL, NULL);
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
+
+  self = (GskRepeatNode *) gsk_render_node_new (&GSK_REPEAT_NODE_CLASS, 0);
+
+  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
+  self->child = gsk_render_node_ref (child);
+  if (child_bounds)
+    graphene_rect_init_from_rect (&self->child_bounds, child_bounds);
+  else
+    graphene_rect_init_from_rect (&self->child_bounds, &child->bounds);
+
+  return &self->render_node;
+}
+
+GskRenderNode *
+gsk_repeat_node_get_child (GskRenderNode *node)
+{
+  GskRepeatNode *self = (GskRepeatNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_REPEAT_NODE), NULL);
+
+  return self->child;
+}
+
+const graphene_rect_t *
+gsk_repeat_node_peek_child_bounds (GskRenderNode *node)
+{
+  GskRepeatNode *self = (GskRepeatNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_REPEAT_NODE), NULL);
+
+  return &self->child_bounds;
+}
+
 /*** GSK_CLIP_NODE ***/
 
 typedef struct _GskClipNode GskClipNode;
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index 90deb10..938c948 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -50,6 +50,9 @@ GskRenderNode * gsk_color_matrix_node_get_child (GskRenderNode *node);
 const graphene_matrix_t * gsk_color_matrix_node_peek_color_matrix (GskRenderNode *node);
 const graphene_vec4_t * gsk_color_matrix_node_peek_color_offset (GskRenderNode *node);
 
+GskRenderNode * gsk_repeat_node_get_child (GskRenderNode *node);
+const graphene_rect_t * gsk_repeat_node_peek_child_bounds (GskRenderNode *node);
+
 const graphene_point_t * gsk_linear_gradient_node_peek_start (GskRenderNode *node);
 const graphene_point_t * gsk_linear_gradient_node_peek_end (GskRenderNode *node);
 const gsize gsk_linear_gradient_node_get_n_color_stops (GskRenderNode *node);
diff --git a/gtk/gtkrenderbackground.c b/gtk/gtkrenderbackground.c
index 051a9fd..67e8d4f 100644
--- a/gtk/gtkrenderbackground.c
+++ b/gtk/gtkrenderbackground.c
@@ -407,14 +407,9 @@ gtk_theming_background_snapshot_layer (GtkThemingBackground *bg,
     }
   else
     {
-      int surface_width, surface_height;
-      cairo_rectangle_t fill_rect;
-      cairo_surface_t *surface;
-      cairo_t *cr, *cr2;
-
-      cr = gtk_snapshot_append_cairo_node (snapshot,
-                                           &GRAPHENE_RECT_INIT (0, 0, width, height),
-                                           "BackgroundLayer<%u>", idx);
+      float repeat_width, repeat_height;
+      float position_x, position_y;
+      graphene_rect_t fill_rect;
 
       /* If ‘background-repeat’ is ‘round’ for one (or both) dimensions,
        * there is a second step. The UA must scale the image in that
@@ -460,62 +455,58 @@ gtk_theming_background_snapshot_layer (GtkThemingBackground *bg,
       if (hrepeat == GTK_CSS_REPEAT_STYLE_SPACE)
         {
           double n = floor (width / image_width);
-          surface_width = n ? round (width / n) : 0;
+          repeat_width = n ? round (width / n) : 0;
         }
       else
-        surface_width = round (image_width);
+        repeat_width = round (image_width);
 
       if (vrepeat == GTK_CSS_REPEAT_STYLE_SPACE)
         {
           double n = floor (height / image_height);
-          surface_height = n ? round (height / n) : 0;
+          repeat_height = n ? round (height / n) : 0;
         }
       else
-        surface_height = round (image_height);
-
-      surface = cairo_surface_create_similar (cairo_get_target (cr),
-                                              CAIRO_CONTENT_COLOR_ALPHA,
-                                              surface_width, surface_height);
-      cr2 = cairo_create (surface);
-      cairo_translate (cr2,
-                       0.5 * (surface_width - image_width),
-                       0.5 * (surface_height - image_height));
-      _gtk_css_image_draw (image, cr2, image_width, image_height);
-      cairo_destroy (cr2);
-
-      cairo_set_source_surface (cr, surface,
-                                _gtk_css_position_value_get_x (pos, width - image_width),
-                                _gtk_css_position_value_get_y (pos, height - image_height));
-      cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
-      cairo_surface_destroy (surface);
+        repeat_height = round (image_height);
 
       if (hrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT)
         {
-          fill_rect.x = _gtk_css_position_value_get_x (pos, width - image_width);
-          fill_rect.width = image_width;
+          fill_rect.origin.x = _gtk_css_position_value_get_x (pos, width - image_width);
+          fill_rect.size.width = image_width;
         }
       else
         {
-          fill_rect.x = 0;
-          fill_rect.width = width;
+          fill_rect.origin.x = 0;
+          fill_rect.size.width = width;
         }
 
       if (vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT)
         {
-          fill_rect.y = _gtk_css_position_value_get_y (pos, height - image_height);
-          fill_rect.height = image_height;
+          fill_rect.origin.y = _gtk_css_position_value_get_y (pos, height - image_height);
+          fill_rect.size.height = image_height;
         }
       else
         {
-          fill_rect.y = 0;
-          fill_rect.height = height;
+          fill_rect.origin.y = 0;
+          fill_rect.size.height = height;
         }
 
-      cairo_rectangle (cr, fill_rect.x, fill_rect.y,
-                       fill_rect.width, fill_rect.height);
-      cairo_fill (cr);
+      position_x = _gtk_css_position_value_get_x (pos, width - image_width);
+      position_y = _gtk_css_position_value_get_y (pos, height - image_height);
+
+      gtk_snapshot_push_repeat (snapshot,
+                                &fill_rect,
+                                &GRAPHENE_RECT_INIT (
+                                    position_x, position_y,
+                                    repeat_width, repeat_height
+                                ),
+                                "BackgroundLayerRepeat<%u>", idx);
+                                
+      gtk_snapshot_translate_2d (snapshot,
+                                 position_x + 0.5 * (repeat_width - image_width),
+                                 position_y + 0.5 * (repeat_height - image_height));
+      gtk_css_image_snapshot (image, snapshot, image_width, image_height);
 
-      cairo_destroy (cr);
+      gtk_snapshot_pop_and_append (snapshot);
     }
 
   gtk_snapshot_translate_2d (snapshot, - origin->bounds.origin.x, - origin->bounds.origin.y);
diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c
index a7bfa7d..76adc59 100644
--- a/gtk/gtksnapshot.c
+++ b/gtk/gtksnapshot.c
@@ -401,6 +401,76 @@ rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
 }
 
 static GskRenderNode *
+gtk_snapshot_collect_repeat (GskRenderNode **nodes,
+                             guint           n_nodes,
+                             const char     *name,
+                             gpointer        data)
+{
+  GskRenderNode *node, *repeat_node;
+  graphene_rect_t *bounds = data;
+
+  node = gtk_snapshot_collect_default (nodes, n_nodes, name, NULL);
+  if (node == NULL)
+    return NULL;
+
+  repeat_node = gsk_repeat_node_new (&bounds[0],
+                                     node,
+                                     bounds[1].size.width > 0 ? &bounds[1] : NULL);
+  gsk_render_node_set_name (repeat_node, name);
+
+  gsk_render_node_unref (node);
+  g_free (data);
+
+  return repeat_node;
+}
+
+void
+gtk_snapshot_push_repeat (GtkSnapshot           *snapshot,
+                          const graphene_rect_t *bounds,
+                          const graphene_rect_t *child_bounds,
+                          const char            *name,
+                          ...)
+{
+  cairo_region_t *clip;
+  graphene_rect_t *data;
+  char *str;
+
+  if (name)
+    {
+      va_list args;
+
+      va_start (args, name);
+      str = g_strdup_vprintf (name, args);
+      va_end (args);
+    }
+  else
+    str = NULL;
+
+  data = g_new0 (graphene_rect_t, 2);
+  graphene_rect_offset_r (bounds, snapshot->state->translate_x, snapshot->state->translate_y, &data[0]);
+  if (child_bounds)
+    {
+      cairo_rectangle_int_t rect;
+      graphene_rect_offset_r (child_bounds, snapshot->state->translate_x, snapshot->state->translate_y, 
&data[1]);
+      rectangle_init_from_graphene (&rect, &data[1]);
+      clip = cairo_region_create_rectangle (&rect);
+    }
+  else
+    clip = NULL;
+
+  snapshot->state = gtk_snapshot_state_new (snapshot->state,
+                                            str,
+                                            clip,
+                                            snapshot->state->translate_x,
+                                            snapshot->state->translate_y,
+                                            gtk_snapshot_collect_repeat,
+                                            data);
+
+  if (clip)
+    cairo_region_destroy (clip);
+}
+
+static GskRenderNode *
 gtk_snapshot_collect_clip (GskRenderNode **nodes,
                            guint           n_nodes,
                            const char     *name,
diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h
index e7b35b8..f03cef7 100644
--- a/gtk/gtksnapshot.h
+++ b/gtk/gtksnapshot.h
@@ -58,6 +58,12 @@ void            gtk_snapshot_push_color_matrix          (GtkSnapshot
                                                          const char             *name,
                                                          ...) G_GNUC_PRINTF (4, 5);
 GDK_AVAILABLE_IN_3_90
+void            gtk_snapshot_push_repeat                (GtkSnapshot            *snapshot,
+                                                         const graphene_rect_t  *bounds,
+                                                         const graphene_rect_t  *child_bounds,
+                                                         const char             *name,
+                                                         ...) G_GNUC_PRINTF (4, 5);
+GDK_AVAILABLE_IN_3_90
 void            gtk_snapshot_push_clip                  (GtkSnapshot            *snapshot,
                                                          const graphene_rect_t  *bounds,
                                                          const char             *name,
diff --git a/gtk/inspector/gtktreemodelrendernode.c b/gtk/inspector/gtktreemodelrendernode.c
index ddf6d21..63e3001 100644
--- a/gtk/inspector/gtktreemodelrendernode.c
+++ b/gtk/inspector/gtktreemodelrendernode.c
@@ -545,6 +545,10 @@ append_node (GtkTreeModelRenderNode *nodemodel,
       append_node (nodemodel, gsk_color_matrix_node_get_child (node), priv->nodes->len - 1);
       break;
 
+    case GSK_REPEAT_NODE:
+      append_node (nodemodel, gsk_repeat_node_get_child (node), priv->nodes->len - 1);
+      break;
+
     case GSK_CLIP_NODE:
       append_node (nodemodel, gsk_clip_node_get_child (node), priv->nodes->len - 1);
       break;
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index f6b1fc6..48b11e6 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -164,6 +164,8 @@ node_type_name (GskRenderNodeType type)
       return "Opacity";
     case GSK_COLOR_MATRIX_NODE:
       return "Color Matrix";
+    case GSK_REPEAT_NODE:
+      return "Repeat";
     case GSK_CLIP_NODE:
       return "Clip";
     case GSK_ROUNDED_CLIP_NODE:


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