backend interface: Add visible hint

Pass the visible region of the image to the backend, so the backend
could optimize based on that information.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-03-04 21:37:22 +00:00
parent a2ae36c1d6
commit c57f267535
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
5 changed files with 217 additions and 116 deletions

View File

@ -6,9 +6,9 @@
#include "common.h" #include "common.h"
#include "compiler.h" #include "compiler.h"
#include "config.h" #include "config.h"
#include "log.h"
#include "region.h" #include "region.h"
#include "win.h" #include "win.h"
#include "log.h"
backend_init_fn backend_list[NUM_BKEND] = { backend_init_fn backend_list[NUM_BKEND] = {
[BKEND_XRENDER] = backend_xrender_init, [BKEND_XRENDER] = backend_xrender_init,
@ -37,16 +37,19 @@ region_t get_damage(session_t *ps) {
/// paint all windows /// paint all windows
void paint_all_new(session_t *ps, win *const t, bool ignore_damage) { void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
region_t region; // All painting will be limited to the damage, if _some_ of
// the paints bleed out of the damage region, it will destroy
// part of the image we want to reuse
region_t reg_damage;
if (!ignore_damage) { if (!ignore_damage) {
region = get_damage(ps); reg_damage = get_damage(ps);
} else { } else {
pixman_region32_init(&region); pixman_region32_init(&reg_damage);
pixman_region32_copy(&region, &ps->screen_reg); pixman_region32_copy(&reg_damage, &ps->screen_reg);
} }
if (!pixman_region32_not_empty(&region)) { if (!pixman_region32_not_empty(&reg_damage)) {
pixman_region32_fini(&region); pixman_region32_fini(&reg_damage);
return; return;
} }
@ -54,27 +57,26 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
static struct timespec last_paint = {0}; static struct timespec last_paint = {0};
#endif #endif
region_t reg_tmp; // A hint to backend, the region that will be visible on screen
const region_t *reg_paint; // backend can optimize based on this info
pixman_region32_init(&reg_tmp); region_t reg_visible;
pixman_region32_init(&reg_visible);
pixman_region32_copy(&reg_visible, &ps->screen_reg);
if (t) { if (t) {
// Calculate the region upon which the root window (wallpaper) is to be // Calculate the region upon which the root window (wallpaper) is to be
// painted based on the ignore region of the lowest window, if available // painted based on the ignore region of the lowest window, if available
pixman_region32_subtract(&reg_tmp, &region, t->reg_ignore); pixman_region32_subtract(&reg_visible, &reg_visible, t->reg_ignore);
reg_paint = &reg_tmp;
} else {
reg_paint = &region;
} }
// TODO Bind root pixmap // TODO Bind root pixmap
if (ps->backend_data->ops->prepare) { if (ps->backend_data->ops->prepare) {
ps->backend_data->ops->prepare(ps->backend_data, reg_paint); ps->backend_data->ops->prepare(ps->backend_data, &reg_visible);
} }
if (ps->root_image) { if (ps->root_image) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0, ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0,
reg_paint); &reg_damage, &reg_visible);
} }
// Windows are sorted from bottom to top // Windows are sorted from bottom to top
@ -83,28 +85,25 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
// //
// Whether this is beneficial is to be determined XXX // Whether this is beneficial is to be determined XXX
for (win *w = t; w; w = w->prev_trans) { for (win *w = t; w; w = w->prev_trans) {
// Calculate the region based on the reg_ignore of the next (higher) pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore);
// window and the bounding region
// XXX XXX
pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
if (!pixman_region32_not_empty(&reg_tmp)) {
continue;
}
// The bounding shape of the window, in global/target coordinates
// reminder: bounding shape contains the WM frame
auto reg_bound = win_get_bounding_shape_global_by_val(w); auto reg_bound = win_get_bounding_shape_global_by_val(w);
// Draw shadow on target // Draw shadow on target
if (w->shadow) { if (w->shadow) {
// Clip region for the shadow
// reg_shadow \in reg_damage
auto reg_shadow = win_extents_by_val(w); auto reg_shadow = win_extents_by_val(w);
pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_tmp); pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_damage);
if (!ps->o.wintype_option[w->window_type].full_shadow) { if (!ps->o.wintype_option[w->window_type].full_shadow) {
pixman_region32_subtract(&reg_shadow, &reg_shadow, &reg_bound); pixman_region32_subtract(&reg_shadow, &reg_shadow, &reg_bound);
} }
// Mask out the region we don't want shadow on // Mask out the region we don't want shadow on
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) { if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
pixman_region32_subtract(&reg_tmp, &reg_tmp, pixman_region32_subtract(&reg_shadow, &reg_shadow,
&ps->shadow_exclude_reg); &ps->shadow_exclude_reg);
} }
@ -123,17 +122,18 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
} }
assert(w->shadow_image); assert(w->shadow_image);
ps->backend_data->ops->compose(ps->backend_data, w->shadow_image, ps->backend_data->ops->compose(
w->g.x + w->shadow_dx, ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow); w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
pixman_region32_fini(&reg_shadow); pixman_region32_fini(&reg_shadow);
} }
pixman_region32_intersect(&reg_tmp, &reg_tmp, &reg_bound); // The clip region for the current window, in global/target coordinates
pixman_region32_fini(&reg_bound); // reg_paint \in reg_damage
if (!pixman_region32_not_empty(&reg_tmp)) { region_t reg_paint;
continue; pixman_region32_init(&reg_paint);
} pixman_region32_intersect(&reg_paint, &reg_bound, &reg_damage);
// Blur window background // Blur window background
bool win_transparent = ps->backend_data->ops->is_image_transparent( bool win_transparent = ps->backend_data->ops->is_image_transparent(
ps->backend_data, w->win_image); ps->backend_data, w->win_image);
@ -142,35 +142,61 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
(win_transparent || (ps->o.blur_background_frame && frame_transparent))) { (win_transparent || (ps->o.blur_background_frame && frame_transparent))) {
// Minimize the region we try to blur, if the window // Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is. // itself is not opaque, only the frame is.
if (win_is_solid(ps, w)) { // TODO resize blur region to fix black line artifact
region_t reg_blur; if (!win_is_solid(ps, w)) {
pixman_region32_init(&reg_blur); // We need to blur the bounding shape of the window
win_get_region_noframe_local(w, &reg_blur); // (reg_paint = reg_bound \cap reg_damage)
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_paint, &reg_visible);
} else if (frame_transparent && ps->o.blur_background_frame) {
// Window itself is solid, we only need to blur the frame region
auto reg_blur = win_get_region_frame_local_by_val(w);
pixman_region32_translate(&reg_blur, w->g.x, w->g.y); pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
pixman_region32_subtract(&reg_blur, &reg_tmp, &reg_blur); // make sure reg_blur \in reg_damage
pixman_region32_intersect(&reg_blur, &reg_blur, &reg_damage);
ps->backend_data->ops->blur(ps->backend_data, w->opacity, ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_blur); &reg_blur, &reg_visible);
pixman_region32_fini(&reg_blur); pixman_region32_fini(&reg_blur);
} else {
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_tmp);
} }
} }
// Draw window on target // Draw window on target
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) { if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image, ps->backend_data->ops->compose(ps->backend_data, w->win_image, w->g.x,
w->g.x, w->g.y, &reg_tmp); w->g.y, &reg_paint, &reg_visible);
} else { } else {
region_t reg_local; // For window image processing, we don't need to limit the process
pixman_region32_init(&reg_local); // region to damage, since the window image data is independent from
pixman_region32_copy(&reg_local, &reg_tmp); // the target image data, which we want to protect.
pixman_region32_translate(&reg_local, -w->g.x, -w->g.y);
// The bounding shape, in window local coordinates
region_t reg_bound_local;
pixman_region32_init(&reg_bound_local);
pixman_region32_copy(&reg_bound_local, &reg_bound);
pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
// The visible region, in window local coordinates
// Although we don't limit process region to damage, we provide
// that info in reg_visible as a hint. Since window image data
// outside of the damage region won't be painted onto target
region_t reg_visible_local;
pixman_region32_init(&reg_visible_local);
pixman_region32_intersect(&reg_visible_local, &reg_visible, &reg_damage);
pixman_region32_translate(&reg_visible_local, -w->g.x, -w->g.y);
// Data outside of the bounding shape won't be visible, but it is
// not necessary to limit the image operations to the bounding shape
// yet. So pass that as the visible region, not the clip region.
pixman_region32_intersect(&reg_visible_local, &reg_visible_local,
&reg_bound_local);
// A region covers the entire window
region_t reg_win;
pixman_region32_init_rect(&reg_win, 0, 0, w->g.width, w->g.height);
auto new_img = ps->backend_data->ops->copy( auto new_img = ps->backend_data->ops->copy(
ps->backend_data, w->win_image, &reg_local); ps->backend_data, w->win_image, &reg_visible_local);
if (w->invert_color) { if (w->invert_color) {
ps->backend_data->ops->image_op(ps->backend_data, ps->backend_data->ops->image_op(
IMAGE_OP_INVERT_COLOR, ps->backend_data, IMAGE_OP_INVERT_COLOR, new_img,
new_img, &reg_local, NULL); &reg_win, &reg_visible_local, NULL);
} }
if (w->dim) { if (w->dim) {
double dim_opacity = ps->o.inactive_dim; double dim_opacity = ps->o.inactive_dim;
@ -178,30 +204,32 @@ void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
dim_opacity *= w->opacity; dim_opacity *= w->opacity;
} }
ps->backend_data->ops->image_op( ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_DIM, new_img, &reg_local, ps->backend_data, IMAGE_OP_DIM, new_img, &reg_win,
(double[]){dim_opacity}); &reg_visible_local, (double[]){dim_opacity});
} }
if (w->frame_opacity != 1) { if (w->frame_opacity != 1) {
auto reg_frame = win_get_region_noframe_local_by_val(w); auto reg_frame = win_get_region_frame_local_by_val(w);
pixman_region32_subtract(&reg_frame, &reg_local, &reg_frame);
ps->backend_data->ops->image_op( ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame,
&reg_frame, (double[]){w->frame_opacity}); &reg_visible_local, (double[]){w->frame_opacity});
pixman_region32_fini(&reg_frame);
} }
if (w->opacity != 1) { if (w->opacity != 1) {
ps->backend_data->ops->image_op( ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, NULL, ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
(double[]){w->opacity}); NULL, &reg_visible_local, (double[]){w->opacity});
} }
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x, ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
w->g.y, &reg_tmp); w->g.y, &reg_paint, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img); ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_win);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
} }
pixman_region32_fini(&reg_bound);
pixman_region32_fini(&reg_paint);
} }
// Free up all temporary regions
pixman_region32_fini(&reg_tmp);
if (ps->backend_data->ops->present) { if (ps->backend_data->ops->present) {
// Present the rendered scene // Present the rendered scene
// Vsync is done here // Vsync is done here

View File

@ -23,9 +23,15 @@ typedef struct backend_base {
} backend_t; } backend_t;
enum image_operations { enum image_operations {
// Invert the color of the image
IMAGE_OP_INVERT_COLOR, IMAGE_OP_INVERT_COLOR,
// Dim the image, argument is the percentage
IMAGE_OP_DIM, IMAGE_OP_DIM,
// Multiply the alpha channel by the argument
IMAGE_OP_APPLY_ALPHA, 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,
}; };
struct backend_operations { struct backend_operations {
@ -64,25 +70,29 @@ struct backend_operations {
/// Optional? /// Optional?
void (*prepare)(backend_t *backend_data, const region_t *reg_paint); void (*prepare)(backend_t *backend_data, const region_t *reg_paint);
/// Paint the content of an imageonto the (possibly buffered) /**
/// target picture. Always called after render_win(). Maybe called * Paint the content of an image onto the (possibly buffered)
/// multiple times between render_win() and finish_render_win(). * target picture.
/// The origin is the top left of the window, exclude the shadow, *
/// (dst_x, dst_y) refers to where the origin should be in the target * @param backend_data the backend data
/// buffer. * @param image_data the image to paint
* @param dst_x, dst_y the top left corner of the image in the target
* @param reg_paint the clip region, in target coordinates
* @param reg_visibile the visible region, in target coordinates
*/
void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y, void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y,
const region_t *reg_paint); const region_t *reg_paint, const region_t *reg_visible);
/// Blur a given region on of the target. /// Blur a given region on of the target.
bool (*blur)(backend_t *backend_data, double opacity, const region_t *) bool (*blur)(backend_t *backend_data, double opacity, const region_t *reg_blur,
__attribute__((nonnull(1, 3))); const region_t *reg_visible) attr_nonnull(1, 3, 4);
/// Present the buffered target picture onto the screen. If target /// Present the buffered target picture onto the screen. If target
/// is not buffered, this should be NULL. Otherwise, it should always /// is not buffered, this should be NULL. Otherwise, it should always
/// be non-NULL. /// be non-NULL.
/// ///
/// Optional /// Optional
void (*present)(backend_t *backend_data) __attribute__((nonnull(1))); void (*present)(backend_t *backend_data) attr_nonnull(1);
/** /**
* Bind a X pixmap to the backend's internal image data structure. * Bind a X pixmap to the backend's internal image data structure.
@ -137,17 +147,22 @@ struct backend_operations {
* Manipulate an image * Manipulate an image
* *
* @param backend_data backend data * @param backend_data backend data
* @param op the operation to perform * @param op the operation to perform
* @param image_data an image data structure returned by the backend * @param image_data an image data structure returned by the backend
* @param reg_paint the clip region, limit the region of the image to be * @param reg_op the clip region, define the part of the image to be
* manipulated * operated on.
* @param args extra arguments, specific to each operation * @param reg_visible define the part of the image that will eventually
* @return a new image data structure contains the same image as `image_data` * be visible on screen. 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
*/ */
void (*image_op)(backend_t *backend_data, enum image_operations op, bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
void *image_data, const region_t *reg_paint, void *args); const region_t *reg_op, const region_t *reg_visible, void *args);
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_copy); /// Create another instance of the `image_data`. All `image_op` calls on the
/// returned image should not affect the original image
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_visible);
// =========== Hooks ============ // =========== Hooks ============
/// Let the backend hook into the event handling queue /// Let the backend hook into the event handling queue

