1
0
Fork 0
mirror of https://github.com/yshui/picom.git synced 2025-03-10 17:16:30 -04:00

backend: give backends more flexibility regarding shadow creation

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2022-08-25 05:21:19 +01:00
parent e942f253f7
commit 84407099a9
No known key found for this signature in database
GPG key ID: D3A4405BE6CC17F4
13 changed files with 100 additions and 32 deletions

View file

@ -16,6 +16,8 @@
typedef struct session session_t; typedef struct session session_t;
struct managed_win; struct managed_win;
struct backend_shadow_context;
struct ev_loop; struct ev_loop;
struct backend_operations; struct backend_operations;
@ -213,10 +215,24 @@ struct backend_operations {
void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap, void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned); struct xvisual_info fmt, bool owned);
struct backend_shadow_context *(*create_shadow_context)(backend_t *backend_data,
double radius);
void (*destroy_shadow_context)(backend_t *backend_data,
struct backend_shadow_context *ctx);
/// Create a shadow image based on the parameters /// Create a shadow image based on the parameters
/// Default implementation: default_backend_render_shadow /// Default implementation: default_backend_render_shadow
///
/// Required.
void *(*render_shadow)(backend_t *backend_data, int width, int height, void *(*render_shadow)(backend_t *backend_data, int width, int height,
const conv *kernel, double r, double g, double b, double a); struct backend_shadow_context *ctx, struct color color);
/// Create a shadow by blurring a mask. `size` is the size of the blur. The
/// backend can use whichever blur method is the fastest.
///
/// Optional.
void *(*shadow_from_mask)(backend_t *backend_data, void *mask,
struct backend_shadow_context *ctx, struct color color);
/// Create a mask image from region `reg`. This region can be used to create /// Create a mask image from region `reg`. This region can be used to create
/// shadow, or used as a mask for composing. When used as a mask, it should mask /// shadow, or used as a mask for composing. When used as a mask, it should mask

View file

