empathy r1682 - trunk/libempathy-gtk



Author: xclaesse
Date: Tue Nov 11 15:26:25 2008
New Revision: 1682
URL: http://svn.gnome.org/viewvc/empathy?rev=1682&view=rev

Log:
Implement scaling and converting avatars before sending.

Modified:
   trunk/libempathy-gtk/empathy-avatar-chooser.c

Modified: trunk/libempathy-gtk/empathy-avatar-chooser.c
==============================================================================
--- trunk/libempathy-gtk/empathy-avatar-chooser.c	(original)
+++ trunk/libempathy-gtk/empathy-avatar-chooser.c	Tue Nov 11 15:26:25 2008
@@ -49,15 +49,13 @@
 
 	gulong ready_handler_id;
 
-	gchar *image_data;
-	gsize  image_data_size;
-	gchar *mime_type;
+	EmpathyAvatar *avatar;
 } EmpathyAvatarChooserPriv;
 
 static void       avatar_chooser_finalize              (GObject              *object);
 static void       avatar_chooser_set_account           (EmpathyAvatarChooser *self,
 							McAccount            *account);
-static void       avatar_chooser_set_image_from_data   (EmpathyAvatarChooser *chooser,
+static void       avatar_chooser_set_image             (EmpathyAvatarChooser *chooser,
 							gchar                *data,
 							gsize                 size,
 							gboolean              set_locally);
@@ -254,8 +252,9 @@
 	avatar_chooser_set_account (EMPATHY_AVATAR_CHOOSER (object), NULL);
 	g_assert (priv->account == NULL && priv->tp_contact_factory == NULL);
 
-	g_free (priv->image_data);
-	g_free (priv->mime_type);
+	if (priv->avatar != NULL) {
+		empathy_avatar_unref (priv->avatar);
+	}
 
 	G_OBJECT_CLASS (empathy_avatar_chooser_parent_class)->finalize (object);
 }
@@ -310,47 +309,248 @@
 }
 
 
