Compare commits

...

6 Commits

Author SHA1 Message Date
Jasper St. Pierre
ff58cd44cb xxx clip 2014-09-03 13:02:48 -07:00
Jasper St. Pierre
05d4e37a40 cssshadowvalue: Convert the current point into a translate
Pango uses the current point to determine where to paint the text, which
is considerably inconvenient since it doesn't work well with
translations.

To make this simpler for us, turn the current point into a translation,
which lets us merge the three different pieces of context setup into
just one.
2014-09-03 13:02:47 -07:00
Jasper St. Pierre
6fb2699ac4 cssshadowvalue: Add a cache for blurred Pango layouts
Drawing text with Pango is quite expensive, and drawing text and also
blurring it is *really* expensive. To prevent us from drawing a lot of
text and then blurring it a lot is *really* expensive.

We now cache the blurred pixels for the last layout and shadow we made,
which means we can repeatedly draw labels with a blurred text-shadow
extremely fast.

To detect whether the shadow is up-to-date, we track the serial of the
PangoLayout alongside the radius of the box shadow. We don't support
inset shadows nor spread on text-shadow, so we don't need to track
these.
2014-09-03 13:02:47 -07:00
Jasper St. Pierre
1f60b915d7 cssshadowvalue: Move the check for blurring into its own function
This makes it easier to update the check later.
2014-09-03 13:02:47 -07:00
Jasper St. Pierre
6a505da304 cssshadowvalue: Rename shadow_key to original_cr_key
shadow_key is a poor name for this, and we're going to add more user
data keys, so rename it to something more indicative of what it's used
for.
2014-09-03 13:02:47 -07:00
Jasper St. Pierre
bdd100ed01 cssshadowvalue: Remove the move_to in paint_layout
_gtk_css_shadow_value_paint_layout is always called from
gtk_theming_engine_render_layout, which always moves the current point
on the context.
2014-09-03 13:02:47 -07:00

View File

