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
This commit is contained in:
Owen W. Taylor 2010-08-26 14:10:46 -04:00
parent b9f9dd948a
commit 5e7c25e136
9 changed files with 210 additions and 3 deletions

View File

@ -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);
}

View File

@ -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__ */

View File

@ -77,6 +77,37 @@ st_shadow_free (StShadow *shadow)
g_slice_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
@ -105,7 +136,6 @@ st_shadow_get_box (StShadow *shadow,
+ shadow->blur + shadow->spread;
}
GType
st_shadow_get_type (void)
{

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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,