diff --git a/plug-ins/file-webp/file-webp-dialog.c b/plug-ins/file-webp/file-webp-dialog.c
index c7b466d44a..7f8aff1f1a 100644
--- a/plug-ins/file-webp/file-webp-dialog.c
+++ b/plug-ins/file-webp/file-webp-dialog.c
@@ -92,27 +92,25 @@ save_dialog (WebPSaveParams *params,
gint32 image_ID,
gint32 n_layers)
{
- GtkWidget *dialog;
- GtkWidget *vbox;
- GtkWidget *label;
- GtkWidget *table;
- GtkWidget *expander;
- GtkWidget *frame;
- GtkWidget *vbox2;
- GtkWidget *save_exif;
- GtkWidget *save_xmp;
- GtkWidget *preset_label;
- GtkListStore *preset_list;
- GtkWidget *preset_combo;
- GtkWidget *lossless_checkbox;
- GtkWidget *animation_checkbox;
- GtkWidget *loop_anim_checkbox;
- GtkObject *quality_scale;
- GtkObject *alpha_quality_scale;
- gboolean animation_supported = FALSE;
- gint slider1 , slider2;
- gboolean run;
- gchar *text;
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *table;
+ GtkWidget *expander;
+ GtkWidget *frame;
+ GtkWidget *vbox2;
+ GtkWidget *save_exif;
+ GtkWidget *save_xmp;
+ GtkWidget *preset_label;
+ GtkListStore *preset_list;
+ GtkWidget *preset_combo;
+ GtkWidget *lossless_checkbox;
+ GtkWidget *animation_checkbox;
+ GtkObject *quality_scale;
+ GtkObject *alpha_quality_scale;
+ gboolean animation_supported = FALSE;
+ gboolean run;
+ gchar *text;
animation_supported = n_layers > 1;
@@ -127,7 +125,11 @@ save_dialog (WebPSaveParams *params,
gtk_widget_show (vbox);
/* Create the descriptive label at the top */
- label = gtk_label_new (_("Use the options below to customize the image."));
+ text = g_strdup_printf ("%s", _("WebP Options"));
+ label = gtk_label_new (NULL);
+ gtk_label_set_markup(GTK_LABEL(label), text);
+ g_free (text);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
@@ -138,80 +140,9 @@ save_dialog (WebPSaveParams *params,
gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
gtk_widget_show (table);
- /* Create the label for the selecting a preset */
- preset_label = gtk_label_new (_("Preset:"));
- gtk_label_set_xalign (GTK_LABEL (preset_label), 0.0);
- gtk_table_attach (GTK_TABLE (table), preset_label,
- 0, 1, 0, 1,
- GTK_FILL, GTK_FILL, 0, 0);
- gtk_widget_show (preset_label);
-
- /* Create the combobox containing the presets */
- preset_list = save_dialog_presets ();
- preset_combo = gimp_string_combo_box_new (GTK_TREE_MODEL (preset_list), 0, 1);
- g_object_unref (preset_list);
-
- gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (preset_combo),
- params->preset);
- gtk_table_attach (GTK_TABLE (table), preset_combo,
- 1, 3, 0, 1,
- GTK_FILL, GTK_FILL, 0, 0);
- gtk_widget_show (preset_combo);
-
- g_signal_connect (preset_combo, "changed",
- G_CALLBACK (save_dialog_preset_changed),
- ¶ms->preset);
-
- /* Create the lossless checkbox */
- lossless_checkbox = gtk_check_button_new_with_label (_("Lossless"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lossless_checkbox),
- params->lossless);
- gtk_table_attach (GTK_TABLE (table), lossless_checkbox,
- 1, 3, 1, 2,
- GTK_FILL, GTK_FILL, 0, 0);
- gtk_widget_show (lossless_checkbox);
-
- g_signal_connect (lossless_checkbox, "toggled",
- G_CALLBACK (gimp_toggle_button_update),
- ¶ms->lossless);
-
- slider1 = 2;
- slider2 = 3;
- if (animation_supported)
- {
- slider1 = 4;
- slider2 = 5;
-
- /* Create the animation checkbox */
- animation_checkbox = gtk_check_button_new_with_label (_("Use animation"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (animation_checkbox),
- params->animation);
- gtk_table_attach (GTK_TABLE (table), animation_checkbox,
- 1, 3, 2, 3,
- GTK_FILL, GTK_FILL, 0, 0);
- gtk_widget_show (animation_checkbox);
-
- g_signal_connect (animation_checkbox, "toggled",
- G_CALLBACK (gimp_toggle_button_update),
- ¶ms->animation);
-
- /* Create the loop animation checkbox */
- loop_anim_checkbox = gtk_check_button_new_with_label (_("Loop infinitely"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (loop_anim_checkbox),
- params->loop);
- gtk_table_attach (GTK_TABLE (table), loop_anim_checkbox,
- 1, 3, 3, 4,
- GTK_FILL, GTK_FILL, 0, 0);
- gtk_widget_show (loop_anim_checkbox);
-
- g_signal_connect (loop_anim_checkbox, "toggled",
- G_CALLBACK (gimp_toggle_button_update),
- ¶ms->loop);
- }
-
/* Create the slider for image quality */
quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
- 0, slider1,
+ 0, 0,
_("Image quality:"),
125,
0,
@@ -230,7 +161,7 @@ save_dialog (WebPSaveParams *params,
/* Create the slider for alpha channel quality */
alpha_quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
- 0, slider2,
+ 0, 1,
_("Alpha quality:"),
125,
0,
@@ -247,6 +178,43 @@ save_dialog (WebPSaveParams *params,
G_CALLBACK (gimp_float_adjustment_update),
¶ms->alpha_quality);
+ /* Create the label for the selecting a preset */
+ preset_label = gtk_label_new (_("Preset:"));
+ gtk_label_set_xalign (GTK_LABEL (preset_label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), preset_label,
+ 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (preset_label);
+
+ /* Create the combobox containing the presets */
+ preset_list = save_dialog_presets ();
+ preset_combo = gimp_string_combo_box_new (GTK_TREE_MODEL (preset_list), 0, 1);
+ g_object_unref (preset_list);
+
+ gimp_string_combo_box_set_active (GIMP_STRING_COMBO_BOX (preset_combo),
+ params->preset);
+ gtk_table_attach (GTK_TABLE (table), preset_combo,
+ 1, 3, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (preset_combo);
+
+ g_signal_connect (preset_combo, "changed",
+ G_CALLBACK (save_dialog_preset_changed),
+ ¶ms->preset);
+
+ /* Create the lossless checkbox */
+ lossless_checkbox = gtk_check_button_new_with_label (_("Lossless"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lossless_checkbox),
+ params->lossless);
+ gtk_table_attach (GTK_TABLE (table), lossless_checkbox,
+ 0, 4, 3, 4,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (lossless_checkbox);
+
+ g_signal_connect (lossless_checkbox, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ ¶ms->lossless);
+
/* Enable and disable the sliders when the lossless option is selected */
g_signal_connect (lossless_checkbox, "toggled",
G_CALLBACK (save_dialog_toggle_scale),
@@ -255,6 +223,113 @@ save_dialog (WebPSaveParams *params,
G_CALLBACK (save_dialog_toggle_scale),
alpha_quality_scale);
+ if (animation_supported)
+ {
+ GtkWidget *animation_box;
+ GtkWidget *animation_box2;
+ GtkWidget *animation_label;
+ GtkWidget *loop_anim_checkbox;
+ GtkWidget *animation_expander;
+ GtkWidget *animation_frame;
+ GtkAdjustment *adj;
+ GtkWidget *delay;
+ GtkWidget *delay_label;
+ GtkWidget *delay_label2;
+ GtkWidget *delay_hbox;
+ GtkWidget *delay_checkbox;
+
+ /* Create the top-level animation checkbox */
+ animation_checkbox = gtk_check_button_new_with_label (_("As animation"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (animation_checkbox),
+ params->animation);
+ gtk_table_attach (GTK_TABLE (table), animation_checkbox,
+ 0, 4, 4, 5,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_set_sensitive (animation_checkbox, TRUE);
+ g_signal_connect (animation_checkbox, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ ¶ms->animation);
+ gtk_widget_show (animation_checkbox);
+
+ text = g_strdup_printf ("%s", _("Animated WebP Options"));
+ animation_expander = gtk_expander_new_with_mnemonic (text);
+ gtk_expander_set_use_markup (GTK_EXPANDER (animation_expander), TRUE);
+ g_free (text);
+ gtk_box_pack_start (GTK_BOX (vbox), animation_expander, TRUE, TRUE, 0);
+ gtk_widget_show (animation_expander);
+
+ /* animation options box */
+ animation_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (animation_box), 6);
+ gtk_container_add (GTK_CONTAINER (animation_expander), animation_box);
+ gtk_widget_show (animation_box);
+
+ animation_frame = gimp_frame_new ("");
+ gtk_box_pack_start (GTK_BOX (animation_box), animation_frame, FALSE, FALSE, 0);
+ gtk_widget_show (animation_frame);
+
+ animation_box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (animation_frame), animation_box2);
+ gtk_widget_show (animation_box2);
+
+ /* loop animation checkbox */
+ loop_anim_checkbox = gtk_check_button_new_with_label (_("Loop forever"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (loop_anim_checkbox),
+ params->loop);
+ gtk_box_pack_start (GTK_BOX (animation_box2), loop_anim_checkbox, FALSE, FALSE, 0);
+ gtk_widget_show (loop_anim_checkbox);
+
+ g_signal_connect (loop_anim_checkbox, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ ¶ms->loop);
+
+ /* create a hbox for delay */
+ delay_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (animation_box2), delay_hbox, FALSE, FALSE, 0);
+ gtk_widget_show (delay_hbox);
+
+ /* label for 'delay' adjustment */
+ delay_label = gtk_label_new (_("Delay between frames where unspecified:"));
+ gtk_label_set_xalign (GTK_LABEL (delay_label), 0.0);
+ gtk_box_pack_start (GTK_BOX (delay_hbox), delay_label, FALSE, FALSE, 0);
+ gtk_widget_show (delay_label);
+
+ /* default delay */
+ adj = (GtkAdjustment *) gtk_adjustment_new (params->delay, 1, 10000, 1, 10, 0);
+ delay = gtk_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (delay), TRUE);
+ gtk_box_pack_start (GTK_BOX (delay_hbox), delay, FALSE, FALSE, 0);
+ gtk_widget_show (delay);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ ¶ms->delay);
+
+ /* label for 'ms' adjustment */
+ delay_label2 = gtk_label_new (_("milliseconds"));
+ gtk_box_pack_start (GTK_BOX (delay_hbox), delay_label2, FALSE, FALSE, 0);
+ gtk_widget_show (delay_label2);
+
+ /* Create the force-delay checkbox */
+ delay_checkbox = gtk_check_button_new_with_label (_("Use delay entered above for all frames"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (delay_checkbox),
+ params->force_delay);
+ gtk_box_pack_start (GTK_BOX (animation_box2), delay_checkbox, FALSE, FALSE, 0);
+ gtk_widget_show (delay_checkbox);
+
+ g_signal_connect (delay_checkbox, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ ¶ms->force_delay);
+
+
+
+ /* bind the animation checkbox to the parameters */
+ g_object_bind_property (animation_checkbox, "active",
+ animation_expander, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ }
+
+ /* Advanced options */
text = g_strdup_printf ("%s", _("_Advanced Options"));
expander = gtk_expander_new_with_mnemonic (text);
gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE);
@@ -295,6 +370,7 @@ save_dialog (WebPSaveParams *params,
G_CALLBACK (gimp_toggle_button_update),
¶ms->xmp);
+
gtk_widget_show (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
diff --git a/plug-ins/file-webp/file-webp-save.c b/plug-ins/file-webp/file-webp-save.c
index b864296198..ea072e0ad2 100644
--- a/plug-ins/file-webp/file-webp-save.c
+++ b/plug-ins/file-webp/file-webp-save.c
@@ -368,6 +368,53 @@ save_layer (const gchar *filename,
return status;
}
+static gint
+parse_ms_tag (const gchar *str)
+{
+ gint sum = 0;
+ gint offset = 0;
+ gint length;
+
+ length = strlen (str);
+
+ find_another_bra:
+
+ while ((offset < length) && (str[offset] != '('))
+ offset++;
+
+ if (offset >= length)
+ return(-1);
+
+ if (! g_ascii_isdigit (str[++offset]))
+ goto find_another_bra;
+
+ do
+ {
+ sum *= 10;
+ sum += str[offset] - '0';
+ offset++;
+ }
+ while ((offset < length) && (g_ascii_isdigit (str[offset])));
+
+ if (length - offset <= 2)
+ return(-3);
+
+ if ((g_ascii_toupper (str[offset]) != 'M') ||
+ (g_ascii_toupper (str[offset + 1]) != 'S'))
+ return -4;
+
+ return sum;
+}
+
+static gint
+get_layer_delay(gint32 layer)
+{
+ gchar *layer_name = gimp_item_get_name (layer);
+ gint delay_ms = parse_ms_tag (layer_name);
+ g_free (layer_name);
+ return delay_ms;
+}
+
gboolean
save_animation (const gchar *filename,
gint32 nLayers,
@@ -399,7 +446,9 @@ save_animation (const gchar *filename,
do
{
- gint loop;
+ gint loop;
+ gint default_delay = params->delay;
+ gboolean force_delay = params->force_delay;
/* Begin displaying export progress */
gimp_progress_init_printf (_("Saving '%s'"),
@@ -438,6 +487,7 @@ save_animation (const gchar *filename,
WebPPicture picture;
WebPMemoryWriter mw = { 0 };
gint32 drawable = allLayers[nLayers - 1 - loop];
+ gint delay = get_layer_delay (drawable);
/* Obtain the drawable type */
has_alpha = gimp_drawable_has_alpha (drawable);
@@ -537,7 +587,7 @@ save_animation (const gchar *filename,
g_object_unref (geglbuffer);
gimp_progress_update ((loop + 1.0) / nLayers);
- frame_timestamp += 100; /* TODO: should extract the real time stamp from layer */
+ frame_timestamp += (delay <= 0 || force_delay) ? default_delay : delay;
}
if (status == FALSE)
diff --git a/plug-ins/file-webp/file-webp-save.h b/plug-ins/file-webp/file-webp-save.h
index 98fb202da9..006996bca1 100644
--- a/plug-ins/file-webp/file-webp-save.h
+++ b/plug-ins/file-webp/file-webp-save.h
@@ -34,6 +34,8 @@ typedef struct
gboolean exif;
gboolean iptc;
gboolean xmp;
+ gint delay;
+ gboolean force_delay;
} WebPSaveParams;
diff --git a/plug-ins/file-webp/file-webp.c b/plug-ins/file-webp/file-webp.c
index 12853f6af6..0247e61586 100644
--- a/plug-ins/file-webp/file-webp.c
+++ b/plug-ins/file-webp/file-webp.c
@@ -44,7 +44,7 @@ static void run (const gchar *name,
GimpParam **return_vals);
-GimpPlugInInfo PLUG_IN_INFO =
+const GimpPlugInInfo PLUG_IN_INFO =
{
NULL,
NULL,
@@ -85,7 +85,9 @@ query (void)
{ GIMP_PDB_INT32, "anim-loop", "Loop animation infinitely (0/1)" },
{ GIMP_PDB_INT32, "exif", "Toggle saving exif data (0/1)" },
{ GIMP_PDB_INT32, "iptc", "Toggle saving iptc data (0/1)" },
- { GIMP_PDB_INT32, "xmp", "Toggle saving xmp data (0/1)" }
+ { GIMP_PDB_INT32, "xmp", "Toggle saving xmp data (0/1)" },
+ { GIMP_PDB_INT32, "delay", "Delay to use when timestamp are not available or forced" },
+ { GIMP_PDB_INT32, "force-delay", "Toggle to for delay" }
};
gimp_install_procedure (LOAD_PROC,
@@ -172,28 +174,23 @@ run (const gchar *name,
{
WebPSaveParams params;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
- gint32 *layers;
+ gint32 *layers = NULL;
gint32 n_layers;
- /* Initialize the parameters to their defaults */
- params.preset = g_strdup ("default");
- params.lossless = FALSE;
- params.animation = FALSE;
- params.loop = TRUE;
- params.quality = 90.0f;
- params.alpha_quality = 100.0f;
- params.exif = TRUE;
- params.iptc = TRUE;
- params.xmp = TRUE;
+ if (run_mode == GIMP_RUN_INTERACTIVE ||
+ run_mode == GIMP_RUN_WITH_LAST_VALS)
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
image_ID = param[1].data.d_int32;
drawable_ID = param[2].data.d_int32;
switch (run_mode)
{
- case GIMP_RUN_INTERACTIVE:
case GIMP_RUN_WITH_LAST_VALS:
- gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, ¶ms);
+ params.preset = g_strdup ("default"); /* can't serialize strings, so restore default */
export = gimp_export_image (&image_ID, &drawable_ID, "WebP",
GIMP_EXPORT_CAN_HANDLE_RGB |
@@ -205,31 +202,19 @@ run (const gchar *name,
if (export == GIMP_EXPORT_CANCEL)
{
values[0].data.d_status = GIMP_PDB_CANCEL;
- return;
+ status = GIMP_PDB_CANCEL;
+ break;
}
- break;
- default:
- break;
- }
-
- layers = gimp_image_get_layers (image_ID, &n_layers);
-
- switch (run_mode)
- {
- case GIMP_RUN_INTERACTIVE:
- if (! save_dialog (¶ms, image_ID, n_layers))
- status = GIMP_PDB_CANCEL;
break;
case GIMP_RUN_NONINTERACTIVE:
- if (nparams != 10)
+ if (nparams != 16)
{
status = GIMP_PDB_CALLING_ERROR;
}
else
{
- g_free (params.preset);
params.preset = g_strdup (param[5].data.d_string);
params.lossless = param[6].data.d_int32;
params.quality = param[7].data.d_float;
@@ -239,6 +224,8 @@ run (const gchar *name,
params.exif = param[11].data.d_int32;
params.iptc = param[12].data.d_int32;
params.xmp = param[13].data.d_int32;
+ params.delay = param[14].data.d_int32;
+ params.force_delay = param[15].data.d_int32;
}
break;
@@ -246,24 +233,50 @@ run (const gchar *name,
break;
}
+
if (status == GIMP_PDB_SUCCESS)
{
- if (! save_image (param[3].data.d_string,
- n_layers, layers,
- image_ID,
- drawable_ID,
- ¶ms,
- &error))
+ layers = gimp_image_get_layers (image_ID, &n_layers);
+ if (run_mode == GIMP_RUN_INTERACTIVE)
{
- status = GIMP_PDB_EXECUTION_ERROR;
+ if (! save_dialog (¶ms, image_ID, n_layers))
+ {
+ status = GIMP_PDB_CANCEL;
+ }
}
}
+ if (status != GIMP_PDB_SUCCESS)
+ {
+ g_free(params.preset);
+ g_free (layers);
+ return;
+ }
+
+ if (! save_image (param[3].data.d_string,
+ n_layers, layers,
+ image_ID,
+ drawable_ID,
+ ¶ms,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
g_free (params.preset);
+ params.preset = NULL;
+
g_free (layers);
if (export == GIMP_EXPORT_EXPORT)
gimp_image_delete (image_ID);
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* save parameters for later */
+ /* we can't serialize strings this way. params.preset isn't saved. */
+ gimp_set_data (SAVE_PROC, ¶ms, sizeof (params));
+ }
}
/* If an error was supplied, include it in the return values */