-static void
-avatar_chooser_set_image (EmpathyAvatarChooser *chooser,
-			  GdkPixbuf            *pixbuf,
-			  gchar                *image_data,
-			  gsize                 image_data_size,
-			  gchar                *mime_type,
-			  gboolean              set_locally)
+static gboolean
+str_in_strv (gchar  *str,
+	     gchar **strv)
 {
-	EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser);
-	GtkWidget                *image;
-	GdkPixbuf                *pixbuf_view = NULL;
+	if (strv == NULL)
+		return FALSE;
+	while (*strv != NULL)
+	{
+		if (g_str_equal (str, *strv))
+			return TRUE;
+		strv++;
+	}
+	return FALSE;
+}
+
+
+/* The caller must free the strings stored in satisfactory_format_name and
+ * satisfactory_mime_type.
+ */
+static gboolean
+can_satisfy_mime_type_requirements (gchar **accepted_mime_types,
+				    gchar **satisfactory_format_name,
+				    gchar **satisfactory_mime_type)
+{
+	GSList *formats;
+	GSList *i;
+	gchar **j;
+	gboolean done = FALSE;
+
+	if (accepted_mime_types == NULL || *accepted_mime_types == NULL)
+		return FALSE;
+
+	g_assert (satisfactory_format_name != NULL);
+	g_assert (satisfactory_mime_type != NULL);
+
+	formats = gdk_pixbuf_get_formats ();
+
+	for (i = formats; !done && i != NULL; i = i->next) {
+		GdkPixbufFormat *format = i->data;
+		gchar **format_mime_types;
+
+		if (!gdk_pixbuf_format_is_writable (format))
+			continue;
+
+		format_mime_types = gdk_pixbuf_format_get_mime_types (format);
+		for (j = accepted_mime_types; *j != NULL; j++) {
+			if (str_in_strv (*j, format_mime_types)) {
+				*satisfactory_format_name = gdk_pixbuf_format_get_name (format);
+				*satisfactory_mime_type = g_strdup (*j);
+				done = TRUE;
+				break;
+			}
+		}
+		g_strfreev (format_mime_types);
+	}
+
+	g_slist_free (formats);
 
-	g_free (priv->image_data);
-	g_free (priv->mime_type);
+	return done;
+}
+
+
+static EmpathyAvatar *
+avatar_chooser_convert (EmpathyAvatarChooser *chooser,
+			GdkPixbuf            *pixbuf_scaled,
+			gchar               **mime_types,
+			gsize                 max_size)
+{
+	gchar         *format_name = NULL, *new_mime_type = NULL;
+
+	gchar         *converted_image_data = NULL;
+	gsize          converted_image_size = 0;
+	EmpathyAvatar *converted_avatar = NULL;
+
+	gboolean       saved;
+	GError        *error = NULL;
+
+	if (!can_satisfy_mime_type_requirements (mime_types, &format_name,
+						 &new_mime_type)) {
+		DEBUG ("Mon dieu! Can't convert to any acceptable format!");
+		return NULL;
+	}
+
+	saved = gdk_pixbuf_save_to_buffer (pixbuf_scaled, &converted_image_data,
+		&converted_image_size, format_name, &error, NULL);
+	g_free (format_name);
+
+	if (!saved) {
+		DEBUG ("Couldn't convert image: %s", error->message);
+		g_error_free (error);
+
+		g_free (new_mime_type);
+		return NULL;
+	}
 
-	priv->image_data = NULL;
-	priv->image_data_size = 0;
-	priv->mime_type = NULL;
-
-	if (pixbuf) {
-		priv->image_data = image_data;
-		priv->image_data_size = image_data_size;
-		priv->mime_type = mime_type;
-
-		pixbuf_view = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_VIEW);
-		image = gtk_image_new_from_pixbuf (pixbuf_view);
-		g_object_unref (pixbuf_view);
+	/* Takes ownership of new_mime_type */
+	converted_avatar = empathy_avatar_new (converted_image_data,
+		converted_image_size, new_mime_type, NULL);
+
+	if (max_size > 0 && converted_avatar->len > max_size) {
+		/* TODO: We could try converting to a different format; in
+		 *       particular, try converting to jpeg with increasingly
+		 *       high compression (if jpeg is supported). Not sure how
+		 *       much we care.
+		 */
+		DEBUG ("Converted the image, but the new filesize is too big");
+
+		empathy_avatar_unref (converted_avatar);
+		converted_avatar = NULL;
+	}
+
+	return converted_avatar;
+}
+
+
+static EmpathyAvatar *
+avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *chooser,
+					GdkPixbuf            *pixbuf,
+					EmpathyAvatar        *avatar)
+{
+	EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser);
+	EmpathyTpContactFactory  *tp_cf = priv->tp_contact_factory;
+
+	gint                      max_width = 0, max_height = 0, max_size = 0;
+	gchar                   **mime_types = NULL;
+	gboolean                  needs_conversion = FALSE;
+
+	GdkPixbuf                *pixbuf_scaled = NULL;
+
+	/* This should only be called if the user is setting a new avatar,
+	 * which should only be allowed once the avatar requirements have been
+	 * discovered.
+	 */
+	g_return_val_if_fail (tp_cf != NULL, NULL);
+	g_return_val_if_fail (empathy_tp_contact_factory_is_ready (tp_cf),
+		NULL);
+
+	g_object_get (tp_cf,
+		"avatar-mime-types", &mime_types, /* Needs g_strfreev-ing */
+		"avatar-max-width", &max_width,
+		"avatar-max-height", &max_height,
+		"avatar-max-size", &max_size,
+		NULL);
+
+	/* If the avatar's not already the right type, it needs converting. */
+	if (!str_in_strv (avatar->format, mime_types))
+		needs_conversion = TRUE;
+
+	/* If scaling down the pixbuf to fit the dimensions yields a new
+	 * pixbuf, then it needed scaling. if it's the same pixbuf, it did not.
+	 */
+	pixbuf_scaled = empathy_pixbuf_scale_down_if_necessary (pixbuf,
+		MIN(max_width, max_height));
+	if (pixbuf_scaled != pixbuf)
+		needs_conversion = TRUE;
+
+	if (max_size > 0 && avatar->len > max_size)
+		needs_conversion = TRUE;
+
+	if (needs_conversion) {
+		avatar = avatar_chooser_convert (chooser, pixbuf_scaled,
+			mime_types, max_size);
 	} else {
-		image = gtk_image_new_from_icon_name ("stock_person",
-						      GTK_ICON_SIZE_DIALOG);
+		/* Just return another reference to the avatar passed in. */
+		avatar = empathy_avatar_ref (avatar);
 	}
 
+	g_object_unref (pixbuf_scaled);
+	g_strfreev (mime_types);
+	return avatar;
+}
+
+
+static void
+avatar_chooser_clear_image (EmpathyAvatarChooser *chooser)
+{
+	EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser);
+	GtkWidget *image;
+
+	if (priv->avatar == NULL)
+		return;
+
+	empathy_avatar_unref (priv->avatar);
+	priv->avatar = NULL;
+
+	image = gtk_image_new_from_icon_name ("stock_person", GTK_ICON_SIZE_DIALOG);
 	gtk_button_set_image (GTK_BUTTON (chooser), image);
 	g_signal_emit (chooser, signals[CHANGED], 0);
 }
 
 static void
