[eog] Add support for animated images
- From: Felix Riemann <friemann src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [eog] Add support for animated images
- Date: Sun, 18 Oct 2009 15:36:25 +0000 (UTC)
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]