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
This commit is contained in:
Owen W. Taylor 2010-11-11 18:23:25 -05:00
parent 15f9590427
commit ca5f2ac3ec
3 changed files with 71 additions and 46 deletions

View File

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

View File

@ -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
@ -200,15 +203,16 @@ meta_shadow_paint (MetaShadow *shadow,
int window_y,
int window_width,
int window_height,
guint8 opacity)
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,12 +271,31 @@ meta_shadow_paint (MetaShadow *shadow,
}
for (j = 0; j < n_y; j++)
{
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]);
}
}
}
/**
* meta_shadow_get_bounds:

View File

@ -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)
{
@ -665,9 +667,6 @@ meta_window_actor_paint (ClutterActor *actor)
{
MetaWindowActor *self = META_WINDOW_ACTOR (actor);
MetaWindowActorPrivate *priv = self->priv;
if (priv->paint_shadow)
{
gboolean appears_focused = meta_window_appears_focused (priv->window);
MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
@ -684,8 +683,8 @@ meta_window_actor_paint (ClutterActor *actor)
params.y_offset + shape_bounds.y,
shape_bounds.width,
shape_bounds.height,
(clutter_actor_get_paint_opacity (actor) * params.opacity) / 255);
}
(clutter_actor_get_paint_opacity (actor) * params.opacity) / 255,
priv->shadow_clip);
}
CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor);
@ -1550,6 +1549,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,
@ -1710,18 +1721,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);
}
}
@ -1739,7 +1740,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