[gnome-shell] Notice style transitions that don't change how StThemeNode paints



commit 5e7c25e136f28c2263770f86e1942b0baea080fd
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Thu Aug 26 14:10:46 2010 -0400

    Notice style transitions that don't change how StThemeNode paints
    
    Add st_theme_node_paint_equal() and use that to do two things:
    
     1) Avoid animating transitions where nothing changes.
     2) Copy cached painting state from the old theme node to the new
        theme node.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=627083

 src/st/st-border-image.c          |   25 +++++++++++
 src/st/st-border-image.h          |    3 +
 src/st/st-shadow.c                |   32 +++++++++++++-
 src/st/st-shadow.h                |    3 +
 src/st/st-theme-node-drawing.c    |   37 ++++++++++++++++
 src/st/st-theme-node-transition.c |    9 +++-
 src/st/st-theme-node.c            |   86 +++++++++++++++++++++++++++++++++++++
 src/st/st-theme-node.h            |    4 ++
 src/st/st-widget.c                |   14 ++++++-
 9 files changed, 210 insertions(+), 3 deletions(-)
---
diff --git a/src/st/st-border-image.c b/src/st/st-border-image.c
index 373b4ab..4e27d6e 100644
--- a/src/st/st-border-image.c
+++ b/src/st/st-border-image.c
@@ -2,6 +2,8 @@
 
 #include <config.h>
 
+#include <string.h>
+
 #include "st-border-image.h"
 
 struct _StBorderImage {
@@ -90,3 +92,26 @@ st_border_image_get_borders (StBorderImage *image,
   if (border_left)
     *border_left = image->border_left;
 }
+
+/**
+ * st_border_image_equal:
+ * @border_image: a #StBorder_Image
+ * @other: a different #StBorder_Image
+ *
+ * Check if two border_image objects are identical.
+ *
+ * Return value: %TRUE if the two border image objects are identical
+ */
+gboolean
+st_border_image_equal (StBorderImage *image,
+                       StBorderImage *other)
+{
+  g_return_val_if_fail (ST_IS_BORDER_IMAGE (image), FALSE);
+  g_return_val_if_fail (ST_IS_BORDER_IMAGE (other), FALSE);
+
+  return (image->border_top == other->border_top &&
+          image->border_right == other->border_right &&
+          image->border_bottom == other->border_bottom &&
+          image->border_left == other->border_left &&
+          strcmp (image->filename, other->filename) == 0);
+}
diff --git a/src/st/st-border-image.h b/src/st/st-border-image.h
index 3f96473..c1bb7e4 100644
--- a/src/st/st-border-image.h
+++ b/src/st/st-border-image.h
@@ -33,6 +33,9 @@ void        st_border_image_get_borders  (StBorderImage *image,
                                           int           *border_bottom,
                                           int           *border_left);
 
+gboolean st_border_image_equal (StBorderImage *image,
+                                StBorderImage *other);
+
 G_END_DECLS
 
 #endif /* __ST_BORDER_IMAGE_H__ */
diff --git a/src/st/st-shadow.c b/src/st/st-shadow.c
index 49fd5b2..f353e65 100644
--- a/src/st/st-shadow.c
+++ b/src/st/st-shadow.c
@@ -78,6 +78,37 @@ st_shadow_free (StShadow *shadow)
 }
 
 /**
+ * st_shadow_equal:
+ * @shadow: a #StShadow
+ * @other: a different #StShadow
+ *
+ * Check if two shadow objects are identical. Note that two shadows may
+ * compare non-identically if they differ only by floating point rounding
+ * errors.
+ *
+ * Return value: %TRUE if the two shadows are identical
+ */
+gboolean
+st_shadow_equal (StShadow *shadow,
+                 StShadow *other)
+{
+  g_return_val_if_fail (shadow != NULL, FALSE);
+  g_return_val_if_fail (other != NULL, FALSE);
+
+  /* We use strict equality to compare double quantities; this means
+   * that, for example, a shadow offset of 0.25in does not necessarily
+   * compare equal to a shadow offset of 18pt in this test. Assume
+   * that a few false negatives are mostly harmless.
+   */
+
+  return (clutter_color_equal (&shadow->color, &other->color) &&
+          shadow->xoffset == other->xoffset &&
+          shadow->yoffset == other->yoffset &&
+          shadow->blur == other->blur &&
+          shadow->spread == other->spread);
+}
+
+/**
  * st_shadow_get_box:
  * @shadow: a #StShadow
  * @actor_box: the box allocated to a #ClutterAlctor
@@ -105,7 +136,6 @@ st_shadow_get_box (StShadow              *shadow,
                    + shadow->blur + shadow->spread;
 }
 
-
 GType
 st_shadow_get_type (void)
 {
diff --git a/src/st/st-shadow.h b/src/st/st-shadow.h
index 97cecf8..53b1494 100644
--- a/src/st/st-shadow.h
+++ b/src/st/st-shadow.h
@@ -41,6 +41,9 @@ StShadow *st_shadow_new      (ClutterColor   *color,
 StShadow *st_shadow_copy     (const StShadow *shadow);
 void      st_shadow_free     (StShadow       *shadow);
 
+gboolean  st_shadow_equal    (StShadow       *shadow,
+                              StShadow       *other);
+
 void      st_shadow_get_box  (StShadow              *shadow,
                               const ClutterActorBox *actor_box,
                               ClutterActorBox       *shadow_box);
diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c
index 5bd41c7..0e23caa 100644
--- a/src/st/st-theme-node-drawing.c
+++ b/src/st/st-theme-node-drawing.c
@@ -1198,3 +1198,40 @@ st_theme_node_paint (StThemeNode           *node,
       paint_texture_with_opacity (node->background_texture, &background_box, paint_opacity);
     }
 }
+
+/**
+ * st_theme_node_copy_cached_paint_state:
+ * @node: a #StThemeNode
+ * @other: a different #StThemeNode
+ *
+ * Copy cached painting state from @other to @node. This function can be used to
+ * optimize redrawing cached background images when the style on an element changess
+ * in a way that doesn't affect background drawing. This function must only be called
+ * if st_theme_node_paint_equal (node, other) returns %TRUE.
+ */
+void
+st_theme_node_copy_cached_paint_state (StThemeNode *node,
+                                       StThemeNode *other)
+{
+  g_return_if_fail (ST_IS_THEME_NODE (node));
+  g_return_if_fail (ST_IS_THEME_NODE (other));
+
+  /* Check omitted for speed: */
+  /* g_return_if_fail (st_theme_node_paint_equal (node, other)); */
+
+  _st_theme_node_free_drawing_state (node);
+
+  node->alloc_width = other->alloc_width;
+  node->alloc_height = other->alloc_height;
+
+  if (other->background_shadow_material)
+    node->background_shadow_material = cogl_handle_ref (other->background_shadow_material);
+  if (other->border_shadow_material)
+    node->border_shadow_material = cogl_handle_ref (other->border_shadow_material);
+  if (other->background_texture)
+    node->background_texture = cogl_handle_ref (other->background_texture);
+  if (other->border_texture)
+    node->border_texture = cogl_handle_ref (other->border_texture);
+  if (other->corner_texture)
+    node->corner_texture = cogl_handle_ref (other->corner_texture);
+}
diff --git a/src/st/st-theme-node-transition.c b/src/st/st-theme-node-transition.c
index 9dbb45c..884f806 100644
--- a/src/st/st-theme-node-transition.c
+++ b/src/st/st-theme-node-transition.c
@@ -165,9 +165,16 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
           guint new_duration = st_theme_node_get_transition_duration (new_node);
 
           clutter_timeline_set_duration (priv->timeline, new_duration);
