[gtk+/wip/ebassi/gsk-renderer] gsk: Add the ability to create fallback renderers



commit 73e3cf689c167bfd7baa5857cc0c77c39431ca3d
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Thu Aug 25 11:31:56 2016 +0100

    gsk: Add the ability to create fallback renderers
    
    While porting GTK to GskRenderer we noticed that the current fallback
    code for widgets using Cairo to draw is not enough to cover all the
    possible cases.
    
    For instance, if a container widget still uses GtkWidget::draw to render
    its children, and at least one of them has been ported to using render
    nodes instead, the container won't know how to draw it.
    
    For this reason we want to provide to layers above GSK the ability to
    create a "fallback" renderer instance, created using a "parent"
    GskRenderer instance, but using a Cairo context as the rendering target
    instead of a GdkDrawingContext.
    
    GTK will use this inside the gtk_widget_draw() implementation, if a
    widget implements GtkWidgetClass.get_render_node().

 gsk/gskcairorenderer.c   |    6 +++-
 gsk/gskglrenderer.c      |   14 ++++++--
 gsk/gskrenderer.c        |   79 ++++++++++++++++++++++++++++++++++++++++++++-
 gsk/gskrenderer.h        |    5 +++
 gsk/gskrendererprivate.h |    1 +
 gtk/gtkwidget.c          |   70 +++++++++++++++++++++++++++++-----------
 6 files changed, 149 insertions(+), 26 deletions(-)
