From e068dc8a73cbf9175d8ac11f3ff6f12cd769d4d6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 21 Jun 2024 13:33:58 +0100 Subject: [PATCH] region: fix region scaling We claim region_scale_floor returns the largest integer region contained by the scaling result. But in fact what it's doing is taking the floor of each rectangle of the region separated, which leaves gaps between rectangles. This is not what we want. Just always take the ceil instead, hopefully the difference is small enough to be unnoticeable. Signed-off-by: Yuxuan Shui --- src/backend/xrender/xrender.c | 2 +- src/region.h | 52 ++++++++++++++-------------------- src/renderer/command_builder.c | 8 +++--- src/renderer/damage.c | 2 +- 4 files changed, 27 insertions(+), 37 deletions(-) diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index a8551f1841..fa38e09f86 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -360,7 +360,7 @@ static bool xrender_blit(struct backend_base *base, ivec2 origin, scoped_region_t source_mask_region; pixman_region32_init(&source_mask_region); pixman_region32_copy(&source_mask_region, args->target_mask); - region_scale_ceil(&source_mask_region, origin, inverse_scale); + region_scale(&source_mask_region, origin, inverse_scale); x_set_picture_clip_region( xd->base.c, tmp_pict, to_i16_checked(-origin.x), to_i16_checked(-origin.y), &source_mask_region); diff --git a/src/region.h b/src/region.h index 1e2261f7d7..a57bf01121 100644 --- a/src/region.h +++ b/src/region.h @@ -162,39 +162,29 @@ static inline void region_intersect(region_t *region, ivec2 origin, const region pixman_region32_translate(region, origin.x, origin.y); } -#define define_region_scale(suffix, lower_bound, upper_bound) \ - static inline void region_scale##suffix(region_t *region, ivec2 origin, vec2 scale) { \ - if (vec2_eq(scale, SCALE_IDENTITY)) { \ - return; \ - } \ - \ - int n; \ - region_t tmp = *region; \ - auto r = pixman_region32_rectangles(&tmp, &n); \ - for (int i = 0; i < n; i++) { \ - r[i].x1 = to_i32_saturated( \ - lower_bound((r[i].x1 - origin.x) * scale.x + origin.x)); \ - r[i].y1 = to_i32_saturated( \ - lower_bound((r[i].y1 - origin.y) * scale.y + origin.y)); \ - r[i].x2 = to_i32_saturated( \ - upper_bound((r[i].x2 - origin.x) * scale.x + origin.x)); \ - r[i].y2 = to_i32_saturated( \ - upper_bound((r[i].y2 - origin.y) * scale.y + origin.y)); \ - } \ - \ - /* Manipulating the rectangles could break assumptions made internally \ - * by pixman, so we recreate the region with the rectangles to let \ - * pixman fix them. */ \ - pixman_region32_init_rects(region, r, n); \ - pixman_region32_fini(&tmp); \ +/// Scale the `region` by `scale`. The origin of scaling is `origin`. Returns the smallest +/// integer region that contains the result. +static inline void region_scale(region_t *region, ivec2 origin, vec2 scale) { + if (vec2_eq(scale, SCALE_IDENTITY)) { + return; + } + + int n; + region_t tmp = *region; + auto r = pixman_region32_rectangles(&tmp, &n); + for (int i = 0; i < n; i++) { + r[i].x1 = to_i32_saturated(floor((r[i].x1 - origin.x) * scale.x + origin.x)); + r[i].y1 = to_i32_saturated(floor((r[i].y1 - origin.y) * scale.y + origin.y)); + r[i].x2 = to_i32_saturated(ceil((r[i].x2 - origin.x) * scale.x + origin.x)); + r[i].y2 = to_i32_saturated(ceil((r[i].y2 - origin.y) * scale.y + origin.y)); } -/// Scale the `region` by `scale`. The origin of scaling is `origin`. Returns the largest integer -/// region that is contained in the result. -define_region_scale(_floor, ceil, floor); -/// Scale the `region` by `scale`. The origin of scaling is `origin`. Returns the smallest integer -/// region that contains the result. -define_region_scale(_ceil, floor, ceil); + /* Manipulating the rectangles could break assumptions made internally + * by pixman, so we recreate the region with the rectangles to let + * pixman fix them. */ + pixman_region32_init_rects(region, r, n); + pixman_region32_fini(&tmp); +} /// Calculate the symmetric difference of `region1`, and `region2`, and union the result /// into `result`. The two input regions has to be in the same coordinate space. diff --git a/src/renderer/command_builder.c b/src/renderer/command_builder.c index ae3250c453..b4228cd435 100644 --- a/src/renderer/command_builder.c +++ b/src/renderer/command_builder.c @@ -51,8 +51,8 @@ commands_for_window_body(struct layer *layer, struct backend_command *cmd, if (w->corner_radius > 0) { win_region_remove_corners(w, layer->window.origin, &cmd->opaque_region); } - region_scale_floor(&cmd->target_mask, layer->window.origin, layer->scale); - region_scale_floor(&cmd->opaque_region, layer->window.origin, layer->scale); + region_scale(&cmd->target_mask, layer->window.origin, layer->scale); + region_scale(&cmd->opaque_region, layer->window.origin, layer->scale); pixman_region32_intersect(&cmd->target_mask, &cmd->target_mask, &crop); pixman_region32_intersect(&cmd->opaque_region, &cmd->opaque_region, &crop); cmd->op = BACKEND_COMMAND_BLIT; @@ -77,7 +77,7 @@ commands_for_window_body(struct layer *layer, struct backend_command *cmd, cmd -= 1; pixman_region32_copy(&cmd->target_mask, frame_region); - region_scale_floor(&cmd->target_mask, cmd->origin, layer->scale); + region_scale(&cmd->target_mask, cmd->origin, layer->scale); pixman_region32_intersect(&cmd->target_mask, &cmd->target_mask, &crop); pixman_region32_init(&cmd->opaque_region); cmd->op = BACKEND_COMMAND_BLIT; @@ -179,7 +179,7 @@ command_for_blur(struct layer *layer, struct backend_command *cmd, } else { return 0; } - region_scale_floor(&cmd->target_mask, layer->window.origin, layer->scale); + region_scale(&cmd->target_mask, layer->window.origin, layer->scale); scoped_region_t crop = region_from_box(layer->crop); pixman_region32_intersect(&cmd->target_mask, &cmd->target_mask, &crop); diff --git a/src/renderer/damage.c b/src/renderer/damage.c index e5986a6888..48c650d5fd 100644 --- a/src/renderer/damage.c +++ b/src/renderer/damage.c @@ -97,7 +97,7 @@ command_blit_damage(region_t *damage, region_t *scratch_region, struct backend_c if (cmd1->source == BACKEND_COMMAND_SOURCE_WINDOW) { layout_manager_collect_window_damage(lm, layer_index, buffer_age, scratch_region); - region_scale_floor(scratch_region, cmd2->origin, cmd2->blit.scale); + region_scale(scratch_region, cmd2->origin, cmd2->blit.scale); pixman_region32_intersect(scratch_region, scratch_region, &cmd1->target_mask); pixman_region32_intersect(scratch_region, scratch_region, &cmd2->target_mask); pixman_region32_union(damage, damage, scratch_region);