changeset 2401:eb6d9ba8a759

Merge image animation * github_heckendorfc_geeqie/animation: Set animation NULL after free on static images. Silence g_object_unref failed assertions. Make animation functions static. Stop animation if no file is showing. Animated images.
author Klaus Ethgen <Klaus@Ethgen.de>
date Sun, 04 Dec 2016 09:33:33 +0100
parents 1c474a4e91a2 (current diff) 0e0926ca9f8b (diff)
children e8439b3f9831 16fcfac12c77 8e9d5cf1edcb
files
diffstat 3 files changed, 179 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/layout_image.c	Sat Dec 03 16:05:32 2016 +0100
+++ b/src/layout_image.c	Sun Dec 04 09:33:33 2016 +0100
@@ -49,6 +49,9 @@
 
 static GtkWidget *layout_image_pop_menu(LayoutWindow *lw);
 static void layout_image_set_buttons(LayoutWindow *lw);
+static void layout_image_animate_stop(LayoutWindow *lw);
+static gboolean layout_image_animate_new_file(LayoutWindow *lw);
+static 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,151 @@
 
 /*
  *----------------------------------------------------------------------------
+ * Animation
+ *----------------------------------------------------------------------------
+ */
+
+static void image_animation_data_free(AnimationData *fd)
+{
+	if(!fd) return;
+	if(fd->iter) g_object_unref(fd->iter);
+	if(fd->gpa) 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;
+}
+
+static gboolean layout_image_animate_check(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return FALSE;
+
+	if(!lw->options.animate || lw->image->image_fd == NULL)
+		{
+		if(lw->animation)
+			{
+			lw->animation->valid = FALSE;
+			lw->animation = NULL;
+			}
+		return FALSE;
+		}
+
+	return TRUE;
+}
+
+static 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;
+		}
+}
+
+static 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;
+		}
+}
+
+static 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);
+		lw->animation = NULL;
+		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;
+}
+
+static 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 +571,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 +715,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 +1162,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	Sat Dec 03 16:05:32 2016 +0100
+++ b/src/options.c	Sun Dec 04 09:33:33 2016 +0100
@@ -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	Sat Dec 03 16:05:32 2016 +0100
+++ b/src/typedefs.h	Sun Dec 04 09:33:33 2016 +0100
@@ -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