[gimp/wip/animation: 46/197] plug-ins: reorder all functions in animation-play...
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/wip/animation: 46/197] plug-ins: reorder all functions in animation-play...
- Date: Sat, 7 Oct 2017 03:02:16 +0000 (UTC)
commit 974ba1c4f41fb621eaa15f406d059c42b26f5b8a
Author: Jehan <jehan girinstud io>
Date: Mon Sep 1 00:57:37 2014 +0200
plug-ins: reorder all functions in animation-play...
and make sure they all have a declaration.
plug-ins/common/animation-play.c | 2815 +++++++++++++++++++-------------------
1 files changed, 1425 insertions(+), 1390 deletions(-)
---
diff --git a/plug-ins/common/animation-play.c b/plug-ins/common/animation-play.c
index a4c5fc4..664b8ab 100644
--- a/plug-ins/common/animation-play.c
+++ b/plug-ins/common/animation-play.c
@@ -160,25 +160,62 @@ static void init_frame_numbers (AnimationPlayDialog *dialog,
gint *layers,
gint num_layers);
static gboolean init_frames (AnimationPlayDialog *dialog);
+static void rec_init_frames (AnimationPlayDialog *dialog,
+ gint32 frames_image_id,
+ gint32 layer,
+ GList *previous_frames,
+ gint image_width,
+ gint image_height);
static void init_ui (AnimationPlayDialog *dialog,
gchar *imagename);
+static GtkUIManager * ui_manager_new (AnimationPlayDialog *dialog);
static void refresh_dialog (AnimationPlayDialog *dialog);
static void update_ui_sensitivity (AnimationPlayDialog *dialog);
-static void update_scale (AnimationPlayDialog *dialog,
- gdouble scale);
/* All callbacks. */
+static void close_callback (GtkAction *action,
+ AnimationPlayDialog *dialog);
+static void help_callback (GtkAction *action,
+ AnimationPlayDialog *dialog);
+static void window_destroy (GtkWidget *widget,
+ AnimationPlayDialog *dialog);
static gboolean popup_menu (GtkWidget *widget,
AnimationPlayDialog *dialog);
-static gboolean button_press (GtkWidget *widget,
+
+static gboolean adjustment_pressed (GtkWidget *widget,
+ GdkEventButton *event,
+ AnimationPlayDialog *dialog);
+static gboolean da_button_press (GtkWidget *widget,
GdkEventButton *event,
AnimationPlayDialog *dialog);
static void da_size_callback (GtkWidget *widget,
GtkAllocation *allocation,
AnimationPlayDialog *dialog);
+static gboolean shape_pressed (GtkWidget *widget,
+ GdkEventButton *event,
+ AnimationPlayDialog *dialog);
+static gboolean shape_released (GtkWidget *widget);
+static gboolean shape_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean repaint_da (GtkWidget *darea,
+ GdkEventExpose *event,
+ AnimationPlayDialog *dialog);
-static void window_destroy (GtkWidget *widget,
+static gboolean progress_button (GtkWidget *widget,
+ GdkEventButton *event,
+ AnimationPlayDialog *dialog);
+static gboolean progress_entered (GtkWidget *widget,
+ GdkEventCrossing *event,
+ AnimationPlayDialog *dialog);
+static gboolean progress_motion (GtkWidget *widget,
+ GdkEventMotion *event,
+ AnimationPlayDialog *dialog);
+static gboolean progress_left (GtkWidget *widget,
+ GdkEventCrossing *event,
+ AnimationPlayDialog *dialog);
+
+static void detach_callback (GtkToggleAction *action,
AnimationPlayDialog *dialog);
static void play_callback (GtkToggleAction *action,
AnimationPlayDialog *dialog);
@@ -222,45 +259,51 @@ static void proxycombo_activated (GtkEntry *combo_entry,
static void proxycombo_changed (GtkWidget *combo,
AnimationPlayDialog *dialog);
-static gboolean repaint_da (GtkWidget *darea,
- GdkEventExpose *event,
- AnimationPlayDialog *dialog);
-
+/* Rendering/Playing Functions */
static void render_frame (AnimationPlayDialog *dialog,
gboolean force);
+static void total_alpha_preview (guchar *drawing_data,
+ guint drawing_width,
+ guint drawing_height);
+static void reshape_from_bitmap (AnimationPlayDialog *dialog,
+ const gchar *bitmap);
static void show_playing_progress (AnimationPlayDialog *dialog);
static void show_loading_progress (AnimationPlayDialog *dialog,
gint layer_nb,
gint num_layers);
+static void do_back_step (AnimationPlayDialog *dialog);
+static void do_step (AnimationPlayDialog *dialog);
+static void set_timer (guint new_timer);
+static gboolean advance_frame_callback (AnimationPlayDialog *dialog);
static void show_goto_progress (guint frame_nb,
AnimationPlayDialog *dialog);
-static void total_alpha_preview (guchar *drawing_data,
- guint drawing_width,
- guint drawing_height);
/* Utils */
+static void connect_accelerators (GtkUIManager *ui_manager,
+ GtkActionGroup *group);
static gdouble get_fps (gint index);
static gdouble get_proxy (AnimationPlayDialog *dialog,
gint index);
+static void update_scale (AnimationPlayDialog *dialog,
+ gdouble scale);
static gdouble get_scale (AnimationPlayDialog *dialog,
gint index);
-static void set_timer (guint new_timer);
static void save_settings (AnimationPlayDialog *dialog);
static void clean_exit (AnimationPlayDialog *dialog);
/* tag util functions*/
-static gint parse_ms_tag (const gchar *str);
-static void rec_set_total_frames (const gint32 layer,
- gint *min,
- gint *max);
-static DisposeType parse_disposal_tag (AnimationPlayDialog *dialog,
- const gchar *str);
static gboolean is_disposal_tag (const gchar *str,
DisposeType *disposal,
gint *taglength);
+static DisposeType parse_disposal_tag (AnimationPlayDialog *dialog,
+ const gchar *str);
static gboolean is_ms_tag (const gchar *str,
gint *duration,
gint *taglength);
+static gint parse_ms_tag (const gchar *str);
+static void rec_set_total_frames (const gint32 layer,
+ gint *min,
+ gint *max);
const GimpPlugInInfo PLUG_IN_INFO =
{
@@ -391,30 +434,30 @@ run (const gchar *name,
}
/* UI setup. */
- dialog->window = NULL;
-
- dialog->play_bar = NULL;
- dialog->progress_bar = NULL;
- dialog->settings_bar = NULL;
- dialog->view_bar = NULL;
- dialog->refresh = NULL;
-
- dialog->fpscombo = NULL;
- dialog->zoomcombo = NULL;
- dialog->proxycombo = NULL;
-
- dialog->ui_manager = NULL;
- dialog->progress = NULL;
- dialog->startframe_spin = NULL;
- dialog->endframe_spin = NULL;
- dialog->shape_window = NULL;
- dialog->shape_drawing_area = NULL;
- dialog->shape_drawing_area_data = NULL;
- dialog->drawing_area = NULL;
- dialog->drawing_area_data = NULL;
- dialog->drawing_area_width = -1;
- dialog->drawing_area_height = -1;
- dialog->shape_drawing_area_width = -1;
+ dialog->window = NULL;
+
+ dialog->play_bar = NULL;
+ dialog->progress_bar = NULL;
+ dialog->settings_bar = NULL;
+ dialog->view_bar = NULL;
+ dialog->refresh = NULL;
+
+ dialog->fpscombo = NULL;
+ dialog->zoomcombo = NULL;
+ dialog->proxycombo = NULL;
+
+ dialog->ui_manager = NULL;
+ dialog->progress = NULL;
+ dialog->startframe_spin = NULL;
+ dialog->endframe_spin = NULL;
+ dialog->shape_window = NULL;
+ dialog->shape_drawing_area = NULL;
+ dialog->shape_drawing_area_data = NULL;
+ dialog->drawing_area = NULL;
+ dialog->drawing_area_data = NULL;
+ dialog->drawing_area_width = -1;
+ dialog->drawing_area_height = -1;
+ dialog->shape_drawing_area_width = -1;
dialog->shape_drawing_area_height = -1;
initialize (dialog);
@@ -433,718 +476,555 @@ run (const gchar *name,
}
static void
-reshape_from_bitmap (AnimationPlayDialog *dialog,
- const gchar *bitmap)
+initialize (AnimationPlayDialog *dialog)
{
- static gchar *prev_bitmap = NULL;
- static guint prev_width = -1;
- static guint prev_height = -1;
-
- if ((! prev_bitmap) ||
- prev_width != dialog->shape_drawing_area_width ||
- prev_height != dialog->shape_drawing_area_height ||
- (memcmp (prev_bitmap, bitmap,
- (dialog->shape_drawing_area_width *
- dialog->shape_drawing_area_height) / 8 +
- dialog->shape_drawing_area_height)))
+ /* Freeing existing data after a refresh. */
+ /* Catch the case when the user has closed the image in the meantime. */
+ if (! gimp_image_is_valid (dialog->image_id))
{
- GdkBitmap *shape_mask;
-
- shape_mask = gdk_bitmap_create_from_data (gtk_widget_get_window (dialog->shape_window),
- bitmap,
- dialog->shape_drawing_area_width,
dialog->shape_drawing_area_height);
- gtk_widget_shape_combine_mask (dialog->shape_window, shape_mask, 0, 0);
- g_object_unref (shape_mask);
-
- if (!prev_bitmap || prev_width != dialog->shape_drawing_area_width || prev_height !=
dialog->shape_drawing_area_height)
- {
- g_free(prev_bitmap);
- prev_bitmap = g_malloc ((dialog->shape_drawing_area_width * dialog->shape_drawing_area_height) / 8
+ dialog->shape_drawing_area_height);
- prev_width = dialog->shape_drawing_area_width;
- prev_height = dialog->shape_drawing_area_height;
- }
-
- memcpy (prev_bitmap, bitmap, (dialog->shape_drawing_area_width * dialog->shape_drawing_area_height) /
8 + dialog->shape_drawing_area_height);
+ gimp_message (_("Invalid image. Did you close it?"));
+ clean_exit (dialog);
+ return;
}
-}
-/***************** CALLBACKS ********************/
-
-static gboolean
-popup_menu (GtkWidget *widget,
- AnimationPlayDialog *dialog)
-{
- GtkWidget *menu = gtk_ui_manager_get_widget (dialog->ui_manager, "/anim-play-popup");
-
- gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
- gtk_menu_popup (GTK_MENU (menu),
- NULL, NULL, NULL, NULL,
- 0, gtk_get_current_event_time ());
-
- return TRUE;
-}
-
-static gboolean
-button_press (GtkWidget *widget,
- GdkEventButton *event,
- AnimationPlayDialog *dialog)
-{
- if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ if (! dialog->window)
{
- GtkWidget *menu = gtk_ui_manager_get_widget (dialog->ui_manager, "/anim-play-popup");
-
- gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
- gtk_menu_popup (GTK_MENU (menu),
- NULL, NULL, NULL, NULL,
- event->button,
- event->time);
- return TRUE;
+ /* First run. */
+ gchar *name = gimp_image_get_name (dialog->image_id);
+ init_ui (dialog, name);
+ g_free (name);
}
+ refresh_dialog (dialog);
- return FALSE;
+ /* I want to make sure the progress bar is realized before init_frames()
+ * which may take quite a bit of time. */
+ if (!gtk_widget_get_realized (dialog->progress))
+ gtk_widget_realize (dialog->progress);
+
+ render_frame (dialog, init_frames (dialog));
}
-/*
- * Update the actual drawing area metrics, which may be different from
- * requested, since there is no full control of the WM.
+/**
+ * Set num_frames, which is not necessarily the number of layers, in
+ * particular with the DISPOSE_TAGS disposal.
+ * Will set 0 if no layer has frame tags in tags mode, or if there is
+ * no layer in combine/replace.
*/
static void
-da_size_callback (GtkWidget *drawing_area,
- GtkAllocation *allocation,
- AnimationPlayDialog *dialog)
+init_frame_numbers (AnimationPlayDialog *dialog,
+ gint *layers,
+ gint num_layers)
{
- guchar **drawing_data;
-
- if (drawing_area == dialog->shape_drawing_area)
- {
- if (allocation->width == dialog->shape_drawing_area_width &&
- allocation->height == dialog->shape_drawing_area_height)
- return;
+ GtkAdjustment *startframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON
(dialog->startframe_spin));
+ GtkAdjustment *endframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->endframe_spin));
+ gboolean start_from_first = FALSE;
+ gboolean end_at_last = FALSE;
- dialog->shape_drawing_area_width = allocation->width;
- dialog->shape_drawing_area_height = allocation->height;
+ /* As a special exception, when we start or end at first or last frames,
+ * we want to stay that way, even with different first and last frames. */
+ if (dialog->settings.start_frame == dialog->settings.frame_min)
+ start_from_first = TRUE;
+ if (dialog->settings.end_frame == dialog->settings.frame_max)
+ end_at_last = TRUE;
- g_free (dialog->shape_drawing_area_data);
- drawing_data = &dialog->shape_drawing_area_data;
+ if (dialog->settings.frame_disposal != DISPOSE_TAGS)
+ {
+ dialog->settings.num_frames = num_layers;
+ dialog->settings.frame_min = 1;
+ dialog->settings.frame_max = dialog->settings.frame_min + dialog->settings.num_frames - 1;
}
else
{
- if (allocation->width == dialog->drawing_area_width &&
- allocation->height == dialog->drawing_area_height)
- return;
+ gint i;
+ gint max = G_MININT;
+ gint min = G_MAXINT;
- dialog->drawing_area_width = allocation->width;
- dialog->drawing_area_height = allocation->height;
+ for (i = 0; i < num_layers; i++)
+ rec_set_total_frames (layers[i], &min, &max);
- g_free (dialog->drawing_area_data);
- drawing_data = &dialog->drawing_area_data;
+ dialog->settings.num_frames = (max > min)? max + 1 - min : 0;
+ dialog->settings.frame_min = min;
+ dialog->settings.frame_max = max;
}
- dialog->settings.scale = MIN ((gdouble) allocation->width / (gdouble) dialog->preview_width,
- (gdouble) allocation->height / (gdouble) dialog->preview_height);
-
- *drawing_data = g_malloc (allocation->width * allocation->height * 3);
+ /* Keep the same frame number, unless it is now invalid. */
+ if (dialog->settings.current_frame > dialog->settings.end_frame ||
+ dialog->settings.current_frame < dialog->settings.start_frame)
+ {
+ dialog->settings.current_frame = dialog->settings.start_frame;
+ }
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->view_actions,
"detach"))) &&
- drawing_area == dialog->drawing_area)
+ /* Update frame counting UI widgets. */
+ if (startframe_adjust)
{
- /* Set "alpha grid" background. */
- total_alpha_preview (dialog->drawing_area_data,
- allocation->width,
- allocation->height);
- repaint_da (dialog->drawing_area, NULL, dialog);
+ dialog->settings.start_frame = gtk_adjustment_get_value (startframe_adjust);
+ if (start_from_first ||
+ dialog->settings.start_frame < dialog->settings.frame_min ||
+ dialog->settings.start_frame > dialog->settings.frame_max)
+ {
+ dialog->settings.start_frame = dialog->settings.frame_min;
+ }
}
- else
+ else
{
- /* Update the zoom information. */
- GtkEntry *zoomcombo_text_child;
+ dialog->settings.start_frame = dialog->settings.frame_min;
+ }
- zoomcombo_text_child = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (dialog->zoomcombo)));
- if (zoomcombo_text_child)
+ if (endframe_adjust)
+ {
+ dialog->settings.end_frame = gtk_adjustment_get_value (endframe_adjust);
+ if (end_at_last ||
+ dialog->settings.end_frame < dialog->settings.frame_min ||
+ dialog->settings.end_frame > dialog->settings.frame_max ||
+ dialog->settings.end_frame < dialog->settings.start_frame)
{
- char* new_entry_text = g_strdup_printf (_("%.1f %%"), dialog->settings.scale * 100.0);
-
- gtk_entry_set_text (zoomcombo_text_child, new_entry_text);
- g_free (new_entry_text);
+ dialog->settings.end_frame = dialog->settings.frame_max;
}
-
- /* As we re-allocated the drawn data, let's render it again. */
- if (dialog->settings.current_frame - dialog->settings.frame_min < dialog->settings.num_frames)
- render_frame (dialog, TRUE);
}
-}
-
-static gboolean
-shape_pressed (GtkWidget *widget,
- GdkEventButton *event,
- AnimationPlayDialog *dialog)
-{
- if (button_press (widget, event, dialog))
- return TRUE;
-
- /* ignore double and triple click */
- if (event->type == GDK_BUTTON_PRESS)
+ else
{
- CursorOffset *p = g_object_get_data (G_OBJECT(widget), "cursor-offset");
-
- if (!p)
- return FALSE;
-
- p->x = (gint) event->x;
- p->y = (gint) event->y;
-
- gtk_grab_add (widget);
- gdk_pointer_grab (gtk_widget_get_window (widget), TRUE,
- GDK_BUTTON_RELEASE_MASK |
- GDK_BUTTON_MOTION_MASK |
- GDK_POINTER_MOTION_HINT_MASK,
- NULL, NULL, 0);
- gdk_window_raise (gtk_widget_get_window (widget));
+ dialog->settings.end_frame = dialog->settings.frame_max;
}
-
- return FALSE;
}
+/* Initialize the frames, and return TRUE if render must be forced. */
static gboolean
-shape_released (GtkWidget *widget)
+init_frames (AnimationPlayDialog *dialog)
{
- gtk_grab_remove (widget);
- gdk_display_pointer_ungrab (gtk_widget_get_display (widget), 0);
- gdk_flush ();
-
- return FALSE;
-}
+ /* Frames are associated to an unused image. */
+ static gint32 frames_image_id = 0;
+ /* We keep track of the frames in a separate structure to free drawable
+ * memory. We can't use easily dialog->frames because some of the
+ * drawables may be used in more than 1 frame. */
+ static GList *previous_frames = NULL;
-static gboolean
-adjustment_pressed (GtkWidget *widget,
- GdkEventButton *event,
- AnimationPlayDialog *dialog)
-{
- gboolean event_processed = FALSE;
+ gint *layers;
+ gint num_layers;
+ gint image_width;
+ gint image_height;
+ gint32 new_frame, previous_frame, new_layer;
+ gint duration = 0;
+ DisposeType disposal = dialog->settings.frame_disposal;
+ gint frame_spin_size;
- if (event->type == GDK_BUTTON_PRESS &&
- event->button == 2)
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->play_actions,
"play"))))
{
- GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
- GtkAdjustment *adj = gtk_spin_button_get_adjustment (spin);
-
- gtk_adjustment_set_value (adj,
- (gdouble) dialog->settings.current_frame);
-
- /* We don't want the middle click to have another usage (in
- * particular, there is likely no need to copy-paste in these spin
- * buttons). */
- event_processed = TRUE;
+ gtk_action_activate (gtk_action_group_get_action (dialog->play_actions, "play"));
}
- return event_processed;
-}
-
-static gboolean
-progress_button (GtkWidget *widget,
- GdkEventButton *event,
- AnimationPlayDialog *dialog)
-{
- GtkAdjustment *startframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON
(dialog->startframe_spin));
- GtkAdjustment *endframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->endframe_spin));
-
- /* ignore double and triple click */
- if (event->type == GDK_BUTTON_PRESS)
+ if (dialog->frames_lock)
{
- GtkAllocation allocation;
- guint goto_frame;
-
- gtk_widget_get_allocation (widget, &allocation);
-
- goto_frame = dialog->settings.frame_min + (gint) (event->x / (allocation.width /
dialog->settings.num_frames));
-
- if (goto_frame < dialog->settings.start_frame)
- gtk_adjustment_set_value (startframe_adjust, (gdouble) goto_frame);
-
- if (goto_frame > dialog->settings.end_frame)
- gtk_adjustment_set_value (endframe_adjust, (gdouble) goto_frame);
-
- if (goto_frame >= dialog->settings.frame_min && goto_frame < dialog->settings.frame_min +
dialog->settings.num_frames)
- {
- dialog->settings.current_frame = goto_frame;
- render_frame (dialog, FALSE);
- }
+ return FALSE;
}
+ dialog->frames_lock = TRUE;
- return FALSE;
-}
-
-static gboolean
-progress_entered (GtkWidget *widget,
- GdkEventCrossing *event,
- AnimationPlayDialog *dialog)
-{
- GtkAllocation allocation;
- guint goto_frame;
-
- gtk_widget_get_allocation (widget, &allocation);
-
- goto_frame = dialog->settings.frame_min + (gint) (event->x / (allocation.width /
dialog->settings.num_frames));
-
- show_goto_progress (goto_frame, dialog);
-
- return FALSE;
-}
-
-static gboolean
-progress_motion (GtkWidget *widget,
- GdkEventMotion *event,
- AnimationPlayDialog *dialog)
-{
- GtkAllocation allocation;
- guint goto_frame;
-
- gtk_widget_get_allocation (widget, &allocation);
-
- goto_frame = dialog->settings.frame_min + (gint) (event->x / (allocation.width /
dialog->settings.num_frames));
-
- show_goto_progress (goto_frame, dialog);
-
- return FALSE;
-}
-
-static gboolean
-progress_left (GtkWidget *widget,
- GdkEventCrossing *event,
- AnimationPlayDialog *dialog)
-{
- show_playing_progress (dialog);
-
- return FALSE;
-}
+ /* Block most UI during frame initialization. */
+ gtk_action_group_set_sensitive (dialog->play_actions, FALSE);
+ gtk_widget_set_sensitive (dialog->play_bar, FALSE);
+ gtk_widget_set_sensitive (dialog->progress_bar, FALSE);
+ gtk_action_group_set_sensitive (dialog->settings_actions, FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->settings_bar), FALSE);
+ gtk_action_group_set_sensitive (dialog->view_actions, FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->view_bar), FALSE);
-static gboolean
-shape_motion (GtkWidget *widget,
- GdkEventMotion *event)
-{
- GdkModifierType mask;
- gint xp, yp;
- GdkWindow *root_win;
+ /* Cleanup before re-generation. */
+ if (dialog->frames)
+ {
+ GList *idx;
- root_win = gdk_get_default_root_window ();
- gdk_window_get_pointer (root_win, &xp, &yp, &mask);
+ gimp_image_delete (frames_image_id);
+ frames_image_id = 0;
- /* if a button is still held by the time we process this event... */
- if (mask & GDK_BUTTON1_MASK)
- {
- CursorOffset *p = g_object_get_data (G_OBJECT (widget), "cursor-offset");
+ /* Freeing previous frames only once. */
+ for (idx = g_list_first (previous_frames); idx != NULL; idx = g_list_next (idx))
+ {
+ Frame* frame = (Frame*) idx->data;
- if (!p)
- return FALSE;
+ g_list_free (frame->indexes);
+ g_list_free (frame->updated_indexes);
+ g_free (frame);
+ }
+ g_list_free (previous_frames);
+ previous_frames = NULL;
- gtk_window_move (GTK_WINDOW (widget), xp - p->x, yp - p->y);
+ g_free (dialog->frames);
+ dialog->frames = NULL;
}
- else /* the user has released all buttons */
+ if (! gimp_image_is_valid (dialog->image_id))
{
- shape_released (widget);
+ /* This is not necessarily an error. We may have simply wanted
+ * to clean up our GEGL buffers. */
+ return FALSE;
}
- return FALSE;
-}
-
-static gboolean
-repaint_da (GtkWidget *darea,
- GdkEventExpose *event,
- AnimationPlayDialog *dialog)
-{
- GtkStyle *style = gtk_widget_get_style (darea);
- gint da_width;
- gint da_height;
- guchar *da_data;
-
- if (darea == dialog->drawing_area)
+ layers = gimp_image_get_layers (dialog->image_id, &num_layers);
+ init_frame_numbers (dialog, layers, num_layers);
+ if (dialog->settings.num_frames <= 0)
{
- da_width = dialog->drawing_area_width;
- da_height = dialog->drawing_area_height;
- da_data = dialog->drawing_area_data;
+ update_ui_sensitivity (dialog);
+ dialog->frames_lock = FALSE;
+ g_free (layers);
+ return FALSE;
}
- else
+
+ dialog->frames = g_try_malloc0_n (dialog->settings.num_frames, sizeof (Frame*));
+ if (! dialog->frames)
{
- da_width = dialog->shape_drawing_area_width;
- da_height = dialog->shape_drawing_area_height;
- da_data = dialog->shape_drawing_area_data;
+ dialog->frames_lock = FALSE;
+ gimp_message (_("Memory could not be allocated to the frame container."));
+ g_free (layers);
+ clean_exit (dialog);
+ return FALSE;
}
- gdk_draw_rgb_image (gtk_widget_get_window (darea),
- style->white_gc,
- (gint) ((da_width - dialog->settings.scale * dialog->preview_width) / 2),
- (gint) ((da_height - dialog->settings.scale * dialog->preview_height) / 2),
- da_width, da_height,
- (dialog->settings.num_frames == 1) ? GDK_RGB_DITHER_MAX : DITHERTYPE,
- da_data, da_width * 3);
-
- return TRUE;
-}
-
-static void
-close_callback (GtkAction *action,
- AnimationPlayDialog *dialog)
-{
- clean_exit (dialog);
-}
-
-static void
-help_callback (GtkAction *action,
- AnimationPlayDialog *dialog)
-{
- gimp_standard_help_func (PLUG_IN_PROC, dialog->window);
-}
+ image_width = gimp_image_width (dialog->image_id);
+ image_height = gimp_image_height (dialog->image_id);
+ /* We only use RGB images for display because indexed images would somehow
+ render terrible colors. Layers from other types will be automatically
+ converted. */
+ frames_image_id = gimp_image_new (dialog->preview_width, dialog->preview_height, GIMP_RGB);
-static void
-detach_callback (GtkToggleAction *action,
- AnimationPlayDialog *dialog)
-{
- gboolean detached = gtk_toggle_action_get_active (action);
+ /* Save processing time and memory by not saving history and merged frames. */
+ gimp_image_undo_disable (frames_image_id);
- if (detached)
+ if (disposal == DISPOSE_TAGS)
{
- gint x, y;
-
- gtk_window_set_screen (GTK_WINDOW (dialog->shape_window),
- gtk_widget_get_screen (dialog->drawing_area));
-
- gtk_widget_show (dialog->shape_window);
+ gint i;
- if (! gtk_widget_get_realized (dialog->drawing_area))
- {
- gtk_widget_realize (dialog->drawing_area);
- }
- if (! gtk_widget_get_realized (dialog->shape_drawing_area))
+ for (i = 0; i < num_layers; i++)
{
- gtk_widget_realize (dialog->shape_drawing_area);
+ show_loading_progress (dialog, i, num_layers);
+ rec_init_frames (dialog,
+ frames_image_id,
+ layers[num_layers - (i + 1)],
+ previous_frames,
+ image_width, image_height);
}
- gdk_window_get_origin (gtk_widget_get_window (dialog->drawing_area), &x, &y);
-
- gtk_window_move (GTK_WINDOW (dialog->shape_window), x + 6, y + 6);
-
- gdk_window_set_back_pixmap (gtk_widget_get_window (dialog->shape_drawing_area), NULL, TRUE);
+ for (i = 0; i < dialog->settings.num_frames; i++)
+ {
+ /* If for some reason a frame is absent, use the previous one.
+ * We are ensured that there is at least a "first" frame for this. */
+ if (! dialog->frames[i])
+ {
+ dialog->frames[i] = dialog->frames[i - 1];
+ dialog->frames[i]->indexes = g_list_append (dialog->frames[i]->indexes, GINT_TO_POINTER (i));
+ }
- /* Set "alpha grid" background. */
- total_alpha_preview (dialog->drawing_area_data,
- dialog->drawing_area_width,
- dialog->drawing_area_height);
- repaint_da (dialog->drawing_area, NULL, dialog);
+ /* A zero duration only means we use the global duration, whatever it is at the time. */
+ dialog->frames[i]->duration = 0;
+ }
}
else
{
- gtk_widget_hide (dialog->shape_window);
- }
-
- render_frame (dialog, TRUE);
-}
-
-static void
-connect_accelerators (GtkUIManager *ui_manager,
- GtkActionGroup *group)
-{
- GList *action_list;
- GList *iter;
+ gint layer_offx;
+ gint layer_offy;
+ gchar *layer_name;
+ gint i;
- action_list = gtk_action_group_list_actions (group);
- iter = action_list;
- while (iter)
- {
- /* Make sure all the action's accelerator are correctly connected,
- * even when there are no associated UI item. */
- GtkAction *action = GTK_ACTION (iter->data);
+ for (i = 0; i < dialog->settings.num_frames; i++)
+ {
+ show_loading_progress (dialog, i, num_layers);
- gtk_action_set_accel_group (action,
- gtk_ui_manager_get_accel_group (ui_manager));
- gtk_action_connect_accelerator (action);
+ dialog->frames[i] = g_new (Frame, 1);
+ dialog->frames[i]->indexes = NULL;
+ dialog->frames[i]->indexes = g_list_append (dialog->frames[i]->indexes, GINT_TO_POINTER (i));
+ dialog->frames[i]->updated_indexes = NULL;
- iter = iter->next;
- }
- g_list_free (action_list);
-}
+ previous_frames = g_list_append (previous_frames, dialog->frames[i]);
+ layer_name = gimp_item_get_name (layers[num_layers - (i + 1)]);
+ if (layer_name)
+ {
+ duration = parse_ms_tag (layer_name);
+ disposal = parse_disposal_tag (dialog, layer_name);
+ g_free (layer_name);
+ }
-static GtkUIManager *
-ui_manager_new (AnimationPlayDialog *dialog)
-{
- static GtkActionEntry play_entries[] =
- {
- { "step-back", "media-skip-backward",
- N_("Step _back"), "d", N_("Step back to previous frame"),
- G_CALLBACK (step_back_callback) },
+ if (i > 0 && disposal != DISPOSE_REPLACE)
+ {
+ previous_frame = gimp_layer_copy (dialog->frames[i - 1]->drawable_id);
- { "step", "media-skip-forward",
- N_("_Step"), "f", N_("Step to next frame"),
- G_CALLBACK (step_callback) },
+ gimp_image_insert_layer (frames_image_id, previous_frame, 0, 0);
+ gimp_item_set_visible (previous_frame, TRUE);
+ }
- { "rewind", "media-seek-backward",
- NULL, "s", N_("Rewind the animation"),
- G_CALLBACK (rewind_callback) },
- };
+ new_layer = gimp_layer_new_from_drawable (layers[num_layers - (i + 1)], frames_image_id);
- static GtkToggleActionEntry play_toggle_entries[] =
- {
- { "play", "media-playback-start",
- NULL, "space", N_("Start playback"),
- G_CALLBACK (play_callback), FALSE },
- };
+ gimp_image_insert_layer (frames_image_id, new_layer, 0, 0);
+ gimp_item_set_visible (new_layer, TRUE);
+ gimp_layer_scale (new_layer, (gimp_drawable_width (layers[num_layers - (i + 1)]) * (gint)
dialog->preview_width) / image_width,
+ (gimp_drawable_height (layers[num_layers - (i + 1)]) * (gint)
dialog->preview_height) / image_height, FALSE);
+ gimp_drawable_offsets (layers[num_layers - (i + 1)], &layer_offx, &layer_offy);
+ gimp_layer_set_offsets (new_layer, (layer_offx * (gint) dialog->preview_width) / image_width,
+ (layer_offy * (gint) dialog->preview_height) / image_height);
+ gimp_layer_resize_to_image_size (new_layer);
- static GtkActionEntry settings_entries[] =
- {
- /* Refresh is not really a settings, but it makes sense to be grouped
- * as such, because we want to be able to refresh and set things in the
- * same moments. */
- { "refresh", GIMP_ICON_VIEW_REFRESH,
- N_("Refresh"), "<control>R", N_("Reload the image"),
- G_CALLBACK (refresh_callback) },
+ if (gimp_item_is_group (new_layer))
+ {
+ gint num_children;
+ gint32 *children;
+ gint j;
- {
- "speed-up", NULL,
- N_("Faster"), "bracketright", N_("Increase the speed of the animation"),
- G_CALLBACK (speed_up_callback)
- },
- {
- "speed-down", NULL,
- N_("Slower"), "bracketleft", N_("Decrease the speed of the animation"),
- G_CALLBACK (speed_down_callback)
- },
- };
+ /* I want to make all layers in the group visible, so that when I'll make
+ * the group visible too at render time, it will display everything in it. */
+ children = gimp_item_get_children (new_layer, &num_children);
+ for (j = 0; j < num_children; j++)
+ gimp_item_set_visible (children[j], TRUE);
+ }
- static GtkActionEntry view_entries[] =
- {
- { "zoom-in", GTK_STOCK_ZOOM_IN,
- NULL, "plus", N_("Zoom in"),
- G_CALLBACK (zoom_in_callback) },
+ new_frame = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
+ dialog->frames[i]->drawable_id = new_frame;
+ gimp_item_set_visible (new_frame, FALSE);
- { "zoom-in-accel", GTK_STOCK_ZOOM_IN,
- NULL, "KP_Add", N_("Zoom in"),
- G_CALLBACK (zoom_in_callback) },
+ if (duration <= 0)
+ duration = 0;
+ dialog->frames[i]->duration = (guint) duration;
+ }
+ }
- { "zoom-out", GTK_STOCK_ZOOM_OUT,
- NULL, "minus", N_("Zoom out"),
- G_CALLBACK (zoom_out_callback) },
+ /* Update the UI. */
+ frame_spin_size = (gint) (log10 (dialog->settings.frame_max - (dialog->settings.frame_max % 10))) + 1;
+ gtk_entry_set_width_chars (GTK_ENTRY (dialog->startframe_spin), frame_spin_size);
+ gtk_entry_set_width_chars (GTK_ENTRY (dialog->endframe_spin), frame_spin_size);
- { "zoom-out-accel", GTK_STOCK_ZOOM_OUT,
- NULL, "KP_Subtract", N_("Zoom out"),
- G_CALLBACK (zoom_out_callback) },
+ gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->startframe_spin)),
+ dialog->settings.start_frame,
+ dialog->settings.frame_min,
+ dialog->settings.frame_max,
+ 1.0, 5.0, 0.0);
+ gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->endframe_spin)),
+ dialog->settings.end_frame,
+ dialog->settings.start_frame,
+ dialog->settings.frame_max,
+ 1.0, 5.0, 0.0);
- { "zoom-reset", GTK_STOCK_ZOOM_OUT,
- NULL, "equal", N_("Zoom out"),
- G_CALLBACK (zoom_reset_callback) },
+ update_ui_sensitivity (dialog);
- { "zoom-reset-accel", GTK_STOCK_ZOOM_OUT,
- NULL, "KP_Equal", N_("Zoom out"),
- G_CALLBACK (zoom_reset_callback) },
- };
+ dialog->frames_lock = FALSE;
+ g_free (layers);
- static GtkToggleActionEntry view_toggle_entries[] =
- {
- { "detach", GIMP_ICON_DETACH,
- N_("Detach"), NULL,
- N_("Detach the animation from the dialog window"),
- G_CALLBACK (detach_callback), FALSE }
- };
+ return TRUE;
+}
- static GtkActionEntry various_entries[] =
- {
- { "help", "help-browser",
- N_("About the animation plug-in"), "question", NULL,
- G_CALLBACK (help_callback) },
+/**
+ * Recursive call to generate frames in TAGS mode.
+ **/
+static void
+rec_init_frames (AnimationPlayDialog *dialog,
+ gint32 frames_image_id,
+ gint32 layer,
+ GList *previous_frames,
+ gint image_width,
+ gint image_height)
+{
+ Frame *empty_frame = NULL;
+ gchar *layer_name;
+ gchar *nospace_name;
+ GMatchInfo *match_info;
+ gboolean preview_quality;
+ gint32 new_layer;
+ gint j, k;
- { "close", "window-close",
- N_("Quit"), "<control>W", NULL,
- G_CALLBACK (close_callback)
- },
+ if (gimp_item_is_group (layer))
{
- "quit", "application-quit",
- N_("Quit"), "<control>Q", NULL,
- G_CALLBACK (close_callback)
- },
- };
+ gint num_children;
+ gint32 *children;
- GtkUIManager *ui_manager = gtk_ui_manager_new ();
- GError *error = NULL;
+ children = gimp_item_get_children (layer, &num_children);
+ for (j = 0; j < num_children; j++)
+ rec_init_frames (dialog, frames_image_id,
+ children[num_children - j - 1],
+ previous_frames,
+ image_width, image_height);
- /* All playback related actions. */
- dialog->play_actions = gtk_action_group_new ("playback");
- gtk_action_group_set_translation_domain (dialog->play_actions, NULL);
- gtk_action_group_add_actions (dialog->play_actions,
- play_entries,
- G_N_ELEMENTS (play_entries),
- dialog);
- gtk_action_group_add_toggle_actions (dialog->play_actions,
- play_toggle_entries,
- G_N_ELEMENTS (play_toggle_entries),
- dialog);
- connect_accelerators (ui_manager, dialog->play_actions);
- gtk_ui_manager_insert_action_group (ui_manager, dialog->play_actions, -1);
+ return;
+ }
- /* All settings related actions. */
- dialog->settings_actions = gtk_action_group_new ("settings");
- gtk_action_group_set_translation_domain (dialog->settings_actions, NULL);
- gtk_action_group_add_actions (dialog->settings_actions,
- settings_entries,
- G_N_ELEMENTS (settings_entries),
- dialog);
- connect_accelerators (ui_manager, dialog->settings_actions);
- gtk_ui_manager_insert_action_group (ui_manager, dialog->settings_actions, -1);
+ layer_name = gimp_item_get_name (layer);
+ nospace_name = g_regex_replace_literal (nospace_reg, layer_name, -1, 0, "", 0, NULL);
+ preview_quality = dialog->preview_width != image_width || dialog->preview_height != image_height;
- /* All view actions. */
- dialog->view_actions = gtk_action_group_new ("view");
- gtk_action_group_set_translation_domain (dialog->view_actions, NULL);
- gtk_action_group_add_actions (dialog->view_actions,
- view_entries,
- G_N_ELEMENTS (view_entries),
- dialog);
- gtk_action_group_add_toggle_actions (dialog->view_actions,
- view_toggle_entries,
- G_N_ELEMENTS (view_toggle_entries),
- dialog);
- connect_accelerators (ui_manager, dialog->view_actions);
- gtk_ui_manager_insert_action_group (ui_manager, dialog->view_actions, -1);
+ if (g_regex_match (all_reg, nospace_name, 0, NULL))
+ {
+ for (j = 0; j < dialog->settings.num_frames; j++)
+ {
+ if (! dialog->frames[j])
+ {
+ if (! empty_frame)
+ {
+ empty_frame = g_new (Frame, 1);
+ empty_frame->indexes = NULL;
+ empty_frame->updated_indexes = NULL;
+ empty_frame->drawable_id = 0;
- /* Remaining various actions. */
- dialog->various_actions = gtk_action_group_new ("various");
+ previous_frames = g_list_append (previous_frames, empty_frame);
+ }
- gtk_action_group_set_translation_domain (dialog->various_actions, NULL);
+ if (! g_list_find (empty_frame->updated_indexes, GINT_TO_POINTER (j)))
+ empty_frame->updated_indexes = g_list_append (empty_frame->updated_indexes, GINT_TO_POINTER
(j));
- gtk_action_group_add_actions (dialog->various_actions,
- various_entries,
- G_N_ELEMENTS (various_entries),
- dialog);
- connect_accelerators (ui_manager, dialog->various_actions);
- gtk_ui_manager_insert_action_group (ui_manager, dialog->various_actions, -1);
+ dialog->frames[j] = empty_frame;
+ }
+ else if (! g_list_find (dialog->frames[j]->updated_indexes, GINT_TO_POINTER (j)))
+ dialog->frames[j]->updated_indexes = g_list_append (dialog->frames[j]->updated_indexes,
GINT_TO_POINTER (j));
+ }
+ }
+ else
+ {
+ g_regex_match (layers_reg, nospace_name, 0, &match_info);
- /* Finalize. */
- gtk_window_add_accel_group (GTK_WINDOW (dialog->window),
- gtk_ui_manager_get_accel_group (ui_manager));
- gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
+ while (g_match_info_matches (match_info))
+ {
+ gchar *tag = g_match_info_fetch (match_info, 1);
+ gchar** tokens = g_strsplit(tag, ",", 0);
- /* Finally make some limited popup menu. */
- gtk_ui_manager_add_ui_from_string (ui_manager,
- "<ui>"
- " <popup name=\"anim-play-popup\" accelerators=\"true\">"
- " <menuitem action=\"refresh\" />"
- " <separator />"
- " <menuitem action=\"zoom-in\" />"
- " <menuitem action=\"zoom-out\" />"
- " <menuitem action=\"zoom-reset\" />"
- " <separator />"
- " <menuitem action=\"speed-up\" />"
- " <menuitem action=\"speed-down\" />"
- " <separator />"
- " <menuitem action=\"help\" />"
- " <separator />"
- " <menuitem action=\"close\" />"
- " </popup>"
- "</ui>",
- -1, &error);
+ for (j = 0; tokens[j] != NULL; j++)
+ {
+ gchar* hyphen = g_strrstr(tokens[j], "-");
- if (error)
- {
- g_warning ("error parsing ui: %s", error->message);
- g_clear_error (&error);
- }
+ if (hyphen != NULL)
+ {
+ gint32 first = (gint32) g_ascii_strtoll (tokens[j], NULL, 10);
+ gint32 second = (gint32) g_ascii_strtoll (&hyphen[1], NULL, 10);
- return ui_manager;
-}
+ for (k = first; k <= second; k++)
+ {
+ if (! dialog->frames[k - dialog->settings.frame_min])
+ {
+ if (! empty_frame)
+ {
+ empty_frame = g_new (Frame, 1);
+ empty_frame->indexes = NULL;
+ empty_frame->updated_indexes = NULL;
+ empty_frame->drawable_id = 0;
-static void
-refresh_dialog (AnimationPlayDialog *dialog)
-{
- GdkScreen *screen;
- guint screen_width, screen_height;
- gint window_width, window_height;
+ previous_frames = g_list_append (previous_frames, empty_frame);
+ }
- /* Update GUI size. */
- screen = gtk_widget_get_screen (dialog->window);
- screen_height = gdk_screen_get_height (screen);
- screen_width = gdk_screen_get_width (screen);
- gtk_window_get_size (GTK_WINDOW (dialog->window), &window_width, &window_height);
+ if (! g_list_find (dialog->frames[k -
dialog->settings.frame_min]->updated_indexes, GINT_TO_POINTER (k - dialog->settings.frame_min)))
+ empty_frame->updated_indexes = g_list_append (empty_frame->updated_indexes,
GINT_TO_POINTER (k - dialog->settings.frame_min));
- /* if the *window* size is bigger than the screen size,
- * diminish the drawing area by as much, then compute the corresponding scale. */
- if (window_width + 50 > screen_width || window_height + 50 > screen_height)
- {
- gint expected_drawing_area_width = MAX (1, dialog->preview_width - window_width + screen_width);
- gint expected_drawing_area_height = MAX (1, dialog->preview_height - window_height + screen_height);
+ dialog->frames[k - dialog->settings.frame_min] = empty_frame;
+ }
+ else if (! g_list_find (dialog->frames[k -
dialog->settings.frame_min]->updated_indexes, GINT_TO_POINTER (k - dialog->settings.frame_min)))
+ dialog->frames[k - dialog->settings.frame_min]->updated_indexes = g_list_append
(dialog->frames[k - dialog->settings.frame_min]->updated_indexes,
+ GINT_TO_POINTER (k -
dialog->settings.frame_min));
+ }
+ }
+ else
+ {
+ gint32 num = (gint32) g_ascii_strtoll (tokens[j], NULL, 10);
- gdouble expected_scale = MIN ((gdouble) expected_drawing_area_width / (gdouble) dialog->preview_width,
- (gdouble) expected_drawing_area_height / (gdouble)
dialog->preview_height);
- update_scale (dialog, expected_scale);
+ if (! dialog->frames[num - dialog->settings.frame_min])
+ {
+ if (! empty_frame)
+ {
+ empty_frame = g_new (Frame, 1);
+ empty_frame->indexes = NULL;
+ empty_frame->updated_indexes = NULL;
+ empty_frame->drawable_id = 0;
- /* There is unfortunately no good way to know the size of the decorations, taskbars, etc.
- * So we take a wild guess by making the window slightly smaller to fit into most case. */
- gtk_window_set_default_size (GTK_WINDOW (dialog->window),
- MIN (expected_drawing_area_width + 20, screen_width - 60),
- MIN (expected_drawing_area_height + 90, screen_height - 60));
+ previous_frames = g_list_append (previous_frames, empty_frame);
+ }
- gtk_window_reshow_with_initial_size (GTK_WINDOW (dialog->window));
- }
-}
+ if (! g_list_find (dialog->frames[num - dialog->settings.frame_min]->updated_indexes,
GINT_TO_POINTER (num - dialog->settings.frame_min)))
+ empty_frame->updated_indexes = g_list_append (empty_frame->updated_indexes,
GINT_TO_POINTER (num - dialog->settings.frame_min));
-/******************************/
-/*** Some Update procedures ***/
-/******************************/
+ dialog->frames[num - dialog->settings.frame_min] = empty_frame;
+ }
+ else if (! g_list_find (dialog->frames[num - dialog->settings.frame_min]->updated_indexes,
GINT_TO_POINTER (num - dialog->settings.frame_min)))
+ dialog->frames[num - dialog->settings.frame_min]->updated_indexes = g_list_append
(dialog->frames[num - dialog->settings.frame_min]->updated_indexes,
+ GINT_TO_POINTER (num -
dialog->settings.frame_min));
+ }
+ }
+ g_strfreev (tokens);
+ g_free (tag);
+ g_match_info_next (match_info, NULL);
+ }
+ g_match_info_free (match_info);
+ }
-/* Update the tool sensitivity for playing, depending on the number of frames. */
-static void
-update_ui_sensitivity (AnimationPlayDialog *dialog)
-{
- gboolean animated;
+ for (j = 0; j < dialog->settings.num_frames; j++)
+ {
+ /* Check which frame must be updated with the current layer. */
+ if (dialog->frames[j] && g_list_length (dialog->frames[j]->updated_indexes))
+ {
+ new_layer = gimp_layer_new_from_drawable (layer, frames_image_id);
+ gimp_image_insert_layer (frames_image_id, new_layer, 0, 0);
- animated = dialog->settings.end_frame - dialog->settings.start_frame > 1;
- /* Play actions only if we selected several frames between start/end. */
- gtk_action_group_set_sensitive (dialog->play_actions, animated);
- gtk_widget_set_sensitive (GTK_WIDGET (dialog->play_bar), animated);
+ if (preview_quality)
+ {
+ gint layer_offx, layer_offy;
- /* We can access the progress bar if there are several frames. */
- gtk_widget_set_sensitive (GTK_WIDGET (dialog->progress_bar),
- dialog->settings.num_frames > 1);
+ gimp_layer_scale (new_layer,
+ (gimp_drawable_width (layer) * (gint) dialog->preview_width) / (gint)
image_width,
+ (gimp_drawable_height (layer) * (gint) dialog->preview_height) / (gint)
image_height,
+ FALSE);
+ gimp_drawable_offsets (layer, &layer_offx, &layer_offy);
+ gimp_layer_set_offsets (new_layer, (layer_offx * (gint) dialog->preview_width) / (gint)
image_width,
+ (layer_offy * (gint) dialog->preview_height) / (gint) image_height);
+ }
+ gimp_layer_resize_to_image_size (new_layer);
- /* Settings are always changeable. */
- gtk_action_group_set_sensitive (dialog->settings_actions, TRUE);
- gtk_widget_set_sensitive (GTK_WIDGET (dialog->settings_bar), TRUE);
+ if (dialog->frames[j]->drawable_id == 0)
+ {
+ dialog->frames[j]->drawable_id = new_layer;
+ dialog->frames[j]->indexes = dialog->frames[j]->updated_indexes;
+ dialog->frames[j]->updated_indexes = NULL;
+ }
+ else if (g_list_length (dialog->frames[j]->indexes) == g_list_length
(dialog->frames[j]->updated_indexes))
+ {
+ gimp_item_set_visible (new_layer, TRUE);
+ gimp_item_set_visible (dialog->frames[j]->drawable_id, TRUE);
- /* View are always meaningfull with at least 1 frame. */
- gtk_action_group_set_sensitive (dialog->view_actions,
- dialog->settings.num_frames >= 1);
- gtk_widget_set_sensitive (GTK_WIDGET (dialog->view_bar),
- dialog->settings.num_frames >= 1);
-}
+ dialog->frames[j]->drawable_id = gimp_image_merge_visible_layers (frames_image_id,
GIMP_CLIP_TO_IMAGE);
+ g_list_free (dialog->frames[j]->updated_indexes);
+ dialog->frames[j]->updated_indexes = NULL;
+ }
+ else
+ {
+ GList *idx;
+ gboolean move_j = FALSE;
+ Frame *forked_frame = g_new (Frame, 1);
+ gint32 forked_drawable_id = gimp_layer_new_from_drawable (dialog->frames[j]->drawable_id,
frames_image_id);
-static void
-update_scale (AnimationPlayDialog *dialog,
- gdouble scale)
-{
- guint expected_drawing_area_width;
- guint expected_drawing_area_height;
+ /* if part only of the dialog->frames are updated, we fork the existing frame. */
+ gimp_image_insert_layer (frames_image_id, forked_drawable_id, 0, 1);
+ gimp_item_set_visible (new_layer, TRUE);
+ gimp_item_set_visible (forked_drawable_id, TRUE);
+ forked_drawable_id = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
- /* FIXME: scales under 0.5 are broken. See bug 690265. */
- if (scale <= 0.5)
- scale = 0.51;
+ forked_frame->drawable_id = forked_drawable_id;
+ forked_frame->indexes = dialog->frames[j]->updated_indexes;
+ forked_frame->updated_indexes = NULL;
+ dialog->frames[j]->updated_indexes = NULL;
- expected_drawing_area_width = dialog->preview_width * scale;
- expected_drawing_area_height = dialog->preview_height * scale;
+ for (idx = g_list_first (forked_frame->indexes); idx != NULL; idx = g_list_next (idx))
+ {
+ dialog->frames[j]->indexes = g_list_remove (dialog->frames[j]->indexes, idx->data);
+ if (GPOINTER_TO_INT (idx->data) != j)
+ dialog->frames[GPOINTER_TO_INT (idx->data)] = forked_frame;
+ else
+ /* Frame j must also be moved to the forked frame, but only after the loop. */
+ move_j = TRUE;
+ }
+ if (move_j)
+ dialog->frames[j] = forked_frame;
- /* We don't update dialog->settings.scale directly because this might
- * end up not being the real scale. Instead we request this size for
- * the drawing areas, and the actual scale update will be done on the
- * callback when size is actually allocated. */
- gtk_widget_set_size_request (dialog->drawing_area, expected_drawing_area_width,
expected_drawing_area_height);
- gtk_widget_set_size_request (dialog->shape_drawing_area, expected_drawing_area_width,
expected_drawing_area_height);
- /* I force the shape window to a smaller size if we scale down. */
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->view_actions,
"detach"))))
- {
- gint x, y;
+ gimp_item_set_visible (forked_drawable_id, FALSE);
- gdk_window_get_origin (gtk_widget_get_window (dialog->shape_window), &x, &y);
- gtk_window_reshow_with_initial_size (GTK_WINDOW (dialog->shape_window));
- gtk_window_move (GTK_WINDOW (dialog->shape_window), x, y);
+ previous_frames = g_list_append (previous_frames, forked_frame);
+ }
+
+ gimp_item_set_visible (dialog->frames[j]->drawable_id, FALSE);
+ }
}
-}
+ g_free (layer_name);
+ g_free (nospace_name);
+}
static void
init_ui (AnimationPlayDialog *dialog,
@@ -1404,7 +1284,7 @@ init_ui (AnimationPlayDialog *dialog,
G_CALLBACK(da_size_callback),
dialog);
g_signal_connect (dialog->drawing_area, "button-press-event",
- G_CALLBACK (button_press),
+ G_CALLBACK (da_button_press),
dialog);
/*****************/
@@ -1574,822 +1454,627 @@ init_ui (AnimationPlayDialog *dialog,
gtk_widget_set_size_request (dialog->shape_drawing_area, dialog->preview_width, dialog->preview_height);
}
-/**
- * Recursive call to generate frames in TAGS mode.
- **/
-static void
-rec_init_frames (AnimationPlayDialog *dialog,
- gint32 frames_image_id,
- gint32 layer,
- GList *previous_frames,
- gint image_width,
- gint image_height)
+static GtkUIManager *
+ui_manager_new (AnimationPlayDialog *dialog)
{
- Frame *empty_frame = NULL;
- gchar *layer_name;
- gchar *nospace_name;
- GMatchInfo *match_info;
- gboolean preview_quality;
- gint32 new_layer;
- gint j, k;
+ static GtkActionEntry play_entries[] =
+ {
+ { "step-back", "media-skip-backward",
+ N_("Step _back"), "d", N_("Step back to previous frame"),
+ G_CALLBACK (step_back_callback) },
- if (gimp_item_is_group (layer))
- {
- gint num_children;
- gint32 *children;
+ { "step", "media-skip-forward",
+ N_("_Step"), "f", N_("Step to next frame"),
+ G_CALLBACK (step_callback) },
- children = gimp_item_get_children (layer, &num_children);
- for (j = 0; j < num_children; j++)
- rec_init_frames (dialog, frames_image_id,
- children[num_children - j - 1],
- previous_frames,
- image_width, image_height);
+ { "rewind", "media-seek-backward",
+ NULL, "s", N_("Rewind the animation"),
+ G_CALLBACK (rewind_callback) },
+ };
- return;
- }
+ static GtkToggleActionEntry play_toggle_entries[] =
+ {
+ { "play", "media-playback-start",
+ NULL, "space", N_("Start playback"),
+ G_CALLBACK (play_callback), FALSE },
+ };
- layer_name = gimp_item_get_name (layer);
- nospace_name = g_regex_replace_literal (nospace_reg, layer_name, -1, 0, "", 0, NULL);
- preview_quality = dialog->preview_width != image_width || dialog->preview_height != image_height;
+ static GtkActionEntry settings_entries[] =
+ {
+ /* Refresh is not really a settings, but it makes sense to be grouped
+ * as such, because we want to be able to refresh and set things in the
+ * same moments. */
+ { "refresh", GIMP_ICON_VIEW_REFRESH,
+ N_("Refresh"), "<control>R", N_("Reload the image"),
+ G_CALLBACK (refresh_callback) },
- if (g_regex_match (all_reg, nospace_name, 0, NULL))
{
- for (j = 0; j < dialog->settings.num_frames; j++)
- {
- if (! dialog->frames[j])
- {
- if (! empty_frame)
- {
- empty_frame = g_new (Frame, 1);
- empty_frame->indexes = NULL;
- empty_frame->updated_indexes = NULL;
- empty_frame->drawable_id = 0;
-
- previous_frames = g_list_append (previous_frames, empty_frame);
- }
-
- if (! g_list_find (empty_frame->updated_indexes, GINT_TO_POINTER (j)))
- empty_frame->updated_indexes = g_list_append (empty_frame->updated_indexes, GINT_TO_POINTER
(j));
-
- dialog->frames[j] = empty_frame;
- }
- else if (! g_list_find (dialog->frames[j]->updated_indexes, GINT_TO_POINTER (j)))
- dialog->frames[j]->updated_indexes = g_list_append (dialog->frames[j]->updated_indexes,
GINT_TO_POINTER (j));
- }
- }
- else
+ "speed-up", NULL,
+ N_("Faster"), "bracketright", N_("Increase the speed of the animation"),
+ G_CALLBACK (speed_up_callback)
+ },
{
- g_regex_match (layers_reg, nospace_name, 0, &match_info);
-
- while (g_match_info_matches (match_info))
- {
- gchar *tag = g_match_info_fetch (match_info, 1);
- gchar** tokens = g_strsplit(tag, ",", 0);
-
- for (j = 0; tokens[j] != NULL; j++)
- {
- gchar* hyphen = g_strrstr(tokens[j], "-");
-
- if (hyphen != NULL)
- {
- gint32 first = (gint32) g_ascii_strtoll (tokens[j], NULL, 10);
- gint32 second = (gint32) g_ascii_strtoll (&hyphen[1], NULL, 10);
+ "speed-down", NULL,
+ N_("Slower"), "bracketleft", N_("Decrease the speed of the animation"),
+ G_CALLBACK (speed_down_callback)
+ },
+ };
- for (k = first; k <= second; k++)
- {
- if (! dialog->frames[k - dialog->settings.frame_min])
- {
- if (! empty_frame)
- {
- empty_frame = g_new (Frame, 1);
- empty_frame->indexes = NULL;
- empty_frame->updated_indexes = NULL;
- empty_frame->drawable_id = 0;
+ static GtkActionEntry view_entries[] =
+ {
+ { "zoom-in", GTK_STOCK_ZOOM_IN,
+ NULL, "plus", N_("Zoom in"),
+ G_CALLBACK (zoom_in_callback) },
- previous_frames = g_list_append (previous_frames, empty_frame);
- }
+ { "zoom-in-accel", GTK_STOCK_ZOOM_IN,
+ NULL, "KP_Add", N_("Zoom in"),
+ G_CALLBACK (zoom_in_callback) },
- if (! g_list_find (dialog->frames[k -
dialog->settings.frame_min]->updated_indexes, GINT_TO_POINTER (k - dialog->settings.frame_min)))
- empty_frame->updated_indexes = g_list_append (empty_frame->updated_indexes,
GINT_TO_POINTER (k - dialog->settings.frame_min));
+ { "zoom-out", GTK_STOCK_ZOOM_OUT,
+ NULL, "minus", N_("Zoom out"),
+ G_CALLBACK (zoom_out_callback) },
- dialog->frames[k - dialog->settings.frame_min] = empty_frame;
- }
- else if (! g_list_find (dialog->frames[k -
dialog->settings.frame_min]->updated_indexes, GINT_TO_POINTER (k - dialog->settings.frame_min)))
- dialog->frames[k - dialog->settings.frame_min]->updated_indexes = g_list_append
(dialog->frames[k - dialog->settings.frame_min]->updated_indexes,
- GINT_TO_POINTER (k -
dialog->settings.frame_min));
- }
- }
- else
- {
- gint32 num = (gint32) g_ascii_strtoll (tokens[j], NULL, 10);
+ { "zoom-out-accel", GTK_STOCK_ZOOM_OUT,
+ NULL, "KP_Subtract", N_("Zoom out"),
+ G_CALLBACK (zoom_out_callback) },
- if (! dialog->frames[num - dialog->settings.frame_min])
- {
- if (! empty_frame)
- {
- empty_frame = g_new (Frame, 1);
- empty_frame->indexes = NULL;
- empty_frame->updated_indexes = NULL;
- empty_frame->drawable_id = 0;
+ { "zoom-reset", GTK_STOCK_ZOOM_OUT,
+ NULL, "equal", N_("Zoom out"),
+ G_CALLBACK (zoom_reset_callback) },
- previous_frames = g_list_append (previous_frames, empty_frame);
- }
+ { "zoom-reset-accel", GTK_STOCK_ZOOM_OUT,
+ NULL, "KP_Equal", N_("Zoom out"),
+ G_CALLBACK (zoom_reset_callback) },
+ };
- if (! g_list_find (dialog->frames[num - dialog->settings.frame_min]->updated_indexes,
GINT_TO_POINTER (num - dialog->settings.frame_min)))
- empty_frame->updated_indexes = g_list_append (empty_frame->updated_indexes,
GINT_TO_POINTER (num - dialog->settings.frame_min));
+ static GtkToggleActionEntry view_toggle_entries[] =
+ {
+ { "detach", GIMP_STOCK_DETACH,
+ N_("Detach"), NULL,
+ N_("Detach the animation from the dialog window"),
+ G_CALLBACK (detach_callback), FALSE }
+ };
- dialog->frames[num - dialog->settings.frame_min] = empty_frame;
- }
- else if (! g_list_find (dialog->frames[num - dialog->settings.frame_min]->updated_indexes,
GINT_TO_POINTER (num - dialog->settings.frame_min)))
- dialog->frames[num - dialog->settings.frame_min]->updated_indexes = g_list_append
(dialog->frames[num - dialog->settings.frame_min]->updated_indexes,
- GINT_TO_POINTER (num -
dialog->settings.frame_min));
- }
- }
- g_strfreev (tokens);
- g_free (tag);
- g_match_info_next (match_info, NULL);
- }
- g_match_info_free (match_info);
- }
+ static GtkActionEntry various_entries[] =
+ {
+ { "help", "help-browser",
+ N_("About the animation plug-in"), "question", NULL,
+ G_CALLBACK (help_callback) },
- for (j = 0; j < dialog->settings.num_frames; j++)
+ { "close", "window-close",
+ N_("Quit"), "<control>W", NULL,
+ G_CALLBACK (close_callback)
+ },
{
- /* Check which frame must be updated with the current layer. */
- if (dialog->frames[j] && g_list_length (dialog->frames[j]->updated_indexes))
- {
- new_layer = gimp_layer_new_from_drawable (layer, frames_image_id);
- gimp_image_insert_layer (frames_image_id, new_layer, 0, 0);
+ "quit", "application-quit",
+ N_("Quit"), "<control>Q", NULL,
+ G_CALLBACK (close_callback)
+ },
+ };
- if (preview_quality)
- {
- gint layer_offx, layer_offy;
+ GtkUIManager *ui_manager = gtk_ui_manager_new ();
+ GError *error = NULL;
- gimp_layer_scale (new_layer,
- (gimp_drawable_width (layer) * (gint) dialog->preview_width) / (gint)
image_width,
- (gimp_drawable_height (layer) * (gint) dialog->preview_height) / (gint)
image_height,
- FALSE);
- gimp_drawable_offsets (layer, &layer_offx, &layer_offy);
- gimp_layer_set_offsets (new_layer, (layer_offx * (gint) dialog->preview_width) / (gint)
image_width,
- (layer_offy * (gint) dialog->preview_height) / (gint) image_height);
- }
- gimp_layer_resize_to_image_size (new_layer);
+ /* All playback related actions. */
+ dialog->play_actions = gtk_action_group_new ("playback");
+ gtk_action_group_set_translation_domain (dialog->play_actions, NULL);
+ gtk_action_group_add_actions (dialog->play_actions,
+ play_entries,
+ G_N_ELEMENTS (play_entries),
+ dialog);
+ gtk_action_group_add_toggle_actions (dialog->play_actions,
+ play_toggle_entries,
+ G_N_ELEMENTS (play_toggle_entries),
+ dialog);
+ connect_accelerators (ui_manager, dialog->play_actions);
+ gtk_ui_manager_insert_action_group (ui_manager, dialog->play_actions, -1);
- if (dialog->frames[j]->drawable_id == 0)
- {
- dialog->frames[j]->drawable_id = new_layer;
- dialog->frames[j]->indexes = dialog->frames[j]->updated_indexes;
- dialog->frames[j]->updated_indexes = NULL;
- }
- else if (g_list_length (dialog->frames[j]->indexes) == g_list_length
(dialog->frames[j]->updated_indexes))
- {
- gimp_item_set_visible (new_layer, TRUE);
- gimp_item_set_visible (dialog->frames[j]->drawable_id, TRUE);
+ /* All settings related actions. */
+ dialog->settings_actions = gtk_action_group_new ("settings");
+ gtk_action_group_set_translation_domain (dialog->settings_actions, NULL);
+ gtk_action_group_add_actions (dialog->settings_actions,
+ settings_entries,
+ G_N_ELEMENTS (settings_entries),
+ dialog);
+ connect_accelerators (ui_manager, dialog->settings_actions);
+ gtk_ui_manager_insert_action_group (ui_manager, dialog->settings_actions, -1);
- dialog->frames[j]->drawable_id = gimp_image_merge_visible_layers (frames_image_id,
GIMP_CLIP_TO_IMAGE);
- g_list_free (dialog->frames[j]->updated_indexes);
- dialog->frames[j]->updated_indexes = NULL;
- }
- else
- {
- GList *idx;
- gboolean move_j = FALSE;
- Frame *forked_frame = g_new (Frame, 1);
- gint32 forked_drawable_id = gimp_layer_new_from_drawable (dialog->frames[j]->drawable_id,
frames_image_id);
+ /* All view actions. */
+ dialog->view_actions = gtk_action_group_new ("view");
+ gtk_action_group_set_translation_domain (dialog->view_actions, NULL);
+ gtk_action_group_add_actions (dialog->view_actions,
+ view_entries,
+ G_N_ELEMENTS (view_entries),
+ dialog);
+ gtk_action_group_add_toggle_actions (dialog->view_actions,
+ view_toggle_entries,
+ G_N_ELEMENTS (view_toggle_entries),
+ dialog);
+ connect_accelerators (ui_manager, dialog->view_actions);
+ gtk_ui_manager_insert_action_group (ui_manager, dialog->view_actions, -1);
- /* if part only of the dialog->frames are updated, we fork the existing frame. */
- gimp_image_insert_layer (frames_image_id, forked_drawable_id, 0, 1);
- gimp_item_set_visible (new_layer, TRUE);
- gimp_item_set_visible (forked_drawable_id, TRUE);
- forked_drawable_id = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
+ /* Remaining various actions. */
+ dialog->various_actions = gtk_action_group_new ("various");
- forked_frame->drawable_id = forked_drawable_id;
- forked_frame->indexes = dialog->frames[j]->updated_indexes;
- forked_frame->updated_indexes = NULL;
- dialog->frames[j]->updated_indexes = NULL;
+ gtk_action_group_set_translation_domain (dialog->various_actions, NULL);
- for (idx = g_list_first (forked_frame->indexes); idx != NULL; idx = g_list_next (idx))
- {
- dialog->frames[j]->indexes = g_list_remove (dialog->frames[j]->indexes, idx->data);
- if (GPOINTER_TO_INT (idx->data) != j)
- dialog->frames[GPOINTER_TO_INT (idx->data)] = forked_frame;
- else
- /* Frame j must also be moved to the forked frame, but only after the loop. */
- move_j = TRUE;
- }
- if (move_j)
- dialog->frames[j] = forked_frame;
+ gtk_action_group_add_actions (dialog->various_actions,
+ various_entries,
+ G_N_ELEMENTS (various_entries),
+ dialog);
+ connect_accelerators (ui_manager, dialog->various_actions);
+ gtk_ui_manager_insert_action_group (ui_manager, dialog->various_actions, -1);
- gimp_item_set_visible (forked_drawable_id, FALSE);
+ /* Finalize. */
+ gtk_window_add_accel_group (GTK_WINDOW (dialog->window),
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
- previous_frames = g_list_append (previous_frames, forked_frame);
- }
+ /* Finally make some limited popup menu. */
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <popup name=\"anim-play-popup\" accelerators=\"true\">"
+ " <menuitem action=\"refresh\" />"
+ " <separator />"
+ " <menuitem action=\"zoom-in\" />"
+ " <menuitem action=\"zoom-out\" />"
+ " <menuitem action=\"zoom-reset\" />"
+ " <separator />"
+ " <menuitem action=\"speed-up\" />"
+ " <menuitem action=\"speed-down\" />"
+ " <separator />"
+ " <menuitem action=\"help\" />"
+ " <separator />"
+ " <menuitem action=\"close\" />"
+ " </popup>"
+ "</ui>",
+ -1, &error);
- gimp_item_set_visible (dialog->frames[j]->drawable_id, FALSE);
- }
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
}
- g_free (layer_name);
- g_free (nospace_name);
+ return ui_manager;
}
-/* Initialize the frames, and return TRUE if render must be forced. */
-static gboolean
-init_frames (AnimationPlayDialog *dialog)
+static void
+refresh_dialog (AnimationPlayDialog *dialog)
{
- /* Frames are associated to an unused image. */
- static gint32 frames_image_id = 0;
- /* We keep track of the frames in a separate structure to free drawable
- * memory. We can't use easily dialog->frames because some of the
- * drawables may be used in more than 1 frame. */
- static GList *previous_frames = NULL;
-
- gint *layers;
- gint num_layers;
- gint image_width;
- gint image_height;
- gint32 new_frame, previous_frame, new_layer;
- gint duration = 0;
- DisposeType disposal = dialog->settings.frame_disposal;
- gint frame_spin_size;
-
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->play_actions,
"play"))))
- {
- gtk_action_activate (gtk_action_group_get_action (dialog->play_actions, "play"));
- }
-
- if (dialog->frames_lock)
- {
- return FALSE;
- }
- dialog->frames_lock = TRUE;
-
- /* Block most UI during frame initialization. */
- gtk_action_group_set_sensitive (dialog->play_actions, FALSE);
- gtk_widget_set_sensitive (dialog->play_bar, FALSE);
- gtk_widget_set_sensitive (dialog->progress_bar, FALSE);
- gtk_action_group_set_sensitive (dialog->settings_actions, FALSE);
- gtk_widget_set_sensitive (GTK_WIDGET (dialog->settings_bar), FALSE);
- gtk_action_group_set_sensitive (dialog->view_actions, FALSE);
- gtk_widget_set_sensitive (GTK_WIDGET (dialog->view_bar), FALSE);
-
- /* Cleanup before re-generation. */
- if (dialog->frames)
- {
- GList *idx;
-
- gimp_image_delete (frames_image_id);
- frames_image_id = 0;
-
- /* Freeing previous frames only once. */
- for (idx = g_list_first (previous_frames); idx != NULL; idx = g_list_next (idx))
- {
- Frame* frame = (Frame*) idx->data;
-
- g_list_free (frame->indexes);
- g_list_free (frame->updated_indexes);
- g_free (frame);
- }
- g_list_free (previous_frames);
- previous_frames = NULL;
-
- g_free (dialog->frames);
- dialog->frames = NULL;
- }
- if (! gimp_image_is_valid (dialog->image_id))
- {
- /* This is not necessarily an error. We may have simply wanted
- * to clean up our GEGL buffers. */
- return FALSE;
- }
+ GdkScreen *screen;
+ guint screen_width, screen_height;
+ gint window_width, window_height;
- layers = gimp_image_get_layers (dialog->image_id, &num_layers);
- init_frame_numbers (dialog, layers, num_layers);
- if (dialog->settings.num_frames <= 0)
- {
- update_ui_sensitivity (dialog);
- dialog->frames_lock = FALSE;
- g_free (layers);
- return FALSE;
- }
+ /* Update GUI size. */
+ screen = gtk_widget_get_screen (dialog->window);
+ screen_height = gdk_screen_get_height (screen);
+ screen_width = gdk_screen_get_width (screen);
+ gtk_window_get_size (GTK_WINDOW (dialog->window), &window_width, &window_height);
- dialog->frames = g_try_malloc0_n (dialog->settings.num_frames, sizeof (Frame*));
- if (! dialog->frames)
- {
- dialog->frames_lock = FALSE;
- gimp_message (_("Memory could not be allocated to the frame container."));
- g_free (layers);
- clean_exit (dialog);
- return FALSE;
- }
+ /* if the *window* size is bigger than the screen size,
+ * diminish the drawing area by as much, then compute the corresponding scale. */
+ if (window_width + 50 > screen_width || window_height + 50 > screen_height)
+ {
+ gint expected_drawing_area_width = MAX (1, dialog->preview_width - window_width + screen_width);
+ gint expected_drawing_area_height = MAX (1, dialog->preview_height - window_height + screen_height);
- image_width = gimp_image_width (dialog->image_id);
- image_height = gimp_image_height (dialog->image_id);
+ gdouble expected_scale = MIN ((gdouble) expected_drawing_area_width / (gdouble) dialog->preview_width,
+ (gdouble) expected_drawing_area_height / (gdouble)
dialog->preview_height);
+ update_scale (dialog, expected_scale);
- /* We only use RGB images for display because indexed images would somehow
- render terrible colors. Layers from other types will be automatically
- converted. */
- frames_image_id = gimp_image_new (dialog->preview_width, dialog->preview_height, GIMP_RGB);
+ /* There is unfortunately no good way to know the size of the decorations, taskbars, etc.
+ * So we take a wild guess by making the window slightly smaller to fit into most case. */
+ gtk_window_set_default_size (GTK_WINDOW (dialog->window),
+ MIN (expected_drawing_area_width + 20, screen_width - 60),
+ MIN (expected_drawing_area_height + 90, screen_height - 60));
- /* Save processing time and memory by not saving history and merged frames. */
- gimp_image_undo_disable (frames_image_id);
+ gtk_window_reshow_with_initial_size (GTK_WINDOW (dialog->window));
+ }
+}
- if (disposal == DISPOSE_TAGS)
- {
- gint i;
+/* Update the tool sensitivity for playing, depending on the number of frames. */
+static void
+update_ui_sensitivity (AnimationPlayDialog *dialog)
+{
+ gboolean animated;
- for (i = 0; i < num_layers; i++)
- {
- show_loading_progress (dialog, i, num_layers);
- rec_init_frames (dialog,
- frames_image_id,
- layers[num_layers - (i + 1)],
- previous_frames,
- image_width, image_height);
- }
+ animated = dialog->settings.end_frame - dialog->settings.start_frame > 1;
+ /* Play actions only if we selected several frames between start/end. */
+ gtk_action_group_set_sensitive (dialog->play_actions, animated);
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->play_bar), animated);
- for (i = 0; i < dialog->settings.num_frames; i++)
- {
- /* If for some reason a frame is absent, use the previous one.
- * We are ensured that there is at least a "first" frame for this. */
- if (! dialog->frames[i])
- {
- dialog->frames[i] = dialog->frames[i - 1];
- dialog->frames[i]->indexes = g_list_append (dialog->frames[i]->indexes, GINT_TO_POINTER (i));
- }
+ /* We can access the progress bar if there are several frames. */
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->progress_bar),
+ dialog->settings.num_frames > 1);
- /* A zero duration only means we use the global duration, whatever it is at the time. */
- dialog->frames[i]->duration = 0;
- }
- }
- else
- {
- gint layer_offx;
- gint layer_offy;
- gchar *layer_name;
- gint i;
+ /* Settings are always changeable. */
+ gtk_action_group_set_sensitive (dialog->settings_actions, TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->settings_bar), TRUE);
- for (i = 0; i < dialog->settings.num_frames; i++)
- {
- show_loading_progress (dialog, i, num_layers);
+ /* View are always meaningfull with at least 1 frame. */
+ gtk_action_group_set_sensitive (dialog->view_actions,
+ dialog->settings.num_frames >= 1);
+ gtk_widget_set_sensitive (GTK_WIDGET (dialog->view_bar),
+ dialog->settings.num_frames >= 1);
+}
- dialog->frames[i] = g_new (Frame, 1);
- dialog->frames[i]->indexes = NULL;
- dialog->frames[i]->indexes = g_list_append (dialog->frames[i]->indexes, GINT_TO_POINTER (i));
- dialog->frames[i]->updated_indexes = NULL;
+/***************** CALLBACKS ********************/
- previous_frames = g_list_append (previous_frames, dialog->frames[i]);
+static void
+close_callback (GtkAction *action,
+ AnimationPlayDialog *dialog)
+{
+ clean_exit (dialog);
+}
- layer_name = gimp_item_get_name (layers[num_layers - (i + 1)]);
- if (layer_name)
- {
- duration = parse_ms_tag (layer_name);
- disposal = parse_disposal_tag (dialog, layer_name);
- g_free (layer_name);
- }
+static void
+help_callback (GtkAction *action,
+ AnimationPlayDialog *dialog)
+{
+ gimp_standard_help_func (PLUG_IN_PROC, dialog->window);
+}
- if (i > 0 && disposal != DISPOSE_REPLACE)
- {
- previous_frame = gimp_layer_copy (dialog->frames[i - 1]->drawable_id);
+static void
+window_destroy (GtkWidget *widget,
+ AnimationPlayDialog *dialog)
+{
+ clean_exit (dialog);
+}
- gimp_image_insert_layer (frames_image_id, previous_frame, 0, 0);
- gimp_item_set_visible (previous_frame, TRUE);
- }
+static gboolean
+popup_menu (GtkWidget *widget,
+ AnimationPlayDialog *dialog)
+{
+ GtkWidget *menu = gtk_ui_manager_get_widget (dialog->ui_manager, "/anim-play-popup");
- new_layer = gimp_layer_new_from_drawable (layers[num_layers - (i + 1)], frames_image_id);
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ 0, gtk_get_current_event_time ());
- gimp_image_insert_layer (frames_image_id, new_layer, 0, 0);
- gimp_item_set_visible (new_layer, TRUE);
- gimp_layer_scale (new_layer, (gimp_drawable_width (layers[num_layers - (i + 1)]) * (gint)
dialog->preview_width) / image_width,
- (gimp_drawable_height (layers[num_layers - (i + 1)]) * (gint)
dialog->preview_height) / image_height, FALSE);
- gimp_drawable_offsets (layers[num_layers - (i + 1)], &layer_offx, &layer_offy);
- gimp_layer_set_offsets (new_layer, (layer_offx * (gint) dialog->preview_width) / image_width,
- (layer_offy * (gint) dialog->preview_height) / image_height);
- gimp_layer_resize_to_image_size (new_layer);
+ return TRUE;
+}
- if (gimp_item_is_group (new_layer))
- {
- gint num_children;
- gint32 *children;
- gint j;
+static gboolean
+adjustment_pressed (GtkWidget *widget,
+ GdkEventButton *event,
+ AnimationPlayDialog *dialog)
+{
+ gboolean event_processed = FALSE;
- /* I want to make all layers in the group visible, so that when I'll make
- * the group visible too at render time, it will display everything in it. */
- children = gimp_item_get_children (new_layer, &num_children);
- for (j = 0; j < num_children; j++)
- gimp_item_set_visible (children[j], TRUE);
- }
+ if (event->type == GDK_BUTTON_PRESS &&
+ event->button == 2)
+ {
+ GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
+ GtkAdjustment *adj = gtk_spin_button_get_adjustment (spin);
- new_frame = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
- dialog->frames[i]->drawable_id = new_frame;
- gimp_item_set_visible (new_frame, FALSE);
+ gtk_adjustment_set_value (adj,
+ (gdouble) dialog->settings.current_frame);
- if (duration <= 0)
- duration = 0;
- dialog->frames[i]->duration = (guint) duration;
- }
+ /* We don't want the middle click to have another usage (in
+ * particular, there is likely no need to copy-paste in these spin
+ * buttons). */
+ event_processed = TRUE;
}
- /* Update the UI. */
- frame_spin_size = (gint) (log10 (dialog->settings.frame_max - (dialog->settings.frame_max % 10))) + 1;
- gtk_entry_set_width_chars (GTK_ENTRY (dialog->startframe_spin), frame_spin_size);
- gtk_entry_set_width_chars (GTK_ENTRY (dialog->endframe_spin), frame_spin_size);
-
- gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->startframe_spin)),
- dialog->settings.start_frame,
- dialog->settings.frame_min,
- dialog->settings.frame_max,
- 1.0, 5.0, 0.0);
- gtk_adjustment_configure (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->endframe_spin)),
- dialog->settings.end_frame,
- dialog->settings.start_frame,
- dialog->settings.frame_max,
- 1.0, 5.0, 0.0);
-
- update_ui_sensitivity (dialog);
-
- dialog->frames_lock = FALSE;
- g_free (layers);
-
- return TRUE;
+ return event_processed;
}
-static void
-initialize (AnimationPlayDialog *dialog)
+static gboolean
+da_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ AnimationPlayDialog *dialog)
{
- /* Freeing existing data after a refresh. */
- /* Catch the case when the user has closed the image in the meantime. */
- if (! gimp_image_is_valid (dialog->image_id))
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
{
- gimp_message (_("Invalid image. Did you close it?"));
- clean_exit (dialog);
- return;
- }
+ GtkWidget *menu = gtk_ui_manager_get_widget (dialog->ui_manager, "/anim-play-popup");
- if (! dialog->window)
- {
- /* First run. */
- gchar *name = gimp_image_get_name (dialog->image_id);
- init_ui (dialog, name);
- g_free (name);
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ event->button,
+ event->time);
+ return TRUE;
}
- refresh_dialog (dialog);
- /* I want to make sure the progress bar is realized before init_frames()
- * which may take quite a bit of time. */
- if (!gtk_widget_get_realized (dialog->progress))
- gtk_widget_realize (dialog->progress);
-
- render_frame (dialog, init_frames (dialog));
+ return FALSE;
}
-/* Rendering Functions */
-
+/*
+ * Update the actual drawing area metrics, which may be different from
+ * requested, since there is no full control of the WM.
+ */
static void
-render_frame (AnimationPlayDialog *dialog,
- gboolean force_render)
+da_size_callback (GtkWidget *drawing_area,
+ GtkAllocation *allocation,
+ AnimationPlayDialog *dialog)
{
- static gint last_frame_index = -1;
- static gchar *shape_preview_mask = NULL;
- static guint shape_preview_mask_size = 0;
- static guchar *rawframe = NULL;
- static guint rawframe_size = 0;
- GeglBuffer *buffer;
- gint i, j, k;
- guchar *srcptr;
- guchar *destptr;
- GtkWidget *da;
- guint drawing_width;
- guint drawing_height;
- guchar *preview_data;
-
- /* Do not try to update the drawing areas while init_frames() is still running. */
- if (dialog->frames_lock)
- return;
+ guchar **drawing_data;
- /* Unless we are in a case where we always want to redraw
- * (after a zoom, preview mode change, reinitialization, and such),
- * we don't redraw if the same frame was already drawn. */
- if ((! force_render) && dialog->settings.num_frames > 0 && last_frame_index > -1 &&
- g_list_find (dialog->frames[last_frame_index]->indexes,
- GINT_TO_POINTER (dialog->settings.current_frame - dialog->settings.frame_min)))
+ if (drawing_area == dialog->shape_drawing_area)
{
- show_playing_progress (dialog);
- return;
- }
-
- dialog->frames_lock = TRUE;
-
- g_assert (dialog->settings.num_frames < 1 || (dialog->settings.current_frame >=
dialog->settings.start_frame &&
- dialog->settings.current_frame <=
dialog->settings.end_frame));
+ if (allocation->width == dialog->shape_drawing_area_width &&
+ allocation->height == dialog->shape_drawing_area_height)
+ return;
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->view_actions,
"detach"))))
- {
- da = dialog->shape_drawing_area;
- preview_data = dialog->shape_drawing_area_data;
- drawing_width = dialog->shape_drawing_area_width;
- drawing_height = dialog->shape_drawing_area_height;
+ dialog->shape_drawing_area_width = allocation->width;
+ dialog->shape_drawing_area_height = allocation->height;
- if (dialog->settings.num_frames < 1)
- total_alpha_preview (preview_data, drawing_width, drawing_height);
+ g_free (dialog->shape_drawing_area_data);
+ drawing_data = &dialog->shape_drawing_area_data;
}
else
{
- da = dialog->drawing_area;
- preview_data = dialog->drawing_area_data;
- drawing_width = dialog->drawing_area_width;
- drawing_height = dialog->drawing_area_height;
+ if (allocation->width == dialog->drawing_area_width &&
+ allocation->height == dialog->drawing_area_height)
+ return;
- /* Set "alpha grid" background. */
- total_alpha_preview (preview_data, drawing_width, drawing_height);
+ dialog->drawing_area_width = allocation->width;
+ dialog->drawing_area_height = allocation->height;
+
+ g_free (dialog->drawing_area_data);
+ drawing_data = &dialog->drawing_area_data;
}
- /* When there is no frame to show, we simply display the alpha background and return. */
- if (dialog->settings.num_frames > 0)
- {
- /* Update the rawframe. */
- if (rawframe_size < drawing_width * drawing_height * 4)
- {
- rawframe_size = drawing_width * drawing_height * 4;
- g_free (rawframe);
- rawframe = g_malloc (rawframe_size);
- }
+ dialog->settings.scale = MIN ((gdouble) allocation->width / (gdouble) dialog->preview_width,
+ (gdouble) allocation->height / (gdouble) dialog->preview_height);
- buffer = gimp_drawable_get_buffer (dialog->frames[dialog->settings.current_frame -
dialog->settings.frame_min]->drawable_id);
+ *drawing_data = g_malloc (allocation->width * allocation->height * 3);
- /* Fetch and scale the whole raw new frame */
- gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, drawing_width, drawing_height),
- dialog->settings.scale, babl_format ("R'G'B'A u8"),
- rawframe, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->view_actions,
"detach"))) &&
+ drawing_area == dialog->drawing_area)
+ {
+ /* Set "alpha grid" background. */
+ total_alpha_preview (dialog->drawing_area_data,
+ allocation->width,
+ allocation->height);
+ repaint_da (dialog->drawing_area, NULL, dialog);
+ }
+ else
+ {
+ /* Update the zoom information. */
+ GtkEntry *zoomcombo_text_child;
- /* Number of pixels. */
- i = drawing_width * drawing_height;
- destptr = preview_data;
- srcptr = rawframe;
- while (i--)
+ zoomcombo_text_child = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (dialog->zoomcombo)));
+ if (zoomcombo_text_child)
{
- if (! (srcptr[3] & 128))
- {
- srcptr += 4;
- destptr += 3;
- continue;
- }
-
- *(destptr++) = *(srcptr++);
- *(destptr++) = *(srcptr++);
- *(destptr++) = *(srcptr++);
+ char* new_entry_text = g_strdup_printf (_("%.1f %%"), dialog->settings.scale * 100.0);
- srcptr++;
+ gtk_entry_set_text (zoomcombo_text_child, new_entry_text);
+ g_free (new_entry_text);
}
- /* calculate the shape mask */
- if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action
(dialog->view_actions, "detach"))))
- {
- gint ideal_shape_size = (drawing_width * drawing_height) /
- 8 + 1 + drawing_height;
-
- if (shape_preview_mask_size < ideal_shape_size)
- {
- shape_preview_mask_size = ideal_shape_size;
- g_free (shape_preview_mask);
- shape_preview_mask = g_malloc (ideal_shape_size);
- }
-
- memset (shape_preview_mask, 0,
- (drawing_width * drawing_height) / 8 + drawing_height);
- srcptr = rawframe + 3;
-
- for (j = 0; j < drawing_height; j++)
- {
- k = j * ((7 + drawing_width) / 8);
+ /* As we re-allocated the drawn data, let's render it again. */
+ if (dialog->settings.current_frame - dialog->settings.frame_min < dialog->settings.num_frames)
+ render_frame (dialog, TRUE);
+ }
+}
- for (i = 0; i < drawing_width; i++)
- {
- if ((*srcptr) & 128)
- shape_preview_mask[k + i/8] |= (1 << (i&7));
+static gboolean
+shape_pressed (GtkWidget *widget,
+ GdkEventButton *event,
+ AnimationPlayDialog *dialog)
+{
+ if (da_button_press (widget, event, dialog))
+ return TRUE;
- srcptr += 4;
- }
- }
- reshape_from_bitmap (dialog, shape_preview_mask);
- }
+ /* ignore double and triple click */
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ CursorOffset *p = g_object_get_data (G_OBJECT(widget), "cursor-offset");
- /* clean up */
- g_object_unref (buffer);
+ if (!p)
+ return FALSE;
- /* Update UI. */
- show_playing_progress (dialog);
+ p->x = (gint) event->x;
+ p->y = (gint) event->y;
- last_frame_index = dialog->settings.current_frame - dialog->settings.frame_min;
+ gtk_grab_add (widget);
+ gdk_pointer_grab (gtk_widget_get_window (widget), TRUE,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK,
+ NULL, NULL, 0);
+ gdk_window_raise (gtk_widget_get_window (widget));
}
- /* Display the preview buffer. */
- gdk_draw_rgb_image (gtk_widget_get_window (da),
- (gtk_widget_get_style (da))->white_gc,
- (gint) ((drawing_width - dialog->settings.scale * dialog->preview_width) / 2),
- (gint) ((drawing_height - dialog->settings.scale * dialog->preview_height) / 2),
- drawing_width, drawing_height,
- (dialog->settings.num_frames == 1 ?
- GDK_RGB_DITHER_MAX : DITHERTYPE),
- preview_data, drawing_width * 3);
-
- dialog->frames_lock = FALSE;
+ return FALSE;
}
-
-static void
-show_goto_progress (guint goto_frame,
- AnimationPlayDialog *dialog)
+static gboolean
+shape_released (GtkWidget *widget)
{
- gchar *text;
-
- /* update the dialog's progress bar */
- gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress),
- ((gfloat) (dialog->settings.current_frame - dialog->settings.frame_min) /
- (gfloat) (dialog->settings.num_frames - 0.999)));
+ gtk_grab_remove (widget);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget), 0);
+ gdk_flush ();
- if (dialog->settings.frame_disposal != DISPOSE_TAGS || dialog->settings.frame_min == 1)
- text = g_strdup_printf (_("Go to frame %d of %d"), goto_frame, dialog->settings.num_frames);
- else
- text = g_strdup_printf (_("Go to frame %d (%d) of %d"), goto_frame - dialog->settings.frame_min + 1,
goto_frame, dialog->settings.num_frames);
- gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress), text);
- g_free (text);
+ return FALSE;
}
-static void
-show_loading_progress (AnimationPlayDialog *dialog,
- gint layer_nb,
- gint num_layers)
+static gboolean
+shape_motion (GtkWidget *widget,
+ GdkEventMotion *event)
{
- gchar *text;
- gfloat load_rate = (gfloat) layer_nb / ((gfloat) num_layers - 0.999);
+ GdkModifierType mask;
+ gint xp, yp;
+ GdkWindow *root_win;
- /* update the dialog's progress bar */
- gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress), load_rate);
+ root_win = gdk_get_default_root_window ();
+ gdk_window_get_pointer (root_win, &xp, &yp, &mask);
- text = g_strdup_printf (_("Loading animation %d %%"), (gint) (load_rate * 100));
- gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress), text);
- g_free (text);
+ /* if a button is still held by the time we process this event... */
+ if (mask & GDK_BUTTON1_MASK)
+ {
+ CursorOffset *p = g_object_get_data (G_OBJECT (widget), "cursor-offset");
- /* Forcing the UI to update even with intensive computation. */
- while (gtk_events_pending ())
- gtk_main_iteration ();
+ if (!p)
+ return FALSE;
+
+ gtk_window_move (GTK_WINDOW (widget), xp - p->x, yp - p->y);
+ }
+ else /* the user has released all buttons */
+ {
+ shape_released (widget);
+ }
+
+ return FALSE;
}
-static void
-show_playing_progress (AnimationPlayDialog *dialog)
+static gboolean
+repaint_da (GtkWidget *darea,
+ GdkEventExpose *event,
+ AnimationPlayDialog *dialog)
{
- gchar *text;
-
- /* update the dialog's progress bar */
- gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress),
- ((gfloat) (dialog->settings.current_frame - dialog->settings.frame_min) /
- (gfloat) (dialog->settings.num_frames - 0.999)));
+ GtkStyle *style = gtk_widget_get_style (darea);
+ gint da_width;
+ gint da_height;
+ guchar *da_data;
- if (dialog->settings.frame_disposal != DISPOSE_TAGS || dialog->settings.frame_min == 1)
+ if (darea == dialog->drawing_area)
{
- text = g_strdup_printf (_("Frame %d of %d"),
- dialog->settings.current_frame,
- dialog->settings.num_frames);
+ da_width = dialog->drawing_area_width;
+ da_height = dialog->drawing_area_height;
+ da_data = dialog->drawing_area_data;
}
else
{
- text = g_strdup_printf (_("Frame %d (%d) of %d"),
- dialog->settings.current_frame - dialog->settings.frame_min + 1,
- dialog->settings.current_frame, dialog->settings.num_frames);
+ da_width = dialog->shape_drawing_area_width;
+ da_height = dialog->shape_drawing_area_height;
+ da_data = dialog->shape_drawing_area_data;
}
- gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress), text);
- g_free (text);
+
+ gdk_draw_rgb_image (gtk_widget_get_window (darea),
+ style->white_gc,
+ (gint) ((da_width - dialog->settings.scale * dialog->preview_width) / 2),
+ (gint) ((da_height - dialog->settings.scale * dialog->preview_height) / 2),
+ da_width, da_height,
+ (dialog->settings.num_frames == 1) ? GDK_RGB_DITHER_MAX : DITHERTYPE,
+ da_data, da_width * 3);
+
+ return TRUE;
}
-/* total_alpha_preview:
- * Fill the @drawing_data with an alpha (grey chess) pattern.
- * This uses a static array, copied over each line (with some shift to
- * reproduce the pattern), using `memcpy()`.
- * The reason why we keep the pattern in the statically allocated memory,
- * instead of simply looping through @drawing_data and recreating the
- * pattern is simply because `memcpy()` implementations are supposed to
- * be more efficient than loops over an array. */
-static void
-total_alpha_preview (guchar *drawing_data,
- guint drawing_width,
- guint drawing_height)
+static gboolean
+progress_button (GtkWidget *widget,
+ GdkEventButton *event,
+ AnimationPlayDialog *dialog)
{
- static guint alpha_line_width = 0;
- static guchar *alpha_line = NULL;
- gint i;
-
- g_assert (drawing_width > 0);
+ GtkAdjustment *startframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON
(dialog->startframe_spin));
+ GtkAdjustment *endframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->endframe_spin));
- /* If width change, we update the "alpha" line. */
- if (alpha_line_width < drawing_width + 8)
+ /* ignore double and triple click */
+ if (event->type == GDK_BUTTON_PRESS)
{
- alpha_line_width = drawing_width + 8;
+ GtkAllocation allocation;
+ guint goto_frame;
- g_free (alpha_line);
+ gtk_widget_get_allocation (widget, &allocation);
- /* A full line + 8 pixels (1 square). */
- alpha_line = g_malloc (alpha_line_width * 3);
+ goto_frame = dialog->settings.frame_min + (gint) (event->x / (allocation.width /
dialog->settings.num_frames));
- for (i = 0; i < alpha_line_width; i++)
- {
- /* 8 pixels dark grey, 8 pixels light grey, and so on. */
- if (i & 8)
- {
- alpha_line[i * 3 + 0] =
- alpha_line[i * 3 + 1] =
- alpha_line[i * 3 + 2] = 102;
- }
- else
- {
- alpha_line[i * 3 + 0] =
- alpha_line[i * 3 + 1] =
- alpha_line[i * 3 + 2] = 154;
- }
- }
- }
+ if (goto_frame < dialog->settings.start_frame)
+ gtk_adjustment_set_value (startframe_adjust, (gdouble) goto_frame);
- for (i = 0; i < drawing_height; i++)
- {
- if (i & 8)
- {
- memcpy (&drawing_data[i * 3 * drawing_width],
- alpha_line,
- 3 * drawing_width);
- }
- else
+ if (goto_frame > dialog->settings.end_frame)
+ gtk_adjustment_set_value (endframe_adjust, (gdouble) goto_frame);
+
+ if (goto_frame >= dialog->settings.frame_min && goto_frame < dialog->settings.frame_min +
dialog->settings.num_frames)
{
- /* Every 8 vertical pixels, we shift the horizontal line by 8 pixels. */
- memcpy (&drawing_data[i * 3 * drawing_width],
- alpha_line + 24,
- 3 * drawing_width);
+ dialog->settings.current_frame = goto_frame;
+ render_frame (dialog, FALSE);
}
}
+
+ return FALSE;
}
-static void
-do_back_step (AnimationPlayDialog *dialog)
+static gboolean
+progress_entered (GtkWidget *widget,
+ GdkEventCrossing *event,
+ AnimationPlayDialog *dialog)
{
- if (dialog->settings.current_frame == dialog->settings.start_frame)
- {
- dialog->settings.current_frame = dialog->settings.end_frame;
- }
- else
- {
- --dialog->settings.current_frame;
- }
- render_frame (dialog, FALSE);
+ GtkAllocation allocation;
+ guint goto_frame;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ goto_frame = dialog->settings.frame_min + (gint) (event->x / (allocation.width /
dialog->settings.num_frames));
+
+ show_goto_progress (goto_frame, dialog);
+
+ return FALSE;
}
-static void
-do_step (AnimationPlayDialog *dialog)
+static gboolean
+progress_motion (GtkWidget *widget,
+ GdkEventMotion *event,
+ AnimationPlayDialog *dialog)
{
- dialog->settings.current_frame = dialog->settings.start_frame +
- ((dialog->settings.current_frame - dialog->settings.start_frame + 1) %
- (dialog->settings.end_frame - dialog->settings.start_frame + 1));
+ GtkAllocation allocation;
+ guint goto_frame;
- render_frame (dialog, FALSE);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ goto_frame = dialog->settings.frame_min + (gint) (event->x / (allocation.width /
dialog->settings.num_frames));
+
+ show_goto_progress (goto_frame, dialog);
+
+ return FALSE;
}
-static void
-window_destroy (GtkWidget *widget,
- AnimationPlayDialog *dialog)
+static gboolean
+progress_left (GtkWidget *widget,
+ GdkEventCrossing *event,
+ AnimationPlayDialog *dialog)
{
- clean_exit (dialog);
+ show_playing_progress (dialog);
+
+ return FALSE;
}
static void
-set_timer (guint new_timer)
+detach_callback (GtkToggleAction *action,
+ AnimationPlayDialog *dialog)
{
- static guint timer = 0;
+ gboolean detached = gtk_toggle_action_get_active (action);
- if (timer)
+ if (detached)
{
- g_source_remove (timer);
- }
- timer = new_timer;
-}
+ gint x, y;
-static gboolean
-advance_frame_callback (AnimationPlayDialog *dialog)
-{
- guint duration;
- guint timer;
+ gtk_window_set_screen (GTK_WINDOW (dialog->shape_window),
+ gtk_widget_get_screen (dialog->drawing_area));
- duration = dialog->frames[(dialog->settings.current_frame - dialog->settings.frame_min + 1) %
- dialog->settings.num_frames]->duration;
- if (duration <= 0)
- {
- duration = (guint) (1000.0 / ((AnimationPlayDialog *) dialog)->settings.frame_rate);
- }
+ gtk_widget_show (dialog->shape_window);
- timer = g_timeout_add (duration,
- (GSourceFunc) advance_frame_callback,
- (AnimationPlayDialog *) dialog);
- set_timer (timer);
+ if (! gtk_widget_get_realized (dialog->drawing_area))
+ {
+ gtk_widget_realize (dialog->drawing_area);
+ }
+ if (! gtk_widget_get_realized (dialog->shape_drawing_area))
+ {
+ gtk_widget_realize (dialog->shape_drawing_area);
+ }
- do_step (dialog);
+ gdk_window_get_origin (gtk_widget_get_window (dialog->drawing_area), &x, &y);
- return G_SOURCE_REMOVE;
+ gtk_window_move (GTK_WINDOW (dialog->shape_window), x + 6, y + 6);
+
+ gdk_window_set_back_pixmap (gtk_widget_get_window (dialog->shape_drawing_area), NULL, TRUE);
+
+ /* Set "alpha grid" background. */
+ total_alpha_preview (dialog->drawing_area_data,
+ dialog->drawing_area_width,
+ dialog->drawing_area_height);
+ repaint_da (dialog->drawing_area, NULL, dialog);
+ }
+ else
+ {
+ gtk_widget_hide (dialog->shape_window);
+ }
+
+ render_frame (dialog, TRUE);
}
static void
@@ -2848,133 +2533,409 @@ proxycombo_changed (GtkWidget *combo,
}
}
-/**
- * Set num_frames, which is not necessarily the number of layers, in
- * particular with the DISPOSE_TAGS disposal.
- * Will set 0 if no layer has frame tags in tags mode, or if there is
- * no layer in combine/replace.
- */
+/* Rendering Functions */
+
static void
-init_frame_numbers (AnimationPlayDialog *dialog,
- gint *layers,
- gint num_layers)
+render_frame (AnimationPlayDialog *dialog,
+ gboolean force_render)
{
- GtkAdjustment *startframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON
(dialog->startframe_spin));
- GtkAdjustment *endframe_adjust = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (dialog->endframe_spin));
- gboolean start_from_first = FALSE;
- gboolean end_at_last = FALSE;
+ static gint last_frame_index = -1;
+ static gchar *shape_preview_mask = NULL;
+ static guint shape_preview_mask_size = 0;
+ static guchar *rawframe = NULL;
+ static guint rawframe_size = 0;
+ GeglBuffer *buffer;
+ gint i, j, k;
+ guchar *srcptr;
+ guchar *destptr;
+ GtkWidget *da;
+ guint drawing_width;
+ guint drawing_height;
+ guchar *preview_data;
- /* As a special exception, when we start or end at first or last frames,
- * we want to stay that way, even with different first and last frames. */
- if (dialog->settings.start_frame == dialog->settings.frame_min)
- start_from_first = TRUE;
- if (dialog->settings.end_frame == dialog->settings.frame_max)
- end_at_last = TRUE;
+ /* Do not try to update the drawing areas while init_frames() is still running. */
+ if (dialog->frames_lock)
+ return;
- if (dialog->settings.frame_disposal != DISPOSE_TAGS)
+ /* Unless we are in a case where we always want to redraw
+ * (after a zoom, preview mode change, reinitialization, and such),
+ * we don't redraw if the same frame was already drawn. */
+ if ((! force_render) && dialog->settings.num_frames > 0 && last_frame_index > -1 &&
+ g_list_find (dialog->frames[last_frame_index]->indexes,
+ GINT_TO_POINTER (dialog->settings.current_frame - dialog->settings.frame_min)))
{
- dialog->settings.num_frames = num_layers;
- dialog->settings.frame_min = 1;
- dialog->settings.frame_max = dialog->settings.frame_min + dialog->settings.num_frames - 1;
+ show_playing_progress (dialog);
+ return;
}
- else
+
+ dialog->frames_lock = TRUE;
+
+ g_assert (dialog->settings.num_frames < 1 || (dialog->settings.current_frame >=
dialog->settings.start_frame &&
+ dialog->settings.current_frame <=
dialog->settings.end_frame));
+
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->view_actions,
"detach"))))
{
- gint i;
- gint max = G_MININT;
- gint min = G_MAXINT;
+ da = dialog->shape_drawing_area;
+ preview_data = dialog->shape_drawing_area_data;
+ drawing_width = dialog->shape_drawing_area_width;
+ drawing_height = dialog->shape_drawing_area_height;
- for (i = 0; i < num_layers; i++)
- rec_set_total_frames (layers[i], &min, &max);
+ if (dialog->settings.num_frames < 1)
+ total_alpha_preview (preview_data, drawing_width, drawing_height);
+ }
+ else
+ {
+ da = dialog->drawing_area;
+ preview_data = dialog->drawing_area_data;
+ drawing_width = dialog->drawing_area_width;
+ drawing_height = dialog->drawing_area_height;
- dialog->settings.num_frames = (max > min)? max + 1 - min : 0;
- dialog->settings.frame_min = min;
- dialog->settings.frame_max = max;
+ /* Set "alpha grid" background. */
+ total_alpha_preview (preview_data, drawing_width, drawing_height);
}
- /* Keep the same frame number, unless it is now invalid. */
- if (dialog->settings.current_frame > dialog->settings.end_frame ||
- dialog->settings.current_frame < dialog->settings.start_frame)
+ /* When there is no frame to show, we simply display the alpha background and return. */
+ if (dialog->settings.num_frames > 0)
{
- dialog->settings.current_frame = dialog->settings.start_frame;
+ /* Update the rawframe. */
+ if (rawframe_size < drawing_width * drawing_height * 4)
+ {
+ rawframe_size = drawing_width * drawing_height * 4;
+ g_free (rawframe);
+ rawframe = g_malloc (rawframe_size);
+ }
+
+ buffer = gimp_drawable_get_buffer (dialog->frames[dialog->settings.current_frame -
dialog->settings.frame_min]->drawable_id);
+
+ /* Fetch and scale the whole raw new frame */
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, drawing_width, drawing_height),
+ dialog->settings.scale, babl_format ("R'G'B'A u8"),
+ rawframe, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
+
+ /* Number of pixels. */
+ i = drawing_width * drawing_height;
+ destptr = preview_data;
+ srcptr = rawframe;
+ while (i--)
+ {
+ if (! (srcptr[3] & 128))
+ {
+ srcptr += 4;
+ destptr += 3;
+ continue;
+ }
+
+ *(destptr++) = *(srcptr++);
+ *(destptr++) = *(srcptr++);
+ *(destptr++) = *(srcptr++);
+
+ srcptr++;
+ }
+
+ /* calculate the shape mask */
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action
(dialog->view_actions, "detach"))))
+ {
+ gint ideal_shape_size = (drawing_width * drawing_height) /
+ 8 + 1 + drawing_height;
+
+ if (shape_preview_mask_size < ideal_shape_size)
+ {
+ shape_preview_mask_size = ideal_shape_size;
+ g_free (shape_preview_mask);
+ shape_preview_mask = g_malloc (ideal_shape_size);
+ }
+
+ memset (shape_preview_mask, 0,
+ (drawing_width * drawing_height) / 8 + drawing_height);
+ srcptr = rawframe + 3;
+
+ for (j = 0; j < drawing_height; j++)
+ {
+ k = j * ((7 + drawing_width) / 8);
+
+ for (i = 0; i < drawing_width; i++)
+ {
+ if ((*srcptr) & 128)
+ shape_preview_mask[k + i/8] |= (1 << (i&7));
+
+ srcptr += 4;
+ }
+ }
+ reshape_from_bitmap (dialog, shape_preview_mask);
+ }
+
+ /* clean up */
+ g_object_unref (buffer);
+
+ /* Update UI. */
+ show_playing_progress (dialog);
+
+ last_frame_index = dialog->settings.current_frame - dialog->settings.frame_min;
}
- /* Update frame counting UI widgets. */
- if (startframe_adjust)
+ /* Display the preview buffer. */
+ gdk_draw_rgb_image (gtk_widget_get_window (da),
+ (gtk_widget_get_style (da))->white_gc,
+ (gint) ((drawing_width - dialog->settings.scale * dialog->preview_width) / 2),
+ (gint) ((drawing_height - dialog->settings.scale * dialog->preview_height) / 2),
+ drawing_width, drawing_height,
+ (dialog->settings.num_frames == 1 ?
+ GDK_RGB_DITHER_MAX : DITHERTYPE),
+ preview_data, drawing_width * 3);
+
+ dialog->frames_lock = FALSE;
+}
+
+/* total_alpha_preview:
+ * Fill the @drawing_data with an alpha (grey chess) pattern.
+ * This uses a static array, copied over each line (with some shift to
+ * reproduce the pattern), using `memcpy()`.
+ * The reason why we keep the pattern in the statically allocated memory,
+ * instead of simply looping through @drawing_data and recreating the
+ * pattern is simply because `memcpy()` implementations are supposed to
+ * be more efficient than loops over an array. */
+static void
+total_alpha_preview (guchar *drawing_data,
+ guint drawing_width,
+ guint drawing_height)
+{
+ static guint alpha_line_width = 0;
+ static guchar *alpha_line = NULL;
+ gint i;
+
+ g_assert (drawing_width > 0);
+
+ /* If width change, we update the "alpha" line. */
+ if (alpha_line_width < drawing_width + 8)
{
- dialog->settings.start_frame = gtk_adjustment_get_value (startframe_adjust);
- if (start_from_first ||
- dialog->settings.start_frame < dialog->settings.frame_min ||
- dialog->settings.start_frame > dialog->settings.frame_max)
+ alpha_line_width = drawing_width + 8;
+
+ g_free (alpha_line);
+
+ /* A full line + 8 pixels (1 square). */
+ alpha_line = g_malloc (alpha_line_width * 3);
+
+ for (i = 0; i < alpha_line_width; i++)
{
- dialog->settings.start_frame = dialog->settings.frame_min;
+ /* 8 pixels dark grey, 8 pixels light grey, and so on. */
+ if (i & 8)
+ {
+ alpha_line[i * 3 + 0] =
+ alpha_line[i * 3 + 1] =
+ alpha_line[i * 3 + 2] = 102;
+ }
+ else
+ {
+ alpha_line[i * 3 + 0] =
+ alpha_line[i * 3 + 1] =
+ alpha_line[i * 3 + 2] = 154;
+ }
}
}
- else
+
+ for (i = 0; i < drawing_height; i++)
{
- dialog->settings.start_frame = dialog->settings.frame_min;
+ if (i & 8)
+ {
+ memcpy (&drawing_data[i * 3 * drawing_width],
+ alpha_line,
+ 3 * drawing_width);
+ }
+ else
+ {
+ /* Every 8 vertical pixels, we shift the horizontal line by 8 pixels. */
+ memcpy (&drawing_data[i * 3 * drawing_width],
+ alpha_line + 24,
+ 3 * drawing_width);
+ }
}
+}
- if (endframe_adjust)
+static void
+reshape_from_bitmap (AnimationPlayDialog *dialog,
+ const gchar *bitmap)
+{
+ static gchar *prev_bitmap = NULL;
+ static guint prev_width = -1;
+ static guint prev_height = -1;
+
+ if ((! prev_bitmap) ||
+ prev_width != dialog->shape_drawing_area_width ||
+ prev_height != dialog->shape_drawing_area_height ||
+ (memcmp (prev_bitmap, bitmap,
+ (dialog->shape_drawing_area_width *
+ dialog->shape_drawing_area_height) / 8 +
+ dialog->shape_drawing_area_height)))
{
- dialog->settings.end_frame = gtk_adjustment_get_value (endframe_adjust);
- if (end_at_last ||
- dialog->settings.end_frame < dialog->settings.frame_min ||
- dialog->settings.end_frame > dialog->settings.frame_max ||
- dialog->settings.end_frame < dialog->settings.start_frame)
+ GdkBitmap *shape_mask;
+
+ shape_mask = gdk_bitmap_create_from_data (gtk_widget_get_window (dialog->shape_window),
+ bitmap,
+ dialog->shape_drawing_area_width,
dialog->shape_drawing_area_height);
+ gtk_widget_shape_combine_mask (dialog->shape_window, shape_mask, 0, 0);
+ g_object_unref (shape_mask);
+
+ if (!prev_bitmap || prev_width != dialog->shape_drawing_area_width || prev_height !=
dialog->shape_drawing_area_height)
{
- dialog->settings.end_frame = dialog->settings.frame_max;
+ g_free(prev_bitmap);
+ prev_bitmap = g_malloc ((dialog->shape_drawing_area_width * dialog->shape_drawing_area_height) / 8
+ dialog->shape_drawing_area_height);
+ prev_width = dialog->shape_drawing_area_width;
+ prev_height = dialog->shape_drawing_area_height;
}
+
+ memcpy (prev_bitmap, bitmap, (dialog->shape_drawing_area_width * dialog->shape_drawing_area_height) /
8 + dialog->shape_drawing_area_height);
+ }
+}
+
+static void
+show_playing_progress (AnimationPlayDialog *dialog)
+{
+ gchar *text;
+
+ /* update the dialog's progress bar */
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress),
+ ((gfloat) (dialog->settings.current_frame - dialog->settings.frame_min) /
+ (gfloat) (dialog->settings.num_frames - 0.999)));
+
+ if (dialog->settings.frame_disposal != DISPOSE_TAGS || dialog->settings.frame_min == 1)
+ {
+ text = g_strdup_printf (_("Frame %d of %d"),
+ dialog->settings.current_frame,
+ dialog->settings.num_frames);
}
else
{
- dialog->settings.end_frame = dialog->settings.frame_max;
+ text = g_strdup_printf (_("Frame %d (%d) of %d"),
+ dialog->settings.current_frame - dialog->settings.frame_min + 1,
+ dialog->settings.current_frame, dialog->settings.num_frames);
}
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress), text);
+ g_free (text);
}
-static gboolean
-is_disposal_tag (const gchar *str,
- DisposeType *disposal,
- gint *taglength)
+static void
+show_loading_progress (AnimationPlayDialog *dialog,
+ gint layer_nb,
+ gint num_layers)
{
- if (strlen (str) != 9)
- return FALSE;
+ gchar *text;
+ gfloat load_rate = (gfloat) layer_nb / ((gfloat) num_layers - 0.999);
- if (strncmp (str, "(combine)", 9) == 0)
+ /* update the dialog's progress bar */
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress), load_rate);
+
+ text = g_strdup_printf (_("Loading animation %d %%"), (gint) (load_rate * 100));
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress), text);
+ g_free (text);
+
+ /* Forcing the UI to update even with intensive computation. */
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void
+do_back_step (AnimationPlayDialog *dialog)
+{
+ if (dialog->settings.current_frame == dialog->settings.start_frame)
{
- *taglength = 9;
- *disposal = DISPOSE_COMBINE;
- return TRUE;
+ dialog->settings.current_frame = dialog->settings.end_frame;
}
- else if (strncmp (str, "(replace)", 9) == 0)
+ else
{
- *taglength = 9;
- *disposal = DISPOSE_REPLACE;
- return TRUE;
+ --dialog->settings.current_frame;
}
+ render_frame (dialog, FALSE);
+}
- return FALSE;
+static void
+do_step (AnimationPlayDialog *dialog)
+{
+ dialog->settings.current_frame = dialog->settings.start_frame +
+ ((dialog->settings.current_frame - dialog->settings.start_frame + 1) %
+ (dialog->settings.end_frame - dialog->settings.start_frame + 1));
+
+ render_frame (dialog, FALSE);
}
-static DisposeType
-parse_disposal_tag (AnimationPlayDialog *dialog,
- const gchar *str)
+static void
+set_timer (guint new_timer)
{
- gint i;
- gint length = strlen (str);
+ static guint timer = 0;
- for (i = 0; i < length; i++)
+ if (timer)
{
- DisposeType rtn;
- gint dummy;
+ g_source_remove (timer);
+ }
+ timer = new_timer;
+}
- if (is_disposal_tag (&str[i], &rtn, &dummy))
- return rtn;
+static gboolean
+advance_frame_callback (AnimationPlayDialog *dialog)
+{
+ guint duration;
+ guint timer;
+
+ duration = dialog->frames[(dialog->settings.current_frame - dialog->settings.frame_min + 1) %
+ dialog->settings.num_frames]->duration;
+ if (duration <= 0)
+ {
+ duration = (guint) (1000.0 / ((AnimationPlayDialog *) dialog)->settings.frame_rate);
}
- return dialog->settings.frame_disposal;
+ timer = g_timeout_add (duration,
+ (GSourceFunc) advance_frame_callback,
+ (AnimationPlayDialog *) dialog);
+ set_timer (timer);
+
+ do_step (dialog);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+show_goto_progress (guint goto_frame,
+ AnimationPlayDialog *dialog)
+{
+ gchar *text;
+
+ /* update the dialog's progress bar */
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->progress),
+ ((gfloat) (dialog->settings.current_frame - dialog->settings.frame_min) /
+ (gfloat) (dialog->settings.num_frames - 0.999)));
+
+ if (dialog->settings.frame_disposal != DISPOSE_TAGS || dialog->settings.frame_min == 1)
+ text = g_strdup_printf (_("Go to frame %d of %d"), goto_frame, dialog->settings.num_frames);
+ else
+ text = g_strdup_printf (_("Go to frame %d (%d) of %d"), goto_frame - dialog->settings.frame_min + 1,
goto_frame, dialog->settings.num_frames);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dialog->progress), text);
+ g_free (text);
}
-/* Util. */
+/************ UTILS ****************/
+
+static void
+connect_accelerators (GtkUIManager *ui_manager,
+ GtkActionGroup *group)
+{
+ GList *action_list;
+ GList *iter;
+
+ action_list = gtk_action_group_list_actions (group);
+ iter = action_list;
+ while (iter)
+ {
+ /* Make sure all the action's accelerator are correctly connected,
+ * even when there are no associated UI item. */
+ GtkAction *action = GTK_ACTION (iter->data);
+
+ gtk_action_set_accel_group (action,
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_action_connect_accelerator (action);
+
+ iter = iter->next;
+ }
+ g_list_free (action_list);
+}
/* get_fps:
* Frame rate proposed as default.
@@ -3044,6 +3005,37 @@ get_proxy (AnimationPlayDialog *dialog,
}
}
+static void
+update_scale (AnimationPlayDialog *dialog,
+ gdouble scale)
+{
+ guint expected_drawing_area_width;
+ guint expected_drawing_area_height;
+
+ /* FIXME: scales under 0.5 are broken. See bug 690265. */
+ if (scale <= 0.5)
+ scale = 0.51;
+
+ expected_drawing_area_width = dialog->preview_width * scale;
+ expected_drawing_area_height = dialog->preview_height * scale;
+
+ /* We don't update dialog->settings.scale directly because this might
+ * end up not being the real scale. Instead we request this size for
+ * the drawing areas, and the actual scale update will be done on the
+ * callback when size is actually allocated. */
+ gtk_widget_set_size_request (dialog->drawing_area, expected_drawing_area_width,
expected_drawing_area_height);
+ gtk_widget_set_size_request (dialog->shape_drawing_area, expected_drawing_area_width,
expected_drawing_area_height);
+ /* I force the shape window to a smaller size if we scale down. */
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (dialog->view_actions,
"detach"))))
+ {
+ gint x, y;
+
+ gdk_window_get_origin (gtk_widget_get_window (dialog->shape_window), &x, &y);
+ gtk_window_reshow_with_initial_size (GTK_WINDOW (dialog->shape_window));
+ gtk_window_move (GTK_WINDOW (dialog->shape_window), x, y);
+ }
+}
+
static gdouble
get_scale (AnimationPlayDialog *dialog,
gint index)
@@ -3166,7 +3158,50 @@ clean_exit (AnimationPlayDialog *dialog)
gimp_quit ();
}
-/* tag util. */
+/******* TAG UTILS **************/
+
+static gboolean
+is_disposal_tag (const gchar *str,
+ DisposeType *disposal,
+ gint *taglength)
+{
+ if (strlen (str) != 9)
+ return FALSE;
+
+ if (strncmp (str, "(combine)", 9) == 0)
+ {
+ *taglength = 9;
+ *disposal = DISPOSE_COMBINE;
+ return TRUE;
+ }
+ else if (strncmp (str, "(replace)", 9) == 0)
+ {
+ *taglength = 9;
+ *disposal = DISPOSE_REPLACE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static DisposeType
+parse_disposal_tag (AnimationPlayDialog *dialog,
+ const gchar *str)
+{
+ gint i;
+ gint length = strlen (str);
+
+ for (i = 0; i < length; i++)
+ {
+ DisposeType rtn;
+ gint dummy;
+
+ if (is_disposal_tag (&str[i], &rtn, &dummy))
+ return rtn;
+ }
+
+ return dialog->settings.frame_disposal;
+}
static gboolean
is_ms_tag (const gchar *str,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]