[gtk/wip/baedert/radial-gradient: 1/13] gsk: Add a radial gradient node




commit 0c6226c20b2b3dcf3bc77595edcd28b0d4626dfb
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Sep 11 22:55:37 2020 -0400

    gsk: Add a radial gradient node
    
    Only a fallback implementation for now.
    
    Fixes #2262

 gsk/gl/gskglrenderer.c           |   2 +
 gsk/gskenums.h                   |   4 +
 gsk/gskrendernode.h              |  43 ++++++
 gsk/gskrendernodeimpl.c          | 312 +++++++++++++++++++++++++++++++++++++++
 gsk/gskrendernodeparser.c        |  36 +++++
 gsk/vulkan/gskvulkanrenderpass.c |   2 +
 gtk/inspector/recorder.c         |  13 ++
 7 files changed, 412 insertions(+)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index edc2fd2618..2af766c99b 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -3384,6 +3384,8 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
     break;
 
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+    case GSK_RADIAL_GRADIENT_NODE:
+    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
     case GSK_CAIRO_NODE:
     default:
       {
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index d7607fbcb1..24aafec502 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -30,6 +30,8 @@
  * @GSK_COLOR_NODE: A node drawing a single color rectangle
  * @GSK_LINEAR_GRADIENT_NODE: A node drawing a linear gradient
  * @GSK_REPEATING_LINEAR_GRADIENT_NODE: A node drawing a repeating linear gradient
+ * @GSK_RADIAL_GRADIENT_NODE: A node drawing a radial gradient
+ * @GSK_REPEATING_RADIAL_GRADIENT_NODE: A node drawing a repeating radial gradient
  * @GSK_BORDER_NODE: A node stroking a border around an area
  * @GSK_TEXTURE_NODE: A node drawing a #GdkTexture
  * @GSK_INSET_SHADOW_NODE: A node drawing an inset shadow
@@ -56,6 +58,8 @@ typedef enum {
   GSK_COLOR_NODE,
   GSK_LINEAR_GRADIENT_NODE,
   GSK_REPEATING_LINEAR_GRADIENT_NODE,
+  GSK_RADIAL_GRADIENT_NODE,
+  GSK_REPEATING_RADIAL_GRADIENT_NODE,
   GSK_BORDER_NODE,
   GSK_TEXTURE_NODE,
   GSK_INSET_SHADOW_NODE,
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 331be3230b..613a2d7370 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -104,6 +104,8 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 #define GSK_TYPE_TEXTURE_NODE                   (gsk_texture_node_get_type())
 #define GSK_TYPE_LINEAR_GRADIENT_NODE           (gsk_linear_gradient_node_get_type())
 #define GSK_TYPE_REPEATING_LINEAR_GRADIENT_NODE (gsk_repeating_linear_gradient_node_get_type())
+#define GSK_TYPE_RADIAL_GRADIENT_NODE           (gsk_radial_gradient_node_get_type())
+#define GSK_TYPE_REPEATING_RADIAL_GRADIENT_NODE (gsk_repeating_radial_gradient_node_get_type())
 #define GSK_TYPE_BORDER_NODE                    (gsk_border_node_get_type())
 #define GSK_TYPE_INSET_SHADOW_NODE              (gsk_inset_shadow_node_get_type())
 #define GSK_TYPE_OUTSET_SHADOW_NODE             (gsk_outset_shadow_node_get_type())
@@ -126,6 +128,8 @@ typedef struct _GskColorNode                    GskColorNode;
 typedef struct _GskTextureNode                  GskTextureNode;
 typedef struct _GskLinearGradientNode           GskLinearGradientNode;
 typedef struct _GskRepeatingLinearGradientNode  GskRepeatingLinearGradientNode;
+typedef struct _GskRadialGradientNode           GskRadialGradientNode;
+typedef struct _GskRepeatingRadialGradientNode  GskRepeatingRadialGradientNode;
 typedef struct _GskBorderNode                   GskBorderNode;
 typedef struct _GskInsetShadowNode              GskInsetShadowNode;
 typedef struct _GskOutsetShadowNode             GskOutsetShadowNode;
@@ -196,6 +200,45 @@ GskRenderNode *         gsk_repeating_linear_gradient_node_new      (const graph
                                                                      const GskColorStop       *color_stops,
                                                                      gsize                     
n_color_stops);
 
+GDK_AVAILABLE_IN_ALL
+GType                   gsk_radial_gradient_node_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_radial_gradient_node_new      (const graphene_rect_t    *bounds,
+                                                           const graphene_point_t   *center,
+                                                           float                     radius,
+                                                           float                     scale,
+                                                           float                     start,
+                                                           float                     end,
+                                                           const GskColorStop       *color_stops,
+                                                           gsize                     n_color_stops);
+GDK_AVAILABLE_IN_ALL
+gsize                   gsk_radial_gradient_node_get_n_color_stops (GskRenderNode *node);
+GDK_AVAILABLE_IN_ALL
+const GskColorStop *    gsk_radial_gradient_node_peek_color_stops  (GskRenderNode *node,
+                                                                    gsize         *n_stops);
+GDK_AVAILABLE_IN_ALL
+const graphene_point_t *gsk_radial_gradient_node_peek_center       (GskRenderNode *node);
+GDK_AVAILABLE_IN_ALL
+float                   gsk_radial_gradient_node_get_radius        (GskRenderNode *node);
+GDK_AVAILABLE_IN_ALL
+float                   gsk_radial_gradient_node_get_scale         (GskRenderNode *node);
+GDK_AVAILABLE_IN_ALL
+float                   gsk_radial_gradient_node_get_start         (GskRenderNode *node);
+GDK_AVAILABLE_IN_ALL
+float                   gsk_radial_gradient_node_get_end           (GskRenderNode *node);
+
+GDK_AVAILABLE_IN_ALL
+GType                   gsk_repeating_radial_gradient_node_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_repeating_radial_gradient_node_new      (const graphene_rect_t    *bounds,
+                                                                     const graphene_point_t   *center,
+                                                                     float                     radius,
+                                                                     float                     scale,
+                                                                     float                     start,
+                                                                     float                     end,
+                                                                     const GskColorStop       *color_stops,
+                                                                     gsize                     
n_color_stops);
+
 GDK_AVAILABLE_IN_ALL
 GType                   gsk_border_node_get_type                (void) G_GNUC_CONST;
 GDK_AVAILABLE_IN_ALL
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index c89da757ad..271f8b214d 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -386,6 +386,284 @@ gsk_linear_gradient_node_peek_color_stops (GskRenderNode *node,
   return self->stops;
 }
 
+/*** GSK_RADIAL_GRADIENT_NODE ***/
+
+struct _GskRadialGradientNode
+{
+  GskRenderNode render_node;
+
+  graphene_point_t center;
+
+  gboolean circle;
+
+  float radius;
+  float scale;
+  float start;
+  float end;
+
+  gsize n_stops;
+  GskColorStop *stops;
+};
+
+static void
+gsk_radial_gradient_node_finalize (GskRenderNode *node)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_RADIAL_GRADIENT_NODE));
+
+  g_free (self->stops);
+
+  parent_class->finalize (node);
+}
+
+static void
+gsk_radial_gradient_node_draw (GskRenderNode *node,
+                               cairo_t       *cr)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+  cairo_pattern_t *pattern;
+  cairo_matrix_t matrix;
+  gsize i;
+  float offset;
+  int last;
+
+  pattern = cairo_pattern_create_radial (0, 0, self->radius * self->start,
+                                         0, 0, self->radius * self->end);
+
+  if (self->scale != 1.0)
+    {
+      cairo_matrix_init_scale (&matrix, 1.0, 1.0 / self->scale);
+      cairo_pattern_set_matrix (pattern, &matrix);
+    }
+
+  if (gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE)
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+  else
+    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+
+  offset = self->start;
+  last = -1;
+
+  for (i = 0; i < self->n_stops; i++)
+    {
+      double pos, step;
+
+      if (self->stops[i].offset == 0.0)
+        {
+          if (i == 0)
+            pos = 0.0;
+          else if (i + 1 == self->n_stops)
+            pos = 1.0;
+          else
+            continue;
+        }
+      else
+        pos = self->stops[i].offset;
+
+      pos = MAX (pos, 0);
+      step = (pos - offset) / (i - last);
+      for (last = last + 1; last <= i; last++)
+        {
+          offset += step;
+
+          cairo_pattern_add_color_stop_rgba (pattern,
+                                             (offset - self->start) / (self->end - self->start),
+                                             self->stops[i].color.red,
+                                             self->stops[i].color.green,
+                                             self->stops[i].color.blue,
+                                             self->stops[i].color.alpha);
+        }
+
+      offset = pos;
+      last = i;
+    }
+
+  gsk_cairo_rectangle (cr, &node->bounds);
+  cairo_translate (cr, self->center.x, self->center.y);
+  cairo_set_source (cr, pattern);
+  cairo_fill (cr);
+
+  cairo_pattern_destroy (pattern);
+}
+
+static void
+gsk_radial_gradient_node_diff (GskRenderNode  *node1,
+                               GskRenderNode  *node2,
+                               cairo_region_t *region)
+{
+  GskRadialGradientNode *self1 = (GskRadialGradientNode *) node1;
+  GskRadialGradientNode *self2 = (GskRadialGradientNode *) node2;
+
+  if (graphene_point_equal (&self1->center, &self2->center) &&
+      self1->radius == self2->radius &&
+      self1->scale == self2->scale &&
+      self1->start == self2->start &&
+      self1->end == self2->end &&
+      self1->n_stops == self2->n_stops)
+    {
+      gsize i;
+
+      for (i = 0; i < self1->n_stops; i++)
+        {
+          GskColorStop *stop1 = &self1->stops[i];
+          GskColorStop *stop2 = &self2->stops[i];
+
+          if (stop1->offset == stop2->offset &&
+              gdk_rgba_equal (&stop1->color, &stop2->color))
+            continue;
+
+          gsk_render_node_diff_impossible (node1, node2, region);
+          return;
+        }
+
+      return;
+    }
+
+  gsk_render_node_diff_impossible (node1, node2, region);
+}
+
+GskRenderNode *
+gsk_radial_gradient_node_new (const graphene_rect_t  *bounds,
+                              const graphene_point_t *center,
+                              float                   radius,
+                              float                   scale,
+                              float                   start,
+                              float                   end,
+                              const GskColorStop     *color_stops,
+                              gsize                   n_color_stops)
+{
+  GskRadialGradientNode *self;
+  GskRenderNode *node;
+  gsize i;
+
+  g_return_val_if_fail (bounds != NULL, NULL);
+  g_return_val_if_fail (center != NULL, NULL);
+  g_return_val_if_fail (color_stops != NULL, NULL);
+  g_return_val_if_fail (n_color_stops >= 2, NULL);
+  g_return_val_if_fail (color_stops[0].offset >= 0, NULL);
+  for (i = 1; i < n_color_stops; i++)
+    g_return_val_if_fail (color_stops[i].offset >= color_stops[i - 1].offset, NULL);
+  g_return_val_if_fail (color_stops[n_color_stops - 1].offset <= 1, NULL);
+
+  self = gsk_render_node_alloc (GSK_RADIAL_GRADIENT_NODE);
+  node = (GskRenderNode *) self;
+
+  graphene_rect_init_from_rect (&node->bounds, bounds);
+  graphene_point_init_from_point (&self->center, center);
+
+  self->radius = radius;
+  self->scale = scale;
+  self->start = start;
+  self->end = end;
+
+  self->n_stops = n_color_stops;
+  self->stops = g_malloc_n (n_color_stops, sizeof (GskColorStop));
+  memcpy (self->stops, color_stops, n_color_stops * sizeof (GskColorStop));
+
+  return node;
+}
+
+GskRenderNode *
+gsk_repeating_radial_gradient_node_new (const graphene_rect_t  *bounds,
+                                        const graphene_point_t *center,
+                                        float                   radius,
+                                        float                   scale,
+                                        float                   start,
+                                        float                   end,
+                                        const GskColorStop     *color_stops,
+                                        gsize                   n_color_stops)
+{
+  GskRadialGradientNode *self;
+  GskRenderNode *node;
+  gsize i;
+
+  g_return_val_if_fail (bounds != NULL, NULL);
+  g_return_val_if_fail (center != NULL, NULL);
+  g_return_val_if_fail (color_stops != NULL, NULL);
+  g_return_val_if_fail (n_color_stops >= 2, NULL);
+  g_return_val_if_fail (color_stops[0].offset >= 0, NULL);
+  for (i = 1; i < n_color_stops; i++)
+    g_return_val_if_fail (color_stops[i].offset >= color_stops[i - 1].offset, NULL);
+  g_return_val_if_fail (color_stops[n_color_stops - 1].offset <= 1, NULL);
+
+  self = gsk_render_node_alloc (GSK_REPEATING_RADIAL_GRADIENT_NODE);
+  node = (GskRenderNode *) self;
+
+  graphene_rect_init_from_rect (&node->bounds, bounds);
+  graphene_point_init_from_point (&self->center, center);
+
+  self->radius = radius;
+  self->scale = scale;
+  self->start = start;
+  self->end = end;
+
+  self->n_stops = n_color_stops;
+  self->stops = g_malloc_n (n_color_stops, sizeof (GskColorStop));
+  memcpy (self->stops, color_stops, n_color_stops * sizeof (GskColorStop));
+
+  return node;
+}
+
+gsize
+gsk_radial_gradient_node_get_n_color_stops (GskRenderNode *node)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+
+  return self->n_stops;
+}
+
+const GskColorStop *
+gsk_radial_gradient_node_peek_color_stops (GskRenderNode *node,
+                                           gsize         *n_stops)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+
+  if (n_stops != NULL)
+    *n_stops = self->n_stops;
+
+  return self->stops;
+}
+
+const graphene_point_t *
+gsk_radial_gradient_node_peek_center (GskRenderNode *node)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+
+  return &self->center;
+}
+
+float
+gsk_radial_gradient_node_get_radius (GskRenderNode *node)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+
+  return self->radius;
+}
+
+float
+gsk_radial_gradient_node_get_scale (GskRenderNode *node)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+
+  return self->scale;
+}
+
+float
+gsk_radial_gradient_node_get_start (GskRenderNode *node)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+
+  return self->start;
+}
+
+float
+gsk_radial_gradient_node_get_end (GskRenderNode *node)
+{
+  GskRadialGradientNode *self = (GskRadialGradientNode *) node;
+
+  return self->end;
+}
+
 /*** GSK_BORDER_NODE ***/
 
 struct _GskBorderNode
