diff --git a/src/backend/backend.c b/src/backend/backend.c index 0e1fe09..6863b9f 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -308,9 +308,9 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { } else { auto new_img = ps->backend_data->ops->clone_image( ps->backend_data, w->shadow_image, ®_visible); - ps->backend_data->ops->image_op( - ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img, - NULL, ®_visible, (double[]){w->opacity}); + ps->backend_data->ops->set_image_property( + ps->backend_data, IMAGE_PROPERTY_OPACITY, new_img, + &w->opacity); ps->backend_data->ops->compose( ps->backend_data, new_img, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, ®_shadow, ®_visible); @@ -321,9 +321,9 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // Set max brightness if (ps->o.max_brightness < 1.0) { - ps->backend_data->ops->image_op( - ps->backend_data, IMAGE_OP_MAX_BRIGHTNESS, w->win_image, NULL, - ®_visible, &ps->o.max_brightness); + ps->backend_data->ops->set_image_property( + ps->backend_data, IMAGE_PROPERTY_MAX_BRIGHTNESS, w->win_image, + &ps->o.max_brightness); } // Draw window on target @@ -363,18 +363,17 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { auto new_img = ps->backend_data->ops->clone_image( ps->backend_data, w->win_image, ®_visible_local); if (w->invert_color) { - ps->backend_data->ops->image_op( - ps->backend_data, IMAGE_OP_INVERT_COLOR_ALL, new_img, - NULL, ®_visible_local, NULL); + ps->backend_data->ops->set_image_property( + ps->backend_data, IMAGE_PROPERTY_INVERTED, new_img, NULL); } if (w->dim) { double dim_opacity = ps->o.inactive_dim; if (!ps->o.inactive_dim_fixed) { dim_opacity *= w->opacity; } - ps->backend_data->ops->image_op( - ps->backend_data, IMAGE_OP_DIM_ALL, new_img, NULL, - ®_visible_local, (double[]){dim_opacity}); + ps->backend_data->ops->set_image_property( + ps->backend_data, IMAGE_PROPERTY_DIM_LEVEL, new_img, + &dim_opacity); } if (w->frame_opacity != 1) { auto reg_frame = win_get_region_frame_local_by_val(w); @@ -384,9 +383,9 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { pixman_region32_fini(®_frame); } if (w->opacity != 1) { - ps->backend_data->ops->image_op( - ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img, - NULL, ®_visible_local, (double[]){w->opacity}); + ps->backend_data->ops->set_image_property( + ps->backend_data, IMAGE_PROPERTY_OPACITY, new_img, + &w->opacity); } ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x, w->g.y, ®_paint_in_bound, diff --git a/src/backend/backend.h b/src/backend/backend.h index 5f04113..575bf97 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -32,23 +32,36 @@ typedef struct backend_base { typedef void (*backend_ready_callback_t)(void *); +// When image properties are actually applied to the image, they are applied in a +// particular order: +// +// Color inversion -> Dimming -> Opacity multiply -> Limit maximum brightness +enum image_properties { + // Whether the color of the image is inverted + // 1 boolean, default: false + IMAGE_PROPERTY_INVERTED, + // How much the image is dimmed + // 1 double, default: 0 + IMAGE_PROPERTY_DIM_LEVEL, + // Image opacity, i.e. an alpha value multiplied to the alpha channel + // 1 double, default: 1 + IMAGE_PROPERTY_OPACITY, + // The effective size of the image, the image will be tiled to fit. + // 2 int, default: the actual size of the image + IMAGE_PROPERTY_EFFECTIVE_SIZE, + // Limit how bright image can be. The image brightness is estimated by averaging + // the pixels in the image, and dimming will be applied to scale the average + // brightness down to the max brightness value. + // 1 double, default: 1 + IMAGE_PROPERTY_MAX_BRIGHTNESS, +}; + enum image_operations { - // Invert the color of the entire image, `reg_op` ignored - IMAGE_OP_INVERT_COLOR_ALL, - // Dim the entire image, argument is the percentage. `reg_op` ignored - IMAGE_OP_DIM_ALL, + // Apply the image properties, reset the image properties to their defaults + // afterwards. + IMAGE_OP_BAKE_PROPERTIES, // Multiply the alpha channel by the argument IMAGE_OP_APPLY_ALPHA, - // Same as APPLY_ALPHA, but `reg_op` is ignored and the operation applies to the - // full image - IMAGE_OP_APPLY_ALPHA_ALL, - // Change the effective size of the image, without touching the backing image - // itself. When the image is used, the backing image should be tiled to fill its - // effective size. `reg_op` and `reg_visible` is ignored. `arg` is two integers, - // width and height, in that order. - IMAGE_OP_RESIZE_TILE, - // Limit how bright image can be - IMAGE_OP_MAX_BRIGHTNESS, }; struct gaussian_blur_args { @@ -203,7 +216,20 @@ struct backend_operations { * they were originally applied. This might lead to inconsistencies.*/ /** - * Manipulate an image + * Change image properties + * + * @param backend_data backend data + * @param prop the property to change + * @param image_data an image data structure returned by the backend + * @param args property value + * @return whether the operation is successful + */ + bool (*set_image_property)(backend_t *backend_data, enum image_properties prop, + void *image_data, void *args); + + /** + * Manipulate an image. Image properties are untouched by and have no effects on + * operations other than BAKE. * * @param backend_data backend data * @param op the operation to perform @@ -214,13 +240,14 @@ struct backend_operations { * be visible on target. this is a hint to the backend * for optimization purposes. * @param args extra arguments, operation specific - * @return a new image data structure containing the result + * @return whether the operation is successful */ bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data, const region_t *reg_op, const region_t *reg_visible, void *args); /** - * Read the color of the pixel at given position of the given image + * Read the color of the pixel at given position of the given image. Image + * properties have no effect. BAKE them first before reading the pixels. * * @param backend_data backend_data * @param image_data an image data structure previously returned by the @@ -233,7 +260,7 @@ struct backend_operations { struct color *output); /// Create another instance of the `image_data`. All `image_op` and - /// `image_set_property` calls on the returned image should not affect the + /// `set_image_property` calls on the returned image should not affect the /// original image void *(*clone_image)(backend_t *base, const void *image_data, const region_t *reg_visible); diff --git a/src/backend/backend_common.c b/src/backend/backend_common.c index ad4aabf..c0377d3 100644 --- a/src/backend/backend_common.c +++ b/src/backend/backend_common.c @@ -426,6 +426,51 @@ struct dual_kawase_params *generate_dual_kawase_params(void *args) { return params; } +void *default_clone_image(backend_t *base attr_unused, const void *image_data, + const region_t *reg_visible attr_unused) { + auto new_img = ccalloc(1, struct backend_image); + *new_img = *(struct backend_image *)image_data; + new_img->inner->refcount++; + return new_img; +} + +bool default_set_image_property(backend_t *base attr_unused, enum image_properties op, + void *image_data, void *arg) { + struct backend_image *tex = image_data; + int *iargs = arg; + bool *bargs = arg; + double *dargs = arg; + switch (op) { + case IMAGE_PROPERTY_INVERTED: tex->color_inverted = bargs[0]; break; + case IMAGE_PROPERTY_DIM_LEVEL: tex->dim = dargs[0]; break; + case IMAGE_PROPERTY_OPACITY: tex->opacity = dargs[0]; break; + case IMAGE_PROPERTY_EFFECTIVE_SIZE: + // texture is already set to repeat, so nothing else we need to do + tex->ewidth = iargs[0]; + tex->eheight = iargs[1]; + break; + case IMAGE_PROPERTY_MAX_BRIGHTNESS: tex->max_brightness = dargs[0]; break; + } + + return true; +} + +bool default_is_image_transparent(backend_t *base attr_unused, void *image_data) { + struct backend_image *img = image_data; + return img->opacity < 1 || img->inner->has_alpha; +} + +struct backend_image *default_new_backend_image(int w, int h) { + auto ret = ccalloc(1, struct backend_image); + ret->opacity = 1; + ret->dim = 0; + ret->max_brightness = 1; + ret->eheight = h; + ret->ewidth = w; + ret->color_inverted = false; + return ret; +} + void init_backend_base(struct backend_base *base, session_t *ps) { base->c = ps->c; base->loop = ps->loop; diff --git a/src/backend/backend_common.h b/src/backend/backend_common.h index 938b02e..702be5c 100644 --- a/src/backend/backend_common.h +++ b/src/backend/backend_common.h @@ -7,6 +7,7 @@ #include +#include "backend.h" #include "config.h" #include "region.h" @@ -25,6 +26,22 @@ struct dual_kawase_params { int expand; }; +struct backend_image_inner_base { + int refcount; + bool has_alpha; +}; + +struct backend_image { + // Backend dependent inner image data + struct backend_image_inner_base *inner; + double opacity; + double dim; + double max_brightness; + // Effective size of the image + int ewidth, eheight; + bool color_inverted; +}; + bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width, int height, const conv *kernel, xcb_render_picture_t shadow_pixel, xcb_pixmap_t *pixmap, xcb_render_picture_t *pict); @@ -51,3 +68,9 @@ void init_backend_base(struct backend_base *base, session_t *ps); struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count); struct dual_kawase_params *generate_dual_kawase_params(void *args); + +void *default_clone_image(backend_t *base, const void *image_data, const region_t *reg); +bool default_is_image_transparent(backend_t *base attr_unused, void *image_data); +bool default_set_image_property(backend_t *base attr_unused, enum image_properties op, + void *image_data, void *arg); +struct backend_image *default_new_backend_image(int w, int h); diff --git a/src/backend/dummy/dummy.c b/src/backend/dummy/dummy.c index e00d359..7fa53d3 100644 --- a/src/backend/dummy/dummy.c +++ b/src/backend/dummy/dummy.c @@ -121,6 +121,12 @@ bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unu return true; } +bool dummy_set_image_property(struct backend_base *base, enum image_properties prop attr_unused, + void *image, void *arg attr_unused) { + dummy_check_image(base, image); + return true; +} + void *dummy_clone_image(struct backend_base *base, const void *image, const region_t *reg_visible attr_unused) { auto img = (const struct dummy_image *)image; @@ -160,6 +166,7 @@ struct backend_operations dummy_ops = { .image_op = dummy_image_op, .clone_image = dummy_clone_image, + .set_image_property = dummy_set_image_property, .create_blur_context = dummy_create_blur_context, .destroy_blur_context = dummy_destroy_blur_context, .get_blur_size = dummy_get_blur_size, diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index 9ecfb5d..b7b2652 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -271,25 +271,26 @@ _gl_average_texture_color(backend_t *base, GLuint source_texture, GLuint destina * Returned texture must not be deleted, since it's owned by the gl_image. It will be * deleted when the gl_image is released. */ -static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) { +static GLuint gl_average_texture_color(backend_t *base, struct backend_image *img) { auto gd = (struct gl_data *)base; + auto inner = (struct gl_texture *)img; // Prepare textures which will be used for destination and source of rendering // during downscaling. - const int texture_count = ARR_SIZE(img->inner->auxiliary_texture); - if (!img->inner->auxiliary_texture[0]) { - assert(!img->inner->auxiliary_texture[1]); - glGenTextures(texture_count, img->inner->auxiliary_texture); + const int texture_count = ARR_SIZE(inner->auxiliary_texture); + if (!inner->auxiliary_texture[0]) { + assert(!inner->auxiliary_texture[1]); + glGenTextures(texture_count, inner->auxiliary_texture); glActiveTexture(GL_TEXTURE0); for (int i = 0; i < texture_count; i++) { - glBindTexture(GL_TEXTURE_2D, img->inner->auxiliary_texture[i]); + glBindTexture(GL_TEXTURE_2D, inner->auxiliary_texture[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (GLint[]){0, 0, 0, 0}); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, img->inner->width, - img->inner->height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, inner->width, + inner->height, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL); } } @@ -302,7 +303,7 @@ static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) { // Enable shaders glUseProgram(gd->brightness_shader.prog); glUniform2f(glGetUniformLocationChecked(gd->brightness_shader.prog, "texsize"), - (GLfloat)img->inner->width, (GLfloat)img->inner->height); + (GLfloat)inner->width, (GLfloat)inner->height); // Prepare vertex attributes GLuint vao; @@ -327,8 +328,8 @@ static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) { // Do actual recursive render to 1x1 texture GLuint result_texture = _gl_average_texture_color( - base, img->inner->texture, img->inner->auxiliary_texture[0], - img->inner->auxiliary_texture[1], fbo, img->inner->width, img->inner->height); + base, inner->texture, inner->auxiliary_texture[0], + inner->auxiliary_texture[1], fbo, inner->width, inner->height); // Cleanup vertex attributes glDisableVertexAttribArray(vert_coord_loc); @@ -365,10 +366,11 @@ static GLuint gl_average_texture_color(backend_t *base, struct gl_image *img) { * @param reg_tgt the clip region, in Xorg coordinate system * @param reg_visible ignored */ -static void _gl_compose(backend_t *base, struct gl_image *img, GLuint target, +static void _gl_compose(backend_t *base, struct backend_image *img, GLuint target, GLint *coord, GLuint *indices, int nrects) { auto gd = (struct gl_data *)base; - if (!img || !img->inner->texture) { + auto inner = (struct gl_texture *)img->inner; + if (!img || !inner->texture) { log_error("Missing texture."); return; } @@ -406,7 +408,7 @@ static void _gl_compose(backend_t *base, struct gl_image *img, GLuint target, glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, brightness); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, img->inner->texture); + glBindTexture(GL_TEXTURE_2D, inner->texture); GLuint vao; glGenVertexArrays(1, &vao); @@ -516,7 +518,8 @@ x_rect_to_coords(int nrects, const rect_t *rects, int dst_x, int dst_y, int text void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y, const region_t *reg_tgt, const region_t *reg_visible attr_unused) { auto gd = (struct gl_data *)base; - struct gl_image *img = image_data; + struct backend_image *img = image_data; + auto inner = (struct gl_texture *)img->inner; // Painting int nrects; @@ -536,8 +539,8 @@ void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y, auto coord = ccalloc(nrects * 16, GLint); auto indices = ccalloc(nrects * 6, GLuint); - x_rect_to_coords(nrects, rects, dst_x, dst_y, img->inner->height, gd->height, - img->inner->y_inverted, coord, indices); + x_rect_to_coords(nrects, rects, dst_x, dst_y, inner->height, gd->height, + inner->y_inverted, coord, indices); _gl_compose(base, img, gd->back_fbo, coord, indices, nrects); free(indices); @@ -1067,33 +1070,26 @@ void gl_fill(backend_t *base, struct color c, const region_t *clip) { return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true); } -void gl_release_image(backend_t *base, void *image_data) { - struct gl_image *wd = image_data; +static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) { auto gd = (struct gl_data *)base; - wd->inner->refcount--; - assert(wd->inner->refcount >= 0); - if (wd->inner->refcount > 0) { - free(wd); - return; - } + gd->release_user_data(base, inner); + assert(inner->user_data == NULL); - gd->release_user_data(base, wd->inner); - assert(wd->inner->user_data == NULL); - - glDeleteTextures(1, &wd->inner->texture); - glDeleteTextures(2, wd->inner->auxiliary_texture); - free(wd->inner); - free(wd); + glDeleteTextures(1, &inner->texture); + glDeleteTextures(2, inner->auxiliary_texture); + free(inner); gl_check_err(); } -void *gl_clone(backend_t *base attr_unused, const void *image_data, - const region_t *reg_visible attr_unused) { - const struct gl_image *img = image_data; - auto new_img = ccalloc(1, struct gl_image); - *new_img = *img; - new_img->inner->refcount++; - return new_img; +void gl_release_image(backend_t *base, void *image_data) { + struct backend_image *wd = image_data; + auto inner = (struct gl_texture *)wd->inner; + inner->refcount--; + assert(inner->refcount >= 0); + if (inner->refcount == 0) { + gl_release_image_inner(base, inner); + } + free(wd); } static inline void gl_free_blur_shader(gl_blur_shader_t *shader) { @@ -1750,24 +1746,58 @@ GLuint gl_new_texture(GLenum target) { return texture; } -/// Decouple `img` from the image it references, also applies all the lazy operations -static inline void gl_image_decouple(backend_t *base, struct gl_image *img) { +/// Actually duplicate a texture into a new one, if this texture is shared +static inline void gl_image_decouple(backend_t *base, struct backend_image *img) { if (img->inner->refcount == 1) { return; } - auto gd = (struct gl_data *)base; + auto inner = (struct gl_texture *)img->inner; auto new_tex = ccalloc(1, struct gl_texture); new_tex->texture = gl_new_texture(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, new_tex->texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img->inner->width, img->inner->height, 0, - GL_BGRA, GL_UNSIGNED_BYTE, NULL); new_tex->y_inverted = true; - new_tex->height = img->inner->height; - new_tex->width = img->inner->width; + new_tex->height = inner->height; + new_tex->width = inner->width; new_tex->refcount = 1; - new_tex->user_data = gd->decouple_texture_user_data(base, img->inner->user_data); + new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data); + + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + inner->texture, 0); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindTexture(GL_TEXTURE_2D, new_tex->texture); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, new_tex->width, new_tex->height, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + + img->inner = (struct backend_image_inner_base *)new_tex; + inner->refcount--; +} + +/// Decouple `img` from the image it references, also applies all the lazy operations +static inline void gl_image_bake(backend_t *base, struct backend_image *img) { + if (!img->color_inverted && img->opacity == 1 && img->max_brightness == 1 && + img->dim == 0) { + // Nothing to bake + return; + } + auto gd = (struct gl_data *)base; + auto new_tex = ccalloc(1, struct gl_texture); + auto inner = (struct gl_texture *)img->inner; + + new_tex->texture = gl_new_texture(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, new_tex->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, inner->width, inner->height, 0, GL_BGRA, + GL_UNSIGNED_BYTE, NULL); + new_tex->y_inverted = true; + new_tex->height = inner->height; + new_tex->width = inner->width; + new_tex->refcount = 1; + new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data); GLuint fbo; glGenFramebuffers(1, &fbo); @@ -1786,16 +1816,16 @@ static inline void gl_image_decouple(backend_t *base, struct gl_image *img) { 0, 0, // texture coord // top right - img->inner->width, 0, // vertex coord - img->inner->width, 0, // texture coord + inner->width, 0, // vertex coord + inner->width, 0, // texture coord // bottom right - img->inner->width, img->inner->height, - img->inner->width, img->inner->height, + inner->width, inner->height, + inner->width, inner->height, // bottom left - 0, img->inner->height, - 0, img->inner->height, + 0, inner->height, + 0, inner->height, }; // clang-format on @@ -1803,8 +1833,11 @@ static inline void gl_image_decouple(backend_t *base, struct gl_image *img) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); - img->inner->refcount--; - img->inner = new_tex; + inner->refcount--; + if (inner->refcount == 0) { + gl_release_image_inner(base, inner); + } + img->inner = (struct backend_image_inner_base *)new_tex; // Clear lazy operation flags img->color_inverted = false; @@ -1814,16 +1847,17 @@ static inline void gl_image_decouple(backend_t *base, struct gl_image *img) { gl_check_err(); } -static void gl_image_apply_alpha(backend_t *base, struct gl_image *img, +static void gl_image_apply_alpha(backend_t *base, struct backend_image *img, const region_t *reg_op, double alpha) { // Result color = 0 (GL_ZERO) + alpha (GL_CONSTANT_ALPHA) * original color + auto inner = (struct gl_texture *)img->inner; glBlendFunc(GL_ZERO, GL_CONSTANT_ALPHA); glBlendColor(0, 0, 0, (GLclampf)alpha); GLuint fbo; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - img->inner->texture, 0); + inner->texture, 0); glDrawBuffer(GL_COLOR_ATTACHMENT0); _gl_fill(base, (struct color){0, 0, 0, 0}, reg_op, fbo, 0, false); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -1883,40 +1917,28 @@ void gl_present(backend_t *base, const region_t *region) { free(indices); } -/// stub for backend_operations::image_op bool gl_image_op(backend_t *base, enum image_operations op, void *image_data, const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) { - struct gl_image *tex = image_data; - int *iargs = arg; + struct backend_image *tex = image_data; switch (op) { - case IMAGE_OP_INVERT_COLOR_ALL: tex->color_inverted = true; break; - case IMAGE_OP_DIM_ALL: - tex->dim = 1.0 - (1.0 - tex->dim) * (1.0 - *(double *)arg); - break; - case IMAGE_OP_APPLY_ALPHA_ALL: tex->opacity *= *(double *)arg; break; case IMAGE_OP_APPLY_ALPHA: gl_image_decouple(base, tex); assert(tex->inner->refcount == 1); gl_image_apply_alpha(base, tex, reg_op, *(double *)arg); break; - case IMAGE_OP_RESIZE_TILE: - // texture is already set to repeat, so nothing else we need to do - tex->ewidth = iargs[0]; - tex->eheight = iargs[1]; - break; - case IMAGE_OP_MAX_BRIGHTNESS: tex->max_brightness = *(double *)arg; break; + case IMAGE_OP_BAKE_PROPERTIES: gl_image_bake(base, tex); break; } return true; } -bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color *output) { - struct gl_image *tex = image_data; - gl_image_decouple(base, tex); - assert(tex->inner->refcount == 1); +bool gl_read_pixel(backend_t *base attr_unused, void *image_data, int x, int y, + struct color *output) { + struct backend_image *tex = image_data; + auto inner = (struct gl_texture *)tex->inner; GLfloat color[4]; - glReadPixels(x, tex->inner->y_inverted ? tex->inner->height - y : y, 1, 1, - GL_RGBA, GL_FLOAT, color); + glReadPixels(x, inner->y_inverted ? inner->height - y : y, 1, 1, GL_RGBA, + GL_FLOAT, color); output->alpha = color[3]; output->red = color[0]; output->green = color[1]; @@ -1926,8 +1948,3 @@ bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color gl_clear_err(); return ret; } - -bool gl_is_image_transparent(backend_t *base attr_unused, void *image_data) { - struct gl_image *img = image_data; - return img->has_alpha; -} diff --git a/src/backend/gl/gl_common.h b/src/backend/gl/gl_common.h index 709dc29..c1e56bf 100644 --- a/src/backend/gl/gl_common.h +++ b/src/backend/gl/gl_common.h @@ -44,8 +44,10 @@ typedef struct { GLint color_loc; } gl_fill_shader_t; +/// @brief Wrapper of a binded GLX texture. struct gl_texture { int refcount; + bool has_alpha; GLuint texture; int width, height; bool y_inverted; @@ -55,17 +57,6 @@ struct gl_texture { void *user_data; }; -/// @brief Wrapper of a binded GLX texture. -typedef struct gl_image { - struct gl_texture *inner; - double opacity; - double dim; - double max_brightness; - int ewidth, eheight; - bool has_alpha; - bool color_inverted; -} gl_image_t; - struct gl_data { backend_t base; // If we are using proprietary NVIDIA driver @@ -123,7 +114,6 @@ void *gl_create_blur_context(backend_t *base, enum blur_method, void *args); void gl_destroy_blur_context(backend_t *base, void *ctx); void gl_get_blur_size(void *blur_context, int *width, int *height); -bool gl_is_image_transparent(backend_t *base, void *image_data); void gl_fill(backend_t *base, struct color, const region_t *clip); void gl_present(backend_t *base, const region_t *); diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index be6183c..6cccaa9 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -388,11 +388,12 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b } log_trace("Binding pixmap %#010x", pixmap); - auto wd = ccalloc(1, struct gl_image); + auto wd = ccalloc(1, struct backend_image); wd->max_brightness = 1; - wd->inner = ccalloc(1, struct gl_texture); - wd->inner->width = wd->ewidth = r->width; - wd->inner->height = wd->eheight = r->height; + auto inner = ccalloc(1, struct gl_texture); + inner->width = wd->ewidth = r->width; + inner->height = wd->eheight = r->height; + wd->inner = (struct backend_image_inner_base *)inner; free(r); auto fbcfg = glx_find_fbconfig(gd->display, gd->screen, fmt); @@ -420,7 +421,7 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b 0, }; - wd->inner->y_inverted = fbcfg->y_inverted; + inner->y_inverted = fbcfg->y_inverted; glxpixmap = cmalloc(struct _glx_pixmap); glxpixmap->pixmap = pixmap; @@ -436,14 +437,14 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b log_trace("GLXPixmap %#010lx", glxpixmap->glpixmap); // Create texture - wd->inner->user_data = glxpixmap; - wd->inner->texture = gl_new_texture(GL_TEXTURE_2D); + inner->user_data = glxpixmap; + inner->texture = gl_new_texture(GL_TEXTURE_2D); + inner->has_alpha = fmt.alpha_size != 0; wd->opacity = 1; wd->color_inverted = false; wd->dim = 0; - wd->has_alpha = fmt.alpha_size != 0; wd->inner->refcount = 1; - glBindTexture(GL_TEXTURE_2D, wd->inner->texture); + glBindTexture(GL_TEXTURE_2D, inner->texture); glXBindTexImageEXT(gd->display, glxpixmap->glpixmap, GLX_FRONT_LEFT_EXT, NULL); glBindTexture(GL_TEXTURE_2D, 0); @@ -526,10 +527,11 @@ struct backend_operations glx_ops = { .release_image = gl_release_image, .compose = gl_compose, .image_op = gl_image_op, + .set_image_property = default_set_image_property, .read_pixel = gl_read_pixel, - .clone_image = gl_clone, + .clone_image = default_clone_image, .blur = gl_blur, - .is_image_transparent = gl_is_image_transparent, + .is_image_transparent = default_is_image_transparent, .present = glx_present, .buffer_age = glx_buffer_age, .render_shadow = default_backend_render_shadow, diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index e60c8de..725e5bb 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -70,41 +70,136 @@ struct _xrender_blur_context { int x_blur_kernel_count; }; -struct _xrender_image_data { +struct _xrender_image_data_inner { + // struct backend_image_inner_base + int refcount; + bool has_alpha; + // Pixmap that the client window draws to, // it will contain the content of client window. xcb_pixmap_t pixmap; // A Picture links to the Pixmap xcb_render_picture_t pict; int width, height; - // The effective size of the image - int ewidth, eheight; - bool has_alpha; - double opacity; xcb_visualid_t visual; uint8_t depth; + // Whether we own this image, e.g. we allocated it; + // or not, e.g. this is a named pixmap of a X window. bool owned; }; +static void compose_impl(struct _xrender_data *xd, const struct backend_image *img, + int dst_x, int dst_y, const region_t *reg_paint, + const region_t *reg_visible, xcb_render_picture_t result) { + auto alpha_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)]; + auto inner = (struct _xrender_image_data_inner *)img->inner; + region_t reg; + + bool has_alpha = inner->has_alpha || img->opacity != 1; + const auto tmpw = to_u16_checked(inner->width); + const auto tmph = to_u16_checked(inner->height); + const auto tmpew = to_u16_checked(img->ewidth); + const auto tmpeh = to_u16_checked(img->eheight); + const xcb_render_color_t dim_color = { + .red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * img->dim)}; + + // Clip region of rendered_pict might be set during rendering, clear it to + // make sure we get everything into the buffer + x_clear_picture_clip_region(xd->base.c, inner->pict); + + pixman_region32_init(®); + pixman_region32_intersect(®, (region_t *)reg_paint, (region_t *)reg_visible); + x_set_picture_clip_region(xd->base.c, result, 0, 0, ®); + if ((img->color_inverted || img->dim != 0) && has_alpha) { + // Apply image properties using a temporary image, because the source + // image is transparent. Otherwise the properties can be applied directly + // on the target image. + auto tmp_pict = + x_create_picture_with_visual(xd->base.c, xd->base.root, inner->width, + inner->height, inner->visual, 0, NULL); + + // Set clip region translated to source coordinate + x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst_x), + to_i16_checked(-dst_y), ®); + // Copy source -> tmp + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict, + XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); + if (img->color_inverted) { + if (inner->has_alpha) { + auto tmp_pict2 = x_create_picture_with_visual( + xd->base.c, xd->base.root, tmpw, tmph, inner->visual, + 0, NULL); + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, + tmp_pict, XCB_NONE, tmp_pict2, 0, 0, + 0, 0, 0, 0, tmpw, tmph); + + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE, + xd->white_pixel, XCB_NONE, tmp_pict, + 0, 0, 0, 0, 0, 0, tmpw, tmph); + xcb_render_composite( + xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE, tmp_pict2, + XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); + xcb_render_free_picture(xd->base.c, tmp_pict2); + } else { + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE, + xd->white_pixel, XCB_NONE, tmp_pict, + 0, 0, 0, 0, 0, 0, tmpw, tmph); + } + } + if (img->dim != 0) { + // Dim the actually content of window + xcb_rectangle_t rect = { + .x = 0, + .y = 0, + .width = tmpw, + .height = tmph, + }; + + xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER, + tmp_pict, dim_color, 1, &rect); + } + + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict, + alpha_pict, result, 0, 0, 0, 0, to_i16_checked(dst_x), + to_i16_checked(dst_y), tmpew, tmpeh); + xcb_render_free_picture(xd->base.c, tmp_pict); + } else { + uint8_t op = (has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC); + + xcb_render_composite(xd->base.c, op, inner->pict, alpha_pict, result, 0, + 0, 0, 0, to_i16_checked(dst_x), + to_i16_checked(dst_y), tmpew, tmpeh); + if (img->dim != 0 || img->color_inverted) { + // Apply properties, if we reach here, then has_alpha == false + assert(!has_alpha); + if (img->color_inverted) { + xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE, + xd->white_pixel, XCB_NONE, result, 0, + 0, 0, 0, to_i16_checked(dst_x), + to_i16_checked(dst_y), tmpew, tmpeh); + } + + if (img->dim != 0) { + // Dim the actually content of window + xcb_rectangle_t rect = { + .x = to_i16_checked(dst_x), + .y = to_i16_checked(dst_y), + .width = tmpew, + .height = tmpeh, + }; + + xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER, + result, dim_color, 1, &rect); + } + } + } + pixman_region32_fini(®); +} + static void compose(backend_t *base, void *img_data, int dst_x, int dst_y, const region_t *reg_paint, const region_t *reg_visible) { struct _xrender_data *xd = (void *)base; - struct _xrender_image_data *img = img_data; - uint8_t op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC); - auto alpha_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)]; - region_t reg; - pixman_region32_init(®); - pixman_region32_intersect(®, (region_t *)reg_paint, (region_t *)reg_visible); - - // Clip region of rendered_pict might be set during rendering, clear it to make - // sure we get everything into the buffer - x_clear_picture_clip_region(base->c, img->pict); - - x_set_picture_clip_region(base->c, xd->back[2], 0, 0, ®); - xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0, - to_i16_checked(dst_x), to_i16_checked(dst_y), - to_u16_checked(img->ewidth), to_u16_checked(img->eheight)); - pixman_region32_fini(®); + return compose_impl(xd, img_data, dst_x, dst_y, reg_paint, reg_visible, xd->back[2]); } static void fill(backend_t *base, struct color c, const region_t *clip) { @@ -255,31 +350,42 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool return NULL; } - auto img = ccalloc(1, struct _xrender_image_data); - img->depth = (uint8_t)fmt.visual_depth; - img->width = img->ewidth = r->width; - img->height = img->eheight = r->height; - img->pixmap = pixmap; - img->opacity = 1; - img->has_alpha = fmt.alpha_size != 0; - img->pict = + auto img = ccalloc(1, struct backend_image); + auto inner = ccalloc(1, struct _xrender_image_data_inner); + inner->depth = (uint8_t)fmt.visual_depth; + inner->width = img->ewidth = r->width; + inner->height = img->eheight = r->height; + inner->pixmap = pixmap; + inner->has_alpha = fmt.alpha_size != 0; + inner->pict = x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL); - img->owned = owned; - img->visual = fmt.visual; + inner->owned = owned; + inner->visual = fmt.visual; + inner->refcount = 1; + + img->inner = (struct backend_image_inner_base *)inner; + img->opacity = 1; free(r); - if (img->pict == XCB_NONE) { + if (inner->pict == XCB_NONE) { + free(inner); free(img); return NULL; } return img; } - +static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) { + xcb_render_free_picture(base->c, inner->pict); + if (inner->owned) { + xcb_free_pixmap(base->c, inner->pixmap); + } + free(inner); +} static void release_image(backend_t *base, void *image) { - struct _xrender_image_data *img = image; - xcb_render_free_picture(base->c, img->pict); - if (img->owned) { - xcb_free_pixmap(base->c, img->pixmap); + struct backend_image *img = image; + img->inner->refcount--; + if (img->inner->refcount == 0) { + release_image_inner(base, (void *)img->inner); } free(img); } @@ -375,71 +481,99 @@ static int buffer_age(backend_t *backend_data) { return xd->buffer_age[xd->curr_back]; } -static bool is_image_transparent(backend_t *bd attr_unused, void *image) { - struct _xrender_image_data *img = image; - return img->has_alpha; +static struct _xrender_image_data_inner * +new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) { + auto new_inner = ccalloc(1, struct _xrender_image_data_inner); + new_inner->pixmap = x_create_pixmap(base->c, depth, base->root, w, h); + if (new_inner->pixmap == XCB_NONE) { + log_error("Failed to create pixmap for copy"); + free(new_inner); + return NULL; + } + new_inner->pict = x_create_picture_with_visual_and_pixmap( + base->c, visual, new_inner->pixmap, 0, NULL); + if (new_inner->pict == XCB_NONE) { + log_error("Failed to create picture for copy"); + xcb_free_pixmap(base->c, new_inner->pixmap); + free(new_inner); + return NULL; + } + new_inner->width = w; + new_inner->height = h; + new_inner->visual = visual; + new_inner->depth = depth; + new_inner->refcount = 1; + new_inner->owned = true; + return new_inner; +} + +static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) { + if (img->inner->refcount == 1) { + return true; + } + auto inner = (struct _xrender_image_data_inner *)img->inner; + auto inner2 = + new_inner(base, inner->width, inner->height, inner->visual, inner->depth); + if (!inner2) { + return false; + } + + x_set_picture_clip_region(base->c, inner->pict, 0, 0, reg); + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE, + inner2->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width), + to_u16_checked(inner->height)); + + img->inner = (struct backend_image_inner_base *)inner2; + inner->refcount--; + return true; +} +static bool bake_image(backend_t *base, struct backend_image *img, const region_t *reg) { + struct _xrender_data *xd = (void *)base; + struct _xrender_image_data_inner *inner = (void *)img->inner; + assert(inner->visual != XCB_NONE); + + if (!img->color_inverted && img->opacity == 1 && img->dim == 0) { + // Nothing to bake + return true; + } + + log_trace("xrender: copying %#010x visual %#x", inner->pixmap, inner->visual); + auto inner2 = + new_inner(base, inner->width, inner->height, inner->visual, inner->depth); + if (!inner2) { + return false; + } + + inner2->has_alpha = (inner->has_alpha || img->opacity != 1); + compose_impl(xd, img, 0, 0, reg, NULL, inner2->pict); + + img->opacity = 1; + img->inner = (struct backend_image_inner_base *)inner2; + img->dim = 0; + img->color_inverted = false; + inner->refcount--; + if (inner->refcount == 0) { + release_image_inner(base, inner); + } + return true; } static bool image_op(backend_t *base, enum image_operations op, void *image, const region_t *reg_op, const region_t *reg_visible, void *arg) { struct _xrender_data *xd = (void *)base; - struct _xrender_image_data *img = image; + struct backend_image *img = image; region_t reg; double *dargs = arg; - int *iargs = arg; - if (op == IMAGE_OP_APPLY_ALPHA_ALL) { - img->opacity *= dargs[0]; - img->has_alpha = true; - return true; - } pixman_region32_init(®); + pixman_region32_intersect(®, (region_t *)reg_op, (region_t *)reg_visible); - const auto tmpw = to_u16_checked(img->width); - const auto tmph = to_u16_checked(img->height); switch (op) { - case IMAGE_OP_INVERT_COLOR_ALL: - x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible); - if (img->has_alpha) { - auto tmp_pict = - x_create_picture_with_visual(base->c, base->root, img->width, - img->height, img->visual, 0, NULL); - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict, - XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); + case IMAGE_OP_BAKE_PROPERTIES: return bake_image(base, img, ®); - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE, - xd->white_pixel, XCB_NONE, img->pict, 0, 0, - 0, 0, 0, 0, tmpw, tmph); - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN_REVERSE, - tmp_pict, XCB_NONE, img->pict, 0, 0, 0, 0, 0, - 0, tmpw, tmph); - xcb_render_free_picture(base->c, tmp_pict); - } else { - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE, - xd->white_pixel, XCB_NONE, img->pict, 0, 0, - 0, 0, 0, 0, tmpw, tmph); - } - break; - case IMAGE_OP_DIM_ALL: - x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible); - - xcb_render_color_t color = { - .red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * dargs[0])}; - - // Dim the actually content of window - xcb_rectangle_t rect = { - .x = 0, - .y = 0, - .width = tmpw, - .height = tmph, - }; - - xcb_render_fill_rectangles(base->c, XCB_RENDER_PICT_OP_OVER, img->pict, - color, 1, &rect); - break; case IMAGE_OP_APPLY_ALPHA: assert(reg_op); - pixman_region32_intersect(®, (region_t *)reg_op, (region_t *)reg_visible); + if (!pixman_region32_not_empty(®)) { break; } @@ -448,58 +582,25 @@ static bool image_op(backend_t *base, enum image_operations op, void *image, break; } + if (!decouple_image(base, img, reg_visible)) { + pixman_region32_fini(®); + return false; + } + + auto inner = (struct _xrender_image_data_inner *)img->inner; auto alpha_pict = xd->alpha_pict[(int)((1 - dargs[0]) * MAX_ALPHA)]; - x_set_picture_clip_region(base->c, img->pict, 0, 0, ®); + x_set_picture_clip_region(base->c, inner->pict, 0, 0, ®); xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OUT_REVERSE, alpha_pict, - XCB_NONE, img->pict, 0, 0, 0, 0, 0, 0, tmpw, tmph); - img->has_alpha = true; + XCB_NONE, inner->pict, 0, 0, 0, 0, 0, 0, + to_u16_checked(inner->width), + to_u16_checked(inner->height)); + inner->has_alpha = true; break; - case IMAGE_OP_RESIZE_TILE: - img->ewidth = iargs[0]; - img->eheight = iargs[1]; - break; - case IMAGE_OP_APPLY_ALPHA_ALL: assert(false); - case IMAGE_OP_MAX_BRIGHTNESS: assert(false); } pixman_region32_fini(®); return true; } -// TODO(yshui): use copy-on-write -static void *clone_image(backend_t *base, const void *image, const region_t *reg) { - const struct _xrender_image_data *img = image; - struct _xrender_data *xd = (void *)base; - auto new_img = ccalloc(1, struct _xrender_image_data); - assert(img->visual != XCB_NONE); - log_trace("xrender: copying %#010x visual %#x", img->pixmap, img->visual); - *new_img = *img; - x_set_picture_clip_region(base->c, img->pict, 0, 0, reg); - new_img->pixmap = - x_create_pixmap(base->c, img->depth, base->root, img->width, img->height); - new_img->opacity = 1; - new_img->owned = true; - if (new_img->pixmap == XCB_NONE) { - log_error("Failed to create pixmap for copy"); - free(new_img); - return NULL; - } - new_img->pict = x_create_picture_with_visual_and_pixmap(base->c, img->visual, - new_img->pixmap, 0, NULL); - if (new_img->pict == XCB_NONE) { - log_error("Failed to create picture for copy"); - xcb_free_pixmap(base->c, new_img->pixmap); - free(new_img); - return NULL; - } - - xcb_render_picture_t alpha_pict = - img->opacity == 1 ? XCB_NONE : xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)]; - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict, alpha_pict, - new_img->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(img->width), - to_u16_checked(img->height)); - return new_img; -} - static void * create_blur_context(backend_t *base attr_unused, enum blur_method method, void *args) { auto ret = ccalloc(1, struct _xrender_blur_context); @@ -562,9 +663,10 @@ static void get_blur_size(void *blur_context, int *width, int *height) { static bool read_pixel(backend_t *backend_data, void *image_data, int x, int y, struct color *output) { auto xd = (struct _xrender_data *)backend_data; - auto img = (struct _xrender_image_data *)image_data; + auto img = (struct backend_image *)image_data; + auto inner = (struct _xrender_image_data_inner *)img->inner; - auto r = XCB_AWAIT(xcb_get_image, xd->base.c, XCB_IMAGE_FORMAT_XY_PIXMAP, img->pixmap, + auto r = XCB_AWAIT(xcb_get_image, xd->base.c, XCB_IMAGE_FORMAT_XY_PIXMAP, inner->pixmap, to_i16_checked(x), to_i16_checked(y), 1, 1, (uint32_t)-1L); if (!r) { @@ -673,13 +775,14 @@ struct backend_operations xrender_ops = { .render_shadow = default_backend_render_shadow, //.prepare_win = prepare_win, //.release_win = release_win, - .is_image_transparent = is_image_transparent, + .is_image_transparent = default_is_image_transparent, .buffer_age = buffer_age, .max_buffer_age = 2, .image_op = image_op, .read_pixel = read_pixel, - .clone_image = clone_image, + .clone_image = default_clone_image, + .set_image_property = default_set_image_property, .create_blur_context = create_blur_context, .destroy_blur_context = destroy_blur_context, .get_blur_size = get_blur_size, diff --git a/src/picom.c b/src/picom.c index 81e3561..ee94b35 100644 --- a/src/picom.c +++ b/src/picom.c @@ -893,9 +893,9 @@ void root_damaged(session_t *ps) { if (pixmap != XCB_NONE) { ps->root_image = ps->backend_data->ops->bind_pixmap( ps->backend_data, pixmap, x_get_visual_info(ps->c, ps->vis), false); - ps->backend_data->ops->image_op( - ps->backend_data, IMAGE_OP_RESIZE_TILE, ps->root_image, NULL, - NULL, (int[]){ps->root_width, ps->root_height}); + ps->backend_data->ops->set_image_property( + ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE, + ps->root_image, (int[]){ps->root_width, ps->root_height}); } }