changeset 2396:00faf5a63f6c

Animated images.
author Christian Heckendorf <heckendorfc@gmail.com>
date Fri, 26 Aug 2016 18:38:51 -0400
parents a22a815359f9
children e371af9d2caf
files src/layout_image.c src/options.c src/typedefs.h
diffstat 3 files changed, 178 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/layout_image.c	Wed Aug 24 17:28:23 2016 +0100
+++ b/src/layout_image.c	Fri Aug 26 18:38:51 2016 -0400
@@ -49,6 +49,9 @@
 
 static GtkWidget *layout_image_pop_menu(LayoutWindow *lw);
 static void layout_image_set_buttons(LayoutWindow *lw);
+void layout_image_animate_stop(LayoutWindow *lw);
+gboolean layout_image_animate_new_file(LayoutWindow *lw);
+void layout_image_animate_update_image(LayoutWindow *lw);
 
 /*
  *----------------------------------------------------------------------------
@@ -100,6 +103,7 @@
 	layout_actions_add_window(lw, lw->full_screen->window);
 
 	image_osd_copy_status(lw->full_screen->normal_imd, lw->image);
+	layout_image_animate_update_image(lw);
 }
 
 void layout_image_full_screen_stop(LayoutWindow *lw)
@@ -111,6 +115,8 @@
 		image_osd_copy_status(lw->image, lw->full_screen->normal_imd);
 
 	fullscreen_stop(lw->full_screen);
+
+	layout_image_animate_update_image(lw);
 }
 
 void layout_image_full_screen_toggle(LayoutWindow *lw)
@@ -265,6 +271,150 @@
 
 /*
  *----------------------------------------------------------------------------
+ * Animation
+ *----------------------------------------------------------------------------
+ */
+
+static void image_animation_data_free(AnimationData *fd)
+{
+	if(!fd) return;
+	g_object_unref(fd->iter);
+	g_object_unref(fd->gpa);
+	g_free(fd);
+}
+
+static gboolean animation_should_continue(AnimationData *fd)
+{
+	if (!fd->valid)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean show_next_frame(gpointer data)
+{
+	AnimationData *fd = (AnimationData*)data;
+	int delay;
+	PixbufRenderer *pr;
+
+	if(animation_should_continue(fd)==FALSE)
+		{
+		image_animation_data_free(fd);
+		return FALSE;
+		}
+
+	pr = (PixbufRenderer*)fd->iw->pr;
+
+	if (gdk_pixbuf_animation_iter_advance(fd->iter,NULL)==FALSE)
+		{
+		/* This indicates the animation is complete.
+		   Return FALSE here to disable looping. */
+		}
+
+	fd->gpb = gdk_pixbuf_animation_iter_get_pixbuf(fd->iter);
+	image_change_pixbuf(fd->iw,fd->gpb,pr->zoom,FALSE);
+
+	if (fd->iw->func_update)
+		fd->iw->func_update(fd->iw, fd->iw->data_update);
+
+	delay = gdk_pixbuf_animation_iter_get_delay_time(fd->iter);
+	if (delay!=fd->delay)
+		{
+		if (delay>0) /* Current frame not static. */
+			{
+			fd->delay=delay;
+			g_timeout_add(delay,show_next_frame,fd);
+			}
+		else
+			{
+			image_animation_data_free(fd);
+			}
+		return FALSE;
+		}
+
+	return TRUE;
+}
+
+gboolean layout_image_animate_check(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return FALSE;
+
+	if(!lw->options.animate)
+		{
+		if(lw->animation)
+			{
+			lw->animation->valid = FALSE;
+			lw->animation = NULL;
+			}
+		return FALSE;
+		}
+
+	return TRUE;
+}
+
+void layout_image_animate_stop(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+
+	if(lw->options.animate && lw->animation)
+		{
+		lw->animation->valid = FALSE;
+		lw->animation = NULL;
+		}
+}
+
+void layout_image_animate_update_image(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+
+	if(lw->options.animate && lw->animation)
+		{
+		if (lw->full_screen && lw->image != lw->full_screen->imd)
+			lw->animation->iw = lw->full_screen->imd;
+		else
+			lw->animation->iw = lw->image;
+		}
+}
+
+gboolean layout_image_animate_new_file(LayoutWindow *lw)
+{
+	GError *err=NULL;
+
+	if(!layout_image_animate_check(lw)) return FALSE;
+
+	if(lw->animation) lw->animation->valid = FALSE;
+
+	lw->animation = g_malloc0(sizeof(AnimationData));
+
+	if(!(lw->animation->gpa = gdk_pixbuf_animation_new_from_file(lw->image->image_fd->path,&err)) || err ||
+		gdk_pixbuf_animation_is_static_image(lw->animation->gpa) ||
+		!(lw->animation->iter = gdk_pixbuf_animation_get_iter(lw->animation->gpa,NULL)))
+		{
+		image_animation_data_free(lw->animation);
+		return FALSE;
+		}
+
+	lw->animation->data_adr = lw->image->image_fd;
+	lw->animation->delay = gdk_pixbuf_animation_iter_get_delay_time(lw->animation->iter);
+	lw->animation->valid = TRUE;
+
+	layout_image_animate_update_image(lw);
+
+	g_timeout_add(lw->animation->delay, show_next_frame, lw->animation);
+
+	return TRUE;
+}
+
+void layout_image_animate_toggle(LayoutWindow *lw)
+{
+	if (!lw) return;
+
+	lw->options.animate = !lw->options.animate;
+	layout_image_animate_new_file(lw);
+}
+
+/*
+ *----------------------------------------------------------------------------
  * pop-up menus
  *----------------------------------------------------------------------------
  */
@@ -420,6 +570,13 @@
 	layout_image_full_screen_toggle(lw);
 }
 
