[gtk/wip/otte/conic: 2/4] gsk: Add GskConicGradientNode




commit 55a242bd816893e8b3c777b053a3c5fc0266b5d6
Author: Benjamin Otte <otte redhat com>
Date:   Thu Dec 3 00:47:54 2020 +0100

    gsk: Add GskConicGradientNode

 docs/reference/gsk/gsk4-sections.txt |   5 +
 gsk/broadway/gskbroadwayrenderer.c   |   2 +
 gsk/gl/gskglrenderer.c               |   1 +
 gsk/gskenums.h                       |   2 +
 gsk/gskrendernode.h                  |  20 +++
 gsk/gskrendernodeimpl.c              | 338 +++++++++++++++++++++++++++++++++++
 gsk/gskrendernodeparser.c            |  50 ++++++
 gsk/vulkan/gskvulkanrenderpass.c     |   1 +
 gtk/inspector/recorder.c             |  42 +++++
 9 files changed, 461 insertions(+)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index f671732880..6cd1fa0d17 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -83,6 +83,11 @@ gsk_radial_gradient_node_get_hradius
 gsk_radial_gradient_node_get_vradius
 gsk_radial_gradient_node_get_center
 gsk_repeating_radial_gradient_node_new
+gsk_conic_gradient_node_new
+gsk_conic_gradient_node_get_n_color_stops
+gsk_conic_gradient_node_get_color_stops
+gsk_conic_gradient_node_get_center
+gsk_conic_gradient_node_get_rotation
 gsk_border_node_new
 gsk_border_node_get_outline
 gsk_border_node_get_widths
diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c
index 80a3f84968..5ac62e7ee0 100644
--- a/gsk/broadway/gskbroadwayrenderer.c
+++ b/gsk/broadway/gskbroadwayrenderer.c
@@ -264,6 +264,7 @@ collect_reused_child_nodes (GskRenderer *renderer,
     case GSK_RADIAL_GRADIENT_NODE:
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_CONIC_GRADIENT_NODE:
     case GSK_REPEAT_NODE:
     case GSK_BLEND_NODE:
     case GSK_CROSS_FADE_NODE:
@@ -848,6 +849,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
     case GSK_RADIAL_GRADIENT_NODE:
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_CONIC_GRADIENT_NODE:
     case GSK_REPEAT_NODE:
     case GSK_BLEND_NODE:
     case GSK_CROSS_FADE_NODE:
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index cd93966d5c..bb09be8bdd 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_CONIC_GRADIENT_NODE:
     case GSK_CAIRO_NODE:
     default:
       {
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 0cd0c284fc..243ab67cc8 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -32,6 +32,7 @@
  * @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_CONIC_GRADIENT_NODE: A node drawing a conic 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
@@ -61,6 +62,7 @@ typedef enum {
   GSK_REPEATING_LINEAR_GRADIENT_NODE,
   GSK_RADIAL_GRADIENT_NODE,
   GSK_REPEATING_RADIAL_GRADIENT_NODE,
+  GSK_CONIC_GRADIENT_NODE,
   GSK_BORDER_NODE,
   GSK_TEXTURE_NODE,
   GSK_INSET_SHADOW_NODE,
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index dba436afda..76771a9d8e 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -120,6 +120,7 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 #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_CONIC_GRADIENT_NODE            (gsk_conic_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())
@@ -145,6 +146,7 @@ typedef struct _GskLinearGradientNode           GskLinearGradientNode;
 typedef struct _GskRepeatingLinearGradientNode  GskRepeatingLinearGradientNode;
 typedef struct _GskRadialGradientNode           GskRadialGradientNode;
 typedef struct _GskRepeatingRadialGradientNode  GskRepeatingRadialGradientNode;
+typedef struct _GskConicGradientNode            GskConicGradientNode;
 typedef struct _GskBorderNode                   GskBorderNode;
 typedef struct _GskInsetShadowNode              GskInsetShadowNode;
 typedef struct _GskOutsetShadowNode             GskOutsetShadowNode;
@@ -216,6 +218,24 @@ GskRenderNode *         gsk_repeating_linear_gradient_node_new      (const graph
                                                                      const GskColorStop       *color_stops,
                                                                      gsize                     
n_color_stops);
 
+GDK_AVAILABLE_IN_ALL
+GType                   gsk_conic_gradient_node_get_type            (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_conic_gradient_node_new                 (const graphene_rect_t    *bounds,
+                                                                     const graphene_point_t   *center,
+                                                                     float                     rotation,
+                                                                     const GskColorStop       *color_stops,
+                                                                     gsize                     
n_color_stops);
+GDK_AVAILABLE_IN_ALL
+const graphene_point_t * gsk_conic_gradient_node_get_center         (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+float                    gsk_conic_gradient_node_get_rotation       (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+gsize                    gsk_conic_gradient_node_get_n_color_stops  (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+const GskColorStop *     gsk_conic_gradient_node_get_color_stops    (GskRenderNode            *node,
+                                                                     gsize                    *n_stops);
+
 GDK_AVAILABLE_IN_ALL
 GType                   gsk_radial_gradient_node_get_type (void) G_GNUC_CONST;
 GDK_AVAILABLE_IN_ALL
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index e5228d88c9..44fb918ddd 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -741,6 +741,327 @@ gsk_radial_gradient_node_get_end (GskRenderNode *node)
   return self->end;
 }
 
+/*** GSK_CONIC_GRADIENT_NODE ***/
+
+struct _GskConicGradientNode
+{
+  GskRenderNode render_node;
+
+  graphene_point_t center;
+  float rotation;
+
+  gsize n_stops;
+  GskColorStop *stops;
+};
+
+static void
+gsk_conic_gradient_node_finalize (GskRenderNode *node)
+{
+  GskConicGradientNode *self = (GskConicGradientNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_CONIC_GRADIENT_NODE));
+
+  g_free (self->stops);
+
+  parent_class->finalize (node);
+}
+
+#define DEG_TO_RAD(x)          ((x) * (G_PI / 180.f))
+
+static void
+_cairo_mesh_pattern_set_corner_rgba (cairo_pattern_t *pattern,
+                                     guint            corner_num,
+                                     const GdkRGBA   *rgba)
+{
+  cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, rgba->red, rgba->green, rgba->blue, 
rgba->alpha);
+}
+
+static void
+project (double  angle,
+         double  radius,
+         double *x_out,
+         double *y_out)
+{
+  double x, y;
+
+  x = radius * cos (angle);
+  y = radius * sin (angle);
+  if (copysign (x, 1.0) > copysign (y, 1.0))
+    {
+      *x_out = copysign (radius, x);
+      *y_out = y * radius / copysign (x, 1.0);
+    }
+  else
+    {
+      *x_out = x * radius / copysign (y, 1.0);
+      *y_out = copysign (radius, y);
+    }
+}
+
+static void
+gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern, 
+                                   float            radius,
+                                   float            start_angle,
+                                   const GdkRGBA   *start_color,
+                                   float            end_angle,
+                                   const GdkRGBA   *end_color)
+{
+  double x, y;
+
+  cairo_mesh_pattern_begin_patch (pattern);
+
+  cairo_mesh_pattern_move_to  (pattern, 0, 0);
+  project (start_angle, radius, &x, &y);
+  cairo_mesh_pattern_line_to  (pattern, x, y);
+  project (end_angle, radius, &x, &y);
+  cairo_mesh_pattern_line_to  (pattern, x, y);
+  cairo_mesh_pattern_line_to  (pattern, 0, 0);
+
+  _cairo_mesh_pattern_set_corner_rgba (pattern, 0, start_color);
+  _cairo_mesh_pattern_set_corner_rgba (pattern, 1, start_color);
+  _cairo_mesh_pattern_set_corner_rgba (pattern, 2, end_color);
+  _cairo_mesh_pattern_set_corner_rgba (pattern, 3, end_color);
+
+  cairo_mesh_pattern_end_patch (pattern);
+}
+
+static void
+gdk_rgba_color_interpolate (GdkRGBA       *dest,
+                            const GdkRGBA *src1,
+                            const GdkRGBA *src2,
+                            double         progress)
+{
+  double alpha = src1->alpha * (1.0 - progress) + src2->alpha * progress;
+
+  dest->alpha = alpha;
+  if (alpha == 0)
+    {
+      dest->red = src1->red * (1.0 - progress) + src2->red * progress;
+      dest->green = src1->green * (1.0 - progress) + src2->green * progress;
+      dest->blue = src1->blue * (1.0 - progress) + src2->blue * progress;
+    }
+  else
+    {
+      dest->red = (src1->red * src1->alpha * (1.0 - progress) + src2->red * src2->alpha * progress) / alpha;
+      dest->green = (src1->green * src1->alpha * (1.0 - progress) + src2->green * src2->alpha * progress) / 
alpha;
+      dest->blue = (src1->blue * src1->alpha * (1.0 - progress) + src2->blue * src2->alpha * progress) / 
alpha;
+    }
+}
+
+static void
+gsk_conic_gradient_node_draw (GskRenderNode *node,
+                              cairo_t       *cr)
+{
+  GskConicGradientNode *self = (GskConicGradientNode *) node;
+  cairo_pattern_t *pattern;
+  graphene_point_t corner;
+  float radius;
+  gsize i;
+
+  pattern = cairo_pattern_create_mesh ();
+  graphene_rect_get_top_right (&node->bounds, &corner);
+  radius = graphene_point_distance (&self->center, &corner, NULL, NULL);
+  graphene_rect_get_bottom_right (&node->bounds, &corner);
+  radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
+  graphene_rect_get_bottom_left (&node->bounds, &corner);
+  radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
+  graphene_rect_get_top_left (&node->bounds, &corner);
+  radius = MAX (radius, graphene_point_distance (&self->center, &corner, NULL, NULL));
+
+  for (i = 0; i <= self->n_stops; i++)
+    {
+      GskColorStop *stop1 = &self->stops[MAX (i, 1) - 1];
+      GskColorStop *stop2 = &self->stops[MIN (i, self->n_stops - 1)];
+      double offset1 = i > 0 ? stop1->offset : 0;
+      double offset2 = i < self->n_stops ? stop2->offset : 1;
+      double start_angle, end_angle;
+
+      offset1 = offset1 * 360 + self->rotation - 90;
+      offset2 = offset2 * 360 + self->rotation - 90;
+
+      for (start_angle = offset1; start_angle < offset2; start_angle = end_angle)
+        {
+          GdkRGBA start_color, end_color;
+          end_angle = (floor (start_angle / 45) + 1) * 45;
+          end_angle = MIN (end_angle, offset2);
+          gdk_rgba_color_interpolate (&start_color,
+                                      &stop1->color,
+                                      &stop2->color,
+                                      (start_angle - offset1) / (offset2 - offset1));
+          gdk_rgba_color_interpolate (&end_color,
+                                      &stop1->color,
+                                      &stop2->color,
+                                      (end_angle - offset1) / (offset2 - offset1));
+
+          gsk_conic_gradient_node_add_patch (pattern, 
+                                             radius,
+                                             DEG_TO_RAD (start_angle),
+                                             &start_color,
+                                             DEG_TO_RAD (end_angle),
+                                             &end_color);
+        }
+    }
+
+  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+
+  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_conic_gradient_node_diff (GskRenderNode  *node1,
+                              GskRenderNode  *node2,
+                              cairo_region_t *region)
+{
+  GskConicGradientNode *self1 = (GskConicGradientNode *) node1;
+  GskConicGradientNode *self2 = (GskConicGradientNode *) node2;
+  gsize i;
+
+  if (!graphene_point_equal (&self1->center, &self2->center) ||
+      self1->rotation != self2->rotation ||
+      self1->n_stops != self2->n_stops)
+    {
+      gsk_render_node_diff_impossible (node1, node2, region);
+      return;
+    }
+
+  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))
+        {
+          gsk_render_node_diff_impossible (node1, node2, region);
+          return;
+        }
+    }
+}
+
+/**
+ * gsk_conic_gradient_node_new:
+ * @bounds: the bounds of the node
+ * @center: the center of the gradient
+ * @rotation: the rotation of the gradient in degrees
+ * @color_stops: (array length=n_color_stops): a pointer to an array of #GskColorStop defining the gradient
+ *   The offsets of all color steps must be increasing. The first stop's offset must be >= 0 and the last
+ *   stop's offset must be <= 1.
+ * @n_color_stops: the number of elements in @color_stops
+ *
+ * Creates a #GskRenderNode that draws a conic gradient. The conic gradient
+ * starts around @center in the direction of @rotation. A rotation of 0 means
+ * that the gradient points up. Color stops are then added clockwise.
+ *
+ * Returns: (transfer full) (type GskConicGradientNode): A new #GskRenderNode
+ */
+GskRenderNode *
+gsk_conic_gradient_node_new (const graphene_rect_t  *bounds,
+                             const graphene_point_t *center,
+                             float                   rotation,
+                             const GskColorStop     *color_stops,
+                             gsize                   n_color_stops)
+{
+  GskConicGradientNode *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_CONIC_GRADIENT_NODE);
+  node = (GskRenderNode *) self;
+
+  graphene_rect_init_from_rect (&node->bounds, bounds);
+  graphene_point_init_from_point (&self->center, center);
+
+  self->rotation = rotation;
+
+  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;
+}
+
+/**
+ * gsk_conic_gradient_node_get_n_color_stops:
+ * @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
+ *
+ * Retrieves the number of color stops in the gradient.
+ *
+ * Returns: the number of color stops
+ */
+gsize
+gsk_conic_gradient_node_get_n_color_stops (GskRenderNode *node)
+{
+  GskConicGradientNode *self = (GskConicGradientNode *) node;
+
+  return self->n_stops;
+}
+
+/**
+ * gsk_conic_gradient_node_get_color_stops:
+ * @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
+ * @n_stops: (out) (optional): the number of color stops in the returned array
+ *
+ * Retrieves the color stops in the gradient.
+ *
+ * Returns: (array length=n_stops): the color stops in the gradient
+ */
+const GskColorStop *
+gsk_conic_gradient_node_get_color_stops (GskRenderNode *node,
+                                         gsize         *n_stops)
+{
+  GskConicGradientNode *self = (GskConicGradientNode *) node;
+
+  if (n_stops != NULL)
+    *n_stops = self->n_stops;
+
+  return self->stops;
+}
+
+/**
+ * gsk_conic_gradient_node_get_center:
+ * @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
+ *
+ * Retrieves the center pointer for the gradient.
+ *
+ * Returns: the center point for the gradient
+ */
+const graphene_point_t *
+gsk_conic_gradient_node_get_center (GskRenderNode *node)
+{
+  GskConicGradientNode *self = (GskConicGradientNode *) node;
+
+  return &self->center;
+}
+
+/**
+ * gsk_conic_gradient_node_get_rotation:
+ * @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
+ *
+ * Retrieves the rotation for the gradient in degrees.
+ *
+ * Returns: the rotation for the gradient
+ */
+float
+gsk_conic_gradient_node_get_rotation (GskRenderNode *node)
+{
+  GskConicGradientNode *self = (GskConicGradientNode *) node;
+
+  return self->rotation;
+}
+
 /*** GSK_BORDER_NODE ***/
 
 struct _GskBorderNode
