From c84d7ebc6d208b082859cb61b7157db0eedda2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 20 Dec 2018 17:34:18 +0100 Subject: [PATCH] shaped-texture: Get transformed textures pixels via offscreen When a texture is transformed in any way (e.g. Wayland buffer transforms), we cannot just fetch the pixels from the texture directly and be done with it, as that will result in getting the untransformed pixels. To properly get the pixels in their right form, first draw to an offscreen framebuffer, using the same method as when painting on the stage, then read from the framebuffer into a cairo image surface. https://gitlab.gnome.org/GNOME/mutter/merge_requests/362 Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/408 --- src/compositor/meta-shaped-texture.c | 150 ++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index 1f38dbdfd..955375995 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -36,6 +36,7 @@ #include "compositor/meta-cullable.h" #include "compositor/meta-texture-tower.h" #include "compositor/region-utils.h" +#include "core/boxes-private.h" #include "meta/meta-shaped-texture.h" /* MAX_MIPMAPPING_FPS needs to be as small as possible for the best GPU @@ -1038,6 +1039,129 @@ meta_shaped_texture_set_transform (MetaShapedTexture *stex, invalidate_size (stex); } +static gboolean +should_get_via_offscreen (MetaShapedTexture *stex) +{ + switch (stex->transform) + { + case META_MONITOR_TRANSFORM_90: + case META_MONITOR_TRANSFORM_180: + case META_MONITOR_TRANSFORM_270: + case META_MONITOR_TRANSFORM_FLIPPED: + case META_MONITOR_TRANSFORM_FLIPPED_90: + case META_MONITOR_TRANSFORM_FLIPPED_180: + case META_MONITOR_TRANSFORM_FLIPPED_270: + return TRUE; + case META_MONITOR_TRANSFORM_NORMAL: + break; + } + + return FALSE; +} + +static cairo_surface_t * +get_image_via_offscreen (MetaShapedTexture *stex, + cairo_rectangle_int_t *clip) +{ + ClutterBackend *clutter_backend = clutter_get_default_backend (); + CoglContext *cogl_context = + clutter_backend_get_cogl_context (clutter_backend); + CoglTexture *image_texture; + GError *error = NULL; + CoglOffscreen *offscreen; + CoglFramebuffer *fb; + CoglMatrix projection_matrix; + int fb_width, fb_height; + cairo_rectangle_int_t fallback_clip; + CoglColor clear_color; + cairo_surface_t *surface; + + if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TEXTURE_NPOT)) + { + fb_width = stex->dst_width; + fb_height = stex->dst_height; + } + else + { + fb_width = _cogl_util_next_p2 (stex->dst_width); + fb_height = _cogl_util_next_p2 (stex->dst_height); + } + + if (!clip) + { + fallback_clip = (cairo_rectangle_int_t) { + .width = stex->dst_width, + .height = stex->dst_height, + }; + clip = &fallback_clip; + } + + image_texture = + COGL_TEXTURE (cogl_texture_2d_new_with_size (cogl_context, + fb_width, fb_height)); + cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (image_texture), + FALSE); + if (!cogl_texture_allocate (COGL_TEXTURE (image_texture), &error)) + { + g_error_free (error); + cogl_object_unref (image_texture); + return FALSE; + } + + if (fb_width != stex->dst_width || fb_height != stex->dst_height) + { + CoglSubTexture *sub_texture; + + sub_texture = cogl_sub_texture_new (cogl_context, + image_texture, + 0, 0, + stex->dst_width, stex->dst_height); + cogl_object_unref (image_texture); + image_texture = COGL_TEXTURE (sub_texture); + } + + offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (image_texture)); + fb = COGL_FRAMEBUFFER (offscreen); + cogl_object_unref (image_texture); + if (!cogl_framebuffer_allocate (fb, &error)) + { + g_error_free (error); + cogl_object_unref (fb); + return FALSE; + } + + cogl_framebuffer_push_matrix (fb); + cogl_matrix_init_identity (&projection_matrix); + cogl_matrix_scale (&projection_matrix, + 1.0 / (stex->dst_width / 2.0), + -1.0 / (stex->dst_height / 2.0), 0); + cogl_matrix_translate (&projection_matrix, + -(stex->dst_width / 2.0), + -(stex->dst_height / 2.0), 0); + + cogl_framebuffer_set_projection_matrix (fb, &projection_matrix); + + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color); + + do_paint (stex, fb, stex->texture, NULL); + + cogl_framebuffer_pop_matrix (fb); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + clip->width, clip->height); + cogl_framebuffer_read_pixels (fb, + clip->x, clip->y, + clip->width, clip->height, + CLUTTER_CAIRO_FORMAT_ARGB32, + cairo_image_surface_get_data (surface)); + cogl_object_unref (fb); + + cairo_surface_mark_dirty (surface); + + return surface; +} + /** * meta_shaped_texture_get_image: * @stex: A #MetaShapedTexture @@ -1058,7 +1182,6 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex, { cairo_rectangle_int_t *transformed_clip = NULL; CoglTexture *texture, *mask_texture; - cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 }; cairo_surface_t *surface; g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL); @@ -1068,17 +1191,36 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex, if (texture == NULL) return NULL; + ensure_size_valid (stex); + + if (stex->dst_width == 0 || stex->dst_height == 0) + return NULL; if (clip != NULL) { - transformed_clip = alloca (sizeof (cairo_rectangle_int_t)); - *transformed_clip = *clip; + double tex_scale; + cairo_rectangle_int_t dst_rect; - if (!meta_rectangle_intersect (&texture_rect, transformed_clip, + transformed_clip = alloca (sizeof (cairo_rectangle_int_t)); + + clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL); + meta_rectangle_scale_double (clip, 1.0 / tex_scale, + META_ROUNDING_STRATEGY_GROW, + transformed_clip); + + dst_rect = (cairo_rectangle_int_t) { + .width = stex->dst_width, + .height = stex->dst_height, + }; + + if (!meta_rectangle_intersect (&dst_rect, transformed_clip, transformed_clip)) return NULL; } + if (should_get_via_offscreen (stex)) + return get_image_via_offscreen (stex, transformed_clip); + if (transformed_clip) texture = cogl_texture_new_from_sub_texture (texture, transformed_clip->x,