---
diff --git a/gsk/gskcairorenderer.c b/gsk/gskcairorenderer.c
index 3fa2ebe..281587d 100644
--- a/gsk/gskcairorenderer.c
+++ b/gsk/gskcairorenderer.c
@@ -135,7 +135,11 @@ gsk_cairo_renderer_render (GskRenderer   *renderer,
   GdkDrawingContext *context = gsk_renderer_get_drawing_context (renderer);
   cairo_t *cr;
 
-  cr = gdk_drawing_context_get_cairo_context (context);
+  if (context != NULL)
+    cr = gdk_drawing_context_get_cairo_context (context);
+  else
+    cr = gsk_renderer_get_cairo_context (renderer);
+
   if (cr == NULL)
     return;
 
diff --git a/gsk/gskglrenderer.c b/gsk/gskglrenderer.c
index 373f389..c4eec60 100644
--- a/gsk/gskglrenderer.c
+++ b/gsk/gskglrenderer.c
@@ -920,9 +920,17 @@ out:
     GdkWindow *window;
     cairo_t *cr;
 
-    /* XXX: Add GdkDrawingContext API */
-    cr = gdk_drawing_context_get_cairo_context (context);
-    window = gdk_drawing_context_get_window (context);
+    if (context != NULL)
+      {
+        /* XXX: Add GdkDrawingContext API */
+        cr = gdk_drawing_context_get_cairo_context (context);
+        window = gdk_drawing_context_get_window (context);
+      }
+    else
+      {
+        cr = gsk_renderer_get_cairo_context (renderer);
+        window = gsk_renderer_get_window (renderer);
+      }
 
     gdk_cairo_draw_from_gl (cr, window,
                             self->texture_id,
diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c
index ebdb1a4..d77bfe1 100644
--- a/gsk/gskrenderer.c
+++ b/gsk/gskrenderer.c
@@ -37,6 +37,7 @@
 
 #include "gskrendererprivate.h"
 
+#include "gskcairorendererprivate.h"
 #include "gskdebugprivate.h"
 #include "gskglrendererprivate.h"
 #include "gskprofilerprivate.h"
@@ -68,6 +69,7 @@ typedef struct
   GdkDrawingContext *drawing_context;
   GskRenderNode *root_node;
   GdkDisplay *display;
+  cairo_t *cairo_context;
 
   GskProfiler *profiler;
 
@@ -121,6 +123,7 @@ gsk_renderer_dispose (GObject *gobject)
 
   gsk_renderer_unrealize (self);
 
+  g_clear_pointer (&priv->cairo_context, cairo_destroy);
   g_clear_object (&priv->window);
   g_clear_object (&priv->display);
 
@@ -603,6 +606,16 @@ gsk_renderer_render (GskRenderer       *renderer,
 
   if (context != NULL)
     priv->drawing_context = g_object_ref (context);
+  else
+    {
+      if (priv->cairo_context == NULL)
+        {
+          g_critical ("The given GskRenderer instance was not created using "
+                      "gsk_renderer_create_fallback(), but you forgot to pass "
+                      "a GdkDrawingContext.");
+          return;
+        }
+    }
 
   priv->root_node = gsk_render_node_ref (root);
   gsk_render_node_make_immutable (priv->root_node);
@@ -695,7 +708,7 @@ gsk_renderer_get_for_display (GdkDisplay *display)
     }
 
   if (use_software[0] != '0')
-    return NULL;
+    return g_object_new (GSK_TYPE_CAIRO_RENDERER, "display", display, NULL);
 
 #ifdef GDK_WINDOWING_X11
   if (GDK_IS_X11_DISPLAY (display))
@@ -707,7 +720,7 @@ gsk_renderer_get_for_display (GdkDisplay *display)
     renderer_type = GSK_TYPE_GL_RENDERER;
   else
 #endif
-    return NULL;
+    renderer_type = GSK_TYPE_CAIRO_RENDERER;
 
   GSK_NOTE (RENDERER, g_print ("Creating renderer of type '%s' for display '%s'\n",
                                g_type_name (renderer_type),
@@ -717,3 +730,65 @@ gsk_renderer_get_for_display (GdkDisplay *display)
 
   return g_object_new (renderer_type, "display", display, NULL);
 }
+
+static void
+gsk_renderer_set_cairo_context (GskRenderer *renderer,
+                                cairo_t     *cr)
+{
+  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+  g_clear_pointer (&priv->cairo_context, cairo_destroy);
+
+  if (cr != NULL)
+    priv->cairo_context = cairo_reference (cr);
+}
+
+cairo_t *
+gsk_renderer_get_cairo_context (GskRenderer *renderer)
+{
+  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+  return priv->cairo_context;
+}
+
+/**
+ * gsk_renderer_create_fallback:
+ * @renderer: a #GskRenderer
+ * @viewport: the viewport for the fallback renderer
+ * @cr: a Cairo context
+ *
+ * Creates a fallback #GskRenderer using the same display and window of
+ * the given @renderer, and instructs it to render to a given Cairo
+ * context.
+ *
+ * Typically, you'll use this function to implement fallback rendering
+ * of #GskRenderNodes on an intermediate Cairo context, instead of using
+ * the drawing context associated to a #GdkWindow's rendering buffer.
+ *
+ * Returns: (transfer full): a newly created fallback #GskRenderer instance
+ *
+ * Since: 3.22
+ */
+GskRenderer *
+gsk_renderer_create_fallback (GskRenderer           *renderer,
+                              const graphene_rect_t *viewport,
+                              cairo_t               *cr)
+{
+  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+  GskRenderer *res;
+
+  g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
+  g_return_val_if_fail (cr != NULL, NULL);
+
+  res = g_object_new (GSK_TYPE_CAIRO_RENDERER,
+                      "display", priv->display,
+                      "window", priv->window,
+                      "scale-factor", priv->scale_factor,
+                      "viewport", viewport,
+                      NULL);
+
+  gsk_renderer_set_cairo_context (res, cr);
+  gsk_renderer_realize (res);
+
+  return res;
+}
diff --git a/gsk/gskrenderer.h b/gsk/gskrenderer.h
index 33dd958..4c8fc58 100644
--- a/gsk/gskrenderer.h
+++ b/gsk/gskrenderer.h
@@ -71,6 +71,11 @@ GDK_AVAILABLE_IN_3_22
 GskRenderNode *         gsk_renderer_create_render_node         (GskRenderer             *renderer);
 
 GDK_AVAILABLE_IN_3_22
+GskRenderer *           gsk_renderer_create_fallback            (GskRenderer             *renderer,
+                                                                 const graphene_rect_t   *viewport,
+                                                                 cairo_t                 *cr);
+
+GDK_AVAILABLE_IN_3_22
 void                    gsk_renderer_render                     (GskRenderer             *renderer,
                                                                  GskRenderNode           *root,
                                                                  GdkDrawingContext       *context);
diff --git a/gsk/gskrendererprivate.h b/gsk/gskrendererprivate.h
index 1267188..d702ae5 100644
--- a/gsk/gskrendererprivate.h
+++ b/gsk/gskrendererprivate.h
@@ -48,6 +48,7 @@ gboolean gsk_renderer_is_realized (GskRenderer *renderer);
 
 GskRenderNode *         gsk_renderer_get_root_node              (GskRenderer *renderer);
 GdkDrawingContext *     gsk_renderer_get_drawing_context        (GskRenderer *renderer);
+cairo_t *               gsk_renderer_get_cairo_context          (GskRenderer *renderer);
 
 GskProfiler *           gsk_renderer_get_profiler               (GskRenderer *renderer);
 
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 4cb3a01..0c4634b 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -6904,6 +6904,18 @@ gtk_cairo_set_marked_for_draw (cairo_t  *cr,
     cairo_set_user_data (cr, &mark_for_draw_key, NULL, NULL);
 }
 
+GskRenderer *
+gtk_widget_get_renderer (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+
+  toplevel = _gtk_widget_get_toplevel (widget);
+  if (_gtk_widget_is_toplevel (toplevel))
+    return gtk_window_get_renderer (GTK_WINDOW (toplevel));
+
+  return NULL;
+}
+
 /**
  * gtk_cairo_should_draw_window:
  * @cr: a cairo context
@@ -6971,6 +6983,7 @@ gtk_widget_draw_internal (GtkWidget *widget,
 
   if (gdk_cairo_get_clip_rectangle (cr, NULL))
     {
+      GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (widget);
       GdkWindow *event_window = NULL;
       gboolean result;
       gboolean push_group;
@@ -7003,17 +7016,46 @@ gtk_widget_draw_internal (GtkWidget *widget,
       if (_gtk_widget_get_alloc_needed (widget))
         g_warning ("%s %p is drawn without a current allocation. This should not happen.", 
G_OBJECT_TYPE_NAME (widget), widget);
 
-      if (g_signal_has_handler_pending (widget, widget_signals[DRAW], 0, FALSE))
+      /* If the widget uses GSK render nodes then we need a fallback path to
+       * render on the Cairo context; otherwise we just go through the old
+       * GtkWidget::draw path
+       */
+      if (widget_class->get_render_node != NULL)
         {
-          g_signal_emit (widget, widget_signals[DRAW],
-                         0, cr,
-                         &result);
+          GskRenderer *renderer = gtk_widget_get_renderer (widget);
+          GskRenderer *fallback;
+          graphene_rect_t viewport;
+          GskRenderNode *node;
+
+          graphene_rect_init (&viewport,
+                              widget->priv->clip.x,
+                              widget->priv->clip.y,
+                              widget->priv->clip.width,
+                              widget->priv->clip.height);
+          fallback = gsk_renderer_create_fallback (renderer, &viewport, cr);
+          node = gtk_widget_get_render_node (widget, fallback);
+          if (node != NULL)
+            {
+              gsk_renderer_render (fallback, node, NULL);
+              gsk_render_node_unref (node);
+            }
+
+          g_object_unref (fallback);
         }
-      else if (GTK_WIDGET_GET_CLASS (widget)->draw)
+      else
         {
-          cairo_save (cr);
-          GTK_WIDGET_GET_CLASS (widget)->draw (widget, cr);
-          cairo_restore (cr);
+          if (g_signal_has_handler_pending (widget, widget_signals[DRAW], 0, FALSE))
+            {
+              g_signal_emit (widget, widget_signals[DRAW],
+                             0, cr,
+                             &result);
+            }
+          else if (GTK_WIDGET_GET_CLASS (widget)->draw)
+            {
+              cairo_save (cr);
+              GTK_WIDGET_GET_CLASS (widget)->draw (widget, cr);
+              cairo_restore (cr);
+            }
         }
 
 #ifdef G_ENABLE_DEBUG
@@ -17454,18 +17496,6 @@ gtk_widget_reset_controllers (GtkWidget *widget)
     }
 }
 
-GskRenderer *
-gtk_widget_get_renderer (GtkWidget *widget)
-{
-  GtkWidget *toplevel;
-
-  toplevel = _gtk_widget_get_toplevel (widget);
-  if (_gtk_widget_is_toplevel (toplevel))
-    return gtk_window_get_renderer (GTK_WINDOW (toplevel));
-
-  return NULL;
-}
-
 GskRenderNode *
 gtk_widget_create_render_node (GtkWidget   *widget,
                                GskRenderer *renderer,


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