[gtk+/wip/baedert/gl: 64/66] gl renderer: Implement simple border nodes



commit 656abef8541193fe791700a033960c2dbbfa083a
Author: Timm Bäder <mail baedert org>
Date:   Fri Dec 1 11:55:10 2017 +0100

    gl renderer: Implement simple border nodes

 gsk/gl/gskglrenderer.c            |  180 ++++++++++++++++++++++++++++++++++---
 gsk/gl/gskglrenderopsprivate.h    |   14 +++-
 gsk/meson.build                   |    2 +
 gsk/resources/glsl/border.fs.glsl |   32 +++++++
 4 files changed, 212 insertions(+), 16 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 7536c2b..c689572 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -172,6 +172,7 @@ struct _GskGLRenderer
       Program inset_shadow_program;
       Program outset_shadow_program;
       Program shadow_program;
+      Program border_program;
     };
   };
 
@@ -330,6 +331,126 @@ render_text_node (GskGLRenderer   *self,
     }
 }
 
+static inline void
+render_border_node (GskGLRenderer   *self,
+                    GskRenderNode   *node,
+                    RenderOpBuilder *builder)
+{
+  float min_x = node->bounds.origin.x;
+  float min_y = node->bounds.origin.y;
+  float max_x = min_x + node->bounds.size.width;
+  float max_y = min_y + node->bounds.size.height;
+  const GdkRGBA *colors = gsk_border_node_peek_colors (node);
+  const GskRoundedRect *rounded_outline = gsk_border_node_peek_outline (node);
+  const float *widths = gsk_border_node_peek_widths (node);
+  const gboolean needs_clip = TRUE;/*!gsk_rounded_rect_is_rectilinear (rounded_outline);*/
+  GskRoundedRect prev_clip;
+  graphene_rect_t transformed_clip;
+  graphene_rect_t intersection;
+  GskRoundedRect child_clip;
+  RenderOp op;
+  struct {
+    float w;
+    float h;
+  } sizes[4];
+
+  /* Top left */
+  sizes[0].w = MAX (MAX (widths[3], rounded_outline->corner[0].width), 1);
+  sizes[0].h = MAX (MAX (widths[1], rounded_outline->corner[0].height), 1);
+
+  /* Top right */
+  sizes[1].w = MAX (MAX (widths[1], rounded_outline->corner[1].width), 1);
+  sizes[1].h = MAX (MAX (widths[0], rounded_outline->corner[1].height), 1);
+
+  /* Bottom right */
+  sizes[2].w = MAX (MAX (widths[1], rounded_outline->corner[2].width), 1);
+  sizes[2].h = MAX (MAX (widths[2], rounded_outline->corner[2].height), 1);
+
+  /* Bottom left */
+  sizes[3].w = MAX (MAX (widths[3], rounded_outline->corner[3].width), 1);
+  sizes[3].h = MAX (MAX (widths[2], rounded_outline->corner[3].height), 1);
+
+  if (needs_clip)
+    {
+      ops_set_program (builder, &self->border_program);
+
+      transformed_clip = rounded_outline->bounds;
+      graphene_matrix_transform_bounds (&builder->current_modelview, &transformed_clip, &transformed_clip);
+
+      graphene_rect_intersection (&transformed_clip, &builder->current_clip.bounds,
+                                  &intersection);
+      gsk_rounded_rect_init (&child_clip, &intersection,
+                             &rounded_outline->corner[0],
+                             &rounded_outline->corner[1],
+                             &rounded_outline->corner[2],
+                             &rounded_outline->corner[3]);
+
+      prev_clip = ops_set_clip (builder, &child_clip);
+
+      op.op = OP_CHANGE_BORDER;
+      op.border.widths[0] = widths[0];
+      op.border.widths[1] = widths[1];
+      op.border.widths[2] = widths[2];
+      op.border.widths[3] = widths[3];
+      ops_add (builder, &op);
+    }
+  else
+    {
+      ops_set_program (builder, &self->color_program);
+    }
+
+  /* Top */
+  ops_set_color (builder, &colors[0]);
+  ops_draw (builder, (const GskQuadVertex[6]) {
+    { { min_x,              min_y              }, { 0, 1 }, }, /* Upper left */
+    { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */
+    { { max_x,              min_y              }, { 1, 1 }, }, /* Upper right */
+
+    { { max_x - sizes[1].w, min_y + sizes[1].h }, { 1, 0 }, }, /* Lower right */
+    { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */
+    { { max_x,              min_y              }, { 1, 1 }, }, /* Upper right */
+  });
+
+  /* Right */
+  ops_set_color (builder, &colors[1]);
+  ops_draw (builder, (const GskQuadVertex[6]) {
+    { { max_x - sizes[1].w, min_y + sizes[1].h }, { 0, 1 }, }, /* Upper left */
+    { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */
+    { { max_x,              min_y              }, { 1, 1 }, }, /* Upper right */
+
+    { { max_x,              max_y              }, { 1, 0 }, }, /* Lower right */
+    { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */
+    { { max_x,              min_y              }, { 1, 1 }, }, /* Upper right */
+  });
+
+  /* Bottom */
+  ops_set_color (builder, &colors[2]);
+  ops_draw (builder, (const GskQuadVertex[6]) {
+    { { min_x + sizes[3].w, max_y - sizes[3].h }, { 0, 1 }, }, /* Upper left */
+    { { min_x,              max_y              }, { 0, 0 }, }, /* Lower left */
+    { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */
+
+    { { max_x,              max_y              }, { 1, 0 }, }, /* Lower right */
+    { { min_x            ,  max_y              }, { 0, 0 }, }, /* Lower left */
+    { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */
+  });
+
+  /* Left */
+  ops_set_color (builder, &colors[3]);
+  ops_draw (builder, (const GskQuadVertex[6]) {
+    { { min_x,              min_y              }, { 0, 1 }, }, /* Upper left */
+    { { min_x,              max_y              }, { 0, 0 }, }, /* Lower left */
+    { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */
+
+    { { min_x + sizes[3].w, max_y - sizes[2].h }, { 1, 0 }, }, /* Lower right */
+    { { min_x,              max_y              }, { 0, 0 }, }, /* Lower left */
+    { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */
+  });
+
+  if (needs_clip)
+    ops_set_clip (builder, &prev_clip);
+}
+
 static void
 gsk_gl_renderer_dispose (GObject *gobject)
 {
@@ -400,16 +521,17 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
     const char *vs;
     const char *fs;
   } program_definitions[] = {
-      { "blend",           "blend.vs.glsl", "blend.fs.glsl" },
-      { "blit",            "blit.vs.glsl",  "blit.fs.glsl" },
-      { "color",           "blit.vs.glsl",  "color.fs.glsl" },
-      { "coloring",        "blit.vs.glsl",  "coloring.fs.glsl" },
-      { "color matrix",    "blit.vs.glsl",  "color_matrix.fs.glsl" },
-      { "linear gradient", "blit.vs.glsl",  "linear_gradient.fs.glsl" },
-      { "blur",            "blit.vs.glsl",  "blur.fs.glsl" },
-      { "inset shadow",    "blit.vs.glsl",  "inset_shadow.fs.glsl" },
-      { "outset shadow",   "blit.vs.glsl",  "outset_shadow.fs.glsl" },
-      { "shadow",          "blit.vs.glsl",  "shadow.fs.glsl" },
+    { "blend",           "blend.vs.glsl", "blend.fs.glsl" },
+    { "blit",            "blit.vs.glsl",  "blit.fs.glsl" },
+    { "color",           "blit.vs.glsl",  "color.fs.glsl" },
+    { "coloring",        "blit.vs.glsl",  "coloring.fs.glsl" },
+    { "color matrix",    "blit.vs.glsl",  "color_matrix.fs.glsl" },
+    { "linear gradient", "blit.vs.glsl",  "linear_gradient.fs.glsl" },
+    { "blur",            "blit.vs.glsl",  "blur.fs.glsl" },
+    { "inset shadow",    "blit.vs.glsl",  "inset_shadow.fs.glsl" },
+    { "outset shadow",   "blit.vs.glsl",  "outset_shadow.fs.glsl" },
+    { "shadow",          "blit.vs.glsl",  "shadow.fs.glsl" },
+    { "border",          "blit.vs.glsl",  "border.fs.glsl" },
   };
 
   builder = gsk_shader_builder_new ();
@@ -523,6 +645,10 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
   /* shadow */
   INIT_PROGRAM_UNIFORM_LOCATION2 (shadow, color);
 
+  /* border */
+  INIT_PROGRAM_UNIFORM_LOCATION2 (border, color);
+  INIT_PROGRAM_UNIFORM_LOCATION2 (border, widths);
+
   g_object_unref (builder);
   return TRUE;
 }
@@ -1153,7 +1279,7 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
         guint i;
 
         /* TODO: shadow nodes are most commonly used for text and icon shadows.
-         *       In both cases, we can avoit the RTT case!
+         *       In both cases, we can avoid the RTT case!
          *       if the child is neither a text node nor a texture node though, we need
          *       to fall back to rendering it to a texture and then applying the shadow on that one.*/
 
@@ -1221,8 +1347,31 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
       }
     break;
 
-    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_BORDER_NODE:
+      {
+        /* TODO: The cairo backend is exactly as broken as the code in the following
+           function. test case:
+
+          .foo {
+            border-left:   50px solid #0f0;
+            border-top:    10px solid red;
+            border-bottom: 50px solid teal;
+            border-right:  100px solid pink;
+
+
+            border-radius: 100px; 
+            min-height:100px;
+
+            float:left;
+          }
+
+          Or, like, just circular things.
+         */
+        render_border_node (self, node, builder);
+      }
+    break;
+
+    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_CROSS_FADE_NODE:
     case GSK_BLEND_NODE:
     case GSK_REPEAT_NODE:
@@ -1426,7 +1575,7 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
         case OP_CHANGE_COLOR:
           OP_PRINT (" -> Color: (%f, %f, %f, %f)", op->color.red, op->color.green, op->color.blue, 
op->color.alpha);
           g_assert (program == &self->color_program || program == &self->coloring_program ||
-                    program == &self->shadow_program);
+                    program == &self->shadow_program || program == &self->border_program);
           /* TODO: We use color.color_location here and this is right for all three of the programs above,
            *       but that's just a coincidence. */
           glUniform4f (program->color.color_location,
@@ -1503,6 +1652,11 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
           glUniform4fv (program->outset_shadow.corner_heights_location, 1, op->outset_shadow.corner_heights);
           break;
 
+        case OP_CHANGE_BORDER:
+          g_assert (program == &self->border_program);
+          glUniform4fv (program->border.widths_location, 1, op->border.widths);
+          break;
+
         case OP_DRAW:
           OP_PRINT (" -> draw %ld, size %ld and program %s\n",
                     op->draw.vao_offset, op->draw.vao_size, program->name);
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index ae0ebcf..dfa7bac 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -10,7 +10,7 @@
 #include "gskglrendererprivate.h"
 
 #define GL_N_VERTICES 6
-#define GL_N_PROGRAMS 10
+#define GL_N_PROGRAMS 11
 
 enum {
   OP_NONE,
@@ -29,8 +29,9 @@ enum {
   OP_CHANGE_BLUR            =  13,
   OP_CHANGE_INSET_SHADOW    =  14,
   OP_CHANGE_OUTSET_SHADOW   =  15,
-  OP_CLEAR                  =  16,
-  OP_DRAW                   =  17,
+  OP_CHANGE_BORDER          =  16,
+  OP_CLEAR                  =  17,
+  OP_DRAW                   =  18,
 };
 
 typedef struct
@@ -93,6 +94,10 @@ typedef struct
       int corner_widths_location;
       int corner_heights_location;
     } outset_shadow;
+    struct {
+      int color_location;
+      int widths_location;
+    } border;
   };
 
 } Program;
@@ -152,6 +157,9 @@ typedef struct
     struct {
       float color[4];
     } shadow;
+    struct {
+      float widths[4];
+    } border;
   };
 } RenderOp;
 
diff --git a/gsk/meson.build b/gsk/meson.build
index 54828e7..47ac6a4 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -12,6 +12,8 @@ gsk_private_source_shaders = [
   'resources/glsl/inset_shadow.fs.glsl',
   'resources/glsl/outset_shadow.fs.glsl',
   'resources/glsl/shadow.fs.glsl',
+  'resources/glsl/border.vs.glsl',
+  'resources/glsl/border.fs.glsl',
   'resources/glsl/es2_common.fs.glsl',
   'resources/glsl/es2_common.vs.glsl',
   'resources/glsl/gl3_common.fs.glsl',
diff --git a/gsk/resources/glsl/border.fs.glsl b/gsk/resources/glsl/border.fs.glsl
new file mode 100644
index 0000000..f605de2
--- /dev/null
+++ b/gsk/resources/glsl/border.fs.glsl
@@ -0,0 +1,32 @@
+uniform vec4 u_color = vec4(1, 0, 1, 1);
+uniform vec4 u_widths;
+
+// For border we abuse, ehm, re-use, the global clip
+// as rounded rect and shrink it by u_widths
+void main() {
+  vec4 bounds = u_clip;
+  vec4 f = gl_FragCoord;
+
+
+  f.x += u_viewport.x;
+  f.y = (u_viewport.y + u_viewport.w) - f.y;
+
+  bounds.z = bounds.x + bounds.z;
+  bounds.w = bounds.y + bounds.w;
+
+  vec4 corner_widths = max (u_clip_corner_widths, u_widths.wyyw);
+  vec4 corner_heights = max (u_clip_corner_heights, u_widths.xxzz);
+
+  RoundedRect routside = RoundedRect (bounds, corner_widths, corner_heights);
+  RoundedRect rinside = rounded_rect_shrink (routside, u_widths);
+
+  float alpha = clamp (rounded_rect_coverage (routside, f.xy) -
+                       rounded_rect_coverage (rinside, f.xy),
+                       0.0, 1.0);
+
+  /* Pre-multiply */
+  vec4 color = u_color;
+  color.rgb *= color.a;
+
+  setOutputColor (color * alpha * u_alpha);
+}


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