[mutter/wip/garnacho/wayland-emulated-output-transform: 24/34] clutter: Add infrastructure to render ClutterStageViews to offscreen



commit c9988adb843b76255e74df621e6b97dd5b74c736
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Aug 1 02:44:57 2016 +0200

    clutter: Add infrastructure to render ClutterStageViews to offscreen
    
    The offscreen is given through the ::back-buffer property, the ClutterStageView
    will set up the the CoglPipeline used to render it back to the "onscreen"
    framebuffer.
    
    The pipeline can be altered through the setup_pipeline() vfunc, so ClutterStageView
    implementations can alter the default behavior of blitting from offscreen to
    onscreen with no transformations.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=745079

 clutter/clutter/clutter-stage-view.c |  180 +++++++++++++++++++++++++++++++++-
 clutter/clutter/clutter-stage-view.h |   18 ++++
 2 files changed, 197 insertions(+), 1 deletions(-)
---
diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c
index 8eb17b8..ce47bb9 100644
--- a/clutter/clutter/clutter-stage-view.c
+++ b/clutter/clutter/clutter-stage-view.c
@@ -27,6 +27,7 @@ enum
 
   PROP_LAYOUT,
   PROP_FRAMEBUFFER,
+  PROP_OFFSCREEN,
 
   PROP_LAST
 };
@@ -37,6 +38,10 @@ typedef struct _ClutterStageViewPrivate
 {
   cairo_rectangle_int_t layout;
   CoglFramebuffer *framebuffer;
+
+  CoglOffscreen *offscreen;
+  CoglPipeline *pipeline;
+
   guint dirty_viewport   : 1;
   guint dirty_projection : 1;
 } ClutterStageViewPrivate;
@@ -59,9 +64,137 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view)
   ClutterStageViewPrivate *priv =
     clutter_stage_view_get_instance_private (view);
 
+  if (priv->offscreen)
+    return priv->offscreen;
+  else
+    return priv->framebuffer;
+}
+
+CoglFramebuffer *
+clutter_stage_view_get_onscreen (ClutterStageView *view)
+{
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+
   return priv->framebuffer;
 }
 