@@ -4752,6 +5073,7 @@ 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_conic_gradient_node, GSK_CONIC_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)
@@ -4885,6 +5207,22 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_REPEATING_RADIAL_GRADIENT_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_REPEATING_RADIAL_GRADIENT_NODE,
+      sizeof (GskRadialGradientNode),
+      NULL,
+      gsk_conic_gradient_node_finalize,
+      gsk_conic_gradient_node_draw,
+      NULL,
+      gsk_conic_gradient_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskConicGradientNode"), &node_info);
+    gsk_render_node_types[GSK_CONIC_GRADIENT_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 61f94853a1..756147adb2 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -1070,6 +1070,40 @@ parse_repeating_radial_gradient_node (GtkCssParser *parser)
   return parse_radial_gradient_node_internal (parser, TRUE);
 }
 
+static GskRenderNode *
+parse_conic_gradient_node (GtkCssParser *parser)
+{
+  graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
+  graphene_point_t center = GRAPHENE_POINT_INIT (25, 25);
+  double rotation = 0.0;
+  GArray *stops = NULL;
+  const Declaration declarations[] = {
+    { "bounds", parse_rect, NULL, &bounds },
+    { "center", parse_point, NULL, &center },
+    { "rotation", parse_double, NULL, &rotation },
+    { "stops", parse_stops, clear_stops, &stops },
+  };
+  GskRenderNode *result;
+
+  parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
+  if (stops == NULL)
+    {
+      GskColorStop from = { 0.0, GDK_RGBA("AAFF00") };
+      GskColorStop to = { 1.0, GDK_RGBA("FF00CC") };
+
+      stops = g_array_new (FALSE, FALSE, sizeof (GskColorStop));
+      g_array_append_val (stops, from);
+      g_array_append_val (stops, to);
+    }
+
+  result = gsk_conic_gradient_node_new (&bounds, &center, rotation, 
+                                        (GskColorStop *) stops->data, stops->len);
+
+  g_array_free (stops, TRUE);
+
+  return result;
+}
+
 static GskRenderNode *
 parse_inset_shadow_node (GtkCssParser *parser)
 {
@@ -1797,6 +1831,7 @@ parse_node (GtkCssParser *parser,
     { "inset-shadow", parse_inset_shadow_node },
     { "linear-gradient", parse_linear_gradient_node },
     { "radial-gradient", parse_radial_gradient_node },
+    { "conic-gradient", parse_conic_gradient_node },
     { "opacity", parse_opacity_node },
     { "outset-shadow", parse_outset_shadow_node },
     { "repeat", parse_repeat_node },
@@ -2355,6 +2390,21 @@ render_node_print (Printer       *p,
       }
       break;
 
+    case GSK_CONIC_GRADIENT_NODE:
+      {
+        start_node (p, "conic-gradient");
+
+        append_rect_param (p, "bounds", &node->bounds);
+        append_point_param (p, "center", gsk_conic_gradient_node_get_center (node));
+        append_float_param (p, "rotation", gsk_conic_gradient_node_get_rotation (node), 0.0f);
+
+        append_stops_param (p, "stops", gsk_conic_gradient_node_get_color_stops (node, NULL),
+                                        gsk_conic_gradient_node_get_n_color_stops (node));
+
+        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 103412487f..d81d38fa33 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_CONIC_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 dd9a1de67e..7e07e4413a 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -133,6 +133,7 @@ create_list_model_for_render_node (GskRenderNode *node)
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_RADIAL_GRADIENT_NODE:
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_CONIC_GRADIENT_NODE:
     case GSK_BORDER_NODE:
     case GSK_INSET_SHADOW_NODE:
     case GSK_OUTSET_SHADOW_NODE:
@@ -259,6 +260,8 @@ node_type_name (GskRenderNodeType type)
       return "Radial Gradient";
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
       return "Repeating Radial Gradient";
+    case GSK_CONIC_GRADIENT_NODE:
+      return "Conic Gradient";
     case GSK_BORDER_NODE:
       return "Border";
     case GSK_TEXTURE_NODE:
@@ -308,6 +311,7 @@ node_name (GskRenderNode *node)
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_RADIAL_GRADIENT_NODE:
     case GSK_REPEATING_RADIAL_GRADIENT_NODE:
+    case GSK_CONIC_GRADIENT_NODE:
     case GSK_BORDER_NODE:
     case GSK_INSET_SHADOW_NODE:
     case GSK_OUTSET_SHADOW_NODE:
@@ -729,6 +733,44 @@ populate_render_node_properties (GtkListStore  *store,
       }
       break;
 
+    case GSK_CONIC_GRADIENT_NODE:
+      {
+        const graphene_point_t *center = gsk_conic_gradient_node_get_center (node);
+        const float rotation = gsk_conic_gradient_node_get_rotation (node);
+        const gsize n_stops = gsk_conic_gradient_node_get_n_color_stops (node);
+        const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL);
+        gsize i;
+        GString *s;
+        GdkTexture *texture;
+
+        tmp = g_strdup_printf ("%.2f, %.2f", center->x, center->y);
+        add_text_row (store, "Center", tmp);
+        g_free (tmp);
+
+        tmp = g_strdup_printf ("%.2f", rotation);
+        add_text_row (store, "Rotation", tmp);
+        g_free (tmp);
+
+        s = g_string_new ("");
+        for (i = 0; i < n_stops; i++)
+          {
+            tmp = gdk_rgba_to_string (&stops[i].color);
+            g_string_append_printf (s, "%.2f, %s\n", stops[i].offset, tmp);
+            g_free (tmp);
+          }
+
+        texture = get_linear_gradient_texture (n_stops, stops);
+        gtk_list_store_insert_with_values (store, NULL, -1,
+                                           0, "Color Stops",
+                                           1, s->str,
+                                           2, TRUE,
+                                           3, texture,
+                                           -1);
+        g_string_free (s, TRUE);
+        g_object_unref (texture);
+      }
+      break;
+
     case GSK_TEXT_NODE:
       {
         const PangoFont *font = gsk_text_node_get_font (node);


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