Add layer effect option to ignore transparent regions when blurring (#159)

This commit is contained in:
Erik Reider 2023-09-21 03:35:49 +02:00 committed by GitHub
parent 6f6991a1b3
commit d89c365106
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 124 additions and 36 deletions

View file

@ -44,6 +44,7 @@ Sway is an incredible window manager, and certainly one of the most well establi
- SwayIPC Example: `swaymsg "layer_effects 'waybar' 'blur enable; shadows enable; corner_radius 6'"`
- Available Effects:
- `blur <enable|disable>`
- `blur_ignore_transparent <enable|disable>`
- `shadows <enable|disable>`
- `corner_radius <int>`
+ Dim unfocused windows:

View file

@ -32,6 +32,7 @@ struct decoration_data {
float dim;
float *dim_color;
bool has_titlebar;
bool discard_transparent;
bool blur;
bool shadow;
};
@ -113,6 +114,7 @@ struct tex_shader {
GLint dim;
GLint dim_color;
GLint has_titlebar;
GLint discard_transparent;
};
struct fx_renderer {
@ -177,6 +179,15 @@ void fx_renderer_clear(const float color[static 4]);
void fx_renderer_scissor(struct wlr_box *box);
// Initialize the stenciling work
void fx_renderer_stencil_mask_init();
// Close the mask
void fx_renderer_stencil_mask_close(bool draw_inside_mask);
// Finish stenciling and clear the buffer
void fx_renderer_stencil_mask_fini();
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,
const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],
struct decoration_data deco_data);

View file

@ -30,6 +30,7 @@ struct sway_layer_surface {
bool has_shadow;
bool has_blur;
bool blur_ignore_transparent;
int corner_radius;
};

View file

@ -222,6 +222,7 @@ static bool link_tex_program(struct tex_shader *shader,
shader->radius = glGetUniformLocation(prog, "radius");
shader->saturation = glGetUniformLocation(prog, "saturation");
shader->has_titlebar = glGetUniformLocation(prog, "has_titlebar");
shader->discard_transparent = glGetUniformLocation(prog, "discard_transparent");
return true;
}
@ -448,6 +449,35 @@ void fx_renderer_scissor(struct wlr_box *box) {
}
}
void fx_renderer_stencil_mask_init() {
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
// Disable writing to color buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
}
void fx_renderer_stencil_mask_close(bool draw_inside_mask) {
// Reenable writing to color buffer
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
if (draw_inside_mask) {
glStencilFunc(GL_EQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
return;
}
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
void fx_renderer_stencil_mask_fini() {
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glDisable(GL_STENCIL_TEST);
}
bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,
const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],
struct decoration_data deco_data) {
@ -509,6 +539,7 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_te
glUniform1f(shader->dim, deco_data.dim);
glUniform4f(shader->dim_color, dim_color[0], dim_color[1], dim_color[2], dim_color[3]);
glUniform1f(shader->has_titlebar, deco_data.has_titlebar);
glUniform1f(shader->discard_transparent, deco_data.discard_transparent);
glUniform1f(shader->saturation, deco_data.saturation);
glUniform1f(shader->radius, deco_data.corner_radius);
@ -737,7 +768,6 @@ void fx_render_stencil_mask(struct fx_renderer *renderer, const struct wlr_box *
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(shader.pos_attrib);
}
// TODO: alpha input arg?
@ -765,21 +795,10 @@ void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *bo
inner_box.width -= blur_sigma * 2;
inner_box.height -= blur_sigma * 2;
glEnable(GL_STENCIL_TEST);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
// Disable writing to color buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
fx_renderer_stencil_mask_init();
// Draw the rounded rect as a mask
fx_render_stencil_mask(renderer, &inner_box, matrix, corner_radius);
// Close the mask
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// Reenable writing to color buffer
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
fx_renderer_stencil_mask_close(false);
// blending will practically always be needed (unless we have a madman
// who uses opaque shadows with zero sigma), so just enable it
@ -810,9 +829,7 @@ void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *bo
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glDisable(GL_STENCIL_TEST);
fx_renderer_stencil_mask_fini();
}
void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9],
@ -854,4 +871,5 @@ void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9],
glDisableVertexAttribArray(shader->pos_attrib);
glDisableVertexAttribArray(shader->tex_attrib);
}

View file

@ -26,8 +26,9 @@ uniform vec4 dim_color;
uniform vec2 size;
uniform vec2 position;
uniform float radius;
uniform bool has_titlebar;
uniform float saturation;
uniform bool has_titlebar;
uniform bool discard_transparent;
const vec3 saturation_weight = vec3(0.2125, 0.7154, 0.0721);
@ -59,4 +60,8 @@ void main() {
gl_FragColor = mix(vec4(0), gl_FragColor, smooth);
}
}
if (discard_transparent && gl_FragColor.a == 0.0) {
discard;
}
}

