summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimm Bäder <mail@baedert.org>2019-04-28 07:46:17 +0200
committerTimm Bäder <mail@baedert.org>2019-04-28 07:46:17 +0200
commit2b5c49fb58c7a860fd0b1512df58f2f95595976b (patch)
treeb6902a39641445f51e73fd73a4a8083d61eef5f9
parentc0791ca38ebc1982545aee5d15caa07f66d045f4 (diff)
downloadgtk+-wip/baedert/nodeeditor.tar.gz
gl renderer: Add simple blend node implementationwip/baedert/nodeeditor
-rw-r--r--gsk/gl/gskglrenderer.c76
-rw-r--r--gsk/gl/gskglrenderopsprivate.h11
-rw-r--r--gsk/meson.build1
-rw-r--r--gsk/resources/glsl/blend.fs.glsl287
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;
};
};
@@ -1901,6 +1902,54 @@ render_cross_fade_node (GskGLRenderer *self,
}
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;
@@ -215,6 +220,10 @@ typedef struct
int source2;
} cross_fade;
struct {
+ int source2;
+ int mode;
+ } blend;
+ struct {
char *filename;
int width;
int height;
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);
+}