[gtk/wip/baedert/radial-gradient: 126/126] gl renderer: Add radial gradient shader




commit 4f2d44c4f1acdada89d13af6fb93d8050c87547d
Author: Timm Bäder <mail baedert org>
Date:   Fri Sep 18 14:42:36 2020 +0200

    gl renderer: Add radial gradient shader

 gsk/gl/gskglrenderer.c                  | 63 ++++++++++++++++++++++-
 gsk/gl/gskglrenderops.c                 | 29 +++++++++++
 gsk/gl/gskglrenderopsprivate.h          | 28 ++++++++++-
 gsk/gl/opbuffer.c                       |  1 +
 gsk/gl/opbuffer.h                       | 41 +++++++++------
 gsk/gskrendernodeimpl.c                 |  2 -
 gsk/meson.build                         |  1 +
 gsk/resources/glsl/radial_gradient.glsl | 89 +++++++++++++++++++++++++++++++++
 8 files changed, 235 insertions(+), 19 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 00fb5aea5e..96aecbd70b 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -1192,6 +1192,32 @@ render_linear_gradient_node (GskGLRenderer   *self,
   load_vertex_data (ops_draw (builder, NULL), node, builder);
 }
 
+static inline void
+render_radial_gradient_node (GskGLRenderer   *self,
+                             GskRenderNode   *node,
+                             RenderOpBuilder *builder)
+{
+  const float scale = ops_get_scale (builder);
+  const int n_color_stops = MIN (8, gsk_radial_gradient_node_get_n_color_stops (node));
+  const GskColorStop *stops = gsk_radial_gradient_node_peek_color_stops (node, NULL);
+  const graphene_point_t *center = gsk_radial_gradient_node_peek_center (node);
+  const float start = gsk_radial_gradient_node_get_start (node);
+  const float end = gsk_radial_gradient_node_get_end (node);
+  const float hradius = gsk_radial_gradient_node_get_hradius (node);
+  const float vradius = gsk_radial_gradient_node_get_vradius (node);
+
+  ops_set_program (builder, &self->programs->radial_gradient_program);
+  ops_set_radial_gradient (builder,
+                           n_color_stops,
+                           stops,
+                           builder->dx + center->x,
+                           builder->dy + center->y,
+                           start, end,
+                           hradius * scale, vradius * scale);
+
+  load_vertex_data (ops_draw (builder, NULL), node, builder);
+}
+
 static inline gboolean
 rounded_inner_rect_contains_rect (const GskRoundedRect  *rounded,
                                   const graphene_rect_t *rect)
@@ -2761,6 +2787,25 @@ apply_linear_gradient_op (const Program          *program,
   glUniform2f (program->linear_gradient.end_point_location, op->end_point[0], op->end_point[1]);
 }
 
+static inline void
+apply_radial_gradient_op (const Program          *program,
+                          const OpRadialGradient *op)
+{
+  OP_PRINT (" -> Radial gradient");
+  if (op->n_color_stops.send)
+    glUniform1i (program->radial_gradient.num_color_stops_location, op->n_color_stops.value);
+
+  if (op->color_stops.send)
+    glUniform1fv (program->radial_gradient.color_stops_location,
+                  op->n_color_stops.value * 5,
+                  (float *)op->color_stops.value);
+
+  glUniform1f (program->radial_gradient.start_location, op->start);
+  glUniform1f (program->radial_gradient.end_location, op->end);
+  glUniform2f (program->radial_gradient.radius_location, op->radius[0], op->radius[1]);
+  glUniform2f (program->radial_gradient.center_location, op->center[0], op->center[1]);
+}
+
 static inline void
 apply_border_op (const Program  *program,
                  const OpBorder *op)
@@ -2903,6 +2948,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
     { "/org/gtk/libgsk/glsl/cross_fade.glsl",                "cross fade" },
     { "/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/outset_shadow.glsl",             "outset shadow" },
     { "/org/gtk/libgsk/glsl/repeat.glsl",                    "repeat" },
     { "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl",   "unblurred_outset shadow" },
@@ -2984,6 +3030,14 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
   INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, start_point);
   INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, end_point);
 
+  /* radial gradient */
+  INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, color_stops);
+  INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, num_color_stops);
+  INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, center);
+  INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, start);
+  INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, end);
+  INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, radius);
+
   /* blur */
   INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius);
   INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_size);
@@ -3332,6 +3386,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
       render_linear_gradient_node (self, node, builder);
     break;
 
+    case GSK_RADIAL_GRADIENT_NODE:
+      render_radial_gradient_node (self, node, builder);
+    break;
+
     case GSK_CLIP_NODE:
       render_clip_node (self, node, builder);
     break;
@@ -3388,7 +3446,6 @@ 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:
@@ -3684,6 +3741,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
           apply_linear_gradient_op (program, ptr);
           break;
 
