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

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