@ -291,16 +291,16 @@ shadow_picture_err:
return false; return false;
} }
void * void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
default_backend_render_shadow(backend_t *backend_data, int width, int height, struct backend_shadow_context *sctx, struct color color) {
const conv *kernel, double r, double g, double b, double a) { const conv *kernel = (void *)sctx;
xcb_pixmap_t shadow_pixel = solid_picture(backend_data->c, backend_data->root, xcb_pixmap_t shadow_pixel = solid_picture(backend_data->c, backend_data->root, true,
true, 1, r, g, b), 1, color.red, color.green, color.blue),
shadow = XCB_NONE; shadow = XCB_NONE;
xcb_render_picture_t pict = XCB_NONE; xcb_render_picture_t pict = XCB_NONE;
if (!build_shadow(backend_data->c, backend_data->root, a, width, height, kernel, if (!build_shadow(backend_data->c, backend_data->root, color.alpha, width, height,
shadow_pixel, &shadow, &pict)) { kernel, shadow_pixel, &shadow, &pict)) {
return NULL; return NULL;
} }
@ -311,6 +311,19 @@ default_backend_render_shadow(backend_t *backend_data, int width, int height,
return ret; return ret;
} }
struct backend_shadow_context *
default_create_shadow_context(backend_t *backend_data attr_unused, double radius) {
auto ret =
(struct backend_shadow_context *)gaussian_kernel_autodetect_deviation(radius);
sum_kernel_preprocess((conv *)ret);
return ret;
}
void default_destroy_shadow_context(backend_t *backend_data attr_unused,
struct backend_shadow_context *sctx) {
free_conv((conv *)sctx);
}
static struct conv **generate_box_blur_kernel(struct box_blur_args *args, int *kernel_count) { static struct conv **generate_box_blur_kernel(struct box_blur_args *args, int *kernel_count) {
int r = args->size * 2 + 1; int r = args->size * 2 + 1;
assert(r > 0); assert(r > 0);

View file

@ -62,9 +62,13 @@ bool default_is_win_transparent(void *, win *, void *);
/// caveat as `default_is_win_transparent` applies. /// caveat as `default_is_win_transparent` applies.
bool default_is_frame_transparent(void *, win *, void *); bool default_is_frame_transparent(void *, win *, void *);
void * void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
default_backend_render_shadow(backend_t *backend_data, int width, int height, struct backend_shadow_context *sctx, struct color color);
const conv *kernel, double r, double g, double b, double a); struct backend_shadow_context *
default_create_shadow_context(backend_t *backend_data, double radius);
void default_destroy_shadow_context(backend_t *backend_data,
struct backend_shadow_context *sctx);
void init_backend_base(struct backend_base *base, session_t *ps); void init_backend_base(struct backend_base *base, session_t *ps);

View file

@ -175,6 +175,8 @@ struct backend_operations dummy_ops = {
.fill = dummy_fill, .fill = dummy_fill,
.blur = dummy_blur, .blur = dummy_blur,
.bind_pixmap = dummy_bind_pixmap, .bind_pixmap = dummy_bind_pixmap,
.create_shadow_context = default_create_shadow_context,
.destroy_shadow_context = default_destroy_shadow_context,
.render_shadow = default_backend_render_shadow, .render_shadow = default_backend_render_shadow,
.make_mask = dummy_make_mask, .make_mask = dummy_make_mask,
.release_image = dummy_release_image, .release_image = dummy_release_image,

View file

@ -536,6 +536,8 @@ struct backend_operations glx_ops = {
.is_image_transparent = default_is_image_transparent, .is_image_transparent = default_is_image_transparent,
.present = glx_present, .present = glx_present,
.buffer_age = glx_buffer_age, .buffer_age = glx_buffer_age,
.create_shadow_context = default_create_shadow_context,
.destroy_shadow_context = default_destroy_shadow_context,
.render_shadow = default_backend_render_shadow, .render_shadow = default_backend_render_shadow,
.make_mask = gl_make_mask, .make_mask = gl_make_mask,
.fill = gl_fill, .fill = gl_fill,

View file

@ -968,6 +968,8 @@ struct backend_operations xrender_ops = {
.fill = fill, .fill = fill,
.bind_pixmap = bind_pixmap, .bind_pixmap = bind_pixmap,
.release_image = release_image, .release_image = release_image,
.create_shadow_context = default_create_shadow_context,
.destroy_shadow_context = default_destroy_shadow_context,
.render_shadow = default_backend_render_shadow, .render_shadow = default_backend_render_shadow,
.make_mask = make_mask, .make_mask = make_mask,
//.prepare_win = prepare_win, //.prepare_win = prepare_win,

View file

@ -300,8 +300,8 @@ typedef struct session {
xcb_render_picture_t cshadow_picture; xcb_render_picture_t cshadow_picture;
/// 1x1 white Picture. /// 1x1 white Picture.
xcb_render_picture_t white_picture; xcb_render_picture_t white_picture;
/// Gaussian map of shadow. /// Backend shadow context.
struct conv *gaussian_map; struct backend_shadow_context *shadow_context;
// for shadow precomputation // for shadow precomputation
/// A region in which shadow is not painted on. /// A region in which shadow is not painted on.
region_t shadow_exclude_reg; region_t shadow_exclude_reg;

View file

@ -90,15 +90,20 @@ conv *gaussian_kernel(double r, int size) {
/// Estimate the element of the sum of the first row in a gaussian kernel with standard /// Estimate the element of the sum of the first row in a gaussian kernel with standard
/// deviation `r` and size `size`, /// deviation `r` and size `size`,
static inline double estimate_first_row_sum(double size, double r) { static inline double estimate_first_row_sum(double size, double r) {
// `factor` is integral of gaussian from -size to size
double factor = erf(size / r / sqrt(2)); double factor = erf(size / r / sqrt(2));
// `a` is gaussian at (size, 0)
double a = exp(-0.5 * size * size / (r * r)) / sqrt(2 * M_PI) / r; double a = exp(-0.5 * size * size / (r * r)) / sqrt(2 * M_PI) / r;
// The sum of the whole kernel is normalized to 1, i.e. each element is divided by
// factor sqaured. So the sum of the first row is a * factor / factor^2 = a /
// factor
return a / factor; return a / factor;
} }
/// Pick a suitable gaussian kernel radius for a given kernel size. The returned radius /// Pick a suitable gaussian kernel standard deviation for a given kernel size. The
/// is the maximum possible radius (<= size*2) that satisfies no sum of the rows in /// returned radius is the maximum possible radius (<= size*2) that satisfies no sum of
/// the kernel are less than `row_limit` (up to certain precision). /// the rows in the kernel are less than `row_limit` (up to certain precision).
static inline double gaussian_kernel_std_for_size(int size, double row_limit) { static inline double gaussian_kernel_std_for_size(double size, double row_limit) {
assert(size > 0); assert(size > 0);
if (row_limit >= 1.0 / 2.0 / size) { if (row_limit >= 1.0 / 2.0 / size) {
return size * 2; return size * 2;
@ -121,9 +126,9 @@ static inline double gaussian_kernel_std_for_size(int size, double row_limit) {
/// transparent, so the transition from shadow to the background is smooth. /// transparent, so the transition from shadow to the background is smooth.
/// ///
/// @param[in] shadow_radius the radius of the shadow /// @param[in] shadow_radius the radius of the shadow
conv *gaussian_kernel_autodetect_deviation(int shadow_radius) { conv *gaussian_kernel_autodetect_deviation(double shadow_radius) {
assert(shadow_radius >= 0); assert(shadow_radius >= 0);
int size = shadow_radius * 2 + 1; int size = (int)(shadow_radius * 2 + 1);
if (shadow_radius == 0) { if (shadow_radius == 0) {
return gaussian_kernel(0, size); return gaussian_kernel(0, size);

View file

@ -27,7 +27,7 @@ conv *gaussian_kernel(double r, int size);
/// transparent. /// transparent.
/// ///
/// @param[in] shadow_radius the radius of the shadow /// @param[in] shadow_radius the radius of the shadow
conv *gaussian_kernel_autodetect_deviation(int shadow_radius); conv *gaussian_kernel_autodetect_deviation(double shadow_radius);
/// preprocess kernels to make shadow generation faster /// preprocess kernels to make shadow generation faster
/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive /// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive

View file

@ -451,6 +451,11 @@ static void destroy_backend(session_t *ps) {
ps->backend_data, ps->backend_blur_context); ps->backend_data, ps->backend_blur_context);
ps->backend_blur_context = NULL; ps->backend_blur_context = NULL;
} }
if (ps->shadow_context) {
ps->backend_data->ops->destroy_shadow_context(ps->backend_data,
ps->shadow_context);
ps->shadow_context = NULL;
}
ps->backend_data->ops->deinit(ps->backend_data); ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL; ps->backend_data = NULL;
} }
@ -504,6 +509,12 @@ static bool initialize_backend(session_t *ps) {
return false; return false;
} }
ps->backend_data->ops = backend_list[ps->o.backend]; ps->backend_data->ops = backend_list[ps->o.backend];
ps->shadow_context = ps->backend_data->ops->create_shadow_context(
ps->backend_data, ps->o.shadow_radius);
if (!ps->shadow_context) {
log_fatal("Failed to initialize shadow context, aborting...");
goto err;
}
if (!initialize_blur(ps)) { if (!initialize_blur(ps)) {
log_fatal("Failed to prepare for background blur, aborting..."); log_fatal("Failed to prepare for background blur, aborting...");
@ -553,6 +564,11 @@ static bool initialize_backend(session_t *ps) {
// The old backends binds pixmap lazily, nothing to do here // The old backends binds pixmap lazily, nothing to do here
return true; return true;
err: err:
if (ps->shadow_context) {
ps->backend_data->ops->destroy_shadow_context(ps->backend_data,
ps->shadow_context);
ps->shadow_context = NULL;
}
ps->backend_data->ops->deinit(ps->backend_data); ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL; ps->backend_data = NULL;
quit(ps); quit(ps);
@ -1675,7 +1691,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
.black_picture = XCB_NONE, .black_picture = XCB_NONE,
.cshadow_picture = XCB_NONE, .cshadow_picture = XCB_NONE,
.white_picture = XCB_NONE, .white_picture = XCB_NONE,
.gaussian_map = NULL, .shadow_context = NULL,
#ifdef CONFIG_VSYNC_DRM #ifdef CONFIG_VSYNC_DRM
.drm_fd = -1, .drm_fd = -1,
@ -1929,8 +1945,11 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
} }
} }
ps->gaussian_map = gaussian_kernel_autodetect_deviation(ps->o.shadow_radius); if (ps->o.legacy_backends) {
sum_kernel_preprocess(ps->gaussian_map); ps->shadow_context =
(void *)gaussian_kernel_autodetect_deviation(ps->o.shadow_radius);
sum_kernel_preprocess((conv *)ps->shadow_context);
}
rebuild_shadow_exclude_reg(ps); rebuild_shadow_exclude_reg(ps);
@ -2408,7 +2427,9 @@ static void session_destroy(session_t *ps) {
// Flush all events // Flush all events
x_sync(ps->c); x_sync(ps->c);
ev_io_stop(ps->loop, &ps->xiow); ev_io_stop(ps->loop, &ps->xiow);
free_conv(ps->gaussian_map); if (ps->o.legacy_backends) {
free_conv((conv *)ps->shadow_context);
}
destroy_atoms(ps->atoms); destroy_atoms(ps->atoms);
#ifdef DEBUG_XRC #ifdef DEBUG_XRC

View file

@ -649,8 +649,9 @@ static bool get_root_tile(session_t *ps) {
static void paint_root(session_t *ps, const region_t *reg_paint) { static void paint_root(session_t *ps, const region_t *reg_paint) {
// If there is no root tile pixmap, try getting one. // If there is no root tile pixmap, try getting one.
// If that fails, give up. // If that fails, give up.
if (!ps->root_tile_paint.pixmap && !get_root_tile(ps)) if (!ps->root_tile_paint.pixmap && !get_root_tile(ps)) {
return; return;
}
paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint,
ps->root_tile_paint.pict); ps->root_tile_paint.pict);
@ -669,7 +670,7 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit
xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE; xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE;
xcb_gcontext_t gc = XCB_NONE; xcb_gcontext_t gc = XCB_NONE;
shadow_image = make_shadow(ps->c, ps->gaussian_map, opacity, width, height); shadow_image = make_shadow(ps->c, (conv *)ps->shadow_context, opacity, width, height);
if (!shadow_image) { if (!shadow_image) {
log_error("failed to make shadow"); log_error("failed to make shadow");
return XCB_NONE; return XCB_NONE;
@ -689,8 +690,9 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit
ps->c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL); ps->c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
shadow_picture_argb = x_create_picture_with_standard_and_pixmap( shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
ps->c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL); ps->c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
if (!shadow_picture || !shadow_picture_argb) if (!shadow_picture || !shadow_picture_argb) {
goto shadow_picture_err; goto shadow_picture_err;
}
gc = x_new_id(ps->c); gc = x_new_id(ps->c);
xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL); xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);

View file

@ -346,11 +346,10 @@ static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w
} }
bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c, bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
struct conv *kernel) { struct backend_shadow_context *sctx) {
assert(!w->shadow_image); assert(!w->shadow_image);
assert(w->shadow); assert(w->shadow);
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, kernel, c.red, w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, sctx, c);
c.green, c.blue, c.alpha);
if (!w->shadow_image) { if (!w->shadow_image) {
log_error("Failed to bind shadow image, shadow will be disabled for " log_error("Failed to bind shadow image, shadow will be disabled for "
"%#010x (%s)", "%#010x (%s)",
@ -556,7 +555,7 @@ void win_process_image_flags(session_t *ps, struct managed_win *w) {
.green = ps->o.shadow_green, .green = ps->o.shadow_green,
.blue = ps->o.shadow_blue, .blue = ps->o.shadow_blue,
.alpha = ps->o.shadow_opacity}, .alpha = ps->o.shadow_opacity},
ps->gaussian_map); ps->shadow_context);
} }
} }

View file

@ -7,6 +7,8 @@
#include <xcb/render.h> #include <xcb/render.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <backend/backend.h>
#include "uthash_extra.h" #include "uthash_extra.h"
// FIXME shouldn't need this // FIXME shouldn't need this
@ -289,7 +291,7 @@ void win_process_update_flags(session_t *ps, struct managed_win *w);
void win_process_image_flags(session_t *ps, struct managed_win *w); void win_process_image_flags(session_t *ps, struct managed_win *w);
/// Bind a shadow to the window, with color `c` and shadow kernel `kernel` /// Bind a shadow to the window, with color `c` and shadow kernel `kernel`
bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c, bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
struct conv *kernel); struct backend_shadow_context *kernel);
/// Start the unmap of a window. We cannot unmap immediately since we might need to fade /// Start the unmap of a window. We cannot unmap immediately since we might need to fade
/// the window out. /// the window out.