+static void
+clutter_stage_view_ensure_pipeline (ClutterStageView *view)
+{
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+  ClutterStageViewClass *view_class =
+    CLUTTER_STAGE_VIEW_GET_CLASS (view);
+
+  if (!priv->offscreen)
+    return;
+  if (priv->pipeline)
+    return;
+
+  priv->pipeline =
+    cogl_pipeline_new (cogl_framebuffer_get_context (priv->offscreen));
+  cogl_pipeline_set_layer_filters (priv->pipeline, 0,
+                                   COGL_PIPELINE_FILTER_NEAREST,
+                                   COGL_PIPELINE_FILTER_NEAREST);
+  cogl_pipeline_set_layer_texture (priv->pipeline, 0,
+                                   cogl_offscreen_get_texture (priv->offscreen));
+  cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0,
+                                     COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
+
+  if (view_class->setup_pipeline)
+    view_class->setup_pipeline (view, priv->pipeline);
+}
+
+void
+clutter_stage_view_invalidate_pipeline (ClutterStageView *view)
+{
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+
+  g_clear_pointer (&priv->pipeline, cogl_object_unref);
+}
+
+static void
+clutter_stage_view_set_offscreen (ClutterStageView *view,
+                                  CoglOffscreen    *offscreen)
+{
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+
+  g_clear_pointer (&priv->offscreen, cogl_object_unref);
+  priv->offscreen = offscreen ? cogl_object_ref (offscreen) : NULL;
+
+  clutter_stage_view_invalidate_pipeline (view);
+}
+
+static void
+transform_rect_to_onscreen (ClutterStageView        *view,
+                            const cairo_rectangle_t *rect,
+                            cairo_rectangle_t       *rect_out)
+{
+  gfloat x1, y1, x2, y2;
+
+  x1 = rect->x;
+  y1 = rect->y;
+  clutter_stage_view_transform_to_onscreen (view, &x1, &y1);
+
+  x2 = rect->x + rect->width;
+  y2 = rect->y + rect->height;
+  clutter_stage_view_transform_to_onscreen (view, &x2, &y2);
+
+  rect_out->x = MIN (x1, x2);
+  rect_out->y = MIN (y1, y2);
+  rect_out->width = ABS (x2 - x1);
+  rect_out->height = ABS (y2 - y1);
+}
+
+void
+clutter_stage_view_blit_offscreen (ClutterStageView            *view,
+                                   const cairo_rectangle_int_t *rect)
+{
+  ClutterStageViewPrivate *priv =
+    clutter_stage_view_get_instance_private (view);
+  cairo_rectangle_t texture_rect, onscreen_rect;
+  CoglMatrix matrix;
+
+  if (!priv->offscreen)
+    return;
+
+  clutter_stage_view_ensure_pipeline (view);
+  cogl_framebuffer_push_matrix (priv->framebuffer);
+
+  /* Set transform so 0,0 is on the top left corner and 1,1 on
+   * the bottom right corner.
+   */
+  cogl_matrix_init_identity (&matrix);
+  cogl_matrix_translate (&matrix, -1, 1, 0);
+  cogl_matrix_scale (&matrix, 2, -2, 0);
+  cogl_framebuffer_set_projection_matrix (priv->framebuffer, &matrix);
+
+  texture_rect = (cairo_rectangle_t) {
+    (gdouble) rect->x / cogl_framebuffer_get_width (priv->offscreen),
+    (gdouble) rect->y / cogl_framebuffer_get_height (priv->offscreen),
+    (gdouble) rect->width / cogl_framebuffer_get_width (priv->offscreen),
+    (gdouble) rect->height / cogl_framebuffer_get_height (priv->offscreen)
+  };
+
+  transform_rect_to_onscreen (view, &texture_rect, &onscreen_rect);
+
+  cogl_framebuffer_draw_textured_rectangle (priv->framebuffer,
+                                            priv->pipeline,
+                                            onscreen_rect.x,
+                                            onscreen_rect.y,
+                                            onscreen_rect.x + onscreen_rect.width,
+                                            onscreen_rect.y + onscreen_rect.height,
+                                            texture_rect.x,
+                                            texture_rect.y,
+                                            texture_rect.x + texture_rect.width,
+                                            texture_rect.y + texture_rect.height);
+
+  cogl_framebuffer_pop_matrix (priv->framebuffer);
+}
+
 gboolean
 clutter_stage_view_is_dirty_viewport (ClutterStageView *view)
 {
@@ -100,6 +233,27 @@ clutter_stage_view_set_dirty_projection (ClutterStageView *view,
   priv->dirty_projection = dirty;
 }
 
+void
+clutter_stage_view_transform_to_onscreen (ClutterStageView *view,
+                                          gfloat           *x,
+                                          gfloat           *y)
+{
+  ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view);
+  gfloat z = 0, w = 1;
+  CoglMatrix matrix;
+
+  view_class->get_transformation_matrix (view, &matrix);
+  cogl_matrix_get_inverse (&matrix, &matrix);
+  cogl_matrix_transform_point (&matrix, x, y, &z, &w);
+}
+
+static void
+clutter_stage_default_get_transformation_matrix (ClutterStageView *view,
+                                                 CoglMatrix       *matrix)
+{
+  cogl_matrix_init_identity (matrix);
+}
+
 static void
 clutter_stage_view_get_property (GObject    *object,
                                  guint       prop_id,
@@ -118,6 +272,11 @@ clutter_stage_view_get_property (GObject    *object,
     case PROP_FRAMEBUFFER:
       g_value_set_boxed (value, priv->framebuffer);
       break;
+    case PROP_OFFSCREEN:
+      g_value_set_boxed (value, priv->offscreen);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
 }
 
@@ -141,6 +300,11 @@ clutter_stage_view_set_property (GObject      *object,
     case PROP_FRAMEBUFFER:
       priv->framebuffer = g_value_dup_boxed (value);
       break;
+    case PROP_OFFSCREEN:
+      clutter_stage_view_set_offscreen (view, g_value_get_boxed (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
 }
 
@@ -152,6 +316,10 @@ clutter_stage_view_dispose (GObject *object)
     clutter_stage_view_get_instance_private (view);
 
   g_clear_pointer (&priv->framebuffer, cogl_object_unref);
+  g_clear_pointer (&priv->offscreen, cogl_object_unref);
+  g_clear_pointer (&priv->pipeline, cogl_object_unref);
+
+  G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object);
 }
 
 static void
@@ -169,6 +337,8 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+  klass->get_transformation_matrix = clutter_stage_default_get_transformation_matrix;
+
   object_class->get_property = clutter_stage_view_get_property;
   object_class->set_property = clutter_stage_view_set_property;
   object_class->dispose = clutter_stage_view_dispose;
@@ -184,7 +354,15 @@ clutter_stage_view_class_init (ClutterStageViewClass *klass)
   obj_props[PROP_FRAMEBUFFER] =
     g_param_spec_boxed ("framebuffer",
                         "View framebuffer",
-                        "The framebuffer of the view",
+                        "The front buffer of the view",
+                        COGL_TYPE_HANDLE,
+                        G_PARAM_READWRITE |
+                        G_PARAM_STATIC_STRINGS);
+
+  obj_props[PROP_OFFSCREEN] =
+    g_param_spec_boxed ("offscreen",
+                        "Offscreen buffer",
+                        "Framebuffer used as intermediate buffer",
                         COGL_TYPE_HANDLE,
                         G_PARAM_READWRITE |
                         G_PARAM_STATIC_STRINGS);
diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h
index e9fe3a0..257f35d 100644
--- a/clutter/clutter/clutter-stage-view.h
+++ b/clutter/clutter/clutter-stage-view.h
@@ -33,6 +33,12 @@ G_DECLARE_DERIVABLE_TYPE (ClutterStageView, clutter_stage_view,
 struct _ClutterStageViewClass
 {
   GObjectClass parent_class;
+
+  void (* setup_pipeline) (ClutterStageView *view,
+                           CoglPipeline     *pipeline);
+
+  void (* get_transformation_matrix) (ClutterStageView *view,
+                                      CoglMatrix       *matrix);
 };
 
 CLUTTER_AVAILABLE_IN_MUTTER
@@ -41,6 +47,18 @@ void clutter_stage_view_get_layout (ClutterStageView      *view,
 
 CLUTTER_AVAILABLE_IN_MUTTER
 CoglFramebuffer *clutter_stage_view_get_framebuffer (ClutterStageView *view);
+CLUTTER_AVAILABLE_IN_MUTTER
+CoglFramebuffer *clutter_stage_view_get_onscreen (ClutterStageView *view);
+CLUTTER_AVAILABLE_IN_MUTTER
+void             clutter_stage_view_invalidate_pipeline (ClutterStageView *view);
+
+CLUTTER_AVAILABLE_IN_MUTTER
+void             clutter_stage_view_transform_to_onscreen (ClutterStageView *view,
+                                                           gfloat           *x,
+                                                           gfloat           *y);
+
+void clutter_stage_view_blit_offscreen (ClutterStageView            *view,
+                                       const cairo_rectangle_int_t *clip);
 
 gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view);
 


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