[gtk/wip/otte/conic: 3/3] glrenderer: Implement a shader for conic gradients




commit bd0ffb0887c8f647e7a71a239659315285c7c1ef
Author: Benjamin Otte <otte redhat com>
Date:   Thu Dec 3 06:02:18 2020 +0100

    glrenderer: Implement a shader for conic gradients

 gsk/gl/gskglrenderer.c                 | 62 +++++++++++++++++++++++++++++-
 gsk/gl/gskglrenderops.c                | 24 ++++++++++++
 gsk/gl/gskglrenderopsprivate.h         | 15 +++++++-
 gsk/gl/opbuffer.c                      |  1 +
 gsk/gl/opbuffer.h                      |  9 +++++
 gsk/meson.build                        |  1 +
 gsk/resources/glsl/conic_gradient.glsl | 69 ++++++++++++++++++++++++++++++++++
 7 files changed, 179 insertions(+), 2 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index ca8ab90295..028945c5cd 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -1483,6 +1483,35 @@ render_radial_gradient_node (GskGLRenderer   *self,
     }
 }
 
+static inline void
+render_conic_gradient_node (GskGLRenderer   *self,
+                             GskRenderNode   *node,
+                             RenderOpBuilder *builder)
+{
+  const int n_color_stops = gsk_conic_gradient_node_get_n_color_stops (node);
+
+  if (n_color_stops < GL_MAX_GRADIENT_STOPS)
+    {
+      const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL);
+      const graphene_point_t *center = gsk_conic_gradient_node_get_center (node);
+      const float rotation = gsk_conic_gradient_node_get_rotation (node);
+
+      ops_set_program (builder, &self->programs->conic_gradient_program);
+      ops_set_conic_gradient (builder,
+                              n_color_stops,
+                              stops,
+                              builder->dx + center->x,
+                              builder->dy + center->y,
+                              rotation);
+
+      load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
+    }
+  else
+    {
+      render_fallback_node (self, node, builder);
+    }
+}
+
 static inline gboolean
 rounded_inner_rect_contains_rect (const GskRoundedRect  *rounded,
                                   const graphene_rect_t *rect)
@@ -3016,6 +3045,23 @@ apply_radial_gradient_op (const Program          *program,
   glUniform2f (program->radial_gradient.center_location, op->center[0], op->center[1]);
 }
 
+static inline void
+apply_conic_gradient_op (const Program         *program,
+                         const OpConicGradient *op)
+{
+  OP_PRINT (" -> Conic gradient");
+  if (op->n_color_stops.send)
+    glUniform1i (program->conic_gradient.num_color_stops_location, op->n_color_stops.value);
+
+  if (op->color_stops.send)
+    glUniform1fv (program->conic_gradient.color_stops_location,
+                  op->n_color_stops.value * 5,
+                  (float *)op->color_stops.value);
+
+  glUniform1f (program->conic_gradient.rotation_location, op->rotation);
+  glUniform2f (program->conic_gradient.center_location, op->center[0], op->center[1]);
+}
+
 static inline void
 apply_border_op (const Program  *program,
                  const OpBorder *op)
@@ -3250,6 +3296,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
     { "/org/gtk/libgsk/glsl/inset_shadow.glsl",              "inset shadow" },
     { "/org/gtk/libgsk/glsl/linear_gradient.glsl",           "linear gradient" },
     { "/org/gtk/libgsk/glsl/radial_gradient.glsl",           "radial gradient" },
+    { "/org/gtk/libgsk/glsl/conic_gradient.glsl",            "conic gradient" },
     { "/org/gtk/libgsk/glsl/outset_shadow.glsl",             "outset shadow" },
     { "/org/gtk/libgsk/glsl/repeat.glsl",                    "repeat" },
     { "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl",   "unblurred_outset shadow" },
@@ -3312,6 +3359,12 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
   INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, end);
   INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, radius);
 
+  /* conic gradient */
+  INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, color_stops);
+  INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, num_color_stops);
+  INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, center);
+  INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, rotation);
+
   /* blur */
   INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius);
   INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_size);