+        case OP_CHANGE_RADIAL_GRADIENT:
+          apply_radial_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 1d48112804..7713f294eb 100644
--- a/gsk/gl/gskglrenderops.c
+++ b/gsk/gl/gskglrenderops.c
@@ -960,3 +960,32 @@ ops_set_linear_gradient (RenderOpBuilder     *self,
   op->end_point[0] = end_x;
   op->end_point[1] = end_y;
 }
+
+void
+ops_set_radial_gradient (RenderOpBuilder    *self,
+                         guint               n_color_stops,
+                         const GskColorStop *color_stops,
+                         float               center_x,
+                         float               center_y,
+                         float               start,
+                         float               end,
+                         float               hradius,
+                         float               vradius)
+{
+  const guint real_n_color_stops = MIN (MAX_GRADIENT_STOPS, n_color_stops);
+  OpRadialGradient *op;
+
+  /* TODO: State tracking? */
+
+  op = ops_begin (self, OP_CHANGE_RADIAL_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->radius[0] = hradius;
+  op->radius[1] = vradius;
+  op->start = start;
+  op->end = end;
+}
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index ebb39e355a..a6c6d0f232 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 13
+#define GL_N_PROGRAMS 14
 #define MAX_GRADIENT_STOPS 8
 
 typedef struct
@@ -62,6 +62,14 @@ struct _Program
       int start_point_location;
       int end_point_location;
     } linear_gradient;
+    struct {
+      int num_color_stops_location;
+      int color_stops_location;
+      int center_location;
+      int start_location;
+      int end_location;
+      int radius_location;
+    } radial_gradient;
     struct {
       int blur_radius_location;
       int blur_size_location;
@@ -143,6 +151,14 @@ typedef struct
       float start_point[2];
       float end_point[2];
     } linear_gradient;
+    struct {
+      int n_color_stops;
+      GskColorStop color_stops[MAX_GRADIENT_STOPS];
+      float center[2];
+      float start;
+      float end;
+      float radius[2]; /* h/v */
+    } radial_gradient;
   };
 } ProgramState;
 
@@ -161,6 +177,7 @@ typedef struct {
       Program cross_fade_program;
       Program inset_shadow_program;
       Program linear_gradient_program;
+      Program radial_gradient_program;
       Program outset_shadow_program;
       Program repeat_program;
       Program unblurred_outset_shadow_program;
@@ -278,6 +295,15 @@ void              ops_set_linear_gradient (RenderOpBuilder     *self,
                                            float                start_y,
                                            float                end_x,
                                            float                end_y);
+void              ops_set_radial_gradient (RenderOpBuilder        *self,
+                                           guint                   n_color_stops,
+                                           const GskColorStop     *color_stops,
+                                           float                   center_x,
+                                           float                   center_y,
+                                           float                   start,
+                                           float                   end,
+                                           float                   hradius,
+                                           float                   vradius);
 
 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 496308c5ae..f212e96eec 100644
--- a/gsk/gl/opbuffer.c
+++ b/gsk/gl/opbuffer.c
@@ -15,6 +15,7 @@ static guint op_sizes[OP_LAST] = {
   sizeof (OpTexture),
   sizeof (OpRepeat),
   sizeof (OpLinearGradient),
+  sizeof (OpRadialGradient),
   sizeof (OpColorMatrix),
   sizeof (OpBlur),
   sizeof (OpShadow),
diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h
index 9604f4b852..dcdedc6c50 100644
--- a/gsk/gl/opbuffer.h
+++ b/gsk/gl/opbuffer.h
@@ -23,21 +23,22 @@ typedef enum
   OP_CHANGE_SOURCE_TEXTURE             =  9,
   OP_CHANGE_REPEAT                     = 10,
   OP_CHANGE_LINEAR_GRADIENT            = 11,
-  OP_CHANGE_COLOR_MATRIX               = 12,
-  OP_CHANGE_BLUR                       = 13,
-  OP_CHANGE_INSET_SHADOW               = 14,
-  OP_CHANGE_OUTSET_SHADOW              = 15,
-  OP_CHANGE_BORDER                     = 16,
-  OP_CHANGE_BORDER_COLOR               = 17,
-  OP_CHANGE_BORDER_WIDTH               = 18,
-  OP_CHANGE_CROSS_FADE                 = 19,
-  OP_CHANGE_UNBLURRED_OUTSET_SHADOW    = 20,
-  OP_CLEAR                             = 21,
-  OP_DRAW                              = 22,
-  OP_DUMP_FRAMEBUFFER                  = 23,
-  OP_PUSH_DEBUG_GROUP                  = 24,
-  OP_POP_DEBUG_GROUP                   = 25,
-  OP_CHANGE_BLEND                      = 26,
+  OP_CHANGE_RADIAL_GRADIENT            = 12,
+  OP_CHANGE_COLOR_MATRIX               = 13,
+  OP_CHANGE_BLUR                       = 14,
+  OP_CHANGE_INSET_SHADOW               = 15,
+  OP_CHANGE_OUTSET_SHADOW              = 16,
+  OP_CHANGE_BORDER                     = 17,
+  OP_CHANGE_BORDER_COLOR               = 18,
+  OP_CHANGE_BORDER_WIDTH               = 19,
+  OP_CHANGE_CROSS_FADE                 = 20,
+  OP_CHANGE_UNBLURRED_OUTSET_SHADOW    = 21,
+  OP_CLEAR                             = 22,
+  OP_DRAW                              = 23,
+  OP_DUMP_FRAMEBUFFER                  = 24,
+  OP_PUSH_DEBUG_GROUP                  = 25,
+  OP_POP_DEBUG_GROUP                   = 26,
+  OP_CHANGE_BLEND                      = 27,
   OP_LAST
 } OpKind;
 
@@ -137,6 +138,16 @@ typedef struct
   float end_point[2];
 } OpLinearGradient;
 
