summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Doliner <markdoliner@pidgin.im>2011-06-22 07:09:42 +0000
committerMark Doliner <markdoliner@pidgin.im>2011-06-22 07:09:42 +0000
commit21c4e17f9a3e032e52c8ccec552fb73fd9bbe0ce (patch)
tree81a3482cdc7962a1a74102b80863b36f19d5436a
parentde25d0e68933a3742195cbbf9fe6b2e73718f5c5 (diff)
downloadpidgin-21c4e17f9a3e032e52c8ccec552fb73fd9bbe0ce.tar.gz
applied changes from 8300e1e9a38c8d28e0710569d22be71f6c1abca9
through 96183796df0c52bb91e9828948c24cfa36845c1d Original commit message: 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.
-rw-r--r--pidgin/gtkaccount.c2
-rw-r--r--pidgin/gtkblist.c24
-rw-r--r--pidgin/gtkconv.c73
-rw-r--r--pidgin/gtkdialogs.c2
-rw-r--r--pidgin/gtkft.c4
-rw-r--r--pidgin/gtkimhtml.c12
-rw-r--r--pidgin/gtkmain.c2
-rw-r--r--pidgin/gtkprefs.c8
-rw-r--r--pidgin/gtkrequest.c58
-rw-r--r--pidgin/gtksmiley.c9
-rw-r--r--pidgin/gtkstatusbox.c53
-rw-r--r--pidgin/gtkutils.c145
-rw-r--r--pidgin/gtkutils.h106
13 files changed, 363 insertions, 135 deletions
diff --git a/pidgin/gtkaccount.c b/pidgin/gtkaccount.c
index 39d6b11371..7bb0524961 100644
--- a/pidgin/gtkaccount.c
+++ b/pidgin/gtkaccount.c
@@ -2135,7 +2135,7 @@ populate_accounts_list(AccountsWindow *dialog)
gtk_list_store_clear(dialog->model);
if ((path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon")) != NULL) {
- GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL);
+ GdkPixbuf *pixbuf = pidgin_pixbuf_new_from_file(path);
if (pixbuf != NULL) {
global_buddyicon = gdk_pixbuf_scale_simple(pixbuf, 22, 22, GDK_INTERP_HYPER);
g_object_unref(G_OBJECT(pixbuf));
diff --git a/pidgin/gtkblist.c b/pidgin/gtkblist.c
index c32f1b5e42..f0db91ae8d 100644
--- a/pidgin/gtkblist.c
+++ b/pidgin/gtkblist.c
@@ -2643,7 +2643,6 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
gboolean scaled, gboolean greyed)
{
gsize len;
- GdkPixbufLoader *loader;
PurpleBuddy *buddy = NULL;
PurpleGroup *group = NULL;
const guchar *data = NULL;
@@ -2710,21 +2709,20 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node,
return NULL;
}
- loader = gdk_pixbuf_loader_new();
- gdk_pixbuf_loader_write(loader, data, len, NULL);
- gdk_pixbuf_loader_close(loader, NULL);
-
- purple_imgstore_unref(custom_img);
+ buf = pidgin_pixbuf_from_data(data, len);
purple_buddy_icon_unref(icon);
-
- buf = gdk_pixbuf_loader_get_pixbuf(loader);
- if (buf)
- g_object_ref(G_OBJECT(buf));
- g_object_unref(G_OBJECT(loader));
-
if (!buf) {
+ purple_debug_warning("gtkblist", "Couldn't load buddy icon "
+ "on account %s (%s) buddyname=%s "
+ "custom_img_data=%p\n",
+ account ? purple_account_get_username(account) : "(no account)",
+ account ? purple_account_get_protocol_id(account) : "(no account)",
+ buddy ? purple_buddy_get_name(buddy) : "(no buddy)",
+ custom_img ? purple_imgstore_get_data(custom_img) : NULL);
+ purple_imgstore_unref(custom_img);
return NULL;
}
+ purple_imgstore_unref(custom_img);
if (greyed) {
gboolean offline = FALSE, idle = FALSE;
@@ -3952,7 +3950,7 @@ static GdkPixbuf * _pidgin_blist_get_cached_emblem(gchar *path) {
g_object_ref(pb);
g_free(path);
} else {
- pb = gdk_pixbuf_new_from_file(path, NULL);
+ pb = pidgin_pixbuf_new_from_file(path);
if (pb != NULL) {
/* We don't want to own a ref to the pixbuf, but we need to keep clean up. */
/* I'm not sure if it would be better to just keep our ref and not let the emblem ever be destroyed */
diff --git a/pidgin/gtkconv.c b/pidgin/gtkconv.c
index 6402147a28..3fa34571eb 100644
--- a/pidgin/gtkconv.c
+++ b/pidgin/gtkconv.c
@@ -6408,8 +6408,8 @@ pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
{
PidginConversation *gtkconv;
GtkIMHtmlSmiley *smiley;
- GdkPixbufLoader *loader;
const char *sml;
+ GError *error = NULL;
sml = purple_account_get_protocol_name(conv->account);
gtkconv = PIDGIN_CONVERSATION(conv);
@@ -6422,11 +6422,24 @@ pidgin_conv_custom_smiley_write(PurpleConversation *conv, const char *smile,
g_memmove((guchar *)smiley->data + smiley->datasize, data, size);
smiley->datasize += size;
- loader = smiley->loader;
- if (!loader)
+ if (!smiley->loader)
return;
- gdk_pixbuf_loader_write(loader, data, size, NULL);
+ if (!gdk_pixbuf_loader_write(smiley->loader, data, size, &error) || error) {
+ purple_debug_warning("gtkconv", "gdk_pixbuf_loader_write() "
+ "failed with size=%zu: %s\n", size,
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ /* We must stop using the GdkPixbufLoader because trying to load
+ certain invalid GIFs with at least gdk-pixbuf 2.23.3 can return
+ a GdkPixbuf that will cause some operations (like
+ gdk_pixbuf_scale_simple()) to consume memory in an infinite loop.
+ But we also don't want to set smiley->loader to NULL because our
+ code might expect it to be set. So create a new loader. */
+ g_object_unref(G_OBJECT(smiley->loader));
+ smiley->loader = gdk_pixbuf_loader_new();
+ }
}
static void
@@ -6434,8 +6447,8 @@ pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
{
PidginConversation *gtkconv;
GtkIMHtmlSmiley *smiley;
- GdkPixbufLoader *loader;
const char *sml;
+ GError *error = NULL;
g_return_if_fail(conv != NULL);
g_return_if_fail(smile != NULL);
@@ -6447,17 +6460,27 @@ pidgin_conv_custom_smiley_close(PurpleConversation *conv, const char *smile)
if (!smiley)
return;
- loader = smiley->loader;
-
- if (!loader)
+ if (!smiley->loader)
return;
-
-
purple_debug_info("gtkconv", "About to close the smiley pixbuf\n");
- gdk_pixbuf_loader_close(loader, NULL);
-
+ if (!gdk_pixbuf_loader_close(smiley->loader, &error) || error) {
+ purple_debug_warning("gtkconv", "gdk_pixbuf_loader_close() "
+ "failed: %s\n",
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ /* We must stop using the GdkPixbufLoader because if we tried to
+ load certain invalid GIFs with all current versions of GDK (as
+ of 2011-06-15) then it's possible the loader will contain data
+ that could cause some operations (like gdk_pixbuf_scale_simple())
+ to consume memory in an infinite loop. But we also don't want
+ to set smiley->loader to NULL because our code might expect it
+ to be set. So create a new loader. */
+ g_object_unref(G_OBJECT(smiley->loader));
+ smiley->loader = gdk_pixbuf_loader_new();
+ }
}
static void
@@ -6957,10 +6980,6 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
PurpleBuddy *buddy;
- GdkPixbufLoader *loader;
- GdkPixbufAnimation *anim;
- GError *err = NULL;
-
PurpleStoredImage *custom_img = NULL;
gconstpointer data = NULL;
size_t len;
@@ -7038,7 +7057,6 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
if (data == NULL) {
icon = purple_conv_im_get_icon(PURPLE_CONV_IM(conv));
-
if (icon == NULL)
{
gtk_widget_set_size_request(gtkconv->u.im->icon_container,
@@ -7047,7 +7065,6 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
}
data = purple_buddy_icon_get_data(icon, &len);
-
if (data == NULL)
{
gtk_widget_set_size_request(gtkconv->u.im->icon_container,
@@ -7056,25 +7073,13 @@ pidgin_conv_update_buddy_icon(PurpleConversation *conv)
}
}
- loader = gdk_pixbuf_loader_new();
- gdk_pixbuf_loader_write(loader, data, len, NULL);
- gdk_pixbuf_loader_close(loader, &err);
-
+ gtkconv->u.im->anim = pidgin_pixbuf_anim_from_data(data, len);
purple_imgstore_unref(custom_img);
- anim = gdk_pixbuf_loader_get_animation(loader);
- if (anim)
- g_object_ref(G_OBJECT(anim));
- g_object_unref(loader);
-
- if (!anim)
+ if (!gtkconv->u.im->anim) {
+ purple_debug_error("gtkconv", "Couldn't load icon for conv %s\n",
+ purple_conversation_get_name(conv));
return;
- gtkconv->u.im->anim = anim;
-
- if (err) {
- purple_debug(PURPLE_DEBUG_ERROR, "gtkconv",
- "Buddy icon error: %s\n", err->message);
- g_error_free(err);
}
if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) {
diff --git a/pidgin/gtkdialogs.c b/pidgin/gtkdialogs.c
index da6da82b2d..01d964b74b 100644
--- a/pidgin/gtkdialogs.c
+++ b/pidgin/gtkdialogs.c
@@ -433,7 +433,7 @@ pidgin_build_help_dialog(const char *title, const char *role, GString *string)
/* Generate a logo with a version number */
filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "logo.png", NULL);
- pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file(filename);
g_free(filename);
#if 0 /* Don't versionize the logo when the logo has the version in it */
diff --git a/pidgin/gtkft.c b/pidgin/gtkft.c
index 17324714f9..4c1bc658ea 100644
--- a/pidgin/gtkft.c
+++ b/pidgin/gtkft.c
@@ -1149,8 +1149,8 @@ pidgin_xfer_add_thumbnail(PurpleXfer *xfer, const gchar *formats)
if (purple_xfer_get_size(xfer) <= PIDGIN_XFER_MAX_SIZE_IMAGE_THUMBNAIL) {
GdkPixbuf *thumbnail =
- gdk_pixbuf_new_from_file_at_size(
- purple_xfer_get_local_filename(xfer), 128, 128, NULL);
+ pidgin_pixbuf_new_from_file_at_size(
+ purple_xfer_get_local_filename(xfer), 128, 128);
if (thumbnail) {
gchar **formats_split = g_strsplit(formats, ",", 0);
diff --git a/pidgin/gtkimhtml.c b/pidgin/gtkimhtml.c
index 44be7c291c..d74407fe2a 100644
--- a/pidgin/gtkimhtml.c
+++ b/pidgin/gtkimhtml.c
@@ -5057,16 +5057,8 @@ void gtk_imhtml_insert_image_at_iter(GtkIMHtml *imhtml, int id, GtkTextIter *ite
data = imhtml->funcs->image_get_data(image);
len = imhtml->funcs->image_get_size(image);
-
- if (data && len) {
- GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
- gdk_pixbuf_loader_write(loader, data, len, NULL);
- gdk_pixbuf_loader_close(loader, NULL);
- anim = gdk_pixbuf_loader_get_animation(loader);
- if (anim)
- g_object_ref(G_OBJECT(anim));
- g_object_unref(G_OBJECT(loader));
- }
+ if (data && len)
+ anim = pidgin_pixbuf_anim_from_data(data, len);
}
diff --git a/pidgin/gtkmain.c b/pidgin/gtkmain.c
index 24871a2c09..0fb2a7a393 100644
--- a/pidgin/gtkmain.c
+++ b/pidgin/gtkmain.c
@@ -270,7 +270,7 @@ ui_main(void)
/* use the nice PNG icon for all the windows */
for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
icon_path = g_build_filename(DATADIR, "icons", "hicolor", icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
- icon = gdk_pixbuf_new_from_file(icon_path, NULL);
+ icon = pidgin_pixbuf_new_from_file(icon_path);
g_free(icon_path);
if (icon) {
icons = g_list_append(icons,icon);
diff --git a/pidgin/gtkprefs.c b/pidgin/gtkprefs.c
index 5e4c9d425c..a7a0c30080 100644
--- a/pidgin/gtkprefs.c
+++ b/pidgin/gtkprefs.c
@@ -382,7 +382,7 @@ smileys_refresh_theme_list(void)
* LEAK - Gentoo memprof thinks pixbuf is leaking here... but it
* looks like it should be ok to me. Anyone know what's up? --Mark
*/
- pixbuf = (theme->icon ? gdk_pixbuf_new_from_file(theme->icon, NULL) : NULL);
+ pixbuf = (theme->icon ? pidgin_pixbuf_new_from_file(theme->icon) : NULL);
gtk_list_store_set(prefs_smiley_themes, &iter,
0, pixbuf,
@@ -452,7 +452,7 @@ prefs_themes_sort(PurpleTheme *theme)
image_full = purple_theme_get_image_full(theme);
if (image_full != NULL){
- pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
g_free(image_full);
} else
pixbuf = NULL;
@@ -473,7 +473,7 @@ prefs_themes_sort(PurpleTheme *theme)
image_full = purple_theme_get_image_full(theme);
if (image_full != NULL){
- pixbuf = gdk_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file_at_scale(image_full, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
g_free(image_full);
} else
pixbuf = NULL;
@@ -529,7 +529,7 @@ prefs_themes_refresh(void)
purple_theme_manager_refresh();
tmp = g_build_filename(DATADIR, "icons", "hicolor", "32x32", "apps", "pidgin.png", NULL);
- pixbuf = gdk_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
g_free(tmp);
/* sound themes */
diff --git a/pidgin/gtkrequest.c b/pidgin/gtkrequest.c
index 83f12e9542..0ab41c80ad 100644
--- a/pidgin/gtkrequest.c
+++ b/pidgin/gtkrequest.c
@@ -653,35 +653,30 @@ pidgin_request_action_with_icon(const char *title, const char *primary,
/* Dialog icon. */
if (icon_data) {
- GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
- GdkPixbuf *pixbuf = NULL;
- if (gdk_pixbuf_loader_write(loader, icon_data, icon_size, NULL)) {
- pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
- if (pixbuf) {
- /* scale the image if it is too large */
- int width = gdk_pixbuf_get_width(pixbuf);
- int height = gdk_pixbuf_get_height(pixbuf);
- if (width > 128 || height > 128) {
- int scaled_width = width > height ? 128 : (128 * width) / height;
- int scaled_height = height > width ? 128 : (128 * height) / width;
- GdkPixbuf *scaled =
- gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height,
- GDK_INTERP_BILINEAR);
-
- purple_debug_info("pidgin",
- "dialog icon was too large, scale it down\n");
- if (scaled) {
- g_object_unref(pixbuf);
- pixbuf = scaled;
- }
+ GdkPixbuf *pixbuf = pidgin_pixbuf_from_data(icon_data, icon_size);
+ if (pixbuf) {
+ /* scale the image if it is too large */
+ int width = gdk_pixbuf_get_width(pixbuf);
+ int height = gdk_pixbuf_get_height(pixbuf);
+ if (width > 128 || height > 128) {
+ int scaled_width = width > height ? 128 : (128 * width) / height;
+ int scaled_height = height > width ? 128 : (128 * height) / width;
+ GdkPixbuf *scaled =
+ gdk_pixbuf_scale_simple(pixbuf, scaled_width, scaled_height,
+ GDK_INTERP_BILINEAR);
+
+ purple_debug_info("pidgin",
+ "dialog icon was too large, scaled it down\n");
+ if (scaled) {
+ g_object_unref(pixbuf);
+ pixbuf = scaled;
}
- img = gtk_image_new_from_pixbuf(pixbuf);
}
+ img = gtk_image_new_from_pixbuf(pixbuf);
+ g_object_unref(pixbuf);
} else {
purple_debug_info("pidgin", "failed to parse dialog icon\n");
}
- gdk_pixbuf_loader_close(loader, NULL);
- g_object_unref(loader);
}
if (!img) {
@@ -1016,22 +1011,17 @@ create_image_field(PurpleRequestField *field)
{
GtkWidget *widget;
GdkPixbuf *buf, *scale;
- GdkPixbufLoader *loader;
- loader = gdk_pixbuf_loader_new();
- gdk_pixbuf_loader_write(loader,
- (const guchar *)purple_request_field_image_get_buffer(field),
- purple_request_field_image_get_size(field),
- NULL);
- gdk_pixbuf_loader_close(loader, NULL);
- buf = gdk_pixbuf_loader_get_pixbuf(loader);
+ buf = pidgin_pixbuf_from_data(
+ (const guchar *)purple_request_field_image_get_buffer(field),
+ purple_request_field_image_get_size(field));
scale = gdk_pixbuf_scale_simple(buf,
purple_request_field_image_get_scale_x(field) * gdk_pixbuf_get_width(buf),
purple_request_field_image_get_scale_y(field) * gdk_pixbuf_get_height(buf),
GDK_INTERP_BILINEAR);
widget = gtk_image_new_from_pixbuf(scale);
- g_object_unref(G_OBJECT(loader));
+ g_object_unref(G_OBJECT(buf));
g_object_unref(G_OBJECT(scale));
return widget;
@@ -1132,7 +1122,7 @@ create_list_field(PurpleRequestField *field)
GdkPixbuf* pixbuf = NULL;
if (icon_path)
- pixbuf = gdk_pixbuf_new_from_file(icon_path, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file(icon_path);
gtk_list_store_set(store, &iter,
0, purple_request_field_list_get_data(field, text),
diff --git a/pidgin/gtksmiley.c b/pidgin/gtksmiley.c
index 9d6c8c20c1..2ea41edc14 100644
--- a/pidgin/gtksmiley.c
+++ b/pidgin/gtksmiley.c
@@ -332,7 +332,7 @@ static void do_add_file_cb(const char *filename, gpointer data)
g_free(s->filename);
s->filename = g_strdup(filename);
- pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE);
gtk_image_set_from_pixbuf(GTK_IMAGE(s->smiley_image), pixbuf);
if (pixbuf)
g_object_unref(G_OBJECT(pixbuf));
@@ -690,7 +690,6 @@ smiley_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
FILE *f;
gchar *path;
size_t wc;
- GError *err = NULL;
PidginSmiley *ps;
GdkPixbuf *image;
@@ -709,13 +708,11 @@ smiley_got_url(PurpleUtilFetchUrlData *url_data, gpointer user_data,
}
fclose(f);
- image = gdk_pixbuf_new_from_file(path, &err);
+ image = pidgin_pixbuf_new_from_file(path);
g_unlink(path);
g_free(path);
- if (err) {
- g_error_free(err);
+ if (!image)
return;
- }
ps = pidgin_smiley_edit(dialog->window, NULL);
pidgin_smiley_editor_set_image(ps, image);
diff --git a/pidgin/gtkstatusbox.c b/pidgin/gtkstatusbox.c
index 79ad9fb4fb..ff75b29c19 100644
--- a/pidgin/gtkstatusbox.c
+++ b/pidgin/gtkstatusbox.c
@@ -2225,22 +2225,45 @@ pidgin_status_box_redisplay_buddy_icon(PidginStatusBox *status_box)
if (status_box->buddy_icon_img != NULL)
{
- GdkPixbuf *buf, *scale;
- int scale_width, scale_height;
- GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+ GdkPixbufLoader *loader;
+ GError *error = NULL;
+
+ loader = gdk_pixbuf_loader_new();
+
g_signal_connect(G_OBJECT(loader), "size-prepared", G_CALLBACK(pixbuf_size_prepared_cb), NULL);
- gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(status_box->buddy_icon_img),
- purple_imgstore_get_size(status_box->buddy_icon_img), NULL);
- gdk_pixbuf_loader_close(loader, NULL);
- buf = gdk_pixbuf_loader_get_pixbuf(loader);
- scale_width = gdk_pixbuf_get_width(buf);
- scale_height = gdk_pixbuf_get_height(buf);
- scale = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height);
- gdk_pixbuf_fill(scale, 0x00000000);
- gdk_pixbuf_copy_area(buf, 0, 0, scale_width, scale_height, scale, 0, 0);
- if (pidgin_gdk_pixbuf_is_opaque(scale))
- pidgin_gdk_pixbuf_make_round(scale);
- status_box->buddy_icon = scale;
+ if (!gdk_pixbuf_loader_write(loader,
+ purple_imgstore_get_data(status_box->buddy_icon_img),
+ purple_imgstore_get_size(status_box->buddy_icon_img),
+ &error) || error)
+ {
+ purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_write() "
+ "failed with size=%zu: %s\n",
+ purple_imgstore_get_size(status_box->buddy_icon_img),
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ } else if (!gdk_pixbuf_loader_close(loader, &error) || error) {
+ purple_debug_warning("gtkstatusbox", "gdk_pixbuf_loader_close() "
+ "failed for image of size %zu: %s\n",
+ purple_imgstore_get_size(status_box->buddy_icon_img),
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ } else {
+ GdkPixbuf *buf, *scale;
+ int scale_width, scale_height;
+
+ buf = gdk_pixbuf_loader_get_pixbuf(loader);
+ scale_width = gdk_pixbuf_get_width(buf);
+ scale_height = gdk_pixbuf_get_height(buf);
+ scale = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height);
+ gdk_pixbuf_fill(scale, 0x00000000);
+ gdk_pixbuf_copy_area(buf, 0, 0, scale_width, scale_height, scale, 0, 0);
+ if (pidgin_gdk_pixbuf_is_opaque(scale))
+ pidgin_gdk_pixbuf_make_round(scale);
+ status_box->buddy_icon = scale;
+ }
+
g_object_unref(loader);
}
diff --git a/pidgin/gtkutils.c b/pidgin/gtkutils.c
index b1b1e20356..d4aadf82e8 100644
--- a/pidgin/gtkutils.c
+++ b/pidgin/gtkutils.c
@@ -615,7 +615,7 @@ pidgin_create_prpl_icon_from_prpl(PurplePlugin *prpl, PidginPrplIconSize size, P
tmp, NULL);
g_free(tmp);
- pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file(filename);
g_free(filename);
return pixbuf;
@@ -704,7 +704,7 @@ create_protocols_menu(const char *default_proto_id)
"16", "google-talk.png", NULL);
GtkWidget *item;
- pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file(filename);
g_free(filename);
gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
@@ -723,7 +723,7 @@ create_protocols_menu(const char *default_proto_id)
"16", "facebook.png", NULL);
GtkWidget *item;
- pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+ pixbuf = pidgin_pixbuf_new_from_file(filename);
g_free(filename);
gtk_menu_shell_append(GTK_MENU_SHELL(aop_menu->menu),
@@ -1593,7 +1593,7 @@ pidgin_dnd_file_manage(GtkSelectionData *sd, PurpleAccount *account, const char
}
/* Are we dealing with an image? */
- pb = gdk_pixbuf_new_from_file(filename, NULL);
+ pb = pidgin_pixbuf_new_from_file(filename);
if (pb) {
_DndData *data = g_malloc(sizeof(_DndData));
gboolean ft = FALSE, im = FALSE;
@@ -2265,7 +2265,7 @@ icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog)
filename = gtk_file_chooser_get_preview_filename(
GTK_FILE_CHOOSER(dialog->icon_filesel));
- if (!filename || g_stat(filename, &st) || !(pixbuf = gdk_pixbuf_new_from_file(filename, NULL)))
+ if (!filename || g_stat(filename, &st) || !(pixbuf = pidgin_pixbuf_new_from_file(filename)))
{
gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL);
gtk_label_set_markup(GTK_LABEL(dialog->icon_text), "");
@@ -3086,17 +3086,134 @@ gboolean pidgin_auto_parent_window(GtkWidget *widget)
#endif
}
-GdkPixbuf * pidgin_pixbuf_from_imgstore(PurpleStoredImage *image)
+static GObject *pidgin_pixbuf_from_data_helper(const guchar *buf, gsize count, gboolean animated)
+{
+ GObject *pixbuf;
+ GdkPixbufLoader *loader;
+ GError *error = NULL;
+
+ loader = gdk_pixbuf_loader_new();
+
+ if (!gdk_pixbuf_loader_write(loader, buf, count, &error) || error) {
+ purple_debug_warning("gtkutils", "gdk_pixbuf_loader_write() "
+ "failed with size=%zu: %s\n", count,
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ g_object_unref(G_OBJECT(loader));
+ return NULL;
+ }
+
+ if (!gdk_pixbuf_loader_close(loader, &error) || error) {
+ purple_debug_warning("gtkutils", "gdk_pixbuf_loader_close() "
+ "failed for image of size %zu: %s\n", count,
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ g_object_unref(G_OBJECT(loader));
+ return NULL;
+ }
+
+ if (animated)
+ pixbuf = G_OBJECT(gdk_pixbuf_loader_get_animation(loader));
+ else
+ pixbuf = G_OBJECT(gdk_pixbuf_loader_get_pixbuf(loader));
+ if (!pixbuf) {
+ purple_debug_warning("gtkutils", "%s() returned NULL for image "
+ "of size %zu\n",
+ animated ? "gdk_pixbuf_loader_get_animation"
+ : "gdk_pixbuf_loader_get_pixbuf", count);
+ g_object_unref(G_OBJECT(loader));
+ return NULL;
+ }
+
+ g_object_ref(pixbuf);
+ g_object_unref(G_OBJECT(loader));
+
+ return pixbuf;
+}
+
+GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count)
+{
+ return GDK_PIXBUF(pidgin_pixbuf_from_data_helper(buf, count, FALSE));
+}
+
+GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count)
+{
+ return GDK_PIXBUF_ANIMATION(pidgin_pixbuf_from_data_helper(buf, count, TRUE));
+}
+
+GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image)
+{
+ return pidgin_pixbuf_from_data(purple_imgstore_get_data(image),
+ purple_imgstore_get_size(image));
+}
+
+GdkPixbuf *pidgin_pixbuf_new_from_file(const gchar *filename)
+{
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+
+ pixbuf = gdk_pixbuf_new_from_file(filename, &error);
+ if (!pixbuf || error) {
+ purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file() "
+ "returned %s for file %s: %s\n",
+ pixbuf ? "something" : "nothing",
+ filename,
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ if (pixbuf)
+ g_object_unref(G_OBJECT(pixbuf));
+ return NULL;
+ }
+
+ return pixbuf;
+}
+
+GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height)
+{
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+
+ pixbuf = gdk_pixbuf_new_from_file_at_size(filename,
+ width, height, &error);
+ if (!pixbuf || error) {
+ purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file_at_size() "
+ "returned %s for file %s: %s\n",
+ pixbuf ? "something" : "nothing",
+ filename,
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ if (pixbuf)
+ g_object_unref(G_OBJECT(pixbuf));
+ return NULL;
+ }
+
+ return pixbuf;
+}
+
+GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio)
{
GdkPixbuf *pixbuf;
- GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
- gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(image),
- purple_imgstore_get_size(image), NULL);
- gdk_pixbuf_loader_close(loader, NULL);
- pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
- if (pixbuf)
- g_object_ref(pixbuf);
- g_object_unref(loader);
+ GError *error = NULL;
+
+ pixbuf = gdk_pixbuf_new_from_file_at_scale(filename,
+ width, height, preserve_aspect_ratio, &error);
+ if (!pixbuf || error) {
+ purple_debug_warning("gtkutils", "gdk_pixbuf_new_from_file_at_scale() "
+ "returned %s for file %s: %s\n",
+ pixbuf ? "something" : "nothing",
+ filename,
+ error ? error->message : "(no error message)");
+ if (error)
+ g_error_free(error);
+ if (pixbuf)
+ g_object_unref(G_OBJECT(pixbuf));
+ return NULL;
+ }
+
return pixbuf;
}
diff --git a/pidgin/gtkutils.h b/pidgin/gtkutils.h
index cd048728c4..716ec6de81 100644
--- a/pidgin/gtkutils.h
+++ b/pidgin/gtkutils.h
@@ -834,6 +834,32 @@ gboolean pidgin_auto_parent_window(GtkWidget *window);
GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label);
/**
+ * Create a GdkPixbuf from a chunk of image data.
+ *
+ * @param buf The raw binary image data.
+ * @param count The length of buf in bytes.
+ *
+ * @return A GdkPixbuf created from the image data, or NULL if
+ * there was an error parsing the data.
+ *
+ * @since 2.9.0
+ */
+GdkPixbuf *pidgin_pixbuf_from_data(const guchar *buf, gsize count);
+
+/**
+ * Create a GdkPixbufAnimation from a chunk of image data.
+ *
+ * @param buf The raw binary image data.
+ * @param count The length of buf in bytes.
+ *
+ * @return A GdkPixbufAnimation created from the image data, or NULL if
+ * there was an error parsing the data.
+ *
+ * @since 2.9.0
+ */
+GdkPixbufAnimation *pidgin_pixbuf_anim_from_data(const guchar *buf, gsize count);
+
+/**
* Create a GdkPixbuf from a PurpleStoredImage.
*
* @param image A PurpleStoredImage.
@@ -845,6 +871,86 @@ GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, Gtk
GdkPixbuf *pidgin_pixbuf_from_imgstore(PurpleStoredImage *image);
/**
+ * Helper function that calls gdk_pixbuf_new_from_file() and checks both
+ * the return code and the GError and returns NULL if either one failed.
+ *
+ * The gdk-pixbuf documentation implies that it is sufficient to check
+ * the return value of gdk_pixbuf_new_from_file() to determine
+ * whether the image was able to be loaded. However, this is not the case
+ * with gdk-pixbuf 2.23.3 and probably many earlier versions. In some
+ * cases a GdkPixbuf object is returned that will cause some operations
+ * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an
+ * infinite loop.
+ *
+ * This function shouldn't be necessary once Pidgin requires a version of
+ * gdk-pixbuf where the aforementioned bug is fixed. However, it might be
+ * nice to keep this function around for the debug message that it logs.
+ *
+ * @param filename Name of file to load, in the GLib file name encoding
+ *
+ * @return The GdkPixbuf if successful. Otherwise NULL is returned and
+ * a warning is logged.
+ *
+ * @since 2.9.0
+ */
+GdkPixbuf *pidgin_pixbuf_new_from_file(const char *filename);
+
+/**
+ * Helper function that calls gdk_pixbuf_new_from_file_at_size() and checks
+ * both the return code and the GError and returns NULL if either one failed.
+ *
+ * The gdk-pixbuf documentation implies that it is sufficient to check
+ * the return value of gdk_pixbuf_new_from_file_at_size() to determine
+ * whether the image was able to be loaded. However, this is not the case
+ * with gdk-pixbuf 2.23.3 and probably many earlier versions. In some
+ * cases a GdkPixbuf object is returned that will cause some operations
+ * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an
+ * infinite loop.
+ *
+ * This function shouldn't be necessary once Pidgin requires a version of
+ * gdk-pixbuf where the aforementioned bug is fixed. However, it might be
+ * nice to keep this function around for the debug message that it logs.
+ *
+ * @param filename Name of file to load, in the GLib file name encoding
+ * @param width The width the image should have or -1 to not constrain the width
+ * @param height The height the image should have or -1 to not constrain the height
+ *
+ * @return The GdkPixbuf if successful. Otherwise NULL is returned and
+ * a warning is logged.
+ *
+ * @since 2.9.0
+ */
+GdkPixbuf *pidgin_pixbuf_new_from_file_at_size(const char *filename, int width, int height);
+
+/**
+ * Helper function that calls gdk_pixbuf_new_from_file_at_scale() and checks
+ * both the return code and the GError and returns NULL if either one failed.
+ *
+ * The gdk-pixbuf documentation implies that it is sufficient to check
+ * the return value of gdk_pixbuf_new_from_file_at_scale() to determine
+ * whether the image was able to be loaded. However, this is not the case
+ * with gdk-pixbuf 2.23.3 and probably many earlier versions. In some
+ * cases a GdkPixbuf object is returned that will cause some operations
+ * (like gdk_pixbuf_scale_simple()) to rapidly consume memory in an
+ * infinite loop.
+ *
+ * This function shouldn't be necessary once Pidgin requires a version of
+ * gdk-pixbuf where the aforementioned bug is fixed. However, it might be
+ * nice to keep this function around for the debug message that it logs.
+ *
+ * @param filename Name of file to load, in the GLib file name encoding
+ * @param width The width the image should have or -1 to not constrain the width
+ * @param height The height the image should have or -1 to not constrain the height
+ * @param preserve_aspect_ratio TRUE to preserve the image's aspect ratio
+ *
+ * @return The GdkPixbuf if successful. Otherwise NULL is returned and
+ * a warning is logged.
+ *
+ * @since 2.9.0
+ */
+GdkPixbuf *pidgin_pixbuf_new_from_file_at_scale(const char *filename, int width, int height, gboolean preserve_aspect_ratio);
+
+/**
* Add scrollbars to a widget
* @param widget The child widget
* @hscrollbar_policy Horizontal scrolling policy