[gimp] Bug 338380: zoom and scrolling feature on animation playback plugin.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] Bug 338380: zoom and scrolling feature on animation playback plugin.
- Date: Sat, 20 Apr 2013 23:29:02 +0000 (UTC)
commit 5c38715cce591c80409dd20d037ef28b1a06fcfc
Author: Jehan <jehan girinstud io>
Date: Mon Dec 17 00:26:20 2012 +0900
Bug 338380: zoom and scrolling feature on animation playback plugin.
plug-ins/common/animation-play.c | 742 ++++++++++++++++++++++++++-------------
1 file changed, 503 insertions(+), 239 deletions(-)
---
diff --git a/plug-ins/common/animation-play.c b/plug-ins/common/animation-play.c
index a0e6fb6..f1b7cf8 100644
--- a/plug-ins/common/animation-play.c
+++ b/plug-ins/common/animation-play.c
@@ -3,6 +3,7 @@
*
* (c) Adam D. Moss : 1997-2000 : adam gimp org : adam foxbox org
* (c) Mircea Purdea : 2009 : someone_else exhalus net
+ * (c) Jehan : 2012 : jehan at girinstud.io
*
* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
@@ -27,8 +28,6 @@
*
* speedups (caching? most bottlenecks seem to be in pixelrgns)
* -> do pixelrgns properly!
- *
- * write other half of the user interface (zoom, disposal &c)
*/
#include "config.h"
@@ -62,18 +61,29 @@ typedef struct
}
AnimationSettings;
+/* for shaping */
+typedef struct
+{
+ gint x, y;
+} CursorOffset;
+
/* Declare local functions. */
-static void query (void);
-static void run (const gchar *name,
- gint nparams,
- const GimpParam *param,
- gint *nreturn_vals,
- GimpParam **return_vals);
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
static void initialize (void);
static void build_dialog (gchar *imagename);
static void refresh_dialog (gchar *imagename);
+static void da_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data);
+static void sda_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data);
+
static void window_destroy (GtkWidget *widget);
static void play_callback (GtkToggleAction *action);
static void step_back_callback (GtkAction *action);
@@ -89,6 +99,10 @@ static void speedcombo_changed (GtkWidget *combo,
gpointer data);
static void fpscombo_changed (GtkWidget *combo,
gpointer data);
+static void zoomcombo_activated (GtkEntry *combo,
+ gpointer data);
+static void zoomcombo_changed (GtkWidget *combo,
+ gpointer data);
static gboolean repaint_sda (GtkWidget *darea,
GdkEventExpose *event,
gpointer data);
@@ -99,11 +113,13 @@ static gboolean repaint_da (GtkWidget *darea,
static void init_frames (void);
static void render_frame (gint32 whichframe);
static void show_frame (void);
-static void total_alpha_preview (guchar *ptr);
-static void init_preview (void);
+static void total_alpha_preview (void);
+static void update_alpha_preview (void);
static void update_combobox (void);
static gdouble get_duration_factor (gint index);
static gint get_fps (gint index);
+static gdouble get_scale (gint index);
+static void update_scale (gdouble scale);
/* tag util functions*/
@@ -127,31 +143,50 @@ const GimpPlugInInfo PLUG_IN_INFO =
/* Global widgets'n'stuff */
-static GtkWidget *window = NULL;
-static GtkUIManager *ui_manager = NULL;
-static guchar *preview_data = NULL;
-static GtkWidget *drawing_area = NULL;
-static GtkWidget *shape_drawing_area = NULL;
-static guchar *shape_drawing_area_data = NULL;
-static guchar *drawing_area_data = NULL;
+static GtkWidget *window = NULL;
+static GdkWindow *root_win = NULL;
+static GtkUIManager *ui_manager = NULL;
static GtkWidget *progress;
-static guint width = -1, height = -1;
-static guchar *preview_alpha1_data = NULL;
-static guchar *preview_alpha2_data = NULL;
+static GtkWidget *speedcombo = NULL;
+static GtkWidget *fpscombo = NULL;
+static GtkWidget *zoomcombo = NULL;
+static GtkWidget *frame_disposal_combo = NULL;
+
static gint32 image_id;
-static guchar *rawframe = NULL;
-static gint32 *frames = NULL;
-static guint32 *frame_durations = NULL;
-static gint32 total_frames;
-static guint frame_number = 0;
-static gint32 *layers = NULL;
-static gint32 total_layers;
-static gboolean playing = FALSE;
-static guint timer = 0;
+static guint width = -1,
+ height = -1;
+static gint32 *layers = NULL;
+static gint32 total_layers = 0;
static GimpImageBaseType imagetype;
-static guchar *palette = NULL;
+static guchar *palette = NULL;
static gint ncolours;
+static GtkWidget *drawing_area = NULL;
+static guchar *drawing_area_data = NULL;
+static guint drawing_area_width = -1,
+ drawing_area_height = -1;
+static guchar *preview_alpha1_data = NULL;
+static guchar *preview_alpha2_data = NULL;
+
+static GtkWidget *shape_window = NULL;
+static GtkWidget *shape_drawing_area = NULL;
+static guchar *shape_drawing_area_data = NULL;
+static guint shape_drawing_area_width = -1,
+ shape_drawing_area_height = -1;
+static gchar *shape_preview_mask = NULL;
+
+
+static gint32 total_frames = 0;
+static gint32 *frames = NULL;
+static guchar *rawframe = NULL;
+static guint32 *frame_durations = NULL;
+static guint frame_number = 0;
+
+static gboolean playing = FALSE;
+static guint timer = 0;
+static gboolean detached = FALSE;
+static gdouble scale, shape_scale;
+
/* Default settings. */
static AnimationSettings settings =
{
@@ -160,21 +195,6 @@ static AnimationSettings settings =
100 /* ms */
};
-
-/* for shaping */
-typedef struct
-{
- gint x, y;
-} CursorOffset;
-
-static gchar *shape_preview_mask = NULL;
-static GtkWidget *shape_window = NULL;
-static GdkWindow *root_win = NULL;
-static gboolean detached = FALSE;
-static GtkWidget *speedcombo = NULL;
-static GtkWidget *fpscombo = NULL;
-static GtkWidget *frame_disposal_combo = NULL;
-
MAIN ()
static void
@@ -249,22 +269,30 @@ static void
reshape_from_bitmap (const gchar *bitmap)
{
static gchar *prev_bitmap = NULL;
+ static guint prev_width = -1;
+ static guint prev_height = -1;
if ((!prev_bitmap) ||
- (memcmp (prev_bitmap, bitmap, (width * height) / 8 + height)))
+ prev_width != shape_drawing_area_width || prev_height != shape_drawing_area_height ||
+ (memcmp (prev_bitmap, bitmap, (shape_drawing_area_width * shape_drawing_area_height) / 8 +
shape_drawing_area_height)))
{
GdkBitmap *shape_mask;
shape_mask = gdk_bitmap_create_from_data (gtk_widget_get_window (shape_window),
bitmap,
- width, height);
+ shape_drawing_area_width, shape_drawing_area_height);
gtk_widget_shape_combine_mask (shape_window, shape_mask, 0, 0);
g_object_unref (shape_mask);
- if (!prev_bitmap)
- prev_bitmap = g_malloc ((width * height) / 8 + height);
+ if (!prev_bitmap || prev_width != shape_drawing_area_width || prev_height != shape_drawing_area_height)
+ {
+ g_free(prev_bitmap);
+ prev_bitmap = g_malloc ((shape_drawing_area_width * shape_drawing_area_height) / 8 +
shape_drawing_area_height);
+ prev_width = shape_drawing_area_width;
+ prev_height = shape_drawing_area_height;
+ }
- memcpy (prev_bitmap, bitmap, (width * height) / 8 + height);
+ memcpy (prev_bitmap, bitmap, (shape_drawing_area_width * shape_drawing_area_height) / 8 +
shape_drawing_area_height);
}
}
@@ -293,6 +321,101 @@ button_press (GtkWidget *widget,
return FALSE;
}
+/*
+ * Update the actual drawing area metrics, which may be different as requested,
+ * because there is no full control of the WM.
+ * data is always NULL. */
+static void
+da_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data)
+{
+ if (allocation->width == drawing_area_width && allocation->height == drawing_area_height)
+ return;
+
+ drawing_area_width = allocation->width;
+ drawing_area_height = allocation->height;
+ scale = MIN ((gdouble) drawing_area_width / (gdouble) width, (gdouble) drawing_area_height / (gdouble)
height);
+
+ g_free (drawing_area_data);
+ drawing_area_data = g_malloc (drawing_area_width * drawing_area_height * 3);
+
+ update_alpha_preview ();
+
+ if (! detached)
+ {
+ /* Update the zoom information. */
+ GtkEntry *zoomcombo_text_child;
+
+ zoomcombo_text_child = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (zoomcombo)));
+ if (zoomcombo_text_child)
+ {
+ char* new_entry_text = g_strdup_printf (_("%.1f %%"), scale * 100.0);
+
+ gtk_entry_set_text (zoomcombo_text_child, new_entry_text);
+ g_free (new_entry_text);
+ }
+
+ /* Update the rawframe. */
+ g_free (rawframe);
+ rawframe = g_malloc ((unsigned long) drawing_area_width * drawing_area_height * 4);
+
+ /* As we re-allocated the drawn data, let's render it again. */
+ if (frame_number < total_frames)
+ render_frame (frame_number);
+ }
+ else
+ {
+ /* Set "alpha grid" background. */
+ total_alpha_preview ();
+ repaint_da(drawing_area, NULL, NULL);
+ }
+}
+
+/*
+ * Update the actual shape drawing area metrics, which may be different as requested,
+ * They *should* be the same as the drawing area, but the safe way is to make sure
+ * and process it separately.
+ * data is always NULL. */
+static void
+sda_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data)
+{
+ if (allocation->width == shape_drawing_area_width && allocation->height == shape_drawing_area_height)
+ return;
+
+ shape_drawing_area_width = allocation->width;
+ shape_drawing_area_height = allocation->height;
+ shape_scale = MIN ((gdouble) shape_drawing_area_width / (gdouble) width, (gdouble)
shape_drawing_area_height / (gdouble) height);
+
+ g_free (shape_drawing_area_data);
+ g_free (shape_preview_mask);
+
+ shape_drawing_area_data = g_malloc (shape_drawing_area_width * shape_drawing_area_height * 3);
+ shape_preview_mask = g_malloc ((shape_drawing_area_width * shape_drawing_area_height) / 8 + 1 +
shape_drawing_area_height);
+
+ if (detached)
+ {
+ /* Update the zoom information. */
+ GtkEntry *zoomcombo_text_child;
+
+ zoomcombo_text_child = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (zoomcombo)));
+ if (zoomcombo_text_child)
+ {
+ char* new_entry_text = g_strdup_printf (_("%.1f %%"), shape_scale * 100.0);
+
+ gtk_entry_set_text (zoomcombo_text_child, new_entry_text);
+ g_free (new_entry_text);
+ }
+
+ /* Update the rawframe. */
+ g_free (rawframe);
+ rawframe = g_malloc ((unsigned long) shape_drawing_area_width * shape_drawing_area_height * 4);
+
+ if (frame_number < total_frames)
+ render_frame (frame_number);
+ }
+}
+
static gboolean
shape_pressed (GtkWidget *widget,
GdkEventButton *event)
@@ -369,9 +492,11 @@ repaint_da (GtkWidget *darea,
gdk_draw_rgb_image (gtk_widget_get_window (darea),
style->white_gc,
- 0, 0, width, height,
+ (gint) ((drawing_area_width - scale * width) / 2),
+ (gint) ((drawing_area_height - scale * height) / 2),
+ drawing_area_width, drawing_area_height,
(total_frames == 1) ? GDK_RGB_DITHER_MAX : DITHERTYPE,
- drawing_area_data, width * 3);
+ drawing_area_data, drawing_area_width * 3);
return TRUE;
}
@@ -385,9 +510,11 @@ repaint_sda (GtkWidget *darea,
gdk_draw_rgb_image (gtk_widget_get_window (darea),
style->white_gc,
- 0, 0, width, height,
+ (gint) ((shape_drawing_area_width - shape_scale * width) / 2),
+ (gint) ((shape_drawing_area_height - shape_scale * height) / 2),
+ shape_drawing_area_width, shape_drawing_area_height,
(total_frames == 1) ? GDK_RGB_DITHER_MAX : DITHERTYPE,
- shape_drawing_area_data, width * 3);
+ shape_drawing_area_data, shape_drawing_area_width * 3);
return TRUE;
}
@@ -422,42 +549,36 @@ detach_callback (GtkToggleAction *action)
if (detached)
{
+ gint x, y;
+
/* Create a total-alpha buffer merely for the not-shaped
drawing area to now display. */
- drawing_area_data = g_malloc (width * height * 3);
- total_alpha_preview (drawing_area_data);
-
gtk_window_set_screen (GTK_WINDOW (shape_window),
gtk_widget_get_screen (drawing_area));
- if (gtk_widget_get_realized (drawing_area))
- {
- gint x, y;
+ gtk_widget_show (shape_window);
- gdk_window_get_origin (gtk_widget_get_window (drawing_area), &x, &y);
+ if (!gtk_widget_get_realized (drawing_area))
+ gtk_widget_realize (drawing_area);
+ if (!gtk_widget_get_realized (shape_drawing_area))
+ gtk_widget_realize (shape_drawing_area);
- gtk_window_move (GTK_WINDOW (shape_window), x + 6, y + 6);
- }
+ gdk_window_get_origin (gtk_widget_get_window (drawing_area), &x, &y);
- gtk_widget_show (shape_window);
+ gtk_window_move (GTK_WINDOW (shape_window), x + 6, y + 6);
gdk_window_set_back_pixmap (gtk_widget_get_window (shape_drawing_area), NULL, TRUE);
- memset (shape_preview_mask, 0, (width * height) / 8 + height);
- render_frame (frame_number);
- }
- else
- {
- g_free (drawing_area_data);
- drawing_area_data = shape_drawing_area_data;
-
- render_frame (frame_number);
- gtk_widget_hide (shape_window);
+ /* Set "alpha grid" background. */
+ total_alpha_preview ();
+ repaint_da(drawing_area, NULL, NULL);
}
+ else
+ gtk_widget_hide (shape_window);
- gtk_widget_queue_draw (drawing_area);
+ render_frame (frame_number);
}
static GtkUIManager *
@@ -601,6 +722,9 @@ static void
refresh_dialog (gchar *imagename)
{
gchar *name;
+ GdkScreen *screen;
+ guint screen_width, screen_height;
+ gint window_width, window_height;
/* Image Name */
name = g_strconcat (_("Animation Playback:"), " ", imagename, NULL);
@@ -608,9 +732,29 @@ refresh_dialog (gchar *imagename)
g_free (name);
/* Update GUI size. */
- gtk_widget_set_size_request (drawing_area, width, height);
- gtk_widget_set_size_request (shape_drawing_area, width, height);
- gtk_window_reshow_with_initial_size (GTK_WINDOW (window));
+ screen = gtk_widget_get_screen (window);
+ screen_height = gdk_screen_get_height (screen);
+ screen_width = gdk_screen_get_width (screen);
+ gtk_window_get_size (GTK_WINDOW (window), &window_width, &window_height);
+
+ /* 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)
+ {
+ guint expected_drawing_area_width = MAX (1, width - window_width + screen_width);
+ guint expected_drawing_area_height = MAX (1, height - window_height + screen_height);
+ gdouble expected_scale = MIN ((gdouble) expected_drawing_area_width / (gdouble) width,
+ (gdouble) expected_drawing_area_height / (gdouble) height);
+ update_scale (expected_scale);
+
+ /* 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 any case. */
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ MIN (expected_drawing_area_width + 20, screen_width - 60),
+ MIN (expected_drawing_area_height + 90, screen_height - 60));
+
+ gtk_window_reshow_with_initial_size (GTK_WINDOW (window));
+ }
}
static void
@@ -625,20 +769,15 @@ build_dialog (gchar *imagename)
GtkToolItem *item;
GtkAction *action;
GdkCursor *cursor;
- gchar *name;
gint index;
gchar *text;
gimp_ui_init (PLUG_IN_BINARY, TRUE);
- name = g_strconcat (_("Animation Playback:"), " ", imagename, NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_window_set_title (GTK_WINDOW (window), name);
gtk_window_set_role (GTK_WINDOW (window), "animation-playback");
- g_free (name);
-
g_signal_connect (window, "destroy",
G_CALLBACK (window_destroy),
NULL);
@@ -665,36 +804,73 @@ build_dialog (gchar *imagename)
gtk_tool_item_set_expand (item, TRUE);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
- gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
- abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ /* Alignment for the scrolling window, which can be resized by the user. */
+ abox = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
gtk_box_pack_start (GTK_BOX (vbox), abox, TRUE, TRUE, 0);
gtk_widget_show (abox);
- frame = gtk_frame_new (NULL);
- gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ frame = gtk_scrolled_window_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (frame), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_widget_show (frame);
+ /* I add the drawing area inside an alignment box to prevent it from being resized. */
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (frame), abox);
+ gtk_widget_show (abox);
+
+ /* Build a drawing area, with a default size same as the image */
drawing_area = gtk_drawing_area_new ();
- gtk_widget_set_size_request (drawing_area, width, height);
gtk_widget_add_events (drawing_area, GDK_BUTTON_PRESS_MASK);
- gtk_container_add (GTK_CONTAINER (frame), drawing_area);
+ gtk_container_add (GTK_CONTAINER (abox), drawing_area);
gtk_widget_show (drawing_area);
+ g_signal_connect (drawing_area, "size-allocate",
+ G_CALLBACK(da_size_callback),
+ NULL);
g_signal_connect (drawing_area, "button-press-event",
G_CALLBACK (button_press),
NULL);
+ /* Lower option bar. */
+
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
+ /* Progress bar. */
+
progress = gtk_progress_bar_new ();
gtk_box_pack_end (GTK_BOX (hbox), progress, TRUE, TRUE, 0);
gtk_widget_show (progress);
+ /* Zoom */
+ zoomcombo = gtk_combo_box_text_new_with_entry ();
+ gtk_box_pack_end (GTK_BOX (hbox), zoomcombo, FALSE, FALSE, 0);
+ gtk_widget_show (zoomcombo);
+ for (index = 0; index < 5; index++)
+ {
+ /* list is given in "fps" - frames per second */
+ text = g_strdup_printf (_("%.1f %%"), get_scale (index) * 100.0);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (zoomcombo), text);
+ g_free (text);
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (zoomcombo), 2); /* 1.0 by default. */
+
+ g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (zoomcombo))),
+ "activate",
+ G_CALLBACK (zoomcombo_activated),
+ NULL);
+ g_signal_connect (zoomcombo, "changed",
+ G_CALLBACK (zoomcombo_changed),
+ NULL);
+
+ gimp_help_set_help_data (zoomcombo, _("Zoom"), NULL);
+
/* fps combo */
fpscombo = gtk_combo_box_text_new ();
gtk_box_pack_end (GTK_BOX (hbox), fpscombo, FALSE, FALSE, 0);
@@ -755,23 +931,25 @@ build_dialog (gchar *imagename)
gtk_combo_box_set_active (GTK_COMBO_BOX (frame_disposal_combo), settings.default_frame_disposal);
g_signal_connect (frame_disposal_combo, "changed",
- G_CALLBACK (framecombo_changed),
- NULL);
+ G_CALLBACK (framecombo_changed),
+ NULL);
gtk_box_pack_end (GTK_BOX (hbox), frame_disposal_combo, FALSE, FALSE, 0);
gtk_widget_show (frame_disposal_combo);
+ gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+ gtk_window_set_default_size (GTK_WINDOW (window), width + 20, height + 90);
gtk_widget_show (window);
- /* let's get into shape. */
+ /* shape_drawing_area for detached feature. */
shape_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_resizable (GTK_WINDOW (shape_window), FALSE);
shape_drawing_area = gtk_drawing_area_new ();
- gtk_widget_set_size_request (shape_drawing_area, width, height);
gtk_container_add (GTK_CONTAINER (shape_window), shape_drawing_area);
gtk_widget_show (shape_drawing_area);
gtk_widget_add_events (shape_drawing_area, GDK_BUTTON_PRESS_MASK);
- gtk_widget_realize (shape_window);
+ gtk_widget_realize (shape_drawing_area);
gdk_window_set_back_pixmap (gtk_widget_get_window (shape_window), NULL, FALSE);
@@ -780,6 +958,9 @@ build_dialog (gchar *imagename)
gdk_window_set_cursor (gtk_widget_get_window (shape_window), cursor);
gdk_cursor_unref (cursor);
+ g_signal_connect(shape_drawing_area, "size-allocate",
+ G_CALLBACK(sda_size_callback),
+ NULL);
g_signal_connect (shape_window, "button-press-event",
G_CALLBACK (shape_pressed),
NULL);
@@ -801,15 +982,103 @@ build_dialog (gchar *imagename)
G_CALLBACK (repaint_sda),
NULL);
+ /* We request a minimum size *after* having connecting the
+ * size-allocate signal for correct initialization. */
+ gtk_widget_set_size_request (drawing_area, width, height);
+ gtk_widget_set_size_request (shape_drawing_area, width, height);
+
root_win = gdk_get_default_root_window ();
}
static void
-initialize (void)
+init_frames (void)
{
- guint previous_width = width;
- guint previous_height = height;
+ /* Frames are associated to an unused image. */
+ static gint32 frames_image_id;
+ gint i;
+ gint32 new_frame, previous_frame, new_layer;
+ gboolean animated;
+ GtkAction *action;
+ gint duration = 0;
+ DisposeType disposal = settings.default_frame_disposal;
+ gchar *layer_name;
+
+ total_frames = total_layers;
+
+ /* Cleanup before re-generation. */
+ if (frames)
+ {
+ gimp_image_delete (frames_image_id);
+ g_free (frames);
+ g_free (frame_durations);
+ }
+ frames = g_try_malloc0_n (total_frames, sizeof (gint32));
+ frame_durations = g_try_malloc0_n (total_frames, sizeof (guint32));
+ if (! frames || ! frame_durations)
+ {
+ gimp_message (_("Memory could not be allocated to the frame container."));
+ gtk_main_quit ();
+ gimp_quit ();
+ return;
+ }
+ frames_image_id = gimp_image_new (width, height, imagetype);
+ /* Save processing time and memory by not saving history and merged frames. */
+ gimp_image_undo_disable (frames_image_id);
+
+ for (i = 0; i < total_frames; i++)
+ {
+ layer_name = gimp_item_get_name (layers[total_layers - (i + 1)]);
+ if (layer_name)
+ {
+ duration = parse_ms_tag (layer_name);
+ disposal = parse_disposal_tag (layer_name);
+ g_free (layer_name);
+ }
+ if (i > 0 && disposal != DISPOSE_REPLACE)
+ {
+ previous_frame = gimp_layer_copy (frames[i - 1]);
+ gimp_image_insert_layer (frames_image_id, previous_frame, 0, -1);
+ gimp_item_set_visible (previous_frame, TRUE);
+ }
+ new_layer = gimp_layer_new_from_drawable (layers[total_layers - (i + 1)], frames_image_id);
+ gimp_image_insert_layer (frames_image_id, new_layer, 0, -1);
+ gimp_item_set_visible (new_layer, TRUE);
+ new_frame = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
+ frames[i] = new_frame;
+ gimp_item_set_visible (new_frame, FALSE);
+
+ if (duration <= 0)
+ duration = settings.default_frame_duration;
+ frame_durations[i] = (guint32) duration;
+ }
+
+ /* Update the UI. */
+ animated = total_frames >= 2;
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/play");
+ gtk_action_set_sensitive (action, animated);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/step-back");
+ gtk_action_set_sensitive (action, animated);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/step");
+ gtk_action_set_sensitive (action, animated);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/rewind");
+ gtk_action_set_sensitive (action, animated);
+
+ /* Keep the same frame number, unless it is now invalid. */
+ if (frame_number >= total_frames)
+ frame_number = 0;
+}
+
+static void
+initialize (void)
+{
/* Freeing existing data after a refresh. */
g_free (layers);
g_free (palette);
@@ -843,51 +1112,60 @@ initialize (void)
ncolours = 256;
}
- if (previous_width != width || previous_height != height)
- {
- g_free (rawframe);
- rawframe = g_malloc (width * height * 4);
- init_preview ();
- }
-
- if (window)
- refresh_dialog (gimp_image_get_name (image_id));
- else
+ if (!window)
build_dialog (gimp_image_get_name (image_id));
+ refresh_dialog (gimp_image_get_name (image_id));
init_frames ();
render_frame (frame_number);
show_frame ();
}
-
/* Rendering Functions */
static void
render_frame (gint32 whichframe)
{
- GtkStyle *shape_style = gtk_widget_get_style (shape_drawing_area);
- GtkStyle *drawing_style = gtk_widget_get_style (drawing_area);
GeglBuffer *buffer;
- gint drawable_id;
- gint i, j, k;
+ gint i, j, k;
guchar *srcptr;
guchar *destptr;
+ GtkWidget *da;
+ guint drawing_width, drawing_height;
+ gdouble drawing_scale;
+ guchar *preview_data;
g_assert (whichframe < total_frames);
- drawable_id = frames[whichframe];
- buffer = gimp_drawable_get_buffer (drawable_id);
+ if (detached)
+ {
+ da = shape_drawing_area;
+ preview_data = shape_drawing_area_data;
+ drawing_width = shape_drawing_area_width;
+ drawing_height = shape_drawing_area_height;
+ drawing_scale = shape_scale;
+ }
+ else
+ {
+ da = drawing_area;
+ preview_data = drawing_area_data;
+ drawing_width = drawing_area_width;
+ drawing_height = drawing_area_height;
+ drawing_scale = scale;
+
+ /* Set "alpha grid" background. */
+ total_alpha_preview ();
+ }
- /* Initialise and fetch the whole raw new frame */
- gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
- babl_format ("R'G'B'A u8"),
- rawframe, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ buffer = gimp_drawable_get_buffer (frames[whichframe]);
- /* render... */
- total_alpha_preview (preview_data);
+ /* Fetch and scale the whole raw new frame */
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, drawing_width, drawing_height),
+ drawing_scale, babl_format ("R'G'B'A u8"),
+ rawframe, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
- i = width * height;
+ /* Number of pixels. */
+ i = drawing_width * drawing_height;
destptr = preview_data;
srcptr = rawframe;
while (i--)
@@ -909,14 +1187,14 @@ render_frame (gint32 whichframe)
/* calculate the shape mask */
if (detached)
{
- memset (shape_preview_mask, 0, (width * height) / 8 + height);
+ memset (shape_preview_mask, 0, (drawing_width * drawing_height) / 8 + drawing_height);
srcptr = rawframe + 3;
- for (j = 0; j < height; j++)
+ for (j = 0; j < drawing_height; j++)
{
- k = j * ((7 + width) / 8);
+ k = j * ((7 + drawing_width) / 8);
- for (i = 0; i < width; i++)
+ for (i = 0; i < drawing_width; i++)
{
if ((*srcptr) & 128)
shape_preview_mask[k + i/8] |= (1 << (i&7));
@@ -928,12 +1206,14 @@ render_frame (gint32 whichframe)
}
/* Display the preview buffer. */
- gdk_draw_rgb_image (gtk_widget_get_window (detached? shape_drawing_area : drawing_area),
- detached? shape_style->white_gc : drawing_style->white_gc,
- 0, 0, width, height,
+ gdk_draw_rgb_image (gtk_widget_get_window (da),
+ (gtk_widget_get_style (da))->white_gc,
+ (gint) ((drawing_width - drawing_scale * width) / 2),
+ (gint) ((drawing_height - drawing_scale * height) / 2),
+ drawing_width, drawing_height,
(total_frames == 1 ?
GDK_RGB_DITHER_MAX : DITHERTYPE),
- preview_data, width * 3);
+ preview_data, drawing_width * 3);
/* clean up */
g_object_unref (buffer);
@@ -955,22 +1235,17 @@ show_frame (void)
}
static void
-init_preview (void)
+update_alpha_preview (void)
{
gint i;
- /* Cleaning, after a refresh with different image size. */
- g_free (preview_data);
- g_free (shape_preview_mask);
g_free (preview_alpha1_data);
g_free (preview_alpha2_data);
- preview_data = g_malloc (width * height * 3);
- shape_preview_mask = g_malloc ((width * height) / 8 + 1 + height);
- preview_alpha1_data = g_malloc (width * 3);
- preview_alpha2_data = g_malloc (width * 3);
+ preview_alpha1_data = g_malloc (drawing_area_width * 3);
+ preview_alpha2_data = g_malloc (drawing_area_width * 3);
- for (i = 0; i < width; i++)
+ for (i = 0; i < drawing_area_width; i++)
{
if (i & 8)
{
@@ -991,30 +1266,19 @@ init_preview (void)
preview_alpha2_data[i*3 + 2] = 102;
}
}
-
- if (detached)
- {
- g_free (drawing_area_data);
- drawing_area_data = g_malloc (width * height * 3);
- total_alpha_preview (drawing_area_data);
- memset (shape_preview_mask, 0, (width * height) / 8 + height);
- }
- else
- drawing_area_data = preview_data;
- shape_drawing_area_data = preview_data;
}
static void
-total_alpha_preview (guchar *ptr)
+total_alpha_preview (void)
{
gint i;
- for (i = 0; i < height; i++)
+ for (i = 0; i < drawing_area_height; i++)
{
if (i & 8)
- memcpy (&ptr[i * 3 * width], preview_alpha1_data, 3 * width);
+ memcpy (&drawing_area_data[i * 3 * drawing_area_width], preview_alpha1_data, 3 * drawing_area_width);
else
- memcpy (&ptr[i * 3 * width], preview_alpha2_data, 3 * width);
+ memcpy (&drawing_area_data[i * 3 * drawing_area_width], preview_alpha2_data, 3 * drawing_area_width);
}
}
@@ -1160,6 +1424,40 @@ get_fps (gint index)
}
}
+static gdouble
+get_scale (gint index)
+{
+ switch (index)
+ {
+ case 0:
+ return 0.51;
+ case 1:
+ return 1.0;
+ case 2:
+ return 1.25;
+ case 3:
+ return 1.5;
+ case 4:
+ return 2.0;
+ default:
+ {
+ /* likely -1 returned if there is no active item from the list.
+ * Try a text conversion, locale-aware in such a case, assuming people write in percent. */
+ gchar* active_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (zoomcombo));
+ gdouble zoom = g_strtod (active_text, NULL);
+
+ /* Negative scales are inconsistent. And we want to avoid huge scaling. */
+ if (zoom > 300.0)
+ zoom = 300.0;
+ else if (zoom <= 50.0)
+ /* FIXME: scales under 0.5 are broken. See bug 690265. */
+ zoom = 50.1;
+ g_free (active_text);
+ return zoom / 100.0;
+ }
+ }
+}
+
static void
step_back_callback (GtkAction *action)
{
@@ -1282,9 +1580,60 @@ speedcombo_changed (GtkWidget *combo, gpointer data)
}
static void
+update_scale (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.501;
+
+ expected_drawing_area_width = width * scale;
+ expected_drawing_area_height = height * scale;
+
+ gtk_widget_set_size_request (drawing_area, expected_drawing_area_width, expected_drawing_area_height);
+ gtk_widget_set_size_request (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 (detached)
+ {
+ gint x, y;
+
+ gdk_window_get_origin (gtk_widget_get_window (shape_window), &x, &y);
+ gtk_window_reshow_with_initial_size (GTK_WINDOW (shape_window));
+ gtk_window_move (GTK_WINDOW (shape_window), x, y);
+ }
+}
+
+/*
+ * Callback emitted when the user hits the Enter key of the zoom combo.
+ */
+static void
+zoomcombo_activated (GtkEntry *combo, gpointer data)
+{
+ update_scale (get_scale (-1));
+}
+
+/*
+ * Callback emitted when the user selects a zoom in the dropdown,
+ * or edits the text entry.
+ * We don't want to process manual edits because it greedily emits
+ * signals after each character deleted or added.
+ */
+static void
+zoomcombo_changed (GtkWidget *combo, gpointer data)
+{
+ gint index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ /* If no index, user is probably editing by hand. We wait for him to click "Enter". */
+ if (index != -1)
+ update_scale (get_scale (index));
+}
+
+static void
fpscombo_changed (GtkWidget *combo, gpointer data)
{
- settings.default_frame_duration = 1000 / get_fps(gtk_combo_box_get_active (GTK_COMBO_BOX (combo)));
+ settings.default_frame_duration = 1000 / get_fps (gtk_combo_box_get_active (GTK_COMBO_BOX (combo)));
}
static void
@@ -1415,88 +1764,3 @@ parse_disposal_tag (const gchar *str)
return settings.default_frame_disposal;
}
-static void
-init_frames (void)
-{
- /* Frames are associated to an unused image. */
- static gint32 frames_image_id;
- gint i;
- gint32 new_frame, previous_frame, new_layer;
- gboolean animated;
- GtkAction *action;
- gint duration = 0;
- DisposeType disposal = settings.default_frame_disposal;
- gchar *layer_name;
-
- total_frames = total_layers;
-
- /* Cleanup before re-generation. */
- if (frames)
- {
- gimp_image_delete (frames_image_id);
- g_free (frames);
- g_free (frame_durations);
- }
- frames = g_try_malloc0_n (total_frames, sizeof (gint32));
- frame_durations = g_try_malloc0_n (total_frames, sizeof (guint32));
- if (! frames || ! frame_durations)
- {
- gimp_message (_("Memory could not be allocated to the frame container."));
- gtk_main_quit ();
- gimp_quit ();
- return;
- }
- frames_image_id = gimp_image_new (width, height, imagetype);
- /* Save processing time and memory by not saving history and merged frames. */
- gimp_image_undo_disable (frames_image_id);
-
- for (i = 0; i < total_frames; i++)
- {
- layer_name = gimp_item_get_name (layers[total_layers - (i + 1)]);
- if (layer_name)
- {
- duration = parse_ms_tag (layer_name);
- disposal = parse_disposal_tag (layer_name);
- g_free (layer_name);
- }
-
- if (i > 0 && disposal != DISPOSE_REPLACE)
- {
- previous_frame = gimp_layer_copy (frames[i - 1]);
- gimp_image_insert_layer (frames_image_id, previous_frame, 0, -1);
- gimp_item_set_visible (previous_frame, TRUE);
- }
- new_layer = gimp_layer_new_from_drawable (layers[total_layers - (i + 1)], frames_image_id);
- gimp_image_insert_layer (frames_image_id, new_layer, 0, -1);
- gimp_item_set_visible (new_layer, TRUE);
- new_frame = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
- frames[i] = new_frame;
- gimp_item_set_visible (new_frame, FALSE);
-
- if (duration <= 0)
- duration = settings.default_frame_duration;
- frame_durations[i] = (guint32) duration;
- }
-
- /* Update the UI. */
- animated = total_frames >= 2;
- action = gtk_ui_manager_get_action (ui_manager,
- "/ui/anim-play-toolbar/play");
- gtk_action_set_sensitive (action, animated);
-
- action = gtk_ui_manager_get_action (ui_manager,
- "/ui/anim-play-toolbar/step-back");
- gtk_action_set_sensitive (action, animated);
-
- action = gtk_ui_manager_get_action (ui_manager,
- "/ui/anim-play-toolbar/step");
- gtk_action_set_sensitive (action, animated);
-
- action = gtk_ui_manager_get_action (ui_manager,
- "/ui/anim-play-toolbar/rewind");
- gtk_action_set_sensitive (action, animated);
-
- /* Keep the same frame number, unless it is now invalid. */
- if (frame_number >= total_frames)
- frame_number = 0;
-}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]