Animated images.
authorChristian Heckendorf <heckendorfc@gmail.com>
Fri, 26 Aug 2016 22:38:51 +0000 (18:38 -0400)
committerChristian Heckendorf <heckendorfc@gmail.com>
Fri, 26 Aug 2016 22:38:51 +0000 (18:38 -0400)
src/layout_image.c
src/options.c
src/typedefs.h

index 41dab8d..9df1b2e 100644 (file)
@@ -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 @@ void layout_image_full_screen_start(LayoutWindow *lw)
        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 @@ void layout_image_full_screen_stop(LayoutWindow *lw)
                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)
@@ -263,6 +269,150 @@ static gboolean layout_image_slideshow_continue_check(LayoutWindow *lw)
        return TRUE;
 }
 
+/*
+ *----------------------------------------------------------------------------
+ * 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 @@ static void li_pop_menu_full_screen_cb(GtkWidget *widget, gpointer data)
        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 @@ static GtkWidget *layout_image_pop_menu(LayoutWindow *lw)
                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 @@ void layout_image_set_fd(LayoutWindow *lw, FileData *fd)
        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)
index 2f11320..f5ffb8e 100644 (file)
@@ -238,6 +238,7 @@ LayoutOptions *init_layout_options(LayoutOptions *options)
        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;
 }
 
index 79bf1fd..678a149 100644 (file)
@@ -248,6 +248,8 @@ typedef enum {
 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 @@ struct _ThumbLoader
        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 @@ struct _LayoutOptions
        StartUpPath startup_path;
 
        gboolean exit_on_close;
+
+       gboolean animate;
 };
 
 struct _LayoutWindow
@@ -726,6 +741,8 @@ struct _LayoutWindow
 //     gint bar_width;
 
        GtkWidget *exif_window;
+
+       AnimationData *animation;
 };
 
 struct _ViewDir