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
This commit is contained in:
Jonas Ådahl 2018-12-20 17:34:18 +01:00 committed by Ray Strode
parent 1fe3adcd95
commit 58c2c2c444

View File

@ -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,