diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 0142e86361..b8883d921c 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -130,6 +130,7 @@ struct _GskGLRenderer Program coloring_program; Program color_matrix_program; Program linear_gradient_program; + Program blur_program; }; }; @@ -371,6 +372,22 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, start_point_location, "uStartPoint"); INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, end_point_location, "uEndPoint"); + self->blur_program.id = gsk_shader_builder_create_program (builder, + "blur.vs.glsl", "blur.fs.glsl", + &shader_error); + if (shader_error != NULL) + { + g_propagate_prefixed_error (error, + shader_error, + "Unable to create 'blur' program: "); + goto out; + } + self->blur_program.index = 6; + self->blur_program.name = "blur"; + init_common_locations (self, builder, &self->blur_program); + INIT_PROGRAM_UNIFORM_LOCATION (blur_program, blur_radius_location, "uBlurRadius"); + INIT_PROGRAM_UNIFORM_LOCATION (blur_program, blur_size_location, "uSize"); + res = TRUE; out: @@ -950,11 +967,48 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, } break; + case GSK_BLUR_NODE: + { + int texture_id; + gboolean is_offscreen; + RenderOp op; + add_offscreen_ops (self, builder, min_x, max_x, min_y, max_y, + gsk_blur_node_get_child (node), + &texture_id, &is_offscreen); + + ops_set_program (builder, &self->blur_program); + op.op = OP_CHANGE_BLUR; + graphene_size_init_from_size (&op.blur.size, &node->bounds.size); + op.blur.radius = gsk_blur_node_get_radius (node); + ops_add (builder, &op); + + ops_set_texture (builder, texture_id); + + if (is_offscreen) + { + 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 }, }, + }; + + ops_draw (builder, vertex_data); + } + else + { + ops_draw (builder, vertex_data); + } + } + break; + case GSK_REPEATING_LINEAR_GRADIENT_NODE: case GSK_BORDER_NODE: case GSK_INSET_SHADOW_NODE: case GSK_OUTSET_SHADOW_NODE: - case GSK_BLUR_NODE: case GSK_SHADOW_NODE: case GSK_CROSS_FADE_NODE: case GSK_BLEND_NODE: @@ -1245,6 +1299,12 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self, op->linear_gradient.end_point.x, op->linear_gradient.end_point.y); break; + case OP_CHANGE_BLUR: + g_assert (program == &self->blur_program); + glUniform1f (program->blur_radius_location, op->blur.radius); + glUniform2f (program->blur_size_location, op->blur.size.width, op->blur.size.height); + 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 c3958b9bc4..f324c83607 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 6 +#define GL_N_PROGRAMS 7 enum { OP_NONE, @@ -26,8 +26,9 @@ enum { OP_CHANGE_VAO = 10, OP_CHANGE_LINEAR_GRADIENT = 11, OP_CHANGE_COLOR_MATRIX = 12, - OP_CLEAR = 13, - OP_DRAW = 14, + OP_CHANGE_BLUR = 13, + OP_CLEAR = 14, + OP_DRAW = 15, }; typedef struct @@ -71,6 +72,10 @@ typedef struct int corner_widths_location; int corner_heights_location; }; + struct { + int blur_radius_location; + int blur_size_location; + }; }; } Program; @@ -104,6 +109,10 @@ typedef struct graphene_matrix_t matrix; graphene_vec4_t offset; } color_matrix; + struct { + float radius; + graphene_size_t size; + } blur; }; } RenderOp; diff --git a/gsk/meson.build b/gsk/meson.build index c544a6c82f..548c1937d2 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -7,6 +7,8 @@ gsk_private_source_shaders = [ 'resources/glsl/coloring.fs.glsl', 'resources/glsl/color_matrix.fs.glsl', 'resources/glsl/linear_gradient.fs.glsl', + 'resources/glsl/blur.vs.glsl', + 'resources/glsl/blur.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/blur.fs.glsl b/gsk/resources/glsl/blur.fs.glsl new file mode 100644 index 0000000000..16c2b6f52b --- /dev/null +++ b/gsk/resources/glsl/blur.fs.glsl @@ -0,0 +1,42 @@ + +uniform float uBlurRadius = 4.0; +uniform vec2 uSize; + +const int samples_x = 15; // must be odd +const int samples_y = 15; // must be odd + +const int half_samples_x = samples_x / 2; +const int half_samples_y = samples_y / 2; + +float Gaussian (float sigma, float x) +{ + return exp ( - (x * x) / (2.0 * sigma * sigma)); +} + +vec4 blur_pixel (in vec2 uv) +{ + float total = 0.0; + vec4 ret = vec4 (0); + float pixel_size_x = (1.0 / uSize.x); + float pixel_size_y = (1.0 / uSize.y); + + for (int y = 0; y < samples_y; ++y) + { + float fy = Gaussian (uBlurRadius, float(y) - float(half_samples_x)); + float offset_y = float(y - half_samples_y) * pixel_size_y; + for (int x = 0; x < samples_x; ++x) + { + float fx = Gaussian (uBlurRadius, float(x) - float(half_samples_x)); + float offset_x = float(x - half_samples_x) * pixel_size_x; + total += fx * fy; + ret += Texture(uSource, uv + vec2(offset_x, offset_y)) * fx * fy; + } + } + return ret / total; +} + +void main() +{ + /*color = clip (inPos, blur_pixel (inTexCoord));*/ + setOutputColor(blur_pixel(vUv)); +} diff --git a/gsk/resources/glsl/blur.vs.glsl b/gsk/resources/glsl/blur.vs.glsl new file mode 100644 index 0000000000..fe52014558 --- /dev/null +++ b/gsk/resources/glsl/blur.vs.glsl @@ -0,0 +1,9 @@ + +out vec2 size; +void main() { + gl_Position = uProjection * uModelview * vec4(aPosition, 0.0, 1.0); + + + vUv = vec2(aUv.x, aUv.y); + /*size = vec2(aPosition.z - aPosition.z, aPosition.y - aPosition.w);*/ +}