Skip to content

Commit

Permalink
backend: clarify how scaling affect other blit arguments
Browse files Browse the repository at this point in the history
And fixes inconsistencies when setting these arguments with a scaling
factor.

Fixes #1264

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
  • Loading branch information
yshui committed May 21, 2024
1 parent ff9de51 commit 94faacb
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 16 deletions.
6 changes: 3 additions & 3 deletions src/backend/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,13 @@ struct backend_blit_args {
/// Scale factor for the horizontal and vertical direction (X for horizontal,
/// Y for vertical).
vec2 scale;
/// Corner radius of the source image. The corners of
/// Corner radius of the source image BEFORE scaling. The corners of
/// the source image will be rounded.
double corner_radius;
/// Effective size of the source image, set where the corners
/// Effective size of the source image BEFORE scaling, set where the corners
/// of the image are.
ivec2 effective_size;
/// Border width of the source image. This is used with
/// Border width of the source image BEFORE scaling. This is used with
/// `corner_radius` to create a border for the rounded corners.
/// Setting this has no effect if `corner_radius` is 0.
int border_width;
Expand Down
29 changes: 25 additions & 4 deletions src/backend/xrender/xrender.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,8 @@ static bool xrender_blit(struct backend_base *base, ivec2 origin,
bool has_alpha = inner->has_alpha || args->opacity != 1;
auto const tmpw = to_u16_checked(inner->size.width);
auto const tmph = to_u16_checked(inner->size.height);
auto const tmpew = to_u16_checked(args->effective_size.width);
auto const tmpeh = to_u16_checked(args->effective_size.height);
auto const tmpew = to_u16_saturated(args->effective_size.width * args->scale.x);
auto const tmpeh = to_u16_saturated(args->effective_size.height * args->scale.y);
const xcb_render_color_t dim_color = {
.red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * args->dim)};

Expand Down Expand Up @@ -346,8 +346,25 @@ static bool xrender_blit(struct backend_base *base, ivec2 origin,
auto tmp_pict = x_create_picture_with_pictfmt(
xd->base.c, inner->size.width, inner->size.height, pictfmt, depth, 0, NULL);

x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-origin.x),
to_i16_checked(-origin.y), args->target_mask);
vec2 inverse_scale = (vec2){
.x = 1.0 / args->scale.x,
.y = 1.0 / args->scale.y,
};
if (vec2_eq(args->scale, SCALE_IDENTITY)) {
x_set_picture_clip_region(
xd->base.c, tmp_pict, to_i16_checked(-origin.x),
to_i16_checked(-origin.y), args->target_mask);
} else {
// We need to scale the target_mask back so it's in the source's
// coordinate space.
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);
x_set_picture_clip_region(
xd->base.c, tmp_pict, to_i16_checked(-origin.x),
to_i16_checked(-origin.y), &source_mask_region);
}
// Copy source -> tmp
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
Expand Down Expand Up @@ -398,6 +415,10 @@ static bool xrender_blit(struct backend_base *base, ivec2 origin,
}

set_picture_scale(xd->base.c, tmp_pict, args->scale);
// Transformations don't affect the picture's clip region, so we need to
// set it again
x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-origin.x),
to_i16_checked(-origin.y), args->target_mask);

xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_OVER, tmp_pict,
mask_pict, target->pict, 0, 0, mask_pict_dst_x,
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/command_builder.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ command_for_shadow(struct layer *layer, struct backend_command *cmd,
const struct win_option *wintype_options,
const struct x_monitors *monitors, const struct backend_command *end) {
auto w = layer->win;
auto shadow_size_scaled = vec2_as(
vec2_floor(vec2_scale(ivec2_as(layer->shadow.size), layer->shadow_scale)));
if (!w->shadow) {
return 0;
}
Expand All @@ -105,8 +107,8 @@ command_for_shadow(struct layer *layer, struct backend_command *cmd,
pixman_region32_clear(&cmd->target_mask);
pixman_region32_union_rect(&cmd->target_mask, &cmd->target_mask,
layer->shadow.origin.x, layer->shadow.origin.y,
(unsigned)layer->shadow.size.width,
(unsigned)layer->shadow.size.height);
(unsigned)shadow_size_scaled.width,
(unsigned)shadow_size_scaled.height);
log_trace("Calculate shadow for %#010x (%s)", w->base.id, w->name);
log_region(TRACE, &cmd->target_mask);
if (!wintype_options[w->window_type].full_shadow) {
Expand Down
12 changes: 7 additions & 5 deletions src/renderer/layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ static bool layer_from_window(struct layer *out_layer, struct managed_win *w, iv
out_layer->window.origin =
vec2_as((vec2){.x = w->g.x + win_animatable_get(w, WIN_SCRIPT_OFFSET_X),
.y = w->g.y + win_animatable_get(w, WIN_SCRIPT_OFFSET_Y)});
out_layer->window.size = vec2_as((vec2){.width = w->widthb * out_layer->scale.x,
.height = w->heightb * out_layer->scale.y});
out_layer->window.size = vec2_as((vec2){.width = w->widthb, .height = w->heightb});
out_layer->crop.origin = vec2_as((vec2){
.x = win_animatable_get(w, WIN_SCRIPT_CROP_X),
.y = win_animatable_get(w, WIN_SCRIPT_CROP_Y),
Expand All @@ -70,16 +69,19 @@ static bool layer_from_window(struct layer *out_layer, struct managed_win *w, iv
.y = w->g.y + w->shadow_dy +
win_animatable_get(w, WIN_SCRIPT_SHADOW_OFFSET_Y)});
out_layer->shadow.size =
vec2_as((vec2){.width = w->shadow_width * out_layer->shadow_scale.x,
.height = w->shadow_height * out_layer->shadow_scale.y});
vec2_as((vec2){.width = w->shadow_width, .height = w->shadow_height});
} else {
out_layer->shadow.origin = (ivec2){};
out_layer->shadow.size = (ivec2){};
out_layer->shadow_scale = SCALE_IDENTITY;
}

struct ibox window_scaled = {
.origin = out_layer->window.origin,
.size = ivec2_scale_floor(out_layer->window.size, out_layer->scale),
};
struct ibox screen = {.origin = {0, 0}, .size = size};
if (!ibox_overlap(out_layer->window, screen) || !ibox_overlap(out_layer->crop, screen)) {
if (!ibox_overlap(window_scaled, screen) || !ibox_overlap(out_layer->crop, screen)) {
goto out;
}

Expand Down
4 changes: 2 additions & 2 deletions src/renderer/layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ struct layer {
struct managed_win *win;
/// Damaged region of this layer, in screen coordinates
region_t damaged;
/// Window rectangle in screen coordinates.
/// Window rectangle in screen coordinates, before it's scaled.
struct ibox window;
/// Shadow rectangle in screen coordinates.
/// Shadow rectangle in screen coordinates, before it's scaled.
struct ibox shadow;
/// Scale of the window. The origin of scaling is the top left corner of the
/// window.
Expand Down
38 changes: 38 additions & 0 deletions src/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ struct ibox {

static const vec2 SCALE_IDENTITY = {1.0, 1.0};

static inline vec2 ivec2_as(ivec2 a) {
return (vec2){
.x = a.x,
.y = a.y,
};
}

static inline ivec2 ivec2_add(ivec2 a, ivec2 b) {
return (ivec2){
.x = a.x + b.x,
Expand Down Expand Up @@ -95,6 +102,27 @@ static inline ivec2 vec2_as(vec2 a) {
};
}

static inline vec2 vec2_add(vec2 a, vec2 b) {
return (vec2){
.x = a.x + b.x,
.y = a.y + b.y,
};
}

static inline vec2 vec2_ceil(vec2 a) {
return (vec2){
.x = ceil(a.x),
.y = ceil(a.y),
};
}

static inline vec2 vec2_floor(vec2 a) {
return (vec2){
.x = floor(a.x),
.y = floor(a.y),
};
}

static inline bool vec2_eq(vec2 a, vec2 b) {
return a.x == b.x && a.y == b.y;
}
Expand Down Expand Up @@ -128,5 +156,15 @@ static inline bool ibox_eq(struct ibox a, struct ibox b) {
return ivec2_eq(a.origin, b.origin) && ivec2_eq(a.size, b.size);
}

static inline ivec2 ivec2_scale_ceil(ivec2 a, vec2 scale) {
vec2 scaled = vec2_scale(ivec2_as(a), scale);
return vec2_as(vec2_ceil(scaled));
}

static inline ivec2 ivec2_scale_floor(ivec2 a, vec2 scale) {
vec2 scaled = vec2_scale(ivec2_as(a), scale);
return vec2_as(vec2_floor(scaled));
}

#define MARGIN_INIT \
{ 0, 0, 0, 0 }
33 changes: 33 additions & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,39 @@ safe_isinf(double a) {
(uint32_t) __to_tmp; \
})

static inline uint16_t u64_to_u16_saturated(uint64_t val) {
if (val > UINT16_MAX) {
return UINT16_MAX;
}
return (uint16_t)val;
}
static inline uint16_t double_to_u16_saturated(double val) {
BUG_ON(safe_isnan(val));
if (val < 0) {
return 0;
}
if (val > UINT16_MAX) {
return UINT16_MAX;
}
return (uint16_t)val;
}
static inline uint16_t i64_to_u16_saturated(int64_t val) {
if (val < 0) {
return 0;
}
if (val > UINT16_MAX) {
return UINT16_MAX;
}
return (uint16_t)val;
}

#define to_u16_saturated(val) \
_Generic((val), \
double: double_to_u16_saturated(val), \
float: double_to_u16_saturated((double)(val)), \
uint64_t: u64_to_u16_saturated(val), \
default: i64_to_u16_saturated((int64_t)(val)))

static inline int32_t double_to_i32_saturated(double val) {
BUG_ON(safe_isnan(val));
if (val < INT32_MIN) {
Expand Down

0 comments on commit 94faacb

Please sign in to comment.