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:
parent
e942f253f7
commit
84407099a9
13 changed files with 100 additions and 32 deletions
|
@ -16,6 +16,8 @@
|
|||
typedef struct session session_t;
|
||||
struct managed_win;
|
||||
|
||||
struct backend_shadow_context;
|
||||
|
||||
struct ev_loop;
|
||||
struct backend_operations;
|
||||
|
||||
|
@ -213,10 +215,24 @@ struct backend_operations {
|
|||
void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
|
||||
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
|
||||
/// Default implementation: default_backend_render_shadow
|
||||
///
|
||||
/// Required.
|
||||
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
|
||||
/// shadow, or used as a mask for composing. When used as a mask, it should mask
|
||||
|
|
|
@ -291,16 +291,16 @@ shadow_picture_err:
|
|||
return false;
|
||||
}
|
||||
|
||||
void *
|
||||
default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
||||
const conv *kernel, double r, double g, double b, double a) {
|
||||
xcb_pixmap_t shadow_pixel = solid_picture(backend_data->c, backend_data->root,
|
||||
true, 1, r, g, b),
|
||||
void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
||||
struct backend_shadow_context *sctx, struct color color) {
|
||||
const conv *kernel = (void *)sctx;
|
||||
xcb_pixmap_t shadow_pixel = solid_picture(backend_data->c, backend_data->root, true,
|
||||
1, color.red, color.green, color.blue),
|
||||
shadow = XCB_NONE;
|
||||
xcb_render_picture_t pict = XCB_NONE;
|
||||
|
||||
if (!build_shadow(backend_data->c, backend_data->root, a, width, height, kernel,
|
||||
shadow_pixel, &shadow, &pict)) {
|
||||
if (!build_shadow(backend_data->c, backend_data->root, color.alpha, width, height,
|
||||
kernel, shadow_pixel, &shadow, &pict)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -311,6 +311,19 @@ default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
|||
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) {
|
||||
int r = args->size * 2 + 1;
|
||||
assert(r > 0);
|
||||
|
|
|
@ -62,9 +62,13 @@ bool default_is_win_transparent(void *, win *, void *);
|
|||
/// caveat as `default_is_win_transparent` applies.
|
||||
bool default_is_frame_transparent(void *, win *, void *);
|
||||
|
||||
void *
|
||||
default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
||||
const conv *kernel, double r, double g, double b, double a);
|
||||
void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
||||
struct backend_shadow_context *sctx, struct color color);
|
||||
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);
|
||||
|
||||
|
|
|
@ -175,6 +175,8 @@ struct backend_operations dummy_ops = {
|
|||
.fill = dummy_fill,
|
||||
.blur = dummy_blur,
|
||||
.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,
|
||||
.make_mask = dummy_make_mask,
|
||||
.release_image = dummy_release_image,
|
||||
|
|
|
@ -536,6 +536,8 @@ struct backend_operations glx_ops = {
|
|||
.is_image_transparent = default_is_image_transparent,
|
||||
.present = glx_present,
|
||||
.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,
|
||||
.make_mask = gl_make_mask,
|
||||
.fill = gl_fill,
|
||||
|
|
|
@ -968,6 +968,8 @@ struct backend_operations xrender_ops = {
|
|||
.fill = fill,
|
||||
.bind_pixmap = bind_pixmap,
|
||||
.release_image = release_image,
|
||||
.create_shadow_context = default_create_shadow_context,
|
||||
.destroy_shadow_context = default_destroy_shadow_context,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
.make_mask = make_mask,
|
||||
//.prepare_win = prepare_win,
|
||||
|
|
|
@ -300,8 +300,8 @@ typedef struct session {
|
|||
xcb_render_picture_t cshadow_picture;
|
||||
/// 1x1 white Picture.
|
||||
xcb_render_picture_t white_picture;
|
||||
/// Gaussian map of shadow.
|
||||
struct conv *gaussian_map;
|
||||
/// Backend shadow context.
|
||||
struct backend_shadow_context *shadow_context;
|
||||
// for shadow precomputation
|
||||
/// A region in which shadow is not painted on.
|
||||
region_t shadow_exclude_reg;
|
||||
|
|
17
src/kernel.c
17
src/kernel.c
|
@ -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
|
||||
/// deviation `r` and size `size`,
|
||||
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));
|
||||
// `a` is gaussian at (size, 0)
|
||||
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;
|
||||
}
|
||||
|
||||
/// Pick a suitable gaussian kernel radius for a given kernel size. The returned radius
|
||||
/// is the maximum possible radius (<= size*2) that satisfies no sum of 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) {
|
||||
/// Pick a suitable gaussian kernel standard deviation for a given kernel size. The
|
||||
/// returned radius is the maximum possible radius (<= size*2) that satisfies no sum of
|
||||
/// the rows in the kernel are less than `row_limit` (up to certain precision).
|
||||
static inline double gaussian_kernel_std_for_size(double size, double row_limit) {
|
||||
assert(size > 0);
|
||||
if (row_limit >= 1.0 / 2.0 / size) {
|
||||
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.
|
||||
///
|
||||
/// @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);
|
||||
int size = shadow_radius * 2 + 1;
|
||||
int size = (int)(shadow_radius * 2 + 1);
|
||||
|
||||
if (shadow_radius == 0) {
|
||||
return gaussian_kernel(0, size);
|
||||
|
|
|
@ -27,7 +27,7 @@ conv *gaussian_kernel(double r, int size);
|
|||
/// transparent.
|
||||
///
|
||||
/// @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
|
||||
/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
|
||||
|
|
29
src/picom.c
29
src/picom.c
|
@ -451,6 +451,11 @@ static void destroy_backend(session_t *ps) {
|
|||
ps->backend_data, ps->backend_blur_context);
|
||||
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 = NULL;
|
||||
}
|
||||
|
@ -504,6 +509,12 @@ static bool initialize_backend(session_t *ps) {
|
|||
return false;
|
||||
}
|
||||
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)) {
|
||||
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
|
||||
return true;
|
||||
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 = NULL;
|
||||
quit(ps);
|
||||
|
@ -1675,7 +1691,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||
.black_picture = XCB_NONE,
|
||||
.cshadow_picture = XCB_NONE,
|
||||
.white_picture = XCB_NONE,
|
||||
.gaussian_map = NULL,
|
||||
.shadow_context = NULL,
|
||||
|
||||
#ifdef CONFIG_VSYNC_DRM
|
||||
.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);
|
||||
sum_kernel_preprocess(ps->gaussian_map);
|
||||
if (ps->o.legacy_backends) {
|
||||
ps->shadow_context =
|
||||
(void *)gaussian_kernel_autodetect_deviation(ps->o.shadow_radius);
|
||||
sum_kernel_preprocess((conv *)ps->shadow_context);
|
||||
}
|
||||
|
||||
rebuild_shadow_exclude_reg(ps);
|
||||
|
||||
|
@ -2408,7 +2427,9 @@ static void session_destroy(session_t *ps) {
|
|||
// Flush all events
|
||||
x_sync(ps->c);
|
||||
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);
|
||||
|
||||
#ifdef DEBUG_XRC
|
||||
|
|
|
@ -649,8 +649,9 @@ static bool get_root_tile(session_t *ps) {
|
|||
static void paint_root(session_t *ps, const region_t *reg_paint) {
|
||||
// If there is no root tile pixmap, try getting one.
|
||||
// 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;
|
||||
}
|
||||
|
||||
paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint,
|
||||
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_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) {
|
||||
log_error("failed to make shadow");
|
||||
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);
|
||||
shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
|
||||
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;
|
||||
}
|
||||
|
||||
gc = x_new_id(ps->c);
|
||||
xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);
|
||||
|
|
|
@ -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,
|
||||
struct conv *kernel) {
|
||||
struct backend_shadow_context *sctx) {
|
||||
assert(!w->shadow_image);
|
||||
assert(w->shadow);
|
||||
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, kernel, c.red,
|
||||
c.green, c.blue, c.alpha);
|
||||
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, sctx, c);
|
||||
if (!w->shadow_image) {
|
||||
log_error("Failed to bind shadow image, shadow will be disabled for "
|
||||
"%#010x (%s)",
|
||||
|
@ -556,7 +555,7 @@ void win_process_image_flags(session_t *ps, struct managed_win *w) {
|
|||
.green = ps->o.shadow_green,
|
||||
.blue = ps->o.shadow_blue,
|
||||
.alpha = ps->o.shadow_opacity},
|
||||
ps->gaussian_map);
|
||||
ps->shadow_context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <xcb/render.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include <backend/backend.h>
|
||||
|
||||
#include "uthash_extra.h"
|
||||
|
||||
// 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);
|
||||
/// 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,
|
||||
struct conv *kernel);
|
||||
struct backend_shadow_context *kernel);
|
||||
|
||||
/// Start the unmap of a window. We cannot unmap immediately since we might need to fade
|
||||
/// the window out.
|
||||
|
|
Loading…
Add table
Reference in a new issue