@@ -3662,6 +3715,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
       render_radial_gradient_node (self, node, builder);
     break;
 
+    case GSK_CONIC_GRADIENT_NODE:
+      render_conic_gradient_node (self, node, builder);
+    break;
+
     case GSK_CLIP_NODE:
       render_clip_node (self, node, builder);
     break;
@@ -3723,7 +3780,6 @@ 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:
       {
@@ -4026,6 +4082,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
           apply_radial_gradient_op (program, ptr);
           break;
 
+        case OP_CHANGE_CONIC_GRADIENT:
+          apply_conic_gradient_op (program, ptr);
+          break;
+
         case OP_CHANGE_BLUR:
           apply_blur_op (program, ptr);
           break;
diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c
index 63273db93d..7351cf8746 100644
--- a/gsk/gl/gskglrenderops.c
+++ b/gsk/gl/gskglrenderops.c
@@ -1038,3 +1038,27 @@ ops_set_radial_gradient (RenderOpBuilder    *self,
   op->start = start;
   op->end = end;
 }
+
+void
+ops_set_conic_gradient (RenderOpBuilder    *self,
+                        guint               n_color_stops,
+                        const GskColorStop *color_stops,
+                        float               center_x,
+                        float               center_y,
+                        float               rotation)
+{
+  const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
+  OpConicGradient *op;
+
+  /* TODO: State tracking? */
+
+  op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT);
+  op->n_color_stops.value = real_n_color_stops;
+  op->n_color_stops.send = true;
+  op->color_stops.value = color_stops;
+  op->color_stops.send = true;
+  op->center[0] = center_x;
+  op->center[1] = center_y;
+  op->rotation = rotation;
+}
+
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index 7119ae0680..3dd3cb3411 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -13,7 +13,7 @@
 #include "opbuffer.h"
 
 #define GL_N_VERTICES 6
-#define GL_N_PROGRAMS 14
+#define GL_N_PROGRAMS 15
 #define GL_MAX_GRADIENT_STOPS 6
 
 typedef struct
@@ -129,6 +129,12 @@ struct _Program
       int end_location;
       int radius_location;
     } radial_gradient;
+    struct {
+      int num_color_stops_location;
+      int color_stops_location;
+      int center_location;
+      int rotation_location;
+    } conic_gradient;
     struct {
       int blur_radius_location;
       int blur_size_location;
@@ -193,6 +199,7 @@ typedef struct {
       Program inset_shadow_program;
       Program linear_gradient_program;
       Program radial_gradient_program;
+      Program conic_gradient_program;
       Program outset_shadow_program;
       Program repeat_program;
       Program unblurred_outset_shadow_program;
@@ -327,6 +334,12 @@ void              ops_set_radial_gradient (RenderOpBuilder        *self,
                                            float                   end,
                                            float                   hradius,
                                            float                   vradius);
+void              ops_set_conic_gradient  (RenderOpBuilder        *self,
+                                           guint                   n_color_stops,
+                                           const GskColorStop     *color_stops,
+                                           float                   center_x,
+                                           float                   center_y,
+                                           float                   rotation);
 
 GskQuadVertex *   ops_draw               (RenderOpBuilder        *builder,
                                           const GskQuadVertex     vertex_data[GL_N_VERTICES]);
diff --git a/gsk/gl/opbuffer.c b/gsk/gl/opbuffer.c
index 8a7f8c9b1f..806b8f7ca4 100644
--- a/gsk/gl/opbuffer.c
+++ b/gsk/gl/opbuffer.c
@@ -33,6 +33,7 @@ static guint op_sizes[OP_LAST] = {
   sizeof (OpBlend),
   sizeof (OpGLShader),
   sizeof (OpExtraTexture),
+  sizeof (OpConicGradient),
 };
 
 void
diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h
index a2236dd7a1..08f48b7c5f 100644
--- a/gsk/gl/opbuffer.h
+++ b/gsk/gl/opbuffer.h
@@ -41,6 +41,7 @@ typedef enum
   OP_CHANGE_BLEND                      = 27,
   OP_CHANGE_GL_SHADER_ARGS             = 28,
   OP_CHANGE_EXTRA_SOURCE_TEXTURE       = 29,
+  OP_CHANGE_CONIC_GRADIENT             = 30,
   OP_LAST
 } OpKind;
 
@@ -156,6 +157,14 @@ typedef struct
   float center[2];
 } OpRadialGradient;
 
