From 2b21f1d48c1fac2f4239986417910859cb7b629d Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Thu, 11 Nov 2010 18:23:25 -0500 Subject: [PATCH] Implement more accurate clipping of obscured shadows Instead of making optimizing obscured shadows an all-or-none operation, pass the clip region to meta_shadow_paint() and only paint the 9-slices that are at least partially visible. https://bugzilla.gnome.org/show_bug.cgi?id=592382 --- src/compositor/meta-shadow-factory-private.h | 3 +- src/compositor/meta-shadow-factory.c | 49 +++++++++++---- src/compositor/meta-window-actor.c | 65 ++++++++++---------- 3 files changed, 71 insertions(+), 46 deletions(-) diff --git a/src/compositor/meta-shadow-factory-private.h b/src/compositor/meta-shadow-factory-private.h index 70a662386..c3414ac22 100644 --- a/src/compositor/meta-shadow-factory-private.h +++ b/src/compositor/meta-shadow-factory-private.h @@ -46,7 +46,8 @@ void meta_shadow_paint (MetaShadow *shadow, int window_y, int window_width, int window_height, - guint8 opacity); + guint8 opacity, + cairo_region_t *clip); void meta_shadow_get_bounds (MetaShadow *shadow, int window_x, int window_y, diff --git a/src/compositor/meta-shadow-factory.c b/src/compositor/meta-shadow-factory.c index ade753ca0..a11495264 100644 --- a/src/compositor/meta-shadow-factory.c +++ b/src/compositor/meta-shadow-factory.c @@ -188,6 +188,9 @@ meta_shadow_unref (MetaShadow *shadow) * @window_y: y position of the region to paint a shadow for * @window_width: actual width of the region to paint a shadow for * @window_height: actual height of the region to paint a shadow for + * @clip: (allow-none): if non-%NULL specifies the visible portion + * of the shadow. Drawing won't be strictly clipped to this region + * but it will be used to optimize what is drawn. * * Paints the shadow at the given position, for the specified actual * size of the region. (Since a #MetaShadow can be shared between @@ -195,20 +198,21 @@ meta_shadow_unref (MetaShadow *shadow) * size needs to be passed in here.) */ void -meta_shadow_paint (MetaShadow *shadow, - int window_x, - int window_y, - int window_width, - int window_height, - guint8 opacity) +meta_shadow_paint (MetaShadow *shadow, + int window_x, + int window_y, + int window_width, + int window_height, + guint8 opacity, + cairo_region_t *clip) { float texture_width = cogl_texture_get_width (shadow->texture); float texture_height = cogl_texture_get_height (shadow->texture); int i, j; float src_x[4]; float src_y[4]; - float dest_x[4]; - float dest_y[4]; + int dest_x[4]; + int dest_y[4]; int n_x, n_y; cogl_material_set_color4ub (shadow->material, @@ -267,11 +271,30 @@ meta_shadow_paint (MetaShadow *shadow, } for (j = 0; j < n_y; j++) - for (i = 0; i < n_x; i++) - cogl_rectangle_with_texture_coords (dest_x[i], dest_y[j], - dest_x[i + 1], dest_y[j + 1], - src_x[i], src_y[j], - src_x[i + 1], src_y[j + 1]); + { + cairo_rectangle_int_t dest_rect; + dest_rect.y = dest_y[j]; + dest_rect.height = dest_y[j + 1] - dest_y[j]; + + for (i = 0; i < n_x; i++) + { + cairo_region_overlap_t overlap; + + dest_rect.x = dest_x[i]; + dest_rect.width = dest_x[i + 1] - dest_x[i]; + + if (clip) + overlap = cairo_region_contains_rectangle (clip, &dest_rect); + else + overlap = CAIRO_REGION_OVERLAP_PART; + + if (overlap != CAIRO_REGION_OVERLAP_OUT) + cogl_rectangle_with_texture_coords (dest_x[i], dest_y[j], + dest_x[i + 1], dest_y[j + 1], + src_x[i], src_y[j], + src_x[i + 1], src_y[j + 1]); + } + } } /** diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c index 55a981e48..47da17f04 100644 --- a/src/compositor/meta-window-actor.c +++ b/src/compositor/meta-window-actor.c @@ -60,6 +60,8 @@ struct _MetaWindowActorPrivate /* A rectangular region with the unshaped extends of the window * texture */ cairo_region_t *bounding_region; + /* The region we should clip to when painting the shadow */ + cairo_region_t *shadow_clip; /* Extracted size-invariant shape used for shadows */ MetaWindowShape *shadow_shape; @@ -93,7 +95,6 @@ struct _MetaWindowActorPrivate guint needs_reshape : 1; guint recompute_focused_shadow : 1; guint recompute_unfocused_shadow : 1; - guint paint_shadow : 1; guint size_changed : 1; guint needs_destroy : 1; @@ -140,6 +141,7 @@ static gboolean meta_window_actor_has_shadow (MetaWindowActor *self); static void meta_window_actor_clear_shape_region (MetaWindowActor *self); static void meta_window_actor_clear_bounding_region (MetaWindowActor *self); +static void meta_window_actor_clear_shadow_clip (MetaWindowActor *self); static gboolean is_shaped (MetaDisplay *display, Window xwindow); @@ -283,7 +285,6 @@ meta_window_actor_init (MetaWindowActor *self) MetaWindowActorPrivate); priv->opacity = 0xff; priv->shadow_class = NULL; - priv->paint_shadow = TRUE; } static void @@ -439,6 +440,7 @@ meta_window_actor_dispose (GObject *object) meta_window_actor_clear_shape_region (self); meta_window_actor_clear_bounding_region (self); + meta_window_actor_clear_shadow_clip (self); if (priv->shadow_class != NULL) { @@ -659,27 +661,24 @@ meta_window_actor_paint (ClutterActor *actor) { MetaWindowActor *self = META_WINDOW_ACTOR (actor); MetaWindowActorPrivate *priv = self->priv; + gboolean appears_focused = meta_window_appears_focused (priv->window); + MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow; - if (priv->paint_shadow) + if (shadow != NULL) { - gboolean appears_focused = meta_window_appears_focused (priv->window); - MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow; + MetaShadowParams params; + cairo_rectangle_int_t shape_bounds; - if (shadow != NULL) - { - MetaShadowParams params; - cairo_rectangle_int_t shape_bounds; + meta_window_actor_get_shape_bounds (self, &shape_bounds); + meta_window_actor_get_shadow_params (self, appears_focused, ¶ms); - meta_window_actor_get_shape_bounds (self, &shape_bounds); - meta_window_actor_get_shadow_params (self, appears_focused, ¶ms); - - meta_shadow_paint (shadow, - params.x_offset + shape_bounds.x, - params.y_offset + shape_bounds.y, - shape_bounds.width, - shape_bounds.height, - (clutter_actor_get_paint_opacity (actor) * params.opacity) / 255); - } + meta_shadow_paint (shadow, + params.x_offset + shape_bounds.x, + params.y_offset + shape_bounds.y, + shape_bounds.width, + shape_bounds.height, + (clutter_actor_get_paint_opacity (actor) * params.opacity) / 255, + priv->shadow_clip); } CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor); @@ -1540,6 +1539,18 @@ meta_window_actor_clear_bounding_region (MetaWindowActor *self) } } +static void +meta_window_actor_clear_shadow_clip (MetaWindowActor *self) +{ + MetaWindowActorPrivate *priv = self->priv; + + if (priv->shadow_clip) + { + cairo_region_destroy (priv->shadow_clip); + priv->shadow_clip = NULL; + } +} + static void meta_window_actor_update_bounding_region (MetaWindowActor *self, int width, @@ -1700,18 +1711,8 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self, if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow) { - cairo_rectangle_int_t shadow_bounds; - cairo_region_overlap_t overlap; - - /* We could compute an full clip region as we do for the window - * texture, but the shadow is relatively cheap to draw, and - * a little more complex to clip, so we just catch the case where - * the shadow is completely obscured and doesn't need to be drawn - * at all. - */ - meta_window_actor_get_shadow_bounds (self, appears_focused, &shadow_bounds); - overlap = cairo_region_contains_rectangle (beneath_region, &shadow_bounds); - priv->paint_shadow = overlap != CAIRO_REGION_OVERLAP_OUT; + meta_window_actor_clear_shadow_clip (self); + priv->shadow_clip = cairo_region_copy (beneath_region); } } @@ -1729,7 +1730,7 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self) meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor), NULL); - priv->paint_shadow = TRUE; + meta_window_actor_clear_shadow_clip (self); } static void