diff --git a/src/st/st-border-image.c b/src/st/st-border-image.c index 373b4ab7f..4e27d6ebe 100644 --- a/src/st/st-border-image.c +++ b/src/st/st-border-image.c @@ -2,6 +2,8 @@ #include +#include + #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 3f9647312..c1bb7e477 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 49fd5b2e2..f353e65cf 100644 --- a/src/st/st-shadow.c +++ b/src/st/st-shadow.c @@ -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) { diff --git a/src/st/st-shadow.h b/src/st/st-shadow.h index 97cecf868..53b149438 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 5bd41c7f9..0e23caaf6 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 9dbb45c04..884f806ae 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 a9d33afc3..90b156a85 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 97f8e835c..19796c69f 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 d4850a754..f56331d21 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,