diff options
author | Jasper St. Pierre <jstpierre@mecheye.net> | 2014-08-29 13:51:30 -0700 |
---|---|---|
committer | Jasper St. Pierre <jstpierre@mecheye.net> | 2014-10-03 15:59:15 -0600 |
commit | fabefc35d198a7c6851e6d447478c5238058825e (patch) | |
tree | f545cae551b3940a6fb7e9cfe6956b83a1e09d6a | |
parent | 2d73e0ca97ab869808d2f2d6579bc2d1a64ae318 (diff) | |
download | gtk+-fabefc35d198a7c6851e6d447478c5238058825e.tar.gz |
cssshadowvalue: Add a cache for blurred Pango layoutswip/pango-shadow-cache-2
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.
-rw-r--r-- | gtk/gtkcssshadowvalue.c | 122 |
1 files changed, 110 insertions, 12 deletions
diff --git a/gtk/gtkcssshadowvalue.c b/gtk/gtkcssshadowvalue.c index a2d0ee5857..61378de7f0 100644 --- a/gtk/gtkcssshadowvalue.c +++ b/gtk/gtkcssshadowvalue.c @@ -386,6 +386,92 @@ 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_pango_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; +} + +static cairo_surface_t * +make_blurred_pango_surface (cairo_t *existing_cr, + PangoLayout *layout, + const GtkCssValue *shadow) +{ + cairo_surface_t *surface; + cairo_t *cr; + gdouble radius, clip_radius; + PangoRectangle ink_rect; + + radius = _gtk_css_number_value_get (shadow->radius, 0); + + pango_layout_get_pixel_extents (layout, &ink_rect, NULL); + clip_radius = _gtk_cairo_blur_compute_pixels (radius); + + surface = cairo_surface_create_similar_image (cairo_get_target (existing_cr), + CAIRO_FORMAT_A8, + ink_rect.width + 2 * clip_radius, + ink_rect.height + 2 * clip_radius); + cairo_surface_set_device_offset (surface, -ink_rect.x + clip_radius, -ink_rect.y + clip_radius); + cr = cairo_create (surface); + cairo_move_to (cr, 0, 0); + _gtk_pango_fill_layout (cr, layout); + _gtk_cairo_blur_surface (surface, radius); + + cairo_destroy (cr); + + return surface; +} + +static cairo_surface_t * +get_blurred_pango_surface (cairo_t *cr, + PangoLayout *layout, + const GtkCssValue *shadow) +{ + cairo_surface_t *surface; + guint radius, serial; + + surface = get_cached_pango_surface (layout, shadow); + if (!surface) + { + surface = make_blurred_pango_surface (cr, layout, shadow); + + /* Cache the surface on the PangoLayout */ + radius = _gtk_css_number_value_get (shadow->radius, 0); + cairo_surface_set_user_data (surface, &radius_key, GUINT_TO_POINTER (radius), NULL); + + serial = pango_layout_get_serial (layout); + cairo_surface_set_user_data (surface, &layout_serial_key, GUINT_TO_POINTER (serial), NULL); + + g_object_set_qdata_full (G_OBJECT (layout), pango_cached_blurred_surface_quark (), + surface, (GDestroyNotify) cairo_surface_destroy); + } + + return surface; +} + void _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow, cairo_t *cr, @@ -397,20 +483,32 @@ _gtk_css_shadow_value_paint_layout (const GtkCssValue *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)); - - 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 *blurred_surface = get_blurred_pango_surface (cr, layout, shadow); + double x, y; + cairo_get_current_point (cr, &x, &y); + cairo_translate (cr, x, y); + cairo_translate (cr, + _gtk_css_number_value_get (shadow->hoffset, 0), + _gtk_css_number_value_get (shadow->voffset, 0)); - cr = gtk_css_shadow_value_finish_drawing (shadow, cr); + gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color)); + cairo_mask_surface (cr, blurred_surface, 0, 0); + } + else + { + /* The no blur case -- just paint directly. */ + cairo_rel_move_to (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)); + _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_rel_move_to (cr, - - _gtk_css_number_value_get (shadow->hoffset, 0), - - _gtk_css_number_value_get (shadow->voffset, 0)); cairo_restore (cr); } |