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