[gtk+] Make GtkViewport use GtkPixelCache



commit 2df27ce7f801da92e64ed966d96c47e3f5d190e7
Author: Alexander Larsson <alexl redhat com>
Date:   Sun Apr 21 01:54:05 2013 +0200

    Make GtkViewport use GtkPixelCache
    
    Since gdk_window_move() no longer uses XCopyArea all scrolling
    now re-renders everything in the window. To get performance
    back we use a GtkPixelCache to store already drawn children,
    and we when we expose the viewport we just blit the
    offscreen to the right place.

 gtk/gtkviewport.c |  104 +++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 82 insertions(+), 22 deletions(-)
---
diff --git a/gtk/gtkviewport.c b/gtk/gtkviewport.c
index 145e760..20120f6 100644
--- a/gtk/gtkviewport.c
+++ b/gtk/gtkviewport.c
@@ -32,6 +32,7 @@
 #include "gtkprivate.h"
 #include "gtkscrollable.h"
 #include "gtktypebuiltins.h"
+#include "gtkpixelcacheprivate.h"
 
 
 /**
@@ -66,6 +67,8 @@ struct _GtkViewportPrivate
   GdkWindow      *bin_window;
   GdkWindow      *view_window;
 
+  GtkPixelCache *pixel_cache;
+
   /* GtkScrollablePolicy needs to be checked when
    * driving the scrollable adjustment values */
   guint hscroll_policy : 1;
@@ -113,6 +116,8 @@ static void gtk_viewport_get_preferred_height     (GtkWidget        *widget,
 static void viewport_set_adjustment               (GtkViewport      *viewport,
                                                    GtkOrientation    orientation,
                                                    GtkAdjustment    *adjustment);
+static void gtk_viewport_queue_draw_region        (GtkWidget        *widget,
+                                                  const cairo_region_t *region);
 
 G_DEFINE_TYPE_WITH_CODE (GtkViewport, gtk_viewport, GTK_TYPE_BIN,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
@@ -139,6 +144,7 @@ gtk_viewport_class_init (GtkViewportClass *class)
   widget_class->style_updated = gtk_viewport_style_updated;
   widget_class->get_preferred_width = gtk_viewport_get_preferred_width;
   widget_class->get_preferred_height = gtk_viewport_get_preferred_height;
+  widget_class->queue_draw_region = gtk_viewport_queue_draw_region;
   
   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_VIEWPORT);
 
@@ -249,6 +255,8 @@ gtk_viewport_init (GtkViewport *viewport)
   priv->hadjustment = NULL;
   priv->vadjustment = NULL;
 
+  priv->pixel_cache = _gtk_pixel_cache_new ();
+
   viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, NULL);
   viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, NULL);
 }
@@ -301,10 +309,15 @@ static void
 gtk_viewport_destroy (GtkWidget *widget)
 {
   GtkViewport *viewport = GTK_VIEWPORT (widget);
+  GtkViewportPrivate *priv = viewport->priv;
 
   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
   viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
 
+  if (priv->pixel_cache)
+    _gtk_pixel_cache_free (priv->pixel_cache);
+  priv->pixel_cache = NULL;
+
   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->destroy (widget);
 }
 
@@ -650,6 +663,40 @@ gtk_viewport_get_view_window (GtkViewport *viewport)
 }
 
 static void
