[eog] Add support for animated images



commit a8133caa982f783c615eec8c8aa7eb6b0e5a9a95
Author: Raoul Berger <contact raoulito info>
Date:   Sun Oct 18 17:31:12 2009 +0200

    Add support for animated images
    
    Plays animated images in eog as supported by GdkPixbufAnimation.
    Fixes bgo#335093.

 src/eog-image-private.h |    3 +
 src/eog-image.c         |  139 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/eog-image.h         |    7 +++
 src/eog-scroll-view.c   |   31 ++++++++++-
 4 files changed, 177 insertions(+), 3 deletions(-)
---
diff --git a/src/eog-image-private.h b/src/eog-image-private.h
index ae50f15..a28c0fa 100644
--- a/src/eog-image-private.h
+++ b/src/eog-image-private.h
@@ -35,6 +35,9 @@ struct _EogImagePrivate {
         EogImageMetadataStatus metadata_status;
 
 	GdkPixbuf        *image;
+	GdkPixbufAnimation     *anim;
+	GdkPixbufAnimationIter *anim_iter;
+	gboolean          is_playing;
 	GdkPixbuf        *thumbnail;
 
 	gint              width;
diff --git a/src/eog-image.c b/src/eog-image.c
index cb60f76..850c8a0 100644
--- a/src/eog-image.c
+++ b/src/eog-image.c
@@ -74,6 +74,7 @@ enum {
 	SIGNAL_SIZE_PREPARED,
 	SIGNAL_THUMBNAIL_CHANGED,
 	SIGNAL_SAVE_PROGRESS,
+	SIGNAL_NEXT_FRAME,
 	SIGNAL_LAST
 };
 
@@ -93,6 +94,18 @@ eog_image_free_mem_private (EogImage *image)
 	if (priv->status == EOG_IMAGE_STATUS_LOADING) {
 		eog_image_cancel_load (image);
 	} else {
+		if (priv->anim_iter != NULL) {
+			g_object_unref (priv->anim_iter);
+			priv->anim_iter = NULL;
+		}
+
+		if (priv->anim != NULL) {
+			g_object_unref (priv->anim);
+			priv->anim = NULL;
+		}
+
+		priv->is_playing = FALSE;
+
 		if (priv->image != NULL) {
 			g_object_unref (priv->image);
 			priv->image = NULL;
@@ -228,6 +241,23 @@ eog_image_class_init (EogImageClass *klass)
 			      g_cclosure_marshal_VOID__FLOAT,
 			      G_TYPE_NONE, 1,
 			      G_TYPE_FLOAT);
+ 	/**
+ 	 * EogImage::next-frame:
+  	 * @img: the object which received the signal.
+	 * @delay: number of milliseconds the current frame will be displayed.
+	 *
+	 * The ::next-frame signal will be emitted each time an animated image
+	 * advances to the next frame.
+	 */
+	signals[SIGNAL_NEXT_FRAME] =
+		g_signal_new ("next-frame",
+			      EOG_TYPE_IMAGE,
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (EogImageClass, save_progress),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__INT,
+			      G_TYPE_NONE, 1,
+			      G_TYPE_INT);
 
 	g_type_class_add_private (object_class, sizeof (EogImagePrivate));
 }
@@ -239,6 +269,9 @@ eog_image_init (EogImage *img)
 
 	img->priv->file = NULL;
 	img->priv->image = NULL;
+	img->priv->anim = NULL;
+	img->priv->anim_iter = NULL;
+	img->priv->is_playing = FALSE;
 	img->priv->thumbnail = NULL;
 	img->priv->width = -1;
 	img->priv->height = -1;
@@ -1050,7 +1083,15 @@ eog_image_real_load (EogImage *img,
 			g_object_unref (priv->image);
 		}
 
-		priv->image = gdk_pixbuf_loader_get_pixbuf (loader);
+		priv->anim = gdk_pixbuf_loader_get_animation (loader);
+
+		if (gdk_pixbuf_animation_is_static_image (priv->anim)) {
+			priv->image = gdk_pixbuf_animation_get_static_image (priv->anim);
+			priv->anim = NULL;
+		} else {
+			priv->anim_iter = gdk_pixbuf_animation_get_iter (priv->anim,NULL);
+			priv->image = gdk_pixbuf_animation_iter_get_pixbuf (priv->anim_iter);
+		}
 
 		if (G_LIKELY (priv->image != NULL)) {
 			g_object_ref (priv->image);
@@ -2011,3 +2052,99 @@ eog_image_is_supported_mime_type (const char *mime_type)
 
 	return (result != NULL);
 }
+
+static gboolean
+eog_image_iter_advance (EogImage *img)
+{
+	EogImagePrivate *priv;
+ 	gboolean new_frame;
+
+	g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
+	g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (img->priv->anim_iter), FALSE);
+
+	priv = img->priv;
+
+	if ((new_frame = gdk_pixbuf_animation_iter_advance (img->priv->anim_iter, NULL)) == TRUE)
+	  {      
+		g_mutex_lock (priv->status_mutex);
+		g_object_unref (priv->image);
+		priv->image = gdk_pixbuf_animation_iter_get_pixbuf (priv->anim_iter);
+	 	g_object_ref (priv->image);
+		/* keep the transformation over time */
+		if (EOG_IS_TRANSFORM (priv->trans)) {
+			GdkPixbuf* transformed = eog_transform_apply (priv->trans, priv->image, NULL);
+			g_object_unref (priv->image);
+			priv->image = transformed;
+			priv->width = gdk_pixbuf_get_width (transformed);
+			priv->height = gdk_pixbuf_get_height (transformed);
+		}      
+		g_mutex_unlock (priv->status_mutex);
+		/* Emit next frame signal so we can update the display */
+		g_signal_emit (img, signals[SIGNAL_NEXT_FRAME], 0,
+			       gdk_pixbuf_animation_iter_get_delay_time (priv->anim_iter));
+	  }
+
+	return new_frame;
+}
+
+/**
+ * eog_image_is_animation:
+ * @img: a #EogImage
+ *
+ * Checks whether a given image is animated.
+ *
+ * Returns: #TRUE if it is an animated image, #FALSE otherwise.
+ * 
+ **/
+gboolean
+eog_image_is_animation (EogImage *img)
+{
+	g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
+	return img->priv->anim != NULL;
+}
+
+static gboolean
+private_timeout (gpointer data)
+{
+	EogImage *img = EOG_IMAGE (data);
+	EogImagePrivate *priv = img->priv;
+
+	if (eog_image_is_animation (img) && 
+	    !g_source_is_destroyed (g_main_current_source ()) &&
+	    priv->is_playing) {
+		while (eog_image_iter_advance (img) != TRUE) {}; /* cpu-sucking ? */
+			g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (priv->anim_iter), private_timeout, img);
+	 		return FALSE;
+ 	}
+	priv->is_playing = FALSE;
+	return FALSE; /* stop playing */
+}
+
+/**
+ * eog_image_start_animation:
+ * @img: a #EogImage
+ *
+ * Starts playing an animated image.
+ *
+ * Returns: %TRUE on success, %FALSE if @img is already playing or isn't an animated image.
+ **/
+gboolean
+eog_image_start_animation (EogImage *img)
+{
+	EogImagePrivate *priv;
+
+	g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
+	priv = img->priv;
+
+	if (!eog_image_is_animation (img) || priv->is_playing)
+		return FALSE;
+
+	g_mutex_lock (priv->status_mutex);
+	g_object_ref (priv->anim_iter);
+	priv->is_playing = TRUE;
+	g_mutex_unlock (priv->status_mutex);
+
+ 	g_timeout_add (gdk_pixbuf_animation_iter_get_delay_time (priv->anim_iter), private_timeout, img);
+
+	return TRUE;
+}
diff --git a/src/eog-image.h b/src/eog-image.h
index 4286d4c..52dffc3 100644
--- a/src/eog-image.h
+++ b/src/eog-image.h
@@ -105,6 +105,9 @@ struct _EogImageClass {
 
 	void (* save_progress)     (EogImage *img,
 				    gfloat    progress);
+
+	void (* next_frame)        (EogImage *img,
+				    gint delay);
 };
 
 GType	          eog_image_get_type	             (void) G_GNUC_CONST;