View file

@ -711,6 +711,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
layer_surface->data = sway_layer;
sway_layer->has_blur = false;
sway_layer->blur_ignore_transparent = false;
sway_layer->has_shadow = false;
sway_layer->corner_radius = 0;

View file

@ -203,6 +203,7 @@ void output_layer_for_each_toplevel_surface(struct sway_output *output,
struct render_data *data = user_data;
data->deco_data.blur = layer_surface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ?
layer_surface->has_blur : false;
data->deco_data.discard_transparent = layer_surface->blur_ignore_transparent;
data->deco_data.shadow = layer_surface->has_shadow;
data->deco_data.corner_radius = layer_surface->corner_radius;

View file

@ -40,6 +40,7 @@ struct decoration_data get_undecorated_decoration_data() {
.saturation = 1.0f,
.has_titlebar = false,
.blur = false,
.discard_transparent = false,
.shadow = false,
};
}
@ -266,9 +267,16 @@ struct fx_framebuffer *get_main_buffer_blur(struct fx_renderer *renderer, struct
return current_buffer;
}
void render_blur(bool optimized, struct sway_output *output, pixman_region32_t *output_damage,
const struct wlr_box *dst_box, pixman_region32_t *opaque_region, int corner_radius,
bool should_round_top) {
struct blur_stencil_data {
struct fx_texture *stencil_texture;
const struct wlr_fbox *stencil_src_box;
float *stencil_matrix;
};
void render_blur(bool optimized, struct sway_output *output,
pixman_region32_t *output_damage, const struct wlr_box *dst_box,
pixman_region32_t *opaque_region, struct decoration_data *deco_data,
struct blur_stencil_data *stencil_data) {
struct wlr_output *wlr_output = output->wlr_output;
struct fx_renderer *renderer = output->renderer;
@ -299,16 +307,32 @@ void render_blur(bool optimized, struct sway_output *output, pixman_region32_t *
buffer = get_main_buffer_blur(renderer, output, &translucent_region, dst_box);
}
// Get a stencil of the window ignoring transparent regions
if (deco_data->discard_transparent) {
fx_renderer_scissor(NULL);
fx_renderer_stencil_mask_init();
render_texture(wlr_output, output_damage, stencil_data->stencil_texture, stencil_data->stencil_src_box,
dst_box, stencil_data->stencil_matrix, *deco_data);
fx_renderer_stencil_mask_close(true);
}
// Draw the blurred texture
struct wlr_box monitor_box = get_monitor_box(wlr_output);
enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform);
float matrix[9];
wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, wlr_output->transform_matrix);
struct decoration_data deco_data = get_undecorated_decoration_data();
deco_data.corner_radius = corner_radius;
deco_data.has_titlebar = should_round_top;
render_texture(wlr_output, &damage, &buffer->texture, NULL, dst_box, matrix, deco_data);
struct decoration_data blur_deco_data = get_undecorated_decoration_data();
blur_deco_data.corner_radius = deco_data->corner_radius;
blur_deco_data.has_titlebar = deco_data->has_titlebar;
render_texture(wlr_output, &damage, &buffer->texture, NULL, dst_box, matrix, blur_deco_data);
// Finish stenciling
if (deco_data->discard_transparent) {
fx_renderer_stencil_mask_fini();
}
damage_finish:
pixman_region32_fini(&damage);
@ -404,6 +428,10 @@ static void render_surface_iterator(struct sway_output *output,
struct decoration_data deco_data = data->deco_data;
deco_data.corner_radius *= wlr_output->scale;
struct wlr_fbox src_box;
wlr_surface_get_buffer_source_box(surface, &src_box);
struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture);
// render blur
bool is_subsurface = view ? view->surface != surface : false;
if (deco_data.blur && config_should_parameters_blur() && !is_subsurface) {
@ -420,19 +448,21 @@ static void render_surface_iterator(struct sway_output *output,
}
if (has_alpha) {
bool should_optimize_blur = view ? !container_is_floating_or_child(view->container) ||
config->blur_xray : false;
render_blur(should_optimize_blur, output, output_damage, &dst_box, &opaque_region,
deco_data.corner_radius, deco_data.has_titlebar);
struct wlr_box monitor_box = get_monitor_box(wlr_output);
wlr_box_transform(&monitor_box, &monitor_box,
wlr_output_transform_invert(wlr_output->transform), monitor_box.width, monitor_box.height);
struct blur_stencil_data stencil_data = { &fx_texture, &src_box, matrix };
bool should_optimize_blur = view ? !container_is_floating(view->container) || config->blur_xray : false;
render_blur(should_optimize_blur, output, output_damage, &dst_box,
&opaque_region, &deco_data, &stencil_data);
}
pixman_region32_fini(&opaque_region);
}
deco_data.discard_transparent = false;
// Render surface texture
struct wlr_fbox src_box;
wlr_surface_get_buffer_source_box(surface, &src_box);
struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture);
render_texture(wlr_output, output_damage, &fx_texture, &src_box, &dst_box,
matrix, deco_data);
@ -448,7 +478,7 @@ static void render_layer_iterator(struct sway_output *output,
struct decoration_data deco_data = data->deco_data;
// Ignore effects if this is a subsurface
if (wl_list_length(&surface->current.subsurfaces_above) > 0) {
if (!wlr_surface_is_layer_surface(surface)) {
deco_data = get_undecorated_decoration_data();
}
@ -786,6 +816,8 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output
deco_data.corner_radius *= wlr_output->scale;
struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture);
// render blur
if (deco_data.blur && config_should_parameters_blur()) {
struct wlr_gles2_texture_attribs attribs;
@ -796,16 +828,21 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output
pixman_region32_init(&opaque_region);
pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0);
bool should_optimize_blur = !container_is_floating_or_child(view->container) || config->blur_xray;
struct wlr_box monitor_box = get_monitor_box(wlr_output);
wlr_box_transform(&monitor_box, &monitor_box,
wlr_output_transform_invert(wlr_output->transform), monitor_box.width, monitor_box.height);
struct blur_stencil_data stencil_data = { &fx_texture, &saved_buf->source_box, matrix };
bool should_optimize_blur = !container_is_floating(view->container) || config->blur_xray;
render_blur(should_optimize_blur, output, damage, &dst_box, &opaque_region,
deco_data.corner_radius, deco_data.has_titlebar);
&deco_data, &stencil_data);
pixman_region32_fini(&opaque_region);
}
}
deco_data.discard_transparent = false;
// Render saved surface texture
struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture);
render_texture(wlr_output, damage, &fx_texture,
&saved_buf->source_box, &dst_box, matrix, deco_data);
}
@ -1405,6 +1442,7 @@ static void render_containers_linear(struct sway_output *output,
.saturation = child->saturation,
.has_titlebar = has_titlebar,
.blur = child->blur_enabled,
.discard_transparent = false,
.shadow = child->shadow_enabled,
};
render_view(output, damage, child, colors, deco_data);
@ -1455,6 +1493,7 @@ static void render_containers_tabbed(struct sway_output *output,
.saturation = current->saturation,
.has_titlebar = true,
.blur = current->blur_enabled,
.discard_transparent = false,
.shadow = current->shadow_enabled,
};
@ -1551,6 +1590,7 @@ static void render_containers_stacked(struct sway_output *output,
? 0 : current->corner_radius,
.has_titlebar = true,
.blur = current->blur_enabled,
.discard_transparent = false,
.shadow = current->shadow_enabled,
};
@ -1699,6 +1739,7 @@ static void render_floating_container(struct sway_output *soutput,
.corner_radius = con->corner_radius,
.has_titlebar = has_titlebar,
.blur = con->blur_enabled,
.discard_transparent = false,
.shadow = con->shadow_enabled,
};
render_view(soutput, damage, con, colors, deco_data);
@ -1956,6 +1997,7 @@ void output_render(struct sway_output *output, struct timespec *when,
.saturation = focus->saturation,
.has_titlebar = false,
.blur = false,
.discard_transparent = false,
.shadow = false,
};
render_view_popups(focus->view, output, damage, deco_data);

View file

@ -377,6 +377,10 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
if (lsurface->has_blur) {
json_object_array_add(effects, json_object_new_string("blur"));
}
if (lsurface->blur_ignore_transparent) {
json_object_array_add(effects,
json_object_new_string("blur_ignore_transparent"));
}
if (lsurface->has_shadow) {
json_object_array_add(effects, json_object_new_string("shadows"));
}

View file

@ -67,6 +67,9 @@ void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_cr
if (strcmp(argv[0], "blur") == 0) {
sway_layer->has_blur = parse_boolean(argv[1], true);
continue;
} if (strcmp(argv[0], "blur_ignore_transparent") == 0) {
sway_layer->blur_ignore_transparent = parse_boolean(argv[1], true);
continue;
} else if (strcmp(argv[0], "shadows") == 0) {
sway_layer->has_shadow = parse_boolean(argv[1], true);
continue;

View file

@ -795,6 +795,7 @@ The default colors are:
Effects:
- *blur* <enable|disable>
- *blur_ignore_transparent* <enable|disable>
- *shadows* <enable|disable>
- *corner_radius* <integer>