GNOME Bugzilla – Bug 338380
animation playback should have zoom feature
Last modified: 2013-04-20 23:35:27 UTC
When playing animated GIFs, zooming in to check for mistakes comes in handy. I have to use the HTML image tag's width and height attributes to do the same thing. 2x, 4x, and maybe 8x and a custom value could be used. Also, the playback feature should have a speed-control feature to play animations back at a slower speed without having to repeatedly click the "step" button several dozen times for long animations. All that's needed is to multiply the delay time by some value, like 4.
Sure, why not. If someone wants to implement this functionality, we would certainly accept such a change.
This should be easy enough to be done by someone who wants to start contributing to GIMP. Adding the gnome-love keyword. Whoever wants to work on this should definitely base his/her work on the CVS HEAD branch because there have been substantial changes to the plug-in since 2.2.
Zoom functionality would be very nice to have because currently the plug-in doesn't work very well if the image is very large or very small.
I just did some strange test by creating a gigantic animated GIF file. I made it as 1600x1200, which is my system resolution. It's so big and there's no way to resize it, I can't see anything on the bottom. The window itself is so big, I have to move the window around just to be able to close it with the X (I can still right-click on the window on the task bar and close it that way). Unless you run at a resolution of something like 1680x1400 or higher, you won't see the entire window. In this case, adding scroll bars and limiting the window size used by default to 3/4 the resolution is recommended. Of course animated GIFs don't get that big as there's little point in it, except maybe an animated series of screenshots. For very small animated GIFs, it's hard to really pick out the details. Trying to resize the window of the gigantic animated GIF has strange behavior as well, likely related to Windows rather than the GIMP. All resizing the window does is makes the buttons cover more area without affecting the image. Try previewing this test animated GIF I made: http://www.ulillillia.us/temporary/hugeGIFtest.gif
I'd like to take on this feature request for a university course dealing with development processes in open source projects.
Please base your work on a recent development release or on SVN trunk then. Let us know if you need any help.
Created attachment 89648 [details] [review] Patch for animationplay.c adds three popup items for speed control
I implemented a first version for the speed control feature. At the moment it just shows three new items in the popup menu to double, halve and reset the speed in a range from 1/8x to 8x. There is no visual feedback as to which speed is currently set, but the appropriate options are getting insensitive when either being too slow or too fast. I tried to mess around with the toolbar to add a MenuToolButton but did not succeed with this. The zoom feature seems to be more difficult because I see no other option than to resize the raw image data arrays by hand.
Please use a double instead of a float. And perhaps with more actions being added, it would be a good idea to give the actions more meaningful identifiers. "reset" could be anything. It would also be nice to get this into the toolbar, the popup menu is somewhat hard to discover.
The "Reset Playback Speed" action should also be insensitive when the animation is running at original speed. Perhaps we need to add a statusbar. The playback speed and zoom factor could be displayed there then.
Comment on attachment 89648 [details] [review] Patch for animationplay.c Index: animationplay.c =================================================================== --- animationplay.c (Revision 22453) +++ animationplay.c (Arbeitskopie) @@ -62,36 +62,40 @@ gint *nreturn_vals, GimpParam **return_vals); -static void do_playback (void); +static void do_playback (void); -static void window_destroy (GtkWidget *widget); -static void play_callback (GtkToggleAction *action); -static void step_callback (GtkAction *action); -static void rewind_callback (GtkAction *action); -static gboolean repaint_sda (GtkWidget *darea, - GdkEventExpose *event, - gpointer data); -static gboolean repaint_da (GtkWidget *darea, - GdkEventExpose *event, - gpointer data); +static void window_destroy (GtkWidget *widget); +static void play_callback (GtkToggleAction *action); +static void step_callback (GtkAction *action); +static void rewind_callback (GtkAction *action); +static void speed_up_callback (GtkAction *action); +static void speed_down_callback (GtkAction *action); +static void reset_speed_callback (GtkAction *action); +static gboolean repaint_sda (GtkWidget *darea, + GdkEventExpose *event, + gpointer data); +static gboolean repaint_da (GtkWidget *darea, + GdkEventExpose *event, + gpointer data); -static void render_frame (gint32 whichframe); -static void show_frame (void); -static void total_alpha_preview (guchar *ptr); -static void init_preview (void); +static void render_frame (gint32 whichframe); +static void show_frame (void); +static void total_alpha_preview (guchar *ptr); +static void init_preview (void); +static void update_statusbar (void); /* tag util functions*/ -static gint parse_ms_tag (const gchar *str); -static DisposeType parse_disposal_tag (const gchar *str); -static DisposeType get_frame_disposal (guint whichframe); -static guint32 get_frame_duration (guint whichframe); -static gboolean is_disposal_tag (const gchar *str, - DisposeType *disposal, - gint *taglength); -static gboolean is_ms_tag (const gchar *str, - gint *duration, - gint *taglength); +static gint parse_ms_tag (const gchar *str); +static DisposeType parse_disposal_tag (const gchar *str); +static DisposeType get_frame_disposal (guint whichframe); +static guint32 get_frame_duration (guint whichframe); +static gboolean is_disposal_tag (const gchar *str, + DisposeType *disposal, + gint *taglength); +static gboolean is_ms_tag (const gchar *str, + gint *duration, + gint *taglength); const GimpPlugInInfo PLUG_IN_INFO = @@ -125,7 +129,9 @@ static GimpImageBaseType imagetype; static guchar *palette; static gint ncolours; +static gdouble duration_factor = 1.0; + /* for shaping */ typedef struct { @@ -136,6 +142,10 @@ static GtkWidget *shape_window = NULL; static GdkWindow *root_win = NULL; static gboolean detached = FALSE; +static GtkWidget *statusbar = NULL; +static gchar *speed_text = N_("Speed"); +static guint message_context_id = 0; +static guint message_remove_id; MAIN () @@ -450,6 +460,21 @@ "quit", GTK_STOCK_QUIT, NULL, "<control>Q", NULL, G_CALLBACK (close_callback) + }, + { + "speed_up", GTK_STOCK_GO_UP, + N_("Faster"), "<control>L", N_("Increase the speed of the animation"), + G_CALLBACK (speed_up_callback) + }, + { + "speed_down", GTK_STOCK_GO_DOWN, + N_("Slower"), "<control>J", N_("Decrease the speed of the animation"), + G_CALLBACK (speed_down_callback) + }, + { + "reset_speed", GIMP_STOCK_RESET, + N_("Reset speed"), "<control>K", N_("Reset the speed of the animation"), + G_CALLBACK (reset_speed_callback) } }; @@ -494,6 +519,10 @@ " <toolitem action=\"step\" />" " <toolitem action=\"rewind\" />" " <separator />" + " <toolitem action=\"speed_up\" />" + " <toolitem action=\"reset_speed\" />" + " <toolitem action=\"speed_down\" />" + " <separator />" " <toolitem action=\"detach\" />" " <separator name=\"space\" />" " <toolitem action=\"help\" />" @@ -515,6 +544,9 @@ " <menuitem action=\"play\" />" " <menuitem action=\"step\" />" " <menuitem action=\"rewind\" />" + " <menuitem action=\"speed_up\" />" + " <menuitem action=\"reset_speed\" />" + " <menuitem action=\"speed_down\" />" " <separator />" " <menuitem action=\"detach\" />" " <menuitem action=\"close\" />" @@ -535,13 +567,13 @@ build_dialog (GimpImageBaseType basetype, gchar *imagename) { - GtkWidget *toolbar; - GtkWidget *frame; - GtkWidget *vbox; - GtkWidget *abox; - GtkToolItem *item; - GdkCursor *cursor; - gchar *name; + GtkWidget *toolbar; + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *abox; + GtkToolItem *item; + GdkCursor *cursor; + gchar *name; gimp_ui_init (PLUG_IN_BINARY, TRUE); @@ -601,6 +633,11 @@ gtk_box_pack_start (GTK_BOX (vbox), progress, FALSE, FALSE, 0); gtk_widget_show (progress); + statusbar = gtk_statusbar_new (); + gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 0); + gtk_widget_show (statusbar); + update_statusbar (); + if (total_frames < 2) { GtkAction *action; @@ -618,6 +655,10 @@ gtk_action_set_sensitive (action, FALSE); } + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/reset_speed"), + FALSE); + gtk_widget_show (window); /* let's get into shape. */ @@ -1340,12 +1381,13 @@ gtk_main_quit (); } + static gint advance_frame_callback (gpointer data) { remove_timer(); - timer = g_timeout_add (get_frame_duration ((frame_number + 1) % total_frames), + timer = g_timeout_add (get_frame_duration ((frame_number + 1) % total_frames) * duration_factor, advance_frame_callback, NULL); do_step (); @@ -1354,6 +1396,7 @@ return FALSE; } + static void play_callback (GtkToggleAction *action) { @@ -1363,7 +1406,7 @@ playing = gtk_toggle_action_get_active (action); if (playing) - timer = g_timeout_add (get_frame_duration (frame_number), + timer = g_timeout_add (get_frame_duration (frame_number) * duration_factor, advance_frame_callback, NULL); g_object_set (action, @@ -1392,6 +1435,103 @@ show_frame (); } +static void +speed_up_callback (GtkAction *action) +{ + if (duration_factor > 0.125) + duration_factor /= 2.0; + + if (duration_factor <= 0.125) + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/speed_up"), + FALSE); + if (duration_factor == 1.0) + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/reset_speed"), + FALSE); + else + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/reset_speed"), + TRUE); + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/speed_down"), + TRUE); + update_statusbar (); +} + +static void +speed_down_callback (GtkAction *action) +{ + if (duration_factor < 8.0) + duration_factor *= 2.0; + + if (duration_factor >= 8.0) + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/speed_down"), + FALSE); + if (duration_factor == 1.0) + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/reset_speed"), + FALSE); + else + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/reset_speed"), + TRUE); + + + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/speed_up"), + TRUE); + update_statusbar (); +} + +static void +reset_speed_callback (GtkAction *action) +{ + duration_factor = 1.0; + + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/speed_down"), + TRUE); + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/speed_up"), + TRUE); + gtk_action_set_sensitive (gtk_ui_manager_get_action (ui_manager, + "/anim-play-popup/reset_speed"), + FALSE); + update_statusbar (); +} + +static void +update_statusbar (void) +{ + gchar *status_message; + guint speed_number; + + if (message_context_id == 0) + message_context_id = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), + "display the current speed of playback"); + else + gtk_statusbar_pop (GTK_STATUSBAR (statusbar), + message_context_id); + + if (duration_factor > 1.0) + { + speed_number = (guint) duration_factor; + status_message = g_strdup_printf ("%s: 1/%dx", speed_text, speed_number); + } + else + { + speed_number = (guint) 1.0 / duration_factor; + status_message = g_strdup_printf ("%s: %dx", speed_text, speed_number); + } + + message_remove_id = gtk_statusbar_push (GTK_STATUSBAR (statusbar), + message_context_id, + status_message); + g_free (status_message); +} + /* tag util. */ static DisposeType
Oh, sorry about the spammy patch message before. I'm not very accustomed to the functions of bugzilla. Nevertheless, I changed all the things you asked me for. So we got three new actions in the popup menu as well as in the toolbar. I added a status bar which shows the current speed as you proposed.
Please submit the above patch as an attachment.
Created attachment 89735 [details] [review] adds speed control to animationplay.c
To get us further, I have now committed this patch with some modifications. In particular I have removed the speed controls from the toolbar as I found that they take up too much space there. 2007-06-18 Sven Neumann <sven@gimp.org> * plug-ins/common/animationplay.c: applied modified patch from Paul Seidel that adds controls for the playback speed (bug #338380). We can try to improve the plug-in further from here.
Created attachment 90192 [details] UI mockup I suggest that we streamline the UI somewhat. Instead of adding a statusbar below the progressbar, we could have a combo-box next to the progress-bar. A tooltip could indicate that it controls the playback speed. See attached screenshot.
Created attachment 90217 [details] [review] adds a combobox for speed control I added a combobox to the dialog as proposed and removed all references to the statusbar.The active combobox item is automatically changed when using the popup menu and the popup menu entries are sensitive/insensitive depending on the speed chosen with the combobox. The patch is based on the latest SVN copy (Rev 22794).
That looks nice but there's a small problem. Something that I should have already pointed out earlier. You can't reliably compare double values for equality using the == operator. What I suggest you do is to use an integer or perhaps an enum for the various possible playback speeds. Then just change this integer value in the action callbacks. You can then also use a GimpIntComboBox.
Created attachment 90297 [details] [review] uses integer values to differentiate duration factor values With this patch the speed control now uses the index values of the combo box to differentiate between the various duration factor values. The correct value for the factor is now calculated by using "get_duration_factor" when creating the timeout object.
I have applied this patch with some minor modifications. Thanks a lot for your contribution: 2007-06-20 Sven Neumann <sven@gimp.org> * plug-ins/common/animationplay.c: applied patch from Paul Seidel with further improvements to the playback speed control (bug #338380). I think we can now consider the playback speed aspect fixed. Changing the summary accordingly.
Removing "gnome-love" keyword because code has been committed, and it isn't clear the gnome-love is appropriate for the remaining issues.
The summary has been changed after the code has been committed to reflect what still needs to be done. And the keyword is fully appropriate for that.
A zooming feature is indeed very needed. Not only zooming-in, but also out! As I was working on image quite high resolution, actually bigger than my screen, I could not access/see the buttons and the status bar. I will work on this in a few weeks, if none steps before me (looks like so).
I have developed the feature. It includes zooming in/out, default zoom out when starting the plug-in so that the window does not go out of screen, and scrolling so that you can scale bigger than your screen size but still have the window in-screen. I am waiting for the bug 690265 to be confirmed before uploading the patch.
Committed on master. As discussed with Mitch at the time, because of bug 690265, the current version limits down zooming to 50% (zooming out below half does not work). This limitation will be removed when bug 690265 will be fixed. commit 5c38715cce591c80409dd20d037ef28b1a06fcfc Author: Jehan <jehan@girinstud.io> Date: Mon Dec 17 00:26:20 2012 +0900 Bug 338380: zoom and scrolling feature on animation playback plugin.