View File

@ -83,28 +83,42 @@ struct _xrender_image_data {
bool owned; bool owned;
}; };
static void static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
compose(backend_t *base, void *img_data, int dst_x, int dst_y, const region_t *reg_paint) { const region_t *reg_paint, const region_t *reg_visible) {
struct _xrender_data *xd = (void *)base; struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = img_data; struct _xrender_image_data *img = img_data;
int op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC); int op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
auto alpha_pict = xd->alpha_pict[(int)(img->opacity * 255.0)]; auto alpha_pict = xd->alpha_pict[(int)(img->opacity * 255.0)];
region_t reg;
pixman_region32_init(&reg);
pixman_region32_intersect(&reg, (region_t *)reg_paint, (region_t *)reg_visible);
// Clip region of rendered_pict might be set during rendering, clear it to make // Clip region of rendered_pict might be set during rendering, clear it to make
// sure we get everything into the buffer // sure we get everything into the buffer
x_clear_picture_clip_region(base->c, img->pict); x_clear_picture_clip_region(base->c, img->pict);
x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, reg_paint); x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back], xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back],
0, 0, 0, 0, dst_x, dst_y, img->width, img->height); 0, 0, 0, 0, dst_x, dst_y, img->width, img->height);
pixman_region32_fini(&reg);
} }
static bool blur(backend_t *backend_data, double opacity, const region_t *reg_paint) { static bool blur(backend_t *backend_data, double opacity, const region_t *reg_blur,
const region_t *reg_visible) {
struct _xrender_data *xd = (void *)backend_data; struct _xrender_data *xd = (void *)backend_data;
xcb_connection_t *c = xd->base.c; xcb_connection_t *c = xd->base.c;
const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint); region_t reg_op;
const int height = reg->y2 - reg->y1; pixman_region32_init(&reg_op);
const int width = reg->x2 - reg->x1; pixman_region32_intersect(&reg_op, (region_t *)reg_blur, (region_t *)reg_visible);
if (!pixman_region32_not_empty(&reg_op)) {
pixman_region32_fini(&reg_op);
return true;
}
const pixman_box32_t *extent = pixman_region32_extents(&reg_op);
const int height = extent->y2 - extent->y1;
const int width = extent->x2 - extent->x1;
int src_x = extent->x1, src_y = extent->y1;
static const char *filter0 = "Nearest"; // The "null" filter static const char *filter0 = "Nearest"; // The "null" filter
static const char *filter = "convolution"; static const char *filter = "convolution";
@ -116,26 +130,26 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_pa
x_create_picture_with_visual(xd->base.c, xd->base.root, width, height, x_create_picture_with_visual(xd->base.c, xd->base.root, width, height,
xd->default_visual, 0, NULL)}; xd->default_visual, 0, NULL)};
region_t clip;
pixman_region32_init(&clip);
pixman_region32_copy(&clip, (region_t *)reg_paint);
pixman_region32_translate(&clip, -reg->x1, -reg->y1);
if (!tmp_picture[0] || !tmp_picture[1]) { if (!tmp_picture[0] || !tmp_picture[1]) {
log_error("Failed to build intermediate Picture."); log_error("Failed to build intermediate Picture.");
pixman_region32_fini(&reg_op);
return false; return false;
} }
region_t clip;
pixman_region32_init(&clip);
pixman_region32_copy(&clip, &reg_op);
pixman_region32_translate(&clip, -src_x, -src_y);
x_set_picture_clip_region(c, tmp_picture[0], 0, 0, &clip); x_set_picture_clip_region(c, tmp_picture[0], 0, 0, &clip);
x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip); x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip);
pixman_region32_fini(&clip);
// The multipass blur implemented here is not correct, but this is what old // The multipass blur implemented here is not correct, but this is what old
// compton did anyway. XXX // compton did anyway. XXX
xcb_render_picture_t src_pict = xd->back[xd->curr_back], dst_pict = tmp_picture[0]; xcb_render_picture_t src_pict = xd->back[xd->curr_back], dst_pict = tmp_picture[0];
auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)]; auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)];
int current = 0; int current = 0;
int src_x = reg->x1, src_y = reg->y1; x_set_picture_clip_region(c, src_pict, 0, 0, &reg_op);
x_set_picture_clip_region(c, src_pict, 0, 0, reg_paint);
// For more than 1 pass, we do: // For more than 1 pass, we do:
// back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ... // back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ...
@ -162,7 +176,7 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_pa
// This is the last pass, and this is also not the first // This is the last pass, and this is also not the first
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict,
alpha_pict, xd->back[xd->curr_back], 0, 0, 0, alpha_pict, xd->back[xd->curr_back], 0, 0, 0,
0, reg->x1, reg->y1, width, height); 0, src_x, src_y, width, height);
} }
// reset filter // reset filter
@ -178,12 +192,13 @@ static bool blur(backend_t *backend_data, double opacity, const region_t *reg_pa
// There is only 1 pass // There is only 1 pass
if (i == 1) { if (i == 1) {
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
xd->back[xd->curr_back], 0, 0, 0, 0, reg->x1, xd->back[xd->curr_back], 0, 0, 0, 0, extent->x1, extent->y1,
reg->y1, width, height); width, height);
} }
xcb_render_free_picture(c, tmp_picture[0]); xcb_render_free_picture(c, tmp_picture[0]);
xcb_render_free_picture(c, tmp_picture[1]); xcb_render_free_picture(c, tmp_picture[1]);
pixman_region32_fini(&reg_op);
return true; return true;
} }
@ -321,16 +336,30 @@ static bool is_image_transparent(backend_t *bd, void *image) {
return img->has_alpha; return img->has_alpha;
} }
static void image_op(backend_t *base, enum image_operations op, void *image, static bool image_op(backend_t *base, enum image_operations op, void *image,
const region_t *reg, void *arg) { const region_t *reg_op, const region_t *reg_visible, void *arg) {
struct _xrender_data *xd = (void *)base; struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = image; struct _xrender_image_data *img = image;
region_t reg;
double dim_opacity; double dim_opacity;
double alpha_multiplier; double alpha_multiplier;
if (op == IMAGE_OP_APPLY_ALPHA_ALL) {
alpha_multiplier = *(double *)arg;
img->opacity *= alpha_multiplier;
img->has_alpha = true;
return true;
}
pixman_region32_init(&reg);
pixman_region32_intersect(&reg, (region_t *)reg_op, (region_t *)reg_visible);
if (!pixman_region32_not_empty(&reg)) {
pixman_region32_fini(&reg);
return true;
}
switch (op) { switch (op) {
case IMAGE_OP_INVERT_COLOR: case IMAGE_OP_INVERT_COLOR:
assert(reg); x_set_picture_clip_region(base->c, img->pict, 0, 0, &reg);
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
if (img->has_alpha) { if (img->has_alpha) {
auto tmp_pict = auto tmp_pict =
x_create_picture_with_visual(base->c, base->root, img->width, x_create_picture_with_visual(base->c, base->root, img->width,
@ -355,8 +384,7 @@ static void image_op(backend_t *base, enum image_operations op, void *image,
} }
break; break;
case IMAGE_OP_DIM: case IMAGE_OP_DIM:
assert(reg); x_set_picture_clip_region(base->c, img->pict, 0, 0, &reg);
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
dim_opacity = *(double *)arg; dim_opacity = *(double *)arg;
xcb_render_color_t color = { xcb_render_color_t color = {
@ -378,19 +406,17 @@ static void image_op(backend_t *base, enum image_operations op, void *image,
if (alpha_multiplier == 1) { if (alpha_multiplier == 1) {
break; break;
} }
if (!reg) {
img->opacity *= alpha_multiplier;
img->has_alpha = true;
break;
}
auto alpha_pict = xd->alpha_pict[(int)(alpha_multiplier * 255)]; auto alpha_pict = xd->alpha_pict[(int)(alpha_multiplier * 255)];
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg); x_set_picture_clip_region(base->c, img->pict, 0, 0, &reg);
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN, img->pict, XCB_NONE, xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN, img->pict, XCB_NONE,
alpha_pict, 0, 0, 0, 0, 0, 0, img->width, img->height); alpha_pict, 0, 0, 0, 0, 0, 0, img->width, img->height);
img->has_alpha = true; img->has_alpha = true;
break; break;
case IMAGE_OP_APPLY_ALPHA_ALL: assert(false);
} }
pixman_region32_fini(&reg);
return true;
} }
static void *copy(backend_t *base, const void *image, const region_t *reg) { static void *copy(backend_t *base, const void *image, const region_t *reg) {

View File

@ -137,6 +137,29 @@ void win_get_region_noframe_local(const win *w, region_t *res) {
gen_by_val(win_get_region_noframe_local) gen_by_val(win_get_region_noframe_local)
void win_get_region_frame_local(const win *w, region_t *res) {
const margin_t extents = win_calc_frame_extents(w);
pixman_region32_fini(res);
pixman_region32_init_rects(res, (rect_t[]){
// top
{.x1 = 0, .y1 = 0, .x2 = w->g.width, .y2 = extents.top},
// bottom
{.x1 = 0, .y1 = w->g.height - extents.bottom, .x2 = w->g.width, .y2 = w->g.height},
//left
{.x1 = 0, .y1 = 0, .x2 = extents.left, .y2 = w->g.height},
// right
{.x1 = w->g.width - extents.right, .y1 = 0, .x2 = w->g.width, .y2 = w->g.height},
}, 4);
// limit the frame region to inside the window
region_t reg_win;
pixman_region32_init_rect(&reg_win, 0, 0, w->g.width, w->g.height);
pixman_region32_intersect(res, &reg_win, res);
pixman_region32_fini(&reg_win);
}
gen_by_val(win_get_region_frame_local)
/** /**
* Add a window to damaged area. * Add a window to damaged area.
* *
@ -1235,7 +1258,10 @@ void win_update_bounding_shape(session_t *ps, win *w) {
free(rects); free(rects);
// Add border width because we are using a different origin. // Add border width because we are using a different origin.
// X thinks the top left of the inner window is the origin, // X thinks the top left of the inner window is the origin
// (for the bounding shape, althought xcb_get_geometry thinks
// the outer top left (outer means outside of the window
// border) is the origin),
// We think the top left of the border is the origin // We think the top left of the border is the origin
pixman_region32_translate(&br, w->g.border_width, w->g.border_width); pixman_region32_translate(&br, w->g.border_width, w->g.border_width);

View File

@ -111,7 +111,8 @@ typedef enum {
* X goes from left to right, Y goes downwards. * X goes from left to right, Y goes downwards.
* *
* Global: the origin is the top left corner of the Xorg screen. * Global: the origin is the top left corner of the Xorg screen.
* Local: the origin is the top left corner of the window, including border. * Local: the origin is the top left corner of the window, border is
* considered part of the window.
*/ */
/// Structure representing a top-level window compton manages. /// Structure representing a top-level window compton manages.
@ -354,6 +355,11 @@ void add_damage_from_win(session_t *ps, win *w);
*/ */
void win_get_region_noframe_local(const win *w, region_t *); void win_get_region_noframe_local(const win *w, region_t *);
region_t win_get_region_noframe_local_by_val(const win *w); region_t win_get_region_noframe_local_by_val(const win *w);
/// Get the region for the frame of the window
void win_get_region_frame_local(const win *w, region_t *res);
/// Get the region for the frame of the window, by value
region_t win_get_region_frame_local_by_val(const win *w);
/** /**
* Retrieve frame extents from a window. * Retrieve frame extents from a window.
*/ */