From ee0007c0f21dbb07637006977b2cfea086397814 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 22 Apr 2024 16:23:58 +0100 Subject: [PATCH] render/pixman: Fix source crop Fix source crop in the pixman render backend. It was being applied by using a source offset as arguments to pixman_image_composite32(). But this is wrong because the source crop should get applied before all the other transforms, not after them (or at least this is how it works in the other wlroots render backends). Instead, apply the source crop as yet another matrix transform when we're doing transforms (Or keep it the same as previously if there's no other transforming going on). Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3830 --- render/pixman/pass.c | 55 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/render/pixman/pass.c b/render/pixman/pass.c index 1709d810..d246d48a 100644 --- a/render/pixman/pass.c +++ b/render/pixman/pass.c @@ -47,6 +47,9 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, return; } + pixman_op_t op = get_pixman_blending(options->blend_mode); + pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); + struct wlr_fbox src_fbox; wlr_render_texture_options_get_src_box(options, &src_fbox); struct wlr_box src_box = { @@ -71,7 +74,6 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, wlr_box_transform(&orig_box, &dst_box, options->transform, buffer->buffer->width, buffer->buffer->height); - int32_t dest_x, dest_y, width, height; if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL || orig_box.width != src_box.width || orig_box.height != src_box.height) { @@ -131,43 +133,46 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, // corner is back at the origin. pixman_transform_translate(&transform, NULL, pixman_int_to_fixed(tr_x), pixman_int_to_fixed(tr_y)); + pixman_transform_translate(&transform, NULL, -pixman_int_to_fixed(orig_box.x), -pixman_int_to_fixed(orig_box.y)); pixman_transform_scale(&transform, NULL, pixman_double_to_fixed(src_box.width / (double)orig_box.width), pixman_double_to_fixed(src_box.height / (double)orig_box.height)); + + // Apply the translation for source crop so the origin is now at the top-left of + // the region we're actually using. Do this last so all the other transforms + // apply on top of this. + pixman_transform_translate(&transform, NULL, + pixman_int_to_fixed(src_box.x), pixman_int_to_fixed(src_box.y)); + + switch (options->filter_mode) { + case WLR_SCALE_FILTER_BILINEAR: + pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0); + break; + case WLR_SCALE_FILTER_NEAREST: + pixman_image_set_filter(texture->image, PIXMAN_FILTER_NEAREST, NULL, 0); + break; + } + pixman_image_set_transform(texture->image, &transform); - dest_x = dest_y = 0; - width = buffer->buffer->width; - height = buffer->buffer->height; + // We've already applied the transforms for source crop and scaling so just + // composite over the whole output and let the transform deal with everything. + pixman_image_composite32(op, texture->image, mask, buffer->image, + 0, 0, 0, 0, 0, 0, buffer->buffer->width, buffer->buffer->height); + + pixman_image_set_transform(texture->image, NULL); } else { + // No transforms or crop needed, just a straight blit from the source pixman_image_set_transform(texture->image, NULL); - dest_x = dst_box.x; - dest_y = dst_box.y; - width = src_box.width; - height = src_box.height; - } - - switch (options->filter_mode) { - case WLR_SCALE_FILTER_BILINEAR: - pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0); - break; - case WLR_SCALE_FILTER_NEAREST: - pixman_image_set_filter(texture->image, PIXMAN_FILTER_NEAREST, NULL, 0); - break; + pixman_image_composite32(op, texture->image, mask, buffer->image, + src_box.x, src_box.y, 0, 0, dst_box.x, dst_box.y, + src_box.width, src_box.height); } - pixman_op_t op = get_pixman_blending(options->blend_mode); - - pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); - pixman_image_composite32(op, texture->image, mask, - buffer->image, src_box.x, src_box.y, 0, 0, dest_x, dest_y, - width, height); pixman_image_set_clip_region32(buffer->image, NULL); - pixman_image_set_transform(texture->image, NULL); - if (texture->buffer != NULL) { wlr_buffer_end_data_ptr_access(texture->buffer); }