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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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