css shadows: Split up rendering of shadows

We split up the rendering of blurred shadows into 9 parts, the
corners, the sides and the rest. This lets us only blur the "blurry"
part, and it lets us completely skip blurry parts that are fully
clipped.
This commit is contained in:
Alexander Larsson 2013-05-06 13:06:13 +02:00
parent 5ba5306dc9
commit 66d3b5a9cd

View File

@ -488,14 +488,18 @@ static void
draw_shadow (const GtkCssValue *shadow, draw_shadow (const GtkCssValue *shadow,
cairo_t *cr, cairo_t *cr,
GtkRoundedBox *box, GtkRoundedBox *box,
GtkRoundedBox *clip_box) GtkRoundedBox *clip_box,
gboolean blur)
{ {
cairo_t *shadow_cr; cairo_t *shadow_cr;
if (has_empty_clip (cr)) if (has_empty_clip (cr))
return; return;
shadow_cr = gtk_css_shadow_value_start_drawing (shadow, cr); if (blur)
shadow_cr = gtk_css_shadow_value_start_drawing (shadow, cr);
else
shadow_cr = cr;
cairo_set_fill_rule (shadow_cr, CAIRO_FILL_RULE_EVEN_ODD); cairo_set_fill_rule (shadow_cr, CAIRO_FILL_RULE_EVEN_ODD);
_gtk_rounded_box_path (box, shadow_cr); _gtk_rounded_box_path (box, shadow_cr);
@ -505,7 +509,8 @@ draw_shadow (const GtkCssValue *shadow,
gdk_cairo_set_source_rgba (shadow_cr, _gtk_css_rgba_value_get_rgba (shadow->color)); gdk_cairo_set_source_rgba (shadow_cr, _gtk_css_rgba_value_get_rgba (shadow->color));
cairo_fill (shadow_cr); cairo_fill (shadow_cr);
gtk_css_shadow_value_finish_drawing (shadow, shadow_cr); if (blur)
gtk_css_shadow_value_finish_drawing (shadow, shadow_cr);
} }
void void
@ -539,6 +544,7 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue *shadow,
clip_box = *padding_box; clip_box = *padding_box;
_gtk_rounded_box_grow (&clip_box, outside, outside, outside, outside); _gtk_rounded_box_grow (&clip_box, outside, outside, outside, outside);
_gtk_rounded_box_clip_path (&clip_box, cr); _gtk_rounded_box_clip_path (&clip_box, cr);
cairo_clip (cr); cairo_clip (cr);
} }
@ -559,7 +565,148 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue *shadow,
clip_box = *padding_box; clip_box = *padding_box;
_gtk_rounded_box_shrink (&clip_box, -radius, -radius, -radius, -radius); _gtk_rounded_box_shrink (&clip_box, -radius, -radius, -radius, -radius);
draw_shadow (shadow, cr, &box, &clip_box); if (radius == 0)
draw_shadow (shadow, cr, &box, &clip_box, FALSE);
else
{
int i, x1, x2, y1, y2;
cairo_region_t *remaining;
cairo_rectangle_int_t r;
/* For the blurred case we divide the rendering into 9 parts,
* 4 of the corners, 4 for the horizonat/vertical lines and
* one for the interior. We make the non-interior parts
* large enought to fit the full radius of the blur, so that
* the interior part can be drawn solidly.
*/
if (shadow->inset)
{
/* In the inset case we want to paint the whole clip-box.
* We could remove the part of "box" where the blur doesn't
* reach, but computing that is a bit tricky since the
* rounded corners are on the "inside" of it. */
r.x = floor (clip_box.box.x);
r.y = floor (clip_box.box.y);
r.width = ceil (clip_box.box.x + clip_box.box.width) - r.x;
r.height = ceil (clip_box.box.y + clip_box.box.height) - r.y;
remaining = cairo_region_create_rectangle (&r);
}
else
{
/* In the outset case we want to paint the entire box, plus as far
* as the radius reaches from it */
r.x = floor (box.box.x - radius);
r.y = floor (box.box.y - radius);
r.width = ceil (box.box.x + box.box.width + radius) - r.x;
r.height = ceil (box.box.y + box.box.height + radius) - r.y;
remaining = cairo_region_create_rectangle (&r);
}
/* First do the corners of box */
for (i = 0; i < 4; i++)
{
if (i == GTK_CSS_TOP_LEFT || i == GTK_CSS_BOTTOM_LEFT)
{
x1 = floor (box.box.x - radius);
x2 = ceil (box.box.x + box.corner[i].horizontal + radius);
}
else
{
x1 = floor (box.box.x + box.box.width - box.corner[i].horizontal - radius);
x2 = ceil (box.box.x + box.box.width + radius);
}
if (i == GTK_CSS_TOP_LEFT || i == GTK_CSS_TOP_RIGHT)
{
y1 = floor (box.box.y - radius);
y2 = ceil (box.box.y + box.corner[i].vertical + radius);
}
else
{
y1 = floor (box.box.y + box.box.height - box.corner[i].vertical - radius);
y2 = ceil (box.box.y + box.box.height + radius);
}
cairo_save (cr);
cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
cairo_clip (cr);
/* Also clip with remaining to ensure we never draw any area twice */
gdk_cairo_region (cr, remaining);
cairo_clip (cr);
draw_shadow (shadow, cr, &box, &clip_box, TRUE);
cairo_restore (cr);
/* We drew the region, remove it from remaining */
r.x = x1;
r.y = y1;
r.width = x2 - x1;
r.height = y2 - y1;
cairo_region_subtract_rectangle (remaining, &r);
}
/* Then the sides */
for (i = 0; i < 4; i++)
{
if (i == GTK_CSS_TOP || i == GTK_CSS_BOTTOM)
{
x1 = floor (box.box.x - radius);
x2 = ceil (box.box.x + box.box.width + radius);
}
else if (i == GTK_CSS_LEFT)
{
x1 = floor (box.box.x -radius);
x2 = ceil (box.box.x + radius);
}
else
{
x1 = floor (box.box.x + box.box.width -radius);
x2 = ceil (box.box.x + box.box.width + radius);
}
if (i == GTK_CSS_LEFT || i == GTK_CSS_RIGHT)
{
y1 = floor (box.box.y - radius);
y2 = ceil (box.box.y + box.box.height + radius);
}
else if (i == GTK_CSS_TOP)
{
y1 = floor (box.box.y -radius);
y2 = ceil (box.box.y + radius);
}
else
{
y1 = floor (box.box.y + box.box.height -radius);
y2 = ceil (box.box.y + box.box.height + radius);
}
cairo_save (cr);
cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
cairo_clip (cr);
/* Also clip with remaining to ensure we never draw any area twice */
gdk_cairo_region (cr, remaining);
cairo_clip (cr);
draw_shadow (shadow, cr, &box, &clip_box, TRUE);
cairo_restore (cr);
/* We drew the region, remove it from remaining */
r.x = x1;
r.y = y1;
r.width = x2 - x1;
r.height = y2 - y1;
cairo_region_subtract_rectangle (remaining, &r);
}
/* Then the rest, which needs no blurring */
cairo_save (cr);
gdk_cairo_region (cr, remaining);
cairo_clip (cr);
draw_shadow (shadow, cr, &box, &clip_box, FALSE);
cairo_restore (cr);
}
cairo_restore (cr); cairo_restore (cr);
} }