@@ -192,6 +195,10 @@ GList		 *eog_image_get_supported_mime_types (void);
 
 gboolean          eog_image_is_supported_mime_type   (const char *mime_type);
 
+gboolean          eog_image_is_animation             (EogImage *img);
+
+gboolean          eog_image_start_animation          (EogImage *img);
+
 G_END_DECLS
 
 #endif /* __EOG_IMAGE_H__ */
diff --git a/src/eog-scroll-view.c b/src/eog-scroll-view.c
index 6972077..50b87a7 100644
--- a/src/eog-scroll-view.c
+++ b/src/eog-scroll-view.c
@@ -80,6 +80,7 @@ struct _EogScrollViewPrivate {
 	/* actual image */
 	EogImage *image;
 	guint image_changed_id;
+	guint frame_changed_id;
 	GdkPixbuf *pixbuf;
 
 	/* zoom mode, either ZOOM_MODE_FIT or ZOOM_MODE_FREE */
@@ -162,6 +163,11 @@ free_image_resources (EogScrollView *view)
 		priv->image_changed_id = 0;
 	}
 
+	if (priv->frame_changed_id > 0) {
+		g_signal_handler_disconnect (G_OBJECT (priv->image), priv->frame_changed_id);
+		priv->frame_changed_id = 0;
+	}
+
 	if (priv->image != NULL) {
 		eog_image_data_unref (priv->image);
 		priv->image = NULL;
@@ -816,8 +822,9 @@ request_paint_area (EogScrollView *view, GdkRectangle *area)
 		 * It's sufficient to add only a antitaliased idle update
 		 */
 		priv->progressive_state = PROGRESSIVE_NONE;
-	else
-		/* do nearest neigbor before anti aliased version */
+	else if (!eog_image_is_animation (priv->image))
+		/* do nearest neigbor before anti aliased version,
+		   except for animations to avoid a "blinking" effect. */
 		paint_rectangle (view, &r, GDK_INTERP_NEAREST);
 
 	/* All other interpolation types are delayed.  */
@@ -1888,6 +1895,21 @@ eog_scroll_view_get_zoom_is_max (EogScrollView *view)
 	return DOUBLE_EQUAL (view->priv->zoom, MAX_ZOOM_FACTOR);
 }
 
+static void
+display_next_frame_cb (EogImage *image, gint delay, gpointer data)
+{
+ 	EogScrollViewPrivate *priv;
+	EogScrollView *view;
+
+	if (!EOG_IS_SCROLL_VIEW (data))
+		return;
+
+	view = EOG_SCROLL_VIEW (data);
+	priv = view->priv;
+	priv->pixbuf = eog_image_get_pixbuf (image);
+	gtk_widget_queue_draw (GTK_WIDGET (priv->display)); 
+}
+
 void
 eog_scroll_view_set_image (EogScrollView *view, EogImage *image)
 {
@@ -1932,6 +1954,11 @@ eog_scroll_view_set_image (EogScrollView *view, EogImage *image)
 
 		priv->image_changed_id = g_signal_connect (image, "changed",
 							   (GCallback) image_changed_cb, view);
+		if (eog_image_is_animation (image) == TRUE ) {
+			eog_image_start_animation (image);
+			priv->frame_changed_id = g_signal_connect (image, "next-frame", 
+								    (GCallback) display_next_frame_cb, view);
+		}
 	}
 
 	priv->image = image;



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