-avatar_chooser_clear_image (EmpathyAvatarChooser *chooser,
-			    gboolean              set_locally)
+avatar_chooser_set_image (EmpathyAvatarChooser *chooser,
+			  gchar                *data,
+			  gsize                 size,
+			  gboolean              set_locally)
 {
-	avatar_chooser_set_image (chooser, NULL, NULL, 0, NULL, set_locally);
+	EmpathyAvatarChooserPriv *priv = GET_PRIV (chooser);
+
+	GdkPixbuf     *pixbuf, *pixbuf_view;
+	GtkWidget     *image;
+	EmpathyAvatar *avatar = NULL;
+	gchar         *mime_type = NULL;
+
+	if (data == NULL || size == 0) {
+		avatar_chooser_clear_image (chooser);
+		g_free (data);
+		return;
+	}
+
+	pixbuf = empathy_pixbuf_from_data (data, size, &mime_type);
+	if (pixbuf == NULL) {
+		g_free (data);
+		return;
+	}
+
+	/* avatar takes ownership of data and mime_type */
+	avatar = empathy_avatar_new (data, size, mime_type, NULL);
+
+	if (set_locally) {
+		EmpathyAvatar *conv = avatar_chooser_maybe_convert_and_scale (
+			chooser, pixbuf, avatar);
+		empathy_avatar_unref (avatar);
+		avatar = conv;
+	}
+
+	if (avatar == NULL) {
+		/* An error occured; don't change the avatar. */
+		return;
+	}
+
+	if (priv->avatar != NULL)
+		empathy_avatar_unref (priv->avatar);
+	priv->avatar = avatar;
+
+	pixbuf_view = empathy_pixbuf_scale_down_if_necessary (pixbuf, AVATAR_SIZE_VIEW);
+	image = gtk_image_new_from_pixbuf (pixbuf_view);
+
+	gtk_button_set_image (GTK_BUTTON (chooser), image);
+	g_signal_emit (chooser, signals[CHANGED], 0);
+
+	g_object_unref (pixbuf_view);
+	g_object_unref (pixbuf);
 }
 
 static void
@@ -371,23 +571,7 @@
 		return;
 	}
 
-	avatar_chooser_set_image_from_data (chooser, image_data, image_size, TRUE);
-}
-
-static void
-avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser,
-				    gchar                *data,
-				    gsize                 size,
-				    gboolean              set_locally)
-{
-	GdkPixbuf       *pixbuf;
-	gchar           *mime_type = NULL;
-
-	pixbuf = empathy_pixbuf_from_data (data, size, &mime_type);
-	avatar_chooser_set_image (chooser, pixbuf, data, size, mime_type, set_locally);
-	if (pixbuf) {
-		g_object_unref (pixbuf);
-	}
+	avatar_chooser_set_image (chooser, image_data, image_size, TRUE);
 }
 
 static gboolean
@@ -517,10 +701,10 @@
 								  data, size,
 								  NULL, NULL);
 				if (bytes_read != -1) {
-					avatar_chooser_set_image_from_data (chooser,
-									    data,
-									    (gsize) bytes_read,
-									    TRUE);
+					avatar_chooser_set_image (chooser,
+								  data,
+								  (gsize) bytes_read,
+								  TRUE);
 					handled = TRUE;
 				}
 
@@ -591,7 +775,8 @@
 		}
 	}
 	else if (response == GTK_RESPONSE_NO) {
-		avatar_chooser_clear_image (chooser, TRUE);
+		/* This corresponds to "No Image", not to "Cancel" */
+		avatar_chooser_clear_image (chooser);
 	}
 
 	gtk_widget_destroy (widget);
@@ -698,6 +883,9 @@
 			     NULL);
 }
 
+/* TODO: when the avatar passed to this function actually can be relied upon to
+ * contain a mime type, we can probably just ref it and store it.
+ */
 void
 empathy_avatar_chooser_set (EmpathyAvatarChooser *chooser,
 			    EmpathyAvatar        *avatar)
@@ -706,9 +894,9 @@
 
 	if (avatar != NULL) {
 		gchar *data = g_memdup (avatar->data, avatar->len);
-		avatar_chooser_set_image_from_data (chooser, data, avatar->len, FALSE);
+		avatar_chooser_set_image (chooser, data, avatar->len, FALSE);
 	} else {
-		avatar_chooser_clear_image (chooser, FALSE);
+		avatar_chooser_clear_image (chooser);
 	}
 }
 
@@ -724,14 +912,26 @@
 
 	priv = GET_PRIV (chooser);
 
-	if (data) {
-		*data = priv->image_data;
-	}
-	if (data_size) {
-		*data_size = priv->image_data_size;
-	}
-	if (mime_type) {
-		*mime_type = priv->mime_type;
+	if (priv->avatar != NULL) {
+		if (data != NULL) {
+			*data = priv->avatar->data;
+		}
+		if (data_size != NULL) {
+			*data_size = priv->avatar->len;
+		}
+		if (mime_type != NULL) {
+			*mime_type = priv->avatar->format;
+		}
+	} else {
+		if (data != NULL) {
+			*data = NULL;
+		}
+		if (data_size != NULL) {
+			*data_size = 0;
+		}
+		if (mime_type != NULL) {
+			*mime_type = NULL;
+		}
 	}
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]