GtkIconHelper: Short circuit _gtk_icon_helper_get_size in some cases

_gtk_icon_helper_get_size() is often used during size request and may
not necessary mean that the icon will be displayed immediately. In
many common cases we know the size without having to ensure a surface.
In many cases this means we can avoid loading an icon until needed, and
in the case of stateless IconHelpers such as GtkCellRendererPixbuf this
is very important as otherwise it will constantly be reloading icons
if the displayed set is larger than the in-memory icon cache.

https://bugzilla.gnome.org/show_bug.cgi?id=734651
This commit is contained in:
Alexander Larsson
2014-08-12 12:34:12 +02:00
parent 8ff47570af
commit 928822f1a5

View File

@ -542,21 +542,19 @@ ensure_surface_from_surface (GtkIconHelper *self,
&self->priv->rendered_surface_height);
}
static void
ensure_surface_from_pixbuf (GtkIconHelper *self,
GtkStyleContext *context)
static gboolean
get_pixbuf_size (GtkIconHelper *self,
GtkStyleContext *context,
gint *width_out,
gint *height_out,
gint *scale_out)
{
gboolean scale_pixmap;
gint width, height;
GdkPixbuf *pixbuf, *stated;
int scale;
if (!check_invalidate_surface (self, context))
return;
if (self->priv->rendered_surface)
return;
scale = get_scale_factor (self, context);
scale_pixmap = FALSE;
if (self->priv->force_scale_pixbuf &&
(self->priv->pixel_size != -1 ||
@ -571,28 +569,56 @@ ensure_surface_from_pixbuf (GtkIconHelper *self,
width = MIN (width * scale, gdk_pixbuf_get_width (self->priv->orig_pixbuf) * scale / self->priv->orig_pixbuf_scale);
height = MIN (height * scale, gdk_pixbuf_get_height (self->priv->orig_pixbuf) * scale / self->priv->orig_pixbuf_scale);
pixbuf = gdk_pixbuf_scale_simple (self->priv->orig_pixbuf,
width, height,
GDK_INTERP_BILINEAR);
scale_pixmap = TRUE;
}
else
{
pixbuf = g_object_ref (self->priv->orig_pixbuf);
width = gdk_pixbuf_get_width (self->priv->orig_pixbuf);
height = gdk_pixbuf_get_height (self->priv->orig_pixbuf);
scale = self->priv->orig_pixbuf_scale;
}
}
else
{
pixbuf = g_object_ref (self->priv->orig_pixbuf);
width = gdk_pixbuf_get_width (self->priv->orig_pixbuf);
height = gdk_pixbuf_get_height (self->priv->orig_pixbuf);
scale = self->priv->orig_pixbuf_scale;
}
*width_out = width;
*height_out = height;
*scale_out = scale;
return scale_pixmap;
}
static void
ensure_surface_from_pixbuf (GtkIconHelper *self,
GtkStyleContext *context)
{
gint width, height;
GdkPixbuf *pixbuf, *stated;
int scale;
if (!check_invalidate_surface (self, context))
return;
if (self->priv->rendered_surface)
return;
if (get_pixbuf_size (self, context, &width, &height, &scale))
pixbuf = gdk_pixbuf_scale_simple (self->priv->orig_pixbuf,
width, height,
GDK_INTERP_BILINEAR);
else
pixbuf = g_object_ref (self->priv->orig_pixbuf);
stated = ensure_stated_pixbuf_from_pixbuf (self, context, pixbuf);
g_object_unref (pixbuf);
pixbuf = stated;
self->priv->rendered_surface_width = (gdk_pixbuf_get_width (pixbuf) + scale - 1) / scale;
self->priv->rendered_surface_height = (gdk_pixbuf_get_height (pixbuf) + scale - 1) / scale;
self->priv->rendered_surface_width = (width + scale - 1) / scale;
self->priv->rendered_surface_height = (height + scale - 1) / scale;
self->priv->rendered_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, self->priv->window);
g_object_unref (pixbuf);
@ -774,25 +800,62 @@ _gtk_icon_helper_get_size (GtkIconHelper *self,
gint *height_out)
{
cairo_surface_t *surface;
gint width, height;
gint width, height, scale;
width = height = 0;
surface = _gtk_icon_helper_ensure_surface (self, context);
if (surface != NULL)
/* Certain kinds of images are easy to calculate the size for, these
we do immediately to avoid having to potentially load the image
data for something that may not yet be visible */
switch (self->priv->storage_type)
{
width = self->priv->rendered_surface_width;
height = self->priv->rendered_surface_height;
cairo_surface_destroy (surface);
case GTK_IMAGE_SURFACE:
get_surface_size (self, context, self->priv->orig_surface,
&width,
&height);
break;
case GTK_IMAGE_PIXBUF:
get_pixbuf_size (self, context, &width, &height, &scale);
width = (width + scale - 1) / scale;
height = (height + scale - 1) / scale;
break;
case GTK_IMAGE_ICON_NAME:
case GTK_IMAGE_GICON:
if (self->priv->pixel_size != -1 || self->priv->force_scale_pixbuf)
ensure_icon_size (self, context, &width, &height);
break;
case GTK_IMAGE_STOCK:
case GTK_IMAGE_ICON_SET:
case GTK_IMAGE_ANIMATION:
case GTK_IMAGE_EMPTY:
default:
break;
}
else if (self->priv->storage_type == GTK_IMAGE_ANIMATION)
/* Otherwise we load the surface to guarantee we get a size */
if (width == 0)
{
width = gdk_pixbuf_animation_get_width (self->priv->animation);
height = gdk_pixbuf_animation_get_height (self->priv->animation);
}
else if (self->priv->icon_size != -1)
{
ensure_icon_size (self, context, &width, &height);
surface = _gtk_icon_helper_ensure_surface (self, context);
if (surface != NULL)
{
width = self->priv->rendered_surface_width;
height = self->priv->rendered_surface_height;
cairo_surface_destroy (surface);
}
else if (self->priv->storage_type == GTK_IMAGE_ANIMATION)
{
width = gdk_pixbuf_animation_get_width (self->priv->animation);
height = gdk_pixbuf_animation_get_height (self->priv->animation);
}
else if (self->priv->icon_size != -1)
{
ensure_icon_size (self, context, &width, &height);
}
}
if (width_out)