+
+          /* If the change doesn't affect painting, we don't need to redraw,
+           * but we still need to replace the node so that we properly share
+           * caching with the painting that happens after the transition finishes.
+           */
+          if (!st_theme_node_paint_equal (priv->new_theme_node, new_node))
+              priv->needs_setup = TRUE;
+
           g_object_unref (priv->new_theme_node);
           priv->new_theme_node = g_object_ref (new_node);
-          priv->needs_setup = TRUE;
         }
     }
 }
diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c
index a9d33af..90b156a 100644
--- a/src/st/st-theme-node.c
+++ b/src/st/st-theme-node.c
@@ -2760,6 +2760,9 @@ st_theme_node_geometry_equal (StThemeNode *node,
 {
   StSide side;
 
+  g_return_val_if_fail (ST_IS_THEME_NODE (node), FALSE);
+  g_return_val_if_fail (ST_IS_THEME_NODE (other), FALSE);
+
   _st_theme_node_ensure_geometry (node);
   _st_theme_node_ensure_geometry (other);
 
@@ -2780,3 +2783,86 @@ st_theme_node_geometry_equal (StThemeNode *node,
 
   return TRUE;
 }
+
+/**
+ * st_theme_node_paint_equal:
+ * @node: a #StThemeNode
+ * @other: a different #StThemeNode
+ *
+ * Check if st_theme_node_paint() will paint identically for @node as it does
+ * for @other. Note that in some cases this function may return %TRUE even
+ * if there is no visible difference in the painting.
+ *
+ * Return value: %TRUE if the two theme nodes paint identically. %FALSE if the
+ *   two nodes potentially paint differently.
+ */
+gboolean
+st_theme_node_paint_equal (StThemeNode *node,
+                           StThemeNode *other)
+{
+  StBorderImage *border_image, *other_border_image;
+  StShadow *shadow, *other_shadow;
+  int i;
+
+  g_return_val_if_fail (ST_IS_THEME_NODE (node), FALSE);
+  g_return_val_if_fail (ST_IS_THEME_NODE (other), FALSE);
+
+  _st_theme_node_ensure_background (node);
+  _st_theme_node_ensure_background (other);
+
+  if (!clutter_color_equal (&node->background_color, &other->background_color))
+    return FALSE;
+
+  if (node->background_gradient_type != other->background_gradient_type)
+    return FALSE;
+
+  if (node->background_gradient_type != ST_GRADIENT_NONE &&
+      !clutter_color_equal (&node->background_gradient_end, &other->background_gradient_end))
+    return FALSE;
+
+  if (g_strcmp0 (node->background_image, other->background_image) != 0)
+    return FALSE;
+
+  _st_theme_node_ensure_geometry (node);
+  _st_theme_node_ensure_geometry (other);
+
+  for (i = 0; i < 4; i++)
+    {
+      if (node->border_width[i] != other->border_width[i])
+        return FALSE;
+
+      if (node->border_width[i] > 0 &&
+          !clutter_color_equal (&node->border_color[i], &other->border_color[i]))
+        return FALSE;
+
+      if (node->border_radius[i] != other->border_radius[i])
+        return FALSE;
+    }
+
+  if (node->outline_width != other->outline_width)
+    return FALSE;
+
+  if (node->outline_width > 0 &&
+      !clutter_color_equal (&node->outline_color, &other->outline_color))
+    return FALSE;
+
+  border_image = st_theme_node_get_border_image (node);
+  other_border_image = st_theme_node_get_border_image (other);
+
+  if ((border_image == NULL) != (other_border_image == NULL))
+    return FALSE;
+
+  if (border_image != NULL && !st_border_image_equal (border_image, other_border_image))
+    return FALSE;
+
+  shadow = st_theme_node_get_shadow (node);
+  other_shadow = st_theme_node_get_shadow (other);
+
+  if ((shadow == NULL) != (other_shadow == NULL))
+    return FALSE;
+
+  if (shadow != NULL && !st_shadow_equal (shadow, other_shadow))
+    return FALSE;
+
+  return TRUE;
+}
diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h
index 97f8e83..19796c6 100644
--- a/src/st/st-theme-node.h
+++ b/src/st/st-theme-node.h
@@ -191,11 +191,15 @@ void st_theme_node_get_paint_box           (StThemeNode           *node,
 
 gboolean st_theme_node_geometry_equal (StThemeNode *node,
                                        StThemeNode *other);
+gboolean st_theme_node_paint_equal    (StThemeNode *node,
+                                       StThemeNode *other);
 
 void st_theme_node_paint (StThemeNode            *node,
                           const ClutterActorBox  *box,
                           guint8                  paint_opacity);
 
+void st_theme_node_copy_cached_paint_state (StThemeNode *node,
+                                            StThemeNode *other);
 
 G_END_DECLS
 
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index d4850a7..f56331d 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -1222,6 +1222,7 @@ st_widget_recompute_style (StWidget    *widget,
 {
   StThemeNode *new_theme_node = st_widget_get_theme_node (widget);
   int transition_duration;
+  gboolean paint_equal;
 
   if (!old_theme_node ||
       !st_theme_node_geometry_equal (old_theme_node, new_theme_node))
@@ -1229,6 +1230,11 @@ st_widget_recompute_style (StWidget    *widget,
 
   transition_duration = st_theme_node_get_transition_duration (new_theme_node);
 
+  paint_equal = old_theme_node && st_theme_node_paint_equal (old_theme_node, new_theme_node);
+
+  if (paint_equal)
+    st_theme_node_copy_cached_paint_state (new_theme_node, old_theme_node);
+
   if (transition_duration > 0)
     {
       if (widget->priv->transition_animation != NULL)
@@ -1236,8 +1242,14 @@ st_widget_recompute_style (StWidget    *widget,
           st_theme_node_transition_update (widget->priv->transition_animation,
                                            new_theme_node);
         }
-      else if (old_theme_node)
+      else if (old_theme_node && !paint_equal)
         {
+          /* Since our transitions are only of the painting done by StThemeNode, we
+           * only want to start a transition when what is painted changes; if
+           * other visual aspects like the foreground color of a label change,
+           * we can't animate that anyways.
+           */
+
           widget->priv->transition_animation =
             st_theme_node_transition_new (old_theme_node,
                                           new_theme_node,



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