Start looking at the GError parameter every time we call these functions:
authorMark Doliner <markdoliner@pidgin.im>
Wed, 22 Jun 2011 07:07:28 +0000
changeset96183796df0c pushlog
parent 8300e1e9a38c
child 70bb95096b14
Start looking at the GError parameter every time we call these functions:
- gdk_pixbuf_loader_write
- gdk_pixbuf_loader_close
- gdk_pixbuf_new_from_file
- gdk_pixbuf_new_from_file_at_size
- gdk_pixbuf_new_from_file_at_scale

There are times when gdkpixbuf returns a semi-invalid GdkPixbuf object and
also sets the GError. If this happens we want to discard and ignore the
GdkPixbuf object because it can cause problems. For example, calling
gdk_pixbuf_scale_simple() causes gdkpixbuf to rapidly consume memory in
an infinite loop. And that's bad.

This commit adds some helper functions to gtkutils.[c|h] that make it a
little easier to check the GError value. We should use them everywhere
we call any of the above functions.
pidgin/gtkaccount.c
pidgin/gtkblist.c
pidgin/gtkconv.c
pidgin/gtkdialogs.c
pidgin/gtkft.c
pidgin/gtkimhtml.c
pidgin/gtkmain.c
pidgin/gtkprefs.c
pidgin/gtkrequest.c
pidgin/gtksmiley.c
pidgin/gtkstatusbox.c
pidgin/gtkutils.c
pidgin/gtkutils.h
     1.1 --- a/pidgin/gtkaccount.c
     1.2 +++ b/pidgin/gtkaccount.c
     1.3 @@ -2135,7 +2135,7 @@
     1.4  	gtk_list_store_clear(dialog->model);
     1.5  
     1.6  	if ((path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) != NULL) {
     1.7 -		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL);
     1.8 +		GdkPixbuf *pixbuf = pidgin_pixbuf_new_from_file(path);
     1.9  		if (pixbuf != NULL) {
    1.10  			global_buddyicon = gdk_pixbuf_scale_simple(pixbuf, 22, 22, GDK_INTERP_HYPER);
    1.11  			g_object_unref(G_OBJECT(pixbuf));
     2.1 --- a/pidgin/gtkblist.c
     2.2 +++ b/pidgin/gtkblist.c
     2.3 @@ -2643,7 +2643,6 @@
     2.4                                                gboolean scaled, gboolean greyed)
     2.5  {
     2.6  	gsize len;
     2.7 -	GdkPixbufLoader *loader;
     2.8  	PurpleBuddy *buddy = NULL;
     2.9  	PurpleGroup *group = NULL;
    2.10  	const guchar *data = NULL;
    2.11 @@ -2710,21 +2709,20 @@
    2.12  			return NULL;
    2.13  	}
    2.14  
    2.15 -	loader = gdk_pixbuf_loader_new();
    2.16 -	gdk_pixbuf_loader_write(loader, data, len, NULL);
    2.17 -	gdk_pixbuf_loader_close(loader, NULL);
    2.18 -
    2.19 +	buf = pidgin_pixbuf_from_data(data, len);
    2.20 +	purple_buddy_icon_unref(icon);
    2.21 +	if (!buf) {
    2.22 +		purple_debug_warning("gtkblist", "Couldn't load buddy icon "
    2.23 +				"on account %s (%s)  buddyname=%s  "
    2.24 +				"custom_img_data=%p\n",
    2.25 +				account ? purple_account_get_username(account) : "(no account)",
    2.26 +				account ? purple_account_get_protocol_id(account) : "(no account)",
    2.27 +				buddy ? purple_buddy_get_name(buddy) : "(no buddy)",
    2.28 +				custom_img ? purple_imgstore_get_data(custom_img) : NULL);
    2.29 +		purple_imgstore_unref(custom_img);
    2.30 +		return NULL;
    2.31 +	}
    2.32  	purple_imgstore_unref(custom_img);
    2.33 -	purple_buddy_icon_unref(icon);
    2.34 -
    2.35 -	buf = gdk_pixbuf_loader_get_pixbuf(loader);
    2.36 -	if (buf)
    2.37 -		g_object_ref(G_OBJECT(buf));
    2.38 -	g_object_unref(G_OBJECT(loader));
    2.39 -
    2.40 -	if (!buf) {
    2.41 -		return NULL;
    2.42 -	}
    2.43  
    2.44  	if (greyed) {
    2.45  		gboolean offline = FALSE, idle = FALSE;
    2.46 @@ -3952,7 +3950,7 @@
    2.47  		g_object_ref(pb);
    2.48  		g_free(path);
    2.49  	} else {
    2.50 -		pb = gdk_pixbuf_new_from_file(path, NULL);
    2.51 +		pb = pidgin_pixbuf_new_from_file(path);
    2.52  		if (pb != NULL) {
    2.53  			/* We don't want to own a ref to the pixbuf, but we need to keep clean up. */
    2.54  			/* I'm not sure if it would be better to just keep our ref and not let the emblem ever be destroyed */
     3.1 --- a/pidgin/gtkconv.c
     3.2 +++ b/pidgin/gtkconv.c
     3.3 @@ -6392,8 +6392,8 @@
     3.4  {
     3.5  	PidginConversation *gtkconv;
     3.6  	GtkIMHtmlSmiley *smiley;
     3.7 -	GdkPixbufLoader *loader;
     3.8  	const char *sml;
     3.9 +	GError *error = NULL;
    3.10  
    3.11  	sml = purple_account_get_protocol_name(conv->account);
    3.12  	gtkconv = PIDGIN_CONVERSATION(conv);
    3.13 @@ -6406,11 +6406,24 @@
    3.14  	g_memmove((guchar *)smiley->data + smiley->datasize, data, size);
    3.15  	smiley->datasize += size;
    3.16  
    3.17 -	loader = smiley->loader;
    3.18 -	if (!loader)
    3.19 +	if (!smiley->loader)
    3.20  		return;
    3.21  
    3.22 -	gdk_pixbuf_loader_write(loader, data, size, NULL);
    3.23 +	if (!gdk_pixbuf_loader_write(smiley->loader, data, size, &error) || error) {
    3.24 +		purple_debug_warning("gtkconv", "gdk_pixbuf_loader_write() "
    3.25 +				"failed with size=%zu: %s\n", size,
    3.26 +				error ? error->message : "(no error message)");
    3.27 +		if (error)
    3.28 +			g_error_free(error);
    3.29 +		/* We must stop using the GdkPixbufLoader because trying to load
    3.30 +		   certain invalid GIFs with at least gdk-pixbuf 2.23.3 can return
    3.31 +		   a GdkPixbuf that will cause some operations (like
    3.32 +		   gdk_pixbuf_scale_simple()) to consume memory in an infinite loop.
    3.33 +		   But we also don't want to set smiley->loader to NULL because our
    3.34 +		   code might expect it to be set.  So create a new loader. */
    3.35 +		g_object_unref(G_OBJECT(smiley->loader));
    3.36 +		smiley->loader = gdk_pixbuf_loader_new();
    3.37 +	}
    3.38  }
    3.39  
    3.40  static void
    3.41 @@ -6418,8 +6431,8 @@
    3.42  {
    3.43  	PidginConversation *gtkconv;
    3.44  	GtkIMHtmlSmiley *smiley;
    3.45 -	GdkPixbufLoader *loader;
    3.46  	const char *sml;
    3.47 +	GError *error = NULL;
    3.48  
    3.49  	g_return_if_fail(conv  != NULL);
    3.50  	g_return_if_fail(smile != NULL);
    3.51 @@ -6431,17 +6444,27 @@
    3.52  	if (!smiley)
    3.53  		return;
    3.54  
    3.55 -	loader = smiley->loader;
    3.56 -
    3.57 -	if (!loader)
    3.58 +	if (!smiley->loader)
    3.59  		return;
    3.60  
    3.61 -
    3.62 -
    3.63  	purple_debug_info("gtkconv", "About to close the smiley pixbuf\n");
    3.64  
    3.65 -	gdk_pixbuf_loader_close(loader, NULL);
    3.66 -
    3.67 +	if (!gdk_pixbuf_loader_close(smiley->loader, &error) || error) {
    3.68 +		purple_debug_warning("gtkconv", "gdk_pixbuf_loader_close() "
    3.69 +				"failed: %s\n",
    3.70 +				error ? error->message : "(no error message)");
    3.71 +		if (error)
    3.72 +			g_error_free(error);
    3.73 +		/* We must stop using the GdkPixbufLoader because if we tried to
    3.74 +		   load certain invalid GIFs with all current versions of GDK (as
    3.75 +		   of 2011-06-15) then it's possible the loader will contain data
    3.76 +		   that could cause some operations (like gdk_pixbuf_scale_simple())
    3.77 +		   to consume memory in an infinite loop.  But we also don't want
    3.78 +		   to set smiley->loader to NULL because our code might expect it
    3.79 +		   to be set.  So create a new loader. */
    3.80 +		g_object_unref(G_OBJECT(smiley->loader));
    3.81 +		smiley->loader = gdk_pixbuf_loader_new();
    3.82 +	}
    3.83  }
    3.84  
    3.85  static void
    3.86 @@ -6941,10 +6964,6 @@
    3.87  
    3.88  	PurpleBuddy *buddy;
    3.89  
    3.90 -	GdkPixbufLoader *loader;
    3.91 -	GdkPixbufAnimation *anim;
    3.92 -	GError *err = NULL;
    3.93 -
    3.94  	PurpleStoredImage *custom_img = NULL;
    3.95  	gconstpointer data = NULL;
    3.96  	size_t len;
    3.97 @@ -7022,7 +7041,6 @@
    3.98  
    3.99  	if (data == NULL) {
   3.100  		icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
   3.101 -
   3.102  		if (icon == NULL)
   3.103  		{
   3.104  			gtk_widget_set_size_request(gtkconv->u.im->icon_container,
   3.105 @@ -7031,7 +7049,6 @@
   3.106  		}
   3.107  
   3.108  		data = purple_buddy_icon_get_data(icon, &len);
   3.109 -
   3.110  		if (data == NULL)
   3.111  		{
   3.112  			gtk_widget_set_size_request(gtkconv->u.im->icon_container,
   3.113 @@ -7040,25 +7057,13 @@
   3.114  		}
   3.115  	}
   3.116  
   3.117 -	loader = gdk_pixbuf_loader_new();
   3.118 -	gdk_pixbuf_loader_write(loader, data, len, NULL);
   3.119 -	gdk_pixbuf_loader_close(loader, &err);
   3.120 -
   3.121 +	gtkconv->u.im->anim = pidgin_pixbuf_anim_from_data(data, len);
   3.122  	purple_imgstore_unref(custom_img);
   3.123  
   3.124 -	anim = gdk_pixbuf_loader_get_animation(loader);
   3.125 -	if (anim)
   3.126 -		g_object_ref(G_OBJECT(anim));
   3.127 -	g_object_unref(loader);
   3.128 -
   3.129 -	if (!anim)
   3.130 +	if (!gtkconv->u.im->anim) {
   3.131 +		purple_debug_error("gtkconv", "Couldn't load icon for conv %s\n",
   3.132 +				purple_conversation_get_name(conv));
   3.133  		return;
   3.134 -	gtkconv->u.im->anim = anim;
   3.135 -
   3.136 -	if (err) {
   3.137 -		purple_debug(PURPLE_DEBUG_ERROR, "gtkconv",
   3.138 -				   "Buddy icon error: %s\n", err->message);
   3.139 -		g_error_free(err);
   3.140  	}
   3.141  
   3.142  	if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) {
     4.1 --- a/pidgin/gtkdialogs.c
     4.2 +++ b/pidgin/gtkdialogs.c
     4.3 @@ -433,7 +433,7 @@
     4.4  
     4.5  	/* Generate a logo with a version number */
     4.6  	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "logo.png", NULL);
     4.7 -	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
     4.8 +	pixbuf = pidgin_pixbuf_new_from_file(filename);
     4.9  	g_free(filename);
    4.10  
    4.11  #if 0  /* Don't versionize the logo when the logo has the version in it */
     5.1 --- a/pidgin/gtkft.c
     5.2 +++ b/pidgin/gtkft.c
     5.3 @@ -1149,8 +1149,8 @@
     5.4  
     5.5  	if (purple_xfer_get_size(xfer) <= PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL) {
     5.6  		GdkPixbuf *thumbnail =
     5.7 -			gdk_pixbuf_new_from_file_at_size(
     5.8 -				purple_xfer_get_local_filename(xfer), 128, 128, NULL);
     5.9 +			pidgin_pixbuf_new_from_file_at_size(
    5.10 +					purple_xfer_get_local_filename(xfer), 128, 128);
    5.11  
    5.12  		if (thumbnail) {
    5.13  			gchar **formats_split = g_strsplit(formats, ",", 0);
     6.1 --- a/pidgin/gtkimhtml.c
     6.2 +++ b/pidgin/gtkimhtml.c
     6.3 @@ -5057,16 +5057,8 @@
     6.4  
     6.5  		data = imhtml->funcs->image_get_data(image);
     6.6  		len = imhtml->funcs->image_get_size(image);
     6.7 -
     6.8 -		if (data && len) {
     6.9 -			GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
    6.10 -			gdk_pixbuf_loader_write(loader, data, len, NULL);
    6.11 -			gdk_pixbuf_loader_close(loader, NULL);
    6.12 -			anim = gdk_pixbuf_loader_get_animation(loader);
    6.13 -			if (anim)
    6.14 -				g_object_ref(G_OBJECT(anim));
    6.15 -			g_object_unref(G_OBJECT(loader));
    6.16 -		}
    6.17 +		if (data && len)
    6.18 +			anim = pidgin_pixbuf_anim_from_data(data, len);
    6.19  
    6.20  	}
    6.21  
     7.1 --- a/pidgin/gtkmain.c
     7.2 +++ b/pidgin/gtkmain.c
     7.3 @@ -270,7 +270,7 @@
     7.4  	/* use the nice PNG icon for all the windows */
     7.5  	for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
     7.6  		icon_path = g_build_filename(DATADIR, "icons", "hicolor", icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
     7.7 -		icon = gdk_pixbuf_new_from_file(icon_path, NULL);
     7.8 +		icon = pidgin_pixbuf_new_from_file(icon_path);
     7.9  		g_free(icon_path);
    7.10  		if (icon) {
    7.11  			icons = g_list_append(icons,icon);
     8.1 --- a/pidgin/gtkprefs.c
     8.2 +++ b/pidgin/gtkprefs.c
     8.3 @@ -382,7 +382,7 @@
     8.4  		 * LEAK - Gentoo memprof thinks pixbuf is leaking here... but it
     8.5  		 * looks like it should be ok to me.  Anyone know what's up?  --Mark
     8.6  		 */
     8.7 -		pixbuf = (theme->icon ? gdk_pixbuf_new_from_file(theme->icon, NULL) : NULL);
     8.8 +		pixbuf = (theme->icon ? pidgin_pixbuf_new_from_file(theme->icon) : NULL);
     8.9  
    8.10  		gtk_list_store_set(prefs_smiley_themes, &iter,
    8.11  				   0, pixbuf,
    8.12 @@ -452,7 +452,7 @@
    8.13  
    8.14  		image_full = purple_theme_get_image_full(theme);
    8.15  		if (image_full != NULL){
    8.16 -			pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
    8.17 +			pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
    8.18  			g_free(image_full);
    8.19  		} else
    8.20  			pixbuf = NULL;
    8.21 @@ -473,7 +473,7 @@
    8.22  
    8.23  		image_full = purple_theme_get_image_full(theme);
    8.24  		if (image_full != NULL){
    8.25 -			pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
    8.26 +			pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
    8.27  			g_free(image_full);
    8.28  		} else
    8.29  			pixbuf = NULL;
    8.30 @@ -529,7 +529,7 @@
    8.31  	purple_theme_manager_refresh();
    8.32  
    8.33  	tmp = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
    8.34 -	pixbuf = gdk_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
    8.35 +	pixbuf = pidgin_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
    8.36  	g_free(tmp);
    8.37  
    8.38  	/* sound themes */
     9.1 --- a/pidgin/gtkrequest.c
     9.2 +++ b/pidgin/gtkrequest.c
     9.3 @@ -653,35 +653,30 @@
     9.4  
     9.5  	/* Dialog icon. */
     9.6  	if (icon_data) {
     9.7 -		GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
     9.8 -		GdkPixbuf *pixbuf = NULL;
     9.9 -		if (gdk_pixbuf_loader_write(loader, icon_data, icon_size, NULL)) {
    9.10 -			pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
    9.11 -			if (pixbuf) {
    9.12 -				/* scale the image if it is too large */
    9.13 -				int width = gdk_pixbuf_get_width(pixbuf);
    9.14 -				int height = gdk_pixbuf_get_height(pixbuf);
    9.15 -				if (width > 128 || height > 128) {
    9.16 -					int scaled_width = width > height ? 128 : (128 * width) / height;
    9.17 -					int scaled_height = height > width ? 128 : (128 * height) / width;
    9.18 -					GdkPixbuf *scaled =
    9.19 -							gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height,
    9.20 -							    GDK_INTERP_BILINEAR);
    9.21 +		GdkPixbuf *pixbuf = pidgin_pixbuf_from_data(icon_data, icon_size);
    9.22 +		if (pixbuf) {
    9.23 +			/* scale the image if it is too large */
    9.24 +			int width = gdk_pixbuf_get_width(pixbuf);
    9.25 +			int height = gdk_pixbuf_get_height(pixbuf);
    9.26 +			if (width > 128 || height > 128) {
    9.27 +				int scaled_width = width > height ? 128 : (128 * width) / height;
    9.28 +				int scaled_height = height > width ? 128 : (128 * height) / width;
    9.29 +				GdkPixbuf *scaled =
    9.30 +						gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height,
    9.31 +						    GDK_INTERP_BILINEAR);
    9.32  
    9.33 -					purple_debug_info("pidgin",
    9.34 -					    "dialog icon was too large, scale it down\n");
    9.35 -					if (scaled) {
    9.36 -						g_object_unref(pixbuf);
    9.37 -						pixbuf = scaled;
    9.38 -					}
    9.39 +				purple_debug_info("pidgin",
    9.40 +				    "dialog icon was too large, scaled it down\n");
    9.41 +				if (scaled) {
    9.42 +					g_object_unref(pixbuf);
    9.43 +					pixbuf = scaled;
    9.44  				}
    9.45 -				img = gtk_image_new_from_pixbuf(pixbuf);
    9.46  			}
    9.47 +			img = gtk_image_new_from_pixbuf(pixbuf);
    9.48 +			g_object_unref(pixbuf);
    9.49  		} else {
    9.50  			purple_debug_info("pidgin", "failed to parse dialog icon\n");
    9.51  		}
    9.52 -		gdk_pixbuf_loader_close(loader, NULL);
    9.53 -		g_object_unref(loader);
    9.54  	}
    9.55  
    9.56  	if (!img) {
    9.57 @@ -1016,22 +1011,17 @@
    9.58  {
    9.59  	GtkWidget *widget;
    9.60  	GdkPixbuf *buf, *scale;
    9.61 -	GdkPixbufLoader *loader;
    9.62  
    9.63 -	loader = gdk_pixbuf_loader_new();
    9.64 -	gdk_pixbuf_loader_write(loader,
    9.65 -							(const guchar *)purple_request_field_image_get_buffer(field),
    9.66 -							purple_request_field_image_get_size(field),
    9.67 -							NULL);
    9.68 -	gdk_pixbuf_loader_close(loader, NULL);
    9.69 -	buf = gdk_pixbuf_loader_get_pixbuf(loader);
    9.70 +	buf = pidgin_pixbuf_from_data(
    9.71 +			(const guchar *)purple_request_field_image_get_buffer(field),
    9.72 +			purple_request_field_image_get_size(field));
    9.73  
    9.74  	scale = gdk_pixbuf_scale_simple(buf,
    9.75  			purple_request_field_image_get_scale_x(field) * gdk_pixbuf_get_width(buf),
    9.76  			purple_request_field_image_get_scale_y(field) * gdk_pixbuf_get_height(buf),
    9.77  			GDK_INTERP_BILINEAR);
    9.78  	widget = gtk_image_new_from_pixbuf(scale);
    9.79 -	g_object_unref(G_OBJECT(loader));
    9.80 +	g_object_unref(G_OBJECT(buf));
    9.81  	g_object_unref(G_OBJECT(scale));
    9.82  
    9.83  	return widget;
    9.84 @@ -1132,7 +1122,7 @@
    9.85  			GdkPixbuf* pixbuf = NULL;
    9.86  
    9.87  			if (icon_path)
    9.88 -				pixbuf = gdk_pixbuf_new_from_file(icon_path, NULL);
    9.89 +				pixbuf = pidgin_pixbuf_new_from_file(icon_path);
    9.90  
    9.91  			gtk_list_store_set(store, &iter,
    9.92  						   0, purple_request_field_list_get_data(field, text),
    10.1 --- a/pidgin/gtksmiley.c
    10.2 +++ b/pidgin/gtksmiley.c
    10.3 @@ -332,7 +332,7 @@
    10.4  
    10.5  	g_free(s->filename);
    10.6  	s->filename = g_strdup(filename);
    10.7 -	pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE, NULL);
    10.8 +	pixbuf = pidgin_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE);
    10.9  	gtk_image_set_from_pixbuf(GTK_IMAGE(s->smiley_image), pixbuf);
   10.10  	if (pixbuf)
   10.11  		g_object_unref(G_OBJECT(pixbuf));
   10.12 @@ -690,7 +690,6 @@
   10.13  	FILE *f;
   10.14  	gchar *path;
   10.15  	size_t wc;
   10.16 -	GError *err = NULL;
   10.17  	PidginSmiley *ps;
   10.18  	GdkPixbuf *image;
   10.19  
   10.20 @@ -709,13 +708,11 @@
   10.21  	}
   10.22  	fclose(f);
   10.23  
   10.24 -	image = gdk_pixbuf_new_from_file(path, &err);
   10.25 +	image = pidgin_pixbuf_new_from_file(path);
   10.26  	g_unlink(path);
   10.27  	g_free(path);
   10.28 -	if (err) {
   10.29 -		g_error_free(err);
   10.30 +	if (!image)
   10.31  		return;
   10.32 -	}
   10.33  
   10.34  	ps = pidgin_smiley_edit(dialog->window, NULL);
   10.35  	pidgin_smiley_editor_set_image(ps, image);
    11.1 --- a/pidgin/gtkstatusbox.c
    11.2 +++ b/pidgin/gtkstatusbox.c
    11.3 @@ -2225,22 +2225,45 @@
    11.4  
    11.5  	if (status_box->buddy_icon_img != NULL)
    11.6  	{
    11.7 -		GdkPixbuf *buf, *scale;
    11.8 -		int scale_width, scale_height;
    11.9 -		GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
   11.10 +		GdkPixbufLoader *loader;
   11.11 +		GError *error = NULL;
   11.12 +
   11.13 +		loader = gdk_pixbuf_loader_new();
   11.14 +
   11.15  		g_signal_connect(G_OBJECT(loader), "size-prepared", G_CALLBACK(pixbuf_size_prepared_cb), NULL);
   11.16 -		gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(status_box->buddy_icon_img),
   11.17 -		                        purple_imgstore_get_size(status_box->buddy_icon_img), NULL);
   11.18 -		gdk_pixbuf_loader_close(loader, NULL);
   11.19 -		buf = gdk_pixbuf_loader_get_pixbuf(loader);
   11.20 -		scale_width = gdk_pixbuf_get_width(buf);
   11.21 -		scale_height = gdk_pixbuf_get_height(buf);
   11.22 -		scale = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height);
   11.23 -		gdk_pixbuf_fill(scale, 0x00000000);
   11.24 -		gdk_pixbuf_copy_area(buf, 0, 0, scale_width, scale_height, scale, 0, 0);
   11.25 -		if (pidgin_gdk_pixbuf_is_opaque(scale))
   11.26 -			pidgin_gdk_pixbuf_make_round(scale);
   11.27 -		status_box->buddy_icon = scale;
   11.28 +		if (!gdk_pixbuf_loader_write(loader,
   11.29 +				purple_imgstore_get_data(status_box->buddy_icon_img),
   11.30 +				purple_imgstore_get_size(status_box->buddy_icon_img),
   11.31 +				&error) || error)
   11.32 +		{
   11.33 +			purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_write() "
   11.34 +					"failed with size=%zu: %s\n",
   11.35 +					purple_imgstore_get_size(status_box->buddy_icon_img),
   11.36 +					error ? error->message : "(no error message)");
   11.37 +			if (error)
   11.38 +				g_error_free(error);
   11.39 +		} else if (!gdk_pixbuf_loader_close(loader, &error) || error) {
   11.40 +			purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_close() "
   11.41 +					"failed for image of size %zu: %s\n",
   11.42 +					purple_imgstore_get_size(status_box->buddy_icon_img),
   11.43 +					error ? error->message : "(no error message)");
   11.44 +			if (error)
   11.45 +				g_error_free(error);
   11.46 +		} else {
   11.47 +			GdkPixbuf *buf, *scale;
   11.48 +			int scale_width, scale_height;
   11.49 +
   11.50 +			buf = gdk_pixbuf_loader_get_pixbuf(loader);
   11.51 +			scale_width = gdk_pixbuf_get_width(buf);
   11.52 +			scale_height = gdk_pixbuf_get_height(buf);
   11.53 +			scale = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height);
   11.54 +			gdk_pixbuf_fill(scale, 0x00000000);
   11.55 +			gdk_pixbuf_copy_area(buf, 0, 0, scale_width, scale_height, scale, 0, 0);
   11.56 +			if (pidgin_gdk_pixbuf_is_opaque(scale))
   11.57 +				pidgin_gdk_pixbuf_make_round(scale);
   11.58 +			status_box->buddy_icon = scale;
   11.59 +		}
   11.60 +
   11.61  		g_object_unref(loader);
   11.62  	}
   11.63  
    12.1 --- a/pidgin/gtkutils.c
    12.2 +++ b/pidgin/gtkutils.c
    12.3 @@ -615,7 +615,7 @@
    12.4  				    tmp, NULL);
    12.5  	g_free(tmp);
    12.6  
    12.7 -	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
    12.8 +	pixbuf = pidgin_pixbuf_new_from_file(filename);
    12.9  	g_free(filename);
   12.10  
   12.11  	return pixbuf;
   12.12 @@ -704,7 +704,7 @@
   12.13  			                                  "16", "google-talk.png", NULL);
   12.14  			GtkWidget *item;
   12.15  
   12.16 -			pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
   12.17 +			pixbuf = pidgin_pixbuf_new_from_file(filename);
   12.18  			g_free(filename);
   12.19  
   12.20  			gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
   12.21 @@ -723,7 +723,7 @@
   12.22  			                                  "16", "facebook.png", NULL);
   12.23  			GtkWidget *item;
   12.24  
   12.25 -			pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
   12.26 +			pixbuf = pidgin_pixbuf_new_from_file(filename);
   12.27  			g_free(filename);
   12.28  
   12.29  			gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
   12.30 @@ -1593,7 +1593,7 @@
   12.31  		}
   12.32  
   12.33  		/* Are we dealing with an image? */
   12.34 -		pb = gdk_pixbuf_new_from_file(filename, NULL);
   12.35 +		pb = pidgin_pixbuf_new_from_file(filename);
   12.36  		if (pb) {
   12.37  			_DndData *data = g_malloc(sizeof(_DndData));
   12.38  			gboolean ft = FALSE, im = FALSE;
   12.39 @@ -2265,7 +2265,7 @@
   12.40  	filename = gtk_file_chooser_get_preview_filename(
   12.41  					GTK_FILE_CHOOSER(dialog->icon_filesel));
   12.42  
   12.43 -	if (!filename || g_stat(filename, &st) || !(pixbuf = gdk_pixbuf_new_from_file(filename, NULL)))
   12.44 +	if (!filename || g_stat(filename, &st) || !(pixbuf = pidgin_pixbuf_new_from_file(filename)))
   12.45  	{
   12.46  		gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL);
   12.47  		gtk_label_set_markup(GTK_LABEL(dialog->icon_text), "");
   12.48 @@ -3086,17 +3086,134 @@
   12.49  #endif
   12.50  }
   12.51  
   12.52 -GdkPixbuf * pidgin_pixbuf_from_imgstore(PurpleStoredImage *image)
   12.53 +static GObject *pidgin_pixbuf_from_data_helper(const guchar *buf, gsize count, gboolean animated)
   12.54 +{
   12.55 +	GObject *pixbuf;
   12.56 +	GdkPixbufLoader *loader;
   12.57 +	GError *error = NULL;
   12.58 +
   12.59 +	loader = gdk_pixbuf_loader_new();
   12.60 +
   12.61 +	if (!gdk_pixbuf_loader_write(loader, buf, count, &error) || error) {
   12.62 +		purple_debug_warning("gtkutils", "gdk_pixbuf_loader_write() "
   12.63 +				"failed with size=%zu: %s\n", count,
   12.64 +				error ? error->message : "(no error message)");
   12.65 +		if (error)
   12.66 +			g_error_free(error);
   12.67 +		g_object_unref(G_OBJECT(loader));
   12.68 +		return NULL;
   12.69 +	}
   12.70 +
   12.71 +	if (!gdk_pixbuf_loader_close(loader, &error) || error) {
   12.72 +		purple_debug_warning("gtkutils", "gdk_pixbuf_loader_close() "
   12.73 +				"failed for image of size %zu: %s\n", count,
   12.74 +				error ? error->message : "(no error message)");
   12.75 +		if (error)
   12.76 +			g_error_free(error);
   12.77 +		g_object_unref(G_OBJECT(loader));
   12.78 +		return NULL;
   12.79 +	}
   12.80 +
   12.81 +	if (animated)
   12.82 +		pixbuf = G_OBJECT(gdk_pixbuf_loader_get_animation(loader));
   12.83 +	else
   12.84 +		pixbuf = G_OBJECT(gdk_pixbuf_loader_get_pixbuf(loader));
   12.85 +	if (!pixbuf) {
   12.86 +		purple_debug_warning("gtkutils", "%s() returned NULL for image "
   12.87 +				"of size %zu\n",
   12.88 +				animated ? "gdk_pixbuf_loader_get_animation"
   12.89 +					: "gdk_pixbuf_loader_get_pixbuf", count);
   12.90 +		g_object_unref(G_OBJECT(loader));
   12.91 +		return NULL;
   12.92 +	}
   12.93 +
   12.94 +	g_object_ref(pixbuf);
   12.95 +	g_object_unref(G_OBJECT(loader));
   12.96 +
   12.97 +	return pixbuf;
   12.98 +}
   12.99 +
  12.100 +GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count)
  12.101 +{
  12.102 +	return GDK_PIXBUF(pidgin_pixbuf_from_data_helper(buf, count, FALSE));
  12.103 +}
  12.104 +
  12.105 +GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count)
  12.106 +{
  12.107 +	return GDK_PIXBUF_ANIMATION(pidgin_pixbuf_from_data_helper(buf, count, TRUE));
  12.108 +}
  12.109 +
  12.110 +GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image)
  12.111 +{
  12.112 +	return pidgin_pixbuf_from_data(purple_imgstore_get_data(image),
  12.113 +			purple_imgstore_get_size(image));
  12.114 +}
  12.115 +
  12.116 +GdkPixbuf *pidgin_pixbuf_new_from_file(const gchar *filename)
  12.117  {
  12.118  	GdkPixbuf *pixbuf;
  12.119 -	GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
  12.120 -	gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(image),
  12.121 -			purple_imgstore_get_size(image), NULL);
  12.122 -	gdk_pixbuf_loader_close(loader, NULL);
  12.123 -	pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
  12.124 -	if (pixbuf)
  12.125 -		g_object_ref(pixbuf);
  12.126 -	g_object_unref(loader);
  12.127 +	GError *error = NULL;
  12.128 +
  12.129 +	pixbuf = gdk_pixbuf_new_from_file(filename, &error);
  12.130 +	if (!pixbuf || error) {
  12.131 +		purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file() "
  12.132 +				"returned %s for file %s: %s\n",
  12.133 +				pixbuf ? "something" : "nothing",
  12.134 +				filename,
  12.135 +				error ? error->message : "(no error message)");
  12.136 +		if (error)
  12.137 +			g_error_free(error);
  12.138 +		if (pixbuf)
  12.139 +			g_object_unref(G_OBJECT(pixbuf));
  12.140 +		return NULL;
  12.141 +	}
  12.142 +
  12.143 +	return pixbuf;
  12.144 +}
  12.145 +
  12.146 +GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height)
  12.147 +{
  12.148 +	GdkPixbuf *pixbuf;
  12.149 +	GError *error = NULL;
  12.150 +
  12.151 +	pixbuf = gdk_pixbuf_new_from_file_at_size(filename,
  12.152 +			width, height, &error);
  12.153 +	if (!pixbuf || error) {
  12.154 +		purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file_at_size() "
  12.155 +				"returned %s for file %s: %s\n",
  12.156 +				pixbuf ? "something" : "nothing",
  12.157 +				filename,
  12.158 +				error ? error->message : "(no error message)");
  12.159 +		if (error)
  12.160 +			g_error_free(error);
  12.161 +		if (pixbuf)
  12.162 +			g_object_unref(G_OBJECT(pixbuf));
  12.163 +		return NULL;
  12.164 +	}
  12.165 +
  12.166 +	return pixbuf;
  12.167 +}
  12.168 +
  12.169 +GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio)
  12.170 +{
  12.171 +	GdkPixbuf *pixbuf;
  12.172 +	GError *error = NULL;
  12.173 +
  12.174 +	pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
  12.175 +			width, height, preserve_aspect_ratio, &error);
  12.176 +	if (!pixbuf || error) {
  12.177 +		purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file_at_scale() "
  12.178 +				"returned %s for file %s: %s\n",
  12.179 +				pixbuf ? "something" : "nothing",
  12.180 +				filename,
  12.181 +				error ? error->message : "(no error message)");
  12.182 +		if (error)
  12.183 +			g_error_free(error);
  12.184 +		if (pixbuf)
  12.185 +			g_object_unref(G_OBJECT(pixbuf));
  12.186 +		return NULL;
  12.187 +	}
  12.188 +
  12.189  	return pixbuf;
  12.190  }
  12.191  
    13.1 --- a/pidgin/gtkutils.h
    13.2 +++ b/pidgin/gtkutils.h
    13.3 @@ -834,6 +834,32 @@
    13.4  GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label);
    13.5  
    13.6  /**
    13.7 + * Create a GdkPixbuf from a chunk of image data.
    13.8 + *
    13.9 + * @param buf The raw binary image data.
   13.10 + * @param count The length of buf in bytes.
   13.11 + *
   13.12 + * @return A GdkPixbuf created from the image data, or NULL if
   13.13 + *         there was an error parsing the data.
   13.14 + *
   13.15 + * @since 2.9.0
   13.16 + */
   13.17 +GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count);
   13.18 +
   13.19 +/**
   13.20 + * Create a GdkPixbufAnimation from a chunk of image data.
   13.21 + *
   13.22 + * @param buf The raw binary image data.
   13.23 + * @param count The length of buf in bytes.
   13.24 + *
   13.25 + * @return A GdkPixbufAnimation created from the image data, or NULL if
   13.26 + *         there was an error parsing the data.
   13.27 + *
   13.28 + * @since 2.9.0
   13.29 + */
   13.30 +GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count);
   13.31 +
   13.32 +/**
   13.33   * Create a GdkPixbuf from a PurpleStoredImage.
   13.34   *
   13.35   * @param  image   A PurpleStoredImage.
   13.36 @@ -845,6 +871,86 @@
   13.37  GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
   13.38  
   13.39  /**
   13.40 + * Helper function that calls gdk_pixbuf_new_from_file() and checks both
   13.41 + * the return code and the GError and returns NULL if either one failed.
   13.42 + *
   13.43 + * The gdk-pixbuf documentation implies that it is sufficient to check
   13.44 + * the return value of gdk_pixbuf_new_from_file() to determine
   13.45 + * whether the image was able to be loaded.  However, this is not the case
   13.46 + * with gdk-pixbuf 2.23.3 and probably many earlier versions.  In some
   13.47 + * cases a GdkPixbuf object is returned that will cause some operations
   13.48 + * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an
   13.49 + * infinite loop.
   13.50 + *
   13.51 + * This function shouldn't be necessary once Pidgin requires a version of
   13.52 + * gdk-pixbuf where the aforementioned bug is fixed.  However, it might be
   13.53 + * nice to keep this function around for the debug message that it logs.
   13.54 + *
   13.55 + * @param filename Name of file to load, in the GLib file name encoding
   13.56 + *
   13.57 + * @return The GdkPixbuf if successful.  Otherwise NULL is returned and
   13.58 + *         a warning is logged.
   13.59 + *
   13.60 + * @since 2.9.0
   13.61 + */
   13.62 +GdkPixbuf *pidgin_pixbuf_new_from_file(const char *filename);
   13.63 +
   13.64 +/**
   13.65 + * Helper function that calls gdk_pixbuf_new_from_file_at_size() and checks
   13.66 + * both the return code and the GError and returns NULL if either one failed.
   13.67 + *
   13.68 + * The gdk-pixbuf documentation implies that it is sufficient to check
   13.69 + * the return value of gdk_pixbuf_new_from_file_at_size() to determine
   13.70 + * whether the image was able to be loaded.  However, this is not the case
   13.71 + * with gdk-pixbuf 2.23.3 and probably many earlier versions.  In some
   13.72 + * cases a GdkPixbuf object is returned that will cause some operations
   13.73 + * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an
   13.74 + * infinite loop.
   13.75 + *
   13.76 + * This function shouldn't be necessary once Pidgin requires a version of
   13.77 + * gdk-pixbuf where the aforementioned bug is fixed.  However, it might be
   13.78 + * nice to keep this function around for the debug message that it logs.
   13.79 + *
   13.80 + * @param filename Name of file to load, in the GLib file name encoding
   13.81 + * @param width The width the image should have or -1 to not constrain the width
   13.82 + * @param height The height the image should have or -1 to not constrain the height
   13.83 + *
   13.84 + * @return The GdkPixbuf if successful.  Otherwise NULL is returned and
   13.85 + *         a warning is logged.
   13.86 + *
   13.87 + * @since 2.9.0
   13.88 + */
   13.89 +GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height);
   13.90 +
   13.91 +/**
   13.92 + * Helper function that calls gdk_pixbuf_new_from_file_at_scale() and checks
   13.93 + * both the return code and the GError and returns NULL if either one failed.
   13.94 + *
   13.95 + * The gdk-pixbuf documentation implies that it is sufficient to check
   13.96 + * the return value of gdk_pixbuf_new_from_file_at_scale() to determine
   13.97 + * whether the image was able to be loaded.  However, this is not the case
   13.98 + * with gdk-pixbuf 2.23.3 and probably many earlier versions.  In some
   13.99 + * cases a GdkPixbuf object is returned that will cause some operations
  13.100 + * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an
  13.101 + * infinite loop.
  13.102 + *
  13.103 + * This function shouldn't be necessary once Pidgin requires a version of
  13.104 + * gdk-pixbuf where the aforementioned bug is fixed.  However, it might be
  13.105 + * nice to keep this function around for the debug message that it logs.
  13.106 + *
  13.107 + * @param filename Name of file to load, in the GLib file name encoding
  13.108 + * @param width The width the image should have or -1 to not constrain the width
  13.109 + * @param height The height the image should have or -1 to not constrain the height
  13.110 + * @param preserve_aspect_ratio TRUE to preserve the image's aspect ratio
  13.111 + *
  13.112 + * @return The GdkPixbuf if successful.  Otherwise NULL is returned and
  13.113 + *         a warning is logged.
  13.114 + *
  13.115 + * @since 2.9.0
  13.116 + */
  13.117 +GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio);
  13.118 +
  13.119 +/**
  13.120   * Add scrollbars to a widget
  13.121   * @param widget      The child widget
  13.122   * @hscrollbar_policy Horizontal scrolling policy