+typedef struct
+{
+  ColorStopUniformValue color_stops;
+  IntUniformValue n_color_stops;
+  float start;
+  float end;
+  float radius[2];
+  float center[2];
+} OpRadialGradient;
+
 typedef struct
 {
   const graphene_matrix_t *matrix;
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index e383ef88be..efb49738b9 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -394,8 +394,6 @@ struct _GskRadialGradientNode
 
   graphene_point_t center;
 
-  gboolean circle;
-
   float hradius;
   float vradius;
   float start;
diff --git a/gsk/meson.build b/gsk/meson.build
index 9cc22359cb..c3567d78ed 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -7,6 +7,7 @@ gsk_private_gl_shaders = [
   'resources/glsl/coloring.glsl',
   'resources/glsl/color.glsl',
   'resources/glsl/linear_gradient.glsl',
+  'resources/glsl/radial_gradient.glsl',
   'resources/glsl/color_matrix.glsl',
   'resources/glsl/blur.glsl',
   'resources/glsl/inset_shadow.glsl',
diff --git a/gsk/resources/glsl/radial_gradient.glsl b/gsk/resources/glsl/radial_gradient.glsl
new file mode 100644
index 0000000000..84673fc470
--- /dev/null
+++ b/gsk/resources/glsl/radial_gradient.glsl
@@ -0,0 +1,89 @@
+// VERTEX_SHADER
+uniform float u_start;
+uniform float u_end;
+uniform float u_color_stops[8 * 5];
+uniform int u_num_color_stops;
+uniform vec2 u_radius;
+uniform vec2 u_center;
+
+_OUT_ vec2 center;
+_OUT_ vec4 color_stops[8];
+_OUT_ float color_offsets[8];
+_OUT_ float start;
+_OUT_ float end;
+
+void main() {
+  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+  center = (u_modelview * vec4(u_center, 0, 1)).xy;
+  start = u_start;
+  end = u_end;
+
+  for (int i = 0; i < u_num_color_stops; i ++) {
+    color_offsets[i] = u_color_stops[(i * 5) + 0];
+    color_stops[i].r = u_color_stops[(i * 5) + 1];
+    color_stops[i].g = u_color_stops[(i * 5) + 2];
+    color_stops[i].b = u_color_stops[(i * 5) + 3];
+    color_stops[i].a = 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;
+#endif
+
+uniform vec2 u_radius;
+uniform float u_end;
+
+_IN_ vec2 center;
+_IN_ vec4 color_stops[8];
+_IN_ float color_offsets[8];
+_IN_ float start;
+_IN_ float end;
+
+// The offsets in the color stops are relative to the
+// start and end values of the gradient.
+float abs_offset(float offset)  {
+  return start + ((end - start) * offset);
+}
+
+vec4 premultiply(vec4 c) {
+  vec4 k = vec4(c.rgb * c.a, c.a);
+  return k;
+}
+
+void main() {
+  vec2 pixel = get_frag_coord();
+  vec2 rel = (center - pixel) / (u_radius);
+  float d = sqrt(dot(rel, rel));
+
+  if (d < abs_offset (color_offsets[0])) {
+    setOutputColor(premultiply(color_stops[0]) * u_alpha);
+    return;
+  }
+
+  if (d > end) {
+    setOutputColor(premultiply(color_stops[u_num_color_stops - 1]) * u_alpha);
+    return;
+  }
+
+  vec4 color = vec4(0, 0, 0, 0);
+  for (int i = 1; i < u_num_color_stops; i++) {
+    float last_offset = abs_offset(color_offsets[i - 1]);
+    float this_offset = abs_offset(color_offsets[i]);
+
+    // We have color_stops[i - 1] at last_offset and color_stops[i] at this_offset.
+    // We now need to map `d` between those two offsets and simply mix linearly between them
+    if (d >= last_offset && d <= this_offset) {
+      float f = (d - last_offset) / (this_offset - last_offset);
+
+      color = mix(color_stops[i - 1], color_stops[i], f);
+      break;
+    }
+  }
+
+  setOutputColor(premultiply(color) * u_alpha);
+}


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