+static void li_pop_menu_animate_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_image_animate_toggle(lw);
+}
+
 static void li_pop_menu_hide_cb(GtkWidget *widget, gpointer data)
 {
 	LayoutWindow *lw = data;
@@ -557,6 +714,8 @@
 		menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(li_pop_menu_full_screen_cb), lw);
 		}
 
+	menu_item_add_check(menu, _("_Animate"), lw->options.animate, G_CALLBACK(li_pop_menu_animate_cb), lw);
+
 	menu_item_add_divider(menu);
 
 	item = menu_item_add_check(menu, _("Hide file _list"), lw->options.tools_hidden,
@@ -1002,6 +1161,7 @@
 	layout_list_sync_fd(lw, fd);
 	layout_image_slideshow_continue_check(lw);
 	layout_bars_new_image(lw);
+	layout_image_animate_new_file(lw);
 }
 
 void layout_image_set_with_ahead(LayoutWindow *lw, FileData *fd, FileData *read_ahead_fd)
--- a/src/options.c	Wed Aug 24 17:28:23 2016 +0100
+++ b/src/options.c	Fri Aug 26 18:38:51 2016 -0400
@@ -238,6 +238,7 @@
 	options->image_overlay.histogram_channel = HCHAN_RGB;
 	options->image_overlay.histogram_mode = 1;
 	options->image_overlay.state = OSD_SHOW_NOTHING;
+	options->animate = FALSE;
 	return options;
 }
 
--- a/src/typedefs.h	Wed Aug 24 17:28:23 2016 +0100
+++ b/src/typedefs.h	Fri Aug 26 18:38:51 2016 -0400
@@ -248,6 +248,8 @@
 typedef struct _ImageLoader ImageLoader;
 typedef struct _ThumbLoader ThumbLoader;
 
+typedef struct _AnimationData AnimationData;
+
 typedef struct _CollectInfo CollectInfo;
 typedef struct _CollectionData CollectionData;
 typedef struct _CollectTable CollectTable;
@@ -329,6 +331,17 @@
 	guint idle_done_id; /* event source id */
 };
 
+struct _AnimationData
+{
+	ImageWindow *iw;
+	GdkPixbufAnimation *gpa;
+	GdkPixbufAnimationIter *iter;
+	GdkPixbuf *gpb;
+	FileData *data_adr;
+	guint delay;
+	gboolean valid;
+};
+
 struct _CollectInfo
 {
 	FileData *fd;
@@ -607,6 +620,8 @@
 	StartUpPath startup_path;
 
 	gboolean exit_on_close;
+
+	gboolean animate;
 };
 
 struct _LayoutWindow
@@ -726,6 +741,8 @@
 //	gint bar_width;
 
 	GtkWidget *exif_window;
+
+	AnimationData *animation;
 };
 
 struct _ViewDir