[gtk/wip/baedert/nodeeditor: 35/35] gl renderer: Add simple blend node implementation



commit 2b5c49fb58c7a860fd0b1512df58f2f95595976b
Author: Timm Bäder <mail baedert org>
Date:   Sun Apr 28 07:46:17 2019 +0200

    gl renderer: Add simple blend node implementation

 gsk/gl/gskglrenderer.c           |  76 ++++++++++-
 gsk/gl/gskglrenderopsprivate.h   |  11 +-
 gsk/meson.build                  |   1 +
 gsk/resources/glsl/blend.fs.glsl | 287 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 373 insertions(+), 2 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 2ec0e40823..93a9765c35 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -326,6 +326,7 @@ struct _GskGLRenderer
       Program unblurred_outset_shadow_program;
       Program border_program;
       Program cross_fade_program;
+      Program blend_program;
     };
   };
 
@@ -1900,6 +1901,54 @@ render_cross_fade_node (GskGLRenderer       *self,
   ops_draw (builder, vertex_data);
 }
 
+static inline void
+render_blend_node (GskGLRenderer   *self,
+                   GskRenderNode   *node,
+                   RenderOpBuilder *builder)
+{
+  GskRenderNode *top_child = gsk_blend_node_get_top_child (node);
+  GskRenderNode *bottom_child = gsk_blend_node_get_bottom_child (node);
+  const float min_x = builder->dx + node->bounds.origin.x;
+  const float min_y = builder->dy + node->bounds.origin.y;
+  const float max_x = min_x + node->bounds.size.width;
+  const float max_y = min_y + node->bounds.size.height;
+  int top_texture_id;
+  int bottom_texture_id;
+  gboolean is_offscreen1, is_offscreen2;
+  RenderOp op;
+  const GskQuadVertex vertex_data[GL_N_VERTICES] = {
+    { { min_x, min_y }, { 0, 1 }, },
+    { { min_x, max_y }, { 0, 0 }, },
+    { { max_x, min_y }, { 1, 1 }, },
+
+    { { max_x, max_y }, { 1, 0 }, },
+    { { min_x, max_y }, { 0, 0 }, },
+    { { max_x, min_y }, { 1, 1 }, },
+  };
+
+  /* TODO: We create 2 textures here as big as the blend node, but both the
+   * start and the end node might be a lot smaller than that. */
+  add_offscreen_ops (self, builder,
+                     &node->bounds,
+                     bottom_child,
+                     &bottom_texture_id, &is_offscreen1,
+                     FORCE_OFFSCREEN | RESET_CLIP);
+
+  add_offscreen_ops (self, builder,
+                     &node->bounds,
+                     top_child,
+                     &top_texture_id, &is_offscreen2,
+                     FORCE_OFFSCREEN | RESET_CLIP);
+
+  ops_set_program (builder, &self->blend_program);
+  ops_set_texture (builder, bottom_texture_id);
+  op.op = OP_CHANGE_BLEND;
+  op.blend.source2 = top_texture_id;
+  op.blend.mode = gsk_blend_node_get_blend_mode (node);
+  ops_add (builder, &op);
+  ops_draw (builder, vertex_data);
+}
+
 static inline void
 apply_viewport_op (const Program  *program,
                    const RenderOp *op)
@@ -2174,6 +2223,18 @@ apply_cross_fade_op (const Program  *program,
   glUniform1f (program->cross_fade.progress_location, op->cross_fade.progress);
 }
 
+static inline void
+apply_blend_op (const Program  *program,
+                const RenderOp *op)
+{
+  /* End texture id */
+  glUniform1i (program->blend.source2_location, 1);
+  glActiveTexture (GL_TEXTURE0 + 1);
+  glBindTexture (GL_TEXTURE_2D, op->blend.source2);
+  /* progress */
+  glUniform1i (program->blend.mode_location, op->blend.mode);
+}
+
 static void
 gsk_gl_renderer_dispose (GObject *gobject)
 {
@@ -2206,6 +2267,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
     { "unblurred outset shadow",   "unblurred_outset_shadow.fs.glsl" },
     { "border",          "border.fs.glsl" },
     { "cross fade",      "cross_fade.fs.glsl" },
+    { "blend",           "blend.fs.glsl" },
   };
 
   builder = gsk_shader_builder_new ();
@@ -2336,6 +2398,10 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
   INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, progress);
   INIT_PROGRAM_UNIFORM_LOCATION (cross_fade, source2);
 
+  /* blend */
+  INIT_PROGRAM_UNIFORM_LOCATION (blend, source2);
+  INIT_PROGRAM_UNIFORM_LOCATION (blend, mode);
+
   g_object_unref (builder);
   return TRUE;
 }
@@ -2588,8 +2654,11 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer   *self,
       render_cross_fade_node (self, node, builder);
     break;
 
-    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_BLEND_NODE:
+      render_blend_node (self, node, builder);
+    break;
+
+    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_REPEAT_NODE:
     case GSK_CAIRO_NODE:
     default:
@@ -2855,6 +2924,11 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
           apply_cross_fade_op (program, op);
           break;
 
