From 04b657b58cf54ac611f73723ec44f51feba16b15 Mon Sep 17 00:00:00 2001 From: ozwaldorf Date: Wed, 3 Jan 2024 12:38:44 -0500 Subject: [PATCH] feat: blur tweaks (#258) --- README.md | 4 ++ include/sway/commands.h | 4 ++ include/sway/config.h | 4 ++ .../sway/desktop/fx_renderer/fx_renderer.h | 21 ++++++- sway/commands.c | 4 ++ sway/commands/blur_brightness.c | 28 +++++++++ sway/commands/blur_contrast.c | 28 +++++++++ sway/commands/blur_noise.c | 28 +++++++++ sway/commands/blur_saturation.c | 29 +++++++++ sway/config.c | 4 ++ sway/desktop/fx_renderer/fx_renderer.c | 61 +++++++++++++++++++ .../fx_renderer/shaders/blur_effects.frag | 54 ++++++++++++++++ sway/desktop/fx_renderer/shaders/meson.build | 1 + sway/desktop/render.c | 17 ++++++ sway/meson.build | 4 ++ sway/sway.5.scd | 16 +++++ 16 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 sway/commands/blur_brightness.c create mode 100644 sway/commands/blur_contrast.c create mode 100644 sway/commands/blur_noise.c create mode 100644 sway/commands/blur_saturation.c create mode 100644 sway/desktop/fx_renderer/shaders/blur_effects.frag diff --git a/README.md b/README.md index 0941944e..01a777da 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ Sway is an incredible window manager, and certainly one of the most well establi - `blur_xray enable|disable`: this will set floating windows to blur based on the background, not the windows below. You probably want to set this to `disable` :) - `blur_passes ` - `blur_radius ` + - `blur_noise ` (**Note**: git only, percentage of noise to add) + - `blur_brightness ` (**Note**: git only, percentage of original brightness to adjust) + - `blur_contrast ` (**Note**: git only, percentage of original contrast to adjust) + - `blur_saturation ` (**Note**: git only, percentage of original saturation to adjust) + Corner radius: `corner_radius ` + Window shadows: - `shadows enable|disable` diff --git a/include/sway/commands.h b/include/sway/commands.h index c29d4ff4..b8f5660a 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -115,8 +115,12 @@ sway_cmd cmd_bindgesture; sway_cmd cmd_bindswitch; sway_cmd cmd_bindsym; sway_cmd cmd_blur; +sway_cmd cmd_blur_brightness; +sway_cmd cmd_blur_contrast; +sway_cmd cmd_blur_noise; sway_cmd cmd_blur_passes; sway_cmd cmd_blur_radius; +sway_cmd cmd_blur_saturation; sway_cmd cmd_blur_xray; sway_cmd cmd_border; sway_cmd cmd_client_noop; diff --git a/include/sway/config.h b/include/sway/config.h index 81213dbc..2bb33352 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -473,6 +473,10 @@ enum xwayland_mode { struct blur_parameters { int num_passes; int radius; + float noise; + float brightness; + float contrast; + float saturation; }; /** diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h index bfae7b72..924d9951 100644 --- a/include/sway/desktop/fx_renderer/fx_renderer.h +++ b/include/sway/desktop/fx_renderer/fx_renderer.h @@ -47,6 +47,18 @@ struct blur_shader { GLint halfpixel; }; +struct effects_shader { + GLuint program; + GLint proj; + GLint tex; + GLint pos_attrib; + GLint tex_attrib; + GLfloat noise; + GLfloat brightness; + GLfloat contrast; + GLfloat saturation; +}; + struct box_shadow_shader { GLuint program; GLint proj; @@ -153,6 +165,7 @@ struct fx_renderer { struct box_shadow_shader box_shadow; struct blur_shader blur1; struct blur_shader blur2; + struct effects_shader blur_effects; struct corner_shader corner; struct quad_shader quad; struct rounded_quad_shader rounded_quad; @@ -211,7 +224,11 @@ void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *bo const float matrix[static 9], int corner_radius, float blur_sigma); void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, struct blur_shader *shader, const struct wlr_box *box, - int blur_radius); + struct fx_framebuffer **buffer, struct blur_shader *shader, + const struct wlr_box *box, int blur_radius); + +void fx_render_blur_effects(struct fx_renderer *renderer, const float matrix[static 9], + struct fx_framebuffer **buffer, float blur_noise, float blur_brightness, + float blur_contrast, float blur_saturation); #endif diff --git a/sway/commands.c b/sway/commands.c index 75c6c1e5..5ce714e6 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -50,8 +50,12 @@ static const struct cmd_handler handlers[] = { { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "blur", cmd_blur }, + { "blur_brightness", cmd_blur_brightness }, + { "blur_contrast", cmd_blur_contrast }, + { "blur_noise", cmd_blur_noise }, { "blur_passes", cmd_blur_passes }, { "blur_radius", cmd_blur_radius }, + { "blur_saturation", cmd_blur_saturation }, { "blur_xray", cmd_blur_xray }, { "client.background", cmd_client_noop }, { "client.focused", cmd_client_focused }, diff --git a/sway/commands/blur_brightness.c b/sway/commands/blur_brightness.c new file mode 100644 index 00000000..79a40266 --- /dev/null +++ b/sway/commands/blur_brightness.c @@ -0,0 +1,28 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *cmd_blur_brightness(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "blur_brightness", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + char *inv; + float value = strtof(argv[0], &inv); + if (*inv != '\0' || value < 0 || value > 2) { + return cmd_results_new(CMD_FAILURE, "Invalid brightness specified"); + } + + config->blur_params.brightness = value; + + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + if (output->renderer) { + output->renderer->blur_buffer_dirty = true; + output_damage_whole(output); + } + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/blur_contrast.c b/sway/commands/blur_contrast.c new file mode 100644 index 00000000..61e3aec4 --- /dev/null +++ b/sway/commands/blur_contrast.c @@ -0,0 +1,28 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *cmd_blur_contrast(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "blur_contrast", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + char *inv; + float value = strtof(argv[0], &inv); + if (*inv != '\0' || value < 0 || value > 2) { + return cmd_results_new(CMD_FAILURE, "Invalid brightness specified"); + } + + config->blur_params.contrast = value; + + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + if (output->renderer) { + output->renderer->blur_buffer_dirty = true; + output_damage_whole(output); + } + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/blur_noise.c b/sway/commands/blur_noise.c new file mode 100644 index 00000000..bd737911 --- /dev/null +++ b/sway/commands/blur_noise.c @@ -0,0 +1,28 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *cmd_blur_noise(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "blur_noise", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + char *inv; + float value = strtof(argv[0], &inv); + if (*inv != '\0' || value < 0 || value > 1) { + return cmd_results_new(CMD_FAILURE, "Invalid noise specified"); + } + + config->blur_params.noise = value; + + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + if (output->renderer) { + output->renderer->blur_buffer_dirty = true; + output_damage_whole(output); + } + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/blur_saturation.c b/sway/commands/blur_saturation.c new file mode 100644 index 00000000..35627363 --- /dev/null +++ b/sway/commands/blur_saturation.c @@ -0,0 +1,29 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *cmd_blur_saturation(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "blur_saturation", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + char *inv; + float value = strtof(argv[0], &inv); + if (*inv != '\0' || value < 0 || value > 2) { + return cmd_results_new(CMD_FAILURE, "Invalid saturation specified"); + } + + config->blur_params.saturation = value; + + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + if (output->renderer) { + output->renderer->blur_buffer_dirty = true; + output_damage_whole(output); + } + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + diff --git a/sway/config.c b/sway/config.c index db395475..58d243e2 100644 --- a/sway/config.c +++ b/sway/config.c @@ -360,6 +360,10 @@ static void config_defaults(struct sway_config *config) { config->blur_xray = false; config->blur_params.num_passes = 2; config->blur_params.radius = 5; + config->blur_params.noise = 0.02; + config->blur_params.brightness = 0.9; + config->blur_params.contrast = 0.9; + config->blur_params.saturation = 1.1; config->titlebar_separator = true; config->scratchpad_minimize = false; diff --git a/sway/desktop/fx_renderer/fx_renderer.c b/sway/desktop/fx_renderer/fx_renderer.c index 7196bb0e..089e1693 100644 --- a/sway/desktop/fx_renderer/fx_renderer.c +++ b/sway/desktop/fx_renderer/fx_renderer.c @@ -23,6 +23,7 @@ // shaders #include "blur1_frag_src.h" #include "blur2_frag_src.h" +#include "blur_effects_frag_src.h" #include "box_shadow_frag_src.h" #include "common_vert_src.h" #include "corner_frag_src.h" @@ -107,6 +108,25 @@ static bool link_blur_program(struct blur_shader *shader, const char *shader_pro return true; } +static bool link_blur_effects_program(struct effects_shader *shader, const char *shader_program) { + GLuint prog; + shader->program = prog = link_program(shader_program); + if (!shader->program) { + return false; + } + shader->proj = glGetUniformLocation(prog, "proj"); + shader->tex = glGetUniformLocation(prog, "tex"); + shader->pos_attrib = glGetAttribLocation(prog, "pos"); + shader->tex_attrib = glGetAttribLocation(prog, "texcoord"); + shader->noise = glGetUniformLocation(prog, "noise"); + shader->brightness = glGetUniformLocation(prog, "brightness"); + shader->contrast = glGetUniformLocation(prog, "contrast"); + shader->saturation = glGetUniformLocation(prog, "saturation"); + + return true; + +} + static bool link_box_shadow_program(struct box_shadow_shader *shader) { GLuint prog; shader->program = prog = link_program(box_shadow_frag_src); @@ -305,6 +325,10 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *w if (!link_blur_program(&renderer->shaders.blur2, blur2_frag_src)) { goto error; } + // effects shader + if (!link_blur_effects_program(&renderer->shaders.blur_effects, blur_effects_frag_src)) { + goto error; + } // box shadow shader if (!link_box_shadow_program(&renderer->shaders.box_shadow)) { goto error; @@ -365,6 +389,7 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *w error: glDeleteProgram(renderer->shaders.blur1.program); glDeleteProgram(renderer->shaders.blur2.program); + glDeleteProgram(renderer->shaders.blur_effects.program); glDeleteProgram(renderer->shaders.box_shadow.program); glDeleteProgram(renderer->shaders.corner.program); glDeleteProgram(renderer->shaders.quad.program); @@ -899,3 +924,39 @@ void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9], glDisableVertexAttribArray(shader->tex_attrib); } + +void fx_render_blur_effects(struct fx_renderer *renderer, const float matrix[static 9], + struct fx_framebuffer **buffer, float blur_noise, float blur_brightness, + float blur_contrast, float blur_saturation) { + struct effects_shader shader = renderer->shaders.blur_effects; + + glEnable(GL_BLEND); + glActiveTexture(GL_TEXTURE0); + glBindTexture((*buffer)->texture.target, (*buffer)->texture.id); + glTexParameteri((*buffer)->texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glUseProgram(shader.program); + + // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set + // to GL_FALSE + float gl_matrix[9]; + wlr_matrix_transpose(gl_matrix, matrix); + glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); + + glUniform1i(shader.tex, 0); + glUniform1f(shader.noise, blur_noise); + glUniform1f(shader.brightness, blur_brightness); + glUniform1f(shader.contrast, blur_contrast); + glUniform1f(shader.saturation, blur_saturation); + + glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); + glVertexAttribPointer(shader.tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); + + glEnableVertexAttribArray(shader.pos_attrib); + glEnableVertexAttribArray(shader.tex_attrib); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glDisableVertexAttribArray(shader.pos_attrib); + glDisableVertexAttribArray(shader.tex_attrib); +} diff --git a/sway/desktop/fx_renderer/shaders/blur_effects.frag b/sway/desktop/fx_renderer/shaders/blur_effects.frag new file mode 100644 index 00000000..2fc16c15 --- /dev/null +++ b/sway/desktop/fx_renderer/shaders/blur_effects.frag @@ -0,0 +1,54 @@ +precision mediump float; +varying vec2 v_texcoord; +uniform sampler2D tex; + +uniform float noise; +uniform float brightness; +uniform float contrast; +uniform float saturation; + +mat4 brightnessMatrix() { + float b = brightness - 1.0; + return mat4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + b, b, b, 1); +} + +mat4 contrastMatrix() { + float t = (1.0 - contrast) / 2.0; + return mat4(contrast, 0, 0, 0, + 0, contrast, 0, 0, + 0, 0, contrast, 0, + t, t, t, 1); +} + +mat4 saturationMatrix() { + vec3 luminance = vec3(0.3086, 0.6094, 0.0820); + float oneMinusSat = 1.0 - saturation; + vec3 red = vec3(luminance.x * oneMinusSat); + red+= vec3(saturation, 0, 0); + vec3 green = vec3(luminance.y * oneMinusSat); + green += vec3(0, saturation, 0); + vec3 blue = vec3(luminance.z * oneMinusSat); + blue += vec3(0, 0, saturation); + return mat4(red, 0, + green, 0, + blue, 0, + 0, 0, 0, 1); +} + +// Fast generative noise function +float hash(vec2 p) { + return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); +} + +void main() { + vec4 color = texture2D(tex, v_texcoord); + color *= brightnessMatrix() * contrastMatrix() * saturationMatrix(); + float noiseHash = hash(v_texcoord); + float noiseAmount = (mod(noiseHash, 1.0) - 0.5); + color.rgb += noiseAmount * noise; + + gl_FragColor = color; +} diff --git a/sway/desktop/fx_renderer/shaders/meson.build b/sway/desktop/fx_renderer/shaders/meson.build index 7a27de80..19f76dc2 100644 --- a/sway/desktop/fx_renderer/shaders/meson.build +++ b/sway/desktop/fx_renderer/shaders/meson.build @@ -3,6 +3,7 @@ embed = find_program('./embed.sh', native: true) shaders = [ 'blur1.frag', 'blur2.frag', + 'blur_effects.frag', 'box_shadow.frag', 'common.vert', 'corner.frag', diff --git a/sway/desktop/render.c b/sway/desktop/render.c index c054b3a5..391533c5 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -258,6 +258,23 @@ struct fx_framebuffer *get_main_buffer_blur(struct fx_renderer *renderer, struct &renderer->shaders.blur2, box, blur_radius); } + float blur_noise = config->blur_params.noise; + float blur_brightness = config->blur_params.brightness; + float blur_contrast = config->blur_params.contrast; + float blur_saturation = config->blur_params.saturation; + + if (pixman_region32_not_empty(&damage)) { + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; ++i) { + const pixman_box32_t box = rects[i]; + struct wlr_box new_box = { box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1 }; + fx_renderer_scissor(&new_box); + fx_render_blur_effects(renderer, gl_matrix, ¤t_buffer, blur_noise, + blur_brightness, blur_contrast, blur_saturation); + } + } + pixman_region32_fini(&tempDamage); pixman_region32_fini(&damage); diff --git a/sway/meson.build b/sway/meson.build index 099f2b52..f0f26346 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -53,9 +53,13 @@ sway_sources = files( 'commands/bar.c', 'commands/bind.c', 'commands/blur.c', + 'commands/blur_brightness.c', + 'commands/blur_contrast.c', 'commands/blur_passes.c', 'commands/blur_radius.c', 'commands/blur_xray.c', + 'commands/blur_noise.c', + 'commands/blur_saturation.c', 'commands/border.c', 'commands/client.c', 'commands/corner_radius.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 08c1a7cb..b0f583f2 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -707,6 +707,22 @@ The default colors are: Adjusts the blur radius of windows between 0 (disabled) and 10 while 5 is the default value. +*blur_noise* + Adjusts the percentage of noise applied to the blur between 0 and 1 + while 0.02 (2%) is the default value. + +*blur_brightness* + Adjusts the percentage of brightness for the blur between 0 and 2 + while 0.9 (90%) is the default value. + +*blur_contrast* + Adjusts the percentage of contrast for the blur between 0 and 2 + while 0.9 (90%) is the default value. + +*blur_saturation* + Adjusts the percentage of saturation for the blur between 0 and 2 + while 1.1 (110%) is the default value. + *default_border* normal|none|pixel [] Set default border style for new tiled windows.