@@ -311,7 +311,17 @@ fail:
return NULL;
}
static const cairo_user_data_key_t shadow_key;
static gboolean
needs_blur (const GtkCssValue *shadow)
{
double radius = _gtk_css_number_value_get (shadow->radius, 0);
if (radius == 0.0)
return FALSE;
return TRUE;
}
static const cairo_user_data_key_t original_cr_key;
static cairo_t *
gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow,
@@ -322,12 +332,12 @@ gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow,
cairo_t *blur_cr;
gdouble radius, clip_radius;
radius = _gtk_css_number_value_get (shadow->radius, 0);
if (radius == 0.0)
if (!needs_blur (shadow))
return cr;
gdk_cairo_get_clip_rectangle (cr, &clip_rect);
radius = _gtk_css_number_value_get (shadow->radius, 0);
clip_radius = _gtk_cairo_blur_compute_pixels (radius);
/* Create a larger surface to center the blur. */
@@ -337,15 +347,8 @@ gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow,
clip_rect.height + 2 * clip_radius);
cairo_surface_set_device_offset (surface, clip_radius - clip_rect.x, clip_radius - clip_rect.y);
blur_cr = cairo_create (surface);
cairo_set_user_data (blur_cr, &shadow_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy);
if (cairo_has_current_point (cr))
{
double x, y;
cairo_get_current_point (cr, &x, &y);
cairo_move_to (blur_cr, x, y);
}
cairo_set_user_data (blur_cr, &original_cr_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy);
cairo_new_sub_path (blur_cr);
return blur_cr;
}
@@ -358,14 +361,14 @@ gtk_css_shadow_value_finish_drawing (const GtkCssValue *shadow,
cairo_t *original_cr;
cairo_surface_t *surface;
radius = _gtk_css_number_value_get (shadow->radius, 0);
if (radius == 0.0)
if (!needs_blur (shadow))
return cr;
surface = cairo_get_target (cr);
original_cr = cairo_get_user_data (cr, &shadow_key);
original_cr = cairo_get_user_data (cr, &original_cr_key);
/* Blur the surface. */
surface = cairo_get_target (cr);
radius = _gtk_css_number_value_get (shadow->radius, 0);
_gtk_cairo_blur_surface (surface, radius);
gdk_cairo_set_source_rgba (original_cr, _gtk_css_rgba_value_get_rgba (shadow->color));
@@ -377,33 +380,96 @@ gtk_css_shadow_value_finish_drawing (const GtkCssValue *shadow,
return original_cr;
}
static const cairo_user_data_key_t radius_key;
static const cairo_user_data_key_t layout_serial_key;
G_DEFINE_QUARK (GtkCssShadowValue pango_cached_blurred_surface, pango_cached_blurred_surface)
static cairo_surface_t *
get_cached_surface (PangoLayout *layout,
const GtkCssValue *shadow)
{
cairo_surface_t *cached_surface = g_object_get_qdata (G_OBJECT (layout), pango_cached_blurred_surface_quark ());
guint cached_radius, cached_serial;
guint radius, serial;
if (!cached_surface)
return NULL;
radius = _gtk_css_number_value_get (shadow->radius, 0);
cached_radius = GPOINTER_TO_UINT (cairo_surface_get_user_data (cached_surface, &radius_key));
if (radius != cached_radius)
return NULL;
serial = pango_layout_get_serial (layout);
cached_serial = GPOINTER_TO_UINT (cairo_surface_get_user_data (cached_surface, &layout_serial_key));
if (serial != cached_serial)
return NULL;
return cached_surface;
}
void
_gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow,
cairo_t *cr,
PangoLayout *layout)
{
g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
double x, y;
PangoRectangle ink_rect;
if (!cairo_has_current_point (cr))
cairo_move_to (cr, 0, 0);
g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
cairo_save (cr);
cairo_rel_move_to (cr,
_gtk_css_number_value_get (shadow->hoffset, 0),
_gtk_css_number_value_get (shadow->voffset, 0));
cairo_get_current_point (cr, &x, &y);
cairo_translate (cr, x, y);
cairo_new_sub_path (cr);
pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
cairo_rectangle (cr, ink_rect.x, ink_rect.y, ink_rect.width, ink_rect.height);
cairo_clip (cr);
cairo_translate (cr,
_gtk_css_number_value_get (shadow->hoffset, 0),
_gtk_css_number_value_get (shadow->voffset, 0));
gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
cr = gtk_css_shadow_value_start_drawing (shadow, cr);
_gtk_pango_fill_layout (cr, layout);
if (needs_blur (shadow))
{
cairo_surface_t *cached_surface = get_cached_surface (layout, shadow);
if (cached_surface)
{
cairo_mask_surface (cr, cached_surface, 0, 0);
}
else
{
guint radius, serial;
cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
cr = gtk_css_shadow_value_start_drawing (shadow, cr);
_gtk_pango_fill_layout (cr, layout);
cached_surface = cairo_get_target (cr);
g_object_set_qdata_full (G_OBJECT (layout), pango_cached_blurred_surface_quark (),
cairo_surface_reference (cached_surface),
(GDestroyNotify) cairo_surface_destroy);
cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
radius = _gtk_css_number_value_get (shadow->radius, 0);
cairo_surface_set_user_data (cached_surface, &radius_key, GUINT_TO_POINTER (radius), NULL);
serial = pango_layout_get_serial (layout);
cairo_surface_set_user_data (cached_surface, &layout_serial_key, GUINT_TO_POINTER (serial), NULL);
}
}
else
{
_gtk_pango_fill_layout (cr, layout);
}
cairo_rel_move_to (cr,
- _gtk_css_number_value_get (shadow->hoffset, 0),
- _gtk_css_number_value_get (shadow->voffset, 0));
cairo_restore (cr);
/* Put the current point back where it was. */
cairo_new_sub_path (cr);
cairo_move_to (cr, x, y);
}
void
@@ -573,7 +639,7 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue *shadow,
clip_box = *padding_box;
_gtk_rounded_box_shrink (&clip_box, -clip_radius, -clip_radius, -clip_radius, -clip_radius);
if (radius == 0)
if (!needs_blur (shadow))
draw_shadow (shadow, cr, &box, &clip_box, FALSE);
else
{