aboutsummaryrefslogtreecommitdiff
path: root/src/backend/xrender
diff options
context:
space:
mode:
authorallusive-dev <[email protected]>2023-10-30 15:12:21 +1100
committerallusive-dev <[email protected]>2023-10-30 15:12:21 +1100
commitac33357e7ce7c474aeaffc92e381020289d767a2 (patch)
tree7f05fa79b3ccd7834f85cc65a07fbd4f8030eb94 /src/backend/xrender
parentCreate FUNDING.yml (diff)
downloadcompfy-ac33357e7ce7c474aeaffc92e381020289d767a2.tar.xz
compfy-ac33357e7ce7c474aeaffc92e381020289d767a2.zip
Version 1.01.0.0
Diffstat (limited to 'src/backend/xrender')
-rw-r--r--src/backend/xrender/xrender.c382
1 files changed, 296 insertions, 86 deletions
diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c
index ccf358b..2b7f8e1 100644
--- a/src/backend/xrender/xrender.c
+++ b/src/backend/xrender/xrender.c
@@ -88,19 +88,153 @@ struct _xrender_image_data_inner {
bool owned;
};
-static void compose_impl(struct _xrender_data *xd, const struct backend_image *img,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
- 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)];
+struct xrender_rounded_rectangle_cache {
+ int refcount;
+ // A cached picture of a rounded rectangle. Xorg rasterizes shapes on CPU so it's
+ // exceedingly slow.
+ xcb_render_picture_t p;
+};
+
+struct xrender_image {
+ struct backend_image base;
+
+ struct xrender_rounded_rectangle_cache *rounded_rectangle;
+};
+
+/// Make a picture of size width x height, which has a rounded rectangle of corner_radius
+/// rendered in it.
+struct xrender_rounded_rectangle_cache *
+make_rounded_corner_cache(xcb_connection_t *c, xcb_render_picture_t src,
+ xcb_drawable_t root, int width, int height, int corner_radius) {
+ auto picture = x_create_picture_with_standard(c, root, width, height,
+ XCB_PICT_STANDARD_ARGB_32, 0, NULL);
+ if (picture == XCB_NONE) {
+ return NULL;
+ }
+
+ int inner_height = height - 2 * corner_radius;
+ int cap_height = corner_radius;
+ if (inner_height < 0) {
+ cap_height = height / 2;
+ inner_height = 0;
+ }
+ auto points = ccalloc(cap_height * 4 + 4, xcb_render_pointfix_t);
+ int point_count = 0;
+
+#define ADD_POINT(px, py) \
+ assert(point_count < cap_height * 4 + 4); \
+ points[point_count].x = DOUBLE_TO_XFIXED(px); \
+ points[point_count].y = DOUBLE_TO_XFIXED(py); \
+ point_count += 1;
+
+ // The top cap
+ for (int i = 0; i <= cap_height; i++) {
+ double y = corner_radius - i;
+ double delta = sqrt(corner_radius * corner_radius - y * y);
+ double left = corner_radius - delta;
+ double right = width - corner_radius + delta;
+ if (left >= right) {
+ continue;
+ }
+ ADD_POINT(left, i);
+ ADD_POINT(right, i);
+ }
+
+ // The middle rectangle
+ if (inner_height > 0) {
+ ADD_POINT(0, cap_height + inner_height);
+ ADD_POINT(width, cap_height + inner_height);
+ }
+
+ // The bottom cap
+ for (int i = cap_height + inner_height + 1; i <= height; i++) {
+ double y = corner_radius - (height - i);
+ double delta = sqrt(corner_radius * corner_radius - y * y);
+ double left = corner_radius - delta;
+ double right = width - corner_radius + delta;
+ if (left >= right) {
+ break;
+ }
+ ADD_POINT(left, i);
+ ADD_POINT(right, i);
+ }
+#undef ADD_POINT
+
+ XCB_AWAIT_VOID(xcb_render_tri_strip, c, XCB_RENDER_PICT_OP_SRC, src, picture,
+ x_get_pictfmt_for_standard(c, XCB_PICT_STANDARD_A_8), 0, 0,
+ (uint32_t)point_count, points);
+ free(points);
+ auto ret = ccalloc(1, struct xrender_rounded_rectangle_cache);
+ ret->p = picture;
+ ret->refcount = 1;
+ return ret;
+}
+
+static xcb_render_picture_t process_mask(struct _xrender_data *xd, struct xrender_image *mask,
+ xcb_render_picture_t alpha_pict, bool *allocated) {
+ auto inner = (struct _xrender_image_data_inner *)mask->base.inner;
+ if (!mask->base.color_inverted && mask->base.corner_radius == 0) {
+ *allocated = false;
+ return inner->pict;
+ }
+ const auto tmpw = to_u16_checked(inner->width);
+ const auto tmph = to_u16_checked(inner->height);
+ *allocated = true;
+ x_clear_picture_clip_region(xd->base.c, inner->pict);
+ auto ret = x_create_picture_with_visual(
+ xd->base.c, xd->base.root, inner->width, inner->height, inner->visual,
+ XCB_RENDER_CP_REPEAT,
+ (xcb_render_create_picture_value_list_t[]){XCB_RENDER_REPEAT_PAD});
+ xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
+ ret, 0, 0, 0, 0, 0, 0, tmpw, tmph);
+ // Remember: the mask has a 1-pixel border
+ if (mask->base.corner_radius != 0) {
+ if (mask->rounded_rectangle == NULL) {
+ mask->rounded_rectangle = make_rounded_corner_cache(
+ xd->base.c, xd->white_pixel, xd->base.root, inner->width - 2,
+ inner->height - 2, (int)mask->base.corner_radius);
+ }
+ xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE,
+ mask->rounded_rectangle->p, XCB_NONE, ret, 0, 0, 0,
+ 0, 1, 1, (uint16_t)(tmpw - 2), (uint16_t)(tmph - 2));
+ }
+
+ if (mask->base.color_inverted) {
+ xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_XOR, xd->white_pixel,
+ XCB_NONE, ret, 0, 0, 0, 0, 0, 0, tmpw, tmph);
+ }
+
+ if (alpha_pict != XCB_NONE) {
+ xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, ret, alpha_pict,
+ ret, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width),
+ to_u16_checked(inner->height));
+ }
+
+ return ret;
+}
+
+static void
+compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
+ struct xrender_image *mask, coord_t mask_dst, const region_t *reg_paint,
+ const region_t *reg_visible, xcb_render_picture_t result) {
+ const struct backend_image *img = &xrimg->base;
+ bool mask_allocated = false;
+ auto mask_pict = xd->alpha_pict[(int)(img->opacity * MAX_ALPHA)];
+ if (mask != NULL) {
+ mask_pict = process_mask(
+ xd, mask, img->opacity < 1.0 ? mask_pict : XCB_NONE, &mask_allocated);
+ }
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(dst_x2 - dst_x1);
- const auto tmph = to_u16_checked(dst_y2 - dst_y1);
- const auto tmpew = to_u16_checked(dst_x2 - dst_x1);
- const auto tmpeh = to_u16_checked(dst_y2 - dst_y1);
+ 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);
+ // Remember: the mask has a 1-pixel border
+ const auto mask_dst_x = to_i16_checked(dst.x - mask_dst.x + 1);
+ const auto mask_dst_y = to_i16_checked(dst.y - mask_dst.y + 1);
const xcb_render_color_t dim_color = {
.red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * img->dim)};
@@ -111,20 +245,12 @@ static void compose_impl(struct _xrender_data *xd, const struct backend_image *i
pixman_region32_init(&reg);
pixman_region32_intersect(&reg, (region_t *)reg_paint, (region_t *)reg_visible);
x_set_picture_clip_region(xd->base.c, result, 0, 0, &reg);
-
-#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t)(((double)(value)) * 65536))
- {
- const xcb_render_transform_t transform = {
- DOUBLE_TO_XFIXED((double)img->ewidth / (double)tmpew), DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(0.0),
- DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED((double)img->eheight / (double)tmpeh), DOUBLE_TO_XFIXED(0.0),
- DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(0.0), DOUBLE_TO_XFIXED(1.0),
- };
- xcb_render_set_picture_transform(xd->base.c, inner->pict, transform);
- xcb_render_set_picture_filter(xd->base.c, inner->pict, 7, "nearest", 0, NULL);
- }
-#undef DOUBLE_TO_XFIXED
-
- if ((img->color_inverted || img->dim != 0) && has_alpha) {
+ if (img->corner_radius != 0 && xrimg->rounded_rectangle == NULL) {
+ xrimg->rounded_rectangle = make_rounded_corner_cache(
+ xd->base.c, xd->white_pixel, xd->base.root, inner->width,
+ inner->height, (int)img->corner_radius);
+ }
+ if (((img->color_inverted || img->dim != 0) && has_alpha) || img->corner_radius != 0) {
// Apply image properties using a temporary image, because the source
// image is transparent. Otherwise the properties can be applied directly
// on the target image.
@@ -133,17 +259,25 @@ static void compose_impl(struct _xrender_data *xd, const struct backend_image *i
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_x1),
- to_i16_checked(-dst_y1), &reg);
+ x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst.x),
+ to_i16_checked(-dst.y), &reg);
// Copy source -> tmp
- xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, inner->pict,
+ 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->corner_radius != 0 && xrimg->rounded_rectangle != NULL) {
+ // Clip tmp_pict with a rounded rectangle
+ xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE,
+ xrimg->rounded_rectangle->p, 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_OVER,
+ 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);
@@ -174,30 +308,31 @@ static void compose_impl(struct _xrender_data *xd, const struct backend_image *i
}
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict,
- alpha_pict, result, 0, 0, 0, 0, to_i16_checked(dst_x1),
- to_i16_checked(dst_y1), tmpew, tmpeh);
+ mask_pict, result, 0, 0, mask_dst_x, mask_dst_y,
+ 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_x1),
- to_i16_checked(dst_y1), tmpew, tmpeh);
+ xcb_render_composite(xd->base.c, op, inner->pict, mask_pict, result, 0, 0,
+ mask_dst_x, mask_dst_y, 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_x1),
- to_i16_checked(dst_y1), tmpew, tmpeh);
+ 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_x1),
- .y = to_i16_checked(dst_y1),
+ .x = to_i16_checked(dst.x),
+ .y = to_i16_checked(dst.y),
.width = tmpew,
.height = tmpeh,
};
@@ -207,15 +342,17 @@ static void compose_impl(struct _xrender_data *xd, const struct backend_image *i
}
}
}
+ if (mask_allocated) {
+ xcb_render_free_picture(xd->base.c, mask_pict);
+ }
pixman_region32_fini(&reg);
}
-static void compose(backend_t *base, void *img_data,
- int dst_x1, int dst_y1, int dst_x2, int dst_y2,
+static void compose(backend_t *base, void *img_data, coord_t dst, void *mask, coord_t mask_dst,
const region_t *reg_paint, const region_t *reg_visible) {
- // TODO(dccsillag): use dst_{x,y}2
struct _xrender_data *xd = (void *)base;
- return compose_impl(xd, img_data, dst_x1, dst_y1, dst_x2, dst_y2, reg_paint, reg_visible, xd->back[2]);
+ return compose_impl(xd, img_data, dst, mask, mask_dst, reg_paint, reg_visible,
+ xd->back[2]);
}
static void fill(backend_t *base, struct color c, const region_t *clip) {
@@ -236,8 +373,8 @@ static void fill(backend_t *base, struct color c, const region_t *clip) {
.height = to_u16_checked(extent->y2 - extent->y1)}});
}
-static bool blur(backend_t *backend_data, double opacity, void *ctx_,
- const region_t *reg_blur, const region_t *reg_visible) {
+static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask,
+ coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible) {
struct _xrender_blur_context *bctx = ctx_;
if (bctx->method == BLUR_METHOD_NONE) {
return true;
@@ -288,7 +425,12 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_,
pixman_region32_fini(&clip);
xcb_render_picture_t src_pict = xd->back[2], dst_pict = tmp_picture[0];
- auto alpha_pict = xd->alpha_pict[(int)(opacity * MAX_ALPHA)];
+ auto mask_pict = xd->alpha_pict[(int)(opacity * MAX_ALPHA)];
+ bool mask_allocated = false;
+ if (mask != NULL) {
+ mask_pict = process_mask(xd, mask, opacity != 1.0 ? mask_pict : XCB_NONE,
+ &mask_allocated);
+ }
int current = 0;
x_set_picture_clip_region(c, src_pict, 0, 0, &reg_op_resized);
@@ -324,11 +466,12 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_,
} else {
x_set_picture_clip_region(c, xd->back[2], 0, 0, &reg_op);
// This is the last pass, and we are doing more than 1 pass
- xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict,
- alpha_pict, xd->back[2], 0, 0, 0, 0,
- to_i16_checked(extent_resized->x1),
- to_i16_checked(extent_resized->y1),
- width_resized, height_resized);
+ xcb_render_composite(
+ c, XCB_RENDER_PICT_OP_OVER, src_pict, mask_pict, xd->back[2],
+ 0, 0, to_i16_checked(extent_resized->x1 - mask_dst.x + 1),
+ to_i16_checked(extent_resized->y1 - mask_dst.y + 1),
+ to_i16_checked(extent_resized->x1),
+ to_i16_checked(extent_resized->y1), width_resized, height_resized);
}
// reset filter
@@ -344,8 +487,10 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_,
if (i == 1) {
x_set_picture_clip_region(c, xd->back[2], 0, 0, &reg_op);
xcb_render_composite(
- c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xd->back[2], 0, 0,
- 0, 0, to_i16_checked(extent_resized->x1),
+ c, XCB_RENDER_PICT_OP_OVER, src_pict, mask_pict, xd->back[2], 0, 0,
+ to_i16_checked(extent_resized->x1 - mask_dst.x + 1),
+ to_i16_checked(extent_resized->y1 - mask_dst.y + 1),
+ to_i16_checked(extent_resized->x1),
to_i16_checked(extent_resized->y1), width_resized, height_resized);
}
@@ -366,11 +511,11 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool
return NULL;
}
- auto img = ccalloc(1, struct backend_image);
+ auto img = ccalloc(1, struct xrender_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->width = img->base.ewidth = r->width;
+ inner->height = img->base.eheight = r->height;
inner->pixmap = pixmap;
inner->has_alpha = fmt.alpha_size != 0;
inner->pict =
@@ -379,8 +524,9 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool
inner->visual = fmt.visual;
inner->refcount = 1;
- img->inner = (struct backend_image_inner_base *)inner;
- img->opacity = 1;
+ img->base.inner = (struct backend_image_inner_base *)inner;
+ img->base.opacity = 1;
+ img->rounded_rectangle = NULL;
free(r);
if (inner->pict == XCB_NONE) {
@@ -397,11 +543,28 @@ static void release_image_inner(backend_t *base, struct _xrender_image_data_inne
}
free(inner);
}
+
+static void
+release_rounded_corner_cache(backend_t *base, struct xrender_rounded_rectangle_cache *cache) {
+ if (!cache) {
+ return;
+ }
+
+ assert(cache->refcount > 0);
+ cache->refcount--;
+ if (cache->refcount == 0) {
+ xcb_free_pixmap(base->c, cache->p);
+ free(cache);
+ }
+}
+
static void release_image(backend_t *base, void *image) {
- struct backend_image *img = image;
- img->inner->refcount--;
- if (img->inner->refcount == 0) {
- release_image_inner(base, (void *)img->inner);
+ struct xrender_image *img = image;
+ release_rounded_corner_cache(base, img->rounded_rectangle);
+ img->rounded_rectangle = NULL;
+ img->base.inner->refcount -= 1;
+ if (img->base.inner->refcount == 0) {
+ release_image_inner(base, (void *)img->base.inner);
}
free(img);
}
@@ -523,6 +686,52 @@ new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
return new_inner;
}
+static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
+ struct _xrender_data *xd = (void *)base;
+ // Give the mask a 1 pixel wide border to emulate the clamp to border behavior of
+ // OpenGL textures.
+ auto w16 = to_u16_checked(size.width + 2);
+ auto h16 = to_u16_checked(size.height + 2);
+ auto inner =
+ new_inner(base, size.width + 2, size.height + 2,
+ x_get_visual_for_standard(base->c, XCB_PICT_STANDARD_ARGB_32), 32);
+ xcb_render_change_picture(base->c, inner->pict, XCB_RENDER_CP_REPEAT,
+ (uint32_t[]){XCB_RENDER_REPEAT_PAD});
+ const rect_t *extent = pixman_region32_extents((region_t *)reg);
+ x_set_picture_clip_region(base->c, xd->back[2], 1, 1, reg);
+ xcb_render_fill_rectangles(
+ base->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
+ (xcb_render_color_t){.red = 0, .green = 0, .blue = 0, .alpha = 0xffff}, 1,
+ (xcb_rectangle_t[]){{.x = to_i16_checked(extent->x1 + 1),
+ .y = to_i16_checked(extent->y1 + 1),
+ .width = to_u16_checked(extent->x2 - extent->x1),
+ .height = to_u16_checked(extent->y2 - extent->y1)}});
+ x_clear_picture_clip_region(xd->base.c, inner->pict);
+
+ // Paint the border transparent
+ xcb_render_fill_rectangles(
+ base->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
+ (xcb_render_color_t){.red = 0, .green = 0, .blue = 0, .alpha = 0}, 4,
+ (xcb_rectangle_t[]){{.x = 0, .y = 0, .width = w16, .height = 1},
+ {.x = 0, .y = 0, .width = 1, .height = h16},
+ {.x = 0, .y = (short)(h16 - 1), .width = w16, .height = 1},
+ {.x = (short)(w16 - 1), .y = 0, .width = 1, .height = h16}});
+ inner->refcount = 1;
+
+ auto img = ccalloc(1, struct xrender_image);
+ img->base.eheight = size.height + 2;
+ img->base.ewidth = size.width + 2;
+ img->base.border_width = 0;
+ img->base.color_inverted = false;
+ img->base.corner_radius = 0;
+ img->base.max_brightness = 1;
+ img->base.opacity = 1;
+ img->base.dim = 0;
+ img->base.inner = (struct backend_image_inner_base *)inner;
+ img->rounded_rectangle = NULL;
+ return img;
+}
+
static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) {
if (img->inner->refcount == 1) {
return true;
@@ -648,30 +857,6 @@ static void get_blur_size(void *blur_context, int *width, int *height) {
*height = ctx->resize_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 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, inner->pixmap,
- to_i16_checked(x), to_i16_checked(y), 1, 1, (uint32_t)-1L);
-
- if (!r) {
- return false;
- }
-
- // Color format seems to be BGRA8888, see glamor_format_for_pixmap from the
- // Xserver codebase.
- uint8_t *pixels = xcb_get_image_data(r);
- output->blue = pixels[0] / 255.0;
- output->green = pixels[1] / 255.0;
- output->red = pixels[2] / 255.0;
- output->alpha = pixels[3] / 255.0;
-
- return true;
-}
-
static backend_t *backend_xrender_init(session_t *ps) {
auto xd = ccalloc(1, struct _xrender_data);
init_backend_base(&xd->base, ps);
@@ -751,6 +936,29 @@ err:
return NULL;
}
+void *clone_image(backend_t *base attr_unused, const void *image_data,
+ const region_t *reg_visible attr_unused) {
+ auto new_img = ccalloc(1, struct xrender_image);
+ *new_img = *(struct xrender_image *)image_data;
+ new_img->base.inner->refcount++;
+ if (new_img->rounded_rectangle) {
+ new_img->rounded_rectangle->refcount++;
+ }
+ return new_img;
+}
+
+static bool
+set_image_property(backend_t *base, enum image_properties op, void *image, void *args) {
+ auto xrimg = (struct xrender_image *)image;
+ if (op == IMAGE_PROPERTY_CORNER_RADIUS &&
+ ((double *)args)[0] != xrimg->base.corner_radius) {
+ // Free cached rounded rectangle if corner radius changed
+ release_rounded_corner_cache(base, xrimg->rounded_rectangle);
+ xrimg->rounded_rectangle = NULL;
+ }
+ return default_set_image_property(base, op, image, args);
+}
+
struct backend_operations xrender_ops = {
.init = backend_xrender_init,
.deinit = deinit,
@@ -760,7 +968,10 @@ 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,
//.release_win = release_win,
.is_image_transparent = default_is_image_transparent,
@@ -768,9 +979,8 @@ struct backend_operations xrender_ops = {
.max_buffer_age = 2,
.image_op = image_op,
- .read_pixel = read_pixel,
- .clone_image = default_clone_image,
- .set_image_property = default_set_image_property,
+ .clone_image = clone_image,
+ .set_image_property = set_image_property,
.create_blur_context = create_blur_context,
.destroy_blur_context = destroy_blur_context,
.get_blur_size = get_blur_size,