+typedef struct
+{
+  ColorStopUniformValue color_stops;
+  IntUniformValue n_color_stops;
+  float center[2];
+  float rotation;
+} OpConicGradient;
+
 typedef struct
 {
   const graphene_matrix_t *matrix;
diff --git a/gsk/meson.build b/gsk/meson.build
index 4ea6091a94..01c5e58fcc 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -8,6 +8,7 @@ gsk_private_gl_shaders = [
   'resources/glsl/color.glsl',
   'resources/glsl/linear_gradient.glsl',
   'resources/glsl/radial_gradient.glsl',
+  'resources/glsl/conic_gradient.glsl',
   'resources/glsl/color_matrix.glsl',
   'resources/glsl/blur.glsl',
   'resources/glsl/inset_shadow.glsl',
diff --git a/gsk/resources/glsl/conic_gradient.glsl b/gsk/resources/glsl/conic_gradient.glsl
new file mode 100644
index 0000000000..e9dee73e07
--- /dev/null
+++ b/gsk/resources/glsl/conic_gradient.glsl
@@ -0,0 +1,69 @@
+// VERTEX_SHADER
+uniform vec2 u_center;
+uniform float u_rotation;
+uniform float u_color_stops[6 * 5];
+uniform int u_num_color_stops;
+
+const float PI = 3.1415926535897932384626433832795;
+
+_OUT_ vec2 center;
+_OUT_ float rotation;
+_OUT_ vec4 color_stops[6];
+_OUT_ float color_offsets[6];
+
+void main() {
+  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+  // The -90 is because conics point to the top by default
+  rotation = mod (u_rotation - 90, 360.0);
+  if (rotation < 0)
+    rotation += 360.0;
+  rotation = PI / 180.0 * rotation;
+
+  center = (u_modelview * vec4(u_center, 0, 1)).xy;
+
+  for (int i = 0; i < u_num_color_stops; i ++) {
+    color_offsets[i] = u_color_stops[(i * 5) + 0];
+    color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
+                                          u_color_stops[(i * 5) + 2],
+                                          u_color_stops[(i * 5) + 3],
+                                          u_color_stops[(i * 5) + 4]));
+  }
+}
+
+// FRAGMENT_SHADER:
+#ifdef GSK_LEGACY
+uniform int u_num_color_stops;
+#else
+uniform highp int u_num_color_stops; // Why? Because it works like this.
+#endif
+
+const float PI = 3.1415926535897932384626433832795;
+
+_IN_ vec2 center;
+_IN_ float rotation;
+_IN_ vec4 color_stops[6];
+_IN_ float color_offsets[6];
+
+void main() {
+  // Position relative to center
+  vec2 pos = gsk_get_frag_coord() - center;
+
+  // direction of point in range [-PI, PI]
+  float angle = atan (pos.y, pos.x);
+  // rotate, it's now [-2 * PI, PI]
+  angle -= rotation;
+  // fract() does the modulo here, so now we have progress
+  // into the current conic
+  float offset = fract (angle / 2 / PI + 2);
+
+  vec4 color = color_stops[0];
+  for (int i = 1; i < u_num_color_stops; i ++) {
+    if (offset >= color_offsets[i - 1])  {
+      float o = (offset - color_offsets[i - 1]) / (color_offsets[i] - color_offsets[i - 1]);
+      color = mix(color_stops[i - 1], color_stops[i], clamp(o, 0.0, 1.0));
+    }
+  }
+
+  gskSetOutputColor(color * u_alpha);
+}


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