+gtk_viewport_bin_window_invalidate_handler (GdkWindow *window,
+                                           cairo_region_t *region)
+{
+  gpointer widget;
+  GtkViewport *viewport;
+  GtkViewportPrivate *priv;
+
+  gdk_window_get_user_data (window, &widget);
+  viewport = GTK_VIEWPORT (widget);
+  priv = viewport->priv;
+
+  _gtk_pixel_cache_invalidate (priv->pixel_cache, region);
+}
+
+static void
+gtk_viewport_queue_draw_region (GtkWidget *widget,
+                               const cairo_region_t *region)
+{
+  GtkViewport *viewport = GTK_VIEWPORT (widget);
+  GtkViewportPrivate *priv = viewport->priv;
+
+  /* There is no way we can know if a region targets the
+     not-currently-visible but in pixel cache region, so we
+     always just invalidate the whole thing whenever the
+     tree view gets a queue draw. This doesn't normally happen
+     in normal scrolling cases anyway. */
+  _gtk_pixel_cache_invalidate (priv->pixel_cache, NULL);
+
+  GTK_WIDGET_CLASS (gtk_viewport_parent_class)->queue_draw_region (widget,
+                                                                  region);
+}
+
+
+static void
 gtk_viewport_realize (GtkWidget *widget)
 {
   GtkViewport *viewport = GTK_VIEWPORT (widget);
@@ -713,6 +760,8 @@ gtk_viewport_realize (GtkWidget *widget)
 
   priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
   gtk_widget_register_window (widget, priv->bin_window);
+  gdk_window_set_invalidate_handler (priv->bin_window,
+                                    gtk_viewport_bin_window_invalidate_handler);
 
   child = gtk_bin_get_child (bin);
   if (child)
@@ -743,6 +792,25 @@ gtk_viewport_unrealize (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unrealize (widget);
 }
 
+static void
+draw_bin (cairo_t *cr,
+         gpointer user_data)
+{
+  GtkWidget *widget = GTK_WIDGET (user_data);
+  GtkViewport *viewport = GTK_VIEWPORT (widget);
+  GtkViewportPrivate *priv = viewport->priv;
+  GtkStyleContext *context;
+  int x, y;
+
+  context = gtk_widget_get_style_context (widget);
+
+  gdk_window_get_position (priv->bin_window, &x, &y);
+  gtk_render_background (context, cr, x, y,
+                        gdk_window_get_width (priv->bin_window),
+                        gdk_window_get_height (priv->bin_window));
+  GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
+}
+
 static gint
 gtk_viewport_draw (GtkWidget *widget,
                    cairo_t   *cr)
@@ -750,7 +818,6 @@ gtk_viewport_draw (GtkWidget *widget,
   GtkViewport *viewport = GTK_VIEWPORT (widget);
   GtkViewportPrivate *priv = viewport->priv;
   GtkStyleContext *context;
-  int x, y;
 
   context = gtk_widget_get_style_context (widget);
 
@@ -766,30 +833,23 @@ gtk_viewport_draw (GtkWidget *widget,
 
       gtk_style_context_restore (context);
     }
-  
-  if (gtk_cairo_should_draw_window (cr, priv->view_window))
-    {
-      /* This is a cute hack to ensure the contents of bin_window are
-       * restricted to where they are visible. We only need to do this
-       * clipping when called via gtk_widget_draw() and not in expose
-       * events. And when that happens every window (including this one)
-       * should be drawn.
-       */
-      gdk_window_get_position (priv->view_window, &x, &y);
-      cairo_rectangle (cr, x, y, 
-                       gdk_window_get_width (priv->view_window),
-                       gdk_window_get_height (priv->view_window));
-      cairo_clip (cr);
-    }
 
   if (gtk_cairo_should_draw_window (cr, priv->bin_window))
     {
-      gdk_window_get_position (priv->bin_window, &x, &y);
-      gtk_render_background (context, cr, x, y,
-                             gdk_window_get_width (priv->bin_window),
-                             gdk_window_get_height (priv->bin_window));
+      cairo_rectangle_int_t view_rect;
+      cairo_rectangle_int_t canvas_rect;
+
+      gdk_window_get_position (priv->view_window, &view_rect.x, &view_rect.y);
+      view_rect.width = gdk_window_get_width (priv->view_window);
+      view_rect.height = gdk_window_get_height (priv->view_window);
+
+      gdk_window_get_position (priv->bin_window, &canvas_rect.x, &canvas_rect.y);
+      canvas_rect.width = gdk_window_get_width (priv->bin_window);
+      canvas_rect.height = gdk_window_get_height (priv->bin_window);
 
-      GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
+      _gtk_pixel_cache_draw (priv->pixel_cache, cr, priv->bin_window,
+                            &view_rect, &canvas_rect,
+                            draw_bin, widget);
     }
 
   return FALSE;
@@ -843,7 +903,7 @@ gtk_viewport_size_allocate (GtkWidget     *widget,
 
   viewport_set_hadjustment_values (viewport);
   viewport_set_vadjustment_values (viewport);
-  
+
   child_allocation.x = 0;
   child_allocation.y = 0;
   child_allocation.width = gtk_adjustment_get_upper (hadjustment);


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