@@ -4191,6 +4469,8 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_cairo_node, GSK_CAIRO_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_color_node, GSK_COLOR_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_linear_gradient_node, GSK_LINEAR_GRADIENT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_linear_gradient_node, GSK_REPEATING_LINEAR_GRADIENT_NODE)
+GSK_DEFINE_RENDER_NODE_TYPE (gsk_radial_gradient_node, GSK_RADIAL_GRADIENT_NODE)
+GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_radial_gradient_node, GSK_REPEATING_RADIAL_GRADIENT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_border_node, GSK_BORDER_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_texture_node, GSK_TEXTURE_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_inset_shadow_node, GSK_INSET_SHADOW_NODE)
@@ -4291,6 +4571,38 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_REPEATING_LINEAR_GRADIENT_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_RADIAL_GRADIENT_NODE,
+      sizeof (GskRadialGradientNode),
+      NULL,
+      gsk_radial_gradient_node_finalize,
+      gsk_radial_gradient_node_draw,
+      NULL,
+      gsk_radial_gradient_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskRadialGradientNode"), &node_info);
+    gsk_render_node_types[GSK_RADIAL_GRADIENT_NODE] = node_type;
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_REPEATING_RADIAL_GRADIENT_NODE,
+      sizeof (GskRadialGradientNode),
+      NULL,
+      gsk_radial_gradient_node_finalize,
+      gsk_radial_gradient_node_draw,
+      NULL,
+      gsk_radial_gradient_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskRepeatingRadialGradientNode"), 
&node_info);
+    gsk_render_node_types[GSK_REPEATING_RADIAL_GRADIENT_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 87bfca110c..4b31493fc3 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -2003,6 +2003,42 @@ render_node_print (Printer       *p,
       }
       break;
 
+    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_RADIAL_GRADIENT_NODE:
+      {
+        const gsize n_stops = gsk_radial_gradient_node_get_n_color_stops (node);
+        const GskColorStop *stops = gsk_radial_gradient_node_peek_color_stops (node, NULL);
+        gsize i;
+
+        if (gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE)
+          start_node (p, "repeating-radial-gradient");
+        else
+          start_node (p, "radial-gradient");
+
+        append_rect_param (p, "bounds", &node->bounds);
+        append_point_param (p, "center", gsk_radial_gradient_node_peek_center (node));
+        append_float_param (p, "radius", gsk_radial_gradient_node_get_radius (node), 0.0f);
+        append_float_param (p, "scale", gsk_radial_gradient_node_get_scale (node), 1.0f);
+        append_float_param (p, "start", gsk_radial_gradient_node_get_start (node), 0.0f);
+        append_float_param (p, "end", gsk_radial_gradient_node_get_end (node), 1.0f);
+
+        _indent (p);
+        g_string_append (p->str, "stops: ");
+        for (i = 0; i < n_stops; i ++)
+          {
+            if (i > 0)
+              g_string_append (p->str, ", ");
+
+            string_append_double (p->str, stops[i].offset);
+            g_string_append_c (p->str, ' ');
+            append_rgba (p->str, &stops[i].color);
+          }
+        g_string_append (p->str, ";\n");
+
+        end_node (p);
+      }
+      break;
+
     case GSK_OPACITY_NODE:
       {
         start_node (p, "opacity");
diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c
index 51ed812ce7..2ea547881c 100644
--- a/gsk/vulkan/gskvulkanrenderpass.c
+++ b/gsk/vulkan/gskvulkanrenderpass.c
@@ -257,6 +257,8 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
       g_assert_not_reached ();
       return;
     case GSK_SHADOW_NODE:
+    case GSK_RADIAL_GRADIENT_NODE:
+    case GSK_REPEATING_RADIAL_GRADIENT_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 e48200a909..043d407dbb 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -131,6 +131,8 @@ create_list_model_for_render_node (GskRenderNode *node)
     case GSK_COLOR_NODE:
     case GSK_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+    case GSK_RADIAL_GRADIENT_NODE:
+    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
     case GSK_BORDER_NODE:
     case GSK_INSET_SHADOW_NODE:
     case GSK_OUTSET_SHADOW_NODE:
@@ -234,6 +236,10 @@ node_type_name (GskRenderNodeType type)
       return "Linear Gradient";
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
       return "Repeating Linear Gradient";
+    case GSK_RADIAL_GRADIENT_NODE:
+      return "Radial Gradient";
+    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+      return "Repeating Radial Gradient";
     case GSK_BORDER_NODE:
       return "Border";
     case GSK_TEXTURE_NODE:
@@ -279,6 +285,8 @@ node_name (GskRenderNode *node)
     case GSK_CAIRO_NODE:
     case GSK_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+    case GSK_RADIAL_GRADIENT_NODE:
+    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
     case GSK_BORDER_NODE:
     case GSK_INSET_SHADOW_NODE:
     case GSK_OUTSET_SHADOW_NODE:
@@ -625,6 +633,11 @@ populate_render_node_properties (GtkListStore  *store,
       }
       break;
 
+    case GSK_RADIAL_GRADIENT_NODE:
+    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+      /* TODO */
+      break;
+
     case GSK_TEXT_NODE:
       {
         const PangoFont *font = gsk_text_node_peek_font (node);


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