[mutter] shaped-texture: Get transformed textures pixels via offscreen



commit c84d7ebc6d208b082859cb61b7157db0eedda2cb
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Thu Dec 20 17:34:18 2018 +0100

    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 | 148 ++++++++++++++++++++++++++++++++++-
 1 file changed, 145 insertions(+), 3 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)
     {
+      double tex_scale;
+      cairo_rectangle_int_t dst_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 (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,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]