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_y,
int window_width, int window_width,
int window_height, int window_height,
guint8 opacity); guint8 opacity,
cairo_region_t *clip);
void meta_shadow_get_bounds (MetaShadow *shadow, void meta_shadow_get_bounds (MetaShadow *shadow,
int window_x, int window_x,
int window_y, 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_y: y position of the region to paint a shadow for
* @window_width: actual width 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 * @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 * Paints the shadow at the given position, for the specified actual
* size of the region. (Since a #MetaShadow can be shared between * 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.) * size needs to be passed in here.)
*/ */
void void
meta_shadow_paint (MetaShadow *shadow, meta_shadow_paint (MetaShadow *shadow,
int window_x, int window_x,
int window_y, int window_y,
int window_width, int window_width,
int window_height, int window_height,
guint8 opacity) guint8 opacity,
cairo_region_t *clip)
{ {
float texture_width = cogl_texture_get_width (shadow->texture); float texture_width = cogl_texture_get_width (shadow->texture);
float texture_height = cogl_texture_get_height (shadow->texture); float texture_height = cogl_texture_get_height (shadow->texture);
int i, j; int i, j;
float src_x[4]; float src_x[4];
float src_y[4]; float src_y[4];
float dest_x[4]; int dest_x[4];
float dest_y[4]; int dest_y[4];
int n_x, n_y; int n_x, n_y;
cogl_material_set_color4ub (shadow->material, cogl_material_set_color4ub (shadow->material,
@ -267,11 +271,30 @@ meta_shadow_paint (MetaShadow *shadow,
} }
for (j = 0; j < n_y; j++) 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], cairo_rectangle_int_t dest_rect;
dest_x[i + 1], dest_y[j + 1], dest_rect.y = dest_y[j];
src_x[i], src_y[j], dest_rect.height = dest_y[j + 1] - dest_y[j];
src_x[i + 1], src_y[j + 1]);
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]);
}
}
} }
/** /**

View File

@ -60,6 +60,8 @@ struct _MetaWindowActorPrivate
/* A rectangular region with the unshaped extends of the window /* A rectangular region with the unshaped extends of the window
* texture */ * texture */
cairo_region_t *bounding_region; 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 */ /* Extracted size-invariant shape used for shadows */
MetaWindowShape *shadow_shape; MetaWindowShape *shadow_shape;
@ -93,7 +95,6 @@ struct _MetaWindowActorPrivate
guint needs_reshape : 1; guint needs_reshape : 1;
guint recompute_focused_shadow : 1; guint recompute_focused_shadow : 1;
guint recompute_unfocused_shadow : 1; guint recompute_unfocused_shadow : 1;
guint paint_shadow : 1;
guint size_changed : 1; guint size_changed : 1;
guint needs_destroy : 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_shape_region (MetaWindowActor *self);
static void meta_window_actor_clear_bounding_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, static gboolean is_shaped (MetaDisplay *display,
Window xwindow); Window xwindow);
@ -283,7 +285,6 @@ meta_window_actor_init (MetaWindowActor *self)
MetaWindowActorPrivate); MetaWindowActorPrivate);
priv->opacity = 0xff; priv->opacity = 0xff;
priv->shadow_class = NULL; priv->shadow_class = NULL;
priv->paint_shadow = TRUE;
} }
static void static void
@ -439,6 +440,7 @@ meta_window_actor_dispose (GObject *object)
meta_window_actor_clear_shape_region (self); meta_window_actor_clear_shape_region (self);
meta_window_actor_clear_bounding_region (self); meta_window_actor_clear_bounding_region (self);
meta_window_actor_clear_shadow_clip (self);
if (priv->shadow_class != NULL) if (priv->shadow_class != NULL)
{ {
@ -665,27 +667,24 @@ meta_window_actor_paint (ClutterActor *actor)
{ {
MetaWindowActor *self = META_WINDOW_ACTOR (actor); MetaWindowActor *self = META_WINDOW_ACTOR (actor);
MetaWindowActorPrivate *priv = self->priv; 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); MetaShadowParams params;
MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow; cairo_rectangle_int_t shape_bounds;
if (shadow != NULL) meta_window_actor_get_shape_bounds (self, &shape_bounds);
{ meta_window_actor_get_shadow_params (self, appears_focused, &params);
MetaShadowParams params;
cairo_rectangle_int_t shape_bounds;
meta_window_actor_get_shape_bounds (self, &shape_bounds); meta_shadow_paint (shadow,
meta_window_actor_get_shadow_params (self, appears_focused, &params); params.x_offset + shape_bounds.x,
params.y_offset + shape_bounds.y,
meta_shadow_paint (shadow, shape_bounds.width,
params.x_offset + shape_bounds.x, shape_bounds.height,
params.y_offset + shape_bounds.y, (clutter_actor_get_paint_opacity (actor) * params.opacity) / 255,
shape_bounds.width, priv->shadow_clip);
shape_bounds.height,
(clutter_actor_get_paint_opacity (actor) * params.opacity) / 255);
}
} }
CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor); 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 static void
meta_window_actor_update_bounding_region (MetaWindowActor *self, meta_window_actor_update_bounding_region (MetaWindowActor *self,
int width, int width,
@ -1710,18 +1721,8 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow) if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow)
{ {
cairo_rectangle_int_t shadow_bounds; meta_window_actor_clear_shadow_clip (self);
cairo_region_overlap_t overlap; priv->shadow_clip = cairo_region_copy (beneath_region);
/* 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;
} }
} }
@ -1739,7 +1740,7 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self)
meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor), meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor),
NULL); NULL);
priv->paint_shadow = TRUE; meta_window_actor_clear_shadow_clip (self);
} }
static void static void