+        case OP_CHANGE_BLEND:
+          g_assert (program == &self->blend_program);
+          apply_blend_op (program, op);
+          break;
+
         case OP_CHANGE_LINEAR_GRADIENT:
           apply_linear_gradient_op (program, op);
           break;
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index 995188fe12..20765a62c7 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -11,7 +11,7 @@
 #include "gskrendernodeprivate.h"
 
 #define GL_N_VERTICES 6
-#define GL_N_PROGRAMS 11
+#define GL_N_PROGRAMS 12
 
 
 
@@ -61,6 +61,7 @@ enum {
   OP_DUMP_FRAMEBUFFER       =  23,
   OP_PUSH_DEBUG_GROUP       =  24,
   OP_POP_DEBUG_GROUP        =  25,
+  OP_CHANGE_BLEND           =  26,
 };
 
 typedef struct
@@ -136,6 +137,10 @@ typedef struct
       int source2_location;
       int progress_location;
     } cross_fade;
+    struct {
+      int source2_location;
+      int mode_location;
+    } blend;
   };
 
 } Program;
@@ -214,6 +219,10 @@ typedef struct
       float progress;
       int source2;
     } cross_fade;
+    struct {
+      int source2;
+      int mode;
+    } blend;
     struct {
       char *filename;
       int width;
diff --git a/gsk/meson.build b/gsk/meson.build
index bd0830b7cf..73806fea27 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -11,6 +11,7 @@ gsk_private_gl_shaders = [
   'resources/glsl/unblurred_outset_shadow.fs.glsl',
   'resources/glsl/border.fs.glsl',
   'resources/glsl/cross_fade.fs.glsl',
+  'resources/glsl/blend.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/blend.fs.glsl b/gsk/resources/glsl/blend.fs.glsl
new file mode 100644
index 0000000000..c762410dcb
--- /dev/null
+++ b/gsk/resources/glsl/blend.fs.glsl
@@ -0,0 +1,287 @@
+uniform int u_mode;
+uniform sampler2D u_source2;
+
+float
+combine (float source, float backdrop)
+{
+  return source + backdrop * (1 - source);
+}
+
+vec4
+composite (vec4 Cs, vec4 Cb, vec3 B)
+{
+  float ao = Cs.a + Cb.a * (1 - Cs.a);
+  vec3 Co = (Cs.a*(1 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1 - Cs.a)*Cb.a*Cb.rgb) / ao;
+  return vec4(Co, ao);
+}
+
+vec4
+normal (vec4 Cs, vec4 Cb)
+{
+  return composite (Cs, Cb, Cs.rgb);
+}
+
+vec4
+multiply (vec4 Cs, vec4 Cb)
+{
+  return composite (Cs, Cb, Cs.rgb * Cb.rgb);
+}
+
+vec4
+difference (vec4 Cs, vec4 Cb)
+{
+  return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb));
+}
+
+vec4
+screen (vec4 Cs, vec4 Cb)
+{
+  return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb);
+}
+
+float
+hard_light (float source, float backdrop)
+{
+  if (source <= 0.5)
+    return 2 * backdrop * source;
+  else
+    return 2 * (backdrop + source - backdrop * source) - 1;
+}
+
+vec4
+hard_light (vec4 Cs, vec4 Cb)
+{
+  vec3 B = vec3 (hard_light (Cs.r, Cb.r),
+                 hard_light (Cs.g, Cb.g),
+                 hard_light (Cs.b, Cb.b));
+  return composite (Cs, Cb, B);
+}
+
+float
+soft_light (float source, float backdrop)
+{
+  float db;
+
+  if (backdrop <= 0.25)
+    db = ((16 * backdrop - 12) * backdrop + 4) * backdrop;
+  else
+    db = sqrt (backdrop);
+
+  if (source <= 0.5)
+    return backdrop - (1 - 2 * source) * backdrop * (1 - backdrop);
+  else
+    return backdrop + (2 * source - 1) * (db - backdrop);
+}
+
+vec4
+soft_light (vec4 Cs, vec4 Cb)
+{
+  vec3 B = vec3 (soft_light (Cs.r, Cb.r),
+                 soft_light (Cs.g, Cb.g),
+                 soft_light (Cs.b, Cb.b));
+  return composite (Cs, Cb, B);
+}
+
+vec4
+overlay (vec4 Cs, vec4 Cb)
+{
+  vec3 B = vec3 (hard_light (Cb.r, Cs.r),
+                 hard_light (Cb.g, Cs.g),
+                 hard_light (Cb.b, Cs.b));
+  return composite (Cs, Cb, B);
+}
+
+vec4
+darken (vec4 Cs, vec4 Cb)
+{
+  vec3 B = min (Cs.rgb, Cb.rgb);
+  return composite (Cs, Cb, B);
+}
+
+vec4
+lighten (vec4 Cs, vec4 Cb)
+{
+  vec3 B = max (Cs.rgb, Cb.rgb);
+  return composite (Cs, Cb, B);
+}
+
+float
+color_dodge (float source, float backdrop)
+{
+  return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0);
+}
+
+vec4
+color_dodge (vec4 Cs, vec4 Cb)
+{
+  vec3 B = vec3 (color_dodge (Cs.r, Cb.r),
+                 color_dodge (Cs.g, Cb.g),
+                 color_dodge (Cs.b, Cb.b));
+  return composite (Cs, Cb, B);
+}
+
+
+float
+color_burn (float source, float backdrop)
+{
+  return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0);
+}
+
+vec4
+color_burn (vec4 Cs, vec4 Cb)
+{
+  vec3 B = vec3 (color_burn (Cs.r, Cb.r),
+                 color_burn (Cs.g, Cb.g),
+                 color_burn (Cs.b, Cb.b));
+  return composite (Cs, Cb, B);
+}
+
+vec4
+exclusion (vec4 Cs, vec4 Cb)
+{
+  vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb;
+  return composite (Cs, Cb, B);
+}
+
+float
+lum (vec3 c)
+{
+  return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b;
+}
+
+vec3
+clip_color (vec3 c)
+{
+  float l = lum (c);
+  float n = min (c.r, min (c.g, c.b));
+  float x = max (c.r, max (c.g, c.b));
+  if (n < 0) c = l + (((c - l) * l) / (l - n));
+  if (x > 1) c = l + (((c - l) * (1 - l)) / (x - l));
+  return c;
+}
+
+vec3
+set_lum (vec3 c, float l)
+{
+  float d = l - lum (c);
+  return clip_color (vec3 (c.r + d, c.g + d, c.b + d));
+}
+
+float
+sat (vec3 c)
+{
+  return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b));
+}
+
+vec3
+set_sat (vec3 c, float s)
+{
+  float cmin = min (c.r, min (c.g, c.b));
+  float cmax = max (c.r, max (c.g, c.b));
+  vec3 res;
+
+  if (cmax == cmin)
+    res = vec3 (0, 0, 0);
+  else
+    {
+      if (c.r == cmax)
+        {
+          if (c.g == cmin)
+            {
+              res.b = ((c.b - cmin) * s) / (cmax - cmin);
+              res.g = 0;
+            }
+          else
+            {
+              res.g = ((c.g - cmin) * s) / (cmax - cmin);
+              res.b = 0;
+            }
+          res.r = s;
+        }
+      else if (c.g == cmax)
+        {
+          if (c.r == cmin)
+            {
+              res.b = ((c.b - cmin) * s) / (cmax - cmin);
+              res.r = 0;
+            }
+          else
+            {
+              res.r = ((c.r - cmin) * s) / (cmax - cmin);
+              res.b = 0;
+            }
+          res.g = s;
+        }
+      else
+        {
+          if (c.r == cmin)
+            {
+              res.g = ((c.g - cmin) * s) / (cmax - cmin);
+              res.r = 0;
+            }
+          else
+            {
+              res.r = ((c.r - cmin) * s) / (cmax - cmin);
+              res.g = 0;
+            }
+          res.b = s;
+        }
+    }
+  return res;
+}
+
+vec4
+color (vec4 Cs, vec4 Cb)
+{
+  vec3 B = set_lum (Cs.rgb, lum (Cb.rgb));
+  return composite (Cs, Cb, B);
+}
+
+vec4
+hue (vec4 Cs, vec4 Cb)
+{
+  vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb));
+  return composite (Cs, Cb, B);
+}
+
+vec4
+saturation (vec4 Cs, vec4 Cb)
+{
+  vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb));
+  return composite (Cs, Cb, B);
+}
+
+vec4
+luminosity (vec4 Cs, vec4 Cb)
+{
+  vec3 B = set_lum (Cb.rgb, lum (Cs.rgb));
+  return composite (Cs, Cb, B);
+}
+
+void main() {
+  vec4 bottom_color = Texture(u_source, vUv);
+  vec4 top_color = Texture(u_source2, vUv);
+
+  vec4 result;
+  switch(u_mode) {
+    case 0:  result = normal(bottom_color, top_color);      break;
+    case 1:  result = multiply(bottom_color, top_color);    break;
+    case 2:  result = screen(bottom_color, top_color);      break;
+    case 3:  result = overlay(bottom_color, top_color);     break;
+    case 4:  result = darken(bottom_color, top_color);      break;
+    case 5:  result = lighten(bottom_color, top_color);     break;
+    case 6:  result = color_dodge(bottom_color, top_color); break;
+    case 7:  result = color_burn(bottom_color, top_color);  break;
+    case 8:  result = hard_light(bottom_color, top_color);  break;
+    case 9:  result = soft_light(bottom_color, top_color);  break;
+    case 10: result = difference(bottom_color, top_color);  break;
+    case 11: result = exclusion(bottom_color, top_color);   break;
+    case 12: result = color(bottom_color, top_color);       break;
+    case 13: result = hue(bottom_color, top_color);         break;
+    case 14: result = saturation(bottom_color, top_color);  break;
+    case 15: result = luminosity(bottom_color, top_color);  break;
+    default: discard;
+  }
+
+  setOutputColor(result * u_alpha);
+}


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