From 58c2c2c444543ebad964b9c0633f45defc1e4114 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: Draw external textures via offscreen EGLStream textures are imported as GL_TEXTURE_EXTERNAL_OES and reading pixels directly from them is not supported. To make it possible to get pixels, create an offscreen framebuffer and paint the actor to it, then read pixels from the framebuffer instead of the texture directly. https://gitlab.gnome.org/GNOME/mutter/merge_requests/362 --- src/compositor/meta-shaped-texture.c | 136 ++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 2 deletions(-) diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index 823bd47f2..d8c250fc9 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -35,6 +35,7 @@ #include "clutter-utils.h" #include "meta-texture-tower.h" +#include "core/boxes-private.h" #include "meta-cullable.h" @@ -906,6 +907,121 @@ meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex) return priv->opaque_region; } +static gboolean +should_get_via_offscreen (MetaShapedTexture *stex) +{ + MetaShapedTexturePrivate *priv = stex->priv; + + if (!cogl_texture_is_get_data_supported (priv->texture)) + return TRUE; + + return FALSE; +} + +static cairo_surface_t * +get_image_via_offscreen (MetaShapedTexture *stex, + cairo_rectangle_int_t *clip) +{ + MetaShapedTexturePrivate *priv = stex->priv; + 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; + unsigned 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 = priv->tex_width; + fb_height = priv->tex_height; + } + else + { + fb_width = clutter_util_next_p2 (priv->tex_width); + fb_height = clutter_util_next_p2 (priv->tex_height); + } + + if (!clip) + { + fallback_clip = (cairo_rectangle_int_t) { + .width = priv->tex_width, + .height = priv->tex_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 != priv->tex_width || fb_height != priv->tex_height) + { + CoglSubTexture *sub_texture; + + sub_texture = cogl_sub_texture_new (cogl_context, + image_texture, + 0, 0, + priv->tex_width, priv->tex_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 / (priv->tex_width / 2.0), + -1.0 / (priv->tex_height / 2.0), 0); + cogl_matrix_translate (&projection_matrix, + -(priv->tex_width / 2.0), + -(priv->tex_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, priv->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 @@ -927,7 +1043,6 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex, MetaShapedTexturePrivate *priv = stex->priv; 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); @@ -937,17 +1052,34 @@ meta_shaped_texture_get_image (MetaShapedTexture *stex, if (texture == NULL) return NULL; + if (priv->tex_width == 0 || priv->tex_height == 0) + return NULL; if (clip != NULL) { + double tex_scale; + cairo_rectangle_int_t tex_rect; + transformed_clip = alloca (sizeof (cairo_rectangle_int_t)); *transformed_clip = *clip; - if (!meta_rectangle_intersect (&texture_rect, transformed_clip, + clutter_actor_get_scale (CLUTTER_ACTOR (stex), &tex_scale, NULL); + meta_rectangle_scale_double (transformed_clip, 1.0 / tex_scale, + META_ROUNDING_STRATEGY_GROW); + + tex_rect = (cairo_rectangle_int_t) { + .width = priv->tex_width, + .height = priv->tex_height, + }; + + if (!meta_rectangle_intersect (&tex_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,