Commit a8133caa authored by Raoul Berger's avatar Raoul Berger Committed by Felix Riemann

Add support for animated images

Plays animated images in eog as supported by GdkPixbufAnimation.
Fixes bgo#335093.
parent 10d243bb
......@@ -35,6 +35,9 @@ struct _EogImagePrivate {
EogImageMetadataStatus metadata_status;
GdkPixbuf *image;
GdkPixbufAnimation *anim;
GdkPixbufAnimationIter *anim_iter;
gboolean is_playing;
GdkPixbuf *thumbnail;
gint width;
......
......@@ -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;
}
......@@ -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__ */
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment