From afb8f614f4146ced84cef941fe4a707fcc5ab547 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 24 Jun 2013 14:13:43 +0200 Subject: [PATCH] GtkIconHelper: render to cairo pattern rather than pixbuf for drawing We render the source into a cairo_patter_t into a pixbuf so that we can render it directly, rather than having to convert it every time we render. We also specify the target window when creating the cairo surface so that rendering can be faster. --- gtk/gtkiconhelper.c | 192 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 186 insertions(+), 6 deletions(-) diff --git a/gtk/gtkiconhelper.c b/gtk/gtkiconhelper.c index 629f007827..7c54549d2f 100644 --- a/gtk/gtkiconhelper.c +++ b/gtk/gtkiconhelper.c @@ -43,6 +43,9 @@ struct _GtkIconHelperPrivate { GdkPixbuf *rendered_pixbuf; GtkStateFlags last_rendered_state; + + cairo_pattern_t *rendered_pattern; + GtkStateFlags last_pattern_state; }; void @@ -379,6 +382,184 @@ _gtk_icon_helper_ensure_pixbuf (GtkIconHelper *self, return pixbuf; } +static gboolean +check_invalidate_pattern (GtkIconHelper *self, + GtkStyleContext *context) +{ + GtkStateFlags state; + + state = gtk_style_context_get_state (context); + + if ((self->priv->rendered_pattern != NULL) && + (self->priv->last_pattern_state == state)) + return FALSE; + + self->priv->last_pattern_state = state; + + if (self->priv->rendered_pattern) + cairo_pattern_destroy (self->priv->rendered_pattern); + self->priv->rendered_pattern = NULL; + + return TRUE; +} + +static void +ensure_pattern_at_size (GtkIconHelper *self, + GtkStyleContext *context) +{ + gint width, height; + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + + if (!check_invalidate_pattern (self, context)) + return; + + if (self->priv->rendered_pattern) + return; + + if (self->priv->force_scale_pixbuf && + (self->priv->pixel_size != -1 || + self->priv->icon_size != GTK_ICON_SIZE_INVALID)) + { + ensure_icon_size (self, context, &width, &height); + + if (width < gdk_pixbuf_get_width (self->priv->orig_pixbuf) || + height < gdk_pixbuf_get_height (self->priv->orig_pixbuf)) + { + pixbuf = gdk_pixbuf_scale_simple (self->priv->orig_pixbuf, + width, height, + GDK_INTERP_BILINEAR); + } + else + pixbuf = g_object_ref (self->priv->orig_pixbuf); + } + else + pixbuf = g_object_ref (self->priv->orig_pixbuf); + + surface = gdk_cairo_pixbuf_to_surface (pixbuf, self->priv->window); + g_object_unref (pixbuf); + self->priv->rendered_pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + +} + +static void +ensure_pattern_for_icon_set (GtkIconHelper *self, + GtkStyleContext *context, + GtkIconSet *icon_set) +{ + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + + if (!check_invalidate_pattern (self, context)) + return; + + pixbuf = + gtk_icon_set_render_icon_pixbuf (icon_set, context, self->priv->icon_size); + + surface = gdk_cairo_pixbuf_to_surface (pixbuf, self->priv->window); + g_object_unref (pixbuf); + self->priv->rendered_pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); +} + +static void +ensure_pattern_for_icon_name_or_gicon (GtkIconHelper *self, + GtkStyleContext *context) +{ + GtkIconTheme *icon_theme; + gint width, height; + GtkIconInfo *info; + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + GtkIconLookupFlags flags; + + if (!check_invalidate_pattern (self, context)) + return; + + icon_theme = gtk_icon_theme_get_default (); + flags = get_icon_lookup_flags (self); + + ensure_icon_size (self, context, &width, &height); + + if (self->priv->storage_type == GTK_IMAGE_ICON_NAME && + self->priv->icon_name != NULL) + { + info = gtk_icon_theme_lookup_icon (icon_theme, + self->priv->icon_name, + MIN (width, height), flags); + } + else if (self->priv->storage_type == GTK_IMAGE_GICON && + self->priv->gicon != NULL) + { + info = gtk_icon_theme_lookup_by_gicon (icon_theme, + self->priv->gicon, + MIN (width, height), flags); + } + else + { + g_assert_not_reached (); + return; + } + + pixbuf = ensure_stated_icon_from_info (self, context, info); + if (pixbuf) + { + surface = gdk_cairo_pixbuf_to_surface (pixbuf, self->priv->window); + g_object_unref (pixbuf); + self->priv->rendered_pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + } + + if (info) + g_object_unref (info); +} + +cairo_pattern_t * +_gtk_icon_helper_ensure_pattern (GtkIconHelper *self, + GtkStyleContext *context) +{ + cairo_pattern_t *pattern = NULL; + GtkIconSet *icon_set; + + switch (self->priv->storage_type) + { + case GTK_IMAGE_PIXBUF: + ensure_pattern_at_size (self, context); + break; + + case GTK_IMAGE_STOCK: + icon_set = gtk_style_context_lookup_icon_set (context, self->priv->stock_id); + if (icon_set != NULL) + ensure_pattern_for_icon_set (self, context, icon_set); + else + pattern = NULL; + break; + + case GTK_IMAGE_ICON_SET: + icon_set = self->priv->icon_set; + ensure_pattern_for_icon_set (self, context, icon_set); + break; + + case GTK_IMAGE_ICON_NAME: + case GTK_IMAGE_GICON: + ensure_pattern_for_icon_name_or_gicon (self, context); + break; + + case GTK_IMAGE_ANIMATION: + case GTK_IMAGE_EMPTY: + default: + pattern = NULL; + break; + } + + if (pattern == NULL && + self->priv->rendered_pattern != NULL) + pattern = cairo_pattern_reference (self->priv->rendered_pattern); + + return pattern; +} + void _gtk_icon_helper_get_size (GtkIconHelper *self, GtkStyleContext *context, @@ -610,14 +791,13 @@ _gtk_icon_helper_draw (GtkIconHelper *self, gdouble x, gdouble y) { - GdkPixbuf *pixbuf; + cairo_pattern_t *pattern; - pixbuf = _gtk_icon_helper_ensure_pixbuf (self, context); - - if (pixbuf != NULL) + pattern = _gtk_icon_helper_ensure_pattern (self, context); + if (pattern != NULL) { - gtk_render_icon (context, cr, pixbuf, x, y); - g_object_unref (pixbuf); + gtk_render_icon_pattern (context, cr, pattern, x, y); + cairo_pattern_destroy (pattern); } }