1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * file-webp - WebP file format plug-in for the GIMP
5  * Copyright (C) 2015  Nathan Osman
6  * Copyright (C) 2016  Ben Touchette
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <libgimp/gimp.h>
25 #include <libgimp/gimpui.h>
26 
27 #include <webp/encode.h>
28 
29 #include "file-webp.h"
30 #include "file-webp-dialog.h"
31 
32 #include "libgimp/stdplugins-intl.h"
33 
34 
35 static void           save_dialog_toggle_scale   (GtkWidget     *widget,
36                                                   gpointer       data);
37 
38 static void           save_dialog_toggle_minsize (GtkWidget     *widget,
39                                                   gpointer       data);
40 
41 static void           show_maxkeyframe_hints     (GtkAdjustment *adj,
42                                                   GtkLabel      *label);
43 
44 
45 static void
save_dialog_toggle_scale(GtkWidget * widget,gpointer data)46 save_dialog_toggle_scale (GtkWidget *widget,
47                           gpointer   data)
48 {
49   gimp_scale_entry_set_sensitive (GTK_OBJECT (data),
50                                   ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
51 }
52 
53 static void
save_dialog_toggle_minsize(GtkWidget * widget,gpointer data)54 save_dialog_toggle_minsize (GtkWidget *widget,
55                             gpointer   data)
56 {
57   gtk_widget_set_sensitive (GTK_WIDGET (data),
58                             ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
59 }
60 
61 static void
show_maxkeyframe_hints(GtkAdjustment * adj,GtkLabel * label)62 show_maxkeyframe_hints (GtkAdjustment *adj,
63                         GtkLabel      *label)
64 {
65   gint kmax;
66 
67   kmax = (gint) gtk_adjustment_get_value (adj);
68   if (kmax == 0)
69     {
70       gtk_label_set_text (label, _("(no keyframes)"));
71     }
72   else if (kmax == 1)
73     {
74       gtk_label_set_text (label, _("(all frames are keyframes)"));
75     }
76   else
77     {
78       gtk_label_set_text (label, "");
79     }
80 }
81 
82 gboolean
save_dialog(WebPSaveParams * params,gint32 image_ID)83 save_dialog (WebPSaveParams *params,
84              gint32          image_ID)
85 {
86   GtkWidget *dialog;
87   GtkWidget *vbox;
88   GtkWidget *table;
89   GtkWidget *expander;
90   GtkWidget *frame;
91   GtkWidget *vbox2;
92   GtkWidget *label;
93   GtkWidget *toggle;
94   GtkWidget *toggle_minsize;
95   GtkWidget *combo;
96   GtkObject *quality_scale;
97   GtkObject *alpha_quality_scale;
98   gint32     nlayers;
99   gboolean   animation_supported = FALSE;
100   gboolean   run;
101   gchar     *text;
102   gint       row = 0;
103 
104   g_free (gimp_image_get_layers (image_ID, &nlayers));
105   animation_supported = nlayers > 1;
106 
107   /* Create the dialog */
108   dialog = gimp_export_dialog_new (_("WebP"), PLUG_IN_BINARY, SAVE_PROC);
109 
110   /* Create the vbox */
111   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
112   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
113   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
114                       vbox, FALSE, FALSE, 0);
115   gtk_widget_show (vbox);
116 
117   /* Create the table */
118   table = gtk_table_new (4, 3, FALSE);
119   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
120   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
121   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
122   gtk_widget_show (table);
123 
124   /* Create the lossless checkbox */
125   toggle = gtk_check_button_new_with_mnemonic (_("_Lossless"));
126   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
127                                 params->lossless);
128   gtk_table_attach (GTK_TABLE (table), toggle,
129                     0, 3, row, row + 1,
130                     GTK_FILL, GTK_FILL, 0, 0);
131   gtk_widget_show (toggle);
132   row++;
133 
134   g_signal_connect (toggle, "toggled",
135                     G_CALLBACK (gimp_toggle_button_update),
136                     &params->lossless);
137 
138   /* Create the slider for image quality */
139   quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
140                                         0, row++,
141                                         _("Image _quality:"),
142                                         125,
143                                         0,
144                                         params->quality,
145                                         0.0, 100.0,
146                                         1.0, 10.0,
147                                         0, TRUE,
148                                         0.0, 0.0,
149                                         _("Image quality"),
150                                         NULL);
151   gimp_scale_entry_set_sensitive (quality_scale, ! params->lossless);
152 
153   g_signal_connect (quality_scale, "value-changed",
154                     G_CALLBACK (gimp_float_adjustment_update),
155                     &params->quality);
156 
157   /* Create the slider for alpha channel quality */
158   alpha_quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
159                                               0, row++,
160                                               _("Alpha q_uality:"),
161                                               125,
162                                               0,
163                                               params->alpha_quality,
164                                               0.0, 100.0,
165                                               1.0, 10.0,
166                                               0, TRUE,
167                                               0.0, 0.0,
168                                               _("Alpha channel quality"),
169                                               NULL);
170   gimp_scale_entry_set_sensitive (alpha_quality_scale, ! params->lossless);
171 
172   g_signal_connect (alpha_quality_scale, "value-changed",
173                     G_CALLBACK (gimp_float_adjustment_update),
174                     &params->alpha_quality);
175 
176   /* Enable and disable the sliders when the lossless option is selected */
177   g_signal_connect (toggle, "toggled",
178                     G_CALLBACK (save_dialog_toggle_scale),
179                     quality_scale);
180   g_signal_connect (toggle, "toggled",
181                     G_CALLBACK (save_dialog_toggle_scale),
182                     alpha_quality_scale);
183 
184   /* Create the combobox containing the presets */
185   combo = gimp_int_combo_box_new ("Default", WEBP_PRESET_DEFAULT,
186                                   "Picture", WEBP_PRESET_PICTURE,
187                                   "Photo",   WEBP_PRESET_PHOTO,
188                                   "Drawing", WEBP_PRESET_DRAWING,
189                                   "Icon",    WEBP_PRESET_ICON,
190                                   "Text",    WEBP_PRESET_TEXT,
191                                   NULL);
192   label = gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
193                                      _("Source _type:"), 0.0, 0.5,
194                                      combo, 2, FALSE);
195   gimp_help_set_help_data (label,
196                            _("WebP encoder \"preset\""),
197                            NULL);
198 
199   gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
200                               params->preset,
201                               G_CALLBACK (gimp_int_combo_box_get_active),
202                               &params->preset);
203 
204   if (animation_supported)
205     {
206       GtkWidget      *animation_box;
207       GtkAdjustment  *adj;
208       GtkWidget      *delay;
209       GtkWidget      *hbox;
210       GtkWidget      *label_kf;
211       GtkAdjustment  *adj_kf;
212       GtkWidget      *kf_distance;
213       GtkWidget      *hbox_kf;
214       PangoAttrList  *attrs;
215       PangoAttribute *attr;
216 
217       vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
218       gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
219       gtk_widget_show (vbox2);
220 
221       text = g_strdup_printf ("<b>%s</b>", _("_Advanced Options"));
222       expander = gtk_expander_new_with_mnemonic (text);
223       gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE);
224       g_free (text);
225 
226 
227       /* Create the top-level animation checkbox expander */
228       text = g_strdup_printf ("<b>%s</b>", _("As A_nimation"));
229       toggle = gtk_check_button_new_with_mnemonic (text);
230       g_free (text);
231 
232       gtk_label_set_use_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (toggle))),
233                                 TRUE);
234       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
235                                     params->animation);
236       gtk_box_pack_start (GTK_BOX (vbox2), toggle, TRUE, TRUE, 0);
237       gtk_widget_show (toggle);
238 
239       g_signal_connect (toggle, "toggled",
240                         G_CALLBACK (gimp_toggle_button_update),
241                         &params->animation);
242 
243       frame = gimp_frame_new ("<expander>");
244       gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, 0);
245       gtk_widget_show (frame);
246 
247       g_object_bind_property (toggle, "active",
248                               frame,  "visible",
249                               G_BINDING_SYNC_CREATE);
250 
251       /* animation options box */
252       animation_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
253       gtk_container_add (GTK_CONTAINER (frame), animation_box);
254       gtk_widget_show (animation_box);
255 
256       /* loop animation checkbox */
257       toggle = gtk_check_button_new_with_mnemonic (_("Loop _forever"));
258       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->loop);
259       gtk_box_pack_start (GTK_BOX (animation_box), toggle,
260                           FALSE, FALSE, 0);
261       gtk_widget_show (toggle);
262 
263       g_signal_connect (toggle, "toggled",
264                         G_CALLBACK (gimp_toggle_button_update),
265                         &params->loop);
266 
267       /* create a hbox for 'max key-frame distance */
268       hbox_kf = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
269       gtk_box_pack_start (GTK_BOX (animation_box), hbox_kf, FALSE, FALSE, 0);
270       gtk_widget_set_sensitive (hbox_kf, TRUE);
271       gtk_widget_show (hbox_kf);
272 
273       /* label for 'max key-frame distance' adjustment */
274       label_kf = gtk_label_new (_("Max distance between key-frames:"));
275       gtk_label_set_xalign (GTK_LABEL (label_kf), 0.2);
276       gtk_box_pack_start (GTK_BOX (hbox_kf), label_kf, FALSE, FALSE, 0);
277       gtk_widget_show (label_kf);
278 
279       /* key-frame distance entry */
280       adj_kf = (GtkAdjustment *) gtk_adjustment_new (params->kf_distance,
281                                                      0.0, 10000.0,
282                                                      1.0, 10.0, 0.0);
283       kf_distance = gimp_spin_button_new (adj_kf, 1, 0);
284       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (kf_distance), TRUE);
285       gtk_box_pack_start (GTK_BOX (hbox_kf), kf_distance, FALSE, FALSE, 0);
286       gtk_widget_show (kf_distance);
287 
288       g_signal_connect (adj_kf, "value-changed",
289                         G_CALLBACK (gimp_int_adjustment_update),
290                         &params->kf_distance);
291 
292       /* Add some hinting text for special values of key-frame distance. */
293       label_kf = gtk_label_new (NULL);
294       gtk_box_pack_start (GTK_BOX (hbox_kf), label_kf, FALSE, FALSE, 0);
295       gtk_widget_show (label_kf);
296 
297       attrs = pango_attr_list_new ();
298       attr  = pango_attr_style_new (PANGO_STYLE_ITALIC);
299       pango_attr_list_insert (attrs, attr);
300       gtk_label_set_attributes (GTK_LABEL (label_kf), attrs);
301       pango_attr_list_unref (attrs);
302 
303       g_signal_connect (adj_kf, "value-changed",
304                         G_CALLBACK (show_maxkeyframe_hints),
305                         label_kf);
306       show_maxkeyframe_hints (adj_kf, GTK_LABEL (label_kf));
307 
308       /* minimize-size checkbox */
309       toggle_minsize = gtk_check_button_new_with_mnemonic (_("_Minimize output size (slower)"));
310 
311       gtk_box_pack_start (GTK_BOX (animation_box), toggle_minsize,
312                           FALSE, FALSE, 0);
313       gtk_widget_show (toggle_minsize);
314 
315       g_signal_connect (toggle_minsize, "toggled",
316                         G_CALLBACK (gimp_toggle_button_update),
317                         &params->minimize_size);
318 
319 
320       /* Enable and disable the kf-distance box when the 'minimize size' option is selected */
321       g_signal_connect (toggle_minsize, "toggled",
322                         G_CALLBACK (save_dialog_toggle_minsize),
323                         hbox_kf);
324       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_minsize), params->minimize_size);
325 
326       /* create a hbox for delay */
327       hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
328       gtk_box_pack_start (GTK_BOX (animation_box), hbox, FALSE, FALSE, 0);
329       gtk_widget_show (hbox);
330 
331       /* label for 'delay' adjustment */
332       label = gtk_label_new (_("Delay between frames where unspecified:"));
333       gtk_label_set_xalign (GTK_LABEL (label), 0.0);
334       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
335       gtk_widget_show (label);
336 
337       /* default delay */
338       adj = (GtkAdjustment *) gtk_adjustment_new (params->delay,
339                                                   1, 10000, 1, 10, 0);
340       delay = gimp_spin_button_new (adj, 1, 0);
341       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (delay), TRUE);
342       gtk_box_pack_start (GTK_BOX (hbox), delay, FALSE, FALSE, 0);
343       gtk_widget_show (delay);
344 
345       g_signal_connect (adj, "value-changed",
346                         G_CALLBACK (gimp_int_adjustment_update),
347                         &params->delay);
348 
349       /* label for 'ms' adjustment */
350       label = gtk_label_new (_("milliseconds"));
351       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
352       gtk_widget_show (label);
353 
354       /* Create the force-delay checkbox */
355       toggle = gtk_check_button_new_with_mnemonic (_("Use _delay entered above for all frames"));
356       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
357                                     params->force_delay);
358       gtk_box_pack_start (GTK_BOX (animation_box), toggle, FALSE, FALSE, 0);
359       gtk_widget_show (toggle);
360 
361       g_signal_connect (toggle, "toggled",
362                         G_CALLBACK (gimp_toggle_button_update),
363                         &params->force_delay);
364   }
365 
366   /* Save EXIF data */
367   toggle = gtk_check_button_new_with_mnemonic (_("_Save Exif data"));
368   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->exif);
369   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
370   gtk_widget_show (toggle);
371 
372   g_signal_connect (toggle, "toggled",
373                     G_CALLBACK (gimp_toggle_button_update),
374                     &params->exif);
375 
376   /* XMP metadata */
377   toggle = gtk_check_button_new_with_mnemonic (_("Save _XMP data"));
378   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->xmp);
379   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
380   gtk_widget_show (toggle);
381 
382   g_signal_connect (toggle, "toggled",
383                     G_CALLBACK (gimp_toggle_button_update),
384                     &params->xmp);
385 
386   /* Color profile */
387   toggle = gtk_check_button_new_with_mnemonic (_("Save color _profile"));
388   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->profile);
389   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
390   gtk_widget_show (toggle);
391 
392   g_signal_connect (toggle, "toggled",
393                     G_CALLBACK (gimp_toggle_button_update),
394                     &params->profile);
395 
396   gtk_widget_show (dialog);
397 
398   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
399 
400   gtk_widget_destroy (dialog);
401 
402   return run;
403 }
404