From c848b9014b73c7295da58eeda5497e40a7647178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Sun, 28 Apr 2019 07:46:17 +0200 Subject: [PATCH] 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(-) create mode 100644 gsk/resources/glsl/blend.fs.glsl diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 00351cdbca..cfe0e02648 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 912c5ed